Implement generic method in swift - swift

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

Related

How to pass generic property in swift?

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")

SwiftUI - View showing Elements conforming to a Protocol and and using == for the Elements

I need to display arrays of different structs, conforming to a common Protocol, in a View.
As advised in SwiftUI - View showing Elements conforming to a Protocol and ForEach over them I tried like this - it works fine!
Now I need to check elements of the array for Equality.
Letting the Protocol conform to Equatable does not compile -
It gets the error: Protocol 'Suggest' can only be used as a generic constraint because it has Self or associated type requirements.
//protocol Suggest :Equatable {
protocol Suggest {
var desc: String { get }
}
struct Person : Suggest {
var surname : String
var name: String
var id: String { return name }
var desc: String { return name }
}
struct Book : Suggest {
var titel : String
var abstact : String
var id: String { return titel }
var desc: String { return titel }
}
let books = [ Book(titel: "book 1", abstact: "abstract1"),
Book(titel: "book 2", abstact: "abstract2")
]
let persons = [ Person(surname: "John", name: "Doe"),
Person(surname: "Susan", name: "Smith"),
Person(surname: "Frank", name: "Miller")
]
struct ContentView: View {
var body: some View {
VStack {
SuggestList(list: books)
SuggestList(list: persons)
}
}
}
struct SuggestList: View {
var list : [Suggest]
// this line does not compile, if Suggest conforms to Equitable
// "Protocol 'Suggest' can only be used as a generic constraint because it has Self or associated type requirements"
var body: some View {
List(list.indices, id: \.self) { index in
Text(list[index].desc)
// .onTapGesture {
// if list.contains(list[index]){print ("hello")}
// }
}
}
}
you need use <SuggestType: Suggest> and also make Suggest protocol Equatable and then use and define == in Person and Book
struct ContentView: View {
var body: some View {
VStack {
SuggestList(list: books)
SuggestList(list: persons)
}
}
}
protocol Suggest: Equatable {
var desc: String { get }
}
struct Person: Suggest {
var surname: String
var name: String
var id: String { return name }
var desc: String { return name }
static func == (lhs: Person, rhs: Person) -> Bool {
return lhs.name == rhs.name
}
}
struct Book: Suggest {
var titel: String
var abstact: String
var id: String { return titel }
var desc: String { return titel }
static func == (lhs: Book, rhs: Book) -> Bool {
return lhs.titel == rhs.titel
}
}
let persons = [Person(surname: "John", name: "Doe"),
Person(surname: "Susan", name: "Smith"),
Person(surname: "Frank", name: "Miller")]
let books = [Book(titel: "book 1", abstact: "abstract1"),
Book(titel: "book 2", abstact: "abstract2")]
struct SuggestList<SuggestType: Suggest>: View {
var list : [SuggestType]
var body: some View {
List(list.indices, id: \.self) { index in
Text(list[index].desc)
.onTapGesture {
if list.contains(list[index]){ print(list[index].desc) }
}
}
}
}
This Answer is belong to question in comments part! About Equatable function.
If you do not define Equatable function explicitly, then Xcode would take care itself if it can infer it by itself, some times in complex struct it will ask you to show it when instance of your struct are equal, but when you define Equatable function explicitly Xcode would apply your custom rule, for example I create 2 type of Person, which in first one PersonV1 we did not define == for it but in second one PersonV2 we did defined! So Xcode would be taking all persons with same name equal in PersonV2 if even they have deferent surname. try this down code for more real testing example. And any update for surname in PersonV2 would not take any place, because it does not count in determine if 2 instance of PersonV2 are equal or not! Once you initialized an instance of PersonV2, the surname will not be updatable anymore. you can try to update but it will not applied because in make no deference if this instance is the same or not!
Notice: We could make PersonV2 equality function to re act to surname change as well with this code, but I think you want to work just with name like in your question:
static func == (lhs: PersonV2, rhs: PersonV2) -> Bool {
return lhs.name == rhs.name && lhs.surname == rhs.surname
}
struct ContentView: View {
#State private var person1: PersonV1 = PersonV1(surname: "Frank", name: "Miller")
#State private var person2: PersonV2 = PersonV2(surname: "John", name: "Doe")
var body: some View {
VStack(spacing: 50.0) {
VStack(spacing: 20.0) {
Button("update name of person1") { person1.name += " updated!" }
Button("update surname of person1") { person1.surname += " updated!" }
}
.padding()
.background(Color.yellow)
.onChange(of: person1) { newValue in print("onChange for person1:", newValue) }
VStack(spacing: 20.0) {
Button("update name of person2") { person2.name += " updated!" }
Button("update surname of person2") { person2.surname += " updated!" }
}
.padding()
.background(Color.red)
.onChange(of: person2) { newValue in print("onChange for person2:", newValue) }
}
}
}
protocol Suggest: Equatable {
var desc: String { get }
}
struct PersonV1: Suggest {
var surname: String
var name: String
var id: String { return name }
var desc: String { return name }
}
struct PersonV2: Suggest {
var surname: String
var name: String
var id: String { return name }
var desc: String { return name }
static func == (lhs: PersonV2, rhs: PersonV2) -> Bool {
return lhs.name == rhs.name
}
}

support var!? in protocol

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 }
}

Does injecting factory make sense in this scenario (Swinject)

What is the right (cleanest and most concise) way to have PetOwner that can at any later point in program create new instances of Cat?
Let's assume that createAnotherAnimal can be called by PetOwner itself after it gets response to some async request, therefore creating as many instances of Cat as needed at the time of creating PetOwner is not possible.
I solved the problem with injecting factory, but I am not convinced that it is the best way to tackle the problem, what are the alternatives in Swinject?
protocol AnimalType {
var name: String? { get set }
func sound() -> String
}
class Cat: AnimalType {
var name: String?
init(name: String?) {
self.name = name
}
func sound() -> String {
return "Meow!"
}
}
protocol PersonType {
func play() -> String
func createAnotherAnimal() -> Void
}
class PetOwner: PersonType {
var pets: [AnimalType] = []
let petFactory : AnimalFactory
init(petFactory : AnimalFactory) {
self.petFactory = petFactory
}
func createAnotherAnimal() {
let pet = petFactory.factoryMethod()
self.pets.append(pet)
}
func play() -> String {
if(pets.count>0) {
let pet : AnimalType = pets[0];
let name = pet.name ?? "someone"
return "I'm playing with \(name). \(pet.sound())"
} else {
return "No animals"
}
}
}
class AnimalFactory {
let factoryMethod : () -> AnimalType
init(factoryMethod: () -> AnimalType) {
self.factoryMethod = factoryMethod
}
}
// Create a container and register service and component pairs.
let container = Container()
container.register(AnimalType.self) { _ in Cat(name: "Mimi") }
container.register(PersonType.self) { r in PetOwner(petFactory: r.resolve(AnimalFactory.self)!) }
container.register(AnimalFactory.self){r in AnimalFactory(factoryMethod:{ () -> AnimalType in r.resolve(AnimalType.self)!}) }
// The person is resolved to a PetOwner with a Cat.
let person = container.resolve(PersonType.self)!
person.createAnotherAnimal()
print(person.play())

Accessing Class Variables by enum

I have following code which basically defines enum for different companies and different methods for each company.
protocol Fetcher {
func getDetail()
static var idRegex:String {get}
}
class FooFetcher:Fetcher {
static var idRegex = "(^\\d{5}$)"
var company = Company.Foo
init (name:String?, id:String) {
println("Name: \(name) id: \(id)")
}
func getDetail() {
println("Foo Get detail")
}
}
class BarFetcher:Fetcher {
static var idRegex = "(^\\d{11}$)"
var company = Company.Bar
init (name:String?, id:String) {
println("Name: \(name) id: \(id)")
}
func getDetail() {
println("Bar Get detail")
}
}
enum Company:Int {
case Foo
case Bar
func fetcher(name:String?, id:String) -> AnyObject {
switch self {
case Foo:
return FooFetcher(name: name, id: id)
case .Bar:
return BarFetcher(name: name, id: id)
}
}
}
var compList = [Company.Foo , Company.Bar]
How can I get idRegex by using compList array only? I don't want to create another array like [FooFetcher.idRegex,BarFetcher.idRegex] because it will bloat the file. I want to use enum because it is easy to store their rawValue with Core Data.
I also wonder how to write better fetcher function for Company enum. Currently I use fetcher function like below
var company = Company.Foo
var strategy = company.fetcher("Some name", id: "Some id") as! Fetcher
strategy.getDetail()
AnyObject does not support Fetcher, in the company.fetcher function instead of returning AnyObject is -> Fetcher
enum Company:Int {
case Foo
case Bar
func fetcher(name:String?, id:String) -> Fetcher {
switch self {
case .Foo:
return FooFetcher(name: name, id: id)
case .Bar:
return BarFetcher(name: name, id: id)
}
}
}
FOO also there corresponds almost .foo
Along with eliasRuizHz's answer, add a new regex method to the Company enum.
func regex() -> String {
switch self {
case .Foo:
return FooFetcher.idRegex
case .Bar:
return BarFetcher.idRegex
}
}
The two responses both provide good contributions. For example, returning a Fetcher rather than AnyObject makes complete sense, as does a regex() function. The thing that I query, however, is whether you could use enums with associated values rather than this enum + class approach, but putting that to one side and answering the question in hand if you really want to directly access the .idRegex from an instance then it needs to be a regular instance variable not a class variable. One way to have it both ways is to return the type variable through an instance variable like so:
protocol Fetcher {
func getDetail()
var idRegex:String {get}
static var regex:String {get}
}
class FooFetcher:Fetcher {
static var regex = "(^\\d{5}$)"
var idRegex:String { return FooFetcher.regex }
var company = Company.Foo
init (name:String?, id:String) {
println("Name: \(name) id: \(id)")
}
func getDetail() {
println("Foo Get detail")
}
}
class BarFetcher:Fetcher {
static var regex = "(^\\d{11}$)"
var idRegex:String { return BarFetcher.regex }
var company = Company.Bar
init (name:String?, id:String) {
println("Name: \(name) id: \(id)")
}
func getDetail() {
println("Bar Get detail")
}
}
enum Company:Int {
case Foo
case Bar
func fetcher(name:String?, id:String) -> Fetcher {
switch self {
case Foo:
return FooFetcher(name: name, id: id)
case .Bar:
return BarFetcher(name: name, id: id)
}
}
}
var compList = [Company.Foo, Company.Bar]
var company = Company.Foo
var strategy = company.fetcher("Some name", id: "Some id")
strategy.getDetail()
strategy.idRegex
Edit Responding to Query
If you want the fetcher to not necessarily initialize an instance then set whatever requirement there is to determine whether a value or nil is returned.
enum Company:Int {
case Foo
case Bar
func fetcher(name:String?, id:String) -> Fetcher? {
switch self {
case Foo:
if FooFetcher.regex == "(^\\d{11}$)" {
return FooFetcher(name: name, id: id)}
else {return nil}
case .Bar:
if BarFetcher.regex == "(^\\d{11}$)" {
return BarFetcher(name: name, id: id)
}
else {return nil}
}
}
}
var compList = [Company.Foo, Company.Bar]
var company = Company.Foo
var strategy = company.fetcher("Some name", id: "Some id")
strategy?.getDetail()
strategy?.idRegex