Search code examples
iosswifticloud

What is the "uncaught exception"?


I'm trying to build a simple iPhone app that saves user entered text to iCloud, using a UITextView and a UIButton. I've never done something like this, so I'm following this tutorial. So far their tutorials have worked flawlessly for me, until this one. The app builds fine, and runs on my iPhone, displaying a place to enter text, and a button to save the text to iCloud. But when I tap the screen to enter text, the app crashes with this message:

2017-03-24 08:45:56.093572 Gaethr2[6342:1419021] * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '* -[NSMetadataQuery valueOfAttribute:forResultAtIndex:]: index (0) out of bounds (0)' *** First throw call stack: (0x184b611b8 0x18359855c 0x184b61100 0x1855f1460 0x100056ffc 0x10005857c 0x184afab10 0x184afa214 0x184af9f90 0x184b69b8c 0x184a3be64 0x185570e0c 0x1855ef268 0x184afab10 0x184afa214 0x184af9f90 0x184b69b8c 0x184a3be64 0x185570e0c 0x1968d93e4 0x1968db29c 0x184b0ea44 0x184b0e240 0x184b0c094 0x184a3a2b8 0x1864ee198 0x18aa817fc 0x18aa7c534 0x10005b720 0x183a1d5b8) libc++abi.dylib: terminating with uncaught exception of type NSException Message from debugger: failed to send the k packet

Here's the ViewController code:

import UIKit

class ViewController: UIViewController
{
    @IBOutlet weak var textView: UITextView!

    var document: MyDocument?
    var documentURL: URL?
    var ubiquityURL: URL?
    var metaDataQuery: NSMetadataQuery?



    func metadataQueryDidFinishGathering(notification: NSNotification) -> Void
    {
        let query: NSMetadataQuery = notification.object as! NSMetadataQuery

        query.disableUpdates()

        NotificationCenter.default.removeObserver(self,
                                                  name: NSNotification.Name.NSMetadataQueryDidFinishGathering,
                                                  object: query)

        query.stop()

        let resultURL = query.value(ofAttribute: NSMetadataItemURLKey,
                                    forResultAt: 0) as! URL

        if query.resultCount == 1 {
            let resultURL = query.value(ofAttribute: NSMetadataItemURLKey,
                                        forResultAt: 0) as! URL

            document = MyDocument(fileURL: resultURL as URL)

            document?.open(completionHandler: {(success: Bool) -> Void in
                if success {
                    print("iCloud file open OK")
                    self.textView.text = self.document?.userText
                    self.ubiquityURL = resultURL as URL
                } else {
                    print("iCloud file open failed")
                }
            })
        } else {
            document = MyDocument(fileURL: ubiquityURL!)

            document?.save(to: ubiquityURL!,
                           for: .forCreating,
                           completionHandler: {(success: Bool) -> Void in
                            if success {
                                print("iCloud create OK")
                            } else {
                                print("iCloud create failed")
                            }
            })
        }
    }



    override func viewDidLoad()
    {
        super.viewDidLoad()

        let filemgr = FileManager.default

        ubiquityURL = filemgr.url(forUbiquityContainerIdentifier: nil)

        guard ubiquityURL != nil else {
            print("Unable to access iCloud Account")
            print("Open the Settings app and enter your Apple ID into iCloud settings")
            return
        }

        ubiquityURL = ubiquityURL?.appendingPathComponent(
            "Documents/savefile.txt")

        metaDataQuery = NSMetadataQuery()

        metaDataQuery?.predicate =
            NSPredicate(format: "%K like 'savefile.txt'",
                        NSMetadataItemFSNameKey)
        metaDataQuery?.searchScopes =
            [NSMetadataQueryUbiquitousDocumentsScope]

        NotificationCenter.default.addObserver(self,
                                               selector: #selector(
                                                ViewController.metadataQueryDidFinishGathering),
                                               name: NSNotification.Name.NSMetadataQueryDidFinishGathering,
                                               object: metaDataQuery!)

        metaDataQuery!.start()
    }

    @IBAction func saveDocument(_ sender: AnyObject)
    {

        document!.userText = textView.text

        document?.save(to: ubiquityURL!,
                       for: .forOverwriting,
                       completionHandler: {(success: Bool) -> Void in
                        if success {
                            print("Save overwrite OK")
                        } else {
                            print("Save overwrite failed")
                        }
        })
    }

    override func didReceiveMemoryWarning()
    {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


}

Here's the code for MyDocument.Swift

import UIKit

class MyDocument: UIDocument
{
    var userText: String? = "Some Sample Text"

    override func contents(forType typeName: String) throws -> Any
    {
        if let content = userText
        {

            let length =
                content.lengthOfBytes(using: String.Encoding.utf8)
            return NSData(bytes:content, length: length)
        }
        else
        {
            return Data()
        }
    }

    override func load(fromContents contents: Any, ofType typeName: String?) throws
    {
        if let userContent = contents as? Data
        {
            userText = NSString(bytes: (contents as AnyObject).bytes,
                                length: userContent.count,
                                encoding: String.Encoding.utf8.rawValue) as? String
        }
    }
}

Here's a screenshot of the crash:

enter image description here

Thank you for your help!


Solution

  • The uncaught exception is

    -[NSMetadataQuery valueOfAttribute:forResultAtIndex:]: index (0) out of bounds (0)'

    Delete the first occurrence of

    let resultURL = query.value(ofAttribute: NSMetadataItemURLKey,
                                    forResultAt: 0) as! URL
    

    It's redundant and causes the error if query.results is empty