i have 3 classes
class A {
var uid: Int64!
var countryCode: String? // <-- Attention
}
class B {
var uid: Int64!
var countryCode: String! // <-- Attention
}
class C {
var uid: Int64!
var countryCode: String = "AA" // <-- Attention
}
witch var for example countryCode as String?, String!
, String
and i create protocol, witch var as String? (As the weakest type to which you can bring all the others)
protocol FullCode {
var uid: Int64! { get }
var countryCode: String? { get } // <-- How to describe a variable for all (`T`, `T!`, `T?`) types?
}
extension FullCode {
var fullCode: String? { return "specificCode"}
}
bat if i added it to my classes
extension A : FullCode {}
extension B : FullCode {}
extension C : FullCode {}
i get error for classes B & C.
How can use one protocol for these types?
You can't make either B or C conform to FullCode, because the types for countryCode are incompatible.
You can't make this work by changing the type of FullCode.countryCode, because there is no common supertype for String, String!, and String?.
You'll need to change FullCode to declare some other property. Example:
protocol FullCode {
var uid: Int64! { get }
var optionalCountryCode: String? { get }
}
extension A : FullCode {
var optionalCountryCode: String? { return countryCode }
}
extension B : FullCode {
var optionalCountryCode: String? { return countryCode }
}
extension C : FullCode {
var optionalCountryCode: String? { return countryCode }
}
Related
Let's assume I have these two models:
struct Vegetarian: Menus {
var id: String? = UUID().uuidString
var vegiDishes: [String]?
var veganDishes: [String]?
}
struct Vegan: Menus {
var id: String? = UUID().uuidString
var veganDishes: [String]?
}
They are both conforms to this protocol:
protocol Menus: Identifiable, Codable, Hashable {
var id: String? {get set}
}
extension Menus {
var vegiDishes: [String]? {
get{ return [] } set{}
}
var veganDishes: [String]? {
get{ return [] } set{}
}
}
I have this object:
var myMenu: Vegetarian = Vegetarian()
If I want to add a dish, I'll do this:
addDish(menu: myMenu, dish: "Some Vegetarian Dish")
But since the two models have a different type of dishes properties, I'll have to have something like this:
func addDish<T: Menus> (menu: T, dish: String) {
switch menu {
case is Vegetarian:
var newMenu: Vegetarian = Vegetarian()
newMenu.veganDishes?.append(dish)
case is Vegan:
var newMenu: Vegan = Vegan()
newMenu.veganDishes?.append(dish)
default:
print("no such type")
}
}
Is there a way to simplify my method so I could specify a property in the call?
Let's say, something like this:
addDish(menu: myMenu, dish: "Some Vegetarian Dish", genericProperty: veganDishes)
Which will make my method look like this?
func addDishGenericProperty<T: Menus> (menu: T, dish: String, genericProperty?) {
menu.genericProperty.append(dish)
}
This seems perfect for a key-path! Or more specifically, a WritableKeyPath.
Since Vegetarian is a struct, it is a value type. This means we can't just set vegiDishes for example and expect that value to be reflected to our myMenu from outside the function. Instead you can use inout so myMenu is changed.
I had to also clear up the Menus and the types quite a bit, so they use non-optional arrays instead. If you need to check if the array is empty, use myMenu.vegiDishes.isEmpty.
Here is an example of how you can achieve all this:
protocol Menus: Identifiable, Codable, Hashable {
var id: String { get }
}
struct Vegetarian: Menus {
enum CodingKeys: CodingKey {
case vegiDishes
case veganDishes
}
let id = UUID().uuidString
var vegiDishes: [String]
var veganDishes: [String]
init(vegiDishes: [String] = [], veganDishes: [String] = []) {
self.vegiDishes = vegiDishes
self.veganDishes = veganDishes
}
}
struct Vegan: Menus {
enum CodingKeys: CodingKey {
case veganDishes
}
let id = UUID().uuidString
var veganDishes: [String]
init(veganDishes: [String] = []) {
self.veganDishes = veganDishes
}
}
Adding dishes:
func addDish<T: Menus>(menu: inout T, dish: String, kind: WritableKeyPath<T, [String]>) {
menu[keyPath: kind].append(dish)
}
var myMenu: Vegetarian = Vegetarian()
addDish(menu: &myMenu, dish: "Some Vegetarian Dish", kind: \.vegiDishes)
print(myMenu.vegiDishes)
Prints:
["Some Vegetarian Dish"]
Or alternatively, you could literally just do the following if possible in your case:
myMenu.vegiDishes.append("Some Vegetarian Dish")
What is the difference between { } and = when assigning an enum to a variable in Swift?
Why would you say var type: ItemType { .recipe } over var type: ItemType = .video.
Does the {} indicate that it is a computed property?
Also, is the { get } needed after the type in the protocol?
enum ItemType: String, Decodable {
case video
case recipe
}
protocol Item: Decodable {
var type: ItemType { get }
var title: String { get }
var imageURL: URL { get }
}
struct Video: Item {
var type: ItemType = .video
var title: String
var imageURL: URL
var url: URL
var duration: String
var resolution: String
}
struct Recipe: Item {
var type: ItemType { .recipe }
var title: String
var imageURL: URL
var text: String
var ingredients: [String]
}
Does the {} indicate that it is a computed property?
Yes.
Also, is the { get } needed after the type in the protocol?
To specify that the property, whether it be computed or stored, needs to support at least a getter. The setter is optional.
I have a use case to compare the enployee's rank. Here is what I want to do:
protocol Enployee: Comparable {
var id: String { get }
var rank: Int { get }
var name: String { get }
var type: String { get }
}
extension Enployee {
static func <(lhs: Enployee, rhs: Enployee) -> Bool {
return lhs.rank < rhs.rank
}
}
But I got the following error:
Protocol 'Enployee' can only be used as a generic constraint because it has Self or associated type requirements
Then I changed my code:
extension Enployee {
static func <(lhs: Self, rhs: Self) -> Bool {
return lhs.rank < rhs.rank
}
}
I can compile it. But when I continue working on my user case:
struct Engineer: Enployee {
var id: String
var rank: Int
var name: String
let type: String = "Engineer"
}
struct Manager: Enployee {
var id: String
var rank: Int
var name: String
let type: String = "Manager"
}
let staff1 = Engineer(id: "123", rank: 2, name: "Joe")
let staff2 = Engineer(id: "124", rank: 2, name: "Frank")
let staff3 = Manager(id: "101", rank: 10, name: "John")
public struct Department<T: Comparable> {
}
let queue = Department<Enployee>()
I got another error message:
Protocol 'Enployee' as a type cannot conform to 'Comparable'
Any idea?
The error message tells you what the problem is. Having declared Department<T: Comparable> you cannot resolve T as Enployee; it is a protocol, not a type conforming to Comparable (such as Engineer).
A generic has to be resolved to one type. It would be legal to declare Department<T: Enployee> if that is what you really mean. But then you are setting yourself up for multiple Department types, a department of Manager and a department of Engineer. That is probably not what you want, so this is a bad use of a generic.
Moreover, type strings are a horrifically bad smell:
struct Engineer: Enployee {
var id: String
var rank: Int
var name: String
let type: String = "Engineer" // uck
}
It seems much more likely that you want a class and subclasses here. Why fight against OOP?
I am implementing Protocol Oriented approach in Swift as codes below. The concept seems fun but I hope you get the idea. The problem for me is how to implement a generic function for that repeated printing tasks. Thank you in advances.
protocol Food {
var name: String { get }
}
struct Grass: Food {
var name: String { return "Grass" }
var calcium: Float!
}
struct Rice: Food {
var name: String { return "Rice" }
var calories: Float!
}
struct Insect: Food {
var name: String { return "Insect" }
var fiber: Float!
}
protocol Eat {
associatedtype food: Food
var name: String { get }
var itsFood: food { get }
}
struct Cow: Eat {
typealias food = Grass
var name: String { return "Cow" }
var itsFood: food {return food(calcium: 100)}
}
struct People: Eat {
typealias food = Rice
var name: String { return "People" }
var itsFood: food {return food(calories: 1000)}
}
struct Reptile: Eat {
typealias food = Insect
var name: String { return "Reptile" }
var itsFood: food {return food(fiber: 300)}
}
let cow = Cow()
print(cow.name)
print(cow.itsFood.name)
print(cow.itsFood.calcium)
let people = People()
print(people.name)
print(people.itsFood.name)
print(people.itsFood.calories)
let reptile = Reptile()
print(reptile.name)
print(reptile.itsFood.name)
print(reptile.itsFood.fiber)
If I understood correctly, you want a way to write a function that prints out an Eat conformer's name, food name, and the food's nutritional value.
Your current Food protocol has not got enough information on the food's nutritional value (calcium, calories, fiber). You should edit your protocol:
protocol Food {
var name: String { get }
var nutritionalValueName: String { get }
var nutritionalValue: Float! { get }
}
And implement the 2 new properties in the Food conformers. Here's an example:
struct Grass: Food {
var name: String { return "Grass" }
var calcium: Float!
var nutritionalValue: Float! { return calcium }
var nutritionalValueName: String { return "Calcium" }
}
Now, you can write a function. Note that since Eat has an associated type, it cannot be used as a parameter type directly, you need to introduce a generic parameter T and constrain it to Eat:
func printEat<T: Eat>(eat: T) {
print(eat.name)
print(eat.itsFood.name)
print("\(eat.itsFood.nutritionalValueName): \(eat.itsFood.nutritionalValue!)")
}
The function body is pretty self-explanatory.
You can call it like this:
printEat(eat: Cow())
printEat(eat: People())
printEat(eat: Reptile())
Output:
Cow
Grass
Calcium: 100.0
People
Rice
Calories: 1000.0
Reptile
Insect
Fiber: 300.0
I would use something like below to be able to use basic functions in one place. You would need super classes for common task functions, protocols would not be enough for that.
class Food {
var name: String? { return nil }
var calcium: Float?
var calories: Float?
var fiber: Float?
}
class Grass: Food {
override var name: String? { return "Grass" }
init(_ calcium: Float) {
super.init()
self.calcium = calcium
}
}
class Rice: Food {
override var name: String? { return "Rice" }
init(_ calories: Float) {
super.init()
self.calories = calories
}
}
class Insect: Food {
override var name: String? { return "Insect" }
init(_ fiber: Float) {
super.init()
self.fiber = fiber
}
}
protocol Eat {
var name: String? { get }
var itsFood: Food? { get }
func printInfo()
}
class Animal: Eat {
var name: String? { return "Cow" }
var itsFood: Food? { return Food() }
func printInfo() {
print(name ?? "")
print(itsFood?.name ?? "")
print(itsFood?.calcium ?? 0)
}
}
class Cow: Animal {
override var name: String? { return "Cow" }
override var itsFood: Grass {return Grass(100) }
}
class People: Animal {
override var name: String? { return "People" }
override var itsFood: Food {return Rice(1000)}
}
class Reptile: Animal {
override var name: String? { return "Reptile" }
override var itsFood: Food {return Insect(300)}
}
let cow = Cow()
cow.printInfo()
Check out this :
struct Fruit {
let fruitName : String
let color : String
init(_ name: String,_ color: String) {
self.fruitName = name
self.color = color
}
}
let fruit1 = Fruit("Apple", "Red")
let fruit2 = Fruit("Grapes", "Green")
let fruitStack = Stack<Fruit>()
fruitStack.push(fruit1)
fruitStack.push(fruit2)
let fruitFfromStack = fruitStack.pop()
print("Fruit popped from Stack, Name : \(String(describing: fruitFfromStack?.fruitName)) ,Color : \(String(describing: fruitFfromStack?.color))")
let fruitFfromStack1 = fruitStack.pop()
print("Fruit popped from Stack, Name : \(String(describing: fruitFfromStack1?.fruitName)) ,Color : \(String(describing: fruitFfromStack1?.color))")
https://reactcodes.blogspot.com/2019/01/generic-stack-implementation-with.html
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?
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
}
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: "cardemail#example.com", paymentSource: Card())
let bankAccountCustomer = Customer(firstName: "Bank Account Fname", lastName: "Bank Account Lname", email: "bankaccountemail#example.com", 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).