QLPreviewController works with URLs provided by UIDocumentBrowserViewController, but not with URLs generated manually - uikit

I'm using UIDocumentBrowserViewController combined with QLPreviewController to preview documents selected by users in UIDocumentBrowserViewController, which works perfectly fine. The pickedDocumentURL variable used by QLPreviewController is populated as follows:
func documentBrowser(_ controller: UIDocumentBrowserViewController, didPickDocumentURLs documentURLs: [URL]) {
// (...)
pickedDocumentURL = documentURLs.first as NSURL?
// Present QLPreviewController instance ...
}
However, when I populate the pickedDocumentURL variable using:
pickedDocumentURL = NSURL(string: documentURLs.first!.absoluteString)
or:
pickedDocumentURL = URL(string: documentURLs.first!.absoluteString) as NSURL?
... then the QLPreviewController does not work (it is presented, but the preview is empty) and I get the following error on the console:
[default] QLUbiquitousItemFetcher: could not create sandbox wrapper. Error: Error
Domain=NSPOSIXErrorDomain Code=1 "couldn't issue sandbox extension
com.apple.quicklook.readonly for
'/private/var/mobile/Containers/Shared/AppGroup/07524B34-D877-449F-A5C3-89A0431560E5/File
Provider
Storage/22207162/1qrbGgy6-u0f69mPqOjjpzlYiUYPR8OG_/Sample.pdf':
Operation not permitted" UserInfo={NSDescription=couldn't issue
sandbox extension com.apple.quicklook.readonly for
'/private/var/mobile/Containers/Shared/AppGroup/07524B34-D877-449F-A5C3-89A0431560E5/File
Provider
Storage/22207162/1qrbGgy6-u0f69mPqOjjpzlYiUYPR8OG_/Sample.pdf':
Operation not permitted} #PreviewItem
Moreover, the URL absolute strings in each of those cases are exactly the same.

you are using .absoluteString, use .path instead, I had same issue and this solved it:
pickedDocumentURL = NSURL(string: documentURLs.first!.path)

Here's the code that works for me:
func getPreviewItem(withName name: String ) -> NSURL
{
//let file = name.components(separatedBy: ".")
let pdfFile = getDocumentsDirectory().appendingPathComponent(name)
let url = pdfFile as NSURL (this line was the key)
return url
}

Related

Swift : Share text through Twitter

So basically I am making an event app. Everything has been going smoothly but there's just sharing the event to twitter.
I have searched the internet but all I am getting is using the native app of twitter which I don't want. I want to use the browser to tweet.
I have implemented this method for FB sharing.
Any idea would help me a lot.
let content = FBSDKShareLinkContent()
content.contentURL=NSURL(string: "http://facebook.com")
content.imageURL = NSURL(string: "http://facebook.com")
content.contentTitle = "Shou 3emlin test app "
content.contentDescription = "testing testing testing"
let shareDialog = FBSDKShareDialog()
shareDialog.fromViewController = self
shareDialog.mode=FBSDKShareDialogMode.Browser
shareDialog.shareContent = content
if !shareDialog.canShow() {
shareDialog.mode=FBSDKShareDialogMode.Native
shareDialog.shareContent = content
}
if shareDialog.canShow() {
shareDialog.show()
}
Put this in an action method of a button or in the method where you want to use the browser to tweet your text Swift 3.0:
let tweetText = "your text"
let tweetUrl = "http://stackoverflow.com/"
let shareString = "https://twitter.com/intent/tweet?text=\(tweetText)&url=\(tweetUrl)"
// encode a space to %20 for example
let escapedShareString = shareString.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)!
// cast to an url
let url = URL(string: escapedShareString)
// open in safari
UIApplication.shared.openURL(url!)
Result:
Take a look at Fabric.io. This SDK allows you to compose tweets directly from your app.
let composer = TWTRComposer()
composer.setText("just setting up my Fabric")
composer.setImage(UIImage(named: "fabric"))
// Called from a UIViewController
composer.showFromViewController(self) { result in
if (result == TWTRComposerResult.Cancelled) {
print("Tweet composition cancelled")
}
else {
print("Sending tweet!")
}
}
let tweetText = "hy"
let tweetUrl = "http://rimmi/"
let shareString = "https://twitter.com/intent/tweet?text=\(tweetText)&url=\(tweetUrl)"
// encode a space to %20 for example
let escapedShareString = shareString.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)!
// cast to an url
let url = URL(string: escapedShareString)
// open in safari
UIApplication.shared.openURL(url!)
#ronatory's solution worked like charm. It also opens a Twitter application if it's already installed on the user's device.
For swift 5+ use UIApplication.shared.open(url!) instead of UIApplication.shared.openURL(url!) as it's deprecated.

Optional(Error Domain=ContentBlockerErrorDomain Code=1 "(null)")

I get the following error while load the json file with SFContentBlockerManager.reloadContentBlockerWithIdentifie
Optional(Error Domain=ContentBlockerErrorDomain Code=1 "(null)")
// This is my code to get attachment
let documentDirectoryURL = try! NSFileManager.defaultManager().URLForDirectory(.DocumentDirectory, inDomain: .UserDomainMask, appropriateForURL: nil, create: true)
let jsonFileUrl = documentDirectoryURL.URLByAppendingPathComponent("phantom.json")
let data = NSData(contentsOfURL: jsonFileUrl)
let attachment = NSItemProvider(item: data,typeIdentifier: kUTTypeJSON as String)
let item = NSExtensionItem()
item.attachments = [attachment]
context.completeRequestReturningItems([item], completionHandler: nil);
// Following code for SFContentBlockerManager
try! SFContentBlockerManager.reloadContentBlockerWithIdentifier("\(NSBundle.mainBundle().bundleIdentifier).PhantomBlocker", completionHandler: {(error) in print(error)})
I was facing the same problem while running one of the open source project than after regular attempt i came to known that the app is using "Bundle Identifier" so i used that in the code and it started running.
I guess you might have made a mistake in your json file. If you any type of error in the json file Xcode throws this error. There are some websites that validate you file. This should help you fix the issue.
For example:
http://jsonlint.com
https://jsonformatter.curiousconcept.com

NSURLSessionDataTask fails on simple HTTPS URL with error -1002

I'm writing a simple Twitter app for OS X and I'm currently stuck on downloading the user's profile image. This is the code responsible for downloading the image:
let imageRequest: NSURLRequest = NSURLRequest(URL: NSURL(string: avatarURL)!)
let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration())
let dataTask = session.dataTaskWithRequest(imageRequest) {
(data, response, error) in
println(error)
if data != nil {
println("Got image")
let image = NSImage(data: data!)
completion(image)
} else {
println("Data is nil")
completion(nil)
}
}
dataTask!.resume()
avatarURL is a String containing this: https://pbs.twimg.com/profile_images/618168482086129664/C0E92y7G.png, which is the URL to my Twitter avatar. As you can see, it's a perfectly valid URL leading to a perfectly valid image.
But whenever I run my app, the println(error) line prints this into my console:
Optional(Error Domain=NSURLErrorDomain Code=-1002 "The operation couldn’t be completed. (NSURLErrorDomain error -1002.)" UserInfo=0x61000006ea80 {NSErrorFailingURLStringKey=Optional(https://pbs.twimg.com/profile_images/618168482086129664/C0E92y7G.png), NSUnderlyingError=0x600000243720 "The operation couldn’t be completed. (kCFErrorDomainCFNetwork error -1002.)", NSErrorFailingURLKey=Optional(https://pbs.twimg.com/profile_images/618168482086129664/C0E92y7G.png)})
Am I doing something wrong? I've already googled (what a word) this, and almost every single SO page I've found has a solution that basically amounts to "Your URL is missing an URL scheme/has an unsupported URL scheme", which is not the problem I'm having.
I finally figured out how to fix this. Before, I just lazily did this:
var avatarURLString = String(responseObject["profile_image_url_https"])
which apparently returns an Optional that can't be unwrapped with an exclamation mark. Yes, I don't know either. It's just what happened.
Now, I do this:
var avatarURLString = responseObject["profile_image_url_https"] as! String
...which actually yields a proper String object instead of some weird not-quite-Optional. Hooray for using beta versions of programming languages.

I got issues writing a string in a txt file using swift XCode 7 [duplicate]

I am trying a simple dictionary retrieve, update key value and write back to file. For some reason the writeToFile does not update the file in the main bundle.
the code reads:
let filename = "testFile"
if let path = NSBundle.mainBundle().pathForResource(filename, ofType: "json") {
var error: NSError?
let InputData: NSData? = NSData(contentsOfFile: path, options: NSDataReadingOptions(), error: &error)
var jsonDictionary: NSMutableDictionary = NSJSONSerialization.JSONObjectWithData(InputData!, options: NSJSONReadingOptions.MutableContainers, error: &error) as NSMutableDictionary
jsonDictionary.setValue(1, forKey: "levelRow")
let options = NSJSONWritingOptions.PrettyPrinted
var outputData : NSData? = NSJSONSerialization.dataWithJSONObject(jsonDictionary, options: options, error: &error)
outputData?.writeToFile(path, atomically: true)
}
the file looks like this:
{
"levelColumn" : 0,
"levelRow" : 0,
}
the read and update work fine... but the file doe not update levelRow to 1?
thanks in advance.
You cannot write to the main bundle. All files in the bundle are read-only. Copy your file into the application documents directory before modifying it.
If you need a different file in the bundle to include in your application, you can update it in the documents directory during development and then manually copy it to the bundle before shipping your app.

Security Scoped Bookmark in App Extension

I am creating a TodayWidget app extension which displays information about user selected folders outside the application directory.
In my main application I am able to use powerbox via NSOpenPanel to select the folder. I can then save a security scoped bookmark to the user defaults of the app group container accessible by my TodayWidget.
The TodayWidget can read in the bookmark data, but when it calls URLByResolvingBookmarkData, it errors out with:
The file couldn’t be opened because it isn’t in the correct format.
Both my main application and the TodayWidget have the below entitlements:
com.apple.security.files.bookmarks.app-scope
com.apple.security.files.user-selected.read-only
From Apple's documentation, only the application that created the security scoped bookmark can use it. I guess these means embedded applications aren't allowed?
I've looked in to using XPC, but that doesn't really help the problem, as XPC can't use security scoped bookmark either, only a normal bookmark. As soon as the computer is restarted, the XPC process will lose access to the directories.
Really all I need is a way for the XPC process to get read access to user specified directories. Is there a way without having to relaunch my main application every restart of the computer?
You have probably already solved this or moved on. But for all those that are attempting something similar I will leave this here for them. In order to access security scoped bookmarks in a different app they have to be transferred as NSData and re-resolved in the other application.
In my case I show an open dialog in the main application and then save the scoped bookmark into a shared NSUserDefaults suite. The other applications are also part of that suite and then access the container of NSData's and resolve them into usable NSURL's
Here are the relevant bits of code:
//Inside my main application's open function
... get url from NSOpenPanel
BookmarkUtils.saveURLForOtherApplications(openPanel.URL!)
//Inside BookmarkUtils.swift
static func saveURLForOtherApplications(url:NSURL)->Bool{
let defaults = NSUserDefaults(suiteName: <#Suite-Name#>)!
//I store them as a dictionary of path->encoded URL
let sandboxedBookmarks:NSMutableDictionary
if let prevBookmarks = defaults.objectForKey(kSandboxKey) as? NSDictionary{
sandboxedBookmarks = NSMutableDictionary(dictionary:prevBookmarks)
}
else{
sandboxedBookmarks = NSMutableDictionary()
}
if let shareData = BookmarkUtils.transportDataForSecureFileURL(url){
sandboxedBookmarks.setObject(shareData, forKey:url.path!)
defaults.setObject(sandboxedBookmarks, forKey:kSandboxKey)
defaults.synchronize()
return true
}
else{
println("Failed to save URL Data");
return false
}
}
static func transportDataForSecureFileURL(fileURL:NSURL)->NSData?{
// use normal bookmark for encoding security-scoped URLs for transport between applications
var error:NSError? = nil
if let data = fileURL.bookmarkDataWithOptions(NSURLBookmarkCreationOptions.allZeros, includingResourceValuesForKeys:nil, relativeToURL:nil, error:&error){
return data;
}
else{
println("Error creating transport data!\(error)")
return nil
}
}
So then in my extension (Today view in my case) I do something like this...
class TodayViewController: ...
...
override func viewDidLoad() {
super.viewDidLoad()
var status = [MyCoolObjects]()
for url in BookmarkUtils.sharedURLSFromApp(){
BookmarkUtils.startAccessingSecureFileURL(url)
status.append(statusOfURL(url))
BookmarkUtils.stopAccessingSecureFileURL(url)
}
self.listViewController.contents = status
}
And the relevant bookmark looks something like:
static func sharedURLSFromApp()->[NSURL]{
var urls = [NSURL]()
if let defaults = NSUserDefaults(suiteName: <#Suite-Name#>){
if let prevBookmarks = defaults.objectForKey(kSandboxKey) as? NSDictionary{
for key in prevBookmarks.allKeys{
if let transportData = prevBookmarks[key as! NSString] as? NSData{
if let url = secureFileURLFromTransportData(transportData){
urls.append(url)
}
}
}
}
}
return urls
}
static func secureFileURLFromTransportData(data:NSData)->NSURL?{
// use normal bookmark for decoding security-scoped URLs received from another application
var bookmarkIsStale:ObjCBool = false;
var error:NSError? = nil;
if let fileURL = NSURL(byResolvingBookmarkData: data, options: NSURLBookmarkResolutionOptions.WithoutUI, relativeToURL: nil, bookmarkDataIsStale: &bookmarkIsStale, error: &error){
return fileURL
}
else if(bookmarkIsStale){
println("Bookmark was stale....")
}
else if let resolveError = error{
println("Error resolving from transport data:\(resolveError)")
}
return nil
}
This solution works for me. Once you resolve the shared URL you can then create a bookmark for that application and save it for later if so desired.There may be better ways out there, hopefully Apple works on this as it is currently painful to share permissions with extensions.
Actually you don't need to startAccessingSecureFileURL and it will return fail to start. just transform bookmark data to url will gain access.

Resources