---
# System prepended metadata

title: Swift extensions
tags: [Swift]

---

---
title: Swift extensions
description: Lateinit and scope fuction in Swift
tags: Swift
type: slide
slideOptions:
  transition: 'fade'
---

## 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
