import ExpoModulesCore import Photos import PhotosUI public class LivePhotoModule: Module { public func definition() -> ModuleDefinition { Name("LivePhoto") /// Generate a Live Photo from an image + animation config and save to photo library. /// Returns the local identifier of the saved PHAsset. AsyncFunction("exportLivePhoto") { (configJson: String, promise: Promise) in guard let data = configJson.data(using: .utf8), let config = try? JSONSerialization.jsonObject(with: data) as? [String: Any], let sourceUri = config["sourceUri"] as? String else { promise.reject("INVALID_CONFIG", "Invalid wallpaper config JSON") return } let shaderId = config["animation"] as? String ?? "ken-burns" let uniforms = config["uniforms"] as? [String: Double] ?? [:] let intensity = Float(uniforms["intensity"] ?? 0.5) let speed = Float(uniforms["speed"] ?? 0.3) let direction = Float(uniforms["direction"] ?? 0.0) // Resolve image path let imagePath: String if sourceUri.hasPrefix("file://") { imagePath = String(sourceUri.dropFirst(7)) } else { imagePath = sourceUri } guard let image = UIImage(contentsOfFile: imagePath) else { promise.reject("IMAGE_NOT_FOUND", "Could not load image at \(imagePath)") return } let screenSize = UIScreen.main.bounds.size let scale = UIScreen.main.scale let targetSize = CGSize(width: screenSize.width * scale, height: screenSize.height * scale) Task { do { let exporter = LivePhotoExporter() let assetId = try await exporter.export( image: image, targetSize: targetSize, shaderId: shaderId, intensity: intensity, speed: speed, direction: direction ) promise.resolve(assetId) } catch { promise.reject("EXPORT_FAILED", error.localizedDescription) } } } /// Check if the photo library is accessible. AsyncFunction("checkPermission") { () -> String in let status = PHPhotoLibrary.authorizationStatus(for: .readWrite) switch status { case .authorized, .limited: return "granted" case .denied, .restricted: return "denied" case .notDetermined: return "undetermined" @unknown default: return "unknown" } } /// Request photo library write permission. AsyncFunction("requestPermission") { (promise: Promise) in PHPhotoLibrary.requestAuthorization(for: .readWrite) { status in switch status { case .authorized, .limited: promise.resolve("granted") case .denied, .restricted: promise.resolve("denied") default: promise.resolve("denied") } } } } }