Inheritance and Initialization in Swift

I am trying to create two different classes: for the superclass I create my own properties and the init () function with appropriate parameters. However, when I create my subclass with my own properties and init () function, I get into trouble.

What I'm trying to achieve here is that at some point in my application, the user enters a bunch of fields, and when he / she is ready and clicks on the done button, the content of the fields will be used as an argument to my init function ( ) subclass. The superclass init () has fewer parameters than the init () of the subclass.

I've read about assigned and convenience initializers, but I'm a little confused. Maybe I really don't need them, I'm not sure.

What I'm trying to avoid is changing the value of any of the subclass properties manually after calling init ().

Any help is greatly appreciated.

/// NEW CODE:

Class Habit {

enum HabitType {
    case health
    case wealth
    case social
    case career
    case noType
}

var habitID: Int
var title: String
var type: HabitType
var duration: Int
var toAccomplish = [String]()
var currentFeelings = [String]()
var howToFix = [String]()

init?(habitID: Int, title: String, type: String, duration: Int, toAccomplish: [String]?, currentFeelings: [String]?, howToFix: [String]?) {

    self.habitID = habitID
    self.title = title
    switch type {
    case "health":
        self.type = HabitType.health
    case "wealth":
        self.type = HabitType.wealth
    case "social":
        self.type = HabitType.social
    case "career":
        self.type = HabitType.career
    default:
        self.type = HabitType.noType
    }
    self.duration = duration

    if let accomplish = toAccomplish {
        self.toAccomplish = accomplish
    }
    if let feelings = currentFeelings {
        self.currentFeelings = feelings
    }
    if let fix = howToFix {
        self.howToFix = fix
    } else {
        return nil
    }
}

      

}

MiniHabit class: Habit {

var trigger: String
var action: String

init(habitID: Int, title: String, type: String, duration: Int, toAccomplish: [String]?, currentFeelings: [String]?, howToFix: [String]?, trigger: String, action: String) {

    ////init() Error: A non-failable initializer cannot chain to failable initializer 'init(habitID:title:type:duration:toAccomplish:currentFeelings:howToFix:)' written with 'init?' 
    super.init(habitID: habitID, title: title, type: type, duration: duration, toAccomplish: toAccomplish, currentFeelings: currentFeelings, howToFix: howToFix)

    self.trigger = trigger
    self.action = action

}

      

}

+3


source to share


3 answers


There is something wrong with your code:

  • You don't need a keyword required

    in the initializer. As stated in the documentation :

Write a modifier required

in front of your class initializer definition to indicate that each subclass of the class must implement that initializer



  1. Properties you assign in the superclass initializer should not be assigned in the subclass

This code works in playground (Xcode 6.3):

public class Habit {
    var habitID: Int
    var title: String
    var type: String
    var duration: Int
    var toAccomplish = [String]()
    var currentFeelings = [String]()
    var howToFix = [String]()

    public init(habitID: Int, title: String, type: String, duration: Int, toAccomplish: [String]?, currentFeelings: [String]?, howToFix: [String]?) {

        self.habitID = habitID
        self.title = title
        self.type = type
        self.duration = duration

        if toAccomplish != nil {
            self.toAccomplish = toAccomplish!
        }
        if currentFeelings != nil {
            self.currentFeelings = currentFeelings!
        }
        if howToFix != nil {
            self.howToFix = howToFix!
        }
    }
}

public class MiniHabit: Habit {
    var trigger: String = ""
    var action: String = ""

    public init(habitID: Int, title: String, type: String, duration: Int, toAccomplish: [String]?, currentFeelings: [String]?, howToFix: [String]?, trigger: String, action: String) {
        super.init(habitID: habitID, title: title, type: type, duration: duration, toAccomplish: toAccomplish, currentFeelings: currentFeelings, howToFix: howToFix)

        self.trigger = trigger
        self.action = action
    }
}

var h = MiniHabit(habitID: 0, title: "", type: "", duration: 1, toAccomplish: nil, currentFeelings: nil, howToFix: nil, trigger: "", action: "")

      

+1


source


I made a few changes to your code, this is how it looks now:

// 1.
class Habit {
    var habitID: Int
    var title: String
    var type: String
    var duration: Int
    var toAccomplish = [String]()
    var currentFeelings = [String]()
    var howToFix = [String]()

    init(habitID: Int, title: String, type: String, duration: Int, toAccomplish: [String]?, currentFeelings: [String]?, howToFix: [String]?) {

        self.habitID = habitID
        self.title = title
        self.type = type
        self.duration = duration

        // 2.
        if let acomplish = toAccomplish {
            self.toAccomplish = acomplish
        }

        if let feelings = currentFeelings {
            self.currentFeelings = feelings
        }

        if let fix = howToFix {
            self.howToFix = fix
        }
    }

    class MiniHabit: Habit {
        // 3.
        var trigger: String
        var action : String

        init(habitID: Int, title: String, type: String, duration: Int, toAccomplish: [String]?, currentFeelings: [String]?, howToFix: [String]?, trigger: String, action: String) {
            // 4.
            self.trigger = trigger
            self.action = action

            super.init(habitID: habitID, title: title, type: type, duration: duration, toAccomplish: toAccomplish, currentFeelings: currentFeelings, howToFix: howToFix)
        }
    }
}

      

1. You probably don't need to make your class public. From the documentation:

Open access allows entities to be used in any source file from their defining module, as well as in a source file from another module that the defining module imports. Typically, you use public access when pointing a public interface to a framework.

By default your classes will have access to it internal

, which should be fine if you are not going to create a framework from these classes.

For more information see Access Control in Swift Programming here: https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/TheBasics.html



2. Using optional binding, using if let

is preferred to check if the optional value is nil and then forcibly expand it. For more information on optional binding, see The Fast Programming Language section.

3. trigger

and action

do not need to specify the initial values of blank lines, if you set them up , you call the super init. Swift states that all properties defined in your current class must be initialized before being called super.init

. Since you set them before calling super.init

, you can also use let

instead var

for trigger

and action

if you want them to be immutable.

4. Now you only initialize the variables that it MiniHabit

defines, all other variables are left to be set by the designated initializer on Habitat

.

Also, I noticed that Habit

there is a variable in type

. If there type

should only be a few values ​​I would recommend using enum

, for example:

class Habit {
    enum HabitType {
        // Give these more appropriate names.
        case HabitType1
        case HabitType2
        case HabitType3
    }

    var type: HabitType

    // Other variables...
}

      

Hope it helps.

+4


source


New answer in response to updated code

It looks better than last time, but there are a few more changes you could make:

First, you don't need to use a switch statement to check what type of enum type

should be. You can use pass enum type as an argument to the initializer. For example, say this is your class Habit

:

class Habit {
    enum HabitType {
        case Health
        case Wealth
        case Social
        case Career
    }

    let type: HabitType

    // Don't use a String here, use the HabitType enum.
    init(type: HabitType) {
        self.type = type
    }
}

      

Then you could create a Habit instance:

let aHabit = Habit(type: .Health)

      

For more information, see the Swift enumeration.

Second, regarding your listing HabitType

, I see that you have a case NoType

. This would be a perfect place to use options instead of using NoType

. For example, you can declare a variable type

optional, then when it is equal nil

, you know that there was no type. For example, this will be your HabitType

enum:

enum HabitType {
    case Health
    case Wealth
    case Social
    case Career
}

// And this is how could could define type to be optional.
let type: HabitType?

      

Third, the error you get in the initializer MiniHabit

is that you have declared the initializer to Habit

be fail-safe (denoted ?

after init

). It is not necessary in this situation, and instead your initializer might look like this:

// Not failable anymore. 
init(habitID: Int, title: String, type: String, duration: Int, toAccomplish: [String]?, currentFeelings: [String]?, howToFix: [String]?) {

    // Setup other variables

    if let accomplish = toAccomplish {
        self.toAccomplish = accomplish
    }
    if let feelings = currentFeelings {
        self.currentFeelings = feelings
    }
    if let fix = howToFix {
        self.howToFix = fix
    } 

    // No need to return nil if the user didn't supply a value for howToFix.
}

      

For more information on failable initialiser see here: https://developer.apple.com/swift/blog/?id=17

Hope it helps! Let me know if anything else needs clarification.

+1


source







All Articles