Core data Relationship in swift - ios

I am working on iPhone application where I need to implement one to many relationship from one entity to another entity.I have export NSManagedObject subclass from coredata database but could not find relation accessors in core data model file like Objective-C.
Although I'm able to set data in relationship NSSet but this only persist while iPhone app is running. Once I kill and restart the application I didn't get entity relationship for in fetch request from core data.
I am not sure what I am doing wrong. It will be great if anyone can tell me how to set data in NSSet relationship object. Any sample example will be a great help
Here is my core data model files. One folder can content multiple content as folder detail
extension FolderContent {
#NSManaged var contentID: NSNumber?
#NSManaged var contentTitle: String?
#NSManaged var contentType: String?
#NSManaged var publishDate: String?
#NSManaged var folderList: NSSet?
}
extension FolderList {
#NSManaged var folderID: NSNumber?
#NSManaged var folderName: String?
#NSManaged var folderDetail: NSSet?
}
func updateFolderList()
{
// Initialize Fetch Request
let fetchRequest = NSFetchRequest()
// Create Entity Description
let entityDescription = NSEntityDescription.entityForName(FOLDER_LIST, inManagedObjectContext: self.managedObjectContext)
// Configure Fetch Request
fetchRequest.entity = entityDescription
do {
let result = try self.managedObjectContext.executeFetchRequest(fetchRequest).last as! FolderList
let content = result.mutableSetValueForKey("folderDetail")
content.addObject(self.getContent())
var folderContent:FolderContent = result.folderDetail?.allObjects.first as! FolderContent
print(folderContent.contentTitle)
self.save()
print(result)
} catch {
let fetchError = error as NSError
print(fetchError)
}
}
func getContent()->FolderContent
{
let folderContent = NSEntityDescription.insertNewObjectForEntityForName(FOLDER_CONTENT, inManagedObjectContext: self.managedObjectContext) as! FolderContent
folderContent.contentID = 1
folderContent.contentTitle = "Sandeep"
folderContent.contentType = "Product"
return folderContent
}

If the relationship of FolderContent and FolderList is defined as
A FolderContent have many FolderList(s)
A FolderList only belongs to a FolderContent
FolderContent
extension FolderContent {
#NSManaged var contentID: NSNumber?
#NSManaged var contentTitle: String?
#NSManaged var contentType: String?
#NSManaged var publishDate: String?
#NSManaged var folderList: Set<FolderList>?
}
FolderList
extension FolderList {
#NSManaged var folderID: NSNumber?
#NSManaged var folderName: String?
#NSManaged var folderDetail: FolderContent?
}
Let say you want to persist the record and its relationship
func persistRecords() {
// Insert the new records
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let folderContentEntity = NSEntityDescription.entityForName("FolderContent", inManagedObjectContext: managedContext)
let folderListEntity = NSEntityDescription.entityForName("FolderList", inManagedObjectContext: managedContext)
//Create FolderContent record
let folderContentObject = FolderContent(entity: folderContentEntity!, insertIntoManagedObjectContext: managedContext)
folderContentObject.setValue(CONTENTID, forKeyPath: "contentID")
...
//Create FolderList record
let folderListObject = FolderList(entity: folderListEntity!, insertIntoManagedObjectContext: managedContext)
folderListObject.setValue(FOLDERID, forKeyPath: "folderID")
...
//Set relationship here
folderListObject.folderDetail = folderContentObject
do {
try managedContext.save()
} catch let error as NSError {
print("Could not save \(error), \(error.userInfo)")
}
}

I am guessing you did something like folder.folderDetail.addObject. With core data that won't work because core data behind the scenes is doing lots of things to maintain graph integrity. You have to use mutableSetValueForKey to get the set and have core data work its magic.

Related

Core data: The entity is not key value coding for the key

I have six entities in my xcdatamodeld. Article, Section, References, Bookmarks, BookmarkReferences and BookmarkSections. I am trying to save the bookmarked articles separately to the Bookmarks entity. The Article has inverse set to Sections and References similarly Bookmarks has inverse set to BookmarkReferences and BookmarkSections. When i try to set the values from Article NSManagedObject to Bookmarks NSManagedObj i am getting KVC error. I have made sure that there are no typos in the key names. Have a look at the code and log.
extension Bookmarks {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Bookmarks> {
return NSFetchRequest<Bookmarks>(entityName: "Bookmarks");
}
#NSManaged public var id: String?
#NSManaged public var status: String?
#NSManaged public var createdBy: String?
#NSManaged public var articleType: String?
#NSManaged public var modifiedBy: String?
#NSManaged public var tag: String?
#NSManaged public var sendNotification: Bool
#NSManaged public var bookmark: Bool
#NSManaged public var isRead: Bool
#NSManaged public var isArticleFromSearch: Bool
#NSManaged public var searchString: String?
#NSManaged public var isArticleFromCategory: Bool
#NSManaged public var modifiedDate: String?
#NSManaged public var createdDate: String?
#NSManaged public var category: String?
#NSManaged public var subCategory: String?
#NSManaged public var title: String?
#NSManaged public var general: NSDictionary?
#NSManaged public var bookmarkSections: NSSet?
#NSManaged public var bookmarkReferences: NSSet?
}
extension BookmarkSections {
static func saveSectionsForBookmarks(entity: Article, privateContext: NSManagedObjectContext) -> NSSet {
let sections = entity.sections
let sectionED = NSEntityDescription.entity(forEntityName: "BookmarkSections", in: privateContext)
let sectionMO = NSManagedObject(entity: sectionED!, insertInto: privateContext)
let sectionsSet = NSMutableSet()
let allSectionItems = sections?.allObjects
for (_, sectionItem) in (allSectionItems?.enumerated())! {
let castedSection = sectionItem as? Sections
sectionMO.setValue(castedSection!.id, forKey: "id")
sectionMO.setValue(castedSection!.text, forKey: "text")
sectionMO.setValue(castedSection!.imageUrl, forKey: "imageUrl")
sectionMO.setValue(castedSection!.imageName, forKey: "imageName")
sectionMO.setValue(castedSection!.title, forKey: "title")
sectionsSet.add(sectionMO)
}
return sectionsSet
}
}
[`
extension Bookmarks {
public static func saveToNew(Entity: Article) {
let managedContext = CoreDataStorage.sharedInstance.privateQueueContext
let entityDesc =
NSEntityDescription.entity(forEntityName: "Bookmarks",
in: managedContext)!
let articleMO = NSManagedObject(entity: entityDesc,
insertInto: managedContext)
articleMO.setValue(Entity.id, forKey: "id")
articleMO.setValue(Entity.status, forKey: "status")
articleMO.setValue(Entity.createdBy, forKey: "createdBy")
articleMO.setValue(Entity.articleType, forKey: "articleType")
articleMO.setValue(Entity.modifiedBy, forKey: "modifiedBy")
let isSendNotification=NSNumber(value: false)
articleMO.setValue(isSendNotification, forKey: "sendNotification")
let modifiedDate = String(describing: Entity.modifiedDate)
articleMO.setValue(modifiedDate, forKey: "modifiedDate")
let createdDate = String(describing: Entity.createdDate)
articleMO.setValue(createdDate, forKey: "createdDate")
articleMO.setValue(Entity.category, forKey: "category")
articleMO.setValue(Entity.subCategory, forKey: "subCategory")
let general = NSMutableDictionary()
let dict = Entity.general
for (key,subJson):(Any, Any) in dict! {
general.setValue(subJson as! String, forKey: key as! String)
}
articleMO.setValue(general, forKey: "general")
articleMO.setValue(Entity.title, forKey: "title")
articleMO.setValue(BookmarkSections.saveSectionsForBookmarks(entity: Entity, privateContext: managedContext), forKey: "bookmarkSections")
articleMO.setValue(Entity.references, forKey: "references")
do {
try managedContext.save()
} catch let error as NSError {
print("Could not save. \(error), \(error.userInfo)")
}
}
`]
Error:

Batch Update request puts NSNumber fields to nil [Swift]

I'm working with web app. I have UITableViewController which deals with NSFetchedResultsController. I store tableView's objects through CoreData. When a user refreshes UI I perform server request and then call batch update for each new entity, my CoreData class looks like:
extension DBOrder {
#NSManaged var comment: String
#NSManaged var date: NSNumber
#NSManaged var id: NSNumber
#NSManaged var maturity_date: NSNumber
#NSManaged var number_of_tasks: NSNumber
#NSManaged var price: NSNumber
#NSManaged var status: String
#NSManaged var subject: String
#NSManaged var taskImages: [String]
#NSManaged var theme: String
}
"id" is unique for each object. "propertiesToUpdate" consists of some fields like "maturity_date": 1470427641000, "status": "some status" and etc. "entityName" is "DBOrder". And privateContext is NSManagedObjectContext type to update entities in background
func updateCoreData(id: NSNumber, entityName: String, propertiesToUpdate: [String: AnyObject], privateContext: NSManagedObjectContext) -> Bool {
let batchRequest = NSBatchUpdateRequest(entityName: entityName)
batchRequest.predicate = NSPredicate(format: "id == %#", id)
if !doesOrderExists(entityName, id: id, context: privateContext) {
return false
}
batchRequest.propertiesToUpdate = propertiesToUpdate
batchRequest.resultType = .UpdatedObjectIDsResultType
do {
let res = try privateContext.executeRequest(batchRequest) as! NSBatchUpdateResult
let orderIDs = res.result as! [NSManagedObjectID]
return (orderIDs.count != 0) ? true : false
} catch {
print(error)
}
return false
}
This function is called for each object that has been loaded from server. If object is already existed then I update it else create the new one.
Finally, the problem: when I use batch update it works incorrect with NSNumber. It always puts NSNumber fields to nil and works as it should with String fields. So, what I'm doing wrong?

Creating core data for chat app in swift

I’m developing an application similar to an simple chat app and I need to use core data to save/fetch the chats based on specific user. I have now successfully created a model to save users, now I need to link them to their chats.
For Saving user data I’ve created Entity UserData and below are my swift files
UserData+CoreDataProperties.swift
import Foundation
import CoreData
extension UserData {
#NSManaged var firstName: String?
#NSManaged var userStatus: String?
#NSManaged var userId: String?
#NSManaged var userStatus: String?
#NSManaged var lastName: String?
#NSManaged var profilePic: String?
}
UserData.swift
class UserData: NSManagedObject {
convenience init(WithDictionary dict:NSDictionary){
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let newEntity = NSEntityDescription.entityForName("UserData", inManagedObjectContext:managedContext)
// initializer on NSManagedObject class
self.init(entity: newEntity!, insertIntoManagedObjectContext: managedContext)
self.userId = (dict.valueForKey(ID) != nil) && !(dict.valueForKey(ID) is NSNull)
? dict.valueForKey(ID) as! String
: ""
//similary initializing other variables
//Saving to database
do {
try managedContext.save()
} catch let error as NSError {
print("Could not save \(error), \(error.userInfo)")
}
}
I was able to successfully save the user list using this class. Now I’ve created a ChatData Entity, below is its structure.
ChatData+CoreDataProperties.swift
import Foundation
import CoreData
extension ChatData {
#NSManaged var chatId: String?
#NSManaged var chatOwnerName: String?
#NSManaged var chatReadStatus: Boolean?
#NSManaged var chatText: String?
#NSManaged var chatTimestamp: String?
#NSManaged var chatType: String?
#NSManaged var imageData: String?
#NSManaged var imageUrl: String?
}
Now for each specific user I should be able to create multiple ChatData objects corresponding to particular userId. I don’t have much experience with core data. I’ve looked at some samples on creating relationship, but I’m still not sure how it works. Do I need to create a relationship? If so how should I do it to make this work?
You can modify UserData like this
extension UserData {
#NSManaged var firstName: String?
#NSManaged var userStatus: String?
#NSManaged var userId: String?
#NSManaged var userStatus: String?
#NSManaged var lastName: String?
#NSManaged var profilePic: String?
#NSManaged var chats: Set<ChatData>?
}
and ChatData like this
extension ChatData {
#NSManaged var chatId: String?
#NSManaged var chatOwnerName: String?
#NSManaged var chatReadStatus: Boolean?
#NSManaged var chatText: String?
#NSManaged var chatTimestamp: String?
#NSManaged var chatType: String?
#NSManaged var imageData: String?
#NSManaged var imageUrl: String?
#NSManaged var user: UserData?
}
The corresponding core data model has a one-to-many releationship from UserData to ChatData (some attributes are missing):
Some code that shows how you can create a user with two messages:
let user=userWithName("MyName")
let chat01=chatWithText("How do you doing?")
let chat02=chatWithText("Fine.")
user?.chats=[chat01!,chat02!]
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
appDelegate.saveContext()
with
func userWithName(name:String) -> UserData? {
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let userEntity = NSEntityDescription.entityForName("UserData", inManagedObjectContext:managedContext)
let user = NSManagedObject(entity: userEntity!, insertIntoManagedObjectContext: managedContext) as! UserData
user.firstName=name
return user
}
func chatWithText(text:String) -> ChatData? {
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
let chatEntity = NSEntityDescription.entityForName("ChatData", inManagedObjectContext:managedContext)
let chat = NSManagedObject(entity: chatEntity!, insertIntoManagedObjectContext: managedContext) as! ChatData
chat.chatText=text
return chat
}

How to delete several different data from Core Data in Swift?

I have Core Data with five objects. It's four String and one NSData (UIImage). I save them in Core Data. Then load in UIViewController and I want to delete them from Core Data and I got different errors. I tried different methods but it doesn't helped me. How do I delete these data?
var name: String!
var text: String!
var image: UIImage!
var url: String!
var data: String!
func deleteAll() {
var appDel: AppDelegate = (UIApplication.sharedApplication().delegate as! AppDelegate)
var managedObjectContext: NSManagedObjectContext = appDel.managedObjectContext!
var coreDataName: NSManagedObject = (name as AnyObject?) as! NSManagedObject
var coreDataImage: NSManagedObject = (image as AnyObject?) as! NSManagedObject
var coreDataText: NSManagedObject = (text as AnyObject?) as! NSManagedObject
var coreDataData: NSManagedObject = (data as AnyObject?) as! NSManagedObject
var coreDataURL: NSManagedObject = (url as AnyObject?) as! NSManagedObject
managedObjectContext.deleteObject(coreDataName)
managedObjectContext.deleteObject(coreDataImage)
managedObjectContext.deleteObject(coreDataText)
managedObjectContext.deleteObject(coreDataData)
managedObjectContext.deleteObject(coreDataURL)
managedObjectContext.save(nil)
buttonDelete.enabled = false
buttonShare.enabled = false
}
}
My Core Data file
import Foundation
import CoreData
#objc(News)
class News: NSManagedObject {
#NSManaged var dateNewsCoreDataString: String
#NSManaged var imageNewsCoreData: NSData // NSData
#NSManaged var nameNewsCoreData: String
#NSManaged var textNewsCoreData: String
#NSManaged var urlNewsCoreData: String
}
I changed my code and it work for me.
var detailObject: AnyObject? // It's data from FavoriteTableViewController
func deleteAll() {
var appDel: AppDelegate = (UIApplication.sharedApplication().delegate as! AppDelegate)
var managedObjectContext: NSManagedObjectContext = appDel.managedObjectContext!
var dataDelete = detailObject as! NSManagedObject
managedObjectContext.deleteObject(dataDelete)
managedObjectContext.save(nil)
managedObjectContext.save(nil)
buttonDelete.enabled = false
buttonShare.enabled = false
}

How to fetch NSSet from Core Data in Swift

I have a Person class and a Brand class that are a relationship in Core Data. I know how to fetch the entire Person class (NSManagedObject), but how do I drill down to only fetch the brand associated with the person?
import Foundation
import CoreData
class Person: NSManagedObject {
#NSManaged var name: String
#NSManaged var brands: NSSet
func addBrandsObject(value: Brand) {
self.mutableSetValueForKey("brands").addObject(value)
}
}
import Foundation
import CoreData
class Brand: NSManagedObject {
#NSManaged var name: String
#NSManaged var people: NSSet
}
Viewcontroller:
![import UIKit
import CoreData
class BrandsTableTableViewController: UITableViewController {
var brands = \[NSManagedObject\]()
var selectedPerson: Person?
var selectedBrand: Brand?
//Fetch Core Data
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.fetchCoreData()
}
//Helper Function to Fetch Core Data
func fetchCoreData() {
//1
let appDelegate =
UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext!
//2
let fetchRequest = NSFetchRequest(entityName:"Person")
//3
var error: NSError?
let fetchedResults =
managedContext.executeFetchRequest(fetchRequest,
error: &error) as? \[NSManagedObject\]
if let results = fetchedResults {
////what here?
} else {
println("Could not fetch \(error), \(error!.userInfo)")
}
}][1]
Since you already have the person instance, just access the relationship:
for person in results {
let brandSet = person.brands
}
No fetch required.
This is assuming of course that your relationship from Person to Brand is called brands.
There will also be some casting to go around, can't have swift without casting...
You need to tell it that what you are fetching is a "Person" object and then you can iterate through brands.
//Helper Function to Fetch Core Data
func fetchCoreData() {
//1
let appDelegate =
UIApplication.sharedApplication().delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext!
//2
let fetchRequest = NSFetchRequest(entityName:"Person")
//3
var error: NSError?
var people =
managedContext.executeFetchRequest(fetchRequest,
error: &error) as! [Person]
// Get all brands from all people
for (var x=0; x<people.count; x++) {
let brands: NSArray = people[x].brands.allObjects
for brand in brands {
println(brand.name)
}
}
}

Resources