Swift inheritance issue. How to get appropriate class object - swift

I have two classes (to simplify I drop other filed the object is more complicated then the Person):
class Person
{
var name: String = "Default name"
init(object: PersonEntity)
{
name = object.daysMask
}
}
class Employer: Person
{
}
I have function that configure for me a person
func getConiguratedPerson(name: String) -> Person
{
let person = Person()
person.name = name
}
In case if I want to get Person I simple do this:
let person = getConiguratedPerson("Alex")
but what if I need Employer instead and I want to use this function as well
let employer = getConiguratedPerson("Alex") // returns Person as expected but need to have employer instead.
In Objective-C we can simple do this:
Employer *employer = Employer([self getConiguratedPerson:"Alex"]) if I remember.

The mistake is the getConfiguredPerson function. You want an initializer. Initializers return your own class, which is exactly what you want.
class Person {
var name: String = "Default name"
init(name: String) {
self.name = name
}
init(object: PersonEntity) {
self.init(object.daysMask)
}
}
class Employer: Person {}
Now to create a person, you just use Person(name: "Alex") and to get an employer you use Employer(name: "Alex").
Note that this is true in ObjC as well. You shouldn't have getConfiguredPerson there either. You'd should have [Person initWithName:].

You can't use upcasting here, because configured person is not of Employee type, but of Person type. To achieve the desired result I would suggest something like this:
class Person {
var name = "name"
required init() { }
}
class Employee: Person { }
func getPerson<T: Person>(name: String) -> T {
let person = T()
person.name = name
return person
}
let employee: Employee = getPerson("Alex")
or this if type of object returning by factory method depends on input:
func getPerson(name: String) -> Person {
if name != "Alex" {
return Person()
}
return Employee()
}
let employee = getPerson("Alex") as! Employee

Related

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 get value from superclass attribute?

Suppose I have SuperClass and I want to get its attribute value to subclass
class Person {
var name = String()
init(name : String){
self.name = name
}
}
var JonSnow = Person(name : "Jon Snow")
class Ranger : Person {
func getRangerName()->String {
return "Ranger name is \(super.name)"
}
// I want to get name value from super class
}
How do I get name value directly from super class Without reinit the value in subclass ?
You can simply access it self.x
So using your code:
class Person {
var name : String = "Jon Snow"
}
class Ranger : Person {
func printName() {
print(self.name)
}
}
Edit
Here is a full working project with that example implemented: download project

Initializers in class swift

What is the difference in initializing a variable:
class Person {
var name = String()
}
instead of:
class Person {
var name : String
init(name: String) {
self.name = name
}
}
thanks
First snippet
You can call
let person = Person()
Second snippet:
You must call
let person = Person(name:"")
to get an instance with an empty name property
PS: "" is easier to write than String()

One-step object creation and property initialization in Swift?

Is there a way to assign property values to a class instance even if it is not a parameter in the init constructor? For example, in C# I can do this:
public class Student
{
public string firstName;
public string lastName;
}
var student1 = new Student();
var student2 = new Student { firstName = "John", lastName = "Doe" };
Notice for student2 I can still assign values during initialization even though there's no constructor in the class.
I could not find in the documentation if you can do something like this for Swift. If not, is there a way to use extensions to extend the Student class to assign property values during initialization?
The reason I'm looking for this is so I can add a bunch of instances to an array without explicitly creating variables for each student instance, like this:
var list = new[] {
new Student { firstName = "John", lastName = "Doe" },
new Student { firstName = "Jane", lastName = "Jones" },
new Student { firstName = "Jason", lastName = "Smith" }
}
Any native or elegant way to achieve this in Swift?
You have a couple of options depending on how you want to configure this type and what syntax is most convenient for you.
You could define a convenient initializer which accepts the properties you want to set. Useful if you're setting the same properties all the time, less useful if you're setting an inconsistent set of optional properties.
public class Student
{
public var firstName:String?;
public var lastName:String?;
}
extension Student {
convenience init(firstName: String, lastName: String) {
self.init()
self.firstName = firstName
self.lastName = lastName
}
}
Student(firstName: "Any", lastName: "Body")
You could define a convenience initializer which accepts a block to configure the new instance.
extension Student {
convenience init(_ configure: (Student) -> Void ) {
self.init()
configure(self)
}
}
Student( { $0.firstName = "Any"; $0.lastName = "Body" } )
You could imitate Ruby's tap method as an extension so you can operate on an object in the middle of a method chain.
extension Student {
func tap(block: (Student) -> Void) -> Self {
block(self)
return self
}
}
Student().tap({ $0.firstName = "Any"; $0.lastName = "body"})
If that last one is useful you might want to be able to adopt tap on any object. I don't think you can do that automatically but you can define a default implementation to make it easier:
protocol Tap: AnyObject {}
extension Tap {
func tap(block: (Self) -> Void) -> Self {
block(self)
return self
}
}
extension Student: Tap {}
Student().tap({ $0.firstName = "Any"; $0.lastName = "body"})
If your class has no required initialiser, you can use a closure method to set the Student properties before returning the new Student object as follow:
public class Student {
var firstName = String()
var lastName = String()
}
let student1 = Student()
let student2: Student = {
let student = Student()
student.firstName = "John"
student.lastName = "Doe"
return student
}()
print(student2.firstName) // John
print(student2.lastName) // Doe
I just wanted to point out that if your structure can be immutable, you can just use a struct rather than a class and you'll get an implicit initializer for free:
Just paste this into a playground and change struct to class and you'll see what I mean.
struct Student
{
var firstName : String?
var lastName : String?
}
var student = Student(firstName: "Dan", lastName: "Beaulieu")
This is just syntactic candy but I use a custom operator to do this kind of thing:
infix operator <- { associativity right precedence 90 }
func <-<T:AnyObject>(var left:T, right:(T)->()) -> T
{
right(left)
return left
}
let rgbButtons:[UIButton] =
[
UIButton() <- { $0.backgroundColor = UIColor.redColor() },
UIButton() <- { $0.backgroundColor = UIColor.greenColor() },
UIButton() <- { $0.backgroundColor = UIColor.blueColor() }
]
The reason I'm looking for this is so I can add a bunch of instances to an array without explicitly creating variables for each student instance[.]
I'm not familiar with C#, but in Swift, you can do that just by initializing the objects inside the array declaration:
var list: [Student] = [
Student(firstName: "John", lastName: "Doe"),
Student(firstName: "Jane", lastName: "Jones"),
Student(firstName: "Jason", lastName: "Smith")
]
The other approaches suggested are all equally valid, but if you are simply trying to populate an array without declaring any variables, Swift makes that easy.

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.