[SwiftUI] swift-argument-parser を使って、SwiftUI 起動時動作を制御する

SwiftUI2021

     
⌛️ 3 min.
SwiftUI アプリに、起動時の引数情報を渡す方法を説明します。

環境&対象

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

  • macOS Sonoma Beta
  • Xcode 15 Beta
  • iOS 17 Beta

ユースケース

アプリの起動目的によって、振る舞いを調整したい時があります。

例えば、AppStore 向けスクリーンショットを撮影する時です。

アプリを AppStore に登録する時には、いくつかスクリーンショットも登録が必要です。アプリによっては、デモ用データが表示された状態のスクリーンショットを撮りたくなりますが、毎回 操作してデモ用データが表示される状態にするのは大変です。

その様な時に、起動時の引数指定で使用するデータを切り替えられる様にしておくと便利です。

ということで、起動時に指定された引数情報を SwiftUI app で使用するための方法を説明します。

以前は、AppDelegate で処理していましたが、SwiftUI では AppDelegate を使用するには、手順が必要です。AppDelegate なしに、使ってみます。

想定ケース

スクリーンショット向けに特定のデータを用意するケースを考えることにします。

引数としては、以下を想定します。

“-snapshot” / 使用例 -snapshot
スナップショット向けに起動されたことを表す引数
“–imageName imageName”/ 使用例 –imageName question
指定された名称のイメージを使用します。

swift-argument-parser

引数に渡された情報を処理するための便利なライブラリが Apple から公開されています。

こちら

swift-argument-parser というライブラリで、名前そのままですが、引数をパースするためのライブラリです。

このライブラリを使用して、パースしますので、SwiftUI App のプロジェクトに、追加します。

ParseableArguments

swift-argument-parser では、パース情報を保持する型が必要となります。

この 型 は、ParsableArguments に conform する必要があります。

以下の様な struct を定義しました。

struct MyArguments: ParsableArguments {
    @Flag(help: "for snapshot setting")
    var snapshotMode: Bool = false
    @Option(help: "run with specific image")
    var imageName: String?
}

普通の struct っぽいですが、@Flag/@Option は、 ParsableArguments に特別な Property Wrapper です。

@Flag を付与すると、指定されたプロパティは、引数で ON/OFF されるプロパティになります。(指定されなかった時用に デフォルト値設定が必須です)

@Option が付与されたプロパティは、引数で指定されるかもしれないが、指定されないことも許容される指定となります。

このほかに、
@Argument / @OptionGroup もあります。

@Argumentを付与すると、明示的にどの引数指定かを指定するのではなく、引数そのものの位置からどの引数に該当するかを解釈するようになります。
簡単にいうと、”command –option arg1″ ではなく “command arg1” のような使い方ができるということです。

@OptionGroup を付与すると、ParsableArguments を階層的に使用できる様になります。

詳細は、swift-argument-parser のドキュメントを参照してください。

SwiftUI への組み込み

プロジェクトによって、引数により与えられた情報を反映したい場所は変わると思いますが、ここでは、ContentView で ViewModel を初期化している前提とします。

そして、引数に与えられた情報に応じて、ViewModel を初期化するということにします。

ViewModel はデフォルトでは、”gear” というシンボル名を持っていますがこの値を 起動時の引数で変更できる様にしてみます。

//
//  ContentView.swift
//
//  Created by : Tomoaki Yagishita on 2023/07/04
//  © 2023  SmallDeskSoftware
//

import SwiftUI
import ArgumentParser

class ViewModel: ObservableObject {
    @Published var imageName: String = "gear"
    init(_ imageName: String = "gear") {
        self.imageName = imageName
    }
}

struct ContentView: View {
    @StateObject var viewModel: ViewModel = {
        do {
            let args = try MyArguments.parse()
            //print(args)
            if let imageName = args.imageName {
                return ViewModel(imageName)
            }
            return ViewModel()
        }
        catch {
            print("Error: Could not parse arguments")
            // help 表示
            print(MyArguments.helpMessage())
        }
        return ViewModel()
    }()

    var body: some View {
        VStack {
            Image(systemName: viewModel.imageName)
                .resizable().scaledToFit()
                .frame(width: 100, height: 100)
                .foregroundStyle(.tint)
        }
        .padding()
    }
}

ViewModel 初期化時に、MyArguments.parse を使用して指定された引数情報を確認しています。うまく取得できた後は、引数に指定された情報をもとに、ViewModel を初期化します。

scheme でのlaunch argment 指定

通常、iOS/macOS アプリをコマンドラインから起動することはないと思いますが、Xcode では、scheme で プロジェクトの launch argument を指定できる様になっています。

「Edit Scheme…」で Scheme 編集シートに移動し、”Arguments passed On Launch” に指定したい引数を追加します。

EditScheme
EditSchemeAPOL
MEMO

swift-argument-parser は、デフォルトでは プロパティ名をベースにしたオプション名となります。
MyArguments で作成したプロパティからは、
“–snapshot-mode” と “–image-name” という名称がそれぞれ デフォルトで設定されます。
オプション名変更は記事の後半で行います。

–snapshot-mode と –image-name を設定してみます。
“Arguments Passed On Launch” の “+” ボタンを押して、オプションを追加して、オプション内容を修正します。

デフォルトでは、引数にはハイフンが2つ付いているので注意してください。

settingLaunchOption

起動確認

Xcode の Scheme 設定では、launch option は、チェックボックスで ON/OFF できる様になっています。
まずは、チェックを外して (option を OFF にして) 起動してみます。

LaunchWithOff
LaunchResultWithOff

無指定時の “gear” が使用されているのがわかります。

次に、チェックを入れて (option を ON にして) 起動してみます。

LaunchWIthOn
LaunchResultWithOn

引数で指定した名称のイメージ (pencil) が使用されているのがわかります。

引数名調整

機能的には完成ですが、引数名を調整してみます。

–snapshot-mode となっていますが、-snapshot にしてみます。

プロパティ名を調整しなくとも、
@Flag 等の property wrapper の初期化時に指定することができます。

以下は、–snapshot-mode を -snapshot に変更し、–image-name を –imageName にしています。

struct MyArguments: ParsableArguments {
    @Flag(name: .customLong("snapshot", withSingleDash: true), help: "for snapshot setting")
    var snapshotMode: Bool = false
    @Option(name: .customLong("imageName"), help: "run with specific image")
    var imageName: String?
}

なお、上記変更だけで、Scheme の launch option 設定を更新せずに実行しないと 以下の様なエラーメッセージが表示されます。(ContentView に記述してあるからですが・・・)

いわゆる unix のコマンドのヘルプ相当が表示されます。

Error: Could not parse arguments
USAGE: my_arguments [-snapshot] [--imageName ]

OPTIONS:
  -snapshot               for snapshot setting
  --imageName  run with specific image
  -h, --help              Show help information.

ということで、ヘルプからも 引数名が変更されていることがわかります。

MEMO

SwiftUI app の 起動引数は、ほとんど開発者しか使用しないと思いますので、その名称にこだわることはあまりないかもしれません・・・

まとめ

swift-argument-parser を使った、引数処理を SwiftUI に組み込む方法を確認しました。

swift-argument-parser を使った、引数処理を SwiftUI に組み込む方法
  • swift-argument-parser を組み込む
  • ParsableArguments に conform した 引数対象の情報を含む 型を作成する
  • @Flag/@Option 等の property wrapper を使い分ける
  • 適切なタイミングで parse する

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

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版が最新版です。

コメントを残す

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