import AppKit @MainActor protocol WallpaperManaging { func download(from url: URL) async throws -> URL func setDesktopImage(from fileURL: URL, screen: NSScreen?) throws func saveToFolder(from sourceURL: URL, to folderURL: URL) throws } @MainActor protocol DesktopImageSetting { func setDesktopImageURL( _ url: URL, for screen: NSScreen, options: [NSWorkspace.DesktopImageOptionKey: Any] ) throws } extension NSWorkspace: DesktopImageSetting {} class WallpaperManager: WallpaperManaging { static let shared = WallpaperManager() private let session: URLSession private let desktopSetter: @MainActor (URL, NSScreen) throws -> Void init( session: URLSession = .shared, desktopSetter: (@MainActor (URL, NSScreen) throws -> Void)? = nil, workspace: DesktopImageSetting = NSWorkspace.shared ) { self.session = session self.desktopSetter = desktopSetter ?? { url, screen in try workspace.setDesktopImageURL( url, for: screen, options: [:] ) } } /// Downloads the file to a temporary location and returns the local URL. func download(from url: URL) async throws -> URL { let (data, response) = try await session.data(from: url) guard let response = response as? HTTPURLResponse, (200...299).contains(response.statusCode) else { throw WallhavenError.apiError("Wallpaper download failed") } let filename = url.lastPathComponent let tempDir = FileManager.default.temporaryDirectory let fileURL = tempDir.appendingPathComponent(filename) try data.write(to: fileURL) return fileURL } /// Sets the desktop image from a local file URL. @MainActor func setDesktopImage(from fileURL: URL, screen: NSScreen? = nil) throws { if let screen = screen { try desktopSetter(fileURL, screen) } else { // Set for all screens or just main? Let's default to main for safety, // but the UI will likely drive this. // If no screen specified, maybe set for the 'active' one. let targetScreen = NSScreen.main ?? NSScreen.screens.first! try desktopSetter(fileURL, targetScreen) } } /// Copies the file to the specified destination directory. func saveToFolder(from sourceURL: URL, to folderURL: URL) throws { // Ensure folder exists try FileManager.default.createDirectory(at: folderURL, withIntermediateDirectories: true) let destinationURL = folderURL.appendingPathComponent(sourceURL.lastPathComponent) // If file exists, maybe don't overwrite or handle gracefully? // For now, we'll remove it if it exists to ensure we have the fresh one, or just skip. if FileManager.default.fileExists(atPath: destinationURL.path) { try FileManager.default.removeItem(at: destinationURL) } try FileManager.default.copyItem(at: sourceURL, to: destinationURL) } // Legacy support wrapper (optional, but cleaner to update call sites) @MainActor func setWallpaper(from url: URL, screen: NSScreen? = nil) async throws { let localURL = try await download(from: url) try setDesktopImage(from: localURL, screen: screen) } }