avregi
avregi

Reputation: 137

Generic array in protocol without generic constraint

How do you specify a generic property in a protocol where the conforming type specifies the array type?

Info: Lets say we want to create a protocol which defines an array property where the type of the array is unknown. The conforming types specify the type of this array. This is a possible solution:

protocol Wallet {
    associatedtype Value
    var moneyStack: [Value] {get set}
}

struct BitcoinWallet: Wallet {
    var moneyStack: [String]
}

struct EthereumWallet: Wallet {
    var moneyStack: [Int]
}

The problem with this approach is that we can't access the moneyStack property:

let bitcoinWallet = BitcoinWallet(moneyStack: ["A","B"])
let etheureumWallet = EthereumWallet(moneyStack: [1,2])

let wallets: [Wallet] = [bitcoinWallet, etheureumWallet]

for wallet in wallets {
    print(wallet.moneyStack) // not possible
}

We can try another approach where the type of the array is any:

protocol Wallet {
    var moneyStack: [Any] {get set}
}

struct BitcoinWallet: Wallet {
    var moneyStack: [Any]
}

struct EthereumWallet: Wallet {
    var moneyStack: [Any]
}

This is a possible solution but an array which holds [Any] is too generic. I can't specify the type. Example:

let bitcoinWallet = BitcoinWallet(moneyStack: ["A","B"])
let ethereumWallet = EthereumWallet(moneyStack: [1,2])

var wallets: [Wallet] = [bitcoinWallet, ethereumWallet]

for wallet in wallets {
    print(wallet.moneyStack.count) // this works
    print(wallet.moneyStack[0]) // this works
}

let badBitcoinWallet = BitcoinWallet(moneyStack: [12.4, 312312.123]) // BitcoinWallets aren't allowed to hold doubles!

wallets.append(badBitcoinWallet) // This shouldn't be allowed!

The first solution is what I want. The type is specified in every struct but Swift complains I can't use generic constraints. The main problem I want to solve is that I want an array which holds different types of wallets, which all have their own array of a different type.

I thought a protocol would make this easy but it doesn't. Am I doing something wrong with my architecture? Should I even use protocols? Maybe subclassing can solve this?

Upvotes: 2

Views: 886

Answers (1)

Ilya Kharabet
Ilya Kharabet

Reputation: 4631

You can't store 2 objects with different types in array (Wallet<String> and Wallet<Int> are different types).

But you can try something like this:

protocol WalletProtocol {
    func printMoneyStack()
}

class Wallet<T> {
    var moneyStack: [T] = []
    init(moneyStack: [T]) {
        self.moneyStack = moneyStack
    }
}

class BitcoinWallet: Wallet<String>, WalletProtocol {
    func printMoneyStack() {
        print(moneyStack)
    }
}

class EthereumWallet: Wallet<Int>, WalletProtocol {
    func printMoneyStack() {
        print(moneyStack)
    }
}


let bitcoinWallet = BitcoinWallet(moneyStack: ["A","B"])
let etheureumWallet = EthereumWallet(moneyStack: [1,2])

let wallets: [WalletProtocol] = [bitcoinWallet, etheureumWallet]

for wallet in wallets {
    wallet.printMoneyStack()
}

Upvotes: 1

Related Questions