Modifications to the layout engine must not be performed from a background thread after it has been accessed from the main thread
Your network fetch code is almost right - you are reloading the table on the main queue but not stopping the activity indicator.
You just need to move that line inside the closure:
let downloadedData_user = try decoder.decode(Top_us.self, from: data)
self.Top_Search = downloadedData_user.users
DispatchQueue.main.async {
self.tableView.reloadData()
self.Indicator.stopAnimating()
}
Note that by convention, properties should start with a lower case letter while classes should start with an upper case letter. Both should use camelCase, so Top_Search
should be topSearch
, Top_us
should be TopUsers
and Indicator
should be indicator
.
reason: 'Modifications to the layout engine must not be performed from a background thread after it has been accessed from the main thread.'
The issue is caused because the alert is being shown from the background thread as the completion handler in getNotificationSettings is being run in the background thread. To prevent this crash present alert in main thread and do the following changes in your code.
func checkNotificationAllowed(){
let data = defaults.object(forKey:"mute") as? Bool
print(data!)
if (data != nil) == true {
let current = UNUserNotificationCenter.current()
current.getNotificationSettings(completionHandler: { permission in
switch permission.authorizationStatus {
case .authorized:
print("User granted permission for notification")
case .denied:
print("User denied notification permission")
DispatchQueue.main.async {[weak self] in
guard let weakSelf = self else {return}
let alert = UIAlertController(title: "Turn On Notifications".localized(), message: "Notifications are disabled. Please turn on app notifications to get device alerts.".localized(), preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Don't Allow".localized(), style: .cancel, handler: { action in
weakSelf.dismiss(animated: true, completion: nil)
}))
alert.addAction(UIAlertAction(title: "Allow".localized(), style: .destructive, handler: { action in
weakSelf.dismiss(animated: true, completion: nil)
}))
weakSelf.present(alert, animated: true, completion: nil)
}
case .notDetermined:
print("Notification permission haven't been asked yet")
case .provisional:
// @available(iOS 12.0, *)
print("The application is authorized to post non-interruptive user notifications.")
case .ephemeral:
// @available(iOS 14.0, *)
print("The application is temporarily authorized to post notifications. Only available to app clips.")
@unknown default:
print("Unknow Status")
}
})
}
}
Modifications to the layout engine must not be performed from a background thread after it has been accessed from the main thread
I finally found out the problem after taking another look at the stack trace. The problem was that I was changing the value
property of a UISlider
instance on a background thread.
But nowhere does it state that you have to change it on the main thread! (Thanks, Apple) Apparently, it seems like UISlider
implements the value
's setter and forces a layout or something similar.
Modifications to the layout engine must not be performed from a background thread after it has been accessed from the main thread in swift
You've probably missed a few spots where you're trying to present an alert when errors are thrown. Why don't you just enter the main queue right after the data request is complete.
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
DispatchQueue.main.async {
if error == nil {
//...
}
}
})
Error in background/main thread using API
You need to present your UIAlertController
on the main thread because the completion callback of URLSession.shared.dataTask(with:completionHandler:)
runs on a background thread.
DispatchQueue.main.async {
let alertController = UIAlertController(title: NSLocalizedString("Cancel successful", comment: "Cancel successful"), message: "", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: NSLocalizableOk, style: .cancel, handler: nil))
self.present(alertController, animated: true, completion: nil)
}
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.
Related Topics
What Is Correct Format of Bundle Identifier in iOS
How to Test for the Class of a Variable in Swift
Swift: Type Must Implement Protocol and Be a Subclass of Given Class
Uislider Setmaximumtracktintcolor in iOS 7.1
Swift: Return Array of Type Self
Uisearchcontroller Not Redisplaying Navigation Bar on Rotate
How to Generate All Possible Combinations
"Can't Find Model for Source Store" Occurring During iPhone "Automatic Lightweight Migration"
Static Linking with Swift, Xcode6-Beta
Xcode 8 Swift 3 Uitableview Space Between Cells
Add a Done Button Within a Pop-Up Datepickerview in Swift
How to Do Something Before Unwind Segue Action
Override Uiappearance Property for Mfmailcomposeviewcontroller
iOS 8+ Framework with Nested Embedded Framework
iOS 10 Heading Arrow for Mkuserlocation Dot
How to Hide the Tabbar When Navigate with Navigationlink in Swiftui
Objective-C Check If Subviews of Rotated Uiviews Intersect
How to Make Uipageviewcontroller Reuse Controller Like Tableview Reuse Cell