UIDocumentInteractionController and Swift: Two rules to never forget



A UIDocumentInteractionController is used to enable the sharing of documents between your app and other apps installed on a user's device. It is simple to set up as long as you remember two rules:
  1. Always make the UIDocumentInteractionController instance a class (type) property. If you only retain a reference to the controller for the life of the method that is triggered by the button press your app will crash.
  2. Configure the UIDocumentInteractionController before the button calling the method is pressed so that there is not a wait in which the app is waiting for the popover to appear.
The second "rule" is applied because while the presentation of the controller happens asynchronously, the instantiation does not. And you may find that there is a noticeable delay to open the popover if you throw all the code for instantiation and presentation inside a single method called on the press of a button.

Note: If you're dealing with a document that is being continuously saved to the same URL, then there is no harm in instantiating early. And even if the user moves the document to a new URL, you can simply update the UIDocumentInteractionController URL property. The only fee you are paying is the memory required to hold the instance and you will likely find this is a small price for an enhanced user experience.

In code

So here is a rather contrived example implementing these two rules. It assumes you have a file called MyFile.txt in the app bundle and that you have a UIBarButtonItem wired up to the controller called actionButton, and that this button is also wired up to the shareDoc() method:
class ViewController: UIViewController {

    // UIDocumentInteractionController instance is a class property
    var docController:UIDocumentInteractionController!

    // called when bar button item is pressed
    @IBAction func shareDoc(sender: AnyObject) {
        // present UIDocumentInteractionController
        docController.presentOptionsMenuFromBarButtonItem(sender as! UIBarButtonItem, animated: true)
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        // retrieve URL to file in main bundle
        let fileURL = NSBundle.mainBundle().URLForResource("MyFile", withExtension: "txt")!
        // Instantiate the interaction controller
        self.docController = UIDocumentInteractionController(URL: fileURL)
      
    }

}
With the two rules in place there's not much to the actual presentation of the controller. But since the popover presentation is asynchronous, it is worth outlining how to take the instantiation of the UIDocumentInterationController off the main thread as well.
class ViewController: UIViewController {

    // UIDocumentInteractionController instance is a class property
    var docController:UIDocumentInteractionController!
    // we're going to disable action button while controller is instantiated
    @IBOutlet weak var actionButton: UIBarButtonItem!

    // called when bar button item is pressed
    @IBAction func shareDoc(sender: AnyObject) {
        // present UIDocumentInteractionController
        docController.presentOptionsMenuFromBarButtonItem(sender as! UIBarButtonItem, animated: true)
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        // disable action button
        actionButton.enabled = false
        // Set priority for dispatch
        let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
        // take instantiation off the main thread
        dispatch_async(dispatch_get_global_queue(priority, 0)) {
            [unowned self] in
            // retrieve URL to file in main bundle
            let fileURL = NSBundle.mainBundle().URLForResource("MyFile", withExtension: "txt")!
            // Instantiate the interaction controller
            self.docController = UIDocumentInteractionController(URL: fileURL)
            dispatch_async(dispatch_get_main_queue()) {
                // re-enable action button
                [unowned self] in self.actionButton.enabled = true
            }
        }
    }

}
In the example you'll be lucky to catch the button in a disabled state but the technique might prove useful when seeking an opportune moment to prepare your UIDocumentInteractionController where lag might be more noticeable. (It might also be worth considering this asynchronous, or at least preprepared, approach when passing NSURLs to a UIActivityViewController if you find any slow downs there.)

Conclusion

A UIDocumentInteractionController performs a similar role to a UIActivityViewController without relying on the existence of extensions for the apps your user might wish to share documents with. It also works with the assumption that the user will commonly be dealing with documents rather than performing an activity or action like copy and paste or sending a tweet.

Although there is cross-over between the two types of sharing – using UIDocumentInteractionController and UIActivityViewController – they do not entirely eclipse one another. It is therefore important to be aware of the existence of both classes and their roles, and I would encourage you now to dig deeper into the UIDocumentInteractionController and the accompanying UIDocumentInteractionControllerDelegate protocol.



Comments

Post a Comment