I came through the following link which demonstrated how to copy file from Gallery to application or other directory (in right answer):
How to copy an image file from iOS Photo Library (ALAssetsLibrary) to the local directory of an App?
But with ALAssetsLibrary class documentation Apple said its now deprecated as of iOS 9.0 instead use Photos framework.
The Assets Library framework is deprecated as of iOS 9.0. Instead, use
the Photos framework instead, which in iOS 8.0 and later provides more
features and better performance for working with a user’s photo
library. For more information, see Photos Framework Reference.
How I can use Photos framework to copy assets from Gallery to other URL?
This seems fairly easy. I added an example code for someone whom it may help:
var item: PHAsset! // you update with actual PHAsset at runtime
let docuPath = NSSearchPathForDirectoriesInDomains(NSSearchPathDirectory.ApplicationDirectory, NSSearchPathDomainMask.UserDomainMask, true) as NSArray
let targetImgeURL = (docuPath[0] as! String) + "/IMG_0005.JPG"
let phManager = PHImageManager.defaultManager()
let options = PHImageRequestOptions()
options.synchronous = true; // do it if you want things running in background thread
phManager.requestImageDataForAsset(item, options: options)
{ imageData,dataUTI,orientation,info in
if let newData:NSData = imageData
{
try! newData.writeToFile(targetImgeURL, atomically: true)
}
}
Here is the Objective C solution.
-(NSURL*)createVideoCopyFromReferenceUrl:(NSURL*)inputUrlFromVideoPicker{
NSURL __block *videoURL;
PHFetchResult *phAssetFetchResult = [PHAsset fetchAssetsWithALAssetURLs:#[inputUrlFromVideoPicker ] options:nil];
PHAsset *phAsset = [phAssetFetchResult firstObject];
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
[[PHImageManager defaultManager] requestAVAssetForVideo:phAsset options:nil resultHandler:^(AVAsset *asset, AVAudioMix *audioMix, NSDictionary *info) {
if ([asset isKindOfClass:[AVURLAsset class]]) {
NSURL *url = [(AVURLAsset *)asset URL];
NSLog(#"Final URL %#",url);
NSData *videoData = [NSData dataWithContentsOfURL:url];
// optionally, write the video to the temp directory
NSString *videoPath = [NSTemporaryDirectory() stringByAppendingPathComponent:[NSString stringWithFormat:#"%f.mp4",[NSDate timeIntervalSinceReferenceDate]]];
videoURL = [NSURL fileURLWithPath:videoPath];
BOOL writeResult = [videoData writeToURL:videoURL atomically:true];
if(writeResult) {
NSLog(#"video success");
}
else {
NSLog(#"video failure");
}
dispatch_group_leave(group);
// use URL to get file content
}
}];
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
return videoURL;
}
In Swift use below set of code
import Photos
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
self.dismissViewControllerAnimated(true, completion: nil)
if let referenceURL = info[UIImagePickerControllerReferenceURL] as? NSURL {
let fetchResult = PHAsset.fetchAssetsWithALAssetURLs([referenceURL], options: nil)
if let phAsset = fetchResult.firstObject as? PHAsset {
PHImageManager.defaultManager().requestAVAssetForVideo(phAsset, options: PHVideoRequestOptions(), resultHandler: { (asset, audioMix, info) -> Void in
if let asset = asset as? AVURLAsset {
let videoData = NSData(contentsOfURL: asset.URL)
// optionally, write the video to the temp directory
let videoPath = NSTemporaryDirectory() + "tmpMovie.MOV"
let videoURL = NSURL(fileURLWithPath: videoPath)
let writeResult = videoData?.writeToURL(videoURL, atomically: true)
if let writeResult = writeResult where writeResult {
print("success")
}
else {
print("failure")
}
}
})
}
}
}
Related
My goal (besides learning how to write an iOS app extension) is to allow a user to share an image using the share button from a variety of apps including Photos and automatically rename them. Lastly then I want to save the image to the "documents" folder of the app for further use.
I'm having some problems trying to get the actual didSelectPost portion working since it seems that, unlike Objective-C examples I've seen, the loadItem operation returns a NSURL instead of an UIImage. When attempting to copy the NSUrl to my apps documents folder I get an error:
Error Domain=NSCocoaErrorDomain Code=260 "The file “IMG_0941.JPG”
couldn’t be opened because there is no such file."
UserInfo={NSFilePath=file:///var/mobile/Media/PhotoData/OutgoingTemp/B79263E5-9512-4317-9C5D-817D7EBEFA9A/RenderedPhoto/IMG_0941.JPG,
NSUnderlyingError=0x283f89080 {Error Domain=NSPOSIXErrorDomain Code=2
"No such file or directory"}}
This happens when I push the share button on a photo in the "photos" app, tap my extension and then press the "post" button.
I get the same error regardless if it's running in a simulator or real device.
Here's my hacked together progress so far:
override func didSelectPost() {
// This is called after the user selects Post. Do the upload of contentText and/or NSExtensionContext attachments.
let inputItem = extensionContext?.inputItems.first as! NSExtensionItem
let attachment = inputItem.attachments!.first!
if attachment.hasItemConformingToTypeIdentifier(kUTTypeJPEG as String) {
attachment.loadItem(forTypeIdentifier: kUTTypeJPEG as String, options: nil) { data, error in
var image: UIImage?
if let someUrl = data as? NSURL {
do {
// a ends up being nil in both of these cases
let a = NSData(contentsOfFile: someUrl.absoluteString!)
image = UIImage(data: a as! Data)
// let a = try Data(contentsOf: someUrl)
// image = UIImage(contentsOfFile: someUrl.absoluteString)
} catch {
print(error)
}
} else if let someImage = data as? UIImage {
image = someImage
}
if let someImage = image {
guard let compressedImagePath = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first?.appendingPathComponent("theimage.jpg", isDirectory: false) else {
return
}
let compressedImageData = someImage.jpegData(compressionQuality: 1)
guard (try? compressedImageData?.write(to: compressedImagePath)) != nil else {
return
}
} else {
print("Bad share data")
}
}
}
// Inform the host that we're done, so it un-blocks its UI. Note: Alternatively you could call super's -didSelectPost, which will similarly complete the extension context.
self.extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
}
Notice I'm casting the img variable as an NSURL. I've tried to cast it as a UIImage but that throws an exception.
I have some other things I'd like to do to the image, like read it's EXIF data but for now this is what I have. Any suggestions would be great as I'm really struggling to wrap my head around and learn this environment.
Similar but unsuccessful posts I've tried, notice they are all Objective-C:
iOS Share Extension issue when sharing images from Photo library
Share image using share extension in ios8
How to add my app to the share sheet action
[edit] Matched the layout of one of the better answers, still with no luck.
I have review your code and there is some mistake in the code. I have fixed it .
Replace your code with it
func share() {
let inputItem = extensionContext!.inputItems.first! as! NSExtensionItem
let attachment = inputItem.attachments!.first as! NSItemProvider
if attachment.hasItemConformingToTypeIdentifier( kUTTypeImage as String) {
attachment.loadItem(forTypeIdentifier: kUTTypeImage as String, options: [:]) { (data, error) in
var image: UIImage?
if let someURl = data as? URL {
image = UIImage(contentsOfFile: someURl.path)
}else if let someImage = data as? UIImage {
image = someImage
}
if let someImage = image {
guard let compressedImagePath = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first?.appendingPathComponent("shareImage.jpg", isDirectory: false) else {
return
}
let compressedImageData = UIImageJPEGRepresentation(someImage, 1)
guard (try? compressedImageData?.write(to: compressedImagePath)) != nil else {
return
}
}else{
print("bad share data")
}
}
}
}
I have the same issue. The solution I was able to implement:
Get URL to image. This URL is useless because I got 260 error when try to load image using this URL. Interesting that this comes after some recent updates because it works before
Get file name with extension from this URL
Iterate over all images in user's photo library and find the image name == name from ULR
Extract the image data
- (void)didSelectPost {
for (NSItemProvider* itemProvider in ((NSExtensionItem*)self.extensionContext.inputItems[0]).attachments ) {
// get type of file extention (jpeg, file, url, png ...)
NSArray *registeredTypeIdentifiers = itemProvider.registeredTypeIdentifiers;
if ([itemProvider hasItemConformingToTypeIdentifier:registeredTypeIdentifiers.firstObject]) {
[itemProvider loadItemForTypeIdentifier:registeredTypeIdentifiers.firstObject options:nil completionHandler:^(id<NSSecureCoding> item, NSError *error) {
NSData *imgData;
NSString* imgPath = ((NSURL*) item).absoluteString;
if(imgPath == nil)
imgPath = [NSString stringWithFormat:#"%#", item];
NSCharacterSet* set = [NSCharacterSet URLHostAllowedCharacterSet];
NSString* imgPathEscaped = [imgPath stringByAddingPercentEncodingWithAllowedCharacters:set];
NSString* fileName = [imgPath lastPathComponent];
NSError* error2 = nil;
//try load from file path
__block NSData* data2 = [NSData dataWithContentsOfFile:imgPath options: NSDataReadingUncached error:&error2];
if(data2 == nil) //try load as URL
data2 = [NSData dataWithContentsOfURL:[NSURL URLWithString:imgPath] options: NSDataReadingUncached error:&error2];
if(data2 == nil) //all failed so try hacky way
{
NSString* searchFilename = [fileName lowercaseString];
PHFetchResult *results = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:nil];
[results enumerateObjectsUsingBlock:^(PHAsset *obj, NSUInteger idx, BOOL * _Nonnull stop) {
NSArray* resources = [PHAssetResource assetResourcesForAsset:obj];
NSString* fileName2 = [NSString stringWithFormat:#"%#", ((PHAssetResource*)resources[0]).originalFilename].lowercaseString;
if ([fileName2 isEqual:searchFilename])
{
NSLog(#"found %#", fileName2);
PHImageManager* mgr = [PHImageManager defaultManager];
PHImageRequestOptions * options = [PHImageRequestOptions alloc];
options.synchronous = YES;
[mgr requestImageDataForAsset:obj options:options resultHandler:^(NSData * _Nullable imageData33, NSString * _Nullable dataUTI, UIImageOrientation orientation, NSDictionary * _Nullable info)
{
//imageData33 is your image
data2 = imageData33;
}];
}
}];
}
}];
}
}
// Inform the host that we're done, so it un-blocks its UI. Note: Alternatively you could call super's -didSelectPost, which will similarly complete the extension context.
[self.extensionContext completeRequestReturningItems:#[] completionHandler:nil];
}
func getPhotofolder() -> String{
let fileManager = FileManager.default
let paths = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString).appendingPathComponent("hsafetyPhoto")
if !fileManager.fileExists(atPath: paths){
try! fileManager.createDirectory(atPath: paths, withIntermediateDirectories: true, attributes: nil)
}else{
print("Already dictionary created.")
}
return paths
}
func saveImageDocumentDirectory(photo : UIImage, photoUrl : String) -> Bool{
let fileManager = FileManager.default
let paths = Utility.getPhotofolder().stringByAppendingPathComponent(pathComponent: photoUrl)
print("image's path \(paths)")
if !fileManager.fileExists(atPath: paths){
print("file already exits \(paths)")
let imageData = UIImageJPEGRepresentation(photo, 0.5)
fileManager.createFile(atPath: paths as String, contents: imageData, attributes: nil)
if !fileManager.fileExists(atPath: paths){
return false
}else{
return true
}
}else{
print(paths)
let imageData = UIImageJPEGRepresentation(photo, 0.5)
fileManager.createFile(atPath: paths as String, contents: imageData, attributes: nil)
if !fileManager.fileExists(atPath: paths){
return false
}else{
return true
}
}
}
func showimage(image_name : String) {
let documentsUrl = URL(fileURLWithPath: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0])
let imgUrl = documentsUrl.appendingPathComponent(image_name)
if(FileManager.default.fileExists(atPath:imgUrl.path))
{
do {
let data = try Data(contentsOf:imgUrl)
self.imageView.image = UIImage(data:data)
}catch {
print(error)
} } else{
self.imageView.image = UIImage(named:"default.jpg") //Display any default image
}
}
Im trying to get the image name using PHAssets. But I couldn't find metadata for filename or any method to get the image name. Is there a different way to get the file name?
I know the question has already been answered, but I figured I would provide another option:
extension PHAsset {
var originalFilename: String? {
var fileName: String?
if #available(iOS 9.0, *) {
let resources = PHAssetResource.assetResources(for: self)
if let resource = resources.first {
fileName = resource.originalFilename
}
}
if fileName == nil {
/// This is an undocumented workaround that works as of iOS 9.1
fileName = self.value(forKey: "filename") as? String
}
return fileName
}
}
If you want to get the image name (for example name of last photo in Photos) like IMG_XXX.JPG, you can try this:
PHAsset *asset = nil;
PHFetchOptions *fetchOptions = [[PHFetchOptions alloc] init];
fetchOptions.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:#"creationDate" ascending:YES]];
PHFetchResult *fetchResult = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:fetchOptions];
if (fetchResult != nil && fetchResult.count > 0) {
// get last photo from Photos
asset = [fetchResult lastObject];
}
if (asset) {
// get photo info from this asset
PHImageRequestOptions * imageRequestOptions = [[PHImageRequestOptions alloc] init];
imageRequestOptions.synchronous = YES;
[[PHImageManager defaultManager]
requestImageDataForAsset:asset
options:imageRequestOptions
resultHandler:^(NSData *imageData, NSString *dataUTI,
UIImageOrientation orientation,
NSDictionary *info)
{
NSLog(#"info = %#", info);
if ([info objectForKey:#"PHImageFileURLKey"]) {
// path looks like this -
// file:///var/mobile/Media/DCIM/###APPLE/IMG_####.JPG
NSURL *path = [info objectForKey:#"PHImageFileURLKey"];
}
}];
}
Hope it helps.
In Swift the code will look like this
PHImageManager.defaultManager().requestImageDataForAsset(asset, options: PHImageRequestOptions(), resultHandler:
{
(imagedata, dataUTI, orientation, info) in
if info!.keys.contains(NSString(string: "PHImageFileURLKey"))
{
let path = info![NSString(string: "PHImageFileURLKey")] as! NSURL
}
})
Swift 4:
let fetchResult = PHAsset.fetchAssets(with: .image, options: nil)
if fetchResult.count > 0 {
if let asset = fetchResult.firstObject {
let date = asset.creationDate ?? Date()
print("Creation date: \(date)")
PHImageManager.default().requestImageData(for: asset, options: PHImageRequestOptions(),
resultHandler: { (imagedata, dataUTI, orientation, info) in
if let info = info {
if info.keys.contains(NSString(string: "PHImageFileURLKey")) {
if let path = info[NSString(string: "PHImageFileURLKey")] as? NSURL {
print(path)
}
}
}
})
}
}
One more option is:
[asset valueForKey:#"filename"]
The "legality" of this is up to you to decide.
Easiest solution for iOS 9+ in Swift 4 (based on skims answer):
extension PHAsset {
var originalFilename: String? {
return PHAssetResource.assetResources(for: self).first?.originalFilename
}
}
For Swift
asset?.value(forKey: "filename") as? String
For objective C
[asset valueForKey:#"filename"]
Simplest answer with Swift when you have reference url to an asset:
if let asset = PHAsset.fetchAssetsWithALAssetURLs([referenceUrl], options: nil).firstObject as? PHAsset {
PHImageManager.defaultManager().requestImageDataForAsset(asset, options: nil, resultHandler: { _, _, _, info in
if let fileName = (info?["PHImageFileURLKey"] as? NSURL)?.lastPathComponent {
//do sth with file name
}
})
}
SWIFT4:
first import Photos
if let asset = PHAsset.fetchAssets(withALAssetURLs: [info[UIImagePickerControllerReferenceURL] as! URL],
options: nil).firstObject {
PHImageManager.default().requestImageData(for: asset, options: nil, resultHandler: { _, _, _, info in
if let fileName = (info?["PHImageFileURLKey"] as? NSURL)?.lastPathComponent {
print("///////" + fileName + "////////")
//do sth with file name
}
})
}
What you really looking for is the localIdentifier which is a unique string that persistently identifies the object.
Use this string to find the object by using the:
fetchAssetsWithLocalIdentifiers:options:, fetchAssetCollectionsWithLocalIdentifiers:options:, or fetchCollectionListsWithLocalIdentifiers:options: method.
More information is available here
I need to use the AVAsset object, in order to play it using AVPlayer and AVPlayerLayer. I started using the Photos framework since AssetsLibrary is deprecated. Now I got to the point where I have an array of PHAsset objects and I need to convert them to AVAsset. I tried enumerating through the PHFetchResult and allocation a new AVAsset using the PHAsset's localized description, but it does not seem to show any video when I play it.
PHAssetCollection *assetColl = [self scaryVideosAlbum];
PHFetchResult *getVideos = [PHAsset fetchAssetsInAssetCollection:assetColl options:nil];
[getVideos enumerateObjectsUsingBlock:^(PHAsset *asset, NSUInteger idx, BOOL *stop) {
NSURL *videoUrl = [NSURL URLWithString:asset.localizedDescription];
AVAsset *avasset = [AVAsset assetWithURL:videoUrl];
[tempArr addObject:avasset];
}];
I assume the localized description is not the absolute url of the video.
I also stumbled upon the PHImageManager and the requestAVAssetForVideo, however, the options parameter when it comes down to video does not have an isSynchrounous property, which is the case with the image options parameter.
PHVideoRequestOptions *option = [PHVideoRequestOptions new];
[[PHImageManager defaultManager] requestAVAssetForVideo:videoAsset options:option resultHandler:^(AVAsset * _Nullable avasset, AVAudioMix * _Nullable audioMix, NSDictionary * _Nullable info) {
Is there a synchronous way to do this?
Thanks.
No, there isn't. But you can build a synchronous version:
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
PHVideoRequestOptions *option = [PHVideoRequestOptions new];
__block AVAsset *resultAsset;
[[PHImageManager defaultManager] requestAVAssetForVideo:videoAsset options:option resultHandler:^(AVAsset * avasset, AVAudioMix * audioMix, NSDictionary * info) {
resultAsset = avasset;
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// yay, we synchronously have the asset
[self doSomethingWithAsset:resultAsset];
However if you do this on the main thread and requestAVAssetForVideo: takes too long, you risk locking up your UI or even being terminated by the iOS watchdog.
It's probably safer to rework your app to work with the asynchronous callback version. Something like this:
__weak __typeof(self) weakSelf = self;
[[PHImageManager defaultManager] requestAVAssetForVideo:videoAsset options:option resultHandler:^(AVAsset * avasset, AVAudioMix * audioMix, NSDictionary * info) {
dispatch_async(dispatch_get_main_queue(), ^{
[weakSelf doSomethingWithAsset:avasset];
});
}];
For Swift 2, you can easily play the video with PHAsset using this method below,
Import File
import AVKit
From PHAsset
static func playVideo (view:UIViewController, asset:PHAsset) {
guard (asset.mediaType == PHAssetMediaType.Video)
else {
print("Not a valid video media type")
return
}
PHCachingImageManager().requestAVAssetForVideo(asset, options: nil, resultHandler: {(asset: AVAsset?, audioMix: AVAudioMix?, info: [NSObject : AnyObject]?) in
let asset = asset as! AVURLAsset
dispatch_async(dispatch_get_main_queue(), {
let player = AVPlayer(URL: asset.URL)
let playerViewController = AVPlayerViewController()
playerViewController.player = player
view.presentViewController(playerViewController, animated: true) {
playerViewController.player!.play()
}
})
})
}
Import
import AVKit
Swift 5
let phAsset = info[UIImagePickerControllerPHAsset] as? PHAsset
PHCachingImageManager().requestAVAsset(forVideo: phAsset, options: nil) { (avAsset, _, _) in
print(avAsset)
}
You can try this trick but it is handy when you have 3,4 or maybe 5 phassets that you want to convert to AVAsset :
[[PHImageManager defaultManager] requestAVAssetForVideo:assetsArray[0] options:option resultHandler:^(AVAsset * avasset, AVAudioMix * audioMix, NSDictionary * info) {
//do something with this asset
[[PHImageManager defaultManager] requestAVAssetForVideo:assetsArray[1] options:option resultHandler:^(AVAsset * avasset, AVAudioMix * audioMix, NSDictionary * info) {
//so on...
}
}
So basically,you can call this method again when you have converted 1 phasset to AVAsset.I know this might not be an efficient code but it should not be forbidden for little purposes.
The following is a Swift 4 implementation that relies on a semaphore to make the request synchronously.
The code is commented to explain the various steps.
func requestAVAsset(asset: PHAsset) -> AVAsset? {
// We only want videos here
guard asset.mediaType == .video else { return nil }
// Create your semaphore and allow only one thread to access it
let semaphore = DispatchSemaphore.init(value: 1)
let imageManager = PHImageManager()
var avAsset: AVAsset?
// Lock the thread with the wait() command
semaphore.wait()
// Now go fetch the AVAsset for the given PHAsset
imageManager.requestAVAsset(forVideo: asset, options: nil) { (asset, _, _) in
// Save your asset to the earlier place holder
avAsset = asset
// We're done, let the semaphore know it can unlock now
semaphore.signal()
}
return avAsset
}
Those who are coming here for asynchronous approach.
Swift version :
func requestAVAsset(asset: PHAsset)-> AVAsset? {
guard asset.mediaType == .video else { return nil }
let phVideoOptions = PHVideoRequestOptions()
phVideoOptions.version = .original
let group = DispatchGroup()
let imageManager = PHImageManager.default()
var avAsset: AVAsset?
group.enter()
imageManager.requestAVAsset(forVideo: asset, options: phVideoOptions) { (asset, _, _) in
avAsset = asset
group.leave()
}
group.wait()
return avAsset
}
I have a question in uploading the video to Facebook using Facebook SDK.I am trying to upload the video which I have selected from SavedPhotos is working fine. But When I am trying to upload the video from my documents directory it is saying this below error. What I know is we can upload the video which are having the asset url. Is there any other way to upload the documents directory video to the facebook???
Error is
2015-05-26 16:30:02.369 graphtwentysixth[3025:1413799] FB: ERROR=Error Domain=com.facebook.sdk.share Code=2 "The operation couldn’t be completed. (com.facebook.sdk.share error 2.)" UserInfo=0x156b0f90 {com.facebook.sdk:FBSDKErrorArgumentValueKey=file:///private/var/mobile/Containers/Bundle/Application/48DA75B3-63BA-400A-AC92-BE6B4A2B954B/graphtwentysixth.app/demo-video-high-quality.mov, com.facebook.sdk:FBSDKErrorArgumentNameKey=videoURL, com.facebook.sdk:FBSDKErrorDeveloperMessageKey=Invalid value for videoURL: file:///private/var/mobile/Containers/Bundle/Application/48DA75B3-63BA-400A-AC92-BE6B4A2B954B/graphtwentysixth.app/demo-video-high-quality.mov}
Code
NSURL *videoURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"demo-video-high-quality" ofType:#"mov"]];
FBSDKShareVideo *video = [[FBSDKShareVideo alloc] init];
video.videoURL = videoURL;
FBSDKShareVideoContent *content1 = [[FBSDKShareVideoContent alloc] init];
content1.video = video;
[FBSDKShareAPI shareWithContent:content1 delegate:self];
Thank you for your valuable time
The video cannot be uploaded from document directory. You can achieve this by making video an asset and than give the url of asset to Facebook and on completion handler call delete that video asset from gallery. This is a trick but not a good solution as when you make video an asset of galley it will visible in savedPhotos.
Like Shoaib said, you'll need to make the video into an asset first. Be sure to include #import <AssetsLibrary/AssetsLibrary.h> in your class.
ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
ALAssetsLibraryWriteVideoCompletionBlock videoWriteCompletionBlock = ^(NSURL *newURL, NSError *error) {
if (error)
{
NSLog( #"Error writing image with metadata to Photo Library: %#", error );
}
else
{
NSLog(#"Wrote image with metadata to Photo Library %#", newURL.absoluteString);
FBSDKShareVideo* video = [FBSDKShareVideo videoWithVideoURL:newURL];
FBSDKShareVideoContent* content = [[FBSDKShareVideoContent alloc] init];
content.video = video;
[FBSDKShareAPI shareWithContent:content delegate:self];
}
};
NSURL *videoURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:#"demo-video-high-quality" ofType:#"mov"]];
if ([library videoAtPathIsCompatibleWithSavedPhotosAlbum:videoURL])
{
[library writeVideoAtPathToSavedPhotosAlbum:videoURL completionBlock:videoWriteCompletionBlock];
}
(EDIT inspired in part by this post)
You should first save the video to your Library, then get the correct PHAsset and generate the correct URL:
guard let schemaUrl = URL(string: "fb://") else {
return //be safe
}
if UIApplication.shared.canOpenURL(schemaUrl) {
PHPhotoLibrary.requestAuthorization({ [weak self]
(newStatus) in
guard let strongSelf = self else {
return
}
if newStatus == PHAuthorizationStatus.authorized {
strongSelf.saveVideoToCameraRoll(url: assetURL, completion: { (result, phAsset) in
phAsset?.getURL(completionHandler: { (url) in
if let url = url {
dispatchAsyncOnMainQueue {
let video = FBSDKShareVideo()
video.videoURL = url
let content = FBSDKShareVideoContent()
content.video = video
dialog.shareContent = content
dialog.show()
}
}
})
})
} else {
//unauthorized
}
})
} else {
//facebookAppNotInstalled
}
...
func saveVideoToCameraRoll(url: URL, completion:#escaping (Bool, PHAsset?) -> ()) {
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: url)
}) { saved, error in
if saved {
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
fetchOptions.fetchLimit = 1
let fetchResult = PHAsset.fetchAssets(with: .video, options: fetchOptions).firstObject
completion(true, fetchResult)
} else {
completion(false, nil)
}
}
}
...
extension PHAsset {
func getURL(completionHandler : #escaping ((_ responseURL : URL?) -> Void)){
if self.mediaType == .video {
let options: PHVideoRequestOptions = PHVideoRequestOptions()
options.version = .original
let nameParts = self.localIdentifier.components(separatedBy: "/")
if nameParts.count > 0 {
let assetFormatString = "assets-library://asset/asset.MP4?id=%#&ext=MP4"
let name = nameParts[0]
let urlString = String(format: assetFormatString, name)
if let url = URL(string: urlString) {
completionHandler(url)
} else {
completionHandler(nil)
}
}
}
}
}
You just try below code may it's help to you.
- (void)upload{
if (FBSession.activeSession.isOpen) {
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"demo-video-high-quality" ofType:#"mov"];
NSURL *pathURL = [[NSURL alloc]initFileURLWithPath:filePath isDirectory:NO];
NSData *videoData = [NSData dataWithContentsOfFile:filePath];
NSDictionary *videoObject = #{
#"title": #"FB SDK 3.1",
#"description": #"hello there !",
[pathURL absoluteString]: videoData
};
FBRequest *uploadRequest = [FBRequest requestWithGraphPath:#"me/videos"
parameters:videoObject
HTTPMethod:#"POST"];
[uploadRequest startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
if (!error)
NSLog(#"Done: %#", result);
else
NSLog(#"Error: %#", error.localizedDescription);
}];
}
}
Im trying to get the image name using PHAssets. But I couldn't find metadata for filename or any method to get the image name. Is there a different way to get the file name?
I know the question has already been answered, but I figured I would provide another option:
extension PHAsset {
var originalFilename: String? {
var fileName: String?
if #available(iOS 9.0, *) {
let resources = PHAssetResource.assetResources(for: self)
if let resource = resources.first {
fileName = resource.originalFilename
}
}
if fileName == nil {
/// This is an undocumented workaround that works as of iOS 9.1
fileName = self.value(forKey: "filename") as? String
}
return fileName
}
}
If you want to get the image name (for example name of last photo in Photos) like IMG_XXX.JPG, you can try this:
PHAsset *asset = nil;
PHFetchOptions *fetchOptions = [[PHFetchOptions alloc] init];
fetchOptions.sortDescriptors = #[[NSSortDescriptor sortDescriptorWithKey:#"creationDate" ascending:YES]];
PHFetchResult *fetchResult = [PHAsset fetchAssetsWithMediaType:PHAssetMediaTypeImage options:fetchOptions];
if (fetchResult != nil && fetchResult.count > 0) {
// get last photo from Photos
asset = [fetchResult lastObject];
}
if (asset) {
// get photo info from this asset
PHImageRequestOptions * imageRequestOptions = [[PHImageRequestOptions alloc] init];
imageRequestOptions.synchronous = YES;
[[PHImageManager defaultManager]
requestImageDataForAsset:asset
options:imageRequestOptions
resultHandler:^(NSData *imageData, NSString *dataUTI,
UIImageOrientation orientation,
NSDictionary *info)
{
NSLog(#"info = %#", info);
if ([info objectForKey:#"PHImageFileURLKey"]) {
// path looks like this -
// file:///var/mobile/Media/DCIM/###APPLE/IMG_####.JPG
NSURL *path = [info objectForKey:#"PHImageFileURLKey"];
}
}];
}
Hope it helps.
In Swift the code will look like this
PHImageManager.defaultManager().requestImageDataForAsset(asset, options: PHImageRequestOptions(), resultHandler:
{
(imagedata, dataUTI, orientation, info) in
if info!.keys.contains(NSString(string: "PHImageFileURLKey"))
{
let path = info![NSString(string: "PHImageFileURLKey")] as! NSURL
}
})
Swift 4:
let fetchResult = PHAsset.fetchAssets(with: .image, options: nil)
if fetchResult.count > 0 {
if let asset = fetchResult.firstObject {
let date = asset.creationDate ?? Date()
print("Creation date: \(date)")
PHImageManager.default().requestImageData(for: asset, options: PHImageRequestOptions(),
resultHandler: { (imagedata, dataUTI, orientation, info) in
if let info = info {
if info.keys.contains(NSString(string: "PHImageFileURLKey")) {
if let path = info[NSString(string: "PHImageFileURLKey")] as? NSURL {
print(path)
}
}
}
})
}
}
One more option is:
[asset valueForKey:#"filename"]
The "legality" of this is up to you to decide.
Easiest solution for iOS 9+ in Swift 4 (based on skims answer):
extension PHAsset {
var originalFilename: String? {
return PHAssetResource.assetResources(for: self).first?.originalFilename
}
}
For Swift
asset?.value(forKey: "filename") as? String
For objective C
[asset valueForKey:#"filename"]
Simplest answer with Swift when you have reference url to an asset:
if let asset = PHAsset.fetchAssetsWithALAssetURLs([referenceUrl], options: nil).firstObject as? PHAsset {
PHImageManager.defaultManager().requestImageDataForAsset(asset, options: nil, resultHandler: { _, _, _, info in
if let fileName = (info?["PHImageFileURLKey"] as? NSURL)?.lastPathComponent {
//do sth with file name
}
})
}
SWIFT4:
first import Photos
if let asset = PHAsset.fetchAssets(withALAssetURLs: [info[UIImagePickerControllerReferenceURL] as! URL],
options: nil).firstObject {
PHImageManager.default().requestImageData(for: asset, options: nil, resultHandler: { _, _, _, info in
if let fileName = (info?["PHImageFileURLKey"] as? NSURL)?.lastPathComponent {
print("///////" + fileName + "////////")
//do sth with file name
}
})
}
What you really looking for is the localIdentifier which is a unique string that persistently identifies the object.
Use this string to find the object by using the:
fetchAssetsWithLocalIdentifiers:options:, fetchAssetCollectionsWithLocalIdentifiers:options:, or fetchCollectionListsWithLocalIdentifiers:options: method.
More information is available here