Core Data with Swift two entities - ios

I am trying to use Core Data to save some of my application data. I have following classes. Basically I want to store the properties of each job, and use it later on.
Following is the class I currently use in my application.
class Job {
var name:String?
var count = 1
var id:String
var startDate:NSDate?
var finishDate:NSDate?
var expected:NSDate?
var detail:Array<JobDetail> = []
var isFinished:Bool?
var sender:String?
var receiver:String?
init(name:String?, id:String) {
self.name = name
self.id = id
self.isFinished = false
self.startDate = NSDate()
}
func addDetail (message:String?, date:NSDate?, location:String?, status: DetailStatus) {
detail.append(JobDetail(message: message, date: date, location: location, status: status))
if status == DetailStatus.OK {
self.isFinished = true
self.finishDate = date
}
}
}
enum DetailStatus {
case OK
case Error
case Exception
case Unknown
}
class JobDetail {
var message:String?
var date:NSDate?
var location:String?
var status:DetailStatus
init(message:String?, date:NSDate?, location:String?, status: DetailStatus) {
self.message = message
self.date = date
self.location = location
self.status = status
}
}
NSManagedObject sub class I created with Xcode after I create the data model.
class Job: NSManagedObject {
#NSManaged var name: String
#NSManaged var count: NSNumber
#NSManaged var id: String
#NSManaged var startDate: NSDate
#NSManaged var finishDate: NSDate
#NSManaged var expected: NSDate
#NSManaged var isFinished: NSNumber
#NSManaged var sender: String
#NSManaged var receiver: String
#NSManaged var details: NSSet
}
class JobDetail: NSManagedObject {
#NSManaged var message: String
#NSManaged var date: NSDate
#NSManaged var location: String
#NSManaged var status: NSNumber
#NSManaged var parent: Job
}
Here are the screenshots of my data model.
Basically I want to CRUD Job in my application so that I can show them in my tableview. I have everything setup, but because I couldn’t setup Core Data I don’t have persistence. I will appreciate if you can help me to setup Core Data.

Refer this. May be it's useful to you...
http://www.raywenderlich.com/85578/first-core-data-app-using-swift

It seems from the screenshots that your setup is correct. Link details with jobs like this.
detail1.parent = job
detail2.parent = job
context.save(nil)
Get all details for a job like this
job.details
This is unordered, but you can sort them using sortedArrayUsingDescriptors.
let sortedDetails = job.details.sortedArrayUsingDescriptors(
[NSSortDescriptor(key:"date" ascending: false)])

Related

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?

Parse+Swift, "-[Swift._NSContiguousString objectId]: unrecognized selector sent to instance" error

I'm using Parse and I had a PFObject I was using to represent a "Job". It worked fined, but it was tedious always using setObject:forKey: and objectForKey: rather than accessing properties.
So, I decided to make a "proper" PFObject subclass. Now, every call made to "objectId" gives the above unrecognized selector error -- even calls that have nothing to do with my subclass.
I created my subclass "by the book", as far as I can tell (below), and I do call Job.registerSubclass() before Parse.setApplicationId: in my AppDelegate. Anybody seen this problem?
import Foundation
import Parse
class Job: PFObject, PFSubclassing {
#NSManaged var categoryName: String
#NSManaged var categoryId: String
#NSManaged var state: String
#NSManaged var details: String?
#NSManaged var jobDescription: String
#NSManaged var location: String
#NSManaged var dates: [String]
#NSManaged var images: PFFile?
#NSManaged var questionSequence: [String]?
#NSManaged var consumerResponseIndices: [Int]?
#NSManaged var isPosted: Bool
#NSManaged var bids: [AnyObject]?
override class func initialize() {
struct Static {
static var onceToken : dispatch_once_t = 0;
}
dispatch_once(&Static.onceToken) {
self.registerSubclass()
}
}
class func parseClassName() -> String {
return "Job"
}
}
I got the same issue before.
You may have this error when trying to convert NSArray/NSDictionary to String type, so it turns to NSContiguousString type.
You can check:
dates
questionSequence
consumerResponseIndices
bids
to see if this happened.
In my case the problem was :
if let countryLocale = (notification.userInfo![Constants.CountryLocale]!.firstObject as? String { code }
and solved with
if let countryLocale = (notification.userInfo![Constants.CountryLocale] as! [AnyObject]).first as? String { code }

Looping through Core Data relationship

In my calendarViewController I'd like to prepare an array containing dates. In my model i have to-may relationship where one medicine can have multiple dates of taking pill. How can i perform a loop through this set to append an array ?
My models:
extension Medicine {
#NSManaged var amount: String?
#NSManaged var endDate: String?
#NSManaged var name: String?
#NSManaged var time: String?
#NSManaged var notificationSet: NSNumber?
#NSManaged var taken: NSOrderedSet?
}
Model Dates
extension Dates {
#NSManaged var date: NSDate?
#NSManaged var takes: Medicine?
}
I'd like to perform loop like this ,but instead these dates i'd like those from CoreData:
var dates = [NSDate(timeIntervalSinceNow: 60*60*24*2), NSDate(timeIntervalSinceNow: 60*60*24*3), NSDate(timeIntervalSinceNow: 60*60*24*5), NSDate(timeIntervalSinceNow: 60*60*24*7)]
func calendar(calendar: CKCalendarView!, configureDateItem dateItem: CKDateItem!, forDate date: NSDate!) {
for dateTaken in dates {
if calendar.date(date, isSameDayAsDate: dateTaken) {
dateItem.backgroundColor = UIColor.redColor()
}
}
}
Fetch your Medicine entity and then create the array like this:
let dates = medicine.taken.map { $0.date }
Not sure about the NSOrderedSet which tends to be buggy so I generally avoid it, but you can try appending as! [NSDate] to make sure you have an proper array of dates.

Usage NSManagedObject model in code

I have a User model class (generated by XCode with Swift):
#objc(User)
class User: NSManagedObject { }
And it's extension:
extension User {
#NSManaged var id: NSNumber?
#NSManaged var firstName: String?
#NSManaged var lastName: String?
#NSManaged var birthYear: NSNumber?
}
I can save/fetch data from CoreData.
But can I use this class for object management without CoreData things? Or i need to create other class/struct for this?
For example, create User object (without ObjectContext), set his attributes and send it as property in some func? Maybe i can create some struct in class User (like struct {var firstNameData, secondNameData,...}) and use it in code?
I updated class:
struct User {
var id: Int!
var firstName: String!
var lastName: String!
var birthYear: UInt?
}
#objc(UserManagedObject)
class UserManagedObject: NSManagedObject {
func toStruct() -> User {
var userData = User()
userData.id = Int(self.id)
userData.firstName = self.firstName
userData.lastName = self.lastName
if let by = self.birthYear {
userData.birthYear = UInt(by)
}
return userData
}
}
Now for object management i use struct User and UserManagedObject for CoreData in/out

Can't delete NSOrderedSet from CoreData

I have one to many relationship in my coredata model. For each News object I have many details object.
class Job: NSManagedObject {
#NSManaged var name: String
#NSManaged var count: NSNumber
#NSManaged var id: String
#NSManaged var startDate: NSDate
#NSManaged var finishDate: NSDate
#NSManaged var expected: NSDate
#NSManaged var isFinished: NSNumber
#NSManaged var sender: String
#NSManaged var receiver: String
#NSManaged var details: NSOrderedSet
}
class JobDetail: NSManagedObject {
#NSManaged var message: String
#NSManaged var date: NSDate
#NSManaged var location: String
#NSManaged var status: NSNumber
#NSManaged var parent: Job
}
So, how can I remove all details from Job? My current approach is to delete Job itself and create it again which is slower.
I have tried to delete with,
context?.deletedObjects(myJob.detail)
but it didn’t work. It says
'(#lvalue NSOrderedSet) -> _' is not identical to 'Set'
It seems that you mixed-up deleteObject() with deletedObjects().
deletedObjects() is a method to get a list of all managed objects which
have been marked for deletion in the managed object context. What you have to call is deleteObject()
for each object. Something like (not compiler-checked):
for detail in myJob.details {
context.deleteObject(detail)
}

Resources