简体   繁体   中英

How to use multiple protocols in Swift with same protocol variables?

In swift I'm implementing two protocols, GADCustomEventInterstitial and GADCustomEventBanner .

Both of these protocols require a property called delegate . delegate is a different type in each protocol, and thus a conflict arises.

 class ChartBoostAdapter : NSObject, GADCustomEventInterstitial, GADCustomEventBanner, ChartboostDelegate{
        var delegate:GADCustomEventInterstitialDelegate?; // Name conflict
        var delegate:GADCustomEventBannerDelegate?; // Name conflict
         override init(){

        }
    ...

    }

The simple answer is that you can't.

Maybe one protocol depends on another, in which case you would use the dependent protocol for the type of your delegate.

They are libraries/frameworks it's not my definition

Then obviously you cannot make the same class adopt both protocols. But you don't really need to. Just separate this functionality into two different classes, as is evidently intended by the designer of these protocols. You are supposed to have one class that adopts GADCustomEventInterstitial and has its delegate , and another class that adopts GADCustomEventBanner and has its delegate . What reason do you have for trying to force these to be one and the same class? As in all things where you are using a framework, don't fight the framework, obey it.

It is actually possible, I just encountered same situation. I had two different but kind of related protocols. In some cases I needed both to be implemented by delegate and in other cases only one and I didn't want to have two properties eg... delegate1, delegate2.

What you need to do is create another combined protocol that inherits from both protocols:

protocol ChartBoostAdapterDelegate: GADCustomEventInterstitialDelegate, GADCustomEventBannerDelegate { }


class ChartBoostAdapter : NSObject, GADCustomEventInterstitial, GADCustomEventBanner, ChartboostDelegate {

    weak var delegate: ChartBoostAdapterDelegate?

    override init(){

    }
    ...

}

Note that this can be solved using Mixins (possible since Swift 2.0) if you are in a Swift-only environment. It just cannot be solved as long as you need to have the code bridged to Obj-C, as this problem is unsolvable in Obj-C. Yet that can usually be solved by a wrapper class, which I will show later on.

Let's break this down to a minimalist example:

import Foundation

@objc
protocol ProtoA {
    var identifier: String { get }
}

@objc
protocol ProtoB {
    var identifier: UUID { get }
}

@objc
class ClassA: NSObject, ProtoA, ProtoB {
    let identifier = "ID1"
    let identifier = UUID()
}

The code above will fail as no two properties can have the same name. If I only declare identifier once and make it a String , compiler will complain that ClassA does not conform to ProtoB and vice verse.

But here is Swift-only code that actually does work:

import Foundation

protocol ProtoA {
    var identifier: String { get }
}

protocol ProtoB {
    var identifier: UUID { get }
}

class ClassA {
    let stringIdentifier = "ID1"
    let uuidIdentifier = UUID()
}

extension ProtoA where Self: ClassA {
    var identifier: String {
        return self.stringIdentifier
    }
}

extension ProtoB where Self: ClassA {
    var identifier: UUID {
        return self.uuidIdentifier
    }
}

extension ClassA: ProtoA, ProtoB { }

Of course, you cannot do that:

let test = ClassA()
print(test.identifier)

The compiler will say ambigous use of 'identifier' , as it has no idea which identifier you want to access but you can do this:

let test = ClassA()
print((test as ProtoA).identifier)
print((test as ProtoB).identifier)

and the output will be

ID1
C3F7A09B-15C2-4FEE-9AFF-0425DF66B12A

as expected.

Now to expose a ClassA instance to Obj-C, you need to wrap it:

class ClassB: NSObject {
    var stringIdentifier: String { return self.wrapped.stringIdentifier }
    var uuidIdentifier: UUID { return self.wrapped.uuidIdentifier }

    private let wrapped: ClassA

    init ( _ wrapped: ClassA )
    {
        self.wrapped = wrapped
    }
}

extension ClassA {
    var asObjCObject: ClassB { return ClassB(self) }
}

If you put it directly into the class declaration of ClassA , you could even make it a stored property, that way you don't have to recreate it ever again but that complicates everything as then ClassB may only hold a weak reference to the wrapped object, otherwise you create a retain cycle and neither of both objects will ever be freed. It's better to cache it somewhere in your Obj-C code.

And to solve your issue, one would use a similar wrapper approach by building a master class and this master class hands out two wrapper class, one conforming to GADCustomEventInterstitial and one conforming to GADCustomEventBanner but these would not have any internal state or logic, they both use the master class as storage backend and pass on all requests to this class that implements all required logic.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM