devxoul / Then

✨ Super sweet syntactic sugar for Swift initializers
MIT License
4.19k stars 291 forks source link

simplifying initializers #60

Open carlos-chaguendo opened 6 years ago

carlos-chaguendo commented 6 years ago

@devxoul What do you think of this syntax?

   let label = UILabel {
        $0.textAlignment = .center
        $0.textColor = .black
        $0.text = "Hello, World!"
   }
devxoul commented 6 years ago

I don't think it works for some classes that take parameters.

carlos-chaguendo commented 6 years ago

That's true, but for classes that don't need parameters it's very useful, keeping in mind that many times we can omit those parameters and set them in the code block

    let label = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 20)).then {
        $0.textAlignment = .center
        $0.textColor = .black
        $0.text = "Hello, World!"
    }

equals to

    let label = UILabel {
        $0.frame = CGRect(x: 0, y: 0, width: 100, height: 20)
        $0.textAlignment = .center
        $0.textColor = .black
        $0.text = "Hello, World!"
    }

I think the second option is easier to understand.

devxoul commented 6 years ago

I'm worried about adding a custom initializer to existing types. I found there is a crash when using a custom NSObject subclass that doesn't implement init().

import Foundation

protocol Then {}
extension NSObject: Then {}

extension Then where Self: NSObject {
  init(block: (Self) -> Void) {
    self.init()
    block(self)
  }
}

class Foo: NSObject {
  var name: String?

  init(handler: () -> Void) {
    super.init()
  }
}

// Fatal error: Use of unimplemented initializer 'init()' for class '__lldb_expr_2.Foo'
let foo = Foo {
  $0.name = ""
}

The compiler cannot ensure that we're not using the safe initializer. Do you have an idea?

carlos-chaguendo commented 6 years ago

https://docs.swift.org/swift-book/LanguageGuide/Initialization.html

Subclasses do not inherit their superclass initializers by default. However, superclass initializers are automatically inherited if certain conditions are met.

Rule 1 If your subclass doesn’t define any designated initializers, it automatically inherits all of its superclass designated initializers.

Rule 2 If your subclass provides an implementation of all of its superclass designated initializers—either by inheriting them as per rule 1, or by providing a custom implementation as part of its definition—then it automatically inherits all of the superclass convenience initializers.

These rules apply even if your subclass adds further convenience initializers.

your error is caused by creating an initializer


class Foo: NSObject {

    init(handler: () -> Void) {
        super.init()
    }
}

// the initializer `init()`  was not inherited  *Rule1*
Foo.init()

That said, it can be assumed that the correct implementation is:

class Foo: NSObject {
    var name: String?

    override init() {
        super.init()
    }

    init(handler: () -> Void) {
        super.init()
    }
}

In my opinion, this type of case should be specified in the documentation of Then