Export file from iOS app in swift - ios

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.

Related

Getting "Error Domain=NSCocoaErrorDomain Code=257" in a document based app while attempting state restoration

Full error message is:
[main] *** Error from FPBookmarkableStringFromDocumentURL, file:///private/var/mobile/Library/Mobile%20Documents/com~apple~CloudDocs/MY_APP/hello.txt -> Error Domain=NSCocoaErrorDomain Code=257 "The file couldn’t be opened because you don’t have permission to view it."
This error is thrown from a SceneDelegate (introduced in iOS 13) when I relaunch my app & attempt to reopen previously opened file (for example when the app was "backgrounded" & the memory freed, so I want to reload the state of the scene).
I followed the steps from this WWDC session https://developer.apple.com/videos/play/wwdc2019/212 & the downloaded example source code from https://developer.apple.com/documentation/uikit/app_and_environment/scenes/supporting_multiple_windows_on_ipad
I basically copied their configure(window: UIWindow?, with activity: NSUserActivity) -> Bool function and transformed it to fit my needs. It seems to work, flawlessly, but I'm getting the error.
I'm using the default NSUserActivity technique described in "Adopting Handoff in Document-Based Apps" section of https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/Handoff/AdoptingHandoff/AdoptingHandoff.html#//apple_ref/doc/uid/TP40014338-CH2-SW17
Here's the body of the configure function:
if activity.activityType == "com.myName.MyApp.openedDocumentUserActivity" {
if let documentURL = activity.userInfo?[UIDocument.userActivityURLKey] as? URL {
if let docBrowserVC = window?.rootViewController as? DocumentBrowserViewController {
documentURL.startAccessingSecurityScopedResource() // tried this, it returns false
docBrowserVC.presentDocument(at: documentURL)
return true
}
}
}
return false
If anyone knows about a workaround, thank you.
I had to create bookmark data from the URL and persist that in the NSUserActivity. Using UIDocument.documentURL is not sufficient because it is security-scoped.
For creating the bookmark (no error checking in this snippet):
let userActivity = NSUserActivity(activityType: "com.foobar")
let bookmarkData = try? documentURL.bookmarkData()
userActivity.addUserInfoEntries(from: ["URLBookmarkData": bookmarkData])
scene.userActivity = userActivity
Then for reading:
let bookmarkData = userActivity.userInfo?["URLBookmarkData"] as? Data {
let resolvedURL = try? URL(resolvingBookmarkData: bookmarkData, options: NSURL.BookmarkResolutionOptions(), relativeTo: nil, bookmarkDataIsStale: &bookmarkDataIsStale)

getting error when i trying to convert AVAssetURL to Data

Error Domain=NSCocoaErrorDomain Code=257 "The file “IMG_9807.MOV” couldn’t be opened because you don’t have permission to view it." UserInfo={NSFilePath=/var/mobile/Media/DCIM/109APPLE/IMG_9807.MOV, NSUnderlyingError=0x1c1e5fe00 {Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted"
i am sending asset URL to other controller and try to convert into data
PHImageManager.default().requestAVAsset(forVideo: self.albumView.phAsset, options: options) { (video, audioMix, info) in
DispatchQueue.main.async {
let urlAsset = video as! AVURLAsset
self.dismiss(animated: false, completion: {
self.delegate?.fusumaVideoCompleted(withFileURL: urlAsset.url)
})
}
}
here below methods for convert AVAssetUrl to data
do {
let data = try Data(contentsOf: product.videoURL, options: .mappedIfSafe)
return .upload(.multipart([MultipartFormData(provider: .data(data), name: "post[video]", fileName: "video.\(pathExtension)", mimeType: "video/\(pathExtension)")]))
} catch {
debugPrint(error)
}
As the error tells you, you cannot access the video file in the user's photo library by way of its URL for purposes of uploading it. You should obtain the video data and upload that. A video is very big, so you should not get the data directly and hold it in memory; instead, export the data to a file in a place that you are allowed to access, such as the Temporary folder.
To do that, you might (for example) use this method:
https://developer.apple.com/documentation/photos/phassetresourcemanager/1616280-writedata
Or this one:
https://developer.apple.com/documentation/photos/phimagemanager/1616981-requestexportsession
If you use the Mail app to email a video from your own device's photo library, you will actually see that happening; there is a pause with a progress bar while the video is exported, and then the email is constructed.

Load mp4 to pasteboard in swift

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!

UIImagePickerController - Save Recorded Video to Camera Roll Error

I'm trying to save a video recorded with camera using the UIImagePickerController. I've tried two different ways, and both are not working at all.
First attempt:
if let pathURL = info["UIImagePickerControllerMediaURL"] as! URL?, mediaType == kUTTypeMovie {
PHPhotoLibrary.shared().performChanges({
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: pathURL)
}, completionHandler: { (isSuccessfull, error) in
if error != nil {
// ERROR
// error.localizedDescription value below
// "The operation couldn’t be completed. (Cocoa error -1.)"
} else if isSuccessfull {
// SUCCESS
}
})
}
On that attempt, I'm always getting the error "The operation couldn’t be completed. (Cocoa error -1.)"
Second attempt:
if let filePath = info["UIImagePickerControllerMediaURL"] as! NSURL,
let stringPath = filePath.path {
UISaveVideoAtPathToSavedPhotosAlbum(stringPath, self, #selector(Controller.videoSaved(videoPath:didFinishSavingWithError:contextInfo:)), nil)
}
This is not working either... The callback, the error is nil, but the video is not saved into the camera roll. Also, when I use the UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(stringPath), this method also returning me false all the time. I'm not able to understand why the video recorded would be incompatible to save.
I really don't know what I'm doing wrong. What am I missing? A configuration in plist?
Here's how look like the value of info["UIImagePickerControllerMediaURL"]:
file:///private/var/mobile/Containers/Data/Application/2B9BE04A-17B3-49CE-B4BA-C45F183E9A64/tmp/837294779101__C4825AFE-8140-420F-ACD0-64623C7A4753.MOV
For pictures taken with camera, PHPhotoLibrary method works very fine... It's only videos that is not able to be saved.
Not sure in 100% but I believe that it wasn't working for me because I was saving the video only after a request to my API was successful. My assumption is that the path URL is temporary and has a very short time of life. When I pass it to another controller, and make API call, it doesn't work. But if I save the video directly in the delegate method of UIImagePickerViewController, then it works fine.

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.

Resources