How to Access a Function Declared as a Default in a Protocol and Using an Associated Type from Another Protocol

returning swift protocol associated type in multiple methods

The solution you came up with by playing around is exactly what you need

As mentioned elsewhere, the main issue with your first protocol is that you're enforcing createSomeView() createAnotherView() both return the same type. While ViewA() and ViewB() are both candidates for V, since they conform to View they are still different types, and therefore cannot BOTH be V in a given object.

By defining both V1 and V2, you allow for each function to return a different type, or the same type, it's all acceptable. By making both V1 and V2 require View conformance, you allow for the some View syntax

Swift Protocol referencing itself and associated type

You would look after something like this:

protocol Handler {
// ...
var next: some Handler<HandlerRequest == Self.HandlerRequest>? { get }
// ...
}

The problem here is that Swift doesn't (yet) have support for opaque return types that are protocols with associated types.

A solution to this limitation is to use a type eraser for the next property:

protocol Handler {
associatedtype HandlerRequest

// shift the generic from a protocol with associated type to a generic struct
var next: AnyHandler<HandlerRequest>? { get }

func handle(_ request: HandlerRequest) -> LocalizedError?
}

struct AnyHandler<HandlerRequest> {

private var _handle: (HandlerRequest) -> LocalizedError?
private var _next: () -> AnyHandler<HandlerRequest>?

init<H: Handler>(_ handler: H) where H.HandlerRequest == HandlerRequest {
_next = { handler.next }
_handle = handler.handle
}
}

extension AnyHandler: Handler {

var next: AnyHandler<HandlerRequest>? { return _next() }

func handle(_ request: HandlerRequest) -> LocalizedError? {
return _handle(request)
}
}

This way you can benefit both the protocol, and having the next property tied to the handler request type that you need.

As an added bonus for using protocols, you can still benefit the default implementation from the base class:

extension Handler {
func handle(_ request: HandlerRequest) -> LocalizedError? {
return next?.handle(request)
}
}

That's how cool protocols are in Swift, they allow you to avoid classes and use value types for as much as possible, by improving the concept of polymorphism.


Usage example:

struct LoginHandler: Handler {
var next: AnyHandler<AccountRequest>?

func handle(_ request: AccountRequest) -> LocalizedError? {
// do the login validation
}
}

struct SignupHandler: Handler {
var next: AnyHandler<AccountRequest>?

func handle(_ request: AccountRequest) -> LocalizedError? {
// do the signup validation
}
}

extension Handler {
// Helper function to easily create a type erased AnyHandler instance
func erase() -> AnyHandler<HandlerRequest> {
return AnyHandler(self)
}
}

// now let's put the handers to work:
let loginHandler = LoginHandler()
let signupHandler = SignupHandler(next: loginHandler.erase())
let someOtherAccountHandler = SomeOtherAccountHandler(next: signupHandler.erase())

return protocol with associated type

In this function

func sendB_<T: BProtocol>() -> T{
return B() as! T
}

you cannot return a B as a T because the person who uses the function defines what T is, not you and T can be any type that conforms to Protocol For example, I could do this:

class C: BProtocol 
{
typealias B = Float
}

let c: C = Main().sendB_()

By doing that, I am setting T to be a C and the forced typecast in sendB_() will fail.

Unfortunately, protocols with associated types cannot, themselves, be treated like a concrete type, so the approach you took with AProtocol will not work.

As I see it, you have two options. Change your function return type to B. After all, you always do return a B

func sendB_() -> B {
return B()
}

If you want to keep it generic, try

protocol BProtocol 
{
associatedtype B

init() // Needed to be able to do T() in generic function
}

func sendB_<T: BProtocol>() -> T{
return T()
}

You need to add the initialiser to the protocol to make sure that an instance of type T always exists.

Swift: Set protocol's associated type in argument / member variable

ConsumerA needs to be generic. Replace ModuleName with your actual module name.

class ConsumerA<KeyValueStore: ModuleName.KeyValueStore>
where KeyValueStore.Key == String, KeyValueStore.Value == Int {

Also, your method pair should be a subscript instead.

subscript(key: Key) -> Value { get set }
keyValueStore["Hello"] = 5

Protocol associatedType and

Defined associated type makes classes which conform protocol strong typed. This provide compile-time error handling.

In other hand, generic type makes classes which conform protocol more flexible.

For example:

protocol AssociatedRepository {
associatedtype T
func add(data : T) -> Bool
}

protocol GenericRepository {
func add<T>(data : T) -> Bool
}

class A: GenericRepository {
func add<T>(data : T) -> Bool {
return true
}
}

class B: AssociatedRepository {
typealias T = UIViewController
func add(data : T) -> Bool {
return true
}
}

class A could put any class into add(data:) function, so you need to makes your sure that function handle all cases.

A().add(data: UIView())
A().add(data: UIViewController())

both would be valid

But for class B you will get compile-time error when you will try to put anything except UIViewController

B().add(data: UIView()) // compile-time error here
B().add(data: UIViewController())

Unable to use protocol as associatedtype in another protocol in Swift

The problem, which David has already alluded to, is that once you constrain a protocol's associatedtype to a specific (non @objc) protocol, you have to use a concrete type to satisfy that requirement.

This is because protocols don't conform to themselves – therefore meaning that you cannot use Address to satisfy the protocol's associated type requirement of a type that conforms to Validator, as Address is not a type that conforms to Validator.

As I demonstrate in my answer here, consider the counter-example of:

protocol Validator {
init()
}
protocol Address : Validator {}

protocol FormRepresentable {
associatedtype ValueWrapper: Validator
}

extension FormRepresentable {
static func foo() {
// if ValueWrapper were allowed to be an Address or Validator,
// what instance should we be constructing here?
// we cannot create an instance of a protocol.
print(ValueWrapper.init())
}
}

// therefore, we cannot say:
enum AddressFrom : FormRepresentable {
typealias ValueWrapper = Address
}

The simplest solution would be to ditch the Validator protocol constraint on your ValueWrapper associated type, allowing you to use an abstract type in the method argument.

protocol FormRepresentable {
associatedtype ValueWrapper
func valueForDetail(valueWrapper: ValueWrapper) -> String
}

enum AddressFrom : Int, FormRepresentable {

// ...

func valueForDetail(valueWrapper: Address) -> String {
// ...
}
}

If you need the associated type constraint, and each AddressFrom instance only expects a single concrete implementation of Address as an input – you could use generics in order for your AddressFrom to be initialised with a given concrete type of address to be used in your method.

protocol FormRepresentable {
associatedtype ValueWrapper : Validator
func valueForDetail(valueWrapper: ValueWrapper) -> String
}

enum AddressFrom<T : Address> : Int, FormRepresentable {

// ...

func valueForDetail(valueWrapper: T) -> String {
// ...
}
}

// replace ShippingAddress with whatever concrete type you want AddressFrom to use
let addressFrom = AddressFrom<ShippingAddress>.Address1

However, if you require both the associated type constraint and each AddressFrom instance must be able to handle an input of any type of Address – you'll have use a type erasure in order to wrap an arbitrary Address in a concrete type.

protocol FormRepresentable {
associatedtype ValueWrapper : Validator
func valueForDetail(valueWrapper: ValueWrapper) -> String
}

struct AnyAddress : Address {

private var _base: Address

var addressLine1: String {
get {return _base.addressLine1}
set {_base.addressLine1 = newValue}
}
var country: String {
get {return _base.country}
set {_base.country = newValue}
}
var city: String {
get {return _base.city}
set {_base.city = newValue}
}

init(_ base: Address) {
_base = base
}
}

enum AddressFrom : Int, FormRepresentable {

// ...

func valueForDetail(valueWrapper: AnyAddress) -> String {
// ...
}
}

let addressFrom = AddressFrom.Address1

let address = ShippingAddress(addressLine1: "", city: "", country: "")

addressFrom.valueForDetail(AnyAddress(address))

implement protocol with different associated type

I just fund a way to archive this. The trick is to add another associated type in one of the subtypes of the protocol:

protocol ConvertableInt : Convertable {
associatedtype TResI
typealias TargetType = TResI
}

extension MyData : Convertable {
typealias TargetType = String
func convert() -> String { return String(self.data) }
}

extension MyData : ConvertableInt {
typealias TResI = Int
func convert() -> TResI { return self.data }
}

This also allows to get rid of the second subtype for string.

While this passes the compiler it totally crashes at runtime!

The compiler always calls the method defined which was defined at the explicit typealias. In this case:

typealias TargetType = String

Which will result in interpreting the address as a integer and give you totally wrong results. If you define it vice versa it will simply crash because it tries to interpret the integer as a address.



Related Topics



Leave a reply



Submit