Swift Initializers and Class Inheritance - swift

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.

Related

How to achieve chain calls with Struct in 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

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

how can I modify the property of a class in its subclasses?

class Animals{
var name : String = "default"
var age : Int = 0
func Details()-> String{
return "This animal is a \(name) and has \(age) years old."
}
}
class Dogs : Animals{
name = "dog"
}
class Cats : Animals{
name = "cat"
}
var MyAnimal = Dogs()
I want to see this message : "This animal is a dog and has 0 years old."
But each time I receive this one : "This animal is a default and has 0 years old."
var HisAnimal = Cats()
If you want stored, not computed, properties, you can set name in the initializer, like this:
class Animal {
let name: String
var age: Int = 0
/* designated initializer: fully initializes all instance properties */
init(name: String) {
self.name = name
}
func details() -> String {
return "This animal is a \(name) and has \(age) years old."
}
}
class Dog : Animal {
/* designated initializer of subclass: must call a designated
initializer from its immediate superclass */
init() {
super.init(name: "dog")
}
}
class Cat : Animal {
/* ... */
init() {
super.init(name: "cat")
}
}
let myAnimal = Dog()
This mechanism ensures that name is set from only one place, and is passed to the initializer explicitly.
Alternatively using protocols (and generics) over classes
You will most likely create instances of Cat:s and Dog:s, but you might not want to do so for abstract weird Animal:s. An alternative for Animal being a common superclass is letting it be a protocol.
protocol Animal {
static var species: String { get }
var name: String? { get }
var age: Int { get set }
var details: String { get }
}
extension Animal {
static var species: String { return "\(self)".lowercased() }
// since all the properties used in 'details' are blueprinted,
// we might as well supply a default implementation of it.
var details: String {
return "This animal is a \(Self.species)\(name.map{ " named \($0)" } ?? ""), aged \(age)."
}
}
struct Dog: Animal {
let name: String?
var age: Int
init(age: Int, name: String? = nil) {
self.name = name
self.age = age
}
}
var myDog = Dog(age: 3, name: "Fred")
print(myDog.details) // This animal is a dog named Fred, aged 3.
myDog.age = 4 // grats to Fred!
print(myDog.details) // This animal is a dog named Fred, aged 4.
let wildDog = Dog(age: 6) // never named ...
print(wildDog.details) // This animal is a dog, aged 3.
Note that I've chosen to use a class property species to name the species of each animal, and reserved the instance property name for those animals that are given a name; say, your dear dog named Fred (rather than your dear dog named dog).
Using protocols will also make it natural choice to prefer generics over typed abstract types (the latter which might be tempting when using a common superclass):
struct Cat: Animal {
let name: String?
var age: Int
init(age: Int, name: String? = nil) {
self.name = name
self.age = age
}
}
var wildCat = Cat(age: 2)
func yieldBirthday<T: Animal>(for animal: inout T) {
print(animal.details)
animal.age += 1
print("This \(T.species) now had a birthday!")
print(animal.details)
}
yieldBirthday(for: &myDog)
/* This animal is a dog named Fred, aged 4.
This dog now had a birthday!
This animal is a dog named Fred, aged 5. */
yieldBirthday(for: &wildCat)
/* This animal is a cat, aged 2.
This cat now had a birthday!
This animal is a cat, aged 3. */
One way to solve will be to use "template method pattern"
class Animals {
lazy var name : String = self.defaultName
var defaultName:String { return "default" }
func Details()-> String{
return "This animal is a \(name) and has \(age) years old."
}
}
class Dogs : Animals {
override var defaultName:String { return "dog" }
}
Another approach will be to create init method for each subclass and override the default values
Here is how I would do this, by making an initializer to set the name (with a default value):
class Animal: CustomStringConvertible {
let name: String
let age: Int
init(name: String = "default", age: Int = 0) { // FIXME: Does a default age of 0 make sense?
self.name = name
self.age = age
}
public var description: String {
return "This animal is a \(name) and is \(age) years old."
}
}
class Dog: Animal {
init() {
super.init(name: "Dog")
}
}
class Cat: Animal {
init() {
super.init(name: "Cat")
}
}
var myAnimal = Dog()
print (myAnimal)

How do I create a class that requires an instance variable to be set?

public class User : NSObject {
var id: Int //will throw an error during build
var name: String?
override init(){
}
convenience init(id: Int, name: String?){
self.id = id
self.name = name
}
}
I want to create a user class. The id should non-optional. However, my above code does not work unless I change the line to:
var id: Int = 0
I don't want to do this. Is there a better way?
Delete the word convenience! Convenience is exactly the opposite of what you want. You want this to be a designated initializer. Like this:
public class User : NSObject {
var id: Int
var name: String?
init(id: Int, name: String?){
self.id = id
self.name = name
super.init()
}
}
This forces the creator of a User to supply an id, which is exactly what you want.
public class User {
let id: Int
var name: String?
init(id: Int, name: String? = nil) {
self.id = id
self.name = name
}
}
let user = User(id: 3) // works without errors
As you can see, I changed some things. You probably don't really need to subclass NSObject, and I don't think you'll want to change a user's id after initialisation, so it makes more sense to make it a let constant.

Overriding custom init from super class

I am trying to override custom init in my code as you can see I am creating Player class with default and custom init
class Player{
//Properties
var name : String
var score : Int
//Methods
func description() -> String{
return( "Player \(name) has score \(score)")
}
//Default Initiallizer
init(){
name = "Sohrab"
score = 0
}
//Custom Initializer
init(name : String, score : Int){
self.name = name
self.score = score
}
}
and below I create PremierPlayer class
class PremierPlayer : Player {
//Properties
var memberLevel : String
//Method
override func description() -> String {
let originalMessage = super.description()
return ("\(originalMessage) is a \(memberLevel)")
}
//Default Initilizer
override init() {
memberLevel = "Gold"
super.init()
}
//Custome Initilizer
init(name: String, score: Int, memberLevel:String) {
self.memberLevel = memberLevel
super.init(name: <#String#>, score: <#Int#>)
}
}
In Line super.init(name: <#string#>, score: <#Int#>) i got an error ISSUE expected ',' separator
What should I do?
The formatting of your (current version of) question is rather confusing, but if you are talking about this line:
super.init(name: <#String#>, score: <#Int#>)
... then you should replace <#String#> with just value of String and <#Int#> with Int types. Those <#...#> are placeholders that XCode injects into code automatically for you to replace them with something sensible.