Files
GIFCollector/GIFCollector/Services/GIFStorageService.swift
Joshua Higgins 7fc60e939e Fixed App Groups
2025-06-05 00:06:17 -04:00

220 lines
8.2 KiB
Swift

import Foundation
class GIFStorageService {
static let shared = GIFStorageService()
// Use constant App Group ID to ensure consistency across targets
private let userDefaults = UserDefaults(suiteName: appGroupID)
// Use a consistent key for saved GIFs across all extensions
private let savedGIFsKey = "sharedGIFs"
private let pendingGIFsKey = "pendingGIFs"
private init() {
// Make sure the shared UserDefaults exists
if userDefaults == nil {
print("ERROR: Could not create UserDefaults with app group. GIFs will not be shared between extensions!")
} else {
print("Successfully connected to shared UserDefaults")
}
// Process any pending GIFs from the Share Extension
checkForSharedGIFs()
// Log the number of GIFs found
let count = fetchGIFs().count
print("Found \(count) GIFs in shared storage")
}
func saveGIF(data: Data, fromURL urlString: String, completion: @escaping (GIF?) -> Void) {
// First store the GIF data to disk
guard let localPath = GIFFileManager.shared.storeGIF(data: data, fromURL: urlString) else {
completion(nil)
return
}
// Create and save the GIF model
let gif = GIF(localFilePath: localPath, originalURL: urlString)
var savedGIFs = fetchGIFs()
// Don't save duplicates of the same URL
if !savedGIFs.contains(where: { $0.originalURL == urlString }) {
savedGIFs.append(gif)
saveToUserDefaults(gifs: savedGIFs)
}
// Perform cleanup if needed
GIFFileManager.shared.performStorageCleanupIfNeeded()
completion(gif)
}
func fetchGIFs() -> [GIF] {
guard let userDefaultsInstance = userDefaults else {
print("ERROR: UserDefaults is nil, cannot fetch GIFs")
return []
}
guard let data = userDefaultsInstance.data(forKey: savedGIFsKey) else {
print("No GIFs data found in UserDefaults for key: \(savedGIFsKey)")
return []
}
do {
let gifs = try JSONDecoder().decode([GIF].self, from: data)
print("Fetched \(gifs.count) GIFs from shared storage")
// Debug IDs
if !gifs.isEmpty {
print("GIF IDs: \(gifs.map { $0.id.uuidString.prefix(8) }.joined(separator: ", "))")
}
// Filter out any GIFs whose files no longer exist
let validGIFs = gifs.filter {
let exists = GIFFileManager.shared.fileExists(at: $0.localFilePath)
if !exists {
print("Warning: GIF file not found at path: \($0.localFilePath)")
}
return exists
}
// If we filtered any out, save the updated list
if validGIFs.count != gifs.count {
print("Removed \(gifs.count - validGIFs.count) invalid GIFs from storage")
saveToUserDefaults(gifs: validGIFs)
}
return validGIFs.sorted(by: { $0.createdAt > $1.createdAt })
} catch {
print("ERROR: Failed to decode GIFs from UserDefaults: \(error)")
return []
}
}
func deleteGIF(with id: UUID) {
var savedGIFs = fetchGIFs()
// Find the GIF to delete
if let gifToDelete = savedGIFs.first(where: { $0.id == id }) {
print("Deleting GIF with ID: \(id), path: \(gifToDelete.localFilePath)")
// Delete the file from storage
let fileDeleted = GIFFileManager.shared.deleteGIF(at: gifToDelete.localFilePath)
if fileDeleted {
print("Successfully deleted GIF file from storage")
} else {
print("Warning: Failed to delete GIF file at path: \(gifToDelete.localFilePath)")
}
// Remove from the list regardless of whether file deletion succeeded
let countBefore = savedGIFs.count
savedGIFs.removeAll(where: { $0.id == id })
if savedGIFs.count < countBefore {
print("Removed GIF from saved list, count before: \(countBefore), after: \(savedGIFs.count)")
saveToUserDefaults(gifs: savedGIFs)
} else {
print("Error: GIF with ID \(id) was not found in memory array")
}
} else {
print("Error: Could not find GIF with ID \(id) to delete")
}
}
func clearAllGIFs() {
// Delete all GIF files
fetchGIFs().forEach { gif in
GIFFileManager.shared.deleteGIF(at: gif.localFilePath)
}
// Clear the list
saveToUserDefaults(gifs: [])
}
private func saveToUserDefaults(gifs: [GIF]) {
do {
let data = try JSONEncoder().encode(gifs)
userDefaults?.set(data, forKey: savedGIFsKey)
print("Successfully saved \(gifs.count) GIFs to shared storage")
// Force UserDefaults to synchronize to ensure immediate visibility across extensions
if userDefaults?.synchronize() == true {
print("UserDefaults successfully synchronized")
} else {
print("Warning: UserDefaults synchronize may have failed")
}
// Double-check that our changes were actually saved
if let checkData = userDefaults?.data(forKey: savedGIFsKey),
let checkGifs = try? JSONDecoder().decode([GIF].self, from: checkData) {
print("Verified: \(checkGifs.count) GIFs stored in UserDefaults")
} else {
print("ERROR: Failed to verify GIFs were saved to UserDefaults")
}
} catch {
print("ERROR: Failed to encode GIFs for storage: \(error)")
}
}
func getGIFData(for gif: GIF) -> Data? {
return GIFFileManager.shared.loadGIFData(from: gif.localFilePath)
}
// MARK: - Share Extension Integration
func checkForSharedGIFs() {
guard let pendingGIFsData = userDefaults?.array(forKey: pendingGIFsKey) as? [[String: Any]] else {
print("No pending GIFs found in shared storage")
return
}
guard !pendingGIFsData.isEmpty else {
print("Pending GIFs array is empty")
return
}
print("Found \(pendingGIFsData.count) pending GIFs to process")
var savedGIFs = fetchGIFs()
var newGIFsAdded = false
for gifInfo in pendingGIFsData {
if let localFilePath = gifInfo["localFilePath"] as? String,
let originalURL = gifInfo["originalURL"] as? String,
let createdAt = gifInfo["createdAt"] as? TimeInterval {
// Create a GIF object - use the provided ID if available, or create a new one
let gifId = (gifInfo["id"] as? String).flatMap { UUID(uuidString: $0) } ?? UUID()
let gifCreatedAt = Date(timeIntervalSince1970: createdAt)
let gif = GIF(
localFilePath: localFilePath,
originalURL: originalURL,
id: gifId,
createdAt: gifCreatedAt
)
// Don't add duplicates
if !savedGIFs.contains(where: { $0.localFilePath == localFilePath }) {
savedGIFs.append(gif)
newGIFsAdded = true
}
}
}
// Save updated GIFs list and clear pending ones
if newGIFsAdded {
print("Added \(pendingGIFsData.count) new GIFs from shared extension")
saveToUserDefaults(gifs: savedGIFs)
} else {
print("No new GIFs were added from pending list")
}
// Clear pending GIFs
userDefaults?.removeObject(forKey: pendingGIFsKey)
userDefaults?.synchronize()
print("Cleared pending GIFs from shared storage")
}
}