Welcome to the Treehouse Community
Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.
Looking to learn something new?
Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.
Start your free trial
Henrik Brüntrup
5,485 PointsUpdating Labels with value from CloudKit
Hello everybody,
UPDATE: Solution in the answers below!
I have been learning Swift for a couple of weeks now and finally decided to try myself at an own app. So far so good. However, earlier today I ran into the following issue:
I am trying to update two labels on a DetailViewController with values from CloudKit. The code I am using is functional (updating both labels with the correct values). However, it throws the following error:
"This application is modifying the autolayout engine from a background thread, which can lead to engine corruption and weird crashes. This will cause an exception in a future release."
After hours of search and basically redoing my whole code (mind you, I am still a beginner), I found the issue being the labels being updated with their values in the "fetchRecords - function", which apparently is a background thread. The CKRecordID is being passed in from the MasterViewController, depending on which cell the user selected.
This is my code:
import UIKit
import CloudKit
class DetailNews: UIViewController {
@IBOutlet weak var detailNewsTitle: UILabel!
@IBOutlet weak var detailNewsFullBody: UILabel!
@IBAction func backDetailNews() {
}
override func viewDidLoad() {
super.viewDidLoad()
dispatch_async(dispatch_get_main_queue(), {
self.fetchRecords()
})
}
func fetchRecords() {
dispatch_async(dispatch_get_main_queue()) {
CKContainer.defaultContainer().publicCloudDatabase.fetchRecordWithID(number) { [unowned self] (record, error) -> Void in
if error == nil {
self.detailNewsTitle.text = (record!.valueForKey("title")) as! String
self.detailNewsFullBody.text = (record!.valueForKey("fullbody")) as! String
// print("\(record!.valueForKey("fullbody"))")
} else {
let ac = UIAlertController(title: "Fetch failed", message: "There was a problem fetching the list of News; please try again: \(error!.localizedDescription)", preferredStyle: .Alert)
ac.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil))
self.presentViewController(ac, animated: false, completion: nil)
}
}
}
}
}
I was unable to extract the (record!.valueForKey("title")) & (record!.valueForKey("fullbody")) out of the closure to use in another function, which I can then call on the main thread. Printing either record in the closure leads to an immediate response, without the error message.
So if anybody has a solution and could provide me with a couple of lines of code and a short explanation, I would be very grateful. I am quite certain its not very difficult. Having looked at it for hours now, I try my luck with you.
Thanks in advance.
Best regards Henrik
1 Answer
Henrik Brüntrup
5,485 PointsHello everybody,
I finally figured it out. Updating the question in case somebody else runs into the same issue as I did.
Calling "dispatch_async(dispatch_get_main_queue()) {" directly on the updating of the UILabels did the trick. Previously I had called it on the whole closure.
Functional code:
import UIKit
import CloudKit
class DetailNews: UIViewController {
@IBOutlet weak var detailNewsTitle: UILabel!
@IBOutlet weak var detailNewsFullBody: UILabel!
@IBAction func backDetailNews() {
}
override func viewDidLoad() {
super.viewDidLoad()
fetchRecords()
}
func fetchRecords() {
CKContainer.defaultContainer().publicCloudDatabase.fetchRecordWithID(number) { (record, error) -> Void in
if error == nil {
dispatch_async(dispatch_get_main_queue()) {
self.detailNewsTitle.text = (record!.valueForKey("title")) as! String
self.detailNewsFullBody.text = (record!.valueForKey("fullbody")) as! String
}
} else {
let ac = UIAlertController(title: "Fetch failed", message: "There was a problem fetching the list of News; please try again: \(error!.localizedDescription)", preferredStyle: .Alert)
ac.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil))
self.presentViewController(ac, animated: false, completion: nil)
}
}
}
}
Best regards Henrik