I have NSManagedObject subclass (CarBrand) with just two properties:
public class CarBrand: NSManagedObject, CDCodable {
#NSManaged public var id: Int64
#NSManaged public var name: String
}
Also I'm using Codable protocol to parse it from server response's JSON. In case if response matches (or nearly matches) my model - there isn't problems with duplication of object (I have static method on that class, that returns object from CoreData or Creates new and returns it).
But one of the responses has following structure:
class CarBrandsResponse: Codable {
let offset: Int
let count: Int
let results: [CarBrand]
}
In this case I cannot find solution to call that static method for each CarBrand, because if I write
self.results = try container.decode([CarBrand].self, forKey: .results)
It calls init(from:) in CarBrand for each entry, that means duplication of entity...
I've tried to repeat Decodable behavior, but there I've not found suitable API-method for that. (unkeyedContainer(), nestedContainer(), etc.)
Hope you will help me to solve this problem or suggest another way to avoid duplicating.
Thank you.
Related
I am trying to implement objects similar to CNContact and CNMutableContact such that I can retrieve my immutable objects from a custom store and make editions only to mutable copies of the objects.
To tackle this I have taken a look at the CNContact source available through Xcode to find this:
open class CNContact : NSObject, NSCopying, NSMutableCopying, NSSecureCoding {
/*! The identifier is unique among contacts on the device. It can be saved and used for fetching contacts next application launch. */
open var identifier: String { get }
open var contactType: CNContactType { get }
...
}
However when I try something similar I get an error:
class MyModel: NSObject, NSCoding, NSSecureCoding {
// MARK: - Properties
var name: String { get }
...
}
The error being Expected '{' to start a getter definition. What am I doing wrong here?
The next step then would be to have a mutable subclass, very similar to CNMutableContact which would looks like this:
class MutableMyModel: MyModel {
// MARK: - Properties
var name: String
...
}
If my approach to this problem is sensible then why am I seeing this syntax error, and why is it valid within the Apple written code but not my own?
Finally am I going to hit any issues within my subclass where I am essentially redefining the name property?
var name: String { get } is only a definition in the protocol. A conforming class, such as MyModel needs to implement the definition, like so:
class MyModel: NSObject, NSCoding, NSSecureCoding {
var name: String { return "String Value here" }
}
I'm using CoreData with MagicalRecord for store management in my app. I made the xcdatamodel and generated the NSManagedObjects subclasses. My problem is that when I try to use one of the generated accessors for a relationship I get an 'unimplemented SQL generation for predicate' error. This is happening only with one of the generated accessors.
To be more specific, here's the code for the classes I'm using. The problem I have is in the Crop -> Row relationship, which is many to many:
extension Crop {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Crop> {
return NSFetchRequest<Crop>(entityName: "Crop");
}
#NSManaged public var phLevels: Int16
#NSManaged public var row: NSSet?
#NSManaged public var states: NSSet?
// MARK: Generated accessors for row:
extension Crop {
#objc(addRowObject:)
#NSManaged public func addToRow(_ value: Row)
#objc(removeRowObject:)
#NSManaged public func removeFromRow(_ value: Row)
#objc(addRow:)
#NSManaged public func addToRow(_ values: NSSet)
#objc(removeRow:)
#NSManaged public func removeFromRow(_ values: NSSet)
Here's the Row:
extension Row {
#nonobjc public class func fetchRequest() -> NSFetchRequest<Row> {
return NSFetchRequest<Row>(entityName: "Row");
}
#NSManaged public var name: String?
#NSManaged public var crops: NSSet?
#NSManaged public var paddock: Paddock?
}
// MARK: Generated accessors for crops
extension Row {
#objc(addCropsObject:)
#NSManaged public func addToCrops(_ value: Crop)
#objc(removeCropsObject:)
#NSManaged public func removeFromCrops(_ value: Crop)
#objc(addCrops:)
#NSManaged public func addToCrops(_ values: NSSet)
#objc(removeCrops:)
#NSManaged public func removeFromCrops(_ values: NSSet)
I'm trying to add a Row to the Crop:
plantingCrop?.addToRow(row)
And there's when I get the
unimplemented SQL generation for predicate
Or if i try the other way around the same result:
row.addToCrops(plantingCrop!)
I'm a bit lost here, plantingCrop is generated ok, i use other accessors before I set the row in the crop and they are working ok (they are one-to-many relationships), I tried to regenerate the subclasses and the same result. In the debugger I can see at that point that the relationship is faulting when I try to use it. There's probably something wrong that I'm missing but i cannot find it!
UPDATE
Tried with the code below and the same result.
if let rowsFromCrop = plantingCrop?.mutableSetValue(forKey: "row") {
rowsFromCrop.add(row)
}
EDIT
From Apple Docs, the tick on the transient property means that the attribute is a property that you define as part of the model, but which is not saved to the persistent store as part of an entity instance’s data. Core Data does track changes you make to transient properties, so they are recorded for undo operations. Then says:
"If you undo a change to a transient property that uses nonmodeled information, Core Data does not invoke your set accessor with the old value — it simply updates the snapshot information."
I don't quite completely understand this last line, but it might be that because it was a transient property CD wasn't invoking the right accessor?
I'm wondering if it's a good idea to include CRUD methods inside custom Swift classes, or are they better off in a separate class?
For example I have a class called User.swift:
class User {
var firstName: String
var lastName: String
var id: int
}
Now, would it be okay to include the get and create methods here? These methods will make API calls via Alamofire:
class User {
var firstName: String
var lastName: String
var id: int
static func add(user: User) -> User {
let parameters = ["firstName": user.FirstName , "lastName": user.LastName]
return sendRequest(.POST, url: "example.com/users", parameters: parameters)
}
static func getById(userId: Int) -> User {
return sendRequest(.GET, url: "example.com/users/\(userId)")
}
}
Should these methods be in a separate class, like in an ApiHelper class?
My application passes around the User object in arrays and dictionaries in several places, so wondering if it's good to keep it clean with just the properties.
I think better declare such methods in some ApiHelper/Router singletone class, as well as they must work async, work with some parse system (RestKit probably) and return fetched objects via closures with some delay
The different opinions about the right approach often causes a heated discussion. And this also extends to questions whether we should perform validation in the model class (User) or in the controller, how we should handle versions, serialisation, errors, undo/redo, locking, asynchronicity, etc., etc.
And the resulting code should still be clean, comprehensible, extensible, testable and put into a library so that we can reuse it in other projects!
IMHO, there's no right approach. IMHO, I would start with the following principles:
Your User class is seen in your solution as an Entity. An entity has a property ID and possibly other principal methods, for example, it exposes an init which takes a dictionary of attributes where the class/struct instance can be initialised.
That entity also knows about a "Persistent Store", so your User class may also conform to a protocol "Storable", which exposes class and instance methods like save, create, update, delete, query, etc.
This is only the tip of an iceberg what comprises a complete solution. You might look how others have solved this problem. For ideas, see
Use Core Data objects which you populate from the JSON
Implement the Active Record Pattern
Look at some Object Relational Mappers (there are a lot of implementations and libraries). Even though, you need to map from JSON to Objects, these gives some hints.
If you are still not satisfied, take a look at ASP.NET
Leveraging the "Active Record" approach you may start with something like this:
public final class User {
public init(firstName: String, lastName: String) {
self.firstName = firstName
self.lastName = lastName
}
public var firstName: String
public var lastName: String
public internal(set) var id: Int
}
protocol ActiveRecord {
static func create(object: Self) throws -> Self
static func fetch(id: Int) throws -> Self
static func update(object: Self) throws -> Self
static func delete(id: Int) throws
}
extension User: ActiveRecord {
static func create(object: User) throws -> User {
...
}
static func fetch(id: Int) throws -> User {
...
}
static func update(object: User) throws -> User {
...
}
static func delete(id: Int) throws {
...
}
}
I have a CoreDataStore class which has two generic placeholders and can be used for each entity type in the model. The idea is that it fetches an NSManagedObject subclass (based on one of the generic types) from the store, converts it into the appropriate object (based on the other generic type) and returns that object.
The purpose of this behaviour is so I'm keeping the Core Data aspects encapsulated and avoiding passing NSManagedObject instances all around the app.
Example potential usage
This is purely how the usage might look to further demonstrate what I am trying to achieve.
let personStore = CoreDataStore<ManagedPerson, Person>()
let personData = personStore.fetchSomeObject() // personData is a value type Person
I have the following code, separated over several files but shown here in a modified fashion for simplicity.
import Foundation
import CoreData
// MARK: - Core Data protocol and managed object
protocol ManagedObjectProtocol { }
class ManagedPerson: NSManagedObject, ManagedObjectProtocol {
var title: String?
}
class ManagedDepartment: NSManagedObject, ManagedObjectProtocol {
var name: String?
}
// MARK: - Simple struct representations
protocol DataProtocol {
typealias ManagedObjectType: ManagedObjectProtocol
init(managedObject: ManagedObjectType)
}
struct Person {
var title: String?
}
struct Department {
var name: String?
}
extension Person: DataProtocol {
typealias ManagedObjectType = ManagedPerson
init(managedObject: ManagedPerson) {
self.title = managedObject.title
}
}
extension Department: DataProtocol {
typealias ManagedObjectType = ManagedDepartment
init(managedObject: ManagedDepartment) {
self.name = managedObject.name
}
}
class CoreDataStore<ManagedObject: ManagedObjectProtocol, DataObject: DataProtocol> {
func fetchSomeObject() -> DataObject {
var managedObject: ManagedObject // fetch an NSManagedObject
// Error here
return DataObject(managedObject: managedObject)
}
}
The error I am receiving is when I try to initialise the struct in fetchSomeObject:
Cannot invoke initializer for type 'DataObject' with an argument list of type '(managedObject: ManagedObject)'
Obviously the compiler can't figure out that the DataObject (which is restricted to types conforming to DataProtocol) can be initialised with a ManagedObject (which is restricted to types conforming to ManagedObjectProtocol) despite it being declared as such in DataProtocol.
Is there any way to achieve this functionality? Additionally is this a reasonable approach or am I completely off the wall with this?
Update
After a bit of digging it seems that Swift generics are invariant which I believe is causing what I'm running into.
Think your CoreDataStore again, for example, CoreDataStore<ManagedPerson, Department> doesn't make any sense. Why not? Because the Department is a DataProtocol without problem, but its corresponding typealias ManagedObjectType is not ManagedPerson.
The reason why your code won't compile is just the same. Here return DataObject(managedObject: managedObject) you can't initialize an DataObject from an armbitary ManagedObject, only a DataObject.ManagedObjectType is acceptable.
So what you need is a type constraint, add this where clause, your code should work:
class CoreDataStore<ManagedObject: ManagedObjectProtocol, DataObject: DataProtocol
where DataObject.ManagedObjectType == ManagedObject>
I have code that saves an RLMObject subclass to the realm database. This code works and I have used the realm browser to verify that it is saved as expected.
I then want to query the realm database for this object that I saved, and I want to cast it to the RLMObject subclass that is was before I saved it.
Here is the code:
let queryResults = RealmSubclass.allObjects()
for result in queryResults {
if result is RealmSubclass {
let temp = result as RealmSubclass
println(temp.name)
println(temp.dateOfBirth)
println(temp.gender)
}
}
When I check the values in the debug console, using print object, I see values that I expect. However, when I do a type cast to RealmSubclass the resulting object has no correct values, only nil values.
Why could this be? I have read the documentation, to no avail.
EDIT:
Here is the RLMObject subclass:
public class RealmSubclass: RLMObject {
public dynamic var id: String = NSUUID().UUIDString
public dynamic var name: String = ""
public dynamic var dateOfBirth: NSDate = NSDate()
public dynamic var gender: NSString = Consts.Gender.Male
override public class func primaryKey() -> String {
return "id"
}
}
Ok, it seems that the values were actually being returned. What happened is that Swift debugging is not up to standard yet. The debug area was showing incorrect information.