NSManagedObject and MKAnnotation - mkmapview

I am trying to extend a NSManagedObject called Location.
First I do this and it give me an error:
I want to set this property through a method on the nsmanagedobject.
How do I add such a property?

Have you tried making it an optional by adding a "?"

I got around it by finding this hint #objc :
#objc
private override init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?) {
super.init(entity: entity, insertInto: context)
self.coordinate = CLLocationCoordinate2DMake(self.latitude, self.longitude)
self.overlayCircle = MKCircle(center: self.coordinate, radius: self.radius as CLLocationDistance)
}

Related

SwiftUI Preview with Core Data: invalid NSEntityDescription

I'm trying to have my SwiftUI Previews work with an in memory Core Data Stack (from Xcode Template). As soon as I call Entity.entity(), I get the following error message:
let context = PersistenceController.preview.container.viewContext
let newBoatMO = Entity(entity: Entity.entity(), insertInto: context)
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'An NSManagedObject of class 'Entity' must have a valid NSEntityDescription.'
I checked in that the name in NSPersistentCloudKitContainer(name: is correct, I also checked in my .xcdatamodeld, the Entity name is correct, the module is empty (ie Global Namespace), and I have this #objc(Entity) at the top of my NSManagedObject subclass.
If I use the non-memory Stack, the Preview works. It's as if the Model was not loaded if I use the in-memory Stack.
For me this was fixed by using public convenience init(context moc: NSManagedObjectContext) rather then the designated public init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?).
So:
Entity(context: context)
instead of
Entity(entity: Entity.entity(), insertInto: context)
Some background:
I had this error when I had 'overridden' the default convenience init(context moc: NSManagedObjectContext). So I solved it by switching (back) to the built-in version.
Before my Location+CoreDataClass looked like this:
#objc(Location)
public class Location: NSManagedObject {
convenience init(context moc: NSManagedObjectContext) {
self.init(entity: Location.entity(), insertInto: moc) // <- Error
timestamp = Date()
}
}
and afterwards like this:
#objc(Location)
public class Location: NSManagedObject {
convenience init(into c: NSManagedObjectContext, timestamp: Date = Date()) {
self.init(context: c) // <- No error
self.timestamp = timestamp
}
}
I'm not sure what causes the error, but maybe this helps someone into the right direction.

Resolving 'Failed to call designated initializer on NSManagedObject class'

I'm new to Swift and I'm trying to learn how to use Core Data. But I'm getting this error and I'm not sure what I've done wrong. I've searched online and tried a few things but I can't get it right.
Failed to call designated initializer on NSManagedObject class 'FirstCoreData.Course'
When this line executes:
ncvc.currentCourse = newCourse
In this function:
class TableViewController: UITableViewController, AddCourseViewControllerDelegate {
var managedObjectContext = NSManagedObjectContext.init(concurrencyType: NSManagedObjectContextConcurrencyType.MainQueueConcurrencyType)
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "addCourse" {
let ncvc = segue.destinationViewController as! NewCourseViewController
ncvc.delegate = self
let newCourse = NSEntityDescription.insertNewObjectForEntityForName("Course", inManagedObjectContext: self.managedObjectContext) as! Course
ncvc.currentCourse = newCourse
}
}
Class generated by "Create NSManagedObject Subclass..." for Course entity:
import Foundation
import CoreData
class Course: NSManagedObject {
// Insert code here to add functionality to your managed object subclass
}
And:
import Foundation
import CoreData
extension Course {
#NSManaged var title: String?
#NSManaged var author: String?
#NSManaged var releaseDate: NSDate?
}
The problem lies not in the code in your question, but in the snippet you included as comments to the other answer:
var currentCourse = Course()
This doesn't just declare currentCourse to be of type Course, it also creates an instance of the Course entity using the standard init method. This is expressly not allowed: You must use the designated initialiser: init(entity entity: NSEntityDescription,
insertIntoManagedObjectContext context: NSManagedObjectContext?). This is described in the Apple Documentation here.
I suspect you do not ever use the instance created by the above var definition, so just define it as being of type Course?:
var currentCourse : Course?
Since it is optional, you do not need to set an initial value, though you will need to unwrap the value whenever it is used.
The simplest way is this:
Define in the applicationDelegate a reference for the context
Instantiate the variable by passing the context
In the AppDelegate (outside the brackets):
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
And in the code:
let currentCourse = Course(context:context)
Now you have your entity created. But don't forget to save with:
appDelegate.saveContext()
I had the same issue. And instantiating the object like this worked, for your course it would be something like this:
var currentCourse = Course.init(entity: NSEntityDescription.entityForName("Course", inManagedObjectContext:mox)!, insertIntoManagedObjectContext: mox)
instead of:
var currentCourse = Course()
I used this in Xcode 8.3.2 with Swift 3.1.
NSEntityDescription.insertNewObject(forEntityName: String(describing: type(of: Record())), into: managedObjectContext) as! Record
And got the same error message. But this data was inserted into db. So maybe this doesn't matter.
Your currentCourse should be NSManagedObject class
Please refer this CoreData: error: Failed to call designated initializer on NSManagedObject class

NSManagedObject subclasses in Swift can not use custom accessor?

I am using Swift in Core Data generated subclass of NSManagedObject. There is a transient optional property title.(The optional is not Swift's optional, but Core Data's optional.)
So I need a custom getter. My code is
class ShoppingList: NSManagedObject {
#NSManaged var title: String
func title() -> String {
return "something"
}
}
The Objective-C version of the getter works fine. However, Xcode tells me that "the func title() is an invalid redeclaration". I tried to use computed property, but get that "#Managed property can not use computed property".
So my question is, is there an alternative way to get custom accessors(getters) in Swift version of NSManagedObject subclassing?
You could use a different name for a computed property, and have it return the title variable.
#NSManaged var title: String
var myTitle : String {
return self.title
}
Would that work for you?
Apple does it like this in many places by naming the actual var with an underscore in front, and the computed property with the same name but without the underscore

How to convert NSDictionary() with values [PSPDFAnnotationParser class] / [MyCustomAnnotationParser class] into MonoTouch?

From the documentation of a bound API I'm using:
overrideClassNames
Use this to use specific subclasses instead of the default PSPDF*
classes. e.g. add an entry of [PSPDFAnnotationParser class] /
[MyCustomAnnotationParser class] as key/value pair to use the custom
subclass. (MyCustomAnnotationParser must be a subclass of
PSPDFAnnotationParser) Throws an exception if the overriding class is
not a subclass of the overridden class. Note: does not get serialized
when saved to disk.
#property (nonatomic, strong) NSDictionary *overrideClassNames
Here's what I tried but doesn't work. Appearently not strings are required but actual types or something. How can I use this in MonoTouch?
var oClassDic = new NSMutableDictionary();
oClassDic.Add(new NSString("[PSPDFAnnotationParser class]"), new NSString("[PSPDFKitAnnotationParser class]"));
oDoc.OverrideClassNames = oClassDic;
The PSPDFKitAnnotationParser I created like this:
[Register("PSPDFKitAnnotationParser")]
public class PSPDFKitAnnotationParser : PSPDFAnnotationParser
{
public PSPDFKitAnnotationParser () : base()
{
}
public PSPDFKitAnnotationParser (PSPDFDocumentProvider provider) : base(provider)
{
}
public PSPDFKitAnnotationParser (IntPtr handle) : base(handle)
{
}
public PSPDFKitAnnotationParser (NSObjectFlag t) : base(t)
{
}
public PSPDFKitAnnotationParser (NSCoder coder) : base(coder)
{
}
MonoTouch's own bindings generally hides the class and replace them with, the more .NETy, System.Type.
However the MonoTouch.ObjCRuntime.Class type exists and can also be used. If the native code expects class instances then you should be able to do something like:
var oClassDic = new NSMutableDictionary();
oClassDic.Add(new Class("PSPDFAnnotationParser"), new Class("PSPDFKitAnnotationParser"));
oDoc.OverrideClassNames = oClassDic;
You might have to tweak this a bit since a Class instance is not an NSObject, it's a NativeObject in MonoTouch, so you might have to go one level deeper and use the Handle properties (IntPtr) when adding values/keys to your dictionary.
Following #poupou answer this might work, I have not tested it this is what you have to do, It Works (TM)
var oClassDic = new NSMutableDictionary();
var key = new Class("PSPDFAnnotationParser");
var val = new Class("PSPDFKitAnnotationParser");
IntPtr selSetObjectForKey = Selector.GetHandle ("setObject:forKey:");
Messaging.void_objc_msgSend_IntPtr_IntPtr (oClassDic.Handle, selSetObjectForKey, val.Handle, key.Handle);
oDoc.OverrideClassNames = oClassDic;
Selector setObject: forKey: expects an ObjC id type on both params
"id" its just a special type that can hold a pointer to any object you can construct with ObjC
So this should work :)
Hope this helps
Alex

AllowMultiple does not work with Property Attributes?

I'm tying to collect all Custom Attributes placed over a Property. There are more than one Attributes of the same type assigned to the Property, but when collecting them , the resulting collection only contains the first Attribute of the specific type:
The Attribute class
[AttributeUsage(System.AttributeTargets.Property,
AllowMultiple = true)]
public class ConditionAttribute : Attribute{...}
Usage:
[ConditionAttribute("Test1")]
[ConditionAttribute("Test2")]
[ConditionAttribute("Test3")]
public Color BackColor{get; set;}
Now when looping through all Props of the object 'value' whose class contains the Prop "BackColor":
foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(value))
{
foreach (Attribute attribute in property.Attributes)
{ ... }
....
}
the collection property.Attributes only contains ONE Attribute of type "ConditionAttribute" : The one with "Test1". The others are ignored;-(
So does AllowMultiple not work for Property Attributes ?
Thanks in advance
henrik
According to a post on MSDN, this is by design as part of the PropertyDescriptor class.
However, you can actually solve the problem by overriding TypeId in your custom attribute (Thanks to Ivan from Mindscape for pointing this out):
public override object TypeId
{
get
{
return this;
}
}
Yes, it does work. Not sure why it does not work via PropertyDescriptors.
You can always do: Attribute.GetCustomAttributes(methodInfo, typeof(ConditionAttribute))
Another way to tweak this,
[ConditionAttribute("Test1,Test2,Test3")]
public Color BackColor{get; set;}
and in your validation code,
Dim lstProperties() As String = _ChkColors.Split(",")
For each strProp as string in lstPropertyes
' your validation
' return
Next

Resources