When and how often should initializeCloudKitSchema be called? - core-data

I am using NSPersistentCloudKitContainer in my Application, and I am struggling to understand when and how often should I call the method initializeCloudKitSchema() of my container.
Do I understand correctly that this is something to use in development to update the development schema update in CloudKit backend?
Should my App in production, in hands of my user ever call this method?
Here is the code that I am using:
import CoreData
import Foundation
final class DataController: ObservableObject {
let container = NSPersistentCloudKitContainer(name: "MyAmazingSchema")
init() {
container.loadPersistentStores {
description, error in
if let error = error as NSError? {
fatalError("Core Data - Unresolved error \(error), \(error.userInfo)")
}
}
do {
// Should I do this ⬇️ in Production?
try container.initializeCloudKitSchema()
}
catch {
print(error)
}
container.viewContext.automaticallyMergesChangesFromParent = true
}
}

Related

SwiftUI crashes due to deleted CoreData object

I have a SwiftUI application, that updates views based on fetched CoreData objects, like so:
var body: some View {
HStack {
VStack(alignment:.leading) {
TaskProgressBar(model: ProgressModel(frequency: Int(task.frequency), deadline: task.deadline))
}
}
}
The app crashes because task.frequency becomes unexpectedly nil, when I delete the CoreData object:
func deleteTasks(_ tasks: [Task]) {
for task in tasks {
persistenContainer.viewContext.delete(task)
}
do {
try persistenContainer.viewContext.save()
} catch {
fatalError("Failed to save context after task deletion")
}
}
I was able to fix it by calling save() asynchronously, but I am just wondering if that is the right solution? Is there a race condition and could my app still potentially crash, if saving asynchronously?

Extended fe_user model, can't access crdate when run via cronjob

I have a problem with accessing the crdate of a fe_user in a croniob task. I have an extended Frontend user model where I added this adjustments:
https://stackoverflow.com/a/50663006/1684975
Additionaly I've added a mapping via ts
config.tx_extbase {
persistence {
classes {
TYPO3\CMS\Extbase\Domain\Model\FrontendUser {
subclasses {
Tx_MyExt_User = ACV\MyExt\Domain\Model\User
}
}
Paul\MyExt\Domain\Model\User {
mapping {
tableName = fe_users
recordType = Tx_MyExt_User
columns {
crdate.mapOnProperty = crdate
}
}
}
}
}
}
When I fire up the Scheduler task manual via the Scheduler BE module it's ok. But when the real cronjob kicks in I get the error
Uncaught TYPO3 Exception Call to a member function getTimestamp() on null
In the Scheduler task, I get the crDate via the getter and try to get the timestamp…
$users = $frontendRepository->findByOptIn(false);
foreach ($users as $user) {
if ($user->getCrdate()->getTimestamp() < strtotime('-5 days')) {
$frontendRepository->remove($user);
}
}
The mode of the user is correct. I can access other custom properties I've added to the model.
It's TYPO3 v9.5.26.
The odd thing is that it runs locally in my dev environment.
Did someone have an idea what could cause the problem?
Add a file <extensionfolder>/ext_typoscript_setup.typoscript and add your TypoScript in there:
config.tx_extbase {
persistence {
classes {
TYPO3\CMS\Extbase\Domain\Model\FrontendUser {
subclasses {
Tx_MyExt_User = ACV\MyExt\Domain\Model\User
}
}
Paul\MyExt\Domain\Model\User {
mapping {
tableName = fe_users
recordType = Tx_MyExt_User
columns {
crdate.mapOnProperty = crdate
}
}
}
}
}
}
Alternativly you can add an include there to your TypoScript file in your extension Configuration/TypoScript folder.
If you are in CLI Context, it can be that the TypoScript is not loaded fully for the mapping/extbase configuration, which would match your description that in the BE Context it works, but not in Cronjob/CLI Context.

In a SwiftUI lifecycle app, where exactly should I register a CoreData transformerValue?

I'm trying to follow this tutorial:
https://www.avanderlee.com/swift/valuetransformer-core-data/
But I'm stumped on where (and how!) exactly to use
UIColorValueTransformer.register()
The author says to do it before setting up the persistent container... and I'm think I'm doing that here:
import SwiftUI
#main
struct ContactApp: App {
let persistenceController = PersistenceController.shared
var body: some Scene {
WindowGroup {
MainTabView()
.environment(\.managedObjectContext, persistenceController.container.viewContext)
}
}
}
But I'm at a loss of what exactly to write, or if this is the right place. Maybe I should be doing it in a different place?
Put it into init, like
class PersistenceController {
static let shared = PersistenceController()
init() {
UIColorValueTransformer.register() // << here !!
// ... other init code
}
// ... other code
}

Saving custom class to array in CoreData SwiftUI after closing app fails

I am getting the error below, when I close my app and then try to save to the core the when reloading the app.
error: Serious application error. Exception was caught during Core Data change processing. This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification. -[__NSSingleObjectArrayI compare:]: unrecognized selector sent to instance 0x600002291510 with userInfo (null)
When I first run the app fresh after deleting it off the simulator it works fine though once I close the program and try to add to the core again I get the error above.
Here is my code:
import SwiftUI
import CoreData
struct ContentView: View {
#Environment(\.managedObjectContext) var managedObjectContext
#State var objRating:[ObjectToRate]
#FetchRequest(entity: ObjectsForRate.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \ObjectsForRate.objectsClass, ascending: true)])
var classesRetrieved: FetchedResults<ObjectsForRate>
var body: some View {
VStack {
Text(self.objRating[0].name!)
Button(action: {
print("Mark 1")
let obj = ObjectsForRate(context: self.managedObjectContext)
obj.objectsClass = self.objRating
do
{
try self.managedObjectContext.save()
}
catch
{
print("ERROR \(error)")
}
}) {
Text("Insert to Core ")
}
Button(action: {
print("---------")
print(self.classesRetrieved.count)
print(self.classesRetrieved)
}) {
Text("Retrieve")
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(objRating:data )
}
}
public class ObjectToRate:NSObject, NSCoding
{
public var name:String?
init(name:String) {
self.name = name
}
public func encode(with coder: NSCoder) {
coder.encode(name,forKey: "name")
}
public required init(coder decoder: NSCoder) {
self.name = decoder.decodeObject(forKey: "name") as? String
}
}
var obj: ObjectToRate = {
let o = ObjectToRate(name: "hi")
return o
}()
var data = [
obj
]
I've tried a lot of things but not sure what I'm doing wrong or if it is a bug.
Here is my Coredata model:
Another thing is when I retrieve the data from the core I get this:
<ObjectsForRate: 0x6000038fd400> (entity: ObjectsForRate; id: 0x8b8684e607da61d2 <x-coredata://0CDCAD97-CA46-402F-B638-3F0ACB6E30A7/ObjectsForRate/p5>; data: <fault>
Thank you in advance for your time and help.

save uiimageview to coredata as binary data swift (5)

I am trying to save a imageview as a image to binary data in core data. My code is not working. It has a compile error. In View controller it is not regisitering cdHandler. All i want to do is save the the imaveview as binary data in a core data model.I have 2 classes a app delegate and a view controller.
CLASS VIEW CONTROLLER
import UIKit
import CoreData
class ViewController: UIViewController {
var canVasView = UIImageView()
#objc func hhh() {
let photo = self.canVasView.image
let data = photo!.pngData()
if cdHandler.saveObject(pic: data!){
}
}
}
APP DELEGATE
import UIKit
import CoreData
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
lazy var persistentContainer: NSPersistentContainer = {
/*
The persistent container for the application. This implementation
creates and returns a container, having loaded the store for the
application to it. This property is optional since there are legitimate
error conditions that could cause the creation of the store to fail.
*/
let container = NSPersistentContainer(name: "Model")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
/*
Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space.
* The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was.
*/
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
class cdHandler: NSObject {
private class func getContext() -> NSManagedObjectContext {
let appdeleagetzz = UIApplication.shared.delegate as! AppDelegate
return appdeleagetzz.persistentContainer.viewContext
}
class func saveObject(pic: Data, userName: String) -> Bool {
let context = getContext()
let entity = NSEntityDescription.entity(forEntityName: "User", in: context)
let managedObject = NSManagedObject(entity: entity!, insertInto: context)
managedObject.setValue(pic, forKey:"pic")
managedObject.setValue(userName, forKey:"userName")
do {
try context.save()
return true
} catch {
return false
}
}
class func deletObject(user: User) -> Bool {
let context = getContext()
context.delete(user)
do {
try context.save()
return true
} catch {
return false
}
}
class func fetchObject() -> [User]? {
do {
let context = getContext()
return try context.fetch(User.fetchRequest())
} catch {
return [User]()
}
}
}
}
The error message, *Value of type 'AppDelegate' has no member named 'persistentContainer', explains the problem. Indeed, when I look at the code for your AppDelegate class, I can confirm that it has no member named 'persistentContainer'. (If I am reading it correctly, the last two lines in the file are closing curly brackets. The first one closes your cdHandler nested class, and the second one closes your AppDelegate class.)
Do the following exercise. In Xcode, click in the menu: File > New Project and select iOS, Application and Single View App. Name your new project Junk. Switch on the Core Data checkbox. Click button Create. After it is done, look at the AppDelegate.swift which Xcode created, and in the AppDelegate class, you see it contains 8 functions (func). The 7th one is lazy var persistentContainer. Aha! The compiler is telling you that you probably should not have deleted those 8 functions, persistentContainer in particular.
You should copy that persistentContainer func from that Junk project into your AppDelegate class in your real project. Or, to head off future trouble, consider copying most of the other 7 funcs also. As you can see, most of them don't do anything except provide comments with explanations that are useful for beginners. After you are done copying, close the Junk project. (I overwrite my Junk project with a new Junk project several times in a typical week, especially when answering StackOverflow questions.)
That should fix this particular error and answer this question. Onward to the next issue. :)
Response to comment that you still get the error with cdHandler
Having nothing else to go on, I presume that the error that you are referring to is the compiler error still in your screenshot. In other words, you are saying that adding the persistentContainer definition did not make it any better.
Well, it works for me. Please replace all of the code in your AppDelegate.swift class with the following, build and run it…
import UIKit
import CoreData
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
AppDelegate.cdHandler.testGetContext()
return true
}
lazy var persistentContainer: NSPersistentContainer = {
/*
The persistent container for the application. This implementation
creates and returns a container, having loaded the store for the
application to it. This property is optional since there are legitimate
error conditions that could cause the creation of the store to fail.
*/
let container = NSPersistentContainer(name: "Junk")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
// Replace this implementation with code to handle the error appropriately.
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
/*
Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space.
* The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was.
*/
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
class cdHandler: NSObject {
private class func getContext() -> NSManagedObjectContext {
let appdeleagetzz = UIApplication.shared.delegate as! AppDelegate
return appdeleagetzz.persistentContainer.viewContext
}
class func testGetContext() {
let context = getContext()
print("getContext() succeeded, got \(context)")
}
class func saveObject(pic: Data, userName: String) -> Bool {
let context = getContext()
let entity = NSEntityDescription.entity(forEntityName: "User", in: context)
let managedObject = NSManagedObject(entity: entity!, insertInto: context)
managedObject.setValue(pic, forKey:"pic")
managedObject.setValue(userName, forKey:"userName")
do {
try context.save()
return true
} catch {
return false
}
}
class func deletObject(user: NSManagedObject) -> Bool {
let context = getContext()
context.delete(user)
do {
try context.save()
return true
} catch {
return false
}
}
}
}
You see that compiles with no errors. Also, it runs and the AppDelegate.cdhandler.getContext() method works. As you can see, in AppDelegate.application(application:didFinishLaunchingWithOptions:), I have added a call to a new method which I defined later,AppDelegate.cdHandler.testGetContext()`. It works perfectly.
Are you getting a different error now? If so, you need to specify whether it is a Build or Run error. In either case, copy and paste the text of the error into your question, and tell us where it occurs.

Resources