Swift init with function to make clear separation - swift

In ObjC I used to call in my init something like
- init() {
[self initView1]
[self initView2]
}
to separate the setup of the views
but in swift I can't call a function before any variable has been set, is there something I'm missing or it's not possible ?

You can assign an initial value on their declaration line:
class MyClass {
var prop1: String = ""
var prop2: String = ""
init() {
self.initView1()
self.initView2()
}
func initView1() {
self.prop1 = "Hello"
}
func initView2() {
self.prop2 = "World"
}
}
Or alternatively, make them implicitly unwrapped optionals by adding an exclamation mark (!):
var prop1: String!
var prop2: String!
This tell the compiler that the programmer has guaranteed that these properties will be properly initialized before use.

In Swift, every property must be initialized before you can call methods.
This is described in detail in the chapter Two-Phase Initialization of the Swift Programming Language Manual.
If you want to delegate initialization to a method, then you at least have to provide some dummy values first.
Alternatively, you can refactor your code to create your views from static methods (if that is possible); then you can use these objects for initialization:
class MyClass {
let prop1: String
let prop2: String
static func create1() -> String {
return "Hello"
}
static func create2() -> String {
return "World"
}
init() {
prop1 = Blah.create1()
prop2 = Blah.create2()
}
}

Related

Empty initializer for swift structures

Is it possible to initialize a swift struct without any values?
To summarize the idea, I'm creating a struct with two attributes, those attributes were filled when I called the init() method, as it should, but the problem is, I have a function inside this struct and I need to use it, in some cases, without filling the fields in the struct initializer.
I've tried adding the init(){} function, hoping that somehow it would work like the empty constructors in java, but I've got no success.
struct Quiz {
var pergunta: String
var resposta: Bool
init(pergunta: String, resposta: Bool) {
self.pergunta = pergunta
self.resposta = resposta
}
//Something like that
init(){}
func popularQuiz() -> Array<Quiz> {
...
}
}
Is there a way to do it or swift doesn't have the option to create a empty structure?
The big question is why do you want to have an empty Quiz? What does an empty Quiz mean?
Those questions aside, you can only create an initializer with no parameters if all of the properties can be given default values or if the properties are optional.
struct Quiz {
var pergunta: String = ""
var resposta: Bool = false
init(pergunta: String, resposta: Bool) {
self.pergunta = pergunta
self.resposta = resposta
}
init(){}
}
Now your empty init will work because all properties are fully initialized. But is that what you really want?
Another way to get the same result is to use default values on the parameters of the main initializer.
struct Quiz {
var pergunta: String
var resposta: Bool
init(pergunta: String = "", resposta: Bool = false) {
self.pergunta = pergunta
self.resposta = resposta
}
}
Now you can call:
Quiz()
Quiz(pergunta: "Some question", resposta: "Some Answer")
Quiz(pergunta: "Question with no answer")
Any of these still result in all properties being initialized.
The last option would be to make the properties optional instead of using special default values. But this adds more headache later.
struct Quiz {
var pergunta: String?
var resposta: Bool?
init(pergunta: String, resposta: Bool) {
self.pergunta = pergunta
self.resposta = resposta
}
init(){}
}
The empty init will leave the properties as nil. But now you need to properly handle the optional values everywhere. Again, is that what you really want?

How to refactor this code with static init variables?

I have a static class like this:
class CDService {
static var initVar:Type?
static var var2:Type2? = {
return initVar?.someProp
}
static func method() {
var2?.doSomething()
}
//....a lot more methods all using var2?
}
Because initVar is optional (require the user to set it before using this service), now var2 has to be optional too.
And then all the methods in this class all of a sudden requires unwrapping. They are use a lot.
Is there a way to refactor this code to just not run if initVar is not yet? I could do a "if let" check in each method but its really tedious. If there is 50 methods?
You can use optional extensions to give default values , I've tried, it's comes really handy when using default datatype optionals.
extension Optional where Wrapped == String { // same goes for other data types
var orEmpty: String {
return self ?? ""
}
var orDash: String {
return self ?? "-"
}
var orZero: String {
return self ?? "0"
}
}
or in your case
extension Optional where Wrapped == Type2 {
var orEmpty: Type2 {
return self ?? Type2()
}
}

Using an overriden static property during initialization

I would like to create a class with a static property that subclasses can override, which would be used to initialize instances. So far, I've tried to accomplish this like this:
import Cocoa
class A: NSObject {
class var staticProperty: String {
return "A"
}
var property: String = A.staticProperty
}
class B: A {
override class var staticProperty: String {
return "B"
}
}
This does not work, since B().property still returns "A". How could I change this code so that property contains the value specified by the subclass? Any help would be appreciated!
Edit
I would like to initialize property with the value of staticProperty, so this could also look like this:
var property: SomeClass = SomeClass(A.staticProperty)
But then, this initialization should still use "A" for class A, and "B" for class B.
Edit 2 (After #RakeshaShastri's comment)
For my specific use-case, I need property to be stored (so not computed) and non-lazy.
Edit 3
In short, I'm trying to build a Realm model class which has a few to-many relationships to other models. For these models (which are quite similar), I'm trying to create a superclass which contains the shared functionality, amongst which is also the inverse relationship. Therefore, I want to have a static property which contains the key in the first model to either of the other models, and then initialize a LinkingObjects property using this key name. Since Realm does not allow this to be lazy or computed, I cannot use these functionalities here.
If you inherit from NSObject why not using it ?
import Cocoa
class A: NSObject {
var property: String
public override init() {
let str = type(of: self).perform(#selector(getter: type(of: self).staticProperty))?.takeUnretainedValue() as! String
property = str
}
#objc class var staticProperty: String {
return "A"
}
}
class B: A {
override class var staticProperty: String {
return "B"
}
}
You can do this with this aproach
class A {
var prop: String{
return "A"
}
}
class B: A {
override var prop: String{
return "B"
}
}
print(A().prop) // "PRINTS A"
print(B().prop) // "PRINTS B"
A.staticProperty will use static dispatch and will always point to A's class property. You need dynamic dispatch here, aka type(of: self).
However, self needs an instance to work with, thus var property: String = type(of: self.staticProperty won't compile.
However, lazy properties can work around this limitation, so you could declare property as a lazy one:
class A: NSObject {
class var staticProperty: String {
return "A"
}
private(set) lazy var property: String = { type(of: self).staticProperty }()
}
class B: A {
override class var staticProperty: String {
return "B"
}
}
print(B().property) // B
P.S. the private(set) part is just something I usually do, I rarely allow extrinsic factors to change my object.
Update As #MartinR has pointed out, lazy is not a choice for the OP. An alternative solution that doesn't use a lazy var is to use a "shadowing" property:
class A: NSObject {
class var staticProperty: String {
return "A"
}
private var _property: String?
var property: String {
get {
return _property ?? type(of: self).staticProperty
}
set {
_property = newValue
}
}
}
class B: A {
override class var staticProperty: String {
return "B"
}
}
let b = B()
print(b.property) // B
b.property = "B'"
print(b.property) // B'

Modifying an array passed as an argument to a function in Swift

Sorry for the newbie question; I'm still learning. I'm running into some odd behavior and couldn't find any documentation on this. I was wondering if you can help point out what I'm doing wrong here.
Error:
Cannot use mutating member on immutable value: 'arr' is a 'let' constant
class mySingleton {
static let sharedInstance = mySingleton()
private init() {}
var men = ["bob", "doug"]
var women = ["alice", "lisa"]
func removeFirst() {
self.arr.removeFirst()
}
func removeFirstByGender(gender: String) {
if gender == "men" {
self.modify(arr: self.men) // <-- error occurs here.
} else {
self.modify(arr: self.women) // <-- error occurs here.
}
}
func modify(arr: [String]) {
arr.removeFirst()
}
}
You need to change the definition of modify to accept an inout parameter. By default, function arguments are immutable, but by using the inout keyword, you can make them mutable. You also need to pass the argument by reference.
func modify( arr: inout [String]) {
arr.removeFirst()
}
var stringArr = ["a","b"]
modify(arr: &stringArr) //stringArr = ["b"] after the function call

Can I initialize a class with a variable which is defined by a function inside that class in Swift 2?

Is it possible that the variable which initializes my class is computed by a function inside that class?
I first initialize MyClass:
let MyClass = MyClass()
Second, this would be the code in the class:
class MyClass {
var myString = computeRandomString()
func computeRandomString() -> String {
piece of code
return(randomString)
}
}
Whenever I create an instance of MyClass I want the myString to be a randomString. In order for that I made a function within the same class.
There are two options.
First, if computeRandomString does not rely on knowing about anything about specific instances of this class, it could be converted to a class function, or simply moved outside of the class entirely.
For example:
class MyClass {
var myString = MyClass.computeRandomString()
class func computeRandomString() -> String {
return "random string"
}
}
or
func computeRandomString() -> String {
return "random string"
}
class MyClass {
var myString = computeRandomString()
}
The second option is to set it in the initializers (rather than giving it a default value), but you'll only be able to do this after all values (including this one) have been assigned a value.
For example:
class MyClass {
var myString: String
init() {
myString = ""
myString = computeRandomString()
}
func computeRandomString() -> String {
return "random string"
}
}
The reason we can't use an instance method to assign a default value for an instance variable is better explained by the warning generated when we try to use the instance method within an initializer before first giving it a value:
class MyClass {
var myString: String
init() {
myString = computeRandomString()
}
func computeRandomString() -> String {
return "random string"
}
}
On the myString =... line in init, we see the following error:
Use of 'self' in method call 'computeRandomString' before all stored properties are initialized.
This error, unfortunately, does not show up when we use it as the property's default value, as you're trying to do, but it does accurately describe the actual problem.
We cannot use self before our class is fully initialized, and that includes calling methods on self. And until all of our stored properties have valid values, our instance is not fully initialized, so we can never use an instance method to give a non-optional stored property its first value.
A possible solution is a lazy computed property.
The string is created when the property is accessed the first time
class MyClass {
lazy var computeRandomString : String = {
let alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXZY0123456789"
let alphaLength = UInt32(alphabet.characters.count)
var randomString : String = ""
for _ in 0..<20 {
let random = Int(arc4random_uniform(alphaLength))
let index = alphabet.startIndex.advancedBy(random)
randomString += String(alphabet[index])
}
return randomString
}()
}
for _ in 0..<10 {
print(MyClass().computeRandomString)
}
You have to do it like this:
class MyClass {
var myString: String
init() {
self.myString = MyClass.computeRandomString()
}
static func computeRandomString() -> String {
piece of code
return(randomString)
}
}
This will set myString to the results of computeRandomString() on initialisation (creating) of a MyClass-object.
I changed computeRandomString() to static because otherwise it could not be used before initialisation has finished, thanks to #nhgrif.