Tableview crashes when I try to get images from firebase - swift

I get this error.Ive also provided a screenshot with a more detailed look at the error I've never seen this before
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Modifications to the layout engine must not be performed from a background thread after it has been accessed from the main thread.'
//MARK: EXPLANATION (your passing item in down below because this allows you to call whatever is in the item class)
func generateCellForITEMS(item : Item){
nameLabel.text = item.name
descriptionLabel.text = item.description
priceLabel.text = convertCurrency(item.price )
priceLabel.adjustsFontSizeToFitWidth = true // this will adjust the font of the label sizse to helpt it fit even with a large price
if item.imageLinks != nil && item.imageLinks.count > 0 {
downLoadImages(imageUrls: [item.imageLinks.first!]) { (images) in
self.ItemImageView.image = images.first as? UIImage
}
}
}

You need to update the UI from the main thread as the error suggests:
downLoadImages(imageUrls: [item.imageLinks.first!]) { (images) in
DispatchQueue.main.async {
self.ItemImageView.image = images.first as? UIImage
}
}

Related

Detect URL Change in ContentView.swift iOS app Swift

Trying to detect change event of the URL being navigated to somewhere else when the user changes the currently viewed webpage. Here is my code.
struct WebView: UIViewRepresentable {
var view = WKWebView()
init() {
print("initialized")
}
func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
if let newValue = change?[.newKey] as? Int, let oldValue = change?[.oldKey] as? Int, newValue != oldValue {
//Value Changed
print(change?[.newKey])
}else{
//Value not Changed
print(change?[.oldKey])
}
}
func makeUIView(context: UIViewRepresentableContext<WebView>) -> WKWebView {
while(!authenticated) {
}
DispatchQueue.main.async {
var url = URL(string:"https://www.runixcoin.com/")!
if (faceIdSuccess) {
url = URL(string:"https://www.runixcoin.com/dashboard/auth-login-basic.php?face_id=true")!
}
else {
url = URL(string:"https://www.runixcoin.com/dashboard/auth-login-basic.php?return_url=https://www.runixcoin.com/dashboard/dashboard-4.php")!
}
let request = URLRequest(url: url)
view.load(request)
view.addObserver(view, forKeyPath: "URL", options: .new, context: nil)
}
return view
}
func updateUIView(_ uiView: WKWebView, context: UIViewRepresentableContext<WebView>) {
}
typealias UIViewType = WKWebView
}
The observeValue function is getting called and printing the results I want when the user navigates away from the current page, however the app is crashing... I want to stop the app from crashing. I even tried using the removeObserver code and it either prevented the function from being called or would still crash... What do I need to do?
2022-04-04 18:21:55.489370-0400 Runixcoin[6797:1762100] [error] warning: View context accessed for persistent container runixcoin with no stores loaded
CoreData: warning: View context accessed for persistent container runixcoin with no stores loaded
initialized
Logged in with FaceID
2022-04-04 18:21:58.914721-0400 Runixcoin[6797:1762100] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '<WKWebView: 0x106025e00; frame = (0 0; 428 796); layer = <CALayer: 0x283bb1b80>>: An -observeValueForKeyPath:ofObject:change:context: message was received but not handled.
Key path: URL
Observed object: <WKWebView: 0x106025e00; frame = (0 0; 428 796); layer = <CALayer: 0x283bb1b80>>
Change: {
kind = 1;
new = "https://www.runixcoin.com/dashboard/auth-password-basic.php";
}
Context: 0x0'
*** First throw call stack:
(0x1811e50fc 0x199a35d64 0x182ad0adc 0x1829a30cc 0x18298ed90 0x1829c8508 0x18fd95468 0x18fd951c0 0x18fe1b82c 0x18fe1b380 0x1901932c0 0x18fa41114 0x18fe69850 0x18fa1ebe4 0x18fa1e1f4 0x18d2d6e44 0x18d2d7fb4 0x1812070d0 0x181217d90 0x181152098 0x1811578a4 0x18116b468 0x19cd0f38c 0x183b0e5d0 0x18388cf74 0x188e2a314 0x188d59508 0x188d3acb8 0x104f080d4 0x104f0818c 0x10510daa4)
libc++abi: terminating with uncaught exception of type NSException
dyld4 config: DYLD_LIBRARY_PATH=/usr/lib/system/introspection DYLD_INSERT_LIBRARIES=/Developer/usr/lib/libBacktraceRecording.dylib:/Developer/usr/lib/libMainThreadChecker.dylib:/Developer/Library/PrivateFrameworks/DTDDISupport.framework/libViewDebuggerSupport.dylib
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '<WKWebView: 0x106025e00; frame = (0 0; 428 796); layer = <CALayer: 0x283bb1b80>>: An -observeValueForKeyPath:ofObject:change:context: message was received but not handled.
Key path: URL
Observed object: <WKWebView: 0x106025e00; frame = (0 0; 428 796); layer = <CALayer: 0x283bb1b80>>
Change: {
kind = 1;
new = "https://www.runixcoin.com/dashboard/auth-password-basic.php";
}
Context: 0x0'
terminating with uncaught exception of type NSException
(lldb)

Error calling on managedobject with EXC_BAD_ACCESS

I searched a lot for this error and was not able to find a solution.
I have my managedObject:
lazy var managedObjectContext: NSManagedObjectContext = {
let coordinator = self.persistentStoreCoordinator
var managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = coordinator
return managedObjectContext
}()
and I have a Singleton class
lazy var managedObjectContext : NSManagedObjectContext? = {
if let appDelegate = UIApplication.sharedApplication().delegate as? AppDelegate {
return appDelegate.managedObjectContext
}
return nil
}()
I have a method which is called lot of time whenever I update / delete / load data from database
class func LoadDatabase(tableName: String, predicateString: String, predicateIntValue: Int) -> [AnyObject]? {
do {
let managedObjectContext = Singleton.sharedInstance.managedObjectContext
if managedObjectContext == nil {
return nil
}
let fReq: NSFetchRequest = NSFetchRequest(entityName: tableName)
if predicateString != "" {
fReq.predicate = NSPredicate(format: predicateString, predicateIntValue)
}
var result: [AnyObject]
do {
if managedObjectContext == nil {
return nil
}
result = try managedObjectContext!.executeFetchRequest(fReq)
} catch {
return nil
}
// update exist event
if
result.count != 0 {
return result
}
} catch let error1 as NSError {
print("No values to load for table: \(error1)")
}
return nil
}
I think I have problem on concurrence access. When the app starts it stars asynchronous requests to update the database. But I also call the method LoadDatabase from varius controller. Once the asynchronous call done with updating the datable, it call another method to delete datas which are not more in remote database.
This is what I get. This does not happens always.
I tried to change to MainQueueConcurrency but now I am getting also this error:
2016-03-20 21:27:01.371 Tamil League[1070:245784] CoreData: error: Serious application error. Exception was caught during Core Data change processing. This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification. -[__NSCFSet addObject:]: attempt to insert nil with userInfo (null)
2016-03-20 21:27:01.371 Tamil League[1070:245784] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFSet addObject:]: attempt to insert nil'
*** First throw call stack:
(0x24a9e2eb 0x2426adff 0x24a9e231 0x249e7fe5 0x261eb683 0x261ea063 0x261e2dfb 0x24a60b21 0x24a5ee17 0x24a5f255 0x249b1bb9 0x249b19ad 0x25c2baf9 0x28c9dfb5 0xa6d5c 0x24664873)
libc++abi.dylib: terminating with uncaught exception of type NSException
Does anyone know how to solve this?

"Realm accessed from incorrect thread" exception thrown from dispatch_group_notify block

I have read the other answers but couldn't find a suitable solution.
I have a product which is uploaded to server only if all IMAGES belonging to that product have finished uploading. The product's details (along with the images) are filled on view controller 1 and then he is taken to the next screen (view controller 2), regardless of whether all images have finished uploading or not. MY VC1 completes the product upload process like this.
let areAllImagesUploaded = RetailProductsService.sharedInstance.checkProductImageDependency(realm!, uuid: retailProduct.getClientId())
if areAllImagesUploaded {
uploadProductToServer(retailProduct)
} else {
do {
try realm = Realm()
RetailProductsService.sharedInstance.updateSyncStatusForSellerProduct(realm!, clientId: retailProduct.getClientId(), syncStatus: ProductSyncStatus.SYNC_FAILED)
let groupT = dispatch_group_create()
for sellerSKU in retailProduct.sellersSKUs {
for productImage in sellerSKU.productImages {
dispatch_group_enter(groupT)
let imageUploadInfo = ImageUploadInfo(productId: retailProduct.getClientId(), imageId: sellerSKU.getId(),imageData: productImage.imageData, uploadURL: ServerConfig.RETAIL_SERVER_UPLOAD_FILE_URL)
ImageUploadManager.sharedInstance.queueImageForUpload(imageUploadInfo, completion: { (success, error) -> Void in
dispatch_group_leave(groupT)
})
dispatch_group_notify(groupT, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), { () -> Void in
//self.uploadProduct(retailProduct)
self.uploadProductToServer(retailProduct) // Fails here
})
}
}
} catch {
print("Error in saving product.")
}
}
I have marked the line on which I get this error. My app has moved to the next view controller while this function in view controller 1 continues uploading images and as soon as all images associated the product are uploaded to server, it tries to upload the product. However it fails with this exception.
Terminating app due to uncaught exception 'RLMException', reason: 'Realm accessed from incorrect thread'
Please help!
Realm Objects cannot be accessed from different threads. Your retailProduct is created or fetched from Realm storage by threadX by than you switch to some other thread (threadY) by invoking dispatch_group_notify. To fix the exception you might want to do something like this:
I assume that your retailProduct object is of type RetailProduct and has an id property used as primary key in Realm storage. Of course you can fetch you retailProduct by using another query than that.
let retailProductId = retailProduct.id
dispatch_group_notify(groupT, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), { () -> Void in
// threadY executing this lines
if let retailProduct = realm.objectForPrimaryKey(RetailProduct.self, key: retailProductId){
self.uploadProductToServer(retailProduct)
}
})
We need to understand the fact Realm Objects cannot be accessed from different threads. What does this means and how to workout this issue.
First, realm objects cannot be access from different thread means, one instance of thread defined in one thread cannot be access from different thread. What we should do actually is we need to have different instance of realm instance for each thread.
For eg. let's look at following e.g. where we insert 50 records in database asynchronously in background thread upon button click and we add notification block in main thread to update the no of people in count label. Each thread (main and background ) have its own instance of realm object to access Realm Database. Because Realm Database is thread safe.
class Person: Object {
dynamic var name = ""
convenience init(_ name: String) {
self.init()
self.name = name
}
}
override func viewDidAppear(_ animated: Bool) {
let realmMain = try! Realm ()
self.people = realmMain.objects(Person.self)
self.notification = self.people?.addNotificationBlock{ [weak self] changes in
print("UI update needed")
guard let countLabel = self?.countLabel else {
return
}
countLabel.text = "Total People: \(String(describing: self?.people?.count))"
}
}
#IBAction func addHandler(_ sender: Any) {
print(#function)
let backgroundQueue = DispatchQueue(label: "com.app.queue",
qos: .background,
target: nil)
backgroundQueue.async {
print("Dispatched to background queue")
let realm = try! Realm()
try! realm.write {
for i in 1..<50 {
let name = String(format: "rajan-%d", i)
//print(#function, name)
realm.add(Person(name))
}
}
}
}

UITableViewCell asynchronously loading images issue - Swift

In my app, I built my own asynchronous image loading class. I pass in a object, then it checks if the cache (NSCache) has the image, if not it will then check the file system if the image is saved already. If the image is not saved already, it will then download the image in the background (NSOperations help).
This works great so far, but I have ran into a few small issues with the table view loading the images.
First off, this is the function I use to set up the table view cell fromtableView(tableView:, willDisplayCell:, forRowAtIndexPath:)
func configureCell(cell: ShowTableViewCell, indexPath: NSIndexPath) {
// Configure cell
if let show = dataSource.showFromIndexPath(indexPath) {
ImageManager.sharedManager.getImageForShow(show, completionHandler: { (image) -> Void in
if self.indexPathsForFadedInImages.indexOf(indexPath) == nil {
self.indexPathsForFadedInImages.append(indexPath)
if let fetchCell = self.tableView.cellForRowAtIndexPath(indexPath) as? ShowTableViewCell {
func fadeInImage() {
// Fade in image
fetchCell.backgroundImageView!.alpha = 0.0
fetchCell.backgroundImage = image
UIView.animateWithDuration(showImageAnimationSpeed, animations: { () -> Void in
fetchCell.backgroundImageView!.alpha = 1.0
})
}
if #available(iOS 9, *) {
if NSProcessInfo.processInfo().lowPowerModeEnabled {
fetchCell.backgroundImage = image
}
else {
fadeInImage()
}
}
else {
fadeInImage()
}
}
else {
// Issues are here
}
}
else {
// Set image
cell.backgroundImage = image
}
})
...
}
Where "// Issues are here" comment is, that is where I run into multiple issues.
So far, I have not figured out another way to validate that the image belongs to the cell for sure where "// Issues are here" is. If I add
cell.backgroundImage = image
there, then it fixes the issue where sometimes the image will not display on the table view cell. So far the only cause I have found for this is that the image is being returned faster than I can return the table view cell so that is why the table view says there is not a cell at that index path.
But if I add that code there, then I run into another issue! Cells will display the wrong images and then it lags down the app and the image will constantly switch, or even just stay on the wrong image.
I have checked that it runs on the main thread, image downloading and caching is all fine. It just has to do that the table is saying there is no cell at that index path, and I have tried getting an indexPath for the cell which returns also nil.
A semi-solution to this problem is called tableView.reloadData() in viewWillAppear/viewDidAppear. This will fix the issue, but then I lose the animation for table view cells on screen.
EDIT:
If I pass the image view into getImageForShow() and set it directly it will fix this issue, but that is less ideal design of code. The image view obviously exists, the cell exists, but for some reason it doesn't want to work every time.
Table views reuse cells to save memory, which can cause problems with any async routines that need to be performed to display the cell's data (like loading an image). If the cell is supposed to be displaying different data when the async operation completes, the app can suddenly go into an inconsistent display state.
To get around this, I recommend adding a generation property to your cells, and checking that property when the async operation completes:
protocol MyImageManager {
static var sharedManager: MyImageManager { get }
func getImageForUrl(url: String, completion: (UIImage?, NSError?) -> Void)
}
struct MyCellData {
let url: String
}
class MyTableViewCell: UITableViewCell {
// The generation will tell us which iteration of the cell we're working with
var generation: Int = 0
override func prepareForReuse() {
super.prepareForReuse()
// Increment the generation when the cell is recycled
self.generation++
self.data = nil
}
var data: MyCellData? {
didSet {
// Reset the display state
self.imageView?.image = nil
self.imageView?.alpha = 0
if let data = self.data {
// Remember what generation the cell is on
var generation = self.generation
// In case the image retrieval takes a long time and the cell should be destroyed because the user navigates away, make a weak reference
weak var wcell = self
// Retrieve the image from the server (or from the local cache)
MyImageManager.sharedManager.getImageForUrl(data.url, completion: { (image, error) -> Void in
if let error = error {
println("There was a problem fetching the image")
} else if let cell = wcell, image = image where cell.generation == generation {
// Make sure that UI updates happen on main thread
dispatch_async(dispatch_get_main_queue(), { () -> Void in
// Only update the cell if the generation value matches what it was prior to fetching the image
cell.imageView?.image = image
cell.imageView?.alpha = 0
UIView.animateWithDuration(0.25, animations: { () -> Void in
cell.imageView?.alpha = 1
})
})
}
})
}
}
}
}
class MyTableViewController: UITableViewController {
var rows: [MyCellData] = []
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("Identifier") as! MyTableViewCell
cell.data = self.rows[indexPath.row]
return cell
}
}
A couple other notes:
Don't forget to do your display updates on the main thread. Updating on a network activity thread can cause the display to change at a seemingly random time (or never)
Be sure to weakly reference the cell (or any other UI elements) when you're performing an async operation in case the UI should be destroyed before the async op completes.

Parse NSInternalInconsistencyException raises with PFImageView

I'm experiencing an odd behaviour of Parse right now.
My App worked fine for weeks of development and never raised a 'NSInternalInconsistencyException' since project setup.
But I just implemented a PFImageView in my ProfileViewController (second VC in UITabBarController, therefore only showing when user is logged in) and my App keeps crashing with this error:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'You have to call setApplicationId:clientKey: on Parse to configure Parse.'
Of course I checked my Parse setup in func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool:
Parse.setApplicationId("myApplicationID", clientKey: "myClientKey")
Parse.enableLocalDatastore()
// This is where I used to activate Parse, but SO and Parse forums kept
// saying you should try to do this at the very beginning
ParseCrashReporting.enable()
PFAnalytics.trackAppOpenedWithLaunchOptionsInBackground(launchOptions, block: nil)
In my ProfileViewController this is where I load my image from Parse:
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
userImageView.file = user["profilePicture"] as PFFile
userImageView.loadInBackground { (image, error) -> Void in
if error != nil {
JSSAlertView().danger(self, title: "Loading Image Failed", text: "Please check your connection")
} else {
self.userImageView.image = image
}
}
}
Setting the instance variable of image:
var image: UIImage? {
// If User picked a new image, automatically update imageView
didSet {
userImageView.image = image
// Convert image to PNG and save in in Parse-Cloud
let imageData = UIImagePNGRepresentation(image)
let imageFile = PFFile(name: "profilePicture.png", data: imageData)
// Attach this file to our user
user["profilePicture"] = imageFile
user.saveInBackgroundWithBlock { (success, error) -> Void in
if error != nil {
JSSAlertView().danger(self, title: "Failed To Save Image", text: "Please try again saving your image!")
}
}
userImageView.file = user["profilePicture"] as PFFile
}
}
I have no clue what kind of connection could be between these things, but I didn't change anything else in the whole project.
Regards,
Ok, finally after hours of trial and error I found the solution for this problem.
You should never ever execute parse calls in the header of view controllers, as these calls could be executed before the API registration.
For example:
class ViewController: UIViewController {
// this will crash the app as above!
var user: PFUser! = PFUser.currentUser()
override func viewDidLoad() {
super.viewDidLoad()
// initialize the user here. This will work just fine
user = PFUser.currentUser()
}
}
Hope this saves you from having the same errors!