🀄️

サードパーティSDKのPrivacy Manifests対応状況確認スクリプト

2024/03/04に公開

こんにちは、スペースマーケットでモバイルエンジニアをしている村田です。
iOS/Flutterエンジニアの皆さん、Privacy Manifests対応進んでいますか???
弊社はネイティブ製/Flutter製2つのiOSアプリを抱えており、毎週時間を確保して両アプリで利用しているサードパーティSDKの対応状況を手動でウォッチしています。アプリチーム少人数で動いている中でこの対応にリソースを割くのは勿体無いなと感じ、今更感ありますが時間削減のため確認用スクリプトを作成してみました。

Privacy Manifests とは

WWDC23「Get started with privacy manifests」で発表されたされた、プライバシーに関する新たな機能です。使用するにあたって理由を宣言する必要のあるAPI の一覧に記載のあるAPIをアプリで使用している場合、 PrivacyInfo.xcprivacy と呼ばれるplistファイルをプロジェクトへ追加し、プライバシーに関する情報(データの種類と目的)を記載する必要があります。
2024年春までに未対応のアプリは審査でリジェクトされてしまう可能性があるようで、アプリエンジニアは今1番温度感高く対応にあたっているのかなと思います。

サードパーティSDKの対応について

Appleが プライバシーマニフェストと署名の対象となるSDK を公開しており、リストへ記載のあるSDKは開発者がPrivacy Manifests対応する必要があります。
リストに記載のあるSDKをアプリで使用している場合、対応状況を確認し、対応バージョンへアップデートします。更新が止まっていたり開発者の反応がない場合、切り捨てて内製化するか他SDKへ乗り換える等の対応が必要になりそうです😷

冒頭に記載した通り、利用SDKのGitHubリポジトリを手動で周って確認するのは時間がかかるため、メインブランチに PrivacyInfo.xcprivacy が含まれているかチェックするスクリプトをPythonで作成しました(Issue/PR上がっているか、リリースバージョンは幾つかなども取りたかったのですが、今年春までの使い捨てスクリプトなのでそこまでは時間かけませんでした...)

スクリプト実装

GitHubのAPIを利用できるPyGithubというライブラリを使いたかったため、上述の通りPythonで実装しました(2年ぶりくらいに書いた気がする...)。APIを利用するにはアクセストークンが必要であり、GitHubの Settings -> Developer settings から生成可能です。

https://github.com/PyGithub/PyGithub?tab=readme-ov-file

流れ

  1. PyGithubを利用して、リポジトリの情報を取得
  2. リポジトリの全てのファイルを再起的に取得し、PrivacyInfo.xcprivacy or project.pbxproj を探索
  3. project.pbxprojが先にヒットした場合、テキストとしてPrivacyInfo.xcprivacyが含まれるかチェック
    • PrivacyInfo.xcprivacyの探索だけでも良さそうですが、ない場合全てのファイルを探索してしまうためproject.pbxprojのチェックをすることで時短
  4. 2,3を対象リポジトリ数分実行し、結果を表で出力
    • 表の出力にはtabulateを利用しました

コード

以下完成したコードになります

manifest.py
from github import Github
from tabulate import tabulate

ACCESS_TOKEN = "XXXXXXXXXXXXXXXXXXXXXX"
XCPRIVACY_FILE_NAME = "PrivacyInfo.xcprivacy"
PBXPROJ_FILE_NAME = "project.pbxproj"
REPOSITORY_NAME_LIST = [
    "abseil/abseil-cpp",
    "Alamofire/Alamofire",
    "AFNetworking/AFNetworking",
    "openid/AppAuth-iOS",
    "google/boringssl",
    "zhangao0086/DKImagePickerController",
    "zhangao0086/DKPhotoGallery",
    "facebook/facebook-ios-sdk",
    "google/promises",
    "firebase/firebase-ios-sdk",
    "ccgus/fmdb",
    "google/GoogleDataTransport",
    "google/GoogleSignIn-iOS",
    "google/GoogleUtilities",
    "grpc/grpc-ios",
    "google/GTMAppAuth",
    "google/gtm-session-fetcher",
    "facebook/hermes",
    "hackiftekhar/IQKeyboardManager",
    "onevcat/Kingfisher",
    "google/leveldb",
    "airbnb/lottie-ios",
    "jdg/MBProgressHUD",
    "nanopb/nanopb",
    "OneSignal/OneSignal-iOS-SDK",
    "openssl/openssl",
    "google/promises",
    "protocolbuffers/protobuf",
    "ashleymills/Reachability.swift",
    "realm/realm-swift",
    "ReactiveX/RxSwift",
    "SDWebImage/SDWebImage",
    "SnapKit/SnapKit",
    "tekartik/sqflite",
    "daltoniam/Starscream",
    "SVProgressHUD/SVProgressHUD",
    "kirualex/SwiftyGif",
    "SwiftyJSON/SwiftyJSON",
    "scalessec/Toast-Swift",
    "chaolunner/UnityFramework",
]

def main():
    result_data = []
    repository_name_list_count = len(REPOSITORY_NAME_LIST)
    for index, repository_name in enumerate(REPOSITORY_NAME_LIST):
        print("👩‍💻 [%d/%d] %s 確認中..." % (index + 1, repository_name_list_count, repository_name))
        is_exists = exists_xcprivacy_file(repository_name)
        result_emoji = "✅" if is_exists else "❌"
        result_data.append((repository_name, result_emoji))

    print(tabulate(result_data, headers=["Repository Name", "Result"], tablefmt="fancy_grid"))

def exists_xcprivacy_file(repository_name):
    github = Github(ACCESS_TOKEN)
    try:
        repo = github.get_repo(repository_name)
        contents = repo.get_contents("")            
        while contents:
            file_content = contents.pop(0)
            if file_content.type == "dir":
                contents.extend(repo.get_contents(file_content.path))
            else:
                if XCPRIVACY_FILE_NAME in file_content.path:
                    return True
                    break
                if PBXPROJ_FILE_NAME in file_content.path:
                    pbxproj_content = file_content.decoded_content.decode()
                    return XCPRIVACY_FILE_NAME in pbxproj_content
                    break
    except Exception as e:
        print(e)

    return False

main()

実行結果

以下出力結果になります(2024/02/26時点)
時間がかかるため実際に自分が使う際はアプリで利用しているSDKのみのリストに絞っています

$ python3 manifest.py

👩‍💻 [1/40] abseil/abseil-cpp 確認中...
👩‍💻 [2/40] Alamofire/Alamofire 確認中...
👩‍💻 [3/40] AFNetworking/AFNetworking 確認中...
👩‍💻 [4/40] openid/AppAuth-iOS 確認中...
👩‍💻 [5/40] google/boringssl 確認中...
👩‍💻 [6/40] zhangao0086/DKImagePickerController 確認中...
👩‍💻 [7/40] zhangao0086/DKPhotoGallery 確認中...
👩‍💻 [8/40] facebook/facebook-ios-sdk 確認中...
👩‍💻 [9/40] google/promises 確認中...
👩‍💻 [10/40] firebase/firebase-ios-sdk 確認中...
👩‍💻 [11/40] ccgus/fmdb 確認中...
👩‍💻 [12/40] google/GoogleDataTransport 確認中...
👩‍💻 [13/40] google/GoogleSignIn-iOS 確認中...
👩‍💻 [14/40] google/GoogleUtilities 確認中...
👩‍💻 [15/40] grpc/grpc-ios 確認中...
👩‍💻 [16/40] google/GTMAppAuth 確認中...
👩‍💻 [17/40] google/gtm-session-fetcher 確認中...
👩‍💻 [18/40] facebook/hermes 確認中...
👩‍💻 [19/40] hackiftekhar/IQKeyboardManager 確認中...
👩‍💻 [20/40] onevcat/Kingfisher 確認中...
👩‍💻 [21/40] google/leveldb 確認中...
👩‍💻 [22/40] airbnb/lottie-ios 確認中...
👩‍💻 [23/40] jdg/MBProgressHUD 確認中...
👩‍💻 [24/40] nanopb/nanopb 確認中...
👩‍💻 [25/40] OneSignal/OneSignal-iOS-SDK 確認中...
👩‍💻 [26/40] openssl/openssl 確認中...
👩‍💻 [27/40] google/promises 確認中...
👩‍💻 [28/40] protocolbuffers/protobuf 確認中...
👩‍💻 [29/40] ashleymills/Reachability.swift 確認中...
👩‍💻 [30/40] realm/realm-swift 確認中...
👩‍💻 [31/40] ReactiveX/RxSwift 確認中...
👩‍💻 [32/40] SDWebImage/SDWebImage 確認中...
👩‍💻 [33/40] SnapKit/SnapKit 確認中...
👩‍💻 [34/40] tekartik/sqflite 確認中...
👩‍💻 [35/40] daltoniam/Starscream 確認中...
👩‍💻 [36/40] SVProgressHUD/SVProgressHUD 確認中...
👩‍💻 [37/40] kirualex/SwiftyGif 確認中...
👩‍💻 [38/40] SwiftyJSON/SwiftyJSON 確認中...
👩‍💻 [39/40] scalessec/Toast-Swift 確認中...
👩‍💻 [40/40] chaolunner/UnityFramework 確認中...
╒═════════════════════════════════════╤══════════╕
│ Repository Name                     │ Result   │
╞═════════════════════════════════════╪══════════╡
│ abseil/abseil-cpp                   │ ✅        │
├─────────────────────────────────────┼──────────┤
│ Alamofire/Alamofire                 │ ✅        │
├─────────────────────────────────────┼──────────┤
│ AFNetworking/AFNetworking           │ ❌        │
├─────────────────────────────────────┼──────────┤
│ openid/AppAuth-iOS                  │ ❌        │
├─────────────────────────────────────┼──────────┤
│ google/boringssl                    │ ❌        │
├─────────────────────────────────────┼──────────┤
│ zhangao0086/DKImagePickerController │ ❌        │
├─────────────────────────────────────┼──────────┤
│ zhangao0086/DKPhotoGallery          │ ❌        │
├─────────────────────────────────────┼──────────┤
│ facebook/facebook-ios-sdk           │ ✅        │
├─────────────────────────────────────┼──────────┤
│ google/promises                     │ ❌        │
├─────────────────────────────────────┼──────────┤
│ firebase/firebase-ios-sdk           │ ✅        │
├─────────────────────────────────────┼──────────┤
│ ccgus/fmdb                          │ ✅        │
├─────────────────────────────────────┼──────────┤
│ google/GoogleDataTransport          │ ✅        │
├─────────────────────────────────────┼──────────┤
│ google/GoogleSignIn-iOS             │ ❌        │
├─────────────────────────────────────┼──────────┤
│ google/GoogleUtilities              │ ✅        │
├─────────────────────────────────────┼──────────┤
│ grpc/grpc-ios                       │ ✅        │
├─────────────────────────────────────┼──────────┤
│ google/GTMAppAuth                   │ ❌        │
├─────────────────────────────────────┼──────────┤
│ google/gtm-session-fetcher          │ ❌        │
├─────────────────────────────────────┼──────────┤
│ facebook/hermes                     │ ❌        │
├─────────────────────────────────────┼──────────┤
│ hackiftekhar/IQKeyboardManager      │ ❌        │
├─────────────────────────────────────┼──────────┤
│ onevcat/Kingfisher                  │ ✅        │
├─────────────────────────────────────┼──────────┤
│ google/leveldb                      │ ❌        │
├─────────────────────────────────────┼──────────┤
│ airbnb/lottie-ios                   │ ✅        │
├─────────────────────────────────────┼──────────┤
│ jdg/MBProgressHUD                   │ ❌        │
├─────────────────────────────────────┼──────────┤
│ nanopb/nanopb                       │ ✅        │
├─────────────────────────────────────┼──────────┤
│ OneSignal/OneSignal-iOS-SDK         │ ❌        │
├─────────────────────────────────────┼──────────┤
│ openssl/openssl                     │ ❌        │
├─────────────────────────────────────┼──────────┤
│ google/promises                     │ ❌        │
├─────────────────────────────────────┼──────────┤
│ protocolbuffers/protobuf            │ ✅        │
├─────────────────────────────────────┼──────────┤
│ ashleymills/Reachability.swift      │ ✅        │
├─────────────────────────────────────┼──────────┤
│ realm/realm-swift                   │ ✅        │
├─────────────────────────────────────┼──────────┤
│ ReactiveX/RxSwift                   │ ❌        │
├─────────────────────────────────────┼──────────┤
│ SDWebImage/SDWebImage               │ ✅        │
├─────────────────────────────────────┼──────────┤
│ SnapKit/SnapKit                     │ ✅        │
├─────────────────────────────────────┼──────────┤
│ tekartik/sqflite                    │ ✅        │
├─────────────────────────────────────┼──────────┤
│ daltoniam/Starscream                │ ❌        │
├─────────────────────────────────────┼──────────┤
│ SVProgressHUD/SVProgressHUD         │ ✅        │
├─────────────────────────────────────┼──────────┤
│ kirualex/SwiftyGif                  │ ✅        │
├─────────────────────────────────────┼──────────┤
│ SwiftyJSON/SwiftyJSON               │ ❌        │
├─────────────────────────────────────┼──────────┤
│ scalessec/Toast-Swift               │ ✅        │
├─────────────────────────────────────┼──────────┤
│ chaolunner/UnityFramework           │ ❌        │
╘═════════════════════════════════════╧══════════╛

最後に

スペースマーケットでは一緒に働く仲間を絶賛募集中です!
詳しくは以下をご確認の上ご応募ください。

https://spacemarket.co.jp/recruit/engineer/

https://www.wantedly.com/projects/1061116

https://www.wantedly.com/projects/1113570

https://www.wantedly.com/projects/1113544

GitHubで編集を提案
スペースマーケット Engineer Blog

Discussion