## Swift extensions #### Allen @ PhotoGrid --- ## Lateinit #### The lateinit lets you defer property initialization. When using lateinit, you should initialize your property as soon as possible. ---- ### Swift ```swift= class LoginViewController: UIViewController { //Class 'LoginViewController' has no initializers private var loginButton: UIButton override func viewDidLoad() { super.viewDidLoad() loginButton = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 36)) view.addSubview(loginButton) } } ``` ---- ### Kotlin ```kotlin= class LoginFragment : Fragment() { private lateinit var loginButton: Button override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) loginButton = view.findViewById(R.id.login_button) } } ``` ---- ### Swift ```swift= class LoginViewController: UIViewController { @Lateinit private var loginButton: UIButton override func viewDidLoad() { super.viewDidLoad() loginButton = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 36)) view.addSubview(loginButton) } } ``` ---- #### Implemente lateinit in Swift ```swift= @propertyWrapper struct Lateinit<T> { private var _value: T? var wrappedValue: T { get { guard let value = _value else { fatalError("Lateinit property has not been initialized") } return value } set { _value = newValue } } } ``` --- ## Scope function #### Scope functions' sole purpose is to execute a block of code within the context of an object. ---- #### Duplicate coding ```swift= acceptButton.setBackgroundColor(SplashGDPRColors.acceptButton.value, for: .normal) acceptButton.setTitleColor(.white, for: .normal) acceptButton.setTitle(String(PG_gdprContinue), for: .normal) acceptButton.clipsToBounds = true acceptButton.layer.cornerRadius = 3 acceptButton.isEnabled = true ``` ```swift= withObject(acceptButton) { $0.setBackgroundColor(SplashGDPRColors.acceptButton.value, for: .normal) $0.setTitleColor(.white, for: .normal) $0.setTitle(String(PG_gdprContinue), for: .normal) $0.clipsToBounds = true $0.layer.cornerRadius = 3 $0.isEnabled = true } ``` ---- #### Redundant variable and statement ```swift= let row = itemArray.firstIndex { $0.type == .tool } if let row = row { let indexPath = IndexPath(row: row, section: 0) if let toolCell = tableView.cellForRow(at: indexPath) as? MainPageToolCell { toolCell.refreshDraftsEntrance() } } ``` ```swift= itemArray.firstIndex { $0.type == .tool }?.let { //$0: Int IndexPath(row: $0, section: 0) }.let { //$0: IndexPath tableView.cellForRow(at: $0) as? MainPageToolCell }?.also { //$0: MainPageToolCell $0.refreshDraftsEntrance() } ``` ---- ### Functional programming! ---- ### Kotlin #### The Kotlin standard library contains several functions: #### let, run, with, apply, and also ---- ### Kotlin <style> .heatMap th { background: gray; } .heatMap tr:nth-child(1) { background: orange; } .heatMap tr:nth-child(2) { background: orange; } .heatMap tr:nth-child(3) { background: green; } .heatMap tr:nth-child(4) { background: blue; } .heatMap tr:nth-child(5) { background: blue; } </style> <div class="heatMap"> <font size=5> | Function | Object reference | Return value | Is extension function | |:-------- |:---------------- |:-------------- |:--------------------- | | let | it | Lambda result | YES | | run | this | Lambda result | YES | | with | this | Lambda result | NO | | apply | this | Context object | YES | | also | it | Context object | YES | </font> </div> ---- ### Kotlin #### Context object: this or it ```kotlin= val str = "Hello" // this str.run { println("The string's length: $length") //println("The string's length: ${this.length}") // does the same } // it str.let { println("The string's length is ${it.length}") } ``` --- ### Kotlin #### run & let ```kotlin= //run val price = Book().run { name = "Scope Functions" price = 400 price } ``` ```kotlin= //let val price = Book().let { it.name = "Scope Functions" it.price = 400 it.price } ``` ---- ### Kotlin #### with ```kotlin= //with val book = Book() val privce = with(book) { name = "Scope Functions" price = 400 price } ``` ---- ### Kotlin #### also & apply ```kotlin= //apply val book = Book().apply { name = "Scope Functions" price = 400 } //also val book = Book().also { it.name = "Scope Functions" it.price = 400 } ``` ---- ### Swift #### Can the context be changed in Swift? ---- ### Swift #### let & also ```swift= //let let price: Int = Book().let { $0.name = "Scope Functions" $0.price = 400 $0.price } ``` ```swift= //also let bool = Book().also { $0.name = "Scope Functions" $0.price = 400 } ``` ---- ### Swift #### with ```swift= //withStruct let book: Book = ... let price: Int = with(book) { book.checked = true return book.price } ``` ---- #### Implement scope functions in Swift ---- ```swift= @inlinable public func withStruct<T: Any, V>(_ value: T, _ block: (inout T) throws -> V) rethrows -> V { var copy = value return try block(&copy) } ``` ```swift= @inlinable @discardableResult public func withObject<T: AnyObject, V>(_ value: T, _ block: (T) throws -> V) rethrows -> V { return try block(value) } ``` ---- ```swift= extension ScopeFunction where Self: Any { @inlinable @discardableResult public func `let`<T>(_ block: (inout Self) throws -> T) rethrows -> T { var copy = self return try block(&copy) } @inlinable public func also(_ block: (inout Self) throws -> Void) rethrows -> Self { var copy = self try block(&copy) return copy } } ``` --- ### Soft Reminder #### Assing value type when using scope function ---- ```swift= let dic = [String: String]() //dic.let { it -> [String: String] in //dic.also { it -> [String: String] in withStruct(dic) { it -> [String: String] in it["Name"] = "Allen" return it }.also { print($0) } print("\(dic)") ``` ---- ```swift= var dic = [String: String]() //assign the return value! dic = withStruct(dic) { it -> [String: String] in it["Name"] = "Allen" return it }.also { print($0) } print("\(dic)") ``` --- ### Summary <font size=5> | Function | Return value | Value type | Reference type | |:---------- |:-------------- |:---------- |:-------------- | | let | Lambda result | v | | | also | Context object | v | | | withStruct | Lambda result | v | | | withObject | Lambda result | | v | | takeIf | Context object | v | | </font> --- ### Thank you
{"metaMigratedAt":"2023-06-16T21:21:14.097Z","metaMigratedFrom":"YAML","title":"Swift extensions","breaks":true,"description":"Lateinit and scope fuction in Swift","slideOptions":"{\"transition\":\"fade\"}","contributors":"[{\"id\":\"33406e7c-1e78-4453-9de4-35a3702ca111\",\"add\":8361,\"del\":1077}]"}
    421 views