Unfortunately the new Core Data semantics make me crazy. My previous question had a clean code that didn't work because of incorrect auto generation of header files. Now I continue my work with deleting objects.
My code seems to be very simple:
func deleteProfile(withID: Int) {
let fetchRequest: NSFetchRequest<Profile> = Profile.fetchRequest()
fetchRequest.predicate = Predicate.init(format: "profileID==\(withID)")
let object = try! context.fetch(fetchRequest)
context.delete(object)
}
I did a "hard" debug with print(object) instead of context.delete(object) and it showed me the right object.
So I need just to delete it.
P.S. there is no deleteObject. Now NSManagedContext has only public func delete(_ sender: AnyObject?)
The result of a fetch is an array of managed objects, in your case
[Event], so you can enumerate the array and delete all matching objects.
Example (using try? instead of try! to avoid a crash in the case
of a fetch error):
if let result = try? context.fetch(fetchRequest) {
for object in result {
context.delete(object)
}
}
do {
try context.save()
} catch {
//Handle error
}
If no matching objects exist then the fetch succeeds, but the resulting
array is empty.
Note: In your code, object has the type [Event] and therefore in
context.delete(object)
the compiler creates a call to the
public func delete(_ sender: AnyObject?)
method of NSObject instead of the expected
public func delete(_ object: NSManagedObject)
method of NSManagedObjectContext. That is why your code compiles
but fails at runtime.
The trick here, it is save context after deleting your objects.
let fetchRequest: NSFetchRequest<Profile> = Profile.fetchRequest()
fetchRequest.predicate = Predicate.init(format: "profileID==\(withID)")
let objects = try! context.fetch(fetchRequest)
for obj in objects {
context.delete(obj)
}
do {
try context.save() // <- remember to put this :)
} catch {
// Do something... fatalerror
}
I hope this can help someone.
func deleteRecords() {
let delegate = UIApplication.shared.delegate as! AppDelegate
let context = delegate.persistentContainer.viewContext
let deleteFetch = NSFetchRequest<NSFetchRequestResult>(entityName: "nameofentity")
let deleteRequest = NSBatchDeleteRequest(fetchRequest: deleteFetch)
do {
try context.execute(deleteRequest)
try context.save()
} catch {
print ("There was an error")
}
}
Delete core data objects swift 3
// MARK: Delete Data Records
func deleteRecords() -> Void {
let moc = getContext()
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "Person")
let result = try? moc.fetch(fetchRequest)
let resultData = result as! [Person]
for object in resultData {
moc.delete(object)
}
do {
try moc.save()
print("saved!")
} catch let error as NSError {
print("Could not save \(error), \(error.userInfo)")
} catch {
}
}
// MARK: Get Context
func getContext () -> NSManagedObjectContext {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
return appDelegate.persistentContainer.viewContext
}
Swift 4.1, 4.2 and 5.0
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let requestDel = NSFetchRequest<NSFetchRequestResult>(entityName: "Users")
requestDel.returnsObjectsAsFaults = false
// If you want to delete data on basis of some condition then you can use NSPredicate
// let predicateDel = NSPredicate(format: "age > %d", argumentArray: [10])
// requestDel.predicate = predicateDel
do {
let arrUsrObj = try context.fetch(requestDel)
for usrObj in arrUsrObj as! [NSManagedObject] { // Fetching Object
context.delete(usrObj) // Deleting Object
}
} catch {
print("Failed")
}
// Saving the Delete operation
do {
try context.save()
} catch {
print("Failed saving")
}
Swift 4 without using string for Entity
let fetchRequest: NSFetchRequest<Profile> = Profile.fetchRequest()
fetchRequest.predicate = Predicate.init(format: "profileID==\(withID)")
do {
let objects = try context.fetch(fetchRequest)
for object in objects {
context.delete(object)
}
try context.save()
} catch _ {
// error handling
}
Delete Core Data Object with query in Swift 5, 4.2
let fetchRequest = NSFetchRequest<Your_Model>(entityName: "Your_Entity_Name")
fetchRequest.predicate = NSPredicate(format: "any your_key == %d", your_value)
hope this will help to someone
Swift 5
Common function for Deleting core data objects for any anity swift 5
func deleteEntityObjectByKeyValue<T>(className: T.Type, key: String, value: Any) -> Bool {
let context = CoreDataStack.sharedStack.mainContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: String(describing: className.self))
//Search based on data type.I used for Int and String
if let sValue = value as? String {
let predicate = NSPredicate(format: "\(key) == %#", sValue)
fetchRequest.predicate = predicate
} else if let iValue = value as? Int64 {
let predicate = NSPredicate(format: "\(key) == %d", iValue)
fetchRequest.predicate = predicate
}
do {
let result = try context.fetch(fetchRequest)
if result.count != 0 {
if let managedObject = result[0] as? NSManagedObject {
context.delete(managedObject)
do {
try context.save()
return true
}
catch let error {
print(error.localizedDescription)
}
}
}
return false
} catch let error {
print(error.localizedDescription)
}
return false
}
How To Use it:
let isSaved = CoreDataOperations.shared.deleteEntityObjectByKeyValue(className: EmpolyeeData.self, key: "employeeId", value:1234)
If isSaved {
print("Deleted obj Successfully")
}
Swift 4,5
It is useful to delete particular record or all records from entity.
1.Create a NSPersistentContainer and NSManagedObjectContext using below code.
class CoreDataStack: NSObject {
static var sharedStack = CoreDataStack()
private override init() {}
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "Employee")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let url = storeDescription.url {
print("SQLITE STORE LOCATION: \(url.absoluteString)")
}
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
container.viewContext.shouldDeleteInaccessibleFaults = true
container.viewContext.automaticallyMergesChangesFromParent = true
return container
}()
private lazy var applicationDocumentsDirectory: URL = {
let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
return urls[urls.count-1]
}()
private lazy var managedObjectModel: NSManagedObjectModel = {
let modelURL = Bundle.main.url(forResource: "____", withExtension: "____")!
return NSManagedObjectModel(contentsOf: modelURL)!
}()
public lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
let url = self.applicationDocumentsDirectory.appendingPathComponent("Employee" + ".sqlite")
var failureReason = "There was an error creating or loading the application's saved data."
do {
let options = [ NSMigratePersistentStoresAutomaticallyOption : true, NSInferMappingModelAutomaticallyOption : true ]
try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: url, options: options)
} catch {
// Report any error we got.
var dict = [String: AnyObject]()
dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data" as AnyObject?
dict[NSLocalizedFailureReasonErrorKey] = failureReason as AnyObject?
dict[NSUnderlyingErrorKey] = error as NSError
let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)
// Replace this with code to handle the error appropriately.
// abort() 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.
NSLog("Unresolved error \(wrappedError), \(wrappedError.userInfo)")
}
return coordinator
}()
lazy var mainContext: NSManagedObjectContext = {
var managedObjectContext: NSManagedObjectContext?
if #available(iOS 10.0, *){
managedObjectContext = self.persistentContainer.viewContext
managedObjectContext?.mergePolicy = NSMergePolicy.init(merge: NSMergePolicyType.mergeByPropertyObjectTrumpMergePolicyType)
}
else{
// Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.) This property is optional since there are legitimate error conditions that could cause the creation of the context to fail.
let coordinator = self.persistentStoreCoordinator
managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
managedObjectContext?.persistentStoreCoordinator = coordinator
managedObjectContext?.mergePolicy = NSMergePolicy.init(merge: NSMergePolicyType.mergeByPropertyObjectTrumpMergePolicyType)
}
return managedObjectContext!
}()
}
2.Common function for Deleting core data all objects for any Entity swift 5
func deleteEntityData(entity : String) {
let deleteFetch = NSFetchRequest<NSFetchRequestResult>(entityName: entity)
let deleteRequest = NSBatchDeleteRequest(fetchRequest: deleteFetch)
do {
try CoreDataStack.sharedStack.mainContext.execute(deleteRequest)
CoreDataStack.sharedStack.saveMainContext()
} catch {
print ("There was an error")
}
}
3.How to use above code.
self.deleteEntityData(entity : "Employee")
4.If you want to delete Particular object from Entity
func deleteEntityObjectByKeyValue<T>(entityName: T.Type, key: String, value: Any) -> Bool {
let context = CoreDataStack.sharedStack.mainContext
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: String(describing: entityName.self))
if let sValue = value as? String {
let predicate = NSPredicate(format: "\(key) == %#", sValue)
fetchRequest.predicate = predicate
} else if let iValue = value as? Int64 {
let predicate = NSPredicate(format: "\(key) == %d", iValue)
fetchRequest.predicate = predicate
}
do {
let result = try context.fetch(fetchRequest)
if result.count != 0 {
if let managedObject = result[0] as? NSManagedObject {
context.delete(managedObject)
do {
CoreDataStack.sharedStack.saveMainContext()
return true
}
catch let error {
print(error.localizedDescription)
}
}
}
return false
} catch let error {
print(error.localizedDescription)
}
return false
}
Delete the object from core data
let entity = NSEntityDescription.entity(forEntityName: "Students", in: managedContext)
let request = NSFetchRequest<NSFetchRequestResult>()
request.entity = entity
if let result = try? managedContext.fetch(request) {
for object in result {
managedContext.delete(object as! NSManagedObject)
}
txtName.text = ""
txtPhone.text = ""
txt_Address.text = ""
labelStatus.text = "Deleted"
}
Related
I am storing and creating posts with this function in my view controller:
#objc func createPost(){
guard let appDelegate =
UIApplication.shared.delegate as? AppDelegate else {
return
}
let managedContext =
appDelegate.persistentContainer.viewContext
let post = Post(context: managedContext)
var mediaURI: URL?
let timeInSeconds = Int64((postDate?.timeIntervalSince1970)!)
if isVideo == true {
let filename = String(timeInSeconds) + ".MOV"
FileService.uploadVideo(videoURL: pickedVideoURL, name: filename)
post.mediaFilename = filename
} else {
let filename = String(timeInSeconds) + ".JPG"
FileService.uploadImage(image: postImage, name: filename)
post.mediaFilename = filename
}
var postTags:[Tag] = []
if let tokens = tagsView.tokens() {
for token in tokens {
let tagFetchRequest: NSFetchRequest<Tag> = Tag.fetchRequest()
tagFetchRequest.predicate = NSPredicate(format: "name == %#", token.title)
do {
let res = try managedContext.fetch(tagFetchRequest)
var tag: Tag!
if res.count > 0 {
tag = res.first
} else {
tag = Tag(context: managedContext)
tag.name = token.title
tag.mostRecentUpdate = NSDate()
tag.mostRecentThumbnail = postImage?.jpegData(compressionQuality: 1.0) as NSData?
}
postTags.append(tag)
} catch let error as NSError {
print("Could not fetch. \(error), \(error.userInfo)")
return
}
}
}
for tag in postTags {
post.addToTags(tag)
}
post.isVideo = isVideo!
post.thumbnail = pickedPostThumbnail?.jpegData(compressionQuality: 1.0) as NSData?
post.notes = notesView.text
post.timeStamp = postDate! as NSDate
do {
try managedContext.save()
dismiss(animated: true, completion: nil)
} catch let error as NSError {
print("Could not save. \(error), \(error.userInfo)")
}
}
I am doing a lot of stuff there, but notably, I am setting the post.thumbnail with a UIImage as NSData? and at the end I am saving the context.
So I create posts and close the app, then open it again and another view controller does this:
fileprivate lazy var posts: NSFetchedResultsController<Post> = {
let appDelegate =
UIApplication.shared.delegate as? AppDelegate
let managedContext =
appDelegate?.persistentContainer.viewContext
let request: NSFetchRequest<Post> = NSFetchRequest(entityName: "Post")
request.predicate = NSPredicate(format: "%# IN self.tags", tag)
let timeSort = NSSortDescriptor(key: "timeStamp", ascending: true)
request.sortDescriptors = [timeSort]
let posts = NSFetchedResultsController(fetchRequest: request, managedObjectContext: managedContext!, sectionNameKeyPath: nil, cacheName: nil)
posts.delegate = self
return posts
}()
override func viewDidLoad(){
....
do {
try posts.performFetch()
} catch let error as NSError {
print("Could not fetch. \(error), \(error.userInfo)")
}
...
}
let post = posts.object(at: indexPath)
cell.mediaFileName = post.mediaFilename
if let data = post.thumbnail {
print(data,"data?")
cell.thumbnail.image = UIImage.init(data: data as Data)
} else {
print("couldn't find")
}
This works fine the first few restarts. However soon, it starts printing ("couldn't find") and not loading the image. The other fields associated with post are still loading. This is being run on the iPhone 8 simulator that comes with Xcode.
What am I doing wrong here? Is there something about how a simulator stores images in core data that doesn't persist? Or am I misusing the managed context?
I have project where I use it from Deployment Target = 9.0, more than 10 Coredata entities, here I am trying to do a approach where model is not called from View, and having a single method that takes request to fetch and perform the task. This is my approach
AppDelegate.swift
// MARK: - Core Data stack
lazy var applicationDocumentsDirectory: URL = {
let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
return urls[urls.count-1]
}()
lazy var managedObjectModel: NSManagedObjectModel = {
let modelURL = Bundle.main.url(forResource: "CoreDB", withExtension: "momd")!
return NSManagedObjectModel(contentsOf: modelURL)!
}()
lazy var managedObjectContext: NSManagedObjectContext = {
let coordinator = self.persistentStoreCoordinator
var managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = coordinator
return managedObjectContext
}()
lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
// Create the coordinator and store
let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("CoreDB.sqlite")
var failureReason = "There was an error creating or loading the application's saved data."
do {
try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: nil)
} catch {
}
return coordinator
}()
// MARK: - Core Data stack
#available(iOS 10.0, *)
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: "CoreDB")
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.
*/
print("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
// MARK: - Core Data Saving support
func saveContext () {
if #available(iOS 10.0, *) {
let context = persistentContainer.viewContext
if context.hasChanges {
do {
try context.save()
} catch {
// 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.
let nserror = error as NSError
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
}
}
}
else
{
if managedObjectContext.hasChanges {
do {
try managedObjectContext.save()
} catch {
// Replace this implementation with code to handle the error appropriately.
// abort() 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.
let nserror = error as NSError
NSLog("Unresolved error \(nserror), \(nserror.userInfo)")
abort()
}
}
}
}
CoredataHelper.swift
class func fetchCoreData(_ entity:String, key:String, order : Bool) -> NSArray {
//let context: NSManagedObjectContext = appDelegate.managedObjectContext
if #available(iOS 10.0, *) {
let persistantContainer = appDelegate.persistentContainer
let privateManagedObjectContext = persistantContainer.newBackgroundContext()
let managedObjectContext = persistantContainer.viewContext
var arr : NSArray = []
let request = NSFetchRequest<NSFetchRequestResult>(entityName: entity)
let sortDescriptor = NSSortDescriptor(key: key, ascending: order)
request.sortDescriptors = [sortDescriptor]
do {
guard let searchResults : NSArray = try privateManagedObjectContext.fetch(request) as NSArray else {
print("Results were not of the expected structure")
}
arr = searchResults
} catch {
print("Error ocurred during execution: \(error)")
}
return arr
}
else
{
let moc = DbHelper .getContext()
let privateMOC = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
privateMOC.parent = moc
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: entity)
fetchRequest.returnsObjectsAsFaults = false;
let sortDescriptor = NSSortDescriptor(key: key, ascending: order)
fetchRequest.sortDescriptors = [sortDescriptor]
do {
let fetchDetails : NSArray = try moc.fetch(fetchRequest) as NSArray
return fetchDetails
} catch {
Crashlytics.sharedInstance().recordError(error)
return []
}
}
}
class func saveData(_ arr : NSArray) {
for i in 0 ..< arr.count
{
let dictionary = arr .object(at: i) as? NSDictionary
let entityDescription =
NSEntityDescription.entity(forEntityName: “1”,
in: managedObjectContext)
let req = NSFetchRequest<NSFetchRequestResult>(entityName: “1”)
req.sortDescriptors = [NSSortDescriptor(key: "createdAt", ascending: true)]
let localId = "\(String(describing: dictionary!["createdAt"]!))"
let pred = NSPredicate(format:"createdAt == \(localId)")
req.predicate = pred
do {
let fetchedResults = try managedObjectContext.fetch(req) as! [1]
if fetchedResults.count > 0 {
}
else
{
let 1DataObj = 1(entity: entityDescription!,
insertInto: managedObjectContext)
print(dictionary)
1DataObj.createdAt = dictionary!["createdAt"]! as! NSNumber
}
}
catch {
print("Error getting values")
}
}
if managedObjectContext.hasChanges {
do {
try managedObjectContext.save()
} catch let nserror as NSError {
}
}
}
ViewController.swift
// Fetching
Array1 = CoredataHelper .fetchCoreData("1", key:"name", order: true)
Array2 = CoredataHelper .fetchCoreData("2", key:"hubId", order: true)
Array3 = CoredataHelper .fetchCoreData("3", key:"createdAt", order: true)
Array4 = CoredataHelper .fetchCoreData("4", key:"name", order: true)
Array5 = CoredataHelper .fetchCoreData("5", key:"name", order: false)
Array6 = CoredataHelper .fetchCoreData("6", key:"name", order: false)
//Save response from API
CoredataHelper .saveData(arr)
Is there any way I can improve this based on Best Practice I want to implement same for iOS 9 to latest iOS release.
I want to write class for manage all entity that i have in my application.
there is my code fore managerClass for one custom entity, but I have a problem to set manageObject type in this context.
public func clearEntityContex() {
let fetchRequest: NSFetchRequest<Shop> = Shop.fetchRequest()
if let fetchResult = try? self.stack.mainQueueContext.fetch(fetchRequest){
for user : Shop in fetchResult {
self.stack.mainQueueContext.delete(user as Shop)
}
do {
try stack.mainQueueContext.save()
}
catch {
debugPrint(error.localizedDescription)
}
}
}
//MARK: _fetch Contex
public func fetchsShopEntity() -> [Shop]? {
var shops : [Shop]?
let fetchRequest: NSFetchRequest<Shop> = Shop.fetchRequest()
do {
let fetchResult =
try self.stack.mainQueueContext.fetch(fetchRequest)
if fetchResult.count > 0 {
shops = fetchResult
}
}
catch {
fatalError("Failed to fetch Account: \(error)")
}
return shops
}
//MARK: _save entity
public func saveEntityInModel(entityItem : Shop){
if let entity = NSEntityDescription.entity(forEntityName: "Shop", in: self.stack.mainQueueContext) {
if let contex = NSManagedObject(entity: entity, insertInto: self.stack.mainQueueContext) as? Shop {
contex.packID = entityItem.packID
contex.packName = entityItem.packName
contex.packImage = entityItem.packImage
contex.priceDolar = entityItem.priceDolar
contex.packDescription = entityItem.packDescription
do {
try stack.mainQueueContext.save()
}
catch {
debugPrint(error.localizedDescription)
}
}
}
}
for example i want to write method that can clear any entityContext. but i cant pass manageObject to this method.
public func clearEntityContex(entityObject: NSManagedObject) {
let fetchRequest: NSFetchRequest<NSFetchRequestResult> = entityObject.fetchRequest()
if let fetchResult = try? self.stack.mainQueueContext.fetch(fetchRequest){
for entity in fetchResult {
self.stack.mainQueueContext.delete(entity as entityObject)
}
do {
try stack.mainQueueContext.save()
}
catch {
debugPrint(error.localizedDescription)
}
}
}
how can solve pass NSManagedObject to this method?
thanks for all reply
Here's a more generic implementation that we use in our projects.
import CoreData
class ACSwiftCoreData: ACCoreDataPlugin {
let managedObjectModelName: String
let databasePath: URL
init(managedObjectModelName: String, databasePath: URL) {
self.managedObjectModelName = managedObjectModelName
self.databasePath = databasePath
}
// MARK: Managed Object Contexts
private var sharedContext: NSManagedObjectContext?
func getSharedManagedObjectContext() throws -> NSManagedObjectContext {
if let sharedContext = self.sharedContext {
return sharedContext
}
let context = try self.createManagedObjectContext()
self.sharedContext = context
return context
}
func createManagedObjectContext() throws -> NSManagedObjectContext {
let storeCoordinator = try self.getPersistentStoreCoordinator()
let managedObjectContext = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = storeCoordinator
managedObjectContext.mergePolicy = NSMergePolicy(merge: NSMergePolicyType.mergeByPropertyObjectTrumpMergePolicyType)
return managedObjectContext
}
// MARK: Creating Entities
func createEntityInSharedContext<EntityType>(_ entityName: String) throws -> EntityType {
let context = try self.getSharedManagedObjectContext()
return try self.createEntity(entityName, context: context)
}
func createEntity<EntityType>(_ entityName: String, context: NSManagedObjectContext) throws -> EntityType {
let entity = NSEntityDescription.insertNewObject(forEntityName: entityName, into: context)
guard let expectedEntity = entity as? EntityType else {
throw self.errorWithMessage("ACSwiftCoreData: Entity for name \(entityName) does not match class \(EntityType.self).")
}
return expectedEntity
}
// MARK: Saving Entity
func saveEntity(_ entity: NSManagedObject) throws {
guard let context = entity.managedObjectContext else {
throw errorWithMessage("ACSwiftCoreData: Cannot save Entity. ManagedObjectContext is missing.")
}
if context.hasChanges {
try context.save()
}
}
// MARK: Delete Entity
func deleteEntity(_ entity: NSManagedObject) throws {
guard let context = entity.managedObjectContext else {
throw errorWithMessage("ACSwiftCoreData: Cannot delete Entity. ManagedObjectContext is missing.")
}
context.delete(entity)
try context.save()
}
// MARK: Fetch Requests
func fetchEntitiesInSharedContext<EntityType: AnyObject>(_ entityName: String, predicate: NSPredicate?) -> [EntityType] {
guard let context = try? self.getSharedManagedObjectContext() else {
return [EntityType]()
}
return self .fetchEntities(entityName, context: context, predicate: predicate)
}
func fetchEntities<EntityType: AnyObject>(_ entityName: String, context: NSManagedObjectContext, predicate: NSPredicate?) -> [EntityType] {
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: entityName)
fetchRequest.predicate = predicate
let results = try? context.fetch(fetchRequest)
guard let resultEntitys = results as? [EntityType] else {
return [EntityType]()
}
return resultEntitys
}
// MARK: Technical Details
private var storeCoordinator: NSPersistentStoreCoordinator?
private func getPersistentStoreCoordinator() throws -> NSPersistentStoreCoordinator {
if let storeCoordinator = self.storeCoordinator {
return storeCoordinator
}
let model = try self.getManagedObjectModel()
let storeCoordinator = NSPersistentStoreCoordinator(managedObjectModel: model)
var options = [AnyHashable: Any]()
options[NSMigratePersistentStoresAutomaticallyOption] = true
options[NSInferMappingModelAutomaticallyOption] = true
try storeCoordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: self.databasePath, options: options)
self.storeCoordinator = storeCoordinator
return storeCoordinator
}
private var objectModel: NSManagedObjectModel?
private func getManagedObjectModel() throws -> NSManagedObjectModel {
if let objectModel = self.objectModel {
return objectModel
}
let momName = self.managedObjectModelName
guard let modelUrl = Bundle.main.url(forResource: momName, withExtension:"momd") else {
throw self.errorWithMessage("ACSwiftCoreData: DataModel Url could not be created.")
}
guard let objectModel = NSManagedObjectModel(contentsOf: modelUrl) else {
throw self.errorWithMessage("ACSwiftCoreData: DataModel could not be loaded.")
}
self.objectModel = objectModel
return objectModel
}
// MARK: Error handling
private func errorWithMessage(_ message: String) -> NSError {
let userInfo = [NSLocalizedDescriptionKey: message]
let error = NSError(domain: "com.appcron.accomponents", code: 0, userInfo: userInfo)
return error
}
}
In some project that I made and when I used CoreData, I usually create a Singleton with function to fetch, save and delete a CoreData object.
This is my CoreDataController:
import Foundation
import CoreData
import UIKit
final class CoreDataController {
static let sharedInstances = CoreDataController()
private var context: NSManagedObjectContext
private init(){
let application = UIApplication.shared.delegate as! AppDelegate
self.context = application.persistentContainer.viewContext
}
func loadAll() {
print("Fetch from CoreData")
let fetchRequest: NSFetchRequest<YourEntity> = YourEntity.fetchRequest()
do {
let entityArray = try self.context.fetch(fetchRequest)
guard entityArray.count > 0 else {
print("There aren't element in CoreData "); return}
} catch let error {
print("FetchRequest error")
print(" Print error: \n \(error) \n")
}
}
func save(entityToSave: String, item: String){
let entity = NSEntityDescription.entity(forEntityName: entityToSave, in: self.context)
let newItem = YourEntity(entity: entity!, insertInto: self.context)
newItem.name = item
do {
try self.context.save()
} catch let error {
print("Problem with \(newItem)")
print(" Print error: \n \(error) \n")
}
print("Element \(newItem) saved in CoreData")
}
func loadFromName(entityName:String, name: String) -> Any {
let request = NSFetchRequest<NSFetchRequestResult>(entityName: entityName)
request.returnsObjectsAsFaults = false
let predicate = NSPredicate(format: "yourEntityAttribute = %#", yourEntityAttribute)
request.predicate = predicate
let items = self.loadFromFetchRequest(request: request)
return items[0]
}
private func loadFromFetchRequest(request: NSFetchRequest<NSFetchRequestResult>) -> [Any] {
var array = [Any]()
do {
array = try self.context.fetch(request)
guard array.count > 0 else {print("There aren't element in CoreData"); return []}
for item in array {
print("Item: \(item)")
}
} catch let error {
print("FetchRequest Error")
print(" Print Error: \n \(error) \n")
}
return array
}
func delete(entityName: String, name: String) {
let item = self.loadFromName(entityName: entityName, name: name)
self.context.delete(item as! NSManagedObject)
do {
try self.context.save()
} catch let error {
print("Deleting problem")
print("Print Error: \n \(error) \n")
}
}
func loadData(entity: String) -> [YourEntity] {
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: entity)
fetchRequest.returnsObjectsAsFaults = false
var data = [YourEntity]()
do {
data = try self.context.fetch(fetchRequest) as! [YourEntity]
} catch let error {
print("Print Error: \n \(error) \n")
}
return data
}
}
When you have to call, just write:
CoreDataController.sharedInstances.save(entityToSave: "Profile", item: textfield.text!)
or other functions!!!
I hope this is useful for you.
For use this Class with every Entity you can write like that:
let entity = NSEntityDescription.entity(forEntityName: entityToSave, in: self.context)
let newItem: Any
switch entityToSave {
case "YourEntity":
newItem = YourEntity(entity: entity!, insertInto: self.context)
(newItem as! YourEntity).entityAttribute = firstItem
(newItem as! YourEntity).entityAttribute = secondItem
case "YourEntity2":
newItem = YourEntity2(entity: entity!, insertInto: self.context)
(newItem as! YourEntity2).entityAttribute = firstItem
(newItem as! YourEntity2).entityAttribute = secondItem
case "YourEntity3":
newItem = YourEntity3(entity: entity!, insertInto: self.context)
(newItem as! YourEntity3).entityAttribute = firstItem
(newItem as! YourEntity3).entityAttribute = secondItem
case "YourEntity4":
newItem = YourEntity4(entity: entity!, insertInto: self.context)
(newItem as! YourEntity4).entityAttribute = firstItem
(newItem as! YourEntity4).entityAttribute = secondItem
default:
fatalError("Error in entityToSave function")
}
do {
try self.context.save()
} catch let error {
print("Problem to save \(newItem)")
print("Print Error: \n \(error) \n")
}
print("Element \(newItem) saved correctly")
}
I'm working on an existing project, using CoreData, that:
Adds many many items from different CoreData Entity types, after receiving them from web service, but it blocks the UI Thread for many seconds, even if I use it in another thread.
Please help me, is there any way to prevent CoreData from blocking UI with minimum changes as the project is almost completed?
I'm new to CoreData and I don't have enough time to study the doc or reprogram the source code, unfortunately.
My DataController:
class DataController {
var managedObjectContext: NSManagedObjectContext
let modelName = "something"
init(closure:()->()) {
guard let modelURL = NSBundle.mainBundle().URLForResource(modelName, withExtension: "momd"),
let managedObjectModel = NSManagedObjectModel.init(contentsOfURL: modelURL)
else {
fatalError("DataController - COULD NOT INIT MANAGED OBJECT MODEL")
}
let coordinator = NSPersistentStoreCoordinator.init(managedObjectModel: managedObjectModel)
managedObjectContext = {
let parentContext = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
parentContext.persistentStoreCoordinator = coordinator
let managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
managedObjectContext.parentContext = parentContext
return managedObjectContext
}()
dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_USER_INITIATED.rawValue), 0)) {
let options = [
NSMigratePersistentStoresAutomaticallyOption: true,
NSInferMappingModelAutomaticallyOption: true,
NSSQLitePragmasOption: ["journal_mode": "DELETE"]
]
let documentsURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).last
let storeURL = NSURL.init(string: "something", relativeToURL: documentsURL)
do {
try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeURL, options: options)
dispatch_async(dispatch_get_main_queue()) {
closure()
}
}
catch let error as NSError {
fatalError("DataController - COULD NOT INIT SQLITE STORE: \(error.localizedDescription)")
}
}
}
func save(moc: NSManagedObjectContext? = nil) {
var managedObjectContext = moc
if moc == nil {
managedObjectContext = self.managedObjectContext
}
managedObjectContext!.performBlockAndWait {
if managedObjectContext!.hasChanges {
do {
try managedObjectContext!.save()
} catch {
print("ERROR saving context \(managedObjectContext!.description) - \(error)")
}
}
if let parentContext = managedObjectContext!.parentContext {
self.save(parentContext)
}
}
}
}
My classes are something like:
class MOCity: NSManagedObject {
#NSManaged var id: NSNumber?
#NSManaged var name: String?
// Insert code here to add functionality to your managed object subclass
class func save(id: NSNumber, json: JSON, managedObjectContext: NSManagedObjectContext) {
guard let newObject = MOCity.getById(id, create: true, managedObjectContext: managedObjectContext) else {
fatalError("City - NO OBJECT")
}
newObject.id <-- json["Id"]
newObject.name <-- json["Name"]
}
internal class func getById(id: NSNumber, create: Bool = false, managedObjectContext: NSManagedObjectContext) -> MOCity? {
let fetchRequest = NSFetchRequest(entityName: "City")
fetchRequest.predicate = NSPredicate(format: "id = \(id)")
do {
guard let fetchResults = try managedObjectContext.executeFetchRequest(fetchRequest) as? [MOCity] else {
fatalError("city - NO FETCH RESULTS")
}
if fetchResults.count > 0 {
return fetchResults.last
}
else if fetchResults.count == 0 {
if create {
guard let object = NSEntityDescription.insertNewObjectForEntityForName("City", inManagedObjectContext: managedObjectContext) as? MOCity else {
fatalError("getCity - COULD NOT CREATE OBJECT")
}
return object
} else {
return nil
}
}
}
catch let error as NSError {
...
}
return nil
}
}
And my web service class:
Alamofire.request(.POST, url,parameters:data).validate().responseJSON(completionHandler: {response in
switch response.result {
case .Success:
if let jsonData = response.result.value {
if let array = jsonData as? NSArray {
for arrayItem in array {
MOCity.save(arrayItem["Id"] as! NSNumber, json: JSON(arrayItem as! NSDictionary)!, managedObjectContext: self.dataController.managedObjectContext)
}
self.dataController.save()
}
}
break
case .Failure(let error):
...
}
})
One of the issue could be connected for the QOS level.
QOS_CLASS_USER_INITIATED: The user initiated class represents tasks
that are initiated from the UI and can be performed asynchronously. It
should be used when the user is waiting for immediate results, and for
tasks required to continue user interaction.
Try to use QOS_CLASS_BACKGROUND.
I would suggest two things. First, save the private queue context inside a performBlock function just as you do the main queue context. Second, set breakpoints at different places, such as before and after saves, to check which thread is calling that code (using NSThread's thread checking methods). That may enlighten you as to what code is being executed from which thread. Hope that helps steer you in the right direction.
I get objects array from core data:
lazy var managedObjectContext : NSManagedObjectContext? = {
let appDelegate = UIApplication.sharedApplication().delegate as AppDelegate
if let managedObjectContext = appDelegate.managedObjectContext {
return managedObjectContext
}
else {
return nil}
}()
var tests=[Test]()
func fetchLog() {
let fetchRequest = NSFetchRequest(entityName: "Test")
if let fetchResults = managedObjectContext!.executeFetchRequest(fetchRequest, error: nil) as? [Test] {
tests = fetchResults
}
And then tray to update it:
managedObjectContext?.updatedObjects(tests[atRow] as NSManagedObject)
But get error: '(NSManagedObject) -> $T5' is not identical to 'NSSet'
Whats wrong?
NSManagedObject is registered with NSManagedObjectContext(MOC). To update it, call MOC's save().
like this:
let aTest = test[atRow] as Test
// update through properties like aTest.id = 2
...
// and save
var error: NSError?
if managedObjectContext!.save(&error) {
println("saved successfully")
} else {
println("failed to save")
if let saveError = error {
println("error=\(saveError.localizedDescription)")
}
}