Post

Replies

Boosts

Views

Created

The following simple function will cause Xcode 12E262 to have "Abort: trap 6"
The following simple function will cause Xcode 12E262 to have "Abort: trap 6" during compilation. import UIKit import CoreData class ViewController: UIViewController {     func xyz() {         let container = NSPersistentContainer(name: "xyz")         let batchUpdateRequest = NSBatchUpdateRequest(entityName: "xyz")         let batchUpdateResult = try! container.viewContext.execute(batchUpdateRequest) as? NSBatchUpdateResult         guard let batchUpdateResult = batchUpdateResult else { return }     }     override func viewDidLoad() {         super.viewDidLoad()         // Do any additional setup after loading the view.     } } We will not observe "Abort: trap 6", if under Build Settings, we are using "Optimize for Speed" in Debug, instead of "No Optimization" We can also avoid "Abort: trap 6", if we change the following code guard let batchUpdateResult = batchUpdateResult else { return } to guard let batchUpdateResult2 = batchUpdateResult else { return } May I know, why is it so? A simpler code example to reproduce problem, without CoreData would be import UIKit class ViewController: UIViewController {     func getAny() throws -> Any? {         return nil     }     func xyz() {         let name = try! getAny() as? UIViewController         guard let name = name else { return }     }     override func viewDidLoad() {         super.viewDidLoad()         // Do any additional setup after loading the view.     } }
2
0
966
Jun ’21
CoreData common practice - Do you usually have a struct based data class, as the bridge between your UI layer, and the CoreData data layer?
I was wondering, when you use CoreData, do you usually create another equivalent struct based data class, to compliment the NSManagedObject? The struct based data class, will act as the bridge, between UI layer, and CoreData data layer. For instance, I have the following CoreData model data class. @objc(NSTabInfo) public class NSTabInfo: NSManagedObject { } extension NSTabInfo { @nonobjc public class func fetchRequest() -> NSFetchRequest<NSTabInfo> { return NSFetchRequest<NSTabInfo>(entityName: "NSTabInfo") } @NSManaged public var colorIndex: Int32 @NSManaged public var customColor: Int32 @NSManaged public var name: String? @NSManaged public var order: Int64 @NSManaged public var typeValue: Int32 @NSManaged public var syncedTimestamp: Int64 @NSManaged public var uuid: UUID } extension NSTabInfo : Identifiable { } We need a UI, to represent such model data object. Initially, we have the following UI class class TabInfoCell: UICollectionViewCell { private var tabInfo: NSTabInfo? func update(_ tabInfo: NSTabInfo) { self.tabInfo = tabInfo } } But, we just feel not comfortable with such design. Letting TabInfoCell (UI class) to hold NSTabInfo (CoreData model data class) doesn't feel right, as NSTabInfo contains CoreData's context. I do not feel comfortable, to expose CoreData's context to an UI. Will holding a class reference to NSTabInfo in UI, affect CoreData memory allocation/ deallocation strategy? Using weak reference might solve the issue. But, what should the UI do when the weak reference become nil? With such concern, We have the following design struct TabInfo { enum Kind: Int { case All = 0 case Calendar case Custom case Settings } let kind: Kind var name: String? var colorIndex: Int var customColor: Int var order: Int var syncedTimestamp: Int64 var uuid: UUID } @objc(NSTabInfo) public class NSTabInfo: NSManagedObject { convenience init(context: NSManagedObjectContext, tabInfo: TabInfo) { self.init(context: context) self.colorIndex = Int32(tabInfo.colorIndex) self.customColor = Int32(tabInfo.customColor) self.name = tabInfo.name self.order = Int64(tabInfo.order) self.typeValue = Int32(tabInfo.kind.rawValue) self.syncedTimestamp = tabInfo.syncedTimestamp self.uuid = tabInfo.uuid } func toTabInfo() -> TabInfo { return TabInfo( kind: TabInfo.Kind(rawValue: Int(self.typeValue))!, name: self.name, colorIndex: Int(self.colorIndex), customColor: Int(self.customColor), order: Int(self.order), syncedTimestamp: self.syncedTimestamp, uuid: uuid ) } } @nonobjc public class func fetchRequest() -> NSFetchRequest<NSTabInfo> { return NSFetchRequest<NSTabInfo>(entityName: "NSTabInfo") } @NSManaged public var colorIndex: Int32 @NSManaged public var customColor: Int32 @NSManaged public var name: String? @NSManaged public var order: Int64 @NSManaged public var typeValue: Int32 @NSManaged public var syncedTimestamp: Int64 @NSManaged public var uuid: UUID } extension NSTabInfo : Identifiable { } Then, in our UI class, it looks like class TabInfoCell: UICollectionViewCell { private var tabInfo: TabInfo? func update(_ tabInfo: TabInfo) { self.tabInfo = tabInfo } } I was wondering, does such design philosophy make sense? Do you usually have a struct based data class, as the bridge between your UI layer, and the CoreData data layer? Thanks.
0
0
709
Jun ’21
Have you ever encountered NSFetchedResultsController not able to properly "section" based on sectionNameKeyPath, randomly?
This problem has buzzed me quite a while. So far, I still haven't founded a good solution. Currently, I have an entity with a Bool column named pinned. I use it as the sectionNameKeyPath for NSFetchedResultsController. So, my UICollectionView will always have 1 sections (All pinned = true, or All pinned = false), or 2 sections (Some pinned = true, and some pinned = false) When I toggle the pinned value from true to false, or false to true, I expect FRC shall fire a "move" callback. (Due to item has moved to a new section) This happen most of the time. However, sometimes, randomly, instead of firing "move" callback, FRC will fire "update" callback. When such incident happen, I will exam the content of fetchedResultsController.sections. Then, I will notice the entity item stays in wrong section. My FRC looks pretty straightforward. lazy var fetchedResultsController: NSFetchedResultsControllerNSPlainNote = { // Create a fetch request for the Quake entity sorted by time. let fetchRequest = NSFetchRequestNSPlainNote(entityName: "NSPlainNote") fetchRequest.sortDescriptors = [ NSSortDescriptor(key: "pinned", ascending: false) ] // Create a fetched results controller and set its fetch request, context, and delegate. let controller = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: CoreDataStack.INSTANCE.persistentContainer.viewContext, sectionNameKeyPath: "pinned", cacheName: nil ) controller.delegate = fetchedResultsControllerDelegate // Perform the fetch. do { try controller.performFetch() } catch { fatalError("Unresolved error \(error)") } return controller }() This is how I update the pinned column using background thread. func updatePinned(_ objectID: NSManagedObjectID, _ pinned: Bool) { let coreDataStack = CoreDataStack.INSTANCE let backgroundContext = coreDataStack.backgroundContext backgroundContext.perform { let nsPlainNote = try! backgroundContext.existingObject(with: objectID) as! NSPlainNote nsPlainNote.pinned = pinned RepositoryUtils.saveContextIfPossible(backgroundContext) } } I am not really sure, whether this can caused by my background thread. As, if I replace the backgroundContext with viewContext, I haven't observed the random problem so far. But, even so, I am not confident to conclude using backgroundContext is the culprit to this problem. The setup of my background thread is also pretty straightforward. I cannot see how it can went wrong. class CoreDataStack { public static let INSTANCE = CoreDataStack() private init() { } lazy var persistentContainer: NSPersistentContainer = { let container = NSPersistentContainer(name: "xxx") container.loadPersistentStores(completionHandler: { (storeDescription, error) in if let error = error as NSError? { fatalError("Unresolved error \(error), \(error.userInfo)") } }) // TODO: container.viewContext.automaticallyMergesChangesFromParent = true //container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy //container.viewContext.undoManager = nil //container.viewContext.shouldDeleteInaccessibleFaults = true return container }() lazy var backgroundContext: NSManagedObjectContext = { let backgroundContext = persistentContainer.newBackgroundContext() // TODO: //backgroundContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy //backgroundContext.undoManager = nil return backgroundContext }() } I was wondering, have anyone of you encounter similar problem when trying to utilize sectionNameKeyPath or FRC? Do you know what? Do you have any workaround/ solution for that? Thank you!
1
0
1.4k
Mar ’21
Is it possible to use background thread on NSFetchedResultsController for heavy read operation to ensure UI responsiveness?
Apple has shown us how to perform heavy write operation using background thread (by using newBackgroundContext) in their official earth quake example - https://github.com/yccheok/earthquakes-WWDC20 But, what about heavy read operation? (millions of rows for stress test purpose) We would also like our app UI to be responsiveness, when we are launching the app for first time, and the app is reading a large amount of data from CoreData. The following is the code snippet which is using NSFetchedResultController. [UI is not responsiveness if there are a lot of rows] } [UI is not responsiveness if there are a lot of rows] We try to perform controller.performFetch() using background thread. Still, but not sure why, the UI is still not responsiveness. My guess is that, after NSFetchedResultsController is occupied UI main thread, to perform some time consuming I/O read operation. } [UI is responsive now. But the solution is incorrect...] I guess, we need to place NSFetchedresultController under background thread too. Hence, we do the following modification. } The UI is responsiveness during fetching, and data is able to be fetched and shown after some time. However, if we investigate further by using the launching argument com.apple.CoreData.ConcurrencyDebug 1 We will experience the following crash after controller.performFetch() finish executed. CoreData`+[NSManagedObjectContext __Multithreading_Violation_AllThatIsLeftToUsIsHonor__]: May I know, is it ever possible to make UI responsive, when we are using NSFetchedResultController to load a large amount of data (few millions rows as per testing)? Can NSFetchedResultController ever operated under background thread?
0
0
752
Mar ’21