From a42065f2df9594157bd37c6778cb66dcc8b0bb62 Mon Sep 17 00:00:00 2001 From: mtz Date: Fri, 15 Mar 2024 15:30:23 -0300 Subject: [PATCH] implement share video on instagram with support to limited photos picker --- ios/Classes/SwiftFlutterShareMePlugin.swift | 184 ++++++++++++++++---- 1 file changed, 150 insertions(+), 34 deletions(-) diff --git a/ios/Classes/SwiftFlutterShareMePlugin.swift b/ios/Classes/SwiftFlutterShareMePlugin.swift index e748eda..5c5066d 100644 --- a/ios/Classes/SwiftFlutterShareMePlugin.swift +++ b/ios/Classes/SwiftFlutterShareMePlugin.swift @@ -16,6 +16,7 @@ public class SwiftFlutterShareMePlugin: NSObject, FlutterPlugin, SharingDelegate var result: FlutterResult? var documentInteractionController: UIDocumentInteractionController? + var onPickFileCompletionHandler: ((String?) -> Void)? = nil public static func register(with registrar: FlutterPluginRegistrar) { @@ -68,7 +69,7 @@ public class SwiftFlutterShareMePlugin: NSObject, FlutterPlugin, SharingDelegate } else if(call.method.elementsEqual(_methodInstagram)){ let args = call.arguments as? Dictionary - shareInstagram(args: args!) + shareInstagram(args: args!, result: result) } else if(call.method.elementsEqual(_methodTelegramShare)){ let args = call.arguments as? Dictionary @@ -272,48 +273,52 @@ public class SwiftFlutterShareMePlugin: NSObject, FlutterPlugin, SharingDelegate // share image via instagram stories. // @ args image url - func shareInstagram(args:Dictionary) { - let imageUrl=args["url"] as! String - - let image = UIImage(named: imageUrl) - if(image==nil){ - self.result!("File format not supported Please check the file.") - return; - } + func shareInstagram(args: Dictionary, result: @escaping FlutterResult) { + let file = args["url"] as! String + let fileType = args["fileType"] as! String + guard let instagramURL = NSURL(string: "instagram://app") else { - if let result = result { - self.result?("Instagram app is not installed on your device") - result(false) - } + result("Instagram app is not installed on your device") return } - do{ - try PHPhotoLibrary.shared().performChangesAndWait { - let request = PHAssetChangeRequest.creationRequestForAsset(from: image!) - let assetId = request.placeholderForCreatedAsset?.localIdentifier - let instShareUrl:String? = "instagram://library?LocalIdentifier=" + assetId! - - //Share image + self.requestAddFileToPhotoLibraryPermission { newStatus in + print("The new status is \(newStatus.rawValue)") + + if #available(iOS 14, *) { + if ![PHAuthorizationStatus.authorized, PHAuthorizationStatus.limited].contains(newStatus) { + result("permission not granted") + return + } + } else { + if newStatus != .authorized { + result("permission not granted") + return + } + } + + self.addFileToPhotoLibrary(to: file, fileType: fileType) { destinationIdentifier in + guard let destinationIdentifier else { + result("failed to save file to library") + return + } + + let instaShareUrl = "instagram://library?LocalIdentifier=\(destinationIdentifier)" + + // Share file if UIApplication.shared.canOpenURL(instagramURL as URL) { - if let sharingUrl = instShareUrl { - if let urlForRedirect = NSURL(string: sharingUrl) { - if #available(iOS 10.0, *) { - UIApplication.shared.open(urlForRedirect as URL, options: [:], completionHandler: nil) - } - else{ - UIApplication.shared.openURL(urlForRedirect as URL) - } + if let urlForRedirect = NSURL(string: instaShareUrl) { + if #available(iOS 10.0, *) { + UIApplication.shared.open(urlForRedirect as URL, options: [:], completionHandler: nil) + } else{ + UIApplication.shared.openURL(urlForRedirect as URL) } - self.result?("Success") } - } else{ - self.result?("Instagram app is not installed on your device") + result("Success") + } else { + result("Instagram app is not installed on your device") } } - - } catch { - print("Fail") } } @@ -331,4 +336,115 @@ public class SwiftFlutterShareMePlugin: NSObject, FlutterPlugin, SharingDelegate public func sharerDidCancel(_ sharer: Sharing) { print("Share: Cancel") } + + private func requestAddFileToPhotoLibraryPermission(_ completionHandler: @escaping (PHAuthorizationStatus) -> Void ) { + if #available(iOS 14.0, *) { + let status = PHPhotoLibrary.authorizationStatus(for: .readWrite) + if [PHAuthorizationStatus.authorized, PHAuthorizationStatus.limited].contains(status) { + completionHandler(status) + return + } + + PHPhotoLibrary.requestAuthorization(for: .readWrite) { newStatus in + completionHandler(newStatus) + } + } else { + PHPhotoLibrary.requestAuthorization { newStatus in + completionHandler(newStatus) + } + } + } + + private func addFileToPhotoLibrary(to filePath: String, + fileType: String, + completionHandler: @escaping (String?) -> Void) { + guard let fileData = try? Data(contentsOf: URL(fileURLWithPath: filePath)) as NSData else { + print("error getting file data") + completionHandler(nil) + return + } + + PHPhotoLibrary.shared().performChanges { + if fileType == "image" { + PHAssetChangeRequest.creationRequestForAssetFromImage( + atFileURL: URL(fileURLWithPath: filePath) + ) + } else { + PHAssetChangeRequest.creationRequestForAssetFromVideo( + atFileURL: URL(fileURLWithPath: filePath) + ) + } + } completionHandler: { success, error in + if let error { + print("error saving file to PHPhotoLibrary \(error.localizedDescription)") + } + + if success { + if #available(iOS 14, *), + PHPhotoLibrary.authorizationStatus(for: .readWrite) == PHAuthorizationStatus.limited { + self.displayPHPhotoPicker { fileIdentifier in + completionHandler(fileIdentifier) + } + return + } + + let fetchOptions = PHFetchOptions() + fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", + ascending: false)] + let fetchResult = PHAsset.fetchAssets( + with: fileType == "image" ? .image : .video, + options: fetchOptions + ) + + if let assetToShare = fetchResult.firstObject { + completionHandler(assetToShare.localIdentifier) + return + } else { + print("error querying file asset") + } + } + + completionHandler(nil) + } + } +} + +// - MARK: PHPickerViewControllerDelegate +@available(iOS 14, *) +extension SwiftFlutterShareMePlugin: PHPickerViewControllerDelegate { + private func displayPHPhotoPicker(completionHandler: ((String?) -> Void)?) { + self.onPickFileCompletionHandler = completionHandler + DispatchQueue.main.async { + self.triggerPicker() + } + } + + @IBAction private func triggerPicker() { + let photoLibrary = PHPhotoLibrary.shared() + var configuration = PHPickerConfiguration(photoLibrary: photoLibrary) + configuration.selectionLimit = 1 + configuration.preferredAssetRepresentationMode = .compatible + + let picker = PHPickerViewController(configuration: configuration) + picker.delegate = self + + UIApplication.shared.keyWindow?.rootViewController?.present(picker, animated: true) + } + + public func picker(_ picker: PHPickerViewController, + didFinishPicking results: [PHPickerResult]) { + picker.dismiss(animated: true, completion: nil) + let identifiers = results.compactMap(\.assetIdentifier) + let fetchOptions = PHFetchOptions() + fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", + ascending: false)] + let fetchResult = PHAsset.fetchAssets(withLocalIdentifiers: identifiers, options: fetchOptions) + guard let localId = fetchResult.firstObject else { + print("no asset selected") + self.onPickFileCompletionHandler?(nil) + return + } + + self.onPickFileCompletionHandler?(localId.localIdentifier) + } }