Protocol extensions not using most specific implementation - swift

I have two classes, Object and SubObject. A protocol, MyProtocol, has an associatedtype of type Object. In two extensions, I have provided implementations of the save function. When I create an instance of TestClass with either of the classes, they both result in a call to the least specific extension implementation, while it would be expected to call the most specific one.
class Object {}
class SubObject: Object {}
protocol MyProtocol {
associatedtype T: Object
func save()
}
extension MyProtocol {
func save() {
print("Object")
}
}
extension MyProtocol where T == SubObject {
func save() {
print("SubObject")
}
}
class MyClass<T: Object>: MyProtocol {
}
class TestClass<T: Object> {
typealias U = MyClass<T>
func test() {
let myClass = U()
myClass.save()
}
}
let testClass1 = TestClass<Object>()
testClass1.test() // returns "Object"
let testClass2 = TestClass<SubObject>()
testClass2.test() // returns "Object" (should be "SubObject")
How can I solve this, so that the TestClass calls the correct implementation of save? Or is this not currently possible in Swift? Any help would be appreciated!

Related

Default Swift protocol implementation in extension for class that conforms to other protocol

I have a class where some subclasses conform to Protocol1. The API from Protocol1 is sufficient to implement Protocol2. I want to do something like below, where I can define a default implementation of Protocol2 when an instance of my class conforms to Protocol1. My first attempt looks like this:
public class MyClass {}
public protocol Protocol1 {
var someProperty: Any { get }
}
public protocol Protocol2 {
var func someFunc()
}
extension Protocol2 where Self: MyClass & Protocol1 {
public func someFunc() {
// Functionality that uses `.someProperty`
}
}
This compiles fine, so I thought it would work. However I tried testing with something like:
public class MySubclass: MyClass {}
extension MySubclass: Protocol1 {
public var someProperty: Any {
// Return property value.
}
}
let instance = MySubclass()
instance.someFunc() // Throws compilation error
Unfortunately, I get a Value of type 'MySubclass' has no member 'someFunc' compilation error when attempting to call the function from Protocol2. Is there a way to define a default implementation of Protocol2 for MyClass subclasses conforming to Protocol1, or would I just need to extend each subclass individually?
Figured it out actually. You can define the default implementation with the where clause, but you still need to explicitly conform to Protocol2. I have several MyClass subclasses conforming to Protocol1, so I didn't want to duplicate the implementations, but just adding explicit protocol conformance to Protocol2 with an empty definition block works:
extension Protocol2 where Self: MyClass & Protocol1 {
public func someFunc() {
// Functionality that uses `.someProperty`
}
}
public class MySubclass: MyClass {}
extension MySubclass: Protocol1 {
public var someProperty: Any {
// Return property value.
}
}
extension MySubclass: Protocol2 {}
MySubclass().someFunc() // Works fine

Create instance of class based on function argument [duplicate]

This question already has answers here:
Swift language NSClassFromString
(25 answers)
Closed 5 years ago.
Suppose I have three classes:
import Foundation
class A {
init() {
print("A")
}
}
class B {
init() {
print("B")
}
}
class C {
init() {
print("C")
}
}
I want to dinamically pass a string ("A", "B" or "C") as a function argument and then, inside the body of this function, create an instance of the class I passed. Is this possible? How?
I tried this one (and other variants) but with no luck:
func test(c:AnyObject){
let _class = c()
//...
}
test(c:A)
[UPDATE] Maybe the question is no different from the one #Code Different suggests but that question is old and there were so many changes in the language that one should try any suggested solution before finding the one that works as of today
What could work is having a base class, let's call it BaseClass. Classes that needs to be used would inherit from BaseClass.
Then, in your function, you would pass it the desired type.
Here is a code snippet that demonstrates this technique:
class BaseClass { }
class A: BaseClass { ... }
class B: BaseClass { ... }
class C: BaseClass { ... }
func test(type: BaseClass.Type) {
let someObject = type.init()
// You can cast the object if required
}
test(type: A.self) // creates an object of class A
test(type: B.self) // creates an object of class B
Edit: If you really need a string to cast your types, you might consider doing some job prior to calling test. Getting the type in a switch case and then passing it to test should do.
Edit: It would also work with a protocol, as long as it defines the initializers you need, along with every functions that must be exposed:
protocol SomeProtocol: class {
init()
func someFunction()
}
class A {
required init() {
print("A")
}
}
extension A: SomeProtocol {
func someFunction() {
print("Some function of A")
}
}
class B {
required init() {
print("B")
}
}
extension B: SomeProtocol {
func someFunction() {
print("Some function of B")
}
}
class C {
required init() {
print("C")
}
}
extension C: SomeProtocol {
func someFunction() {
print("Some function of C")
}
}
func test(someType: SomeProtocol.Type) {
let someObject: SomeProtocol = someType.init()
someObject.someFunction()
}
test(someType: A.self) // creates an object of class A
test(someType: B.self) // creates an object of class B

Generic Swift Protocol inside Generic Controller

Is it possible to have generic inside generic?
I have this protocol
public protocol ListViewModelProtocol {
typealias ViewModel
typealias Cell
func titleForHeaderInSection(section: Int) -> String?
func numberOfSections() -> Int
func numberOfRowsInSection(section: Int) -> Int
func viewModelAtIndexPath(indexPath: NSIndexPath) -> ViewModel
}
I also have base ListViewModel that implements this protocol
public class BaseListViewModel<T, U> : ListViewModelProtocol {
}
But already here it says that my ListViewModelProtocol is not implemented. How can I set T and U to be of specific class inside protocol? Because if I write this in protocol
typealias ViewModel: CustomClass
typealias Cell: CustomCell
Its still not working.
My goal is to subclass BaseListViewModel like
public class TestListViewModel : BaseListViewModel<TestCellViewModel, TestTableViewCell> {
}
Then I could just do this in my BaseViewController
public class BaseViewController<T: ListViewModelProtocol>: UITableViewController {
}
And in some subclass ViewController do this:
public class CustomViewController: BaseViewController<TestListViewModel> {
}
and that way CustomViewController would "get" TestCellViewModel and TestTableViewCell (actually its BaseViewController).
But of course this is not working as I expected. What am I missing? Or I have to define typealias for ListViewModelProtocol in every class that implements it or uses it as generic type? Which means I would have to define ViewModel and Cell of ListViewModelProtocol in both BaseListViewModel class and BaseViewController class, but thats not so generic since I just want to put base types of those in protocol and thats it.
Or maybe there is something wrong with my approach and I should implement this differently?
Any suggestions are useful. Thanks
EDIT
I have managed to fix this but I have another problem.
public class BaseViewController<T: ListViewModelProtocol>: UITableViewController {
var dataSource: T?
}
This datasource is used inside UITableViewDataSource methods by calling its own methods (see ListViewModelProtocol methods). Everything is working fine but when some custom controller:
Controller: BaseViewController<TestListViewModel>
is being deinitialized I get EXC_BAD_ACCESS error. If I put
deinit {
self.dataSource = nil
}
it works but I would like to know why I need to set it to nil.
Thanks.
typealias keyword has more than one meaning ...
// protocol can't be generic
protocol P {
// here typealias is just placeholder, alias
// for some unknown type
typealias A
func foo(a:A)->String
}
// C is generic
class C<T>:P {
// here typealias define the associated type
// in this example it is some generic type
typealias A = T
func foo(a: A) -> String {
return String(a)
}
}
let c1 = C<Int>()
print(c1.foo(1)) // 1
let c2 = C<Double>()
print(c2.foo(1)) // 1.0
// D is not generic!!!
class D: C<Double> {}
let d = D()
print(d.foo(1)) // 1.0
Update, to answer the question from discussion
class Dummy {}
protocol P {
// here typealias is just placeholder, alias
// for some inknown type
typealias A : Dummy
func foo(a:A)->String
}
// C is generic
class C<T where T:Dummy>:P {
// here typealias define the associated type
// in this example it is some generic type
typealias SomeType = T
func foo(a: SomeType) -> String {
return String(a)
}
}
class D:Dummy {}
let c = C<D>()
print(c.foo(D())) // D
and
// now next line doesn't compile
let c1 = C<Int>() // error: 'C' requires that 'Int' inherit from 'Dummy'
If you want to implement a protocol with associated types you have to set these associated types in the your generic implementation:
public class BaseListViewModel<T, U> : ListViewModelProtocol {
typealias ViewModel = T
typealias Cell = U
// implement the methods as well
}

swift how to define abstract class and why apple invent associated type but not use generic protocol

I am a swift beginner. Something puzzled me when learning. Now I want to define an abstract class or define some pure virtual method, but I cannot find a way to do it. I have a protocol with associated type(this also puzzled me, why not use generic protocol), and some methods need to be implemented in a base class, and other classes inherited from the base class, they should implement other methods in the protocol, how can I do?
for instance:
Protocol P{
typealias TypeParam
func A()
func B()
}
class BaseClass<TypeParam> : P {
abstract func A()
func B(){
if someCondition {
A()
}
}
}
class ChildClass : BaseClass<Int> {
func A(){}
}
It seems very strange, and I still cannot find a method to resolve the abstract problem.
Swift has something similar: protocol extensions
They can define default implementations so you don't have to declare the method in your base class but it also doesn't force to do that in any class, struct or enum.
protocol P {
associatedtype TypeParameter
func A()
func B()
}
extension P {
func A(){}
}
class BaseClass<TypeParam> : P {
typealias TypeParameter = TypeParam
func B(){
if someCondition {
A()
}
}
}
class ChildClass : BaseClass<Int> {
// implementation of A() is not forced since it has a default implementation
func A(){}
}
Another approach would be to use a protocol instead of BaseClass which is more in line with protocol oriented programming:
protocol Base {
associatedtype TypeParameter
func A()
func B()
}
extension Base {
func B(){
if someCondition {
A()
}
}
}
class ChildClass : Base {
typealias TypeParameter = Int
// implementation of A() is forced but B() is not forced
func A(){}
}
However one of the big disadvantages would be that a variable of protocol type can only be used in generic code (as generic constraint):
var base: Base = ChildClass() // DISALLOWED in every scope
As a workaround for this limitation you can make a wrapper type:
// wrapper type
struct AnyBase<T>: Base {
typealias TypeParameter = T
let a: () -> ()
let b: () -> ()
init<B: Base>(_ base: B) where B.TypeParameter == T {
// methods are passed by reference and could lead to reference cycles
// below is a more sophisticated way to solve also this problem
a = base.A
b = base.B
}
func A() { a() }
func B() { b() }
}
// using the wrapper:
var base = AnyBase(ChildClass()) // is of type AnyBase<Int>
Regarding the use of "true" generic protocols, the Swift team has chosen to use associatedtype because you can use many generic types without having to write all out in brackets <>.
For example Collection where you have an associated Iterator and Index type. This allows you to have specific iterators (e.g. for Dictionary and Array).
In general, generic/associated types are good for code optimization during compilation but at the same time being sometimes too static where you would have to use a generic wrapper type.
A useful link to some patterns for working with associated types.
(See also above)
A more sophisticated way to solve the problem of passing the methods by reference.
// same as `Base` but without any associated types
protocol _Base {
func A()
func B()
}
// used to store the concrete type
// or if possible let `Base` inherit from `_Base`
// (Note: `extension Base: _Base {}` is currently not possible)
struct BaseBox<B: Base>: _Base {
var base: B
init(_ b: B) { base = b}
func A() { base.A() }
func B() { base.B() }
}
struct AnyBase2<T>: Base {
typealias TypeParameter = T
var base: _Base
init<B: Base>(_ base: B) where B.TypeParameter == T {
self.base = BaseBox(base)
}
func A() { base.A() }
func B() { base.B() }
}
// using the wrapper:
var base2 = AnyBase2(ChildClass()) // is of type AnyBase2<Int>

Comparing two custom objects in swift

I have the following protocol defined in Swift:
protocol RecordingObserver {
func aFunc()
}
Somewhere I have to compare two objects that implement this protocol, to check if they are the same. The problem I'm facing is that apparently Swift doesn't allow us to do this:
func areEqual(a:RecordingObserver,b:RecordingObserver){
if a === b {
println("Equal")
}
}
Any idea why this is happening? And how I can do this in another way?
=== is the identical to operator and is used to test whether two object references both refer to the same object instance. It can be applied
only to reference types (i.e. instances of a class).
=== is different from the "equal to" operator == (which is required in the Equatable protocol).
Therefore, assuming that
the actual observers are instances of a class, and
your intention is to check if a and b refer to the same instance,
you have to define the protocol as a class protocol:
protocol RecordingObserver : class {
// ...
}
Then
func areEqual(a:RecordingObserver,b:RecordingObserver){
if a === b {
println("a and b refer to the same object instance")
}
}
compiles (and works as expected) because the compiler knows that a and b are reference types.
Your class needs to support the Equatable protocol to use ==
https://developer.apple.com/library/ios/documentation/General/Reference/SwiftStandardLibraryReference/Equatable.html
Or if you want to use === something like this...
protocol RecordingObserver {
func aFunc()
}
class MyClass: RecordingObserver {
func aFunc() {
// Do something
}
}
func areEqual(a: MyClass, b: MyClass){
if a === b {
println("Equal")
}
}
I believe there is an 'isEqual' method on NSObject. If your custom objects are both subclassed from that you should be able to compare a.isEqual(b).
It is because you said that you objects implement only RecordingObserver. So compiler don't know if he can compare them.
Try this:
func areEqual<T where T: Equatable, T: RecordingObserver>(a: T,b: T) {
}
You can just copy this code into single view project to test:
protocol RecordingObserver {
}
class SomeClass: NSObject, RecordingObserver {
}
class ViewController: UIViewController {
func areEqual<T where T: Equatable, T: RecordingObserver>(a: T,b: T) -> Bool {
return true
}
override func viewDidLoad() {
super.viewDidLoad()
let a = SomeClass()
let b = SomeClass()
NSLog("\(areEqual(a, b: b))")
}
}