Getting a This application is modifying the autolayout engine from a background thread error?
It needs to be placed inside a different thread that allows the UI to update as soon as execution of thread function completes:
Modern Swift:
DispatchQueue.main.async {
// Update UI
}
Older versions of Swift, pre Swift 3.
dispatch_async(dispatch_get_main_queue(){
// code here
})
Objective-C:
dispatch_async(dispatch_get_main_queue(), ^{
// code here
});
“This application is modifying the autolayout engine from a background thread after the engine was accessed from the main thread” in Swift
All code that relates to UI needs to run on main thread. In your case it's a segue
DispatchQueue.main.async { [weak self] in
self?.performSegue(withIdentifier: "inviteFriends", sender: nil)
}
iOS9 - This application is modifying the autolayout engine from a background thread -- where?
This code PSPDFUIKitMainThreadGuard causes assertions on UIKit access outside the main thread
Steps to use:
- Add to project and compile this file without ARC
- Move
PSPDFAssert
definition to the first of the file - Comment calling of
PSPDFLogError
as it is not defined - import
<UIKit/UIKit.h>
Your app will crash and stop with any attemption to modify any UI element from background thread
For swift use the following code: NBUIKitMainThreadGuard
This application is modifying the autolayout engine from a background thread, which can lead to engine corruption and weird crashes
Do not change UI from anything but the main thread. While it may appear to work on some OS or devices and not others, it is bound to make your application unstable, and crash unpredictably.
If you must respond to a notification, which can happen in the background, then ensure UIKit
invocation takes place on the main thread.
You at least have these 2 options:
Asynchronous Dispatch
Use GCD
(Grand Central Dispatch) if your observer can be notified on any thread. You can listen and do work from any thread, and encapsulate UI changes in a dispatch_async
:
dispatch_async(dispatch_get_main_queue()) {
// Do UI stuff here
}
When to use GCD
? When you do not control who sends the notification. It can be the OS, a Cocoapod, embedded libraries, etc. Using GCD
will woke anytime, every time. Downside: You find yourself re-scheduling the work.
Listen on Main Thread
Conveniently, you can specify on which thread you want the observer to be notified, at the time you are registering for notifications, using the queue
parameter:
addObserverForName:@"notification"
object:nil
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification *note){
// Do UI stuff here
}
When to observe on main thread? When you are both registering and registered. Bu the time you respond to the notification, you are already where you need to be.
Post Notification On Main Thread
[self performSelectorOnMainThread:@selector(postNotification:) withObject:notification waitUntilDone:NO];
Hybrid solution which does not guarantee that the observer is only invoked from said method. It allows for lighter observer, at the cost less robust design. Only mentioned here as a solution you should probably avoid.
Can not find origin of This application is modifying the autolayout engine from a background thread
So I think I found where the error came from.
When I received the response from my API with the asset list, I create a MapInfoModel like this:
private func generateMapInfo(assets: [APIAssetModel]) -> MapInfoModel {
var markers = [ClusterMarkerItem]()
var locations = [CLLocationCoordinate2D]()
assets.forEach({ asset in
if nil != asset.lat && nil != asset.lng {
if asset.lat! > -85 && asset.lat! < 85 {
let latlng = CLLocationCoordinate2D(latitude: asset.lat!, longitude: asset.lng!)
for index in 0..<(asset.productsCount ?? 0) {
let marker = GMSMarker(position: latlng)
marker.title = "\(asset.id)_\(index)"
markers.append(ClusterMarkerItem(position: latlng,
marker: marker,
asset: asset,
prod_position: index))
}
locations.append(latlng)
}
}
})
return MapInfoModel(assetList: assets, clusterList: markers, locationList: locations)
}
This method was called from a background thread and, as you can see, I was instantiating a GMSMarker. This doesn't rise any error/warning and the map is loaded and showing the clusters well. But, here is the thing, I suspect that after loading the cluster, Google Maps SDK does something with the GMSMarkers loaded in it, and I suspect that, that action is been called from the thread where GMSMarker was created, so, if it was created from a background thread, XCode will show the error:
This application is modifying the autolayout engine from a background thread
I'm just guessing but, I am waiting for Google to confirm this. So the solution was to instantiate GMSMarker from Main thread (in this case I use DispatchQueue.main.async{}).
UPDATE
Google just email me and confirm it. This is what they said:
Our specialists have confirmed that all Marker creation and updates
should be happening on the main thread.This is because all drawing and rendering in iOS has to happen on the
main thread, and updates done to the markers translates into that
directly.We will be working on including this information in our official
documentation for future references.
This application is modifying the autolayout engine from a background thread, which can lead to engine corruption
The dataTaskWithRequest
call runs in the background and then calls your completion handler from the same thread. Anything that updates the UI should run on the main thread, so all of your current handler code should be within a dispatch_async
back onto the main queue:
dispatch_async(dispatch_get_main_queue()) {
// Do stuff to UI
}
Swift 3:
DispatchQueue.main.async() {
// Do stuff to UI
}
Therefore, ideally all the code you currently have within if error == nil
should be off in another function, say called handleRequest, so your current code becomes:
session.dataTaskWithRequest(request, completionHandler: {(data, response, error) in
if error == nil {
dispatch_async(dispatch_get_main_queue(), {
self.handleRequest(...)I
})
}
Application is modifying the autolayout engine from a background thread Error
You had better change UI only in the main thread. For swift3 refer to this post,
for more swift<2, 3 objective-c.
Getting the following error: This application is modifying the autolayout engine from a background thread
In iOS all UI code must run on the main thread. You are dispatching to a background thread in order to perform your update check, which is fine, but then trying to update the UI from that same background thread, which is causing the error you are seeing. The solution is to add another dispatch_async
inside the first one, wrapping the call to self.showAlertForUpdate()
, but dispatching to the main queue (dispatch_get_main_queue(...
) instead.
How to solve: This application is modifying the autolayout engine from a background thread
Main problem with "This application is modifying the autolayout engine from a background thread" is that it seem to be logged a long time after the actual problem occurs, this can make it very hard to troubleshoot.
I managed to solve the issue by creating three symbolic breakpoints.
Debug > Breakpoints > Create Symbolic Breakpoint...
Breakpoint 1:
Symbol:
-[UIView setNeedsLayout]
Condition:
!(BOOL)[NSThread isMainThread]
Breakpoint 2:
Symbol:
-[UIView layoutIfNeeded]
Condition:
!(BOOL)[NSThread isMainThread]
Breakpoint 3:
Symbol:
-[UIView updateConstraintsIfNeeded]
Condition:
!(BOOL)[NSThread isMainThread]
With these breakpoints, you can easily get a break on the actual line where you incorrectly call UI methods on non-main thread.
Related Topics
How to Convert Double to Int in Swift
What Does "Fatal Error: Unexpectedly Found Nil While Unwrapping an Optional Value" Mean
Protocol Doesn't Conform to Itself
Using a Dispatch_Once Singleton Model in Swift
What Is an Optional Value in Swift
Get Nth Character of a String in Swift Programming Language
Read and Write a String from Text File
Firestore: How to Get Random Documents in a Collection
Round Trip Swift Number Types To/From Data
How Does String Substring Work in Swift
Difference Between a Weak Reference and an Unowned Reference
Flatten an Array of Arrays in Swift
How to Generate a Random Number in a Range (10...20) Using Swift
Swift 3 For Loop With Increment