Swift: Protocol That Accepts Subclasses [duplicate] - swift

This question already has answers here:
Why can't a get-only property requirement in a protocol be satisfied by a property which conforms?
(3 answers)
Closed 3 years ago.
I have a protocol:
protocol InspectableSource
{
var headerView: NSView? { get }
var footerView: NSView? { get }
var mainContentViews: [NSView] { get }
}
Then I have a class that adopts this protocol:
class MyClass: InspectableSource
{
var mainContentViews: [MyNSViewSubclass]
}
The instance of MyClass provides mainContentViews, which is full of objects that are subclasses of NSView. Swift whines about this and won't compile...because it's Swift.
So how do I declare in my protocol that mainContentViews can be ANY type of object that is an NSView or a subclass thereof?

You could use associated types:
protocol InspectableSource
{
associatedtype ViewType : NSView
var headerView: ViewType? { get }
var footerView: ViewType? { get }
var mainContentViews: [ViewType] { get }
}
And in your conforming class:
class MyClass: InspectableSource
{
typealias ViewType = MyNSViewSubclass
var mainContentViews: [MyNSViewSubclass]
// ...
}
But note that this will prevent you from using InspectableSource as the type of a variable.
If you really want to have InspectableSource as the type of a variable. You can try this less type safe approach:
class MyClass: InspectableSource {
Var mainContentViews: [MyNSViewSubclass]
var inspectableMainContentViews: [NSView] {
return mainContentViews
}
}
protocol InspectableSource {
var inspectableMainContentView: [NSView] { get }
}

If you can modify MyClass, I suggest using a dedicated field to store your [MyNSViewSubclass], then conforms to InspectableSource via computed property.
protocol InspectableSource
{
var mainContentViews: [NSView] { get }
}
class MyClass: InspectableSource
{
var mainContentViews : [NSView]{
return myNSViews
}
var myNSViews : [MyNSViewSubclass]
init() {
self.myNSViews = []
}
}

Related

Procotol composition on the same property

Is there a way to achieve this ?
protocol VCProtocol1: UIViewController {
var viewModel: VMProtocol1? { get set }
}
protocol VCProtocol2: UIViewController {
var viewModel: VMProtocol2? { get set }
}
class VC: UIViewController, VCProtocol1, VCProtocol2 {
var viewModel: (VMProtocol1 & VMProtocol2)?
}
What I want to do is composition on ViewController to avoid re-implementing the same code in multiple ViewControllers.
Edit:
To be more precise, the problem here is I want my viewModel property to be shared between both protocols because ultimately I want to implement something like this:
- View Model
protocol VMProtocol1 {
func vmTest1()
}
protocol VMProtocol2 {
func vmTest2()
}
class ViewModel: VMProtocol1, VMProtocol2 {
func vmTest1() { }
func vmTest2() { }
}
- View Controller
protocol VCProtocol1: UIViewController {
var viewModel: VMProtocol1 { get set }
func vcTest1()
}
extension VCProtocol1 {
func vcTest1() {
// This is the key point, I want to be able to refer to my viewModel property internally in each protocol.
self.viewModel.vmTest1()
}
}
protocol VCProtocol2: UIViewController {
var viewModel: VMProtocol2 { get set }
}
class VC: UIViewController, VCProtocol1, VCProtocol2 {
var viewModel: (VMProtocol1 & VMProtocol2)?
}
This is actually not possible. One entity that implements multiple protocols which declare the same property (here viewModel) but with a different type is simply not possible in Swift.
The closest you can do is to use a protocol which define the viewModel type as an associated type and provide conditional extensions (it will force you to use a generic implementation).
Please note that this is a complex implementation that require a good knowledge of generic protocols. I would not recommend using a such complicated design without understanding exactly what you are doing.
View model
protocol VMProtocol1 {
func vmTest1()
}
protocol VMProtocol2 {
func vmTest2()
}
class ViewModel: VMProtocol1, VMProtocol2 {
func vmTest1() { }
func vmTest2() { }
}
Implementation
protocol VCProtocolBase {
associatedtype ViewModel
var viewModel: ViewModel { get }
}
protocol VCProtocol1: VCProtocolBase {
func vmTest1()
}
protocol VCProtocol2: VCProtocolBase {
func vmTest2()
}
extension VCProtocolBase where Self: VCProtocol1, Self.ViewModel: VMProtocol1 {
func vmTest1() {
viewModel.vmTest1()
}
}
extension VCProtocolBase where Self: VCProtocol2, Self.ViewModel: VMProtocol2 {
func vmTest2() {
viewModel.vmTest2()
}
}
final class VC<ViewModel: VMProtocol1 & VMProtocol2>: VCProtocolBase, VCProtocol1, VCProtocol2 {
var viewModel: ViewModel
init(viewModel: ViewModel) {
self.viewModel = viewModel
}
}
VC(viewModel: ViewModel()).vmTest1()
VC(viewModel: ViewModel()).vmTest2()
The idea here is to rely on generics and conditional extensions to provide the default implementations for the VCProtocolX, which is the role of VCProtocolBase.

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)
}

Swift override generic swift

I am trying to implement generic for MVVM Swift. I have two base class, an protocol for generic class. The special in here is inheritance. I tried for three hour but I can't fix it :(.
protocol ObjectProtocol {
var id: Int { get set }
var name: String { get set }
}
class BaseViewModel<T: ObjectProtocol> {
var objects: [T] = []
init(){
}
}
protocol ListViewControllerType {
associatedtype T: ObjectProtocol
associatedtype ViewModelType: BaseViewModel<T>
var viewModel: ViewModelType! { get set }
func showError(error: String)
}
extension ListViewControllerType {
func showError(error: String) {
print(error)
}
}
class Consult: ObjectProtocol {
var id: Int = 1
var name: String = "Consult"
}
class ConsultViewModel<T: Consult>: BaseViewModel<Consult> {
}
class ConsultViewController: ListViewControllerType {
var viewModel: ConsultViewModel<Consult>!
}
But I get error in var viewModel: ConsultViewModel<Consult>!
This is error: Type 'ConsultViewController' does not conform to protocol 'ListViewControllerType'
Someone have experience with generic and inheritance can help me please.
Thank you so much.
I usually do like this:
class BaseViewModel {
}
class TemplateViewModel<T: ObjectProtocol>: BaseViewModel {
var objects: [T] = []
init(){
}
}

Protocol can only be used as a generic constraint

I have a MapViewController for presenting annotations on map. It contains an
object of type MapPresentable.
protocol MapPresentable {
associatedtype AnnotationElement: MKAnnotation
var annotations: [AnnotationElement] { get }
}
class MapViewController<M: MapPresentable>: UIViewController {
var mapPresentable: M!
}
MapViewController can also present route on map in case mapPresentable conforms to RoutePresentable protocol.
protocol RoutePresentable: MapPresentable {
var getRouteLocations: [CLLocation] { get }
}
But when checking made inside MapViewController
if let routePresentable = mapPresentable as? RoutePresentable {
showRoute(routePresentable.getRouteLocations)
}
I got this Error:
Protocol 'RoutePresentable' can only be used as a generic constraint because it has Self or associated type requirements
Updated
Sorry, I make mistakes. But there is no way to cast a protocol with associated type.
Hope this will help.
As i known the routePresentable.getRouteLocations has nothing to do with protocol MapPresentable.
So you can divide RoutePresentable to two protocol:
protocol MapPresentable {
associatedtype AnnotationElement: MKAnnotation
var annotations: [AnnotationElement] { get }
}
class MapViewController<M: MapPresentable>: UIViewController {
var mapPresentable: M!
}
protocol RoutePresentable: MapPresentable, CanGetRouteLocations {}
protocol CanGetRouteLocations {
var getRouteLocations: [CLLocation] { get }
}
if let routePresentable = mapPresentable as? CanGetRouteLocations {
showRoute(routePresentable.getRouteLocations)
}
Original
Because routePresentable.annotations's Type is unprovided,
You can just remove associatedtype AnnotationElement: MKAnnotation.
Or user generic struct instead:
struct MapPresentable<AnnotationElement: MKAnnotation> {
var annotations: [AnnotationElement] = []
}
struct RoutePresentable<AnnotationElement: MKAnnotation> {
var mapPresentable: MapPresentable<AnnotationElement>
var getRouteLocations: [CLLocation] = []
}
class MapViewController<AnnotationElement: MKAnnotation>: UIViewController {
var mapPresentable: MapPresentable<AnnotationElement>!
}
if let routePresentable = mapPresentable as? RoutePresentable<MKAnnotation> {
showRoute(routePresentable.getRouteLocations)
}