5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

FlutterでiOSの独自ライセンスを自動取得/表示まで

Last updated at Posted at 2024-05-18

はじめに

とあるPJでFlutterを使用したアプリ開発中、セキュリティ診断等でiOSが独自で使用しているライブラリのライセンスの記載が不足しているとの診断結果を受け追加することに。
ですがライブラリが追加されるたびにライセンスファイルを都度追加するのは、メンテナンスコストがかかるため自動化しようということになりました。

ということで本記事では、iOSの独自ライセンスの取得方法とFlutter標準ライセンスに追加する方法までをご紹介します!

目次

1. 使用ライブラリ / ツール紹介

2. 手順
  1. ライセンスファイルの取得
  2. ライセンスファイルからライセンス本文を参照
  3. Flutter標準ライセンス一覧追加

3. まとめ

使用ライブラリ / ツール紹介

① Flutterの標準ライブラリのライセンスを表示する方法

② iOSの独自ライセンスファイルを取得するツール
こちらはCocoaPodsを使ってコマンドを実行することによって指定のフォルダにライセンスファイルを書き出すことができます

③ ①のFlutter標準ライセンスに②を追加する方法

手順

1. ライセンスファイルの取得

②のツールを使用してライセンスファイルを取得するには、
まずCocoapodsでLicensePlistをインストール

pod 'LicensePlist'

その後コマンドで指定のパスにライセンスファイルを取得します。

license-plist --output-path="ライセンスファイル格納先のパス"

これらの一連の処理をiOSをビルドする際に自動化するためPodfileに以下を宣言をしましょう!

target 'Runner' do
    ... // Lintなどの色んなやつ
    pod 'LicensePlist'

post_install do |installer|
    ... 
    system('license-plist --output-path="ライセンスファイルのパス"')

systemとして記載するとflutter ios buildの際にコマンドとして実行してくれます。
自分のPJの場合、外部参照ファイルは基本的にassetsフォルダで管理をしているためライセンス格納先パスは上記の位置に設定しました。

2. ライセンスファイルからライセンス本文を参照

手順1で格納先のフォルダには複数のライセンスファイルが出力されます。
そこからフォルダ内のすべてのファイルにアクセスしてライセンスファイルのパスを取得する必要があります。
自分の場合は以下のように書きました。

Future<List<String>> _getIosLicenseFileAssetPath() async {
    final List<String> filePaths = [];
    try {
        final manifestContent = await rootBundle.loadString('AssetManifest.json');
        final Map<String, dynamic> manifestMap = json.decode(manifestContent);
        // manifestMapからファイルパスを抽出してfilePaths.addなどでList追加
    } catch {
        rethrow;
    }
    return filePaths;
}

AssetManifest.jsonはassetsのファイル名とパスをkey / valueでまとめたjsonファイルです。
そこからパス名'licenses/ios/com.mono0926.LicensePlist'以下のファイル名を抽出して返却する関数になっております!

上記関数で抽出したライセンスファイルを参照するとこのような形式になっています。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>PrefernceSpecifiers</key>
    <array>
        <dict>
            <key>FooterText</key>
            <string> 
                Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/

TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

1. Definitions.

"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
            </string>
        </dict>
    </array>
  </dict>
</plist>

ここから本文を参照するためにはXMLドキュメントとして読み込む必要があるため以下のように書きました。

final fileData = await rootBundle.loadString('ライセンスファイルのパス');
final licenseElement = XmlDocument.parse(fileData).findAllElements('string').first;

// ライセンス本文は`licenseElement.text`で参照

ライセンスファイルのstring属性の一番最初をライセンス本文として扱っています。
もう少しいい方法がある気がしますが、自分のPJで追加した40ファイルほどはすべてこの形式でできたので問題ないかと思います。。。

3. Flutter標準ライセンス一覧追加

ここまででライセンスファイルの作成 / 取得 / 本文抽出まで進めてきたので、最後にこれをFlutter標準のライセンスに追加しましょう!

ライブラリ③を使用したライセンス追加方法は以下です。

final fileData = await rootBundle.loadString('ライセンスファイルのパス');
final licenseElement = XmlDocument.parse(fileData).findAllElements('string').first;

LicenseResistory.addLicense(
    () => Stream<LicenseEntry>.value(
        LicenseEntryWithLineBreaks(<String>['ファイル名'], licenseElement.text),
    )
);

LicenseEntryWithLineBreaksの第一引数にライセンス名を、第二引数にライセンス本文を指定することでFlutter標準のライセンスリストに追加することができます。

ということで手順2, 3を一連のコードで書くと次のようになります。

Future<List<String>> addIosLicense() async {
    try {
        final assetPathList = _getIosLicenseFileAssetPath();
        for (final assetPath in assetPathList) {
            // パスからライセンス名を生成
            final fileName = assetPath.split('/').last.split('.').first;
        
            // pathのライセンスファイルを取得
            final fileData = await rootBundle.loadString(assetPath);
            final licenseElement = XmlDocument.parse(fileData).findAllElements('string').first;
            
            // Flutterの標準ライセンスに追加
            LicenseResistory.addLicense(
                () => Stream<LicenseEntry>.value(
                    LicenseEntryWithLineBreaks(<String>[fileName], licenseElement.text),
                )
            );
        }
    } catch {
        // Logとか出す
    }
}

Future<List<String>> _getIosLicenseFileAssetPath() async {
    final List<String> filePaths = [];
    try {
        final manifestContent = await rootBundle.loadString('AssetManifest.json');
        final Map<String, dynamic> manifestMap = json.decode(manifestContent);
        // manifestMapからファイルパスを抽出してfilePaths.addなどでList追加
    } catch {
        rethrow;
    }
    return filePaths;
}

あとは良きタイミングでshowLicensePageを使用して表示するだけ!

まとめ

ライセンスや規約周りはアプリ審査やセキュリティ診断周りで大きく影響してきます。
OSに依存するものもありますので見直してみてください!

追記

一部PJで使用しているライセンスファイル格納先のパスやコードなどは伏せて記載をしています

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?