Swift Class Pointer as? Class Protocol? - ios

I have a class, lets call it SomeClass. Instances of SomeClass have an optional pointer to SomeOtherClass. In this way, instances of SomeClass can be instantiated, given a pointer to SomeOtherClass (or a subclass of SomeOtherClass), and then this pointer can be used to dynamically create instances of this SomeOtherClass belonging to SomeClass. Eg;
class SomeClass {
var classPointer: SomeOtherClass.Type?
}
class SomeOtherClass {
}
So far so good. Now, I have a protocol - lets call it SomeProtocol - that I want SomeOtherClass to conform to. This protocol has class functions in it:
protocol SomeProtocol {
static func someClassFunction()
}
extension SomeOtherClass : SomeProtocol {
class func someClassFunction() {
print("I am a class function being executed on SomeOtherClass")
}
}
As expected, I can then call this protocol class function on SomeOtherClass like so:
SomeOtherClass.someClassFunction() // Prints "I am a class function being executed on SomeOtherClass"
Here is the troublesome part. I want to dynamically determine if an instance of SomeClass' classPointer conforms to SomeProtocol, and if so execute the class function on it. So, I try to cast the pointer using as?:
// Create an instance of SomeClass and set it's classPointer to the SomeOtherClass class
let someInstance = SomeClass()
someInstance.classPointer = SomeOtherClass.self
// Check if the instance's classPointer class conforms to the SomeProtocol protocol
if let conformingClass = someInstance.classPointer as? SomeProtocol {
// If so, execute the class function in SomeProtocol on the instance's classPointer
conformingClass.someClassFunction() // Build fails "Static member someClassFunction cannot be used on instance of type SomeProtocol"
}
And the build fails with the error "Static member of someClassFunction cannot be used on instance of type SomeProtocol".
Is there a way to accomplish what I'm attempting? Currently if this doesn't work I can only think of these alternatives (none are preferable and they're all rather hacky):
Switch to objective c.
Switch the protocol to use instance functions instead, then instantiate a temporary instance of SomeClass' classPointer and message it with any necessary functions, then release the instance.
For completeness, here is all of the code together that can be pasted into a Playground (it won't build due to the error I mentioned though):
class SomeClass {
var classPointer: SomeOtherClass.Type?
}
class SomeOtherClass {
}
protocol SomeProtocol {
static func someClassFunction()
}
extension SomeOtherClass : SomeProtocol {
class func someClassFunction() {
print("I am a class function being executed on SomeOtherClass")
}
}
// Create an instance of SomeClass and set it's classPointer to the SomeOtherClass class
let someInstance = SomeClass()
someInstance.classPointer = SomeOtherClass.self
// Check if the instance's classPointer class conforms to the SomeProtocol protocol
if let conformingClass = someInstance.classPointer as? SomeProtocol {
// If so, execute the class function in SomeProtocol on the instance's classPointer
conformingClass.someClassFunction() // Build fails "Static member someClassFunction cannot be used on instance of type SomeProtocol"
}
Thanks for any help you can provide,
- Adam

Ahah! As usual, as soon as I make the SO post, I figure out the answer.
For those wondering, you must cast the classPointer as the protocol's Type, not as the protocol itself. The line:
if let conformingClass = someInstance.classPointer as? SomeProtocol {
Needs to be changed to:
if let conformingClass = someInstance.classPointer as? SomeProtocol.Type {
And you'll then be able to message conformingClass with the class functions declared in SomeProtocol. The complete working code is:
class SomeClass {
var classPointer: SomeOtherClass.Type?
}
class SomeOtherClass {
}
protocol SomeProtocol {
static func someClassFunction()
}
extension SomeOtherClass : SomeProtocol {
class func someClassFunction() {
print("I am a class function being executed on SomeOtherClass")
}
}
// Create an instance of SomeClass and set it's classPointer to the SomeOtherClass class
let someInstance = SomeClass()
someInstance.classPointer = SomeOtherClass.self
// Check if the instance's classPointer class conforms to the SomeProtocol protocol
if let conformingClass = someInstance.classPointer as? SomeProtocol.Type {
// If so, execute the class function in SomeProtocol on the instance's classPointer
conformingClass.someClassFunction()
}
And it works :).

Related

override protocol extension default implementation

Consider following code
// GENERATED PROTOCOL, CANNOT BE MODIFIED
protocol SomeProtocol {
}
// GENERATED CLASS, CANNOT BE MODIFIED
class A {
}
// GENERATED CLASS, CANNOT BE MODIFIED
class B: A, SomeProtocol {
}
// I CAN CHANGE ONLY FROM HERE BELOW
extension SomeProtocol {
func someMethod() {
print("protocol implementation")
}
}
extension B {
func someMethod() {
print("class implementation")
}
}
let some: SomeProtocol = B()
some.someMethod() //this prints "protocol implementation"
I want some.someMethod() to print "class implementation". I know there are ways to achieve this, one would be to add in SomeProtocol someMethod, but, unfortunately, I cannot change none of SomeProtocol, A or B, these are generated. I can only play with extensions. Is there a way to achieve this without touching the 3 mentioned before?
If you declare the variable as the protocol type, it will always take the default implementation of the protocol method, since the method is declared in an extension of the protocol.
Without adding the method to the protocol declaration itself (which you've stated to be not possible for you), the only way to access the specific implementation of your conforming type is to downcast some to B or store it as B in the first place.
let some: SomeProtocol = B()
some.someMethod() //this prints "protocol implementation"
(some as? B)?.someMethod() // this prints "class implementation"
let someB = B()
someB.someMethod() // this prints "class implementation"

Extend a Protocol with Multiple Constraints for One OR the Other - Swift

I want to extend protocol with a default implementation that satisfies OR ( || ) constraint.
class A { }
class B { }
protocol SomeProtocol { }
/// It will throw error for ||
extension SomeProtocol where Self: A || Self: B {
}
You can't extend a protocol with OR as you can't do it in a if let, because with this the compiler infers the type of self or of the var, so if it conforms 2 types, the compiler doesn't know of what type is self.
(When you type self. or any var. the compiler always knows what type of var is in compiler type, in that case it would be in runtime). So the easiest way is to make that the 2 types conforms a protocol and do a extension of that protocol. So the compiler knows that self conforms a protocol and he doesn't care of the exact type of Self (But you will be able to use only the properties declared in the protocol).
protocol ABType {
// Properties that you want to use in your extension.
}
class A: ABType, SomeProtocol { }
class B: ABType, SomeProtocol { }
protocol SomeProtocol { }
extension SomeProtocol where Self: ABType {
}
Also if you want to apply the extension to both types you have to do it one by one.
extension A: SomeProtocol { }
extension B: SomeProtocol { }
// Silly example:
(In this case is not really useful, but it is just to show how to make to make 2 classes conforms a protocol and to make a extension of it using a method declared in that protocol and creating a default implementation.)
protocol ABType {
func getName()
}
class AClass: ABType {
func getName() {
print ("A Class")
}
}
class BClass: ABType, someProtocol {
func getName() {
print ("B Class")
}
}
protocol someProtocol {
func anotherFunc()
}
extension someProtocol where Self: ABType {
func anotherFunc() {
self.getName()
}
}
let a = AClass()
// a.anotherFunc() <- Error, A cant call anotherFunc
let b = BClass()
b.anotherFunc()

How to use Realm objects with Swift generic type?

I have generic class Child. At some point I want to check if Child object conforms to MyProtocol and I get the error EXC_BAD_ACCESS. At this point I dont know if this is related to Swift or to Realm library.
protocol MyProtocol: class {
}
class Child<O: Object>: NSObject {
override init() {
let _ = O() as? MyProtocol // here I get error
}
}
class ChildCache: Object {
}

Not able to pass self (which implements a protocol) to init method of a class instantiation.(Swift)

I have a protocol like so
public protocol FooProtocol {
associatedType someType
func doFoo(fooObject:someType) -> Void
}
I have a class that implements this protocol
public class Foo<T> : FooProtocol {
private let _doFoo : (T) -> Void
init<P : FooProtocol where P.someType == T>(_dep : P) {
_doFoo = _dep.doFoo
}
public func doFoo(fooObject: T) {
_doFoo(fooObject)
}
}
Everything upto here looks good to me, Now in another class I implement the FooProtocol with someType = MyType, then when I try to initialize the Foo class with T = MyType by passing self in the init method of the Foo class I get a compilation error, what am I doing wrong here?
Error message:
" Cannot invoke init with an argument list of type (_dep: NSObject -> () -> MyView)"
public class MyView : UIViewController, FooProtocol {
func doFoo(fooObject : MyType) {
...
}
// Here I want to initialize the Foo<T> class
let fooInstant : Foo<MyType> = Foo.init(_dep : self)
// I get the error message on the above line the error
// message reads "Cannot invoke init with an argument list
// of type `(_dep: NSObject -> () -> MyView)`
}
Isn't self conforming to FooProtocol with someType == MyType, and since I am trying to init a Foo object with T == MyType this should technically work yes?
This actually doesn't appear to be anything to do with your generics or protocol conformance. It's simply that you're trying to access self in your property assignment before self has been initialised (default property values are assigned during initialisation).
The solution therefore is to use a lazy property like so:
lazy var fooInstant : Foo<MyType> = Foo(_dep : self)
This will now be created when it's first accessed, and therefore after self has been initialised.
It is very much important to understand the context of self in the place where it is used. In that context, self doesn't mean an instance of MyView.
You can, however get it to work by doing this:
let fooInstant : Foo<String> = Foo.init(_dep : MyView())
I cannot say more without understanding what you want to do here.
So seems like this works
public class MyView : UIViewController, FooProtocol {
var fooInstant : Foo<MyType>!
public func initializeFooInstant(){
fooInstant = Foo.init(_dep : self)
}
func doFoo(fooObject : MyType) {
...
}
}
I think system does not let you access self to initialise let properties because there is no guarantee that self is fully initialised when your let property is being initialised.
In the above case you will just have to make sure that fooInstance is initialised before accessing it else the program will crash.
Hope this helps.

Swift protocol & weak references with class

If I have a protocol:
protocol SomeProtocol {
func doSomething()
}
and in a helper class, I have a reference to a protocol variable:
class someClass {
var delegate: SomeProtocol?
}
because SomeProtocol isn't marked with : class, it's assumed that delegate can be anything and in the case of value types (structs and enums) there's no need for weak var because value types can't create strong references. In fact, the compiler doesn't allow weak var on anything but class types.
However, nothing stops you from setting a class as the delegate and if the protocol isn't marked with : class (as SomeProtocol is),weak var` can't be used and that creates a retain cycle.
class MyClass: NSObject, SomeProtocol {
func doSomething() { }
}
struct MyStruct: SomeProtocol {
func doSomething() { }
}
let someClass = SomeClass()
let myStruct = MyStruct()
someClass.delegate = myStruct
// After myStruct gets assigned to the delegate, do the delegate and the struct refer to the same instance or does the struct get copied?D
let myClass = MyClass()
someClass.delegate = myClass // can't use weak var so myClass is retained
Given the above example, in the case of using delegates and datasource, shouldn't : class always be used? Basically any protocol that is used to maintain a reference should always be restricted to class objects right?
Right. If you a are trying to break retain cycle with weak reference, you have to use classes because weak modifier works only with reference types (classes).
: class is the preferred approach most of the time. As an alternative answer, though, you can set delegate = nil in the deinit method of the parent object.
For example, say the parent object is an NSOperation subclass that has a SomeClass property, and this property has a delegate that implements SomeProtocol:
protocol SomeProtocol {
func doSomething()
}
class SomeClass {
var delegate: SomeProtocol?
}
class CustomOperation: NSOperation {
let foo: SomeClass
}
We'll make a class that implements the protocol too:
class SomeProtocolImplementation: SomeProtocol {
func doSomething() {
print("Hi!")
}
}
Now we can assign foo in init():
class CustomOperation: NSOperation {
let foo: SomeClass
override init() {
foo = SomeClass()
foo.delegate = SomeProtocolImplementation()
super.init()
}
}
This creates a retain cycle. However, consider this deinit method:
deinit {
foo.delegate = nil
}
Now, whenever CustomOperation will be deallocated, foo.delegate will be set to nil, breaking the retain cycle. Then when the autorelease pool drains at the end of the run loop, both SomeClass and SomeProtocolImplementation will be deallocated.

Resources