Using Swift protocols with generics - core-data

I have a simple example that seems like it should work:
import CoreData
#objc protocol CoreDataModel {
#optional class func entityName() -> String
}
class AbstractModel: NSManagedObject, CoreDataModel {
class func create<T : CoreDataModel>(context:NSManagedObjectContext) -> T {
var name = T.entityName?()
var object = NSEntityDescription.insertNewObjectForEntityForName(name, inManagedObjectContext: context) as T
return object
}
}
So we have a class called AbstractModel which conforms to the protocol CoreDataModel, and CoreDataModel defines an optional class method called entityName.
However, this line:
var name = T.entityName?()
causes the error:
Expected member name or constructor call after type name
Any idea what I'm doing wrong?
Edit
Removing the word #optional from the declaration and changing the function a bit allows the code to compile, but now I get a runtime error saying that the
'Swift dynamic cast failed'
#objc protocol CoreDataModel {
class func entityName() -> String
}
class AbstractModel: NSManagedObject, CoreDataModel {
class func entityName() -> String {
return "AbstractModel"
}
class func create<T : CoreDataModel>(aClass:T.Type, context:NSManagedObjectContext) -> T {
var name = aClass.entityName()
var object = NSEntityDescription.insertNewObjectForEntityForName(name, inManagedObjectContext: context) as T
return object
}
}

I cannot explain why your code causes a runtime exception. But it works if you change
the function prototype
class func create<T : CoreDataModel>(aClass:T.Type, context:NSManagedObjectContext) -> T
to
class func create<T : NSManagedObject where T: CoreDataModel>(aClass:T.Type, context:NSManagedObjectContext) -> T
Assuming that your managed object subclass conforms to the protocol, for example
extension Event : CoreDataModel {
class func entityName() -> String {
return "Event"
}
}
then this works and creates a new object:
let newManagedObject = AbstractModel.create(Event.self, context: context)
Alternatively, you could use the approach from the answer to
"Swift: return Array of type self" and
define an extension to the NSManagedObjectContext class:
extension NSManagedObjectContext {
func create<T : NSManagedObject where T : CoreDataModel >(entity: T.Type) -> T {
var classname = entity.entityName()
var object = NSEntityDescription.insertNewObjectForEntityForName(classname, inManagedObjectContext: self) as T
return object
}
}
Then a new object would be created as
let newManagedObject = context.create(Event.self)

From "The Swift Programming Language"
Because T is a placeholder, Swift does not look for an actual type called T.
As T is not a real type, it is maybe not useful to cast to T.

Related

haxe get type info for fields of a function type parameter

Reflect can list an object's fields at runtime, but doesn't have type info. rtti has type info, but doesn't seem to work for type parameters.
In this example, I want MyLibrary to be able to figure out that T's val field is an Int.
import haxe.Constraints;
class Main {
static function main() {
var obj :IntStruct = MyLibrary.foo();
}
}
#:rtti
class IntStruct {
public var val :Int;
public function new() { }
}
class MyLibrary {
#:generic
public static function foo<T:Constructible<()->Void>>() :T {
var something = new T();
trace(Reflect.fields(something)); // [val]
trace(haxe.rtti.Rtti.getRtti(T)); // fails here
return something;
}
}
this gives this compile error:
Main.hx:20: characters 38-39 : Only #:const type parameters on #:generic classes can be used as value
if I add #:const to the type parameter declaration, I get this instead
Main.hx:20: characters 38-39 : foo.T should be Class<Unknown<0>>
Main.hx:20: characters 38-39 : ... For function argument 'c'
Since foo is generic, the compiler will write a separate function for each T, so it should be no problem for it to tell me about T inside foo. How can it be done?

SwiftUI error with ForEach within List: Cannot invoke initializer for type 'List' with an argument list of type '(#escaping () ... (see code)

I am trying to have a simple ForEach within a List (and have done this in another Project), but in this project I am getting the below mentioned error? How could I fix?
Error
Cannot invoke initializer for type 'List' with an argument list of type '(#escaping () -> ForEach, Int64, Text>)'
Code
import SwiftUI
struct ContentView: View {
#FetchRequest(fetchRequest: GCList.allListFetchRequest()) var gcLists: FetchedResults<GCList>
var body : some View {
NavigationView {
List { // <=== ERROR IS MARKED HERE IN XCODE
ForEach(self.gcLists) { gcList in
Text(gcList.title)
}
}
}
}
}
and
extension GCList : Identifiable {
#nonobjc public class func fetchRequest() -> NSFetchRequest<GCList> {
return NSFetchRequest<GCList>(entityName: "GCList")
}
#NSManaged public var id: Int64
#NSManaged public var title: String
}
extension GCList {
static func allListFetchRequest() -> NSFetchRequest<GCList> {
let request: NSFetchRequest<GCList> = GCList.fetchRequest()
request.sortDescriptors = [NSSortDescriptor(key: "id", ascending: true)]
return request
}
}
Turns out in Core Data I had an old entity that I had created called "List" that managed to much things up....

NSArrayController NSTableView Core Data Binding integers

I have an NSTableView bound to an NSArrayController, which in turn is bound to Core Data. The table displays integer values from core data nicely, but if I edit the numbers in the table I get an error:
Unacceptable type of value for attribute: property = "armorclass"; desired type = NSNumber; given type = NSTaggedPointerString; value = 10.
Any suggestions of how I can convert this pointer string back to an Int16 before the Array Controller tries to save it back to Core Data?
I wrote the following ValueTransformer but it's not working properly. I always get the error: Cannot find value transformer with name StringIntegerValueTransformer
class StringIntegerValueTransformer: ValueTransformer {
override class func transformedValueClass() -> AnyClass { //What do I transform
return String.self as! AnyClass
}
override class func allowsReverseTransformation() -> Bool { //Can I transform back?
return false
}
override func transformedValue(_ value: Any?) -> Any? {
if let val = value {
return String(describing: val)
}
return "nil"
}
override func reverseTransformedValue(_ value: Any?) -> Any? { //Revert transformation
if let val = value {
return val as? Int16
}
return nil
}
}
fff
To register the value transformer override init in AppDelegate
override init()
{
let stringIntegerValueTransformer = StringIntegerValueTransformer()
ValueTransformer.setValueTransformer(stringIntegerValueTransformer, forName:NSValueTransformerName(rawValue: "StringIntegerValueTransformer"))
super.init()
}
And consider this note from the documentation
Your NSValueTransformer subclasses are not automatically listed in the Interface Builder bindings inspector. When inspecting a binding you can enter the name that the value transformer is registered with, but the functionality will not be present in Interface Builder’s test mode. When your application is compiled and run the transformer will be used
Add a number formatter to the text field.

How do I get the annotation value 'used'

I want to retrieve the annotation value used from MyAnnot. I am getting 3 annotations in the list even though there are only 2. Also, I have tried obtaining the used field of MyAnnot but with no success. I would like to return a map where MyAnnot's used is the key and type as the map's value.
// Then, define your class with it's annotated Fields
class MyClass {
#MyAnnot(used = "Hey", type = "There")
String fielda
#MyAnnot(used = "denn", type = "Ton")
String fieldc
}
def findAllPropertiesForClassWithAnotation(obj, annotClass) {
def op = []
def annos = []
def i = 0
obj.properties.findAll { prop ->
obj.getClass().declaredFields.find {
it.name == prop.key && annotClass in it.declaredAnnotations*.annotationType()
annos=it.declaredAnnotations
i++
if(annos)
op << annos[0] as Set
// println"Props ${annos[0]}"
}
}
op.each{ println "${it} and i is ${i}"}
}
// Then, define an instance of our class
MyClass a = new MyClass(fielda: 'tim', fieldc: 'dennisStar')
// And print the results of calling our method
println findAllPropertiesForClassWithAnotation(a, MyAnnot)
First of all you need to mark you annotation with: #Retention(RetentionPolicy.RUNTIME) to make it available for processing at runtime, so it will be:
#Retention(RetentionPolicy.RUNTIME)
#interface MyAnnot {
String used()
String type()
}
Then, used and type are not fields, nor properties but methods, so they must be invoked and get.
The script will be:
import java.lang.annotation.RetentionPolicy
import java.lang.annotation.Retention
#Retention(RetentionPolicy.RUNTIME)
#interface MyAnnot {
String used()
String type()
}
class MyClass {
#MyAnnot(used="Hey" ,type="There")
String fielda
#MyAnnot(used="denn", type="Ton")
String fieldc
}
def findAllPropertiesForClassWithAnotation( obj, annotClass ) {
def c = obj.getClass()
c.declaredFields.findAll { field ->
field.isAnnotationPresent(annotClass)
}.collect { found ->
def a = found.getAnnotation(annotClass)
[(a.used()): a.type()]
}.sum()
}
MyClass a = new MyClass(fielda: 'tim', fieldc: 'dennisStar')
println findAllPropertiesForClassWithAnotation(a, MyAnnot)
Mind that passing only a class of annotation is not sufficient, since you don't know the methods (used and type) to be invoked on the annotation. The following method will work only for MyAnnot class.

Swift class introspection & generics

I am trying to dynamically create a class instance based type using generics, however I am encountering difficulty with class introspection.
Here are the questions:
Is there a Swift-equivalent to Obj-C's self.class?
Is there a way to instantiate a class using the AnyClass result from NSClassFromString?
Is there a way to get AnyClass or otherwise type information strictly from a generic parameter T? (Similar to C#'s typeof(T) syntax)
Well, for one, the Swift equivalent of [NSString class] is .self (see Metatype docs, though they're pretty thin).
In fact, NSString.class doesn't even work! You have to use NSString.self.
let s = NSString.self
var str = s()
str = "asdf"
Similarly, with a swift class I tried...
class MyClass {
}
let MyClassRef = MyClass.self
// ERROR :(
let my_obj = MyClassRef()
Hmm… the error says:
Playground execution failed: error: :16:1: error: constructing an object of class type 'X' with a metatype value requires an '#required' initializer
Y().me()
^
<REPL>:3:7: note: selected implicit initializer with type '()'
class X {
^
It took me a while to figure out what this means… turns out it wants the class to have a #required init()
class X {
func me() {
println("asdf")
}
required init () {
}
}
let Y = X.self
// prints "asdf"
Y().me()
Some of the docs refer to this as .Type, but MyClass.Type gives me an error in the playground.
Here's how to use NSClassFromString. You have to know the superclass of what you're going to end up with. Here are a superclass-subclass pair that know how to describe themselves for println:
#objc(Zilk) class Zilk : NSObject {
override var description : String {return "I am a Zilk"}
}
#objc(Zork) class Zork : Zilk {
override var description : String {return "I am a Zork"}
}
Notice the use of the special #obj syntax to dictate the Objective-C munged name of these classes; that's crucial, because otherwise we don't know the munged string that designates each class.
Now we can use NSClassFromString to make the Zork class or the Zilk class, because we know we can type it as an NSObject and not crash later:
let aClass = NSClassFromString("Zork") as NSObject.Type
let anObject = aClass()
println(anObject) // "I am a Zork"
And it's reversible; println(NSStringFromClass(anObject.dynamicType)) also works.
Modern version:
if let aClass = NSClassFromString("Zork") as? NSObject.Type {
let anObject = aClass.init()
print(anObject) // "I am a Zork"
print(NSStringFromClass(type(of:anObject))) // Zork
}
If I'm reading the documentation right, if you deal with instances and e.g. want to return a new instance of the same Type than the object you have been given and the Type can be constructed with an init() you can do:
let typeOfObject = aGivenObject.dynamicType
var freshInstance = typeOfObject()
I quickly tested it with String:
let someType = "Fooo".dynamicType
let emptyString = someType()
let threeString = someType("Three")
which worked fine.
In swift 3
object.dynamicType
is deprecated.
Instead use:
type(of:object)
Swift implementation of comparing types
protocol Decoratable{}
class A:Decoratable{}
class B:Decoratable{}
let object:AnyObject = A()
object.dynamicType is A.Type//true
object.dynamicType is B.Type//false
object.dynamicType is Decoratable.Type//true
NOTE: Notice that it also works with protocols the object may or may not extend
Finally got something to work. Its a bit lazy but even the NSClassFromString() route did not work for me...
import Foundation
var classMap = Dictionary<String, AnyObject>()
func mapClass(name: String, constructor: AnyObject) -> ()
{
classMap[name] = constructor;
}
class Factory
{
class func create(className: String) -> AnyObject?
{
var something : AnyObject?
var template : FactoryObject? = classMap[className] as? FactoryObject
if (template)
{
let somethingElse : FactoryObject = template!.dynamicType()
return somethingElse
}
return nil
}
}
import ObjectiveC
class FactoryObject : NSObject
{
#required init() {}
//...
}
class Foo : FactoryObject
{
class override func initialize()
{
mapClass("LocalData", LocalData())
}
init () { super.init() }
}
var makeFoo : AnyObject? = Factory.create("Foo")
and bingo, "makeFoo" contains a Foo instance.
The downside is your classes must derrive from FactoryObject and they MUST have the Obj-C +initialize method so your class gets automagically inserted in the class map by global function "mapClass".
Here is another example showing class hierarchy implementation, similar to accepted answer, updated for the first release of Swift.
class NamedItem : NSObject {
func display() {
println("display")
}
required override init() {
super.init()
println("base")
}
}
class File : NamedItem {
required init() {
super.init()
println("folder")
}
}
class Folder : NamedItem {
required init() {
super.init()
println("file")
}
}
let y = Folder.self
y().display()
let z = File.self
z().display()
Prints this result:
base
file
display
base
folder
display

Resources