Load mp4 to pasteboard in swift - ios

I'm developing an app in both Swift 4 and Objective-C that loads a set of m4a audio files (tried local and via http) to pasteboard.
Basically, this works when loading text to pasteboard
let pasteBoard = UIPasteboard.general
pasteBoard.string = "Paste me!"
Also, this works when loading an image to pasteboard
let image = UIImage(named: "person.png")
UIPasteboard.general.image = image;
But when working with mp4 files, i tried different approaches
Option 1. Loading from data from url (objective-c)
NSData *data = [NSData dataWithContentsOfURL:[NSURL
URLWithString:#"http://www.example.com/assets/vasir.m4a"]];
UIPasteboard *pasteBoard = [UIPasteboard generalPasteboard];
[pasteBoard setData:data forPasteboardType:#"public.mpeg4"];
Last line i tried public.mpeg4-audio public.audio like in
https://developer.apple.com/library/content/documentation/Miscellaneous/Reference/UTIRef/Articles/System-DeclaredUniformTypeIdentifiers.html
In this case, i receive an error :
Audiokeyboard[2475:996008] NSURLConnection finished with error - code -1022
Audiokeyboard[2475:995924] Could not save pasteboard named com.apple.UIKit.pboard.general. Error: Error Domain=PBErrorDomain Code=0 "Cannot load representation of type public.mpeg4" UserInfo={NSLocalizedDescription=Cannot load representation of type public.mpeg4, NSUnderlyingError=0x1d0640ff0 {Error Domain=PBErrorDomain Code=15 "No loader block available for type public.mpeg4." UserInfo={NSLocalizedDescription=No loader block available for type public.mpeg4.}}}
I search a lot about the "No loader block available for type public.mpeg4." and no results were found.
Option 2. Swift
do {
let url = URL(string : "https://www.example.com/home/vasir.m4a")
let data1 = try Data(contentsOf: url!)
let pb = UIPasteboard.general
pb.setData(data1, forPasteboardType: "public.mpeg-4")
} catch {
print(error.localizedDescription)
}
In this case, no error in received (not even in catch), when i debug the code i see that data1 var is getting the info from file. setData statement does something and this appears in console
swiftCustomKeyboard[2515:1011974] [BoringSSL] Function boringssl_session_errorlog: line 2881 [boringssl_session_read] SSL_ERROR_ZERO_RETURN(6): operation failed because the connection was cleanly shut down with a close_notify alert
swiftCustomKeyboard[2515:1011974] [BoringSSL] Function boringssl_session_errorlog: line 2881 [boringssl_session_read] SSL_ERROR_ZERO_RETURN(6): operation failed because the connection was cleanly shut down with a close_notify alert
(lldb)
So yeah, i'm stuck here. I'm usually not using swift or objective-c so maybe is just my lack of practice.
Any idea what to do?
Thanks!

Related

QLThumbnailGenerator starts failing when called multiple times (on actual device) iOS 13

I am trying to create thumbnail images of multiple Wallet Passes (.pkpass) by running a loop over all (around 200) passes in a specific folder and calling generateBestRepresentation(for:) for each of them.
This is the code:
let passesDirURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("Passes")
let size = CGSize(width: 1600, height: 1600)
let scale = UIScreen.main.scale
if let passURLs = try? FileManager.default.contentsOfDirectory(
at: self.passesDirURL,
includingPropertiesForKeys: nil,
options: .skipsHiddenFiles
),
!passURLs.isEmpty {
for passURL in passURLs {
// Create the thumbnail request.
let request = QLThumbnailGenerator.Request(
fileAt: passURL,
size: size,
scale: scale,
representationTypes: .thumbnail
)
// Retrieve the singleton instance of the thumbnail generator and generate the thumbnails.
let generator = QLThumbnailGenerator.shared
generator.generateBestRepresentation(for: request) { thumbnail, error in
if let error = error as? QLThumbnailError {
print ("Thumbnail generation error: \(error)")
print ("Thumbnail generation localizedDescription: \(error.localizedDescription)")
print ("Thumbnail generation errorUserInfo: \(error.errorUserInfo)")
print ("Thumbnail generation errorCode: \(error.errorCode)")
} else {
print ("Thumbnail generation OK")
//do something with thumbnail here
}
}
}
}
This works fine on the simulator, but on an actual device (iPhone Xs Max) sooner or later I start getting errors and the thumbnail generation fails for a big fraction of the passes. The output looks as follows:
Thumbnail generation error: related decl 'e' for QLThumbnailError(_nsError: Error Domain=QLThumbnailErrorDomain Code=3 "No thumbnail in the cloud for file:///private/var/mobile/Containers/Data/Application/DCF703F7-9A1A-4340-86EB-42579D678EEF/Documents/Passes/pass123.pkpass" UserInfo={NSErrorFailingURLKey=file:///private/var/mobile/Containers/Data/Application/DCF703F7-9A1A-4340-86EB-42579D678EEF/Documents/Passes/pass123.pkpass})
Thumbnail generation localizedDescription: The operation couldn’t be completed. (QLThumbnailErrorDomain error 3.)
Thumbnail generation errorUserInfo: ["NSErrorFailingURLKey": file:///private/var/mobile/Containers/Data/Application/DCF703F7-9A1A-4340-86EB-42579D678EEF/Documents/Passes/pass123.pkpass]
Thumbnail generation errorCode: 3
The error description sounds confusing ("No thumbnail in the cloud for file") as these are not iCloud files.
As the error does not occur when calling the thumbnail generation individually, this seems to be some memory/performance issue. I tried to workaround in many ways, including using a semaphore in the for loop waiting for the completion of one call of generateBestRepresentation to start the next call, which reduced but not eliminated the issue. The only way it worked without error was adding a very long sleep (5 seconds) after the semaphore.wait() statement, but this is no acceptable solution.
Another way I tried was using saveBestRepresentation (as suggested in Apple's documentation), but this did not solve the issue.
Has anyone faced a similar issue and was able to find an acceptable solution?

PHAsset copy video completion

I wish to copy video file from Photo Library to my app's Documents directory and wish to be notified about completion. Here is what I do:
let videoAsset = fetchResult.object(at: indexPath.item)
print(videoAsset.description)
let options = PHVideoRequestOptions()
options.version = .original
PHImageManager.default().requestAVAsset(forVideo: videoAsset, options: options) { [weak self] (avAsset, audioMix, info) in
if let avurlAsset = avAsset as? AVURLAsset {
let url = avurlAsset.url
let toUrl = //some Url
let fileManager = FileManager.default
do {
try fileManager.copyItem(at: url, to: toUrl)
} catch {
NSLog("Unable to copy file from \(url) to \(toUrl)")
}
}
}
Only problem with this approach is I have no way to be notified of completion of copyItem. What is the alternative to copyItem method (or altogether a different approach to above) that is atleast blocking till copy finishes? Is it possible to use FileHandle & read consecutive bytes and write to another file? Will that be synchronous enough?
EDIT: As pointed by Alex, copyItem is actually synchronous routine. On closer inspection, I see I sometimes get errors on copying. Not sure why the permission errors show up when it is app's Documents folder where I copy.
2018-08-27 20:30:07.485841+0530 MyProject[3577:1288452] Copying file...
2018-08-27 20:30:07.487880+0530 MyProject[3577:1288452] stat on /var/mobile/Media/DCIM/107APPLE/IMG_7915.MP4: Operation not permitted
2018-08-27 20:30:07.512994+0530
MyProject[3577:1288452] Unable to copy file from file:///var/mobile/Media/DCIM/107APPLE/IMG_7915.MP4 to file:///var/mobile/Containers/Data/Application/CC13FD5A-E4CF-42A1-931F-2F1FFE799C15/Documents/IMG-0027.mov, Error Domain=NSCocoaErrorDomain Code=513 "“IMG_7915.MP4” couldn’t be copied because you don’t have permission to access “Documents”." UserInfo=
{NSSourceFilePathErrorKey=/var/mobile/Media/DCIM/107APPLE/IMG_7915.MP4, NSUserStringVariant=(
Copy
),
NSDestinationFilePath=/var/mobile/Containers/Data/Application/CC13FD5A-E4CF-42A1-931F-2F1FFE799C15/Documents/IMG-0027.mov, NSFilePath=/var/mobile/Media/DCIM/107APPLE/IMG_7915.MP4, NSUnderlyingError=0x111c441c0 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"}}
Copyitem: Copies the item at the specified path to a new location synchronously.
Returns true if the item was copied successfully or the file manager’s delegate stopped the operation deliberately. Returns false if an error occurred.
This is a sync method so after it executed after catch without error then it means successful copied.
https://developer.apple.com/documentation/foundation/filemanager/1407903-copyitem

converting video to NSData in share extension in ios

I have created a share extension for my ios app. When I click on the share option in the photo app my share extension is shown and I click on it and my controller is shown. Everything is working fine up to this. I am uploading video to youtube using the youtube api. I am using this method to create the parameter
GTLUploadParameters *uploadParameters = [GTLUploadParameters uploadParametersWithData:fileData MIMEType:#"video/*"];
Now if the video is small then it is easily converted to NSData using this code
NSData *fileData = [NSData dataWithContentsOfURL:[NSURL URLWithString:videoURL]];
and everything is working and video is uploaded.
But if the video is large then it simply crash and exit from the share extension(I put breakpoint and found this problem. If I remove the fileData conversion then its not crashing.). So what I did was instead of converting it to NSData I used this youtube api method
GTLUploadParameters *uploadParameters = [GTLUploadParameters uploadParametersWithFileURL:[NSURL URLWithString:videoURL] MIMEType:#"video/*"];
Now app is not crashing but I am getting network error. The error is
Error Domain=NSURLErrorDomain Code=-995 "(null)"
little searching found that it is because of NSURLSession and told to use something like this
sessionConfiguration.sharedContainerIdentifier = #“com.me.myapp.containerIdentifier”;
I am using youtube api. I am not sure where to use it OR is there any other way to use youtube api in share extension with large video file.
NOTE: I am using youtube api in my app and its working fine with NSData.
Hope question is clear. I am stuck on it for a day now. Please help.
Thanks in advance.
EDIT 1:
I used this code
NSData *fileData = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:videoURL] options:0 error:&error];
filedata is nil. The error I am getting is
Error Domain=NSCocoaErrorDomain Code=260 "The file “IMG_2187.MOV”
couldn’t be opened because there is no such file."
UserInfo={NSFilePath=/file:/var/mobile/Media/DCIM/102APPLE/IMG_2187.MOV,
Consider this line:
var videoDataURL = info[UIImagePickerControllerMediaURL] as! NSURL!
This does a forced unwrapping of info[UIImagePickerControllerMediaURL] (which is bad, because if it was nil, the app would crash) and that casts it as an implicitly unwrapped optional NSURL!. That doesn't make sense. Just do a conditional unwrapping (and unwrap to a NSURL, not a NSURL!):
if let videoDataURL = info[UIImagePickerControllerMediaURL] as? NSURL { ... }
The next line calls filePathURL:
var videoFileURL = videoDataURL.filePathURL
If you wanted a file URL, you already have one, so no conversion is needed, but instead just use videoDataURL. If you really wanted a path, you'd use path method:
let videoPath = videoDataURL.path
Frankly, Apple is trying to shift us away from using string paths, so just use the original videoDataURL and avoid the use of both path and filePathURL.
You are using dataWithContentsOfMappedFile:
var video = NSData.dataWithContentsOfMappedFile("\(videoDataURL)")
If you really wanted to use dataWithContentsOfMappedFile, the proper Swift syntax is:
let video = NSData(contentsOfMappedFile: videoPath!)
But dataWithContentsOfMappedFile deprecated, so you should instead use:
let video = try NSData(contentsOfFile: videoPath!, options: .DataReadingMappedIfSafe)
Or, bypassing that videoPath altogether, you could:
let video3 = try NSData(contentsOfURL: videoDataURL, options: .DataReadingMappedIfSafe)
Obviously, those try renditions should be done within a do block with a catch block.
By the way, as you'll see in all of my above examples, one should use let where possible.
Quite frankly, I would advise against loading it into a NSData at all. Just copy it with NSFileManager, which is a more efficient use of memory. If the video is long, it could be quite large, and you should avoid loading the whole thing into memory at any given point in time.
So you could:
if let videoDataURL = info[UIImagePickerControllerMediaURL] as? NSURL {
do {
// build your destination URL however you want
//
// let tempFolder = NSURL(fileURLWithPath: NSTemporaryDirectory())
// let destinationURL = tempFolder.URLByAppendingPathComponent("test.mov")
// or
let documents = try NSFileManager.defaultManager().URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: false)
let destinationURL = documents.URLByAppendingPathComponent("test.mov")
// but just copy from the video URL to the destination URL
try NSFileManager.defaultManager().copyItemAtURL(videoDataURL, toURL: destinationURL)
} catch {
print(error)
}
}
If you're uploading this to a web service, you'd then use a NSURLSessionUploadTask, using file or stream options. The construction of this request is a separate question, but hopefully you get the idea: With large assets like photos or, especially, videos, don't instantiate a NSData with the asset if you can possibly avoid it.
Please try this if your file exist in your phone instead of [NSURL URLWithString:videoURL].
NSData *fileData = [NSData dataWithContentsOfURL:[NSURL fileURLWithPath:videoURL]];

Facebook SDK share on iOS not working

I am working on sharing a content on Facebook for an iOS app using Swift.
I have written a singleton class called FBManager and a function as below.
func shareContent(content:String, contentURL:String?, contentTitle:String? , fromController controller:UIViewController {
let shareDialog = FBSDKShareDialog()
let shareLinkContent = FBSDKShareLinkContent()
shareLinkContent.contentDescription = content
if let url = contentURL
{
shareLinkContent.contentURL = NSURL(string: url)
}
if let title = contentTitle
{
shareLinkContent.contentTitle = title
}
shareDialog.delegate = self
shareDialog.fromViewController = controller
shareDialog.shareContent = shareLinkContent
shareDialog.show()
}
But this does not even show a share dialog both on iOS 8 and iOS 9.
Instead the following delegate method gets called
func sharer(sharer: FBSDKSharing!, didFailWithError error: NSError!) {
}
with the error - "The operation couldn’t be completed. (com.facebook.sdk.share error 2.)"
Can someone please help ?
Facebook SDK's error codes are somewhat ambiguous because they cover rather large domains of errors. The code you provided does not really show the content of the variables and so I cannot pinpoint the problem. However, com.facebook.sdk.share error 2 is an Invalid Argument error, which usually arises from an invalid format of one or more members of FBSDKShareLinkContent.
Generally, you can use the FBSDKErrorCode enum to switch over the (error as NSError).code and find which domain it belongs to. (In this case, it'll point to Invalid Argument)
You can also print(error) directly in the didFailWithError delegate method, which will output a very descriptive log of the error and what caused it specifically.
Check your contentURL, make sure it starts with http:// or https:// or any other valid protocol. Same for the imageURL if you're using or planning to use one. This most likely caused your error!
The SDK's error codes reference may be helpful too.

Export file from iOS app in swift

I'm fairly new to Swift and iOS development in general. My app has a model that can easily be expressed as comma separated values (csv), so naturally I want the user to be able to export the data as an csv file and opening that file in another application. Since I didn't find any examples in Swift, I gave it a try on my own:
func ExportToCSV(delegate: UIDocumentInteractionControllerDelegate){
let fileName = NSTemporaryDirectory().stringByAppendingPathComponent("myFile.csv")
let url: NSURL! = NSURL(fileURLWithPath: fileName)
var data = "Date,Time,Count\n2014-11-21,14.00,42"
data.writeToURL(url, atomically: true, encoding: NSUTF8StringEncoding, error: nil)
if url != nil {
let docController = UIDocumentInteractionController(URL: url)
docController.UTI = "public.comma-separated-values-text"
docController.delegate = delegate
docController.presentPreviewAnimated(true)
}
}
(the delegate parameter is the view that calls the function, as in MyClass.ExportToCSV(self))
This works, mostly, and I see the following views:
However, in the Simulator I get the following warning:
Unbalanced calls to begin/end appearance transitions for <QLRemotePreviewContentController: 0x7fcd720da800>.
as well as
Unknown activity items supplied: ("<QLPrintPageRenderer: 0x7fcd73861ee0>","<UIPrintInfo: 0x7fcd714b9030>")
when I click the action button, then after a while
Communications error: <OS_xpc_error: <error: 0x10e032b10> {
count = 1, contents = "XPCErrorDescription"
=> <string: 0x10e032f18> { length = 22, contents = "Connection interrupted" }
}>
and when I click Mail there is a crash with the following error:
viewServiceDidTerminateWithError: Error Domain=_UIViewServiceInterfaceErrorDomain
Code=3 "The operation couldn’t be completed. (_UIViewServiceInterfaceErrorDomain error 3.)"
UserInfo=0x7fcd71631460 {Message=Service Connection Interrupted}
<MFMailComposeRemoteViewController: 0x7fcd73864aa0> timed out waiting for fence
barrier from com.apple.MailCompositionService
Although on the actual device everything works as planned, so many errors throw me off a bit. Is there a better solution?
This is probably due to objects already have been deallocated when they are accessed by the mail or printing app. Try to declare your docController and url variables as class properties so they keep alive as long as the view controller exists.

Resources