Subclassing Swift Generic Class with NSObject inheritance - xcode

I'm running into an error where I'm not certain if it's a limitation of the Swift language, or a bug. Here's the most basic premise:
class GenericClass<T> : NSObject {
var inputValue: T
init(value: T) {
self.inputValue = value
super.init()
}
}
class SubClass : GenericClass<String> {
override init(value: String) {
super.init(value: value)
}
}
var test = GenericClass(value: "test") //Succeeds
var test2 = SubClass(value: "test2") //Fails with EXC_BAD_ACCESS
I'm getting no compiler warnings here, but the Subclass refuses to instantiate. I have a more complicated goal within subclassing specific contexts of a generic, but this basic issue above is what I boiled it down to.
Interestingly, if I remove the NSObject inheritance on the GenericClass (and the super.init() from the generic's init method), this setup works without issue, so I'm thinking it must have something to do with the fact that I'm inheriting from NSObject. My full implementation MUST inherit from an NSOperation (I'm basically making custom NSOperation classes with a generic superclass), so inheriting from NSObject(i.e. NSOperation) is not optional for me.
It's bothersome that there are no compiler errors and I'm getting something as nasty as a EXC_BAD_ACCESS. It makes me think that maybe this is supposed to work, but isn't currently. I know they only recently began supporting the subclassing of generic classes in Swift. I'm running the latest xCode beta 6.
Any insight appreciated!

I'm pretty sure it's a bug. Interestingly, everything works fine if you delete the initializers, even when you inherit from NSObject:
class GenericClass<T> : NSObject {
}
class SubClass : GenericClass<String> {
}
var test : GenericClass<Int> = GenericClass() // Succeeds
var test2 = SubClass() // Succeeds
var test3 : GenericClass<String> = SubClass() // Succeeds
var test4 : GenericClass<Int> = SubClass() // Fails ("cannot convert SubClass to GenericClass<Int>")
A messy workaround might be to use default protocol implementations, and then extend NSOperation to initialize a new operation from your protocol, but it's probably better to just file a bug report and wait for them to fix this :)

import Foundation
class GenericClass<T> : NSObject {
var inputValue: T
init(value: T) {
print(value)
inputValue = value
super.init()
}
}
class SubClass<T> : GenericClass<T> {
override init(value: T) {
super.init(value: value)
}
}
let test = GenericClass(value: "test") //Succeeds
test.inputValue
let test2 = SubClass(value: "test2") //Succeeds
test2.inputValue
let test3 = GenericClass(value: 3.14) //Succeeds
test3.inputValue
let test4 = SubClass(value: 3.14) //Succeeds

Related

Swift Type Inference Not Working (Xcode 7.1.1)

This is my first question on StackOverflow so please go easy on me.
I've been struggling with getting Swift to invoke the appropriate generic overload.
Suppose I have the following protocol -
protocol MyProtocol { }
And I have the following generic methods -
func foo<T>() -> T
func foo<T: MyProtocol>() -> T
One would expect that invoking foo() with a return type of T conforming to MyProtocol would invoke the appropriate overload.
let bar: MyProtocol = foo()
The above code actually invokes the following function during runtime and Cmd + Click in the IDE navigates to the wrong overload as well.
func foo<T>() -> T
For some reason I cannot get this to work properly in Xcode 7.1.1.
Am I missing something completely fundamental here or is this another Swift quirk?
EDIT
Adding an example of this behavior in action as per matt's request.
protocol MyProtocol { }
class MyProtoClass : MyProtocol { }
class Bar {
func foo<T>(value: T) {
print("T is Generic")
}
func foo(value: MyProtocol) {
print("T conforms to MyProtocol")
}
}
class MyClass<T> {
var value: T
init(value: T) { self.value = value }
var b = Bar()
func print() {
b.foo(value)
}
}
MyClass<MyProtocol>(value: MyProtoClass()).print()
MyClass<String>(value: "").print()
Copying and pasting the above code into a Swift command line application and executing yields the following output.
T is Generic
T is Generic
I think the problem here is that protocols in generics (and generally in Swift) don't work the way you want them to. They are not acting as first-class types. I know that's rather vague... but look at it this way; if you eliminate the func foo<T>(value: T) version of foo, your code won't even compile. In other words, Swift isn't making a choice of foo and choosing wrong; it's saying that b.foo(a1.value) does not call func foo<T: MyProtocol>(value: T).
I have a vague feeling that this is related to my question here:
Protocol doesn't conform to itself?
Okay, I am going to answer my own question here.
After some investigation it seems that Swift wants you to implement an extension with a type constraint on the generic parameter.
extension MyClass where T : MyProtocol {
func print() {
b.foo(value)
}
}
I know this doesn't really solve the problem but it was sufficient enough for me as a work around in my real world use case.
The above sample would wind up looking something like the following.
protocol MyProtocol { }
class MyProtoClass : MyProtocol { }
class Bar {
func foo<T>(value: T) {
print("T is Generic")
}
func foo(value: MyProtocol) {
print("T conforms to MyProtocol")
}
}
class MyClass<T> {
var value: T
init(value: T) { self.value = value }
var b = Bar()
func print() {
b.foo(value)
}
}
extension MyClass where T : MyProtocol {
func print() {
b.foo(value)
}
}
MyClass<MyProtoClass>(value: MyProtoClass()).print()
MyClass<String>(value: "").print()

Swift filter function causes compile error

I have created a wrapper class for storing a weak object reference, and then. I want to remove objects that have not a valid reference inside.
class Weak<T: AnyObject> {
weak var value : T?
init (value: T) {
self.value = value
}
}
protocol CSLocationServicesDelegate : class{
}
class conformance : CSLocationServicesDelegate{
}
class Dispatcher{
var dispatchArray = Array<Weak<CSLocationServicesDelegate>>()
func add( delegate : CSLocationServicesDelegate!){
dispatchArray.append(Weak(value: delegate));
}
func remove(obj : CSLocationServicesDelegate!){
dispatchArray.filter { (weakRef : Weak<CSLocationServicesDelegate>) -> Bool in
return weakRef.value != nil; //change this line to "return true" and it works!!!
}
}
}
However Xcode compilation fails with an error being reported, which shows absolutely no specific error. I suspect that I am using the language in such a false manner, that Xcode cannot figure out what I want to do. If I change the line (in the comments) to what the comment says, it works.
Can anyone help me in achieving what I want?
I don't see anything wrong with your code, but the compiler is crashing when compiling it. That should definitely be reported to Apple.
One workaround for this seems to be to simply declare Weak as a struct instead of a class. This compiles just fine:
struct Weak<T: AnyObject> {
weak var value : T?
init (value: T) {
self.value = value
}
}
protocol CSLocationServicesDelegate : class{
}
class conformance : CSLocationServicesDelegate{
}
class Dispatcher{
var dispatchArray = Array<Weak<CSLocationServicesDelegate>>()
func add( delegate : CSLocationServicesDelegate!){
dispatchArray.append(Weak(value: delegate));
}
func remove(obj : CSLocationServicesDelegate!){
dispatchArray.filter { (weakRef : Weak<CSLocationServicesDelegate>) -> Bool in
return weakRef.value != nil
}
}
}

How can I override the description property of NSObject when subclassing in Swift?

I am subclassing NSObject in order to have an ordered collection that is accessible to Cocoa Bindings. My class looks more or less like this:
public class OrderedCollection<Tk: Hashable, Tv> : NSObject {
var keys: Array<Tk> = []
var values: Dictionary<Tk,Tv> = [:]
override init() {
super.init()
}
// Subscript methods go here
override public var description: String {
var result = "{\n"
for i in 0..<self.count {
result += "[\(i)]: \(self.keys[i]) => \(self[i]!)\n"
}
result += "}"
return result
}
}
It doesn't compile. The error says: '#objc' getter for non-'#objc' property.
Is there a way of making the getter non-'#objc' as it were? I don't need the property to be accessible from Objective-C...
It seems the answer was in the comments of an entirely different question. https://stackoverflow.com/a/26688572/4180258
Essentially, there is a bit of an ugly workaround:
class BaseNSObjectWithDescriptionFix: NSObject {
func makeDescription() -> String {
return super.description
}
override var description: String {
return makeDescription()
}
}
Now you just use BaseNSObjectWithDescriptionFix instead of NSObject and override makeDescription as you like.
In my case, I didn't need it because for my purposes I could use [String] and [String:AnyObject], but this may be of some use to someone in the future.
To override the description property of NSObject when subclassing in Swift, with two generic types like you have and keeping your class public, you just need this:
public class OrderedCollection<Tk: Hashable, Tv>: NSObject {
override public var description: String {
return "hello"
}
}
You could also, instead, conform to the CustomStringConvertible protocol (formerly, pre-Swift 2, known as Printable) and forget NSObject, like so:
public class OrderedCollection<Tk: Hashable, Tv>: CustomStringConvertible {
public var description: String {
return "hello"
}
}
In other words, generics don't really change anything for this case. (Not sure if things were different in an earlier version of Swift...)
The content of description I leave to you (e.g. you don't have a count property above, so I'm guessing you omitted more code than just subscript methods).

Swift generic class type both subclass and conforms to protocol

I'm trying to write a generic class in Swift who's generic type both inherits from a class and conforms to a protocol. However, the following code results in a compiler crash with Segmentation fault: 11.
protocol Protocol {
var protocolProperty: Any? { get }
}
class Class {
var classProperty: Any?
}
class GenericClass<T: Class where T: Protocol> {
var genericProperty: T?
func foo() {
let classProperty: Any? = genericProperty!.classProperty
// This is the culprit
let protocolProperty: Any? = genericProperty!.protocolProperty
}
}
Commenting out the access to the protocol property allows the program to compile. There is no way to access anything from the protocol without the compiler crashing. Is there a workaround to creating a generic class that works like this?
As MikeS notes, you should open a radar for the crash. It should never crash.
But the solution is to focus on what protocol (i.e. list of methods) you actually need T to conform to rather than getting wrapped up in the class. For instance:
protocol WhatGenericClassHolds : Protocol {
var classProperty: Any? { get }
}
class GenericClass<T: WhatGenericClassHolds> { ... }
There is a problem in your declaration. conform your class to the protocol as below
protocol A {
var somePropertyInt : Int? {get }
}
class B:A {
var someProperty : String?
var somePropertyInt:Int?;
}
class GenericClass<T: B where T: A > {
var someGenericProperty : T?
func foo() {
println(someGenericProperty!.someProperty)
println(someGenericProperty!.somePropertyInt)
}
}
var someGen = GenericClass()
someGen.someGenericProperty?.somePropertyInt
someGen.someGenericProperty?.someProperty

Error with Swift and Core Data: fatal error: use of unimplemented initializer 'init(entity:insertIntoManagedObjectContext:)'

I have the following class that inherits from NSManagedObject:
import Foundation
import CoreData
class Note: NSManagedObject {
#NSManaged var text: String
#NSManaged var name: String
init(name: String, text:String, context: NSManagedObjectContext){
let entity = NSEntityDescription.entityForName("Note", inManagedObjectContext: context);
super.init(entity: entity!, insertIntoManagedObjectContext: context)
self.text = text
self.name = name;
}
}
When I create instances of it, everything works fine, but when I make a search for these entities, I get a very odd error:
fatal error: use of unimplemented initializer 'init(entity:insertIntoManagedObjectContext:)'
This is the code that causes the error:
func coreDatePlayground(){
var note = Note(name: "A new note", text: "blah", context: self.managedObjectContext!)
println("\(note.name) \(note.text)")
var noote2 = Note(name: "Another note", text: "blah blah", context: self.managedObjectContext!)
managedObjectContext?.save(nil)
var fetch = NSFetchRequest(entityName: "Note")
// This line causes the crash.
var results = self.managedObjectContext?.executeFetchRequest(fetch, error: nil)
if let objects = results{
println(objects.count)
}
}
I found out that changing the initialiser by making it a convenience one and calling on self instead of on super gets rid of the issue, but I have no idea why.
convenience init(name: String, text:String, context: NSManagedObjectContext){
let entity = NSEntityDescription.entityForName("Note", inManagedObjectContext: context);
self.init(entity: entity!, insertIntoManagedObjectContext: context)
self.text = text
self.name = name;
}
The code above works, but I have no idea why. Anybody knows what's going on? Is it a bug or is my fault?
This is documented behavior.
Swift subclasses do not inherit their superclass initializers by default
For example, following code does not even compile, because Child does not inherit init(id:String) automatically. This mechanism make sure name in Child class properly initialized.
class Parent {
var id:String
init(id:String) {
self.id = id
}
}
class Child:Parent {
var name:String
init(id:String, name:String) {
self.name = name
super.init(id: id)
}
}
var child1 = Child(id:"child1")
If you define only convenience initializers in subclass, then it automatically inherits all of its superclass designated initializers as documented in "Automatic Initializer Inheritance" section
You must implement the following in your NSManagedObject subclass (this is the Swift 3.0 version):
#objc
private override init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?) {
super.init(entity: entity, insertInto: context)
}
The answer is kind of answering it, but not really directly.
The reasoning for this is that Swift does not inherit their supercalls designated initializers by default AND it seems as CoreData by uses this initializer when doing fetches (insert a breakpoint in the method to see). So here we "bring up" the designated initializer for CoreData to use.
If you have NSManagedObject base classes, you must also implement this method in those.
Credits to JSQCoreDataKit for the idea.

Resources