Post

Replies

Boosts

Views

Activity

Is using CoreData + CloudKit to store text notes, and iCloud Document to store note image attachments as image files a good design approach?
Is using CoreData + CloudKit to store text notes and iCloud Document to store note image attachments as image files a good design approach? Currently, I have almost finished implementing the app to store text notes as Core Data and note image attachments as image files. I like to provide iCloud storage support to the app. I come across a few app examples https://www.raywenderlich.com/13219461-getting-started-with-core-data-and-cloudkit https://developer.apple.com/documentation/coredata/synchronizing_a_local_store_to_the_cloud Both examples are using CoreData + CloudKit to store the image as SQLite blob binary data which CoreData will perform such task automatically) I'm aware that when storing the binary image into CoreData, CoreData is smart enough to choose either to store it as a binary blob in SQLite, or an external file. However, I am a little skeptical about such an approach We are using Kingfisher for smooth image loading in the collection view. If the image data are not in a regular flat-file, how can we integrate CoreData's blob data with Kingfisher? Storing images in CoreData seems like a black box. If something goes wrong in between, it is hard to debug where and how goes wrong. We like to provide alternative cloud storage (using cloud S3 storage to store SQLite files and multiple images) for the app. So, saving the image as flat files will make such an effort easier. Some related discussion on storing the image in DB vs flat files - https://stackoverflow.com/questions/3748/storing-images-in-db-yea-or-nay I would prefer Only use CoreData + CloudKit to store the text note and file path. The image file will store in both the app folder and the iCloud document folder (so that it syncs seamlessly to iCloud). If the required images are not in the app folder (App uninstall, then re-install), the app will try to copy the image file from the iCloud document I was wondering, anyone of you has tried such a design in your app? Do you find it is a good approach based on your experience? Thanks.
2
0
1.1k
Apr ’22
May I know how can I use SF Symbols beta to create custom icon?
I am using "checkmark.circle.fill" I wish to have a blue filled circle with the checkmark being solid black. For the blue filled circle, I can choose the tinted color as blue. But, the default SF Symbols "checkmark.circle.fill", its checkmark is transparent. It looks as follow So, I try to use "SF Symbols beta", to create custom icon. During editing, the thing seems like what I want Blue filled circle Solid black color for checkmark Then, I use "Export Symbol...", using Template Version 2.0 (XCode cannot recognise Template Version 3.0) It seems like the checkmark fall back to transparent color. When I import it to XCode It seems like the "solid black color" on the checkmark is gone. When I use this custom icon in my app, it gave same result as "checkmark.circle.fill" (the checkmark still remain as transparent) Doesn't anyone know how to resolve this issue? Thanks.
Topic: Design SubTopic: General Tags:
1
0
4.3k
Jan ’22
Asynchronous read in CoreData - Difference in using newBackgroundContext + FetchRequest vs newBackgroundContext + NSAsynchronousFetchRequest?
It seems there are 2 ways to perform asynchronous read in CoreData, without blocking main thread UI. newBackgroundContext + FetchRequest Source : https://www.advancedswift.com/core-data-background-fetch-save-create/ // Create a new background managed object context let context = persistentContainer.newBackgroundContext() // If needed, ensure the background context stays // up to date with changes from the parent context.automaticallyMergesChangesFromParent = true // Perform operations on the background context // asynchronously context.perform { do { // Create a fetch request let fetchRequest: NSFetchRequest<CustomEntity> fetchRequest = CustomEntity.fetchRequest() fetchRequest.fetchLimit = 1 let objects = try context.fetch(fetchRequest) // Handle fetched objects } catch let error { // Handle error } } newBackgroundContext + NSAsynchronousFetchRequest Source: https://www.marcosantadev.com/coredata_crud_concurrency_swift_2/ let privateManagedObjectContext = persistentContainer.newBackgroundContext() // Creates a fetch request to get all the dogs saved let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Dog") // Creates `asynchronousFetchRequest` with the fetch request and the completion closure let asynchronousFetchRequest = NSAsynchronousFetchRequest(fetchRequest: fetchRequest) { asynchronousFetchResult in // Retrieves an array of dogs from the fetch result `finalResult` guard let result = asynchronousFetchResult.finalResult as? [Dog] else { return } // Dispatches to use the data in the main queue DispatchQueue.main.async { // Do something } } do { // Executes `asynchronousFetchRequest` try privateManagedObjectContext.execute(asynchronousFetchRequest) } catch let error { print("NSAsynchronousFetchRequest error: \(error)") } However, note that, the above code will unfortunately cause fatal error, if I were to enable flag -com.apple.CoreData.ConcurrencyDebug 1. So far, I do not have a good solution to such. For more detail, please refer to https://developer.apple.com/forums/thread/697718 ? May I know, what is the difference among newBackgroundContext + FetchRequest vs newBackgroundContext + NSAsynchronousFetchRequest? How should I choose one over another? Thank you.
0
0
1.7k
Dec ’21
Why I am getting Multithreading_Violation_AllThatIsLeftToUsIsHonor for this simplest NSAsynchronousFetchRequest use case?
I want to start learning to use NSAsynchronousFetchRequest by referring to  https://www.marcosantadev.com/coredata_crud_concurrency_swift_2/ I have the simplest use case of NSAsynchronousFetchRequest NSAsynchronousFetchRequest // Call from UI main thread func X() { let fetchRequest = NSFetchRequest<NSPlainNote>(entityName: "NSPlainNote") let asynchronousFetchRequest = NSAsynchronousFetchRequest(fetchRequest: fetchRequest) { asynchronousFetchResult in guard let result = asynchronousFetchResult.finalResult as? [NSPlainNote] else { return } } let coreDataStack = CoreDataStack.INSTANCE // backgroundContext created via persistentContainer.newBackgroundContext() let backgroundContext = coreDataStack.backgroundContext backgroundContext.perform { do { try backgroundContext.execute(asynchronousFetchRequest) } catch let error { backgroundContext.rollback() error_log(error) } } } However, running the above code will get me the following error CoreData`+[NSManagedObjectContextMultithreading_Violation_AllThatIsLeftToUsIsHonor]: If I modify the code by using NSFetchRequest directly. NSFetchRequest // Call from UI main thread func X() { let fetchRequest = NSFetchRequest<NSPlainNote>(entityName: "NSPlainNote") let coreDataStack = CoreDataStack.INSTANCE // backgroundContext created via persistentContainer.newBackgroundContext() let backgroundContext = coreDataStack.backgroundContext backgroundContext.perform { do { let nsPlainNotes = try fetchRequest.execute() } catch let error { backgroundContext.rollback() error_log(error) } } } Thing works fine. May I know, what's wrong with my NSAsynchronousFetchRequest version of code? This is my CoreDataStack.swift for reference purpose. CoreDataStack.swift import CoreData class CoreDataStack { static let INSTANCE = CoreDataStack() private init() { } private(set) lazy var persistentContainer: NSPersistentContainer = { let container = NSPersistentContainer(name: "wenote") container.loadPersistentStores(completionHandler: { (storeDescription, error) in if let error = error as NSError? { // This is a serious fatal error. We will just simply terminate the app, rather than using error_log. fatalError("Unresolved error \(error), \(error.userInfo)") } }) // So that when backgroundContext write to persistent store, container.viewContext will retrieve update from // persistent store. container.viewContext.automaticallyMergesChangesFromParent = true // TODO: Not sure these are required... // //container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy //container.viewContext.undoManager = nil //container.viewContext.shouldDeleteInaccessibleFaults = true return container }() private(set) lazy var backgroundContext: NSManagedObjectContext = { let backgroundContext = persistentContainer.newBackgroundContext() // Similar behavior as Android's Room OnConflictStrategy.REPLACE backgroundContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy // TODO: Not sure these are required... //backgroundContext.undoManager = nil return backgroundContext }() } Additional information Do note that, in NSAsynchronousFetchRequest example, even if backgroundContext.perform is not used. // Call from UI main thread func X() { let fetchRequest = NSFetchRequest<NSPlainNote>(entityName: "NSPlainNote") let asynchronousFetchRequest = NSAsynchronousFetchRequest(fetchRequest: fetchRequest) { asynchronousFetchResult in guard let result = asynchronousFetchResult.finalResult as? [NSPlainNote] else { return } } let coreDataStack = CoreDataStack.INSTANCE // backgroundContext created via persistentContainer.newBackgroundContext() let backgroundContext = coreDataStack.backgroundContext do { try backgroundContext.execute(asynchronousFetchRequest) } catch let error { backgroundContext.rollback() error_log(error) } } Same fatal error still occur. Please note that, this fatal error will only be triggered, by editing the schema with Arguments Passed On Launch -com.apple.CoreData.ConcurrencyDebug 1 I even try to execute some simple project from  https://github.com/abhishekbedi1432/Core-Data-Asynchronous-Fetching/tree/master which is using NSAsynchronousFetchRequest. If I do not enable -com.apple.CoreData.ConcurrencyDebug 1, the sample project from github able to perform asynchronous fetch without issue. However, once the -com.apple.CoreData.ConcurrencyDebug 1 is enabled, it will also be getting the same fatal error.
0
0
830
Dec ’21
Prevent scroll position from resetting when perform UITextView typing in UICollectionView
Currently, we try to place multiple UITextViews in UICollectionView. To ensure UICollectionView's cell height, will adjust based on the dynamic content of UITextView, this is what we have done. Disable scrolling in UITextView. Use .estimated(CGFloat(44)) for UICollectionViewCompositionalLayout Whenever there is text change, call collectionView.collectionViewLayout.invalidateLayout(). This is a critical step to ensure cell height will adjust accordingly. However, calling collectionView.collectionViewLayout.invalidateLayout() does come with a side effect. The current scroll position of UICollectionView will be reset, after calling collectionView.collectionViewLayout.invalidateLayout(). Does anyone know how can I Prevent unwanted auto scroll position resetting? UICollectionView will auto scroll to current cursor position, so that what is current being typed is visible to user? The code to demonstrate this problem is as follow - https://github.com/yccheok/checklist-demo Here's the code snippet, on what was happening as typing goes on func textViewDidChange(_ checklistCell: ChecklistCell) { // // Critical code to ensure cell will resize based on cell content. // // (But comes with a side effect which will reset scroll position.) self.collectionView.collectionViewLayout.invalidateLayout() // // Ensure our checklists data structure in sync with UI state. // guard let indexPath = collectionView.indexPath(for: checklistCell) else { return } let item = indexPath.item let text = checklistCell.textView.text self.checklists[item].text = text } Side Note Note, the closest solution we have came across is posted at https://medium.com/@georgetsifrikas/embedding-uitextview-inside-uitableviewcell-9a28794daf01 In UITableViewController, during text change, the author is using DispatchQueue.main.async { tableView?.beginUpdates() tableView?.endUpdates() } It works well. But, what is the equivalent solution for UICollectionView? We can't try out with self.collectionView.performBatchUpdates, as our solution is built around Diffable Data Source. I have tried DispatchQueue.main.async { self.collectionView.collectionViewLayout.invalidateLayout() } That doesn't solve the problem either. Thank you.
Topic: UI Frameworks SubTopic: UIKit Tags:
2
0
4.3k
Nov ’21
Is using CoreData + CloudKit to store text notes, and iCloud Document to store note image attachments as image files a good design approach?
Is using CoreData + CloudKit to store text notes and iCloud Document to store note image attachments as image files a good design approach? Currently, I have almost finished implementing the app to store text notes as Core Data and note image attachments as image files. I like to provide iCloud storage support to the app. I come across a few app examples https://www.raywenderlich.com/13219461-getting-started-with-core-data-and-cloudkit https://developer.apple.com/documentation/coredata/synchronizing_a_local_store_to_the_cloud Both examples are using CoreData + CloudKit to store the image as SQLite blob binary data which CoreData will perform such task automatically) I'm aware that when storing the binary image into CoreData, CoreData is smart enough to choose either to store it as a binary blob in SQLite, or an external file. However, I am a little skeptical about such an approach We are using Kingfisher for smooth image loading in the collection view. If the image data are not in a regular flat-file, how can we integrate CoreData's blob data with Kingfisher? Storing images in CoreData seems like a black box. If something goes wrong in between, it is hard to debug where and how goes wrong. We like to provide alternative cloud storage (using cloud S3 storage to store SQLite files and multiple images) for the app. So, saving the image as flat files will make such an effort easier. Some related discussion on storing the image in DB vs flat files - https://stackoverflow.com/questions/3748/storing-images-in-db-yea-or-nay I would prefer Only use CoreData + CloudKit to store the text note and file path. The image file will store in both the app folder and the iCloud document folder (so that it syncs seamlessly to iCloud). If the required images are not in the app folder (App uninstall, then re-install), the app will try to copy the image file from the iCloud document I was wondering, anyone of you has tried such a design in your app? Do you find it is a good approach based on your experience? Thanks.
Replies
2
Boosts
0
Views
1.1k
Activity
Apr ’22
May I know how can I use SF Symbols beta to create custom icon?
I am using "checkmark.circle.fill" I wish to have a blue filled circle with the checkmark being solid black. For the blue filled circle, I can choose the tinted color as blue. But, the default SF Symbols "checkmark.circle.fill", its checkmark is transparent. It looks as follow So, I try to use "SF Symbols beta", to create custom icon. During editing, the thing seems like what I want Blue filled circle Solid black color for checkmark Then, I use "Export Symbol...", using Template Version 2.0 (XCode cannot recognise Template Version 3.0) It seems like the checkmark fall back to transparent color. When I import it to XCode It seems like the "solid black color" on the checkmark is gone. When I use this custom icon in my app, it gave same result as "checkmark.circle.fill" (the checkmark still remain as transparent) Doesn't anyone know how to resolve this issue? Thanks.
Topic: Design SubTopic: General Tags:
Replies
1
Boosts
0
Views
4.3k
Activity
Jan ’22
Asynchronous read in CoreData - Difference in using newBackgroundContext + FetchRequest vs newBackgroundContext + NSAsynchronousFetchRequest?
It seems there are 2 ways to perform asynchronous read in CoreData, without blocking main thread UI. newBackgroundContext + FetchRequest Source : https://www.advancedswift.com/core-data-background-fetch-save-create/ // Create a new background managed object context let context = persistentContainer.newBackgroundContext() // If needed, ensure the background context stays // up to date with changes from the parent context.automaticallyMergesChangesFromParent = true // Perform operations on the background context // asynchronously context.perform { do { // Create a fetch request let fetchRequest: NSFetchRequest<CustomEntity> fetchRequest = CustomEntity.fetchRequest() fetchRequest.fetchLimit = 1 let objects = try context.fetch(fetchRequest) // Handle fetched objects } catch let error { // Handle error } } newBackgroundContext + NSAsynchronousFetchRequest Source: https://www.marcosantadev.com/coredata_crud_concurrency_swift_2/ let privateManagedObjectContext = persistentContainer.newBackgroundContext() // Creates a fetch request to get all the dogs saved let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Dog") // Creates `asynchronousFetchRequest` with the fetch request and the completion closure let asynchronousFetchRequest = NSAsynchronousFetchRequest(fetchRequest: fetchRequest) { asynchronousFetchResult in // Retrieves an array of dogs from the fetch result `finalResult` guard let result = asynchronousFetchResult.finalResult as? [Dog] else { return } // Dispatches to use the data in the main queue DispatchQueue.main.async { // Do something } } do { // Executes `asynchronousFetchRequest` try privateManagedObjectContext.execute(asynchronousFetchRequest) } catch let error { print("NSAsynchronousFetchRequest error: \(error)") } However, note that, the above code will unfortunately cause fatal error, if I were to enable flag -com.apple.CoreData.ConcurrencyDebug 1. So far, I do not have a good solution to such. For more detail, please refer to https://developer.apple.com/forums/thread/697718 ? May I know, what is the difference among newBackgroundContext + FetchRequest vs newBackgroundContext + NSAsynchronousFetchRequest? How should I choose one over another? Thank you.
Replies
0
Boosts
0
Views
1.7k
Activity
Dec ’21
Why I am getting Multithreading_Violation_AllThatIsLeftToUsIsHonor for this simplest NSAsynchronousFetchRequest use case?
I want to start learning to use NSAsynchronousFetchRequest by referring to  https://www.marcosantadev.com/coredata_crud_concurrency_swift_2/ I have the simplest use case of NSAsynchronousFetchRequest NSAsynchronousFetchRequest // Call from UI main thread func X() { let fetchRequest = NSFetchRequest<NSPlainNote>(entityName: "NSPlainNote") let asynchronousFetchRequest = NSAsynchronousFetchRequest(fetchRequest: fetchRequest) { asynchronousFetchResult in guard let result = asynchronousFetchResult.finalResult as? [NSPlainNote] else { return } } let coreDataStack = CoreDataStack.INSTANCE // backgroundContext created via persistentContainer.newBackgroundContext() let backgroundContext = coreDataStack.backgroundContext backgroundContext.perform { do { try backgroundContext.execute(asynchronousFetchRequest) } catch let error { backgroundContext.rollback() error_log(error) } } } However, running the above code will get me the following error CoreData`+[NSManagedObjectContextMultithreading_Violation_AllThatIsLeftToUsIsHonor]: If I modify the code by using NSFetchRequest directly. NSFetchRequest // Call from UI main thread func X() { let fetchRequest = NSFetchRequest<NSPlainNote>(entityName: "NSPlainNote") let coreDataStack = CoreDataStack.INSTANCE // backgroundContext created via persistentContainer.newBackgroundContext() let backgroundContext = coreDataStack.backgroundContext backgroundContext.perform { do { let nsPlainNotes = try fetchRequest.execute() } catch let error { backgroundContext.rollback() error_log(error) } } } Thing works fine. May I know, what's wrong with my NSAsynchronousFetchRequest version of code? This is my CoreDataStack.swift for reference purpose. CoreDataStack.swift import CoreData class CoreDataStack { static let INSTANCE = CoreDataStack() private init() { } private(set) lazy var persistentContainer: NSPersistentContainer = { let container = NSPersistentContainer(name: "wenote") container.loadPersistentStores(completionHandler: { (storeDescription, error) in if let error = error as NSError? { // This is a serious fatal error. We will just simply terminate the app, rather than using error_log. fatalError("Unresolved error \(error), \(error.userInfo)") } }) // So that when backgroundContext write to persistent store, container.viewContext will retrieve update from // persistent store. container.viewContext.automaticallyMergesChangesFromParent = true // TODO: Not sure these are required... // //container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy //container.viewContext.undoManager = nil //container.viewContext.shouldDeleteInaccessibleFaults = true return container }() private(set) lazy var backgroundContext: NSManagedObjectContext = { let backgroundContext = persistentContainer.newBackgroundContext() // Similar behavior as Android's Room OnConflictStrategy.REPLACE backgroundContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy // TODO: Not sure these are required... //backgroundContext.undoManager = nil return backgroundContext }() } Additional information Do note that, in NSAsynchronousFetchRequest example, even if backgroundContext.perform is not used. // Call from UI main thread func X() { let fetchRequest = NSFetchRequest<NSPlainNote>(entityName: "NSPlainNote") let asynchronousFetchRequest = NSAsynchronousFetchRequest(fetchRequest: fetchRequest) { asynchronousFetchResult in guard let result = asynchronousFetchResult.finalResult as? [NSPlainNote] else { return } } let coreDataStack = CoreDataStack.INSTANCE // backgroundContext created via persistentContainer.newBackgroundContext() let backgroundContext = coreDataStack.backgroundContext do { try backgroundContext.execute(asynchronousFetchRequest) } catch let error { backgroundContext.rollback() error_log(error) } } Same fatal error still occur. Please note that, this fatal error will only be triggered, by editing the schema with Arguments Passed On Launch -com.apple.CoreData.ConcurrencyDebug 1 I even try to execute some simple project from  https://github.com/abhishekbedi1432/Core-Data-Asynchronous-Fetching/tree/master which is using NSAsynchronousFetchRequest. If I do not enable -com.apple.CoreData.ConcurrencyDebug 1, the sample project from github able to perform asynchronous fetch without issue. However, once the -com.apple.CoreData.ConcurrencyDebug 1 is enabled, it will also be getting the same fatal error.
Replies
0
Boosts
0
Views
830
Activity
Dec ’21
Prevent scroll position from resetting when perform UITextView typing in UICollectionView
Currently, we try to place multiple UITextViews in UICollectionView. To ensure UICollectionView's cell height, will adjust based on the dynamic content of UITextView, this is what we have done. Disable scrolling in UITextView. Use .estimated(CGFloat(44)) for UICollectionViewCompositionalLayout Whenever there is text change, call collectionView.collectionViewLayout.invalidateLayout(). This is a critical step to ensure cell height will adjust accordingly. However, calling collectionView.collectionViewLayout.invalidateLayout() does come with a side effect. The current scroll position of UICollectionView will be reset, after calling collectionView.collectionViewLayout.invalidateLayout(). Does anyone know how can I Prevent unwanted auto scroll position resetting? UICollectionView will auto scroll to current cursor position, so that what is current being typed is visible to user? The code to demonstrate this problem is as follow - https://github.com/yccheok/checklist-demo Here's the code snippet, on what was happening as typing goes on func textViewDidChange(_ checklistCell: ChecklistCell) { // // Critical code to ensure cell will resize based on cell content. // // (But comes with a side effect which will reset scroll position.) self.collectionView.collectionViewLayout.invalidateLayout() // // Ensure our checklists data structure in sync with UI state. // guard let indexPath = collectionView.indexPath(for: checklistCell) else { return } let item = indexPath.item let text = checklistCell.textView.text self.checklists[item].text = text } Side Note Note, the closest solution we have came across is posted at https://medium.com/@georgetsifrikas/embedding-uitextview-inside-uitableviewcell-9a28794daf01 In UITableViewController, during text change, the author is using DispatchQueue.main.async { tableView?.beginUpdates() tableView?.endUpdates() } It works well. But, what is the equivalent solution for UICollectionView? We can't try out with self.collectionView.performBatchUpdates, as our solution is built around Diffable Data Source. I have tried DispatchQueue.main.async { self.collectionView.collectionViewLayout.invalidateLayout() } That doesn't solve the problem either. Thank you.
Topic: UI Frameworks SubTopic: UIKit Tags:
Replies
2
Boosts
0
Views
4.3k
Activity
Nov ’21