Merge branch 'main' of github.com:akatreyt/Feels
This commit is contained in:
@@ -102,7 +102,9 @@ class BiometricAuthManager: ObservableObject {
|
||||
}
|
||||
return success
|
||||
} catch {
|
||||
#if DEBUG
|
||||
print("Authentication failed: \(error.localizedDescription)")
|
||||
#endif
|
||||
AnalyticsManager.shared.track(.biometricUnlockFailed(error: error.localizedDescription))
|
||||
|
||||
// If biometrics failed, try device passcode as fallback
|
||||
@@ -126,7 +128,9 @@ class BiometricAuthManager: ObservableObject {
|
||||
isUnlocked = success
|
||||
return success
|
||||
} catch {
|
||||
#if DEBUG
|
||||
print("Passcode authentication failed: \(error.localizedDescription)")
|
||||
#endif
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -146,7 +150,9 @@ class BiometricAuthManager: ObservableObject {
|
||||
|
||||
// Only allow enabling if biometrics are available
|
||||
guard context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) else {
|
||||
#if DEBUG
|
||||
print("Biometric authentication not available: \(error?.localizedDescription ?? "Unknown")")
|
||||
#endif
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -164,7 +170,9 @@ class BiometricAuthManager: ObservableObject {
|
||||
|
||||
return success
|
||||
} catch {
|
||||
#if DEBUG
|
||||
print("Failed to enable lock: \(error.localizedDescription)")
|
||||
#endif
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +87,9 @@ class ExportService {
|
||||
trackDataExported(format: "csv", count: entries.count)
|
||||
return tempURL
|
||||
} catch {
|
||||
#if DEBUG
|
||||
print("ExportService: Failed to write CSV: \(error)")
|
||||
#endif
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -177,7 +179,9 @@ class ExportService {
|
||||
try data.write(to: tempURL)
|
||||
return tempURL
|
||||
} catch {
|
||||
#if DEBUG
|
||||
print("ExportService: Failed to write PDF: \(error)")
|
||||
#endif
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
import Foundation
|
||||
import FoundationModels
|
||||
import os.log
|
||||
|
||||
/// Error types for insight generation
|
||||
enum InsightGenerationError: Error, LocalizedError {
|
||||
@@ -250,9 +251,7 @@ class FoundationModelsInsightService: ObservableObject {
|
||||
return insights
|
||||
} catch {
|
||||
// Log detailed error for debugging
|
||||
print("❌ AI Insight generation failed for '\(periodName)': \(error)")
|
||||
print(" Error type: \(type(of: error))")
|
||||
print(" Localized: \(error.localizedDescription)")
|
||||
AppLogger.ai.error("AI Insight generation failed for '\(periodName)': \(error)")
|
||||
|
||||
lastError = .generationFailed(underlying: error)
|
||||
throw lastError!
|
||||
|
||||
@@ -71,7 +71,9 @@ class HealthService: ObservableObject {
|
||||
|
||||
func requestAuthorization() async -> Bool {
|
||||
guard isAvailable else {
|
||||
#if DEBUG
|
||||
print("HealthService: HealthKit not available on this device")
|
||||
#endif
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -82,7 +84,9 @@ class HealthService: ObservableObject {
|
||||
AnalyticsManager.shared.track(.healthKitAuthorized)
|
||||
return true
|
||||
} catch {
|
||||
#if DEBUG
|
||||
print("HealthService: Authorization failed: \(error.localizedDescription)")
|
||||
#endif
|
||||
AnalyticsManager.shared.track(.healthKitAuthFailed(error: error.localizedDescription))
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
import os.log
|
||||
|
||||
/// Exports insights view screenshots for App Store marketing
|
||||
@MainActor
|
||||
@@ -27,7 +28,12 @@ class InsightsExporter {
|
||||
|
||||
// Clean and create export directory
|
||||
try? FileManager.default.removeItem(at: exportPath)
|
||||
try? FileManager.default.createDirectory(at: exportPath, withIntermediateDirectories: true)
|
||||
do {
|
||||
try FileManager.default.createDirectory(at: exportPath, withIntermediateDirectories: true)
|
||||
} catch {
|
||||
AppLogger.export.error("Failed to create insights export directory: \(error)")
|
||||
return nil
|
||||
}
|
||||
|
||||
var totalExported = 0
|
||||
|
||||
@@ -94,7 +100,11 @@ class InsightsExporter {
|
||||
if let image = renderer.uiImage {
|
||||
let url = folder.appendingPathComponent("\(name).png")
|
||||
if let data = image.pngData() {
|
||||
try? data.write(to: url)
|
||||
do {
|
||||
try data.write(to: url)
|
||||
} catch {
|
||||
AppLogger.export.error("Failed to write insights image '\(name)': \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +92,11 @@ class PhotoManager: ObservableObject {
|
||||
let thumbnailURL = thumbnailsDir.appendingPathComponent(filename)
|
||||
if let thumbnail = createThumbnail(from: image),
|
||||
let thumbnailData = thumbnail.jpegData(compressionQuality: 0.6) {
|
||||
try? thumbnailData.write(to: thumbnailURL)
|
||||
do {
|
||||
try thumbnailData.write(to: thumbnailURL)
|
||||
} catch {
|
||||
AppLogger.photos.error("Failed to save thumbnail: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
AnalyticsManager.shared.track(.photoAdded)
|
||||
@@ -107,13 +111,21 @@ class PhotoManager: ObservableObject {
|
||||
let filename = "\(id.uuidString).jpg"
|
||||
let fullURL = photosDir.appendingPathComponent(filename)
|
||||
|
||||
guard FileManager.default.fileExists(atPath: fullURL.path),
|
||||
let data = try? Data(contentsOf: fullURL),
|
||||
let image = UIImage(data: data) else {
|
||||
guard FileManager.default.fileExists(atPath: fullURL.path) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return image
|
||||
do {
|
||||
let data = try Data(contentsOf: fullURL)
|
||||
guard let image = UIImage(data: data) else {
|
||||
AppLogger.photos.error("Failed to create UIImage from photo data: \(id)")
|
||||
return nil
|
||||
}
|
||||
return image
|
||||
} catch {
|
||||
AppLogger.photos.error("Failed to read photo data for \(id): \(error)")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func loadThumbnail(id: UUID) -> UIImage? {
|
||||
@@ -123,10 +135,15 @@ class PhotoManager: ObservableObject {
|
||||
let thumbnailURL = thumbnailsDir.appendingPathComponent(filename)
|
||||
|
||||
// Try thumbnail first
|
||||
if FileManager.default.fileExists(atPath: thumbnailURL.path),
|
||||
let data = try? Data(contentsOf: thumbnailURL),
|
||||
let image = UIImage(data: data) {
|
||||
return image
|
||||
if FileManager.default.fileExists(atPath: thumbnailURL.path) {
|
||||
do {
|
||||
let data = try Data(contentsOf: thumbnailURL)
|
||||
if let image = UIImage(data: data) {
|
||||
return image
|
||||
}
|
||||
} catch {
|
||||
AppLogger.photos.error("Failed to read thumbnail data for \(id): \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
// Fall back to full image if thumbnail doesn't exist
|
||||
@@ -159,7 +176,11 @@ class PhotoManager: ObservableObject {
|
||||
|
||||
// Delete thumbnail
|
||||
if FileManager.default.fileExists(atPath: thumbnailURL.path) {
|
||||
try? FileManager.default.removeItem(at: thumbnailURL)
|
||||
do {
|
||||
try FileManager.default.removeItem(at: thumbnailURL)
|
||||
} catch {
|
||||
AppLogger.photos.error("Failed to delete thumbnail: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
if success {
|
||||
@@ -197,8 +218,13 @@ class PhotoManager: ObservableObject {
|
||||
var totalPhotoCount: Int {
|
||||
guard let photosDir = photosDirectory else { return 0 }
|
||||
|
||||
let files = try? FileManager.default.contentsOfDirectory(atPath: photosDir.path)
|
||||
return files?.filter { $0.hasSuffix(".jpg") }.count ?? 0
|
||||
do {
|
||||
let files = try FileManager.default.contentsOfDirectory(atPath: photosDir.path)
|
||||
return files.filter { $0.hasSuffix(".jpg") }.count
|
||||
} catch {
|
||||
AppLogger.photos.error("Failed to list photos directory: \(error)")
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
var totalStorageUsed: Int64 {
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
import os.log
|
||||
|
||||
/// Exports sharing template screenshots for App Store marketing
|
||||
@MainActor
|
||||
@@ -20,13 +21,23 @@ class SharingScreenshotExporter {
|
||||
|
||||
// Clean and create export directory
|
||||
try? FileManager.default.removeItem(at: exportPath)
|
||||
try? FileManager.default.createDirectory(at: exportPath, withIntermediateDirectories: true)
|
||||
do {
|
||||
try FileManager.default.createDirectory(at: exportPath, withIntermediateDirectories: true)
|
||||
} catch {
|
||||
AppLogger.export.error("Failed to create sharing export directory: \(error)")
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create subdirectories
|
||||
let origDir = exportPath.appendingPathComponent("originals", isDirectory: true)
|
||||
let varDir = exportPath.appendingPathComponent("variations", isDirectory: true)
|
||||
try? FileManager.default.createDirectory(at: origDir, withIntermediateDirectories: true)
|
||||
try? FileManager.default.createDirectory(at: varDir, withIntermediateDirectories: true)
|
||||
do {
|
||||
try FileManager.default.createDirectory(at: origDir, withIntermediateDirectories: true)
|
||||
try FileManager.default.createDirectory(at: varDir, withIntermediateDirectories: true)
|
||||
} catch {
|
||||
AppLogger.export.error("Failed to create sharing subdirectories: \(error)")
|
||||
return nil
|
||||
}
|
||||
|
||||
var totalExported = 0
|
||||
let distantPast = Date(timeIntervalSince1970: 0)
|
||||
@@ -166,7 +177,7 @@ class SharingScreenshotExporter {
|
||||
try data.write(to: url)
|
||||
return true
|
||||
} catch {
|
||||
print("Failed to save \(name): \(error)")
|
||||
AppLogger.export.error("Failed to save sharing screenshot '\(name)': \(error)")
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
import os.log
|
||||
|
||||
/// Exports watch view previews to PNG files for App Store screenshots
|
||||
@MainActor
|
||||
@@ -75,7 +76,12 @@ class WatchExporter {
|
||||
|
||||
// Clean and create export directory
|
||||
try? FileManager.default.removeItem(at: exportPath)
|
||||
try? FileManager.default.createDirectory(at: exportPath, withIntermediateDirectories: true)
|
||||
do {
|
||||
try FileManager.default.createDirectory(at: exportPath, withIntermediateDirectories: true)
|
||||
} catch {
|
||||
AppLogger.export.error("Failed to create watch export directory: \(error)")
|
||||
return nil
|
||||
}
|
||||
|
||||
var totalExported = 0
|
||||
|
||||
@@ -84,7 +90,12 @@ class WatchExporter {
|
||||
for iconOption in allIcons {
|
||||
let folderName = "\(tintOption.name)_\(iconOption.name)"
|
||||
let variantPath = exportPath.appendingPathComponent(folderName, isDirectory: true)
|
||||
try? FileManager.default.createDirectory(at: variantPath, withIntermediateDirectories: true)
|
||||
do {
|
||||
try FileManager.default.createDirectory(at: variantPath, withIntermediateDirectories: true)
|
||||
} catch {
|
||||
AppLogger.export.error("Failed to create watch variant directory '\(folderName)': \(error)")
|
||||
continue
|
||||
}
|
||||
|
||||
let config = WatchExportConfig(
|
||||
moodTint: tintOption.tint,
|
||||
@@ -241,7 +252,11 @@ class WatchExporter {
|
||||
if let image = renderer.uiImage {
|
||||
let url = folder.appendingPathComponent("\(name).png")
|
||||
if let data = image.pngData() {
|
||||
try? data.write(to: url)
|
||||
do {
|
||||
try data.write(to: url)
|
||||
} catch {
|
||||
AppLogger.export.error("Failed to write watch image '\(name)': \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
import os.log
|
||||
|
||||
/// Exports widget previews to PNG files for App Store screenshots
|
||||
@MainActor
|
||||
@@ -75,7 +76,12 @@ class WidgetExporter {
|
||||
|
||||
// Clean and create export directory
|
||||
try? FileManager.default.removeItem(at: exportPath)
|
||||
try? FileManager.default.createDirectory(at: exportPath, withIntermediateDirectories: true)
|
||||
do {
|
||||
try FileManager.default.createDirectory(at: exportPath, withIntermediateDirectories: true)
|
||||
} catch {
|
||||
AppLogger.export.error("Failed to create widget export directory: \(error)")
|
||||
return nil
|
||||
}
|
||||
|
||||
var totalExported = 0
|
||||
|
||||
@@ -84,7 +90,12 @@ class WidgetExporter {
|
||||
for iconOption in allIcons {
|
||||
let folderName = "\(tintOption.name)_\(iconOption.name)"
|
||||
let variantPath = exportPath.appendingPathComponent(folderName, isDirectory: true)
|
||||
try? FileManager.default.createDirectory(at: variantPath, withIntermediateDirectories: true)
|
||||
do {
|
||||
try FileManager.default.createDirectory(at: variantPath, withIntermediateDirectories: true)
|
||||
} catch {
|
||||
AppLogger.export.error("Failed to create variant directory '\(folderName)': \(error)")
|
||||
continue
|
||||
}
|
||||
|
||||
let config = WidgetExportConfig(
|
||||
moodTint: tintOption.tint,
|
||||
@@ -154,7 +165,12 @@ class WidgetExporter {
|
||||
let exportPath = documentsPath.appendingPathComponent("WidgetExports_Current", isDirectory: true)
|
||||
|
||||
try? FileManager.default.removeItem(at: exportPath)
|
||||
try? FileManager.default.createDirectory(at: exportPath, withIntermediateDirectories: true)
|
||||
do {
|
||||
try FileManager.default.createDirectory(at: exportPath, withIntermediateDirectories: true)
|
||||
} catch {
|
||||
AppLogger.export.error("Failed to create current config export directory: \(error)")
|
||||
return nil
|
||||
}
|
||||
|
||||
let config = WidgetExportConfig(
|
||||
moodTint: UserDefaultsStore.moodTintable(),
|
||||
@@ -176,7 +192,12 @@ class WidgetExporter {
|
||||
|
||||
// Clean and create export directory
|
||||
try? FileManager.default.removeItem(at: exportPath)
|
||||
try? FileManager.default.createDirectory(at: exportPath, withIntermediateDirectories: true)
|
||||
do {
|
||||
try FileManager.default.createDirectory(at: exportPath, withIntermediateDirectories: true)
|
||||
} catch {
|
||||
AppLogger.export.error("Failed to create voting layout export directory: \(error)")
|
||||
return nil
|
||||
}
|
||||
|
||||
var totalExported = 0
|
||||
|
||||
@@ -185,7 +206,12 @@ class WidgetExporter {
|
||||
for iconOption in allIcons {
|
||||
let folderName = "\(tintOption.name)_\(iconOption.name)"
|
||||
let variantPath = exportPath.appendingPathComponent(folderName, isDirectory: true)
|
||||
try? FileManager.default.createDirectory(at: variantPath, withIntermediateDirectories: true)
|
||||
do {
|
||||
try FileManager.default.createDirectory(at: variantPath, withIntermediateDirectories: true)
|
||||
} catch {
|
||||
AppLogger.export.error("Failed to create voting variant directory '\(folderName)': \(error)")
|
||||
continue
|
||||
}
|
||||
|
||||
let config = WidgetExportConfig(
|
||||
moodTint: tintOption.tint,
|
||||
@@ -371,7 +397,11 @@ class WidgetExporter {
|
||||
if let image = renderer.uiImage {
|
||||
let url = folder.appendingPathComponent("\(name).png")
|
||||
if let data = image.pngData() {
|
||||
try? data.write(to: url)
|
||||
do {
|
||||
try data.write(to: url)
|
||||
} catch {
|
||||
AppLogger.export.error("Failed to write widget image '\(name)': \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -383,7 +413,11 @@ class WidgetExporter {
|
||||
if let image = renderer.uiImage {
|
||||
let url = folder.appendingPathComponent("\(name).png")
|
||||
if let data = image.pngData() {
|
||||
try? data.write(to: url)
|
||||
do {
|
||||
try data.write(to: url)
|
||||
} catch {
|
||||
AppLogger.export.error("Failed to write live activity image '\(name)': \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user