開発note@パッション

開発ノート:NFC-③DriverKit 実装 前編【アプリプログラム実装編】

開発note@パッション

DriverKit で開発したドライバは、アプリのバンドル内に同梱し、アプリからインストールする必要があります。
このため、ドライバ⾃体の他に macOS アプリが必要となります。

今回はプロジェクトの作成と、ドライバインストールのためのアプリプログラム実装について解説していきます。

・NFCを活用した新しいサービスや製品を企画・開発したい方
・スマートフォンやICカードとの連携機能を実装したいエンジニア
・IoT機器や非接触決済の技術に関心のある開発者

▼関連記事はこちら▼
・開発ノート NFC-① イントロダクション
・開発ノート NFC-② 機材・開発環境準備

 

開発環境

  • OS: macOS Sonoma 14.5
  • Xcode: Xcode 15.4 (15F31d)

 

Xcode プロジェクト作成

Xcode を開き、新しいプロジェクトを作成します。

まずはインストール⽤の macOS アプリをターゲットとしてプロジェクトを作成します。

1.テンプレート選択

①macOS – ②Application – App を選択し、 ③Next をクリックします。

2.プロジェクトの基本設定

各種設定し、 Next をクリックします。

  • Product Name には任意の名前を付けます
  • Team, Organization Identifier はご⾃⾝の環境に応じて適切に設定します
  • その他は要件などに応じて任意に設定します

3.保存場所を選択
プロジェクトの保存場所を選択して Create をクリックします。

以上でプロジェクト作成は完了です。
*プロジェクト作成完了後の画面:

次にドライバをターゲットに追加します。

ドライバの追加

1.新しいターゲットの追加

メニューバーから ①File – ②New – ③Target… を選択します。

2.ターゲットのテンプレート選択

①DriverKit – ②Driver – Driver を選択し、 ③Next をクリックします。

3.ターゲットの基本設定

各種設定し、 Finish をクリックします。

  • Product Name には任意の名前を付けます
  • Team はご⾃⾝の環境に応じて適切に設定します
  • Project は最初に作成したプロジェクトを設定します
  • Embed in Application はドライバをを同梱する macOS アプリを設定します

DriverKit.framework を確認できれば、ターゲット追加は完了です。
*ターゲット追加完了後の画面:

 

アプリプログラム作成

アプリプログラムは DriverKit の導⼊として、ドライバをインストールするのみの簡単なプログラムを実装します。
なお、アプリプログラムの⾔語は Swift としています。

 ViewModel 作成

UI とビジネスロジックを分離するため、ファイルを新規作成して ViewModel クラスを⽤意します。
ここではベースを⽤意する⽬的で、ドライバの Bundle Identifier をプロパティとして定義するのみとします。


import Foundation

class ContentViewModel: NSObject {
    
    // MARK: - Properties

    /// ドライバの Bundle Identifier
    private let dextIdentifier: String = "jp.passion-s.NFCDriverTestApp.NFCDriver"
    
    // MARK: -
}

 ドライバインストール処理の実装

ドライバインストールは、システム拡張のアクティベーション機構を利⽤します。
詳細情報・最新情報は Apple 公式ドキュメント を参照ください。

  1. OSSystemExtensionRequestactivationRequest メソッドを呼び出し、アクティベーション要求を作成します
  2. 作成したアクティベーション要求を OSSystemExtensionManager に送信します
    これにより、システムがアクティベーションプロセスが開始されます
  3. アクティベーションプロセス結果が、指定した OSSystemExtensionRequestDelegate オブジェクトに報告されます

上記の処理コードは以下のようになります。


import SystemExtensions
import os.log

extension ContentViewModel: OSSystemExtensionRequestDelegate {

    // MARK: - SystemExtension Request

    /// ドライバのインストール
    func installDriver() {
        let request = OSSystemExtensionRequest.activationRequest(forExtensionWithIdentifier: dextIdentifier, queue: .main)
        request.delegate = self
        OSSystemExtensionManager.shared.submitRequest(request)
    }

    // MARK: - OSSystemExtensionRequestDelegate
    
    /// アクティベート・ディアクティベート成功時のデリゲート処理
    func request(_ request: OSSystemExtensionRequest, didFinishWithResult result: OSSystemExtensionRequest.Result) {
        os_log("OSSystemExtensionRequest didFinishWithResult: %{public}d", result.rawValue)
        // OSSystemExtensionRequest.Result.willCompleteAfterReboot の考慮も必要
    }

    /// アクティベート・ディアクティベート失敗時のデリゲート処理
    func request(_ request: OSSystemExtensionRequest, didFailWithError error: Error) {
        os_log("OSSystemExtensionRequest didFailWithError: %{public}@", error.localizedDescription)
    }
    
    ///  ユーザーによる承認が必要な場合のデリゲート処理
    func requestNeedsUserApproval(_ request: OSSystemExtensionRequest) {
        os_log("OSSystemExtensionRequest requestNeedsUserApproval")
    }

    /// 異なるバージョンがインストールされている場合のデリゲート処理
    func request(_ request: OSSystemExtensionRequest,
                 actionForReplacingExtension existing: OSSystemExtensionProperties,
                 withExtension ext: OSSystemExtensionProperties) -> OSSystemExtensionRequest.ReplacementAction {
        // 今回は常に更新するものとする
        os_log("OSSystemExtensionRequest actionForReplacingExtension: %{public}@ %{public}@", existing, ext)
        return OSSystemExtensionRequest.ReplacementAction.replace
    }

    // MARK: -
}

ここで、 OSSystemExtensionRequestDelegate のデリゲート処理は、以下の4つのメソッドの実装が必須となります。

・request(OSSystemExtensionRequest, didFinishWithResult:OSSystemExtensionRequest.Result)

  • アクティベート・ディアクティベート要求が正常終了した際に呼ばれます
  • メソッドが呼ばれた時点で有効化されている場合と、再起動後に有効化される場合があり、引数didFinishWithResult で判断できます
  • 今回は特別な処理を実装せず、ログを出⼒するのみとしています

・request(OSSystemExtensionRequest, didFailWithError: any Error)

  • アクティベート・ディアクティベート要求が失敗した際に呼ばれます
  • 引数 didFailWithError に失敗理由が設定されます
  • 今回は特別な処理を実装せず、ログを出⼒するのみとしています

・requestNeedsUserApproval(OSSystemExtensionRequest)

  • アクティベート前にユーザーの承認が必要である際に呼ばれます
  • 今回は特別な処理を実装せず、ログを出⼒するのみとしています

・request(OSSystemExtensionRequest, actionForReplacingExtension: OSSystemExtensionProperties, withExtension: OSSystemExtensionProperties)

  • システムに異なるバージョンがインストールされている場合に呼ばれます
  • 今回は常に更新するものとしています

なお、今回は割愛いたしますが、ドライバをアンインストールする場合は OSSystemExtensionRequest のdeactivationRequest メソッドを利⽤します。

 UI作成

ドライバインストールボタンを配置しただけのシンプルな UI とします。


struct ContentView: View {
    var viewModel: ContentViewModel = .init()

    var body: some View {
        VStack {
            Text("NFC Driver テストアプリ")
                .font(.title)
            Divider()
            HStack {
               Button(
                  action: {
                        self.viewModel.installDriver()
                  }, label: {
                        Text("インストール")
                  }
               )
            }
        }
        .padding()
    }
}

 

エンタイトルメント設定

DriverKit のドライバをインストールするために、アプリの .entitlements ファイルでcom.apple.developer.system-extension.install有効にする必要があります。

 1.アプリの .entitlements ファイルを開く
 2.Entitlements File 横の + アイコンをクリックし、 System Extension を選択する

 3.Value に YES を設定する
 *エンタイトルメント追加後:

おわりに

今回はDriverKit実装-前編ということで、プロジェクトの作成やアプリプログラム実装についてご紹介いたしました。
次回はドライバプログラム実装について解説していきたいと思います。

 


 

TOP