How to achieve chain calls with Struct in Swift - swift

How to achieve a similar chain style with struct, do comment if it's a duplicate.
class Employee {
var name: String?
var designation: String?
func name(_ name: String) -> Employee {
self.name = name
return self
}
func designation(_ designation: String) -> Employee {
self.designation = designation
return self
}
}
/// Usage
let emp = Employee()
.name("Speedy")
.designation("iOS Enigineer")
print(emp.designation)

You should call the initialiser of the struct and pass it the given value:
struct Employee {
var name: String?
var designation: String?
func name(_ name: String) -> Employee {
.init(name: name, designation: self.designation)
}
func designation(_ designation: String) -> Employee {
.init(name: self.name, designation: designation)
}
}
If all the methods that you are chaining are boring property setters, you might as well just call the automatically generated initialisers:
// either parameter can be omitted!
Employee(name: "...", designation: "...")
This sort of chaining is only really useful when there are more complicated things going on with your properties. For example, when 2 properties must be set at the same time, or if you require generic parameters when setting those properties. For a good example of this, see how SwiftUI does this.
See also: Builder pattern set method in swift

Related

I need help to Swift with function

There is this job in Swift 5.0:
The class is presented below. In the body of this class, create a function that will print the parameters of this class for a specific object. Create such an object of class Student, call it this function and display the result on the screen:
Job class
class Student {
var name: String
var surname: String
var yearOfBorn: Int
var mark: Double
init(name: String, surname: String, yearOfBorn: Int, mark: Double) {
self.name = name
self.surname = surname
self.yearOfBorn = yearOfBorn
self.mark = mark
}
}
How i can make it?
I trying:
func printStudent() {
if name == name {
print(name)
} else if surname == surname {
print(surname)
} else if yearOfBorn == yearOfBorn {
print(yearOfBorn)
} else if mark == mark {
print(mark)
}
}
I’m not sure what your intent was with these if statements. Perhaps you are thinking of:
if let foo = foo { ... }
But that technique is only used if foo was an optional. But your properties are not optionals, so if let syntax is unnecessary.
Needless to say, you could just do:
func printStudent() {
print(name)
print(surname)
print(yearOfBorn)
print(mark)
}
FWIW, if your intent is just to print this out for your own purposes, you might want to make your class conform to CustomStringConvertible:
extension Student: CustomStringConvertible {
var description: String { return "<Student name=\(name); surname=\(surname); yearOfBorn=\(yearOfBorn); mark=\(mark)>" }
}
Then you don’t need to write your own printStudent method at all, but can use print directly:
let student = Student(name: "Rob", surname: "Ryan", yearOfBorn: 2000, mark: 4)
print(student)
And that will produce:
<Student name=Rob; surname=Ryan; yearOfBorn=2000; mark=4.0>
Alternatively, if you’re OK with struct value type instead, you don’t need the init method or the CustomStringConvertible protocol, at all. Then you can define Student as simply:
struct Student {
var name: String
var surname: String
var yearOfBorn: Int
var mark: Double
}
And then
let student = Student(name: "Rob", surname: "Ryan", yearOfBorn: 2000, mark: 4)
print(student)
Will produce:
Student(name: "Rob", surname: "Ryan", yearOfBorn: 2000, mark: 4.0)
If you want to print all the attributes of the object you don’t need this if statements; as a matter of fact if you pass name == name as the parameter the first if statement will be always entered and thus the other ones skipped.
You just need to create a function like this where you print each attribute:
func printStudent() {
print(self.name)
print(self.surname)
print(self.yearOfBorn)
...
}
You just need to print the variables:
func printStudent() {
print("Name: \(self.name), Surname: \(self.surname), Year Of Born: \(self.yearOfBorn)")
}
try this code:
func printStudent () {
print("name: \(self.name), surname: \(self.surname), yearOfBorn: \ .
(self.yearOfBorn), mark: \(self.mark)")
}

Swift Initializers and Class Inheritance

I just wanted to ask why does this code print out the quantity 1 even though the RecipeIngredient doesn't provide a default value:
class Food {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "[Unnamed]")
}
}
class RecipeIngredient: Food {
var quantity: Int
init(name: String, quantity: Int) {
self.quantity = quantity
super.init(name: name)
}
override convenience init(name: String) {
self.init(name: name, quantity: 1)
}
}
class ShoppingListItem: RecipeIngredient {
var purchased = false
var description: String {
var output = "\(quantity) x \(name)"
output += purchased ? " ✔" : " ✘"
return output
}
}
let item = ShoppingListItem()
print(item.quantity)
The default value is provided by the convenience initializer of RecipeIngredient.
ShoppingListItem() is shorthand for
ShoppingListItem.init() (inherited from `Food) which calls
ShoppingListItem.init(name:) (inherited RecipeIngredient) which calls
ShoppingListItem.init(name: name, quantity: 1) (inherited RecipeIngredient) which calls
ShoppingListItem.init(name: String, quantity: Int) (inherited RecipeIngredient) which ultimately sets quantity to 1
This is some pretty basic debugging. You should really look into how to use the Xcode debugger.

Variable declaration with underscore

I have seen this in some videos on Youtube.
class Student {
private var _name: String!
private var _studentID: Int!
var name: String {
return _name
}
var studentID:Int {
return _studentID
}
init(name: String, studentID: Int) {
self._name = name
self._studentID = studentID
}
}
Any reason why they are doing this (adding _name and _studentID) instead of:
class Student {
private var name: String!
private var studentID: Int!
init(name: String, studentID: Int) {
self.name = name
self.studentID = studentID
}
}
Thank you very much.
The first examples are essentially creating properties that are publicly readable but privately writable.
The second set of code does not do the same thing as the first set.
The proper way to write this code is:
private (set) var name: String // no need for the !
private (set) var studentID: Int // no need for the !
init(name: String, studentID: Int) {
self.name = name
self.studentID = studentID
}
This makes the properties readable by outside users but only settable by the class. This is what the 1st set of code implements but in a much more verbose and needless manner.
The use of underscore is just a naming convention carried over from Objective-C when creating private instance variables.
Personally, I'd avoid videos and tutorials that use the 1st set of code.

Convert Abstract Type to Concrete Type in Swift

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

Access properties via subscripting in Swift

I have a custom class in Swift and I'd like to use subscripting to access its properties, is this possible?
What I want is something like this:
class User {
var name: String
var title: String
subscript(key: String) -> String {
// Something here
return // Return the property that matches the key…
}
init(name: String, title: String) {
self.name = name
self.title = title
}
}
myUser = User(name: "Bob", title: "Superboss")
myUser["name"] // "Bob"
Update: The reason why I'm looking for this is that I'm using GRMustache to render from HTML templates. I'd like to be able to just pass my model object to the GRMustache renderer…
GRMustache fetches values with the keyed subscripting objectForKeyedSubscript: method and the Key-Value Coding valueForKey: method. Any compliant object can provide values to templates.
https://github.com/groue/GRMustache/blob/master/Guides/view_model.md#viewmodel-objects
This is a bit of a hack using reflection. Something along the lines of the following could be used.
protocol PropertyReflectable { }
extension PropertyReflectable {
subscript(key: String) -> Any? {
let m = Mirror(reflecting: self)
for child in m.children {
if child.label == key { return child.value }
}
return nil
}
}
struct Person {
let name: String
let age: Int
}
extension Person : PropertyReflectable {}
Then create a Person and access it's keyed properties.
let p = Person(name: "John Doe", age: 18)
p["name"] // gives "John Doe"
p["age"] // gives 18
You could modify the subscript to always return an interpolated string of the property value.
Adding some syntax sugar to Benzi's answer:
protocol PropertyReflectable { }
extension PropertyReflectable {
subscript(key: String) -> Any? {
let m = Mirror(reflecting: self)
return m.children.first { $0.label == key }?.value
}
}
struct Person: PropertyReflectable {
let name: String
let age: Int
}
Then create a Person and access it's keyed properties.
let p = Person(name: "John Doe", age: 18)
p["name"] // gives "John Doe"
p["age"] // gives 18
Using valueForKey should enable you to access properties using their names. Be sure that you're working with a object that inherit NSObject
class people: NSObject {
var age: NSString = "44"
var height: NSString = "153"
}
let person:people = people()
let stringVariable = "age"
person.valueForKey("age")
// Print "44"
person.valueForKey("\(stringVariable)")
// Print "44"
(GRMustache author here)
Until a swift-oriented Mustache library is out, I suggest having your classes inherit from NSObject (so that they have the valueForKey: method). GRMustache will then fetch values with this method.
In case this would still not work (blank values in the rendering), you may try to disable GRMustache security features (see https://github.com/groue/GRMustache/blob/master/Guides/security.md#disabling-safe-key-access)
Should you experience any other trouble, please open an issue right into the repository: https://github.com/groue/GRMustache/issues
EDIT February 2, 2015: GRMustache.swift is out: http://github.com/groue/GRMustache.swift
Shim's answer above doesn't work anymore in Swift 4. There are two things you should be aware of.
First of all, if you want to use value(forKey:) function, your class must inherit NSObject.
Secondly, since Objective-C doesn't know anything about value type, you have to put the #objc keyword in front of your value type properties and Swift will do the heavy-lifting for you.
Here is the example:
import Foundation
class Person: NSObject {
#objc var name: String = "John Dow"
#objc var age: Int = 25
#objc var height: Int = 180
subscript(key: String) -> Any? {
return self.value(forKey: key)
}
}
let person: Person = Person()
person["name"] // "John Dow"
person["age"] // 25
person["height"] // 180
I suppose you could do:
class User {
let properties = Dictionary<String,String>()
subscript(key: String) -> String? {
return properties[key]
}
init(name: String, title: String) {
properties["name"] = name
properties["title"] = title
}
}
Without knowing your use case I would strongly advise against doing this.
Another approach:
class User {
var name : String
var title : String
subscript(key: String) -> String? {
switch key {
case "name" : return name
case "title" : return title
default : return nil
}
}
init(name: String, title: String) {
self.name = name
self.title = title
}
}
It might be worth noting that Swift doesn't appear to currently support reflection by names. The reflect function returns a Mirror whose subscript is Int based, not String based.