3

I'm pretty new to IOS Application Development.

I'm trying to stop viewWillAppear from finishing until after my function has finished working. How do I do that?

Here's viewWillAppear:

override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(true)

    checkFacts()

    if reset != 0 {
        print("removing all bird facts")
        birdFacts.removeAll()
    }
}

func checkFacts() {
    let date = getDate()
    var x: Bool = true
    var ind: Int = 0
    print("count is ", birdFacts.count)
    while ind < birdFacts.count {
        print("accessing each bird fact in checkFacts")
        let imageAsset: CKAsset = birdFacts[ind].valueForKey("birdPicture") as! CKAsset
        let image = UIImage(contentsOfFile: imageAsset.fileURL.path!)
        print(image)
        if image == nil {
            if (birdFacts[ind].valueForKey("sortingDate") != nil){
                print("replacing fact")
                print("accessing the sortingDate of current fact in checkFacts")
                let sdate = birdFacts[ind].valueForKey("sortingDate") as! NSNumber
                replaceFact(sdate, index: ind)
            }
            /*else {
                birdFacts.removeAll()
                print("removing all bird facts")
            }*/

        }
        ind = ind + 1
        print(ind)
    }
    self.saveFacts()
    let y = checkRepeatingFacts()
    if y {
        print("removing all facts")
        birdFacts.removeAll()
        //allprevFacts(date, olddate: 0)
    }
}

checkFacts references 2 others functions, but I'm not sure they're relevant here (but I will add them in if they are and I'm mistaken)

tryKuldeepTanwar
  • 3,490
  • 2
  • 19
  • 49
Alex
  • 141
  • 2
  • 10
  • Is it checkFacts that needs to finish before viewWillAppear? – Ali Beadle Jul 11 '16 at 16:13
  • Yes, checkFacts needs to finish first – Alex Jul 11 '16 at 16:35
  • as shown it will finish before the function viewWillAppear finishes (unless you have some asynchronous operations in those functions called in checkFacts) so I think I am missing something - do you mean that you don't want the view to appear until checkFacts has finished? – Ali Beadle Jul 11 '16 at 16:40
  • The issue I'm having is checkFacts is finding duplicates facts, which causes the function to delete all facts and re-download a new set from the cloud. However, when tableView runs, it tries to find the non-existent facts and crashes the code. – Alex Jul 11 '16 at 20:35

2 Answers2

2

Instead of trying to alter or halt the application's actual lifecycle, why don't you try using a closure?

override func viewWillAppear(animated: Bool) {
    super.viewWillAppear(true)

    checkFacts(){ Void in
        if self.reset != 0 {
            print("removing all bird facts")
            birdFacts.removeAll()
        }
    }
}

func checkFacts(block: (()->Void)? = nil) {
    let date = getDate()
    var x: Bool = true
    var ind: Int = 0
    print("count is ", birdFacts.count)
    while ind < birdFacts.count {
        print("accessing each bird fact in checkFacts")
        let imageAsset: CKAsset = birdFacts[ind].valueForKey("birdPicture") as! CKAsset
        let image = UIImage(contentsOfFile: imageAsset.fileURL.path!)
        print(image)
        if image == nil {
            if (birdFacts[ind].valueForKey("sortingDate") != nil){
                print("replacing fact")
                print("accessing the sortingDate of current fact in checkFacts")
                let sdate = birdFacts[ind].valueForKey("sortingDate") as! NSNumber
                replaceFact(sdate, index: ind)
            }
            /*else {
                birdFacts.removeAll()
                print("removing all bird facts")
            }*/

        }
        ind = ind + 1
        print(ind)
    }
    self.saveFacts()
    let y = checkRepeatingFacts()
    if y {
        print("removing all facts")
        birdFacts.removeAll()
        //allprevFacts(date, olddate: 0)
    }

    // CALL CODE IN CLOSURE LAST //
    if let block = block {
        block()
    }
}

According to Apple Documentation:

Closures are self-contained blocks of functionality that can be passed around and used in your code.

Closures can capture and store references to any constants and variables from the context in which they are defined.

So by defining checkFacts() as: func checkFacts(block: (()->Void)? = nil){...} we can optionally pass in a block of code to be executed within the checkFacts() function.

The syntax block: (()->Void)? = nil means that we can take in a block of code that will return void, but if nothing is passed in, block will simply be nil. This allows us to call the function with or without the use of a closure.

By using:

if let block = block {
    block()
}

we can safely call block(). If block comes back as nil, we pass over it and pretend like nothing happened. If block is not nil, we can execute the code contained within it, and go on our way.

One way we can pass our closure code into checkFacts() is by means of a trailing closure. A trailing closure looks like this:

checkFacts(){ Void in
    if self.reset != 0 {
        print("removing all bird facts")
        birdFacts.removeAll()
    }
}

Edit: Added syntax explanation.

ZGski
  • 2,398
  • 1
  • 21
  • 34
  • Would you mind explaining the syntax to me, since I haven't worked with blocks before? – Alex Jul 11 '16 at 16:35
  • @Alex I've updated my answer with an explanation that I hope helps make everything easier to understand. – ZGski Jul 11 '16 at 18:20
  • ok, that makes more sense now. The issue I'm having is checkFacts is finding duplicates facts, which causes the function to delete all facts and re-download a new set from the cloud. However, when tableView runs, it tries to find the non-existent facts and crashes the code. I tried your code and that didn't solve the issue, is there something specific I should do? – Alex Jul 11 '16 at 20:35
  • 1
    If that's the case, I think your issue isn't so much about waiting until a function has completed its code, but maybe more how you're handling duplicate facts. – ZGski Jul 11 '16 at 20:45
1

So based on the comments, checkFacts is calling asynchronous iCloud operations that if they are not complete will result in null data that your view cannot manage.

Holding up viewWillAppear is not the way to manage this - that will just result in a user interface delay that will irritate your users.

Firstly, your view should be able to manage null data without crashing. Even when you solve this problem there may be other occasions when the data becomes bad and users hate crashes. So I recommend you fix that.

To fix the original problem: allow the view to load with unchecked data. Then trigger the checkData process and when it completes post an NSNotification. Make your view watch for that notification and redraw its contents when it occurs. Optionally, if you don't want your users to interact with unchecked data: disable appropriate controls and display an activity indicator until the notification occurs.

Ali Beadle
  • 4,486
  • 3
  • 30
  • 55