Search code examples
swiftuicollectionviewbackground-fetch

App crashes at reloadData() for Collectionview with a background fetch


So I am doing a background fetch, and after I want to update the UI. The background fetching itself (database) works, but when I want to update the UI, I get a crash unexpectedly found nil while unwrapping an Optional value According to my xCode self.newestPublicationsCollectionView.reloadData() is where the crash occurs in the updateCollections() function.

 func updateCollections() {
        // get latest data from Database
        latestPublications = getNewestPublications()

        self.newestPublicationsCollectionView.reloadData()

        self.newestPublicationsCollectionView.layoutIfNeeded()

       // fix wrong content height
       self.newestPublicationsCollectionViewHeight.constant = self.newestPublicationsCollectionView.collectionViewLayout.collectionViewContentSize().height  
    }

Other code in the AppDelegate:

func application(application: UIApplication, performFetchWithCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) {

    let fetchViewController = PublicationOverviewController()
                fetchViewController.fetch {
                    dispatch_async(dispatch_get_main_queue()) {
                    fetchViewController.updateCollections()
                    }
                    completionHandler(.NewData)
                }
}

The .fetch in my PublicationOverviewController

func fetch(completion: () -> Void) {
    PublicationFetcher.shared.fetchAllPublications()
    completion()
}

I thought the crash was because I needed the UI on the main thread, but that didn't help. Any input would be nice.

DETAILS:

In my PublicationFetcher.shared.fetchAllPublications() I do the following:

  • get data from backend
  • dispatch_async(dispatch_get_main_queue()) { () -> Void in NSNotificationCenter.defaultCenter().postNotificationName("RELOAD_NOTIFICATION", object: nil) }

and under that Reload Notification I do a updateCollections()

Swift 3:

DispatchQueue.global(attributes: .qosBackground).async {
    print("This is run on the background queue")

    DispatchQueue.main.async {
        print("This is run on the main queue, after the previous code in outer block")
    }
}

Solution

  • Don't initialize PublicationOverviewController() again because it will give you new instance of that view and you would not get your referenced collectionView. Also you will get all UI controls nil before viewDidLoad: get calls.

    Example:

    let *controller : PublicationOverviewController = PublicationOverviewController()
    // controller.collectionView will be nil here because view hasn't loaded yet
    

    What you can do Post a notification in applicationDidBecomeActive: method and observe it in PublicationOverviewController then reload your collectionView in respective method.