220 lines
8.2 KiB
Swift
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")
|
|
}
|
|
}
|