Sharing Video PHAsset via UIActivityController - ios

I am trying to share video PHAsset via UIActivityController using requestAVAsset. This works with Messaging, but not with AirDrop, indicating as 'Failed'.
PHCachingImageManager.default().requestAVAsset(forVideo: asset, options: nil, resultHandler:
{ (givenAsset, audioMix, info) in
let videoAsset = givenAsset as! AVURLAsset
let videoURL = videoAsset.url
DispatchQueue.main.async {
let activityViewController = UIActivityViewController(
activityItems: [videoURL],
applicationActivities: nil)
activityViewController.excludedActivityTypes = [UIActivityType.saveToCameraRoll]
if let popoverPresentationController = activityViewController.popoverPresentationController {
popoverPresentationController.barButtonItem = (sender)
}
self.present(activityViewController, animated: true, completion: nil)
}
})
This seems to properly put up UIActivityController and only work with certain activities:
Messaging - ✔️Works, properly exports video.
AirDrop - ✖️Shows "Failed"
Dropbox - ✖️Puts up the proper Dropbox View, yet says "Unknown error occurred"

I've run into similarly odd behavior when working with PHAssets. My guess is this is a (purposely) undocumented security/sandboxing restriction.
I was able to work around this problem by copying the underlying file to a user directory, and then performing the operation on the copied file.
I did this in a loop. Occasionally, the copy fails with a vague file permissions error. When it does, I retry it after a few seconds (using DispatchQueue.main.asyncAfter). Eventually, it works!

Related

How can I find custom UTIs App support in iOS?

I'm developing a custom UTI like "com.xyz".
All the apps supporting this UTI may be shown in the UIActivityViewController.
Code snippet:
let extensionItem = NSExtensionItem()
let data = NSItemProvider.init(item: nil, typeIdentifier: "com.xyz")
extensionItem.attachments = [data]
let activity = UIActivityViewController(
activityItems: [extensionItem],
applicationActivities: nil
)
present(activity, animated: true, completion: nil)
In order to have a better user experience, I would like to check if the iOS device has any App supporting "com.xyz" before presenting the UIActivityViewController.
Just like check URL:
UIApplication.shared.canOpenURL(URL(string: urlStr)!)
Otherwise the user may see an empty UIActivityViewController without any app.
As I know, there is an ActivityNotFoundException could be try and catch to handle the app not found situation in Android.
Does iOS support something like ActivityNotFoundException?
THANKS! 🙏

Why Share Extension is not working on iOS 13?

I was using a Share extension in my app in order to import audio files and it was working on iOS12. Now in iOS 13 is not working anymore, when I press the share button my app doesn't appear in the share sheet.
I think that maybe something has changed in the plist or similar but I coudn't find any information.
Does anyone have the same problem?
NB: I don't wanna use the copy - paste strategies, only the share extension.
Try this for iOS 13
DispatchQueue.main.async {
let activityItem = URL.init(fileURLWithPath: Bundle.main.path(forResource: "audio", ofType: "mp3")!)
let activityVC = UIActivityViewController(activityItems: [activityItem],applicationActivities: nil)
activityVC.popoverPresentationController?.sourceView = self.view
self.present(activityVC, animated: true, completion: nil)
}
For IOS 13 you Should add below code inside your open url method.
UISceneOpenExternalURLOptions * options = [[UISceneOpenExternalURLOptions alloc] init];
options.universalLinksOnly = false;

Extraction of video URL for sharing via UIActivityViewController

I'm working on share videos using UIActivityViewController and have some questions about the URL extraction from PHAsset objects.
I use "requestAVAsset" in "PHImageManager" and cast the AVAsset object to AVURLAsset to access its url property. I've tried the following types of activities:
Copy to Drive - this opens Google Drive app (Success)
Google Drive - a dialog shows up for confirmation (Fail) (no video attached in the dialog)
Gmail - (Fail) (mail can be sent but no video attached)
Add to Notes - this add video to built-in Notes app, a dialog should show up for confirmation (Fail) (app freezes after UIActivityViewController disappears and no dialog shows up)
Facebook/LINE - (Fail) (the progress bar never moves)
My questions:
Does the URL extracted by this method has the access to the real resource file of video?
If yes, am I missing something? Are there bugs in my code (see below)?
Code to share contents (inside an UIViewController):
PHImageManager.default().requestAVAsset(forVideo: videoAsset, options: nil, resultHandler: {
(asset: AVAsset?, audioMix: AVAudioMix?, info: [AnyHashable: Any]?) in
if let urlAsset = asset as? AVURLAsset {
print("Share url=\(urlAsset.url.absoluteURL)")
let shareVC: UIActivityViewController = UIActivityViewController(activityItems: [urlAsset.url.absoluteURL], applicationActivities: nil)
shareVC.completionWithItemsHandler = {
(type: UIActivityType?, completed: Bool, returnedItems: [Any]?, err: Error?) in
print("Share result: completed=\(completed), \(type)")
if err != nil {
print("\(err.debugDescription)")
}
}
DispatchQueue.main.async {
self.present(shareVC, animated: true, completion: nil)
}
}
})
Environment: iPhone 7 plus, iOS 10.1.1
Btw, I also tried another 2 methods for sharing.
Using "writeData" in "PHAssetResourceManager" to output video to a temporary directory and then build URL by the file path.
Using "requestExportSession" in "PHImageManager" to output video to a temporary directory and then build URL by the file path.
These method work fine. In my opinion, this is because the video file can be accessed directly by the extracted url. But they are not suitable for me since I would like to share not only single file but also multiple files within one action. (They take time to process data)

UIActivityViewController - Remove all Activities starting with com.apple.UIKit.activity.Open.Copy rawValue

I'm trying to remove all activity types start with com.apple.UIKit.activity.Open.Copy from UIActivityViewController.
I tried following but didn't get success, anybody does this?
func showShareActivity(videoURL : NSURL, onComplete:#escaping (Bool, UIActivityType?, Error?)->()) {
let activityVC = UIActivityViewController(activityItems: [videoURL], applicationActivities: nil)
activityVC.excludedActivityTypes = [UIActivityType(rawValue: "com.apple.UIKit.activity.Open.Copy.*")]
activityVC.completionWithItemsHandler = { (activity, success, items, error) in
onComplete(success, activity, erro
}
appDelegate?.window.rootViewController?.present(activityVC, animated: true, completion: nil)
}
Note: When I try to exclude one by one then it works but I want to remove all together (Some of them I may don't know). (Swift/ObjC any solution works.)
com.apple.UIKit.activity.Open.Copy.com.apple.iMovie
com.apple.UIKit.activity.Open.Copy.com.apple.itunesu
com.apple.UIKit.activity.Open.Copy.net.whatsapp.WhatsApp
Basically those are all that starts with Import with or Copy to text, please refer below image.
I think Evernote app does the same.

Unknown activity items supplied for ActivityViewController

I am trying to share videos from my app using a UIActivityViewController. Below is the code I use:
var url = NSURL(string: path!)!
var activityViewController = UIActivityViewController(activityItems: [url], applicationActivities: nil)
self.presentViewController(activityViewController, animated: true, completion: { () -> Void in })
The path points to a valid .mov file so no problems there. When I present the activity view controller I get the error: Unknown activity items supplied with the path to the .mov file and the sharing options only show AirDrop.
The app is running on iOS 8.
Any ideas?
EDIT I found out that when I save the video to camera roll. the user can share it with Photos app to any app. So there's nothing wrong with the video format I guess.
You should use init?(fileURLWithPath path: String, isDirectory isDir: Bool) if the movie is in resource bundle.
If you use imagePicker controller to select the video, you can get the url from info dictionary using the key UIImagePickerControllerMediaURL, the corresponding delegate method is didFinishPickingMediaWithInfo.
Try this :
var activityViewController = UIActivityViewController(activityItems: [path], applicationActivities: nil)
self.presentViewController(activityViewController, animated: true, completion: { () -> Void in })
I had a similar issue for a zip file and realised the url path I was passing was relative and not an absolute path. In other words, UIActivityViewController urls require the prefix file:///
I was using a relative path because the third party library Objective-Zip needs a relative path to create zip files.
Works:
file:///private/var/mobile/Containers/Data/Application/63284C22-6E22-4865-965C-3B67F58D0659/tmp/myfile.zip
Doesn't work:
/private/var/mobile/Containers/Data/Application/63284C22-6E22-4865-965C-3B67F58D0659/tmp/myfile.zip

Resources