Getting a "This Application Is Modifying the Autolayout Engine from a Background Thread" Error

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:

  1. Add to project and compile this file without ARC
  2. Move PSPDFAssert definition to the first of the file
  3. Comment calling of PSPDFLogError as it is not defined
  4. 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



Leave a reply



Submit