Reputation: 1377
I am trying to make Data Model for my app. here is the scenario:
my app has Customer Model which contains customer's info, and also contain his/her Payment Source. the API gives me two kind of payment sources: card and bank account which they have completely different fields.
So, here is my problem, I want to have abstract type which is PaymentSource then within each PaymentSource have a function to return object casted to it's type. some how I am type erasure.
I needed to put my abstract type in a box and use it as Concrete type (AnyPaymentSource).
So, I've done as following:
protocol PaymentSource {
associatedtype Kind
func cast() -> Kind
}
struct AnyPaymentSource<PS: PaymentSource> {
private var paymentSource: PS
init(paymentSource: PS) {
self.paymentSource = paymentSource
}
func cast() -> PS.Kind {
return paymentSource.cast()
}
}
struct Card: PaymentSource {
func cast() -> Card {
return self
}
}
struct BankAccount: PaymentSource {
func cast() -> BankAccount {
return self
}
}
struct Customer {
var firstName: String
var lastName: String
var email: String
var paymentSource : AnyPaymentSource<PaymentSource>
}
but Customer
gives me error with following description:
Using 'PaymentSource' as a concrete type conforming to protocol 'PaymentSource' is not supported
where am I doing wrong?
Upvotes: 4
Views: 3567
Reputation: 2294
Swift is statically typed language. That means the type of a variable must be known at compile time.
When i was faced with this problem, i solved it something like this
protocol PaymentSource {
associatedtype Kind
func cast() -> Kind
}
struct AnyPaymentSource<PS: PaymentSource> {
private var paymentSource: PS
init(paymentSource: PS) {
self.paymentSource = paymentSource
}
func cast() -> PS.Kind {
return paymentSource.cast()
}
}
struct Card: PaymentSource {
func cast() -> Card {
return self
}
}
struct BankAccount: PaymentSource {
func cast() -> BankAccount {
return self
}
}
struct Customer<T:PaymentSource> {
var firstName: String
var lastName: String
var email: String
var paymentSource : AnyPaymentSource<T>
}
func test(){
let customerWithCard = Customer<Card>(
firstName: "",
lastName: "",
email: "",
paymentSource: AnyPaymentSource(paymentSource: Card())
)
let customerWithBankAccount = Customer<BankAccount>(
firstName: "",
lastName: "",
email: "",
paymentSource: AnyPaymentSource(paymentSource: BankAccount())
)
print(customerWithCard.paymentSource.cast())
print(customerWithBankAccount.paymentSource.cast())
return
}
Upvotes: 4
Reputation: 31645
If what are you trying to achieve is what @Andrew Ashurov mentioned in his answer, there is no need to implement AnyPaymentSource
. As mentioned in Swift Protocols Documentation:
Protocols do not actually implement any functionality themselves. Nonetheless, any protocol you create will become a fully-fledged type for use in your code.
Meaning that are already able to treat a protocol as a type.
It might be:
protocol PaymentSource {
func cast() -> Self
}
struct Card: PaymentSource {
func cast() -> Card {
return self
}
}
struct BankAccount: PaymentSource {
func cast() -> BankAccount {
return self
}
}
struct Customer {
var firstName: String
var lastName: String
var email: String
var paymentSource : PaymentSource?
}
Creating Customers:
let cardCustomer = Customer(firstName: "Card Fname", lastName: "Card Lname", email: "[email protected]", paymentSource: Card())
let bankAccountCustomer = Customer(firstName: "Bank Account Fname", lastName: "Bank Account Lname", email: "[email protected]", paymentSource: BankAccount())
Note that in Customer
struct, paymentSource
property of type PaymentSource
which means it can assigned as any type that conforms to PaymentSource
protocol (Card
and BankAccount
in your case).
Upvotes: 0