Play a Video in Collection View failed - swift

I am working on an app where I am getting videos from a firebase link.
To be able to display and play the video I am using this extension:
func generateThumbnailForVideoAtURL(filePathLocal: NSString) -> UIImage? {
let vidURL = NSURL(fileURLWithPath:filePathLocal as String)
let asset = AVURLAsset(url: vidURL as URL)
let generator = AVAssetImageGenerator(asset: asset)
generator.appliesPreferredTrackTransform = true
let timestamp = CMTime(seconds: 1, preferredTimescale: 60)
do {
let imageRef = try generator.copyCGImage(at: timestamp, actualTime: nil)
return UIImage(cgImage: imageRef)
} catch let error as NSError {
print("Image generation failed with error \(error)")
return nil
}
}
And then using it like this in cellAtIndexPath:
if posts[indexPath.row].videoUrl != nil {
cell.videoView.generateThumbnailForVideoAtURL(filePathLocal: self.posts[indexPath.row].videoUrl as! NSString)
print("VIDEO IS: \(posts[indexPath.row].videoUrl)")
}
I am getting this error:
Image generation failed with error Error Domain=NSURLErrorDomain Code=-1102 "You do not have permission to access the requested resource." UserInfo={NSLocalizedDescription=You do not have permission to access the requested resource., NSUnderlyingError=0x1c4046ff0 {Error Domain=NSOSStatusErrorDomain Code=-12660 "(null)"}}
It seems like a UserInfo description, but I can not find anything in my plist to add for permission. It may just sound stupid for somebody else because this is how I feel now as I can't find a way to make it work. Please help!

You should use this
let vidURL = URL(string:filePathLocal as String)
instead of
let vidURL = NSURL(fileURLWithPath:filePathLocal as String)

Related

Swift: Getting thumbnail from a video URL often fails with error: "The operation could not be completed" [duplicate]

I'm trying to get video thumbnails with the following code:
let asset = AVAsset(URL: url)
let imageGenerator = AVAssetImageGenerator(asset: asset)
imageGenerator.appliesPreferredTrackTransform = true
do {
let cgImage = try imgGenerator.copyCGImageAtTime(CMTimeMake(1, 30), actualTime: nil)
let uiImage = UIImage(CGImage: cgImage)
imageview.image = uiImage
}
catch let error as NSError
{
print("Image generation failed with error \(error)")
}
Sometimes it works and sometime it doesn't showing the following error:
Error Domain=AVFoundationErrorDomain Code=-11800 "The operation could
not be completed" UserInfo={NSLocalizedDescription=The operation could
not be completed, NSUnderlyingError=0x14eab520 {Error
Domain=NSOSStatusErrorDomain Code=-12792 "(null)"},
NSLocalizedFailureReason=An unknown error occurred (-12792)}
I have tried to figure out what is Domain=NSOSStatusErrorDomain Code=-12792 but I don't understand how I can get more details about this error code. How can I convert this error code into a string to get relevant information about what this error means?
I was able to solve this issue by the following approach.
Swift 4.1
func createThumbnailForVideo(atURL videoURL: URL , completion : #escaping (UIImage?)->Void) {
let asset = AVAsset(url: videoURL)
let assetImgGenerate = AVAssetImageGenerator(asset: asset)
assetImgGenerate.appliesPreferredTrackTransform = true
let time = CMTimeMakeWithSeconds(1, preferredTimescale: 60)
let times = [NSValue(time: time)]
assetImgGenerate.generateCGImagesAsynchronously(forTimes: times, completionHandler: { _, image, _, _, _ in
if let image = image {
let uiImage = UIImage(cgImage: image)
completion(uiImage)
} else {
completion(nil)
}
})
}
Hope this will help.

Firebase Storage Download Response Error

I have been able to successfully upload images to firebase storage but have been unable to successfully download the image.
I have attempted to download images in all three of the suggested ways on Firebase Storage Guides:
1) Download to NSData in memory
2) Download to an NSURL representing a file on device
3) Generate an NSURL representing the file online
An example is below of two different attempts:
func loadProfileImage() {
guard let currentUser = Auth.auth().currentUser else { return }
let profilePhotoFile = "ProfileImages/" + currentUser.uid
let reference = Storage.storage().reference(withPath: profilePhotoFile)
#1st Attempt downloading to memory:
reference.getData(maxSize: 1 * 1024 * 1024) { (data, error) in
if let error = error {
print("an error occurred: \(error)")
print("see data response: \(data)")
} else {
self.profileView.image = UIImage(data: data!)
}
}
#2nd Attempt with download url:
reference.downloadURL { (url, error) in
if let error = error {
print(error)
} else {
self.profileView.sd_setImage(with: url, placeholderImage:
#imageLiteral(resourceName: "placeHolderProfileView")) {
(image, error, type, reference2) in
print("reference location of image in the google
bucket: \(reference2)")
print("error retrieving image: \(String(describing:
error))")
print("type: \(type)")
print("image details: \(String(describing: image))")
}
}
}
}
Also tried using alamofire instead of SDWebImage to see if error code was same and it is the same see below:
Error Domain=FIRStorageErrorDomain Code=-13000 "An unknown error occurred, please check the server response." UserInfo={object=ProfileImages/6I2RhzFI3edYNph9J4WsaXXXX, ResponseErrorCode=100, bucket=bXXXX-production.appspot.com, NSLocalizedDescription=An unknown error occurred, please check the server response., ResponseErrorDomain=NSPOSIXErrorDomain, _kCFStreamErrorDomainKey=1, NSErrorPeerAddressKey={length = 28, capacity = 28, bytes = 0x1c1e01bb000000002607f8b040090813 ... 0000200a00000000}, _kCFStreamErrorCodeKey=100}
I have checked and rechecked the google storage bucket location and believe I have the reference location correct (using the same as the upload file path which works correctly).
Any help would be much appreciated
There you go :
func downloadImage(url : String,
completionHandler: #escaping (Bool?, UIImage?, String?) -> Void) -> Void
{
var success : Bool = false
var img : UIImage? = nil
var errorLog : String? = nil
let u = URL(string: url)
let task = URLSession.shared.dataTask(with: u!, completionHandler: { (data, response, error) in
if error != nil
{
errorLog = error?.localizedDescription
completionHandler(success, img, errorLog)
}
else
{
success = true
img = UIImage(data: data!)
completionHandler(usuccess, img, errorLog)
}
})
task.resume()
}
Get URL using :
imgReference.downloadURL { (url, error) in
guard let url = url else { return }
urlString = url.absoluteString
//do something with the urlString (such as download image)
}
Realized the error was in headers that were included when uploading the image:
I had originally listed the following with the upload, by commenting them out I was able to successfully download with SDWebImage and the suggestion from vbuzze.
let uploadMetadata = StorageMetadata()
uploadMetadata.contentType = "image/jpeg"
uploadMetadata.customMetadata = ["Profile Name" : currentUser.displayName] as? [String : String]

Error Domain=NSCocoaErrorDomain Code=257 The fil "" could not be opened - Firebase

So I am accessing the user's videos & photos through a custom UICollectionView, my issue is that when I attempt to upload to firebase the video through the mobile phone, i am getting this error:
2017-09-03 13:09:20.884509-0400 Project[5797:2021536] Cannot get file size: Error Domain=NSCocoaErrorDomain Code=257 "The file “IMG_3476.MP4” couldn’t be opened because you don’t have permission to view it." UserInfo={NSURL=file:///var/mobile/Media/DCIM/103APPLE/IMG_3476.MP4, NSFilePath=/var/mobile/Media/DCIM/103APPLE/IMG_3476.MP4, NSUnderlyingError=0x17064f450 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}}
/var/mobile/Media/DCIM/103APPLE/IMG_3476.MP4
2017-09-03 13:09:21.261767-0400 Project[5797:2021536] Body file is unreachable: /var/mobile/Media/DCIM/103APPLE/IMG_3476.MP4
Error Domain=NSCocoaErrorDomain Code=257 "The file “IMG_3476.MP4” couldn’t be opened because you don’t have permission to view it." UserInfo={NSURL=file:///var/mobile/Media/DCIM/103APPLE/IMG_3476.MP4, NSFilePath=/var/mobile/Media/DCIM/103APPLE/IMG_3476.MP4, NSUnderlyingError=0x170651b20 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}}
Here is where I am loading the files
struct Media {
var image:UIImage?
var videoURL:NSURL?
}
var mediaArray = [Media]()
func grabPhotos(){
let imgManager = PHImageManager.default()
let requestOptions = PHImageRequestOptions()
requestOptions.isSynchronous = true
requestOptions.deliveryMode = .highQualityFormat
let fetchOptions = PHFetchOptions()
fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
if let fetchResult : PHFetchResult = PHAsset.fetchAssets(with: .video, options: fetchOptions) {
if fetchResult.count > 0 {
for i in 0..<fetchResult.count{
var mediaItem = Media()
//Used for fetch Image//
imgManager.requestImage(for: fetchResult.object(at: i) as PHAsset , targetSize: CGSize(width: 400, height: 400), contentMode: .aspectFit, options: requestOptions, resultHandler: {
image, error in
let imageOfVideo = image! as UIImage
mediaItem.image = imageOfVideo;
//Used for fetch Video//
imgManager.requestAVAsset(forVideo: fetchResult.object(at: i) as PHAsset, options: PHVideoRequestOptions(), resultHandler: {(avAsset, audioMix, info) -> Void in
if let asset = avAsset as? AVURLAsset {
let videoData = NSURL(string: "\(asset.url)")
let duration : CMTime = asset.duration
let durationInSecond = CMTimeGetSeconds(duration)
print(durationInSecond)
mediaItem.videoURL = videoData!
self.mediaArray.append(mediaItem)
print(self.mediaArray.count)
}
})
})
}
}
else{
//showAllertToImportImage()//A function to show alert
}
}
}
I dont think it has to do with when I am uploading the video because i can record a video an upload it from the phone, just not upload from the phone itself; I am just transferring over the url to the nextVC like so:
let videoVC = PrepareVideoVC()
videoVC.url = outputFileURL as URL!
self.navigationController?.pushViewController(videoVC, animated: false)
I looked into this The file “ ” couldn’t be opened because you don’t have permission to view it but couldnt really figure out how to implement it
I also came across https://stackoverflow.com/a/41517165/7823620 but once again I tried implementing it but couldnt get it to not give me errors
Also I can upload from the simulator but not the mobile phone
It is trying to get a file from this directory:
NSFilePath=/var/mobile/Media/DCIM/
You can try
url.startAccessingSecurityScopedResource()
<...>
url.stopAccessingSecurityScopedResource()
use this options:
PHVideoRequestOptions *options = [[PHVideoRequestOptions alloc] init];
options.version = PHVideoRequestOptionsVersionCurrent;
options.deliveryMode = PHVideoRequestOptionsDeliveryModeHighQualityFormat;
options.networkAccessAllowed = true;
if you request the middle-quality video, then u will get an error " Error Domain=NSCocoaErrorDomain Code=257, no permission..."
So if the AppDelegate gets passed in a document on launch via this method:
func application(_: UIApplication, open url: URL, options _: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
I found that I had to do this to FIX it so that FileManager documents loaded successfully using Ivan's solution above but you CAN NOT make that call if say the document is an e-mail attachment.
This fixed it for me and huge thanks again to Ivan Vavilov for his post.
func application(_: UIApplication, open url: URL, options _: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
print("Doc attachment passed")
let docAttachment = url
// files written to 'tmp' are NOT backed up to iCloud or iTunes
let tempDir = getTempDirectory()
// add file name to tmp path
tempFileToLoad = tempDir.appendingPathComponent(docAttachment.lastPathComponent)
let fm = FileManager()
// passed a file from the FileManger app?
let isAttachmentScopedResource = docAttachment.startAccessingSecurityScopedResource()
do {
// put in tmp so we can read/write
try fm.copyItem(at: docAttachment, to: tempFileToLoad!)
if(isAttachmentScopedResource) {
// have MUST release the security access after calling it
docAttachment.stopAccessingSecurityScopedResource()
}
} catch {
print("Error getting file. \(error)")
return false
}

Document file path unreachable in swift

I'm currently working on a small swift application and I'm storing some video records in the documents folder of the app. I would like to retrieve these on a later moment. I already got an array of file locations like this:
file:///private/var/mobile/Containers/Data/Application/6C462C4E-05E2-436F-B2E6-F6D9AAAC9361/Documents/videorecords/196F9A75-28C4-4B65-A06B-6111AEF85F01.mov
Now I want to use such file location to create a thumbnail with the first frame and connect that to my imageview with the following piece of code:
func createVideoStills() {
for video in directoryContents {
print("\(video)")
do {
let asset = AVURLAsset(URL: NSURL(fileURLWithPath: "\(video)"), options: nil)
let imgGenerator = AVAssetImageGenerator(asset: asset)
imgGenerator.appliesPreferredTrackTransform = true
let cgImage = try imgGenerator.copyCGImageAtTime(CMTimeMake(0, 1), actualTime: nil)
let uiImage = UIImage(CGImage: cgImage)
videoCell.imageView = UIImageView(image: uiImage)
//let imageView = UIImageView(image: uiImage)
} catch let error as NSError {
print("Error generating thumbnail: \(error)")
}
}
}
The first print gives me a path like described above. But the AVURLAsset doesn't like this path because it spits out the following error:
Error generating thumbnail: Error Domain=NSURLErrorDomain Code=-1100
"The requested URL was not found on this server."
UserInfo={NSLocalizedDescription=The requested URL was not found on
this server., NSUnderlyingError=0x14ee29170 {Error
Domain=NSPOSIXErrorDomain Code=2 "No such file or directory"}}
Which is weird cause it is right there. Any solutions on how to fix/solve this?
Kind regards,
Wouter
The output of your print("\(video)") is not a file path but a string representation of file URL. You need to use init(string:) than init(fileURLWithPath:) of NSURL.
See what you get with:
let asset = AVURLAsset(URL: NSURL(string: video), options: nil)
(Unnecessary string interpolation would generate some unexpected result without errors -- like getting "Optional(...)", so you should avoid.)
// if You Are Using PHPickerViewController then do this for fetching the url.
// ------
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
picker.dismiss(animated: true, completion: nil)
guard let provider = results.first?.itemProvider else { return }
if provider.hasItemConformingToTypeIdentifier(UTType.movie.identifier) {
provider.loadItem(forTypeIdentifier: UTType.movie.identifier, options: [:]) { [self] (videoURL, error) in
print("resullt:", videoURL, error)
DispatchQueue.main.async {
if let url = videoURL as? URL {
let player = AVPlayer(url: url)
let playerVC = AVPlayerViewController()
playerVC.player = player
present(playerVC, animated: true, completion: nil)
}
}
}
}
}

Parse Video Upload - Fatal error: unexpectedly found nil while unwrapping an Optional Value

I am trying to upload a video to Parse
If I just try to upload the video like so:
let videoData = NSData(contentsOfURL: url)
let videoFile = PFFile(name: "video.mov", data: videoData)
videoUploadObject["Video"] = videoFile
I will receive the error. I tried to remove this by running something like the following.
Main.sharedMain.userVideoOutputURL
returns:
file:///private/var/mobile/Containers/Data/Application/3B78A154-4340-432B-817A-2857EBA8064A/tmp/video.mov
Here is my full code:
let url = Main.sharedMain.userVideoOutputURL!
if let videoData = NSData(contentsOfURL: url) {
let videoFile = PFFile(name: "video.mov", data: videoData)
videoUploadObject["Video"] = videoFile
print("Video File \(videoFile)")
} else {
print("Else")
}
Else is always printed? What can I do to fix this? Thank you!
The URL looks fine (iOS doesn't use "/User/blah/blah/blah" for applicaion storage).
My first thought would be to check the data is writing correctly. NSData has a "writeToFile: options:" method that returns a bool. This will return false if writing has failed, and also throw an error which should give you some extra information.
let url = <YOUR_URL>
do {
// put your options in here
try data.writeToURL(url, options: NSDataWritingOptions.DataWritingAtomic)
} catch _ {
print(error.localizedDescription)
}
If writing has succeeded, then you'll want to try loading the data using "contentsWithUrl: options:". This also throws an error if it fails, and so using the "localisedDescription" property of the thrown error object you should be able to get to the bottom of why it isn't loading.
let url = <YOUR_URL>
var data: NSData? = nil
do {
// put your options in here
try data = NSData(contentsOfURL: url, options: NSDataReadingOptions.DataReadingUncached)
} catch _ {
print(error.localizedDescription)
}
Hope that helps :)