In swift, how do I return an object of the same type that conforms to a protocol - swift

I have the following protocol
protocol JSONInitializable {
static func initialize(fromJSON: NSDictionary) throws -> Self?
}
Now I'm trying to make that function return whatever class has conformed to the protocol. I.e. if I have class Foo conforming to the protocol, I want to return a Foo object from the method. How can I do this?
extension Foo: JSONInitializable {
static func initialize(fromJSON: NSDictionary) throws -> Foo? {
}
}
I get the error:
Method 'initialize' in non-final class 'Foo' must return 'Self' to conform to protocol 'JSONInitializable'

Autocomplete will help you, but basically, if Foo is a class, you just need a method which matches the exact signature of the method in your protocol:
class Foo {}
protocol JSONInitializable {
static func initialize(fromJSON: [String : AnyObject]) throws -> Self?
}
extension Foo: JSONInitializable {
static func initialize(fromJSON: [String : AnyObject]) throws -> Self? {
return nil
}
}
As a note here, you will have to, within this method, replace all instances of Foo with Self. If you want to use a constructor, it will have to be one marked as required.
With this in mind, it probably makes more sense to change your protocol to requiring an initializer rather than a static method called initialize, in which case, take a look at Blake Lockley's answer. However, this answer stands to answer the question of how to deal with Self in protocols, as there are certainly cases where we might want a method that returns Self that isn't an initializer.
If Foo is not a class, it is a value type which cannot be subclasses, and as such, we return the name of the type:
struct Foo: JSONInitializable {
static func initialize(fromJSON: [String : AnyObject]) throws -> Foo? {
return nil
}
}
enum Foo: JSONInitializable {
case One, Two, Three
static func initialize(fromJSON: [String : AnyObject]) throws -> Foo? {
return nil
}
}
The reason you need to return Self? from the method in the case of a class is because of inheritance. The protocol declares that if you conform to it, you will have a method called initialize whose return type will be an optional version of whatever you are.
If we write Foo's initialize method as you wrote it, then if we subclass Foo with Bar, then now we have broken our conformance to the protocol. Bar has inherited the conformance to the protocol from Foo, but it doesn't have a method which is called initialize and returns Bar?. It has one that returns Foo.
Using Self here means that when our subclasses inherit this method, it will return whatever type they are. Self is Swift's equivalent of Objective-C's instancetype.

As mentioned in nhgrif answer change the return type of Foo? to self?.
Alternatively
In Swift you can declare inits in protocol so try something like this:
protocol JSONInitializable {
init?(fromJSON: NSDictionary) throws
}
extension Foo: JSONInitializable {
required init?(fromJSON: NSDictionary) throws {
//Possibly throws or returns nil
}
}

Related

Swift protocol conformance when returning a generic

Here's an example:
protocol Feed {
func items<T>() -> [T]? where T: FeedItem
}
protocol FeedItem {}
class FeedModel: Feed, Decodable {
func items<T>() -> [T]? where T : FeedItem {
return [FeedItemModel]() // Error: Cannot convert return expression of type '[FeedItemModel]' to return type '[T]?'
}
}
class FeedItemModel: FeedItem, Decodable {}
Why does it:
A) try to convert to T when T is a generic, not a type?
B) does not recognize FeedItemModel as conforming to FeedItem?
func items<T>() -> [T]? where T : FeedItem
This says that the caller can define T to be whatever they want, as long as T conforms to FeedItemModel, and this function will return an optional array of those.
FeedItemModel is something that conforms to FeedItem, but it is not promised to be the type T that the caller requested.
As an example, consider:
class OtherModel: FeedItem {}
According to your function signature, I can do this:
let ms: [OtherModel]? = FeedModel().items()
But your function won't then return [OtherModel]? to me. I suspect you don't actually mean this to be generic. I expect you mean:
func items() -> [FeedItemModel]?
or possibly
func items() -> [FeedItem]?
(Though I would think very hard before doing the latter one and make sure that the protocol existential is really doing useful work here.)
A)
T is a type, a homogenous concrete type specified at runtime.
Imaging T is class Foo : FeedItem it's obvious that FeedItemModel cannot be converted to Foo
B)
FeedItemModel is recognized as conforming to FeedItem but this is irrelevant.
It's often a mix-up of generics and protocols. Generic types are not covariant. If you need covariant types use an associated type.
Either you can ignore generics because because it only applies to that one function and it isn't needed since directly saying that the return type is [FeedItem]? yields the same result
protocol Feed {
func items() -> [FeedItem]?
}
class FeedModel: Feed, Decodable {
func items() -> [FeedItem]? {
return [OtherModel]()
}
}
If you on the other hand want a generic protocol then you should use a associated type
protocol Feed2 {
associatedtype T: FeedItem
func items() -> [T]?
}
class FeedModel2: Feed2, Decodable {
typealias T = FeedItemModel
func items() -> [T]? {
return [FeedItemModel]()
}
}

Why do I need to force cast a property to a generic method with the same signature as the property?

This is my code:
class GenericClass<T: UITableViewCell> {
let enumProperty = SomeEnum.myValue
enum SomeEnum {
case myValue
}
func callOtherClass() {
OtherClass.handle(property: enumProperty) // Compile error
}
}
class OtherClass {
static func handle(property: GenericClass<UITableViewCell>.SomeEnum) {}
}
Why do I get the compile error:
Cannot convert value of type 'GenericClass.SomeEnum' to expected
argument type 'GenericClass.SomeEnum'
Ofcourse, the fix would be adding the cast:
as! GenericClass<UITableViewCell>.SomeEnum
which results in this ugly code:
func callOtherClass() {
OtherClass.handle(property: enumProperty) as! GenericClass<UITableViewCell>.SomeEnum
}
But why do I need to cast? self is defined as GenericClass where T always is a UITableViewCell. The method handle expects that signature.
Is there any case this cast is needed because in some situations this will/can fail? I would not expect Swift just randomly asking me to insert a force cast. I expect Swift can just infer the types and sees it is safe, but somehow, Swift doesn't agree with me.
The problem here is that SomeEnum is actually GenericClass<T>.SomeEnum. There's no promise that T is exactly UITableViewCell, so it's not compatible with GenericClass<UITableViewCell> (generics are not covariant).
Typically in this case, what you want to do is move SomeEnum outside of GenericClass, since nothing about it is actually generic:
enum SomeEnum {
case myValue
}
class GenericClass<T: UITableViewCell> {
let enumProperty = SomeEnum.myValue
func callOtherClass() {
OtherClass.handle(property: enumProperty) // Compile error
}
}
class OtherClass {
static func handle(property: SomeEnum) {}
}
But if there's a reason for it to be generic, see Robert Dresler's answer, which is how you would specialize the function correctly:
class OtherClass {
static func handle<T: UITableViewCell>(property: GenericClass<T>.SomeEnum) {}
}
Make your static method also generic and create generic constrain for parameter inheriting from UITableViewCell. Then use this generic parameter in method parameter
class OtherClass {
static func handle<T: UITableViewCell>(property: GenericClass<T>.SomeEnum) {}
}

Default implementation of protocol extension in Swift not working

I'm trying to add functionality to an NSManagedObject via a protocol. I added a default implementation which works fine, but as soon as I try to extend my subclass with the protocol it tells me that parts of it are not implemented, even though I added the default implementation.
Anyone having Ideas of what I'm doing wrong?
class Case: NSManagedObject {
}
protocol ObjectByIdFetchable {
typealias T
typealias I
static var idName: String { get }
static func entityName() -> String
static func objectWithId(ids:[I], context: NSManagedObjectContext) -> [T]
}
extension ObjectByIdFetchable where T: NSManagedObject, I: AnyObject {
static func objectWithId(ids:[I], context: NSManagedObjectContext) -> [T] {
let r = NSFetchRequest(entityName: self.entityName())
r.predicate = NSPredicate(format: "%K IN %#", idName, ids)
return context.typedFetchRequest(r)
}
}
extension Case: ObjectByIdFetchable {
typealias T = Case
typealias I = Int
class var idName: String {
return "id"
}
override class func entityName() -> String {
return "Case"
}
}
The error I get is Type Case doesn't conform to protocol ObjectByIdFetchable
Help very much appreciated.
We'll use a more scaled-down example (below) to shed light on what goes wrong here. The key "error", however, is that Case cannot make use of the default implementation of objectWithId() for ... where T: NSManagedObject, I: AnyObject; since type Int does not conform to the type constraint AnyObject. The latter is used to represent instances of class types, whereas Int is a value type.
AnyObject can represent an instance of any class type.
Any can represent an instance of any type at all, including function types.
From the Language Guide - Type casting.
Subsequently, Case does not have access to any implementation of the blueprinted objectWithId() method, and does hence not conform to protocol ObjectByIdFetchable.
Default extension of Foo to T:s conforming to Any works, since Int conforms to Any:
protocol Foo {
typealias T
static func bar()
static func baz()
}
extension Foo where T: Any {
static func bar() { print ("bar") }
}
class Case : Foo {
typealias T = Int
class func baz() {
print("baz")
}
}
The same is, however, not true for extending Foo to T:s conforming to AnyObject, as Int does not conform to the class-type general AnyObject:
protocol Foo {
typealias T
static func bar()
static func baz()
}
/* This will not be usable by Case below */
extension Foo where T: AnyObject {
static func bar() { print ("bar") }
}
/* Hence, Case does not conform to Foo, as it contains no
implementation for the blueprinted method bar() */
class Case : Foo {
typealias T = Int
class func baz() {
print("baz")
}
}
Edit addition: note that if you change (as you've posted in you own answer)
typealias T = Int
into
typealias T = NSNumber
then naturally Case has access to the default implementation of objectWithId() for ... where T: NSManagedObject, I: AnyObject, as NSNumber is class type, which conforms to AnyObject.
Finally, note from the examples above that the keyword override is not needed for implementing methods blueprinted in a protocol (e.g., entityName() method in your example above). The extension of Case is an protocol extension (conforming to ObjectByIdFetchable by implementing blueprinted types and methods), and not really comparable to subclassing Case by a superclass (in which case you might want to override superclass methods).
I found the solution to the problem. I thought it's the typealias T which is the reason for not compiling. That's actually not true, it's I which I said to AnyObject, the interesting thing is that Int is not AnyObject. I had to change Int to NSNumber

Implementing Swift protocol methods in a base class

I have a Swift protocol that defines a method like the following:
protocol MyProtocol {
class func retrieve(id:String) -> Self?
}
I have several different classes that will conform to this protocol:
class MyClass1 : MyProtocol { ... }
class MyClass2 : MyProtocol { ... }
class MyClass3 : MyProtocol { ... }
The implementation for the retrieve method in each subclass will be nearly identical. I'd like pull the common implementation of those functions into a shared superclass that conforms to the protocol:
class MyBaseClass : MyProtocol
{
class func retrieve(id:String) -> MyBaseClass?
}
class MyClass1 : MyBaseClass { ... }
class MyClass2 : MyBaseClass { ... }
class MyClass3 : MyBaseClass { ... }
The problem with this approach is that my protocol defines the return type of the retrieve method as type Self, which is what I really want in the end. However, as a result I cannot implement retrieve in the base class this way because it causes compiler errors for MyClass1, MyClass2, and MyClass3. Each of those classes must conform to the protocol that they inherit from MyBaseClass. But because the method is implemented with a return type of MyBaseClass and the protocol requires it to be of MyClass1, it says that my class doesn't conform to the protocol.
I'm wondering if there is a clean way of implementing a protocol method that references a Self type in one or more of its methods from within a base class. I could of course implement a differently-named method in the base class and then have each subclass implement the protocol by calling into its superclass's method to do the work, but that doesn't seem particularly elegant to me.
Is there a more straightforward approach that I'm missing here?
This should work:
protocol MyProtocol {
class func retrieve(id:String) -> Self?
}
class MyBaseClass: MyProtocol {
required init() { }
class func retrieve(id:String) -> Self? {
return self()
}
}
required init() { } is necessary to ensure any subclasses derived from MyBaseClass has init() initializer.
Note that this code crashes Swift Playground. I don't know why. So try with real project.
Not sure what you're looking to accomplish here by just your example, so here's a possible solution:
protocol a : class {
func retrieve(id: String) -> a?
}
class b : a {
func retrieve(id: String) -> a? {
return self
}
}
The reasoning behind the
protocol a : class
declaration is so that only reference types can be extensions. You likely don't want to be passing around value types (struct) when you're dealing with your classes.
I have marked the answer from #rintaro as the correct answer because it did answer the question as I asked it. However, I have found this solution to be too limiting so I'm posting the alternate answer I found to work here for any others running into this problem.
The limitation of the previous answer is that it only works if the type represented by Self (in my example that would be MyClass1, MyClass2, or MyClass3) is used in a protocol or as the return type from a class method. So when I have this method
class func retrieve(id:String) -> Self?
everything works as I hoped. However, as I worked through this I realized that this method now needs to be asynchronous and can't return the result directly. So I tried this with the class method:
class func retrieve(id:String, successCallback:(Self) -> (), failureCallback:(NSError) -> ())
I can put this method into MyProtocol but when I try to implement in MyBaseClass I get the following compiler error:
Error:(57, 36) 'Self' is only available in a protocol or as the result of a class method; did you mean 'MyBaseClass'?
So I really can't use this approach unless the type referenced by Self is used in very specific ways.
After some experimentation and lots of SO research, I was finally able to get something working better using generics. I defined the method in my protocol as follows:
class func retrieve(id:String, successCallback:(Self) -> (), failureCallback:(NSError) -> ())
and then in my base class I do the following:
class MyBaseClass : MyProtocol {
class func retrieve<T:MyBaseClass>(id:String, successCallback: (T) -> (), failureCallback: (NSError) -> ()) {
// Perform retrieve logic and on success invoke successCallback with an object of type `T`
}
}
When I want to retrieve an instance of the type MyClass1, I do the following:
class MyClass1 : MyBaseClass {
func success(result:MyClass1} {
...
}
func failure(error:NSError) {
...
}
class func doSomething {
MyClass1.retrieve("objectID", successCallback:success, failureCallback:failure)
}
With this implementation, the function type for success tells the compiler what type should be applied for T in the implementation of retrieve in MyBaseClass.

'Self' is only available in a protocol or as the result of a class method

Update: Swift 3 permits Self to be used from other types, thanks to SE-0068 – Expanding Swift Self to class members and value types.
You can return "Self" from a class function:
extension NSObject {
class func makeOne() -> Self {
return self()
}
}
So you can do:
let set : NSCountedSet = NSCountedSet.makeOne()
However, the following two don't compile:
extension NSObject {
class func makeTwo() -> (Self, Self) {
return (self(), self())
}
class func makeMany() -> [Self] {
return [self(), self(), self(), self(), self()]
}
}
The error is:
<REPL>:11:34: error: 'Self' is only available in a protocol or as the result of a class method; did you mean 'NSObject'?
class func makeTwo() -> (Self, Self) {
^~~~
NSObject
<REPL>:11:40: error: 'Self' is only available in a protocol or as the result of a class method; did you mean 'NSObject'?
class func makeTwo() -> (Self, Self) {
^~~~
NSObject
<REPL>:15:35: error: 'Self' is only available in a protocol or as the result of a class method; did you mean 'NSObject'?
class func makeMany() -> [Self] {
^~~~
NSObject
Does anyone know of any way to declare that a class function returns multiple instances of the class itself?
The problem, I suspect, is that Self is ambiguous; it means "this class or a subclass, whatever the thing happens to be at the time we are called". In other words, Self is polymorphic. But you can't make an array consisting of two different classes, for example. And although the class may permit a certain initializer, we cannot know in advance that its subclass will.
The solution is to use the class name itself. Here's an example for a struct Thing:
extension Thing {
static func makeTwo() -> (Thing, Thing) {
return (Thing(), Thing())
}
}