I am working on implementing a subclass of PFObject called Event, in Swift. I followed the subclassing guide in Parse's docs, but I don't understand how and where to write the code that adds data to the ivars. Below is my what I have in my class so far, including the ivars.
#NSManaged var name:String
#NSManaged var time:NSDate
#NSManaged var favorite:Bool
#NSManaged var moderator: String
#NSManaged var speakers: [String]
#NSManaged var slides: PFFile?
#NSManaged var files: [PFFile]?
override class func initialize() {
var onceToken : dispatch_once_t = 0;
dispatch_once(&onceToken) {
self.registerSubclass()
}
}
class func parseClassName() -> String! {
return "Event"
}
Normally, I would implement an init() constructor or something similar. However, I realized that the data would already be contained in the PFObject's dictionary when it is fetched from the server. Where would I put the code to copy across and put this data in the instance vars from the PFObject's dictionary? This is presuming that I would instantiate the object via a query and fetch from the server and not locally using the object() method.
Based on the comment by deadbeef above, I realized that I can just use Swift's getters and setters for computed properties to read and write values from the PFObject's data dictionary.
For example a property that I intend to have as read-only:
var name:String
{
return self["name"] as String
}
And a property I intend to have as read-write
var favorite:Bool
{
get {
return self["favorite"] as Bool
}
set(value) {
self["favorite"] = value
}
}
You don't have to copy manually.
That's the Parse framework job. The objects will be populated with data after a fetch.
Related
I'm trying to access a singleton value that is accessible throughout my application apart from one function. I can get the value at either end of the function however inside the function it can't be accessed. To confirm I have set these values so they are not empty.
My singleton is declared as follows:
var customer: Customer = Customer.sharedInstance
final class Customer {
static let sharedInstance = Customer()
var name: String!
var address: String!
var zipcode: String!
var id: String!
private init() {}
}
I can access any of these values from within viewDidLoad and other methods apart from one which is below:
fileprivate func processCustomer() {
... Do logic here
print(customer.name) // Print's out nil
... perform closure method
... print(customer.name) // Print's nil
}
Any ideas as I've rewritten it twice already. I've also removed fileprivate and renaming the function. It's almost as if the function can't read the value of a singleton more than once??
Thanks
Scenario
I am working on an SDK that will be used in two separate projects. Both projects will be using CoreData and the SDK needs to be fed data that is present in both project's managed object model. To do this I am employing a protocol-oriented design to get the SDK the data it needs.
The project will start out with a NSManagedObject base class that contains it's properties...
// Project
class Tag: NSManagedObject {
var name: String!
var code: String!
var items: [Any]!
...
}
These names are basic and simple. It would be nice to not have to change these when we conform this class to the protocol.
The SDK declares two protocols. The first protocol will be the data source the SDK will need to populate it's views. Some class in the project will conform to this and be the delegate...
// SDK
protocol SDKDataSource {
func getTaggableObjects() -> [Taggable]
}
var delegate: SDKDataSource!
The second protocol will be the ultimatum the SDK will make to it's project that the NSManagedObject it will be fed should conform to...
// SDK
protocol Taggable {
var name: String { get }
var code: String { get }
var items: [Any] { get }
}
In the project I will create an extension of the class and agree to conform that class to the Taggable protocol...
// Extension (in Project w/ SDK included)
extension Tag: Taggable {
var name: String {
get {
self.name
}
}
var code: String {
get {
self.code
}
}
var items: [Any] {
get {
self.items
}
}
}
This way when the SDK asks it's datasource to getTaggableObjects() the SDK will receive the objects from the Project's CoreData model that it can understand.
Question
Does the name of property in the protocol extension have to be different than the name of the property that is in the base class, or will doing the above work? It would be nice if I knew this is okay before I implement my design.
I can confirm that using the same name is not allowed.
I tried and it's yes.
protocol MyArray {
var count: Int {get}
}
extension Array: MyArray {
}
But you can't make different implementations for different protocols that have the same function or property.
Update
If protocol and base class have property with the same name but different type. You can make different implementations.
protocol MyArray {
var count: Float {get}
}
extension Array: MyArray {
var count: Float {
get {
return 0.0
}
}
}
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 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 a dictionary which I have all data that I want to insert into database as new object. The problem is when I try to cast the newly created object it gives me exception in:
libswift_stdlib_core.dylib`swift_dynamicCast:
part of assembly code.
The code that i am using is like this:
var group:Group
if (array.count == 0) {
group = NSEntityDescription.insertNewObjectForEntityForName("Group", inManagedObjectContext:appDelegate.managedObjectContext) as Group
}
and the structure of Group class is like this:
#objc(Group)
class Group:NSManagedObject{
#NSManaged var createDate:NSString
#NSManaged var groupPictureUrl:NSString
#NSManaged var groupTypeId:NSString
#NSManaged var isDepartment:NSString
#NSManaged var lastMessageRead:NSString
#NSManaged var name:NSString
#NSManaged var unreadMessageCount:NSString
#NSManaged var groupId:NSString
#NSManaged var lastSync:NSString
}
I have also a subclass of NSManagedObject named AppData which has the same structure with Group class. And at inserting part of my code if I replace Group with AppData it works and I can insert into AppData table. As I said before they have the same structure except parameters. But when I try to insert Group object it gives me dynamic cast exception. How can I solve this problem?
I solved the problem by initializing the entity object differently, if you implement the initiation method from super class(NSManagedObject) like: (in Group.swift)
init(entity: NSEntityDescription!, insertIntoManagedObjectContext context: NSManagedObjectContext!) {
super.init(entity: entity, insertIntoManagedObjectContext: context)
}
you can create the object like this:
var desc:NSEntityDescription = NSEntityDescription.entityForName("Group",inManagedObjectContext:appDelegate.managedObjectContext)
var group:Group = Group(entity:desc, insertIntoManagedObjectContext: appDelegate.managedObjectContext)
most of solutions i have read was making the initiation like this:
var group:Group = NSEntityDescription.insertNewObjectForEntityForName("Group", inManagedObjectContext:appDelegate.managedObjectContext) as Group
so this was the problem and object could not be casted to custom managed object class with this way. Thanks to everyone.