Class With Non-Optional Property Conforming To Protocol With Optional Property - swift

If I have a protocol that has an optional property, and a class that needs to conform to the protocol which has the same property already, but as a non-optional property, how do I make the class conform to the protocol.
protocol MyProtocol {
var a: String? { get set }
}
class MyClass {
var a: String
}
extension MyClass: MyProtocol {
// What do I put here to make the class conform
}

Unfortunately, you can't redeclare the same variable in MyClass with a different type.
What Dennis suggests will work, but if you want to keep your variable in MyClass non-Optional, then you could use a computed property to wrap around your stored variable:
protocol MyProtocol {
var a: String? { get set }
}
class MyClass {
// Must use a different identifier than 'a'
// Otherwise, "Invalid redeclaration of 'a'" error
var b: String
}
extension MyClass: MyProtocol {
var a: String? {
get {
return b
}
set {
if let newValue = newValue {
b = newValue
}
}
}
}

You just have to make the property of the class optional as well in order to conform to the protocol, as follows:
protocol MyProtocol {
var a: String? { get set }
}
class MyClass {
var a: String? // Added '?'
}
extension MyClass: MyProtocol {
// Nothing needed here to conform
}

Related

Protocol of reference type to generic constraint of type AnyObject

I have a generic struct declared as follows:
struct WeakReference<T: AnyObject> {
weak var value: T?
init(value: T?) {
self.value = value
}
}
And a protocol:
protocol SomeProtocol: class {
}
But I'm not able to declare a variable of type of WeakReference<SomeProtocol>, the compiler complains that
'WeakReference' requires that SomeProtocol be a class type
Interestingly, in Swift, the class is a typealias of AnyObject.
I actually want to hold an array of WeakReference<SomeProtocol> because the array holds strong references.
Class-only generic constraints in Swift is a similar question but doesn't really solve this problem.
How can we pass the SomeProtocol to WeakReference?
EDIT:
The following scenario compiles fine, but we lose the ability to hold weak reference:
struct Reference<T> {
var value: T?
init(value: T?) {
self.value = value
}
}
var array: [Reference<SomeProtocol>] = []
Thats simple. You are passing SomeProtocol which is a protocol. You need to pass there specific class type.
Eample:
class SomeImplementation: SomeProtocol {
}
var weakSome: WeakReference<SomeImplementation> = ...
Or you can bypass it by marking the protocol with #objc annotation, but I am not a fan of this approach.
#objc protocol SomeProtocol: class {
}
var weakSome: WeakReference<SomeProtocol> = ...
Try checking this answer, it might provide you more context on the issue.
What do you think about this approach?
class WeakReference<T> {
weak var value: AnyObject?
init(value: T?) {
self.value = value as? AnyObject
}
}
protocol SomeProtocol: class {
}
class A: SomeProtocol { }
let araayOfSomeProtocolObjects: [SomeProtocol] = (0...5).map {_ in A() }
let arrayOfWeakReferences: [WeakReference<SomeProtocol>] = araayOfSomeProtocolObjects.map { WeakReference(value: $0) }
for item in arrayOfWeakReferences {
print(item.value is A) // true
}
I think this should solve your problem.
struct WeakReference<T> {
private weak var privateRef: AnyObject?
var ref: T? {
get { return privateRef as? T }
set { privateRef = newValue as AnyObject }
}
init(_ ref: T? = nil) {
self.ref = ref
}
}
// usage
protocol MyProto: class { }
extension UIViewController: MyProto { }
let vc = UIViewController()
var weakRef = WeakReference<MyProto>(vc)
print(weakRef.ref)
You obviously can use WeakReference with non class protocol or non bridged value types.
If you try that, you'll get always nil.
P.S. : Try this code on a real Xcode project because in the Playground doesn't work as expected.

type A requires that type B be a class type swift 4

the following code gives me a compile error
'WeakReference' requires that 'ServiceDelegate' be a class type
protocol ServiceDelegate: AnyObject {
func doIt()
}
class SomeClass() {
// compile error occurs in this line
private var observers = [WeakReference<ServiceDelegate>]()
}
WeakReference code:
final class WeakReference<T: AnyObject> {
private(set) weak var value: T?
init(value: T?) {
self.value = value
}
}
How can I fix this error? Delegate should be declared correctly as per this site.
What I have tried so far:
Changing the delegate protocol conformance from AnyObject to
class does not solve the problem.
Try the above code in a clean playground.
You can't have a WeakReference<ServiceDelegate>. ServiceDelegate itself is not an AnyObject, it just requires that anything that conforms to it be an AnyObject.
You would need to make SomeClass generic and use the generic type as the type for the WeakReference:
class SomeClass<T: ServiceDelegate> {
private var observers = [WeakReference<T>]()
}
If the generic on SomeClass is too constricting and you want to be able to have instances of multiple unrelated classes as observers then I would do it by abandoning the generic parameter on WeakReference:
final class WeakServiceDelegate {
private(set) weak var value: ServiceDelegate?
init(value: ServiceDelegate?) {
self.value = value
}
}
class SomeClass {
private var observers = [WeakServiceDelegate]()
}
Alternatively you could make WeakReference conditionally conform to ServiceDelegate:
extension WeakReference: ServiceDelegate where T: ServiceDelegate {
func doIt() {
value?.doIt()
}
}
And then use an array of ServiceDelegate in SomeClass:
class SomeClass {
private var observers = [ServiceDelegate]()
func addObserver<T: ServiceDelegate>(_ observer: T) {
observers.append(WeakReference(value: observer))
}
}
As you see, ServiceDelegate is a protocol, not a class type.
Even if all types which can conform to ServiceDelegate are class types, ServiceDelegate itself is not a class type. It is the fact of the pure Swift protocols currently.
Try #obc, Objective-C protocols are a bit different:
#objc protocol ServiceDelegate {
func doIt()
}
You may want to exclude Objective-C something and to make some pure Swift classes conform to ServiceDelegate, but I cannot find other ways around.
The problem is that WeakReference<ServiceDelegate> is wrong at line
private var observers = [WeakReference<ServiceDelegate>]()
You have to use a concrete class instead of protocol inside <>
You have two possible solutions:
Create a concrete class and use it:
class ServiceClass: ServiceDelegate {
//...
}
private var observers = [WeakReference<ServiceClass>]()
Or use a protocol. I mean this:
final class WeakReference<T: AnyObject> {
private(set) weak var value: T?
init(value: T?) {
self.value = value
}
}
protocol SomeContainer: AnyObject { }
extension WeakReference: SomeContainer { }
and use this way:
private var observers = [SomeContainer]()
Note
Using this way:
class SomeClass<T: ServiceDelegate> {
private var observers = [WeakReference<T>]()
}
You just move the problem to another part of the code.
I had similar problem and ended up keeping generic WeakReference, but removing type constraint:
struct WeakReference<T> {
private weak var storage: AnyObject?
var value: T? {
get { return storage.map { $0 as! T } }
set {
storage = newValue.map { $0 as AnyObject }
}
}
init(value: T?) {
self.value = value
}
}
This works for classes, Objective-C protocols and Swift protocols:
protocol P: class {}
#objc protocol Q {}
class Z: P, Q {}
var z = Z()
var rz = WeakReference<Z>(value: z)
var rp = WeakReference<P>(value: z)
var rq = WeakReference<Q>(value: z)
assert(rz.value === z)
assert(rp.value === z)
assert(rq.value === z)
z = Z()
assert(rz.value === nil)
assert(rp.value === nil)
assert(rq.value === nil)
Unfortunately it compiles for other things too:
protocol R {}
struct S: R {}
var rr = WeakReference<R>(value: S())
print("rr =", rr.value as Any) // nil
var rs = WeakReference<S>(value: S())
print("rs =", rs.value as Any) // nil
In Swift anything can be casted to AnyObject, but for value types that means boxing - new instance is allocated and immediately lost, so it always produces nil.
This can be used to implement an assertion that casting to AnyObject preserves identity:
struct WeakReference<T> {
private weak var storage: AnyObject?
var value: T? {
get { return storage.map { $0 as! T } }
set {
storage = newValue.map {
let asObject = $0 as AnyObject
assert(asObject === $0 as AnyObject)
return asObject
}
}
}
init(value: T?) {
self.value = value
}
}
Alternative approach would be to use https://github.com/wickwirew/Runtime to validate kind of T.self.
create plain protocol
public protocol AnyWeakValue {
var anyValue: Any? { get }
}
inherit associatedtype protocol from AnyWeakValue
public protocol WeakValue: AnyWeakValue {
associatedtype ValueType
var value: ValueType? { get }
}
extension WeakValue {
public var anyValue: Any? { return value }
}
create class Weak inherit WeakValue
open class Weak<Value: AnyObject>: WeakValue {
public init(value: Value?) { self.value = value }
open private(set) weak var value: Value?
}
using example
private var _delegates: [AnyWeakValue] = []
public var delegates: [SomeProtocol] {
return _delegates.compactMap({$0.anyValue as? SomeProtocol})
}
public func register<Delegate>(_ delegate: Delegate) where Delegate: SomeProtocol {
let weak: Weak<Delegate> = Weak.init(value: delegate)
_delegates.append(weak)
}

Why does a property observer run when a member of the existing value is changed?

Please consider this Swift code. I have a class which wraps an instance of another class. When I set a property on the held value, the wrapper class's property observer is run.
protocol MyProtocol {
var msgStr: String? { get set }
}
class MyClass: MyProtocol {
var msgStr: String? {
didSet {
print("In MyClass didSet")
}
}
}
class MyWrapperClass {
var myValue: MyProtocol! {
didSet {
print("In MyWrapperClass didSet")
}
}
}
let wrapperObj = MyWrapperClass()
wrapperObj.myValue = MyClass() // Line1
wrapperObj.myValue.msgStr = "Some other string" // Line2
The output of above code is:
In MyWrapperClass didSet
In MyClass didSet
In MyWrapperClass didSet
I know that didSet is called when the value of the variable changes.
So when above code at "Line1" executes I understand that "In MyWrapperClass didSet" is printed, and that is fine.
Next when Line2 executes, I expect "In MyClass didSet" to be printed which correctly happens, but I am not sure why "In MyWrapperClass didSet" is printed, as the property myValue is not changed. Can someone explain why?
Swift needs to treat the mutation of myValue.msgStr as having value semantics; meaning that a property observer on myValue needs to be triggered. This is because:
myValue is a protocol-typed property (which also just happens to be optional). This protocol isn't class-bound, so conforming types could be both value and reference types.
The myStr property requirement has an implicitly mutating setter because of both (1) and the fact that it hasn't been marked nonmutating. Therefore the protocol-typed value may well be mutated on mutating though its myStr requirement.
Consider that the protocol could have been adopted by a value type:
struct S : MyProtocol {
var msgStr: String?
}
In which case a mutation of msgStr is semantically equivalent to re-assigning an S value with the mutated value of msgStr back to myValue (see this Q&A for more info).
Or a default implementation could have re-assigned to self:
protocol MyProtocol {
init()
var msgStr: String? { get set }
}
extension MyProtocol {
var msgStr: String? {
get { return nil }
set { self = type(of: self).init() }
}
}
class MyClass : MyProtocol {
required init() {}
}
class MyWrapperClass {
// consider writing an initialiser rather than using an IUO as a workaround.
var myValue: MyProtocol! {
didSet {
print("In MyWrapperClass didSet")
}
}
}
In which case the mutation of myValue.myStr re-assigns a completely new instance to myValue.
If MyProtocol had been class-bound:
protocol MyProtocol : class {
var msgStr: String? { get set }
}
or if the msgStr requirement had specified that the setter must be non-mutating:
protocol MyProtocol {
var msgStr: String? { get nonmutating set }
}
then Swift would treat the mutation of myValue.msgStr as having reference semantics; that is, a property observer on myValue won't get triggered.
This is because Swift knows that the property value cannot change:
In the first case, only classes can conform, and property setters on classes cannot mutate self (as this is an immutable reference to the instance).
In the second case, the msgStr requirement can only either be satisfied by a property in a class (and such properties don't mutate the reference) or by a computed property in a value type where the setter is non-mutating (and must therefore have reference semantics).
Alternatively, if myValue had just been typed as MyClass!, you would also get reference semantics because Swift knows you're dealing with a class:
class MyClass {
var msgStr: String? {
didSet {
print("In MyClass didSet")
}
}
}
class MyWrapperClass {
var myValue: MyClass! {
didSet {
print("In MyWrapperClass didSet")
}
}
}
let wrapperObj = MyWrapperClass()
wrapperObj.myValue = MyClass() // Line1
wrapperObj.myValue.msgStr = "Some other string" // Line2
// In MyWrapperClass didSet
// In MyClass didSet
I suspect this is happening because your protocol is not specified to be a class protocol. Because of that, MyProtocol could be a struct and thus didSet is triggered when the object is changed in any way (which is correct behavior for a value type).
If you change your protocol to:
protocol MyProtocol: class {
var msgStr: String? { get set }
}
then Swift knows that MyProtocol represents a reference type, so didSet will not be called for myValue in MyWrapperClass when the string is set.
It looks like a bug, see: https://bugs.swift.org/browse/SR-239
Also workaround is predefining variable, like:
protocol MyProtocol {
var msgStr: String? { get set }
}
class MyClass: MyProtocol {
var msgStr: String? {
didSet {
print("In MyClass didSet")
}
}
}
class MyWrapperClass {
var myValue: MyProtocol! {
didSet {
print("In MyWrapperClass didSet")
}
}
}
let wrapperObj = MyWrapperClass()
wrapperObj.myValue = MyClass() // Line1
var obj = wrapperObj.myValue!
obj.msgStr = "Some other string" // Line2

Swift Array of Class Types that Each Conform to a Protocol and are Subclasses of Common Class

I have a method in Swift with a signature of the form:
func myMethod<T>(class: T.Type) where T: SomeClass & MyProtocol
I would like to have a variable that is an array of classes that are all subclasses of SomeClass and conform to MyProtocol.
It would look something like:
let classArray = [SubclassOfSomeClass.self, SubclassTwoOfSomeClass.self, SubclassThreeOfSomeClass.self]
Where each of SubclassOfSomeClass, SubclassTwoOfSomeClass and SubclassThreeOfSomeClass conform to MyProtocol
I would then like to call myMethod as follows:
classArray.forEach { classType in
myMethod(class: classType)
}
I receive the compiler error "Generic parameter T could not be inferred". This makes sense to me as the type of classArray variable is inferred as [MyClass.Type].
My question is, can and how do I define the variable classArray such that the compiler knows that every type defined therein conforms to the protocol MyProtocol.
You can achieve that by having SomeClass conform to MyProtocol. Therefore every subclass would also have to conform to MyProtocol.
Then your method signature would be just:
func myMethod<T>(class: T.Type) where T: SomeClass
If that is not desired then you can change the type of myMethod to not be generic. I think swift is unable to type the array to have elements of (SomeClass & MyProtocol).Type. In your case you are not really using a generic object. You do have a specific type in mind and it is (SomeClass & MyProtocol).Type. Your code would look something like this:
protocol MyProtocol: class {
}
class SomeClass: NSObject {
}
class SubClass: SomeClass, MyProtocol {
}
class SubClass2: SomeClass, MyProtocol {
}
let classArray: [(SomeClass & MyProtocol).Type] = [SubClass.self, SubClass2.self]
func myMethod(class: (SomeClass & MyProtocol).Type) {
}
classArray.forEach {
myMethod(class: $0)
}
protocol MyProtocol {
var x: Int { get }
}
class MyClass: NSObject {
var y: Int = 10
}
class SubClass1: MyClass, MyProtocol {
private(set) var x: Int = 9
}
class SubClass2: MyClass {
}
class SubClass3: MyClass, MyProtocol {
var x: Int { return 15 }
}
let items: [MyClass & MyProtocol] = [SubClass1(), SubClass3()]
func myMethod(_ item: MyClass & MyProtocol) {}
classArray.forEach { myMethod($0) }
So for now you can use just MyClass & MyProtocol

Can I declare a type that is a specific class conforming to a protocol in Swift?

If I have the following code:
class MyClass {
...
}
protocol MyProtocol {
...
}
Is it possible to declare a type that accepts a class or subclass of MyClass conforming to MyProtocol?
e.g. in pseudo code:
var thing: MyClass & MyProtocol = ...
The naive approach works (i.e. specify the type first and then use it in the variable declaration):
class MCImplementingMP: MyClass, MyProtocol {
}
var thing: MCImplementingMP = ...
Nope, it was possible in Objective-C but not possible in Swift.
All solutions that I know looks like hacks and require lot of runtime type checking. So I came out with my own - declare a wrapper type which can act like needed class or protocol depending on circumstances:
class MyClass {}
protocol MyProtocol: class {}
class Wrapper {
var instance: AnyObject
init?(instance: MyClass) {
guard instance is MyProtocol else { return nil }
self.instance = instance
}
init?(instance: MyProtocol) {
guard instance is MyClass else { return nil }
self.instance = instance
}
var instanceAsMyClass: MyClass {
return instance as! MyClass
}
var instanceAsMyProtocol: MyProtocol {
return instance as! MyProtocol
}
}
You may want to change property names but idea is clear.