[SwiftUI] ShareLink の item 遅延評価

SwiftUI2021

     
⌛️ 2 min.

ShareLink で 共有アイテムを遅延評価する方法を説明します。

環境&対象

以下の環境で動作確認を行なっています。

  • macOS14.5
  • Xcode 15.4
  • iOS 17.5
  • Swift 5.9

ShareLink

SwiftUI には、共有メニューを作成するための 要素として、ShareLink という View が用意されています。


参考
ShareLinkApple Developer Documentation

以下のように使用します。item として渡しているものが、共有先に渡されるデータです。

struct ContentView: View {
    var body: some View {
        VStack {
            ShareLink("hello", item: "Hello", preview: SharePreview("Hello"))
        }
        .padding()
    }
}
ShareLink_macOS
ShareLink_iOS

以下の記事で説明しています。
SwiftUI2021 [SwiftUI] ShareLink の使い方(その1)

ShareLink での item の評価タイミング

凝った使い方をしようかなぁと考えたときに、気づくことがあります。

item の評価タイミングです。

ShareLink に渡す item は、当然ながら、ShareLink という View が表示されるときに評価されます。

ShareLink を表示する View によっては、この制限が邪魔になることがあります。

ShareLink を表示するためには、その引数も評価することが必要ですので、言語仕様的には当然ですが、使用する側からすると、ShareLink の ボタンがクリックされた時点で、評価して欲しい時があります。

ShareLink の item 評価を遅延させたいケースの例

例えば、共有向けデータの生成に 多くの計算コストがかかる場合です。

ShareLink が表示されるたびに計算されるため、評価の計算量が大きい時には、このことが問題となりえます。

開発者視点では、ShareLink が表示されるタイミングではなく、実際に共有される時にのみ 評価して欲しくなります。

実際に評価タイミングを確認してみます。

import SwiftUI
import OSLog

extension OSLog {
    fileprivate static var log = Logger(subsystem: "sharelinkEval", category: "timing")
}

final class ViewModel: ObservableObject {
    @Published var config: Bool = false
    func shareData() -> String {
        OSLog.log.debug("shareData generated with big calculation.... with config:\(self.config)")
        return "Hello \(config)"
    }
}

struct ContentView: View {
    @StateObject var viewModel = ViewModel()

    var body: some View {
        VStack {
            Text("Hello, world!")
            Toggle(isOn: $viewModel.config, label: { Text("Bool in ViewModel") })
            ShareLink(item: viewModel.shareData())
        }
        .padding()
    }
}

実行すると、以下のように ShareLink が 表示されます。

ShareLinkConfig_macOS
ShareLinkConfig_iOS

ShareLink が”表示された時” に、コンソールに以下のような表示が行われますので、item は、表示された時に評価されているとわかります。

shareData generated with big calculation.... with config:false

デフォルトでは、config は false なので、生成されている shareData の値に問題はありません。

この画面で、Toggle を操作すると、以下のように表示され、(ビューが更新されることによって)item も再評価されていることがわかります。

shareData generated with big calculation.... with config:true

ShareLink の item 評価を遅延させる方法

特に秘策があるわけではありません・・・

item が評価されてしまうのは、item として ShareLink に渡すために必要だからです。

そのため、(良くも悪くも) 表示のたびに item が評価されてしまいます。

表示するタイミングで評価をさせない方法の1つとしては、item の元データを Transferable にしてしまうことがあります。

ここの例では、ViewModel を Transferable にしてしまうということです。

そこにデータとして存在するものを item に渡すので、評価は発生しません。
実際に item に使用されるものは、Transferable に準拠させることで設定します。

extension ViewModel: Transferable {
    static var transferRepresentation: some TransferRepresentation {
        ProxyRepresentation(exporting: { viewModel in
            return viewModel.shareData()
        })
    }
}

上記では、ViewModel を Transferable に準拠させ、渡すデータとしては、先ほど使用していたものと同じ shareData() としています。

実際の共有操作(ファイル保存等)が選択された時に、shareData が呼び出されることになります。
コンソールに表示されるログを確認することで、ShareLink が表示されたタイミングで shareData が計算されることはないことがわかります。

まとめ

ShareLink の item を遅延評価させる

ShareLink の item を遅延評価させる
  • item は、ShareLink が表示される時に評価される
  • item が transferable に準拠していれば実際に共有操作された時に transferRepresentation に従って評価される
  • すでに、transferable に準拠している時は、transferRepresentation の定義順に気をつけないといけない

説明は以上です。
不明な点やおかしな点ありましたら、こちらまで。

SwiftUI おすすめ本

SwiftUI を理解するには、以下の本がおすすめです。

SwiftUI ViewMatery

SwiftUI で開発していくときに、ViewやLayoutのための適切なmodifierを探すのが大変です。
英語での説明になってしまいますが、以下の”SwiftUI Views Mastery Bundle”という本がビジュアル的に確認して探せるので、便利です。

英語ではありますが、1ページに コードと画面が並んでいるので、非常にわかりやすいです。

View に適用できる modifier もわかりやすく説明されているので、ビューの理解だけではなく、どのような装飾ができるかも簡単にわかります。

超便利です

SwiftUIViewsMastery

販売元のページは、こちらです。

SwiftUI 徹底入門

# SwiftUI は、毎年大きく改善されていますので、少し古くなってしまいましたが、いまでも 定番本です。

Swift学習におすすめの本

詳解Swift

Swift の学習には、詳解 Swift という書籍が、おすすめです。

著者は、Swift の初期から書籍を出していますし、Swift の前に主力言語だった Objective-C という言語についても同様の書籍を出しています。

最新版を購入するのがおすすめです。

現時点では、上記の Swift 5 に対応した第5版が最新版です。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です