iT邦幫忙

2023 iThome 鐵人賽

DAY 23
0
Mobile Development

SwiftUI 的大大小小系列 第 23

Day 23 - 在 SwiftUI 中如何獲取和解析 Deep Link 並跳轉

  • 分享至 

  • xImage
  •  

hero

前導

這篇有用到的程式碼和觀念和 Day 22 的內容有關係,歡迎也去讀那一篇

本系列列與 NavigationStack 將關的文章如下,也歡迎讀讀

Deep Link 是什麼及應用

在 iOS 可以透過設定 custom URL schema 的方式,讓 app 外部可以透過這個 schema 開啟 app 。由於設定上幾乎沒有限制,因此在設定的時候必須要注意怎麼樣才不會跟別的 app 相同而相衝,導致在推行活動時因此而導致這一個渠道失效。

格式通常像這樣, myApp 則可以置換成符合 URL schema 或被稱為 "Protocol" 的標準的字串:

myApp://

:// 後面就可以像一般的 URL 由 host 和 path 以及 query string 等組合

不太一樣的地方是 host 可以不需要像是一班網站那樣需要有 subdomain, domain 和 TLD 的組合

因此可以簡單到像 RESTful API 那樣資源 (resource) 和識別子 (identifier) 的寫法:

myApp://food/10

把這個網址輸入 Safari 之後,拆開來後可以這樣理解

  • myApp:// - OS 會透過以這個 protocol 去尋找並開啟 app
  • food/10 - App 內會收到完整的、包含 protocol 的網址,而 app 內必須自己處理接收到這樣的網址該如何幫 app 跳轉頁面

話不多說我們就開始動手吧!

設定 Deep Link 用的 custom schema

2301

如上圖所示,步驟如下

  1. 在專案下選中要加入 custom URL schema 的 target
  2. 前往 Info 頁籤
  3. 前往最下面的 URL Types ,預設會顯示 "No URL Types" ,這時候按下左下角的 + 就可以新增一個

新增完之後依序輸入這兩個欄位的值即可:

欄位 內容 說明
Identifier App ID 輸入這個 app 的 "Bundle Identifier"
URL Schemas 需要被用來開啟的 protocol 就是前一個段落的 "myApp" 這段,這篇就以 ironman2023 為範例

來試試看

2302

就像這樣,在瀏覽器裡面輸入

ironman2023://

就能夠開啟 app

本篇的範例

struct Flavor: Identifiable, Hashable {
    let id = UUID().uuidString
    let name: String
}

struct ContentView: View {
    private let flavors = ["綠豆", "紅豆", "芋粿", "愛玉"].map(Flavor.init)
    @State private var path = [Flavor]()
    var body: some View {
        NavigationStack(path: $path) {
            Button {
                if let flavor1 = flavors.randomElement(),
                   let flavor2 = flavors.randomElement() {
                    path.append(contentsOf: [flavor1, flavor2])
                }
            } label: {
                Text("好手氣")
            }
            .foregroundColor(.purple)
            .padding()
            ForEach(flavors) { flavor in
                NavigationLink(flavor.name, value: flavor).padding()
            }
            .navigationDestination(for: Flavor.self) { flavor in
                Text("\(flavor.name)")
                    .navigationTitle(flavor.name)
            }
            .navigationTitle("點心")
        }
    }
}

透過 onOpenURL 擷取 Deep Link

只要在 View 後面接續 onOpenURL 就可以從 perform 的 closure 取得 URL 。

.onOpenURL { url in
    print(url.absoluteString)
}

加進原先的畫面

struct ContentView: View {
    /* ... */
    var body: some View {
        NavigationStack(path: $path) { /* ... */ }
        .onOpenURL { url in
            print(url.absoluteString)
        }
    }
}

ContentView 是這個 app 的根畫面, onOpenURL 加在最後面。執行 app 之後、從 Safari 觸發,在 Xcode 的 console 就會印出

ironman2023://

從 onOpenURL 觸發開啟畫面

和前一天的「好手氣」一樣,我們先來做

接收到 Deep Link 後,取得一個隨機 Flavor 並顯示出來

這時候就像 Day 22 提到的,透過將 flavor 加入 path ,

  1. NavigationStack 接受到 path 陣列的變更事件
  2. navigationDestination 找到 Flavor 型別需要開啟的畫面
  3. 實際推出畫面

onOpenURL 內的程式碼如下:

.onOpenURL { url in
    if let flavor = flavors.randomElement() {
        path.append(flavor)
    }
}

執行後從瀏覽器跳轉就會像是這樣

2303

指定項目

口味總共有四種

["綠豆", "紅豆", "芋粿", "愛玉"]

爲了簡化範例,在不動資料結構來訂這樣的需求

當收到這樣 Deep Link 的時候,顯示相對應的畫面

ironman2023://flavors/{:index}

若是其他網址,單純開啟 app 不需要做任何跳轉

例如,收到

ironman2023://flavors/1

就應該要顯示位於位置陣列位置 [1] 的「紅豆」的頁面

程式碼

.onOpenURL { url in
    guard let host = url.host() else { return }
    switch host {
    // 正式專案中 "flavors" 通常會用 enum 包裝起來
    // 在本篇文章則以純字串呈現
    case "flavors":
        // pathComponents 中會有斜線,因此先過濾掉
        let pathComponents = url.pathComponents.filter { $0 != "/" }
        // 取出第一個作為 index ,全部達成條件才繼續執行
        guard let first = pathComponents.first,
              let index = Int(first),
              index < flavors.count else { break }
        // 找到目標 flavor 並推入 path
        let flavor = flavors[index]
        path.append(flavor)
    default: break
    }
}

結果畫面

2304

結語

以上就是今天的如何在 SwiftUI 中搭配 Deep Link ,試著去解析傳入的網址後,搭配前幾天提到的方式顯示出對應的畫面。

那今天的 SwiftUI 的大大小小就到這邊,明天見!

環境

  • Xcode 15

本篇使用到的 UI 元件和 modifiers 基本上沒有受到版本更新影響。若要在 Xcode 14 等環境下使用也是沒問題的。


上一篇
Day 22 - SwiftUI 的 NavigationStack 與 path - 1
下一篇
Day 24 - SwiftUI 的 NavigationStack 與 path - 完結
系列文
SwiftUI 的大大小小30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言