How to declare getter for a swift class field? - swift

I have below definition of a getter in a swift class. I want to check whether the value is nil, if yes create a new instance; otherwise return that value. I am now getting into recursive call since I call self.userHomeNvController inside the getter method. I wander how I should achieve this in swift.
var userHomeNavController:UINavigationController? {
get {
var ctr:UINavigationController? = self.userHomeNavController
if self.userHomeNavController == nil{
ctr = self.storyboard?.instantiateViewControllerWithIdentifier("UserHomeNavigationController") as? UINavigationController
}
return ctr
}
}

Use a lazy property initialized by running a closure:
lazy var userHomeNavController: UINavigationController? = {
let controller = self.storyboard?.instantiateViewControllerWithIdentifier("UserHomeNavigationController") as? UINavigationController
return controller
}()

Related

What does this mean: "You do not have to provide an explicit implementation of a required initializer if you can satisfy the requirement"?

I've found this note in the Swift documentation about initializers:
You do not have to provide an explicit implementation of a required initializer if you can satisfy the requirement with an inherited initializer.
What is an "explicit" implementation? What is an "implicit" one then?
What does "satisfy the requirement with an inherited initializer" mean precisely?
Could you give me a code example, in which I don't have to provide an explicit implementation of a required initializer?
Here's an example with an inline explanation:
protocol JSONInitializable { // Use Encoders, but just for example
init(fromJSON: String)
}
class Foo: JSONInitializable {
let x: Int
// "required" is necessary because this init is required for the
// conformance to JSONInitializable
required init(fromJSON json: String) {
//...
x = 123 //some value from the JSON
}
}
class Baz: Foo {
// `init(fromJSON json: String)` can be inherited,
// so it's implicitly defined for Baz, as well as Foo.
}
class Bar: Foo {
// The presence of this uninitialized constant `y` requires an
// a value in the declaration, or an initializer that sets it
let y: Int
// Since we didn't specify a value for `y` in its declaration,
// this initializer must be explicitly specified so as to initialize `y`.
// Doing so blocks the inheritance of `init(fromJSON json: String)` from
// the super class, and requires us to define it ourselves,
// in order to preserve conformance to `JSONInitializable`
required init(fromJSON json: String) {
//...
y = 0
super.init(fromJSON: json)
}
}
It is saying this: If you have initialized all your properties as you declare them, there's no need to write an initializer.
Normally, when you have properties declared and not set, you write an init() method and set them there, and if there's a required initializer in the parent class, you call
super.init(possible, arg: anotherArg)
Since you don't need to set anything, you don't need to write anything, and since there's no init in your class, the super call will happen automatically. (Of course, if the required init needs values passed in, you still need to supply them.) So how is that accomplished? (see below.) The bottom line is that most of the work is done for you.
However, once you write an init(), then you're taking away this automatic behavior, and you have to make sure that those "automatic" calls are explicitly made.
Finally, before I provide an example, I should address this statement:
"if you can satisfy the requirement with an inherited initializer"
If the parent doesn't have an initializer which takes no arguments, how will it get those arguments? In that case you need initialize your class using that required super's init with the proper arguments.
Here's an example of a view controller which is not set up in IB, I would normally create it using the super's required initializer, since I didn't write an initializer for my derived class:
let vc = MagicController(nibName: nil, bundle: nil)
import UIKit
import Dotzu
class MagicController: UIViewController, UIGestureRecognizerDelegate {
let action = #selector(MagicController.buttonTapped(_:))
let action2 = #selector(MagicController.secondButtonTapped(_:))
let mainAction = #selector(MagicController.mainButtonTapped(_:))
let defaultPalette = Palette.randomPalette()
var questions = [Question]()
var startTime = TimeInterval()
var endTime = TimeInterval()
var selectedTimeIndex = 0
var selectedTagIndex = 0
var selectedRatingIndex = 0
var selectedTag = "swift"
let timeSpanDropDown = DropDown()
let ratingDropDown = DropDown()
let tagDropDown = DropDown()
var pageNumber = 1
var savedIndex = 0
var savedPostId = -1
var quotaCount = -1
var isFirstTime = true
let queryFactory = Queries(client: APIClient())
var timeSpanButton = UIBarButtonItem()
var ratingButton = UIBarButtonItem()
var tagButton = UIBarButtonItem()
var dbButton = UIBarButtonItem() // 🗄
/// start the console log, configure the main view's buttons/actions and present the UI
override func viewDidLoad() {
super.viewDidLoad()
Dotzu.sharedManager.enable()
self.edgesForExtendedLayout = []
configureButtons(container: view)
view.setNeedsLayout()
}
///configure the buttons/actions, prepare and present the UI
/// The first time this is called it sets up the buttons and drop downs
/// cleanupDataSource() has no effect the first time it is called
/// - Parameter animated: passed to super
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if isFirstTime {
isFirstTime = false
initializeButtons()
setupDropDowns()
}
cleanupDataSource()
}
/// etc.
}
As you can see, I did not write an init for this view controller, even though a view controller has a required init. I initialized my view controller using an init() that isn't even in my code.

Accessing a computed property outside of a method

I have a generic function inside a class in which a computed property is declared:
class CalculatorBrain {
var internalProgram = [AnyObject]()
var accumulator: Double = 0.0
var variableValues: Dictionary<String,Double> = [:]
func setOperand<T> (operand: T) {
if operand is Double {
accumulator = operand as! Double
internalProgram.append(operand as AnyObject)
}
else if variableName == operand as? String {
var dictionaryValue: Double? {
get {
return variableValues[variableName!]
}
set {
accumulator = newValue!
internalProgram.append(newValue! as AnyObject)
}
}
}
}
I want to set dictionaryValue to the value shown in the display from the view controller:
private var brain = CalculatorBrain()
#IBAction func setVariableValue(_ sender: UIButton) {
brain.dictionaryValue = displayValue
}
Obviously I can't, because dictionaryValue is locally defined and "Value of type CalculatorBrain has no memeber dictionaryValue" Now the question is, how can I make a computed property global, and make changes to it from inside a class method? Or, how can I access a computed property defined inside a class method from outside the function?
The problem is dictionaryValue is not a computed property of your class, it is just a variable declared in the setOperand function, so it is not accessible from outside the function.
You should declare it as a stored property of your class and change it when setOperand is called.

Swift initialization stored property outside init method of class issue

I have an swift class
class ApplicationManager {
var fanMode: FanMode
init()
{
self.applyDefaultSettings()
}
func applyDefaultSettings()
{
if let unwrappedFanMode = userDefaults.valueForKey(Consts.kStoredFanMode) as? FanMode {
self.fanMode = unwrappedFanMode
}
}
}
The code above throws this issue:
Use of 'self' in method call 'applyDefaultSettings' before all stored properties are initialized
What should I do here? So as message say I need to initialize all stored properties before I call any other method of class. So it means in init method I should initialize at least fanMode property. But I want to have method that apply kind of default settings for my properties to provide simple readability and clean code architecture. But maybe it's ok to use initializer of class to init all needed fields.
You also can do it by using this code:
var fanMode: FanMode = {
if let unwrappedFanMode = userDefaults.valueForKey(Consts.kStoredFanMode) as? FanMode {
return unwrappedFanMode
} else {
return FanMode()//some default implementation
}
}()
It is readable as You want.
As per Apple documentation, Swift does not allow you to left uninitialised variables or constants. If you want to set some default settings then assign your variables with initial values that will act as your default settings and later you can change them.
All instance properties must be initialized in the init method. You can either move the initialization to the init (defaultMode would be your default value if userDefaults is nil):
init() {
fanMode = (userDefaults?.valueForKey(Consts.kStoredFanMode) as? FanMode) ?? defaultMode
}
Set a default value for that property, for example:
var fanMode: FanMode = defaultMode
Or you can make your fanMode nullable:
var fanMode: FanMode? = nil
You can use an implicity unwrapped optional. Just add a ! to the variable declaration.
class ApplicationManager {
var fanMode: FanMode! //Implicitly unwrapped optional.
init()
{
self.applyDefaultSettings()
}
func applyDefaultSettings()
{
if let unwrappedFanMode = userDefaults.valueForKey(Consts.kStoredFanMode) as? FanMode {
self.fanMode = unwrappedFanMode
}
}
}
Basically it tricks xCode into telling it "Hey this variable is initialized and value will never be nil". But you want to be careful using these as if it does turn out to be nil your program will crash. But in your case it should be fine since you initialize it in the init method so it will never be nil before using it.

swift setter causing exc_bad_access

I have a simple class below
import Foundation
public class UsefulClass: NSObject{
var test:NSNumber{
get{return self.test}
set{
println(newValue)
self.test = newValue
}
}
override init() {
super.init()
self.test = 5;
}
}
and I'm initializing it here
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
var testClass = UsefulClass()
}
}
But it results in xcode printing out 200 5s and then crashing due to EXC_BAD_ACCESS code = 2. Why does this happen?
#vadian has provided a solution in his answer, which should fix your problem. Let me just explain what's happening.
You have created a computed property, i.e. a property which is not backed by a variable, instead both the getter and the setter do some processing, usually on another stored property, in order to respectively return a value and set a new value.
This is your computed property:
var test: NSNumber {
get { return self.test }
set {
println(newValue)
self.test = newValue
}
}
Look at the getter implementation:
return self.test
What does it do? It reads the test property of the current instance, and returns it. Which is the test property? It's this one:
var test: NSNumber {
get { return self.test }
set {
println(newValue)
self.test = newValue
}
}
Yes, it's the same property. What your getter does is to recursively and indefinitely calling itself, until a crash happen at runtime.
The same rule applies to the setter:
self.test = newValue
it keeps invoking itself, until the app crashes.
Swift variables are synthesized properties by default.
In the most cases this is sufficient (it's recommended to prefer Swift types)
var test: Int
override init() {
super.init()
test = 5
}
If you need to do something after a variable is set, use
var test: Int {
didSet{
println("\(oldValue) - \(newValue)")
}
}
your code sets the variable permanently by calling the setter which calls the setter which …
It's an infinite loop; your setter is recursively calling itself.
var test: NSNumber {
set {
test = newValue
}
}
This compiles fine, and an Objective-C programmer might expect no loop due to instead setting a "backing ivar" such as _test rather than re-calling the setter method.
But property-backing instance variable _ivars do not exist in Swift for computed properties unless you create them yourself.

Swift - Custom setter on property

I am converting a project in to Swift code and have come across an issue in a setter. My Objective-C code looked like this:
- (void)setDocument:(MyDocument *)document
{
if (![_document isEqual:document]) {
_document = document;
[self useDocument];
}
}
and allowed my View Controller to run this each time the document was set (typically in the prepareForSegue: method of the presenting View Controller).
I have found the property observers willSet and didSet but they only work when the property is being updated, not when it’s initialised and updated.
Any ideas? Thanks
UPDATE
after trying get{} and set{} I get the EXC_BAD_ACCESS error
var document: UIDocument? {
get {
return self.document!
}
set {
self.document = newValue
useDocument()
}
}
You can't use set like that because when you call self.document = newValue you're just calling the setter again; you've created an infinite loop.
What you have to do instead is create a separate property to actually store the value in:
private var _document: UIDocument? = nil
var document: UIDocument? {
get {
return self._document
}
set {
self._document = newValue
useDocument()
}
}
Here's a Swift 3 version
var document : UIDocument? {
didSet {
useDocument()
}
}