Merge branch 'main' of github.com:akatreyt/Feels

This commit is contained in:
Trey t
2026-04-04 13:40:42 -05:00
41 changed files with 427 additions and 107 deletions

View File

@@ -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
}
}

View File

@@ -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
}
}

View File

@@ -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!

View File

@@ -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
}

View File

@@ -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)")
}
}
}
}

View File

@@ -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 {

View File

@@ -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

View File

@@ -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)")
}
}
}
}

View File

@@ -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)")
}
}
}
}