> For the complete documentation index, see [llms.txt](https://lochiwei.gitbook.io/ios/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://lochiwei.gitbook.io/ios/master/todo/custom-types/unwrap.md).

# &#x20;Unwrap

可以解開一個 Optional 的值，並將此值轉換成一個 View (Optional\<Content>)。

{% hint style="danger" %}
只要使用 .[map](/ios/swiftui/view/optional-views.md#li-yong-map) 的技巧，可以讓 Unwrap 變成**完全沒有必要**的存在。
{% endhint %}

## 定義

```swift
struct Unwrap<Value, Content: View>: View
```

📦 Unwrap 可以解開一個型別為 **Value?** 的值，然後利用內部的一個 [@ViewBuilder ](/ios/swiftui/view/view-builder.md)closure `turnValueIntoContent: (Value) -> Content` 將此值轉換成型別為 **Content?** == **Optional\<Content>** 的一個 View。

## 語法

使用 Unwrap 時，要提供它

* 一個 **Value?** 的值
* 一個可以將 **Value** 轉換為 **Content** 的 **@ViewBuilder closure**

然後 Unwrap 就可以利用這個 closure 與 .**map**() 將 **Value?** 轉換為 **Content?** 。最後再透過 **body** 這個屬性將這個 Content? 呈現出去。

```swift
Unwrap(value, then: { /* turn value into content view */ })
Unwrap(value) { /* turn value into content view */ }
```

在下面的例子中，我們將一個 **Int?** (**Value** == **Int**) 送給 Unwrap 處理，然後在 Unwrap 內部透過 .**map**(**turnValueIntoContent**) 將 **Int?** 轉換為 **Text?**。

`{ Text(\"$0") }` 這個 **@ViewBuilder closure** 告訴我們：**turnValueIntoContent** 會把 **Int** 轉換為 **Text**，所以這個例子中的 **Content** == **Text**。這同時也告訴我們為什麼 .**map**(**turnValueIntoContent**) 會將 **Int?** 轉換為 **Text?** 了。

{% hint style="info" %}
當 `f: A -> B` 函數用 .**map**() 包起來，這時 `.map(f)` 就會把 `A?` 轉換成 `B?`。
{% endhint %}

因為 **Value** == **Int**、**Content** == **Text**，所以這個例子的 **Unwrap** 的完整型別為：`Unwrap<Int, Text>`。

```swift
let value: Int? = 1

let view = Unwrap(value) {
   Text("\($0)")            // Content == Text
}

let T = type(of: view)      // Unwrap<Int, Text>.Type
print(T)                    // Unwrap<Int, Text>
print(T.Body.self)          // Optional<Text>

// ⭐️ use `T.self` to reference the type object itself
T.Body.self == Text?.self   // true
```

## 程式碼

![](https://1830103165-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-M5-JmwCZMKh_d7RfBaN%2F-MIs5507v1zzt5jxWs_A%2F-MIs8e2YIbaqpRQT-G4n%2Foptional%20views.gif?alt=media\&token=9b4f5234-065d-4880-9d0e-9b881c9495f1)

{% tabs %}
{% tab title="⭐️  使用 .map" %}

```swift
import SwiftUI
import PlaygroundSupport

// live view
struct ContentView: View {
    
    @State private var isValue1Nil = false
    @State private var isValue2Nil = false
    
    var value1: Int? { isValue1Nil ? nil : 1}
    var value2: Int? { isValue2Nil ? nil : 2}
    
    var body: some View {
        VStack {
            
            VStack {
                // ⭐️ use .map
                value1.map {
                    Color.red.overlay(Text("\($0)"))
                }.animation(.default)
                // ⭐️ use .map
                value2.map {
                    Color.orange.overlay(Text("\($0)"))
                }.animation(.default)
            }.border(Color.blue, width: 3)
                
            // toggles
            HStack(spacing: 60) {
                Toggle(isOn: $isValue1Nil) {
                    Text("value1 is nil")
                }
                Toggle(isOn: $isValue2Nil) {
                    Text("value2 is nil")
                }
            } // toggles
                .padding()
                .overlay(Rectangle().stroke(Color.gray, style: .init(dash: [6])))
            
        } // VStack (container)
            .padding().border(Color.gray, width: 3)
    }
}

PlaygroundPage.current.setLiveView(ContentView())
```

{% endtab %}

{% tab title="📦  Unwrap" %}

```swift
/*
 * #todo : turn it into a modifier ?
 */

import SwiftUI

/// # 📦 Unwrap
/// unwraps a value (of type `Value`) and turns it 
/// into `some View` (== `Optional<Content>`).
struct Unwrap<Value, Content: View>: View {
    
    private let value: Value?         // value to be unwrapped
    private let turnValueIntoContent: (Value) -> Content
    
    // Unwrap(_:then:)
    // Unwrap(_:) { /* ... */ }
    init(
        _ value: Value?,
         @ViewBuilder then turnValueIntoContent: @escaping (Value) -> Content
    ) {
        self.value = value
        self.turnValueIntoContent = turnValueIntoContent
    }
    
    var body: some View {   
        value.map(turnValueIntoContent) // Optional<Content>
    }
}
```

{% endtab %}

{% tab title="💈 範例 " %}

```swift
// ⭐️ required: 📦 Unwrap

import SwiftUI
import PlaygroundSupport

// 📦 MyView
struct MyView: View {
    
    @State private var isValue1Nil = false
    @State private var isValue2Nil = false
    
    var value1: Int? { isValue1Nil ? nil : 1}
    var value2: Int? { isValue2Nil ? nil : 2}
    
    var body: some View {
        VStack {
            
            // stack of `Unwrap`s
            VStack {
                Unwrap(value1) {    // 📦 Unwrap
                    Color.red.overlay(Text("\($0)"))
                }
                Unwrap(value2) {
                    Color.orange.overlay(Text("\($0)"))
                }
            }.border(Color.blue, width: 3)
                
            // toggles
            HStack {
                Toggle(isOn: $isValue1Nil) {
                    Text("value1 is nil")
                }
                Toggle(isOn: $isValue2Nil) {
                    Text("value2 is nil")
                }
                Spacer()
            }
                .padding()
                .overlay(Rectangle().stroke(Color.gray, style: .init(dash: [6])))
                
        } // VStack (container)
            .padding()
            .border(Color.gray, width: 3)
    }
}

struct ContentView: View {
    
    var body: some View {
        MyView()            // ⭐️ required: 📦 MyView
    }
}

PlaygroundPage.current.setLiveView(ContentView())
```

{% endtab %}

{% tab title="👥  相關" %}

* [Optional Views](/ios/swiftui/view/optional-views.md)
  {% endtab %}

{% tab title="📘  " %}

* [x] [Optional SwiftUI View](https://www.swiftbysundell.com/tips/optional-swiftui-views) - Swift by Sundell
* [x] [Optional @ViewBuilder closure](https://stackoverflow.com/questions/60687912/optional-viewbuilder-closure) - StackOverflow
  {% endtab %}
  {% endtabs %}
