【macOS】非 App Store アプリの「公証」を確認する方法

Apple 公式の App Store でないサイトで公開してるアプリがありますよね。

少し怖いですよね。

Apple のサービスとして「公証」というしくみがあります。

公証
公証とは、Appleが提供するマルウェアスキャンサービスです。App Store外でmacOS用のアプリを配付したいデベロッパは、配付プロセスの一環として、アプリを提出してスキャンを受けます。Appleはそのアプリをスキャンし、既知のマルウェアが見つからなければ公証チケットを発行します。

👉 macOSでのマルウェアからの保護 - Apple サポート (日本) hatena-bookmark

こんなアプリがあります。

👉 Apparency | User Guide hatena-bookmark

インストールしているアプリの署名や公証を確認するアプリです。

アプリをインストールするのがなんかいやなので

これをコマンドラインでやる方法を調べてみました。


❯ spctl -a -t exec -vvv OneMenu.app

OneMenu.app: accepted
source=Notarized Developer ID
origin=Developer ID Application: Marko Pavlovic (7R2UKFFRJJ)

👉 Signing/notarizing command line tools not build using Xcode| Apple Developer Forums hatena-bookmark


❯ pkgutil --check-signature OneMenu.app

Package "OneMenu.app":
   Status: signed by a certificate trusted by macOS
   Certificate Chain:
    1. Developer ID Application: Marko Pavlovic (7R2UKFFRJJ)
       Expires: 2027-02-01 22:12:15 +0000
       SHA256 Fingerprint:
           59 25 F2 A4 68 42 FE 1C A8 8C 7F 71 D6 86 84 D3 8C 09 20 73 52 D1
           06 CA 12 84 9C 8E 40 FC 02 32
       ------------------------------------------------------------------------
    2. Developer ID Certification Authority
       Expires: 2027-02-01 22:12:15 +0000
       SHA256 Fingerprint:
           7A FC 9D 01 A6 2F 03 A2 DE 96 37 93 6D 4A FE 68 09 0D 2D E1 8D 03
           F2 9C 88 CF B0 B1 BA 63 58 7F
       ------------------------------------------------------------------------
    3. Apple Root CA
       Expires: 2035-02-09 21:40:36 +0000
       SHA256 Fingerprint:
           B0 B1 73 0E CB C7 FF 45 05 14 2C 49 F1 29 5E 6E DA 6B CA ED 7E 2C
           68 C5 BE 91 B5 A1 10 01 F0 24


❯ pkgutil --check-signature YOUR.pkg

Package "Silverlight.pkg":
   Status: signed by a certificate trusted by Mac OS X
   Certificate Chain:
    1. Developer ID Installer: Microsoft Corporation
   SHA1 fingerprint: AE D0 A7 C5 31 01 2B 70 D7 FB 49 5A 23 30 3A 67 05 36 5A 11
   -----------------------------------------------------------------------------
    2. Developer ID Certification Authority
   SHA1 fingerprint: 3B 16 6C 3B 7D C4 B7 51 C9 FE 2A FA B9 13 56 41 E3 88 E1 86
   -----------------------------------------------------------------------------
    3. Apple Root CA
   SHA1 fingerprint: 61 1E 5B 66 2C 59 3A 08 FF 58 D1 4A E2 24 52 D1 98 DF 6C 60

👉 MacOS App Notarized but cannot be opened · Issue #6025 · electron-userland/electron-builder hatena-bookmark
👉 pkgutilの便利なコマンドをまとめてみた #macOS - Qiita hatena-bookmark


❯ codesign --display -vvv OneMenu.app

Executable=/Users/nishi/Desktop/OneMenu.app/Contents/MacOS/OneMenu
Identifier=com.withmarko.OneMenu
Format=app bundle with Mach-O universal (x86_64 arm64)
CodeDirectory v=20500 size=7489 flags=0x10000(runtime) hashes=223+7 location=embedded
Hash type=sha256 size=32
CandidateCDHash sha256=d0c0ca9247b1ed0bb12237d8c7164d71157cded3
CandidateCDHashFull sha256=d0c0ca9247b1ed0bb12237d8c7164d71157cded3b738a4880f86219f60bd2e06
Hash choices=sha256
CMSDigest=d0c0ca9247b1ed0bb12237d8c7164d71157cded3b738a4880f86219f60bd2e06
CMSDigestType=2
CDHash=d0c0ca9247b1ed0bb12237d8c7164d71157cded3
Signature size=8932
Authority=Developer ID Application: Marko Pavlovic (7R2UKFFRJJ)
Authority=Developer ID Certification Authority
Authority=Apple Root CA
Timestamp=Jul 22, 2024 at 4:54:21
Notarization Ticket=stapled
Info.plist entries=24
TeamIdentifier=7R2UKFFRJJ
Runtime Version=14.5.0
Sealed Resources version=2 rules=13 files=8
Internal requirements count=1 size=216

👉 macOS Catalinaにおけるアプリの「公証(Notarization)」への対応方法 | OPTPiX Labs Blog hatena-bookmark

 

🧑🏻‍💻 まとめ

macOS 「公証」についてはこちら。

👉 ゼロから始めるmacOSアプリの署名と公証 (独自配信用) – hiroakit.com hatena-bookmark

テストにダウンロードアーカイブ使いましたが、

便利な良いアプリです「OneMenu」。

👉 OneMenu for Mac — Marko hatena-bookmark

開発者は YouTube もやっています。


👉 【Mac Menubar アプリ】System Monitoring + Keyboard Cleaning 🧹 + Window Manager = OneMenu hatena-bookmark

 

🧑🏻‍💻 参考



【SwiftUI】TextField サンプルコード

自在に書けそうで書けない TextField

カスタムスタイルを使うより、

そのまま View メソッドのチェインと入れ子で

記述したほうが自在でわかりやすように思います。




SwiftUI を使う限り常に使うコードですので、

気に入った記述があれば、

Gist と共にメモとして更新していきたいです。



【Swift】URLSession で Passing argument of non-sendable type '(any URLSessionTaskDelegate)?' outside of main actor-isolated context may introduce data races

今日現在、以下の設定でやっています。


 

🧑🏻‍💻 お天気情報を取得する

無料で公開されている WEB API を使います。

シンプルに取得する。


import SwiftUI

struct TestWeatherView: View {
  private let url = URL(string: "https://wttr.in/?format=3")!

  var body: some View {
    Text(try! String(contentsOf: url))
  }
}

#Preview {
  TestWeatherView()
}

ちょっと窮屈なので砕いていきます。


struct TestWeatherView: View {
  @State private var text = ""
  private let url = URL(string: "https://wttr.in/?format=3")!

  var body: some View {
    Text(text)
      .onAppear {
        text = try! String(contentsOf: url)
      }
  }
}

OK です。

次は、Web クライアントを汎用性のある URLSession に変えます。


struct TestWeatherView: View {
  @State private var text = ""
  private let url = URL(string: "https://wttr.in/?format=3")!

  var body: some View {
    Text(text)
      .task {
        let (data, _) = try! await URLSession.shared.data(from: url)
        text = String(data: data, encoding: .utf8)!
      }
  }
}

ここで、警告がでます。

Passing argument of non-sendable type '(any URLSessionTaskDelegate)?' outside of main actor-isolated context may introduce data races

これは、なんですか。

 

🤔 Passing argument of non-sendable type '(any URLSessionTaskDelegate)?' outside of main actor-isolated context may introduce data races

URLSession のドキュメントを見ておきます。


func data(from url: URL) async throws -> (Data, URLResponse)

👉 data(from:) | Apple Developer Documentation hatena-bookmark

Task 内の以下の部分


let (data, _) = try! await URLSession.shared.data(from: url)

左辺はメインスレッド、

--- 境界 ---

右辺はバックグラウンドスレッド

👉 【Swift】concurrency をマスターするための一つのきっかけ hatena-bookmark

ということで、

いわゆる「アクター境界」を越えているので、

data() の返り値は Sendable でなければなりません。

リファレンスやコードを追いかけてみると、

Data, URLResponse 共に Sendable に準拠しています。

👉 Data | Apple Developer Documentation hatena-bookmark
👉 URLResponse | Apple Developer Documentation hatena-bookmark

あ、Tuple かな ?

などと思いましたがなんか違う。

👉 Pitch: User-defined tuple conformances - Evolution / Pitches - Swift Forums hatena-bookmark

 

🧑🏻‍💻 Actor を使う

ActorSendable に準拠しています。


なので、これでバックグラウンドの通信部分をラップします。


actor WeatherA {

  // OK
  static func getData(url: URL) async -> (Data, URLResponse) {
    try! await URLSession.shared.data(from: url)
  }

  // OK
  nonisolated func getData(url: URL) async -> (Data, URLResponse) {
    try! await URLSession.shared.data(from: url)
  }
}

これで、無警告でOKとなりました。

 

🧑🏻‍💻 extension 化してメインスレッドを避ける

こんな方法でもいけます。


extension URLSession {
  func dataEx(url: URL) async -> (Data, URLResponse) {
    try! await data(from: url)
  }
}

 

🧑🏻‍💻 まとめ

分かれば、なるほど感あるけど、分からなければ全く謎で時間だけ食うので、

そんな誰かと自分用のメモとして。

class でも警告なしでいけるようです。



Actor はデータの非同期操作のために作られてる」感じがする。

外野から使ってみた雰囲気だけだけれども。

 

🧑🏻‍💻 参考