4

This is my first time making an iCloud app, and I'm following this tutorial (and this supplemental tutorial on how to set up an iCloud app). All it does is accept text (via a "Text View" object), and saves that text (via a UIButton) to a document that gets stored in iCloud. I've gotten to the end of the tutorial, and when I test it, it crashes. Well, I shouldn't say "crash" because when I've tested other apps, Xcode ends the test, which is not the case here. The app keeps running, but I'm not able to do anything (I can't write any text or save with the "save" button).

Here's the code for ViewController.swift

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 output when the app "crashes":

2017-03-15 15:12:06.531223 Gaethr2[2708:734758] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[NSMetadataQuery valueOfAttribute:forResultAtIndex:]: index (0) out of bounds (0)'
*** First throw call stack:
(0x1876951b8 0x1860cc55c 0x187695100 0x188125460 0x100102ffc 0x10010457c 0x18762eb10 0x18762e214 0x18762df90 0x18769db8c 0x18756fe64 0x1880a4e0c 0x188123268 0x18762eb10 0x18762e214 0x18762df90 0x18769db8c 0x18756fe64 0x1880a4e0c 0x19940d3e4 0x19940f29c 0x187642a44 0x187642240 0x187640094 0x18756e2b8 0x189022198 0x18d5b57fc 0x18d5b0534 0x100107720 0x1865515b8)
libc++abi.dylib: terminating with uncaught exception of type NSException

And here's some screenshots of the potential problem:

enter image description here

Xcode wanted me to get rid of "let resultURL" so I did (as seen below), hoping that would fix the problem, but it didn't.

enter image description here

I'm really out of my depth here, hence my simply following a tutorial. I would really appreciate some help. Let me know if I need to add more information. I don't know what the problem is, so I could have omitted some important information.

Theodore.K
  • 384
  • 2
  • 7
  • 21

2 Answers2

1

Delete this line:

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

The line where you have your breakpoint reads the first result (index 0) without checking if there were any results (which you do check on a subsequent line).

I'm guessing you didn't receive any results from your query. You chould have a look at the conditions you set when you prepared it and also look on iCloud if the data you're expecting is indeed present.

Alain T.
  • 40,517
  • 4
  • 31
  • 51
  • Thank you for your help! Can you please add some example code? How can I check to see if there are any results? Is checking on a subsequent line the wrong thing to do? I didn't write this code, rather it's a tutorial, so I'm not 100% sure of what's going on. How do I look at the conditions, and how do I look on iCloud to see if there's data? – Theodore.K Mar 23 '17 at 22:51
  • if query.resultCount > 0 will tell you if there are any results. You can either make that line conditional with that if condition or ( as travis suggested) remove it altogether since you don't seem to need it. – Alain T. Mar 24 '17 at 20:48