ねこ島

iOSのこととか、日常について雑に書きます

Kyash iOSのアプリサイズ削減取り組み

これは Kyash Advent Calendar 2021 17日目の記事です


KyashでiOSエンジニアをしている @nekowen です。

開発をしていて意外と見落としがちなのがアプリサイズの肥大化です。

Kyashでも一時期アプリのサイズが100MBを超える規模となっていましたが、リソースデータを見直すことで大幅にサイズを減らすことができました。

この記事では、アプリサイズをどう減らしたのか、同じような問題を起こさないために行った取り組みをご紹介します。

肥大化がもたらす影響

アプリサイズが肥大化するとストレージ領域を占有するだけでなく、一部のユーザーにも影響が出ます。

例えば、Wi-Fiではなくモバイル通信を利用している状態で200MBを超えるアプリをダウンロードしようとすると「モバイルデータ通信を使ってダウンロードしますか?」というダイアログが表示されます。

200MBを超えていなくとも、省データモードを有効にしている場合サイズに関係なくダウンロード時にダイアログが表示されます。

f:id:nekowen:20211214173302j:plain:w400

これらのダイアログはユーザーに数値として可視化されてしまう部分ですので、アプリサイズが大きければ大きいほどユーザーがダウンロードを避けてしまう可能性があります。

最近では通信会社が大容量の通信プランを用意しており数百MB程度でも気にされない場合もあるかもしれませんが、できる限り通信量は抑えるべきです。

Kyash iOSのダイエット 🏃

ダイエット前のアプリサイズはおおよそ127MBと、200MBのラインには到達こそはしていないがそこそこ大きいサイズとなっていました。

まずは、実際にアプリの中身を見てみることでどのファイルがどれだけ容量を消費しているかを把握しました。

Archiveを行い、App Store向けにバイナリをExportします。App ThinningやBitcodeの関係で実際にApp Storeで配信されるバイナリとは異なる点に注意してください。

Exportするとipa形式でファイルが出力されるので、これをzip形式にリネーム、展開します。

Payload以下のディレクトリにアプリを構成するリソースやFrameworkが含まれており、どの部分でサイズを消費しているかがざっとわかるようになります。

Kyashでは、Assets.car というファイルが70MBもあり、パッケージサイズの半分を占めている状態でした。

f:id:nekowen:20211214180207p:plain:w400

Assets.carはAsset Catalog内の画像リソースをパッケージ化したもので、ビルド時に生成されます。

中身はバイナリ形式ですが、assetutil を利用することで各リソースの情報がJSONで取得できます。

$ assetutil --info assets.car
[
  {
    "AssetType" : "Vector",
    "Colorspace" : "generic",
    "Height" : 256,
    "Idiom" : "universal",
    "Name" : "ic_image",
    "NameIdentifier" : 71910,
    "RenditionName" : "ic_image.pdf",
    "SHA1Digest" : "A94A8FE5CCB19BA61C4C0873D391E987982FBBD3",
    "SizeOnDisk" : 3947,
    "Width" : 256
  }
  ...
]

この中で注目すべきkeyは SizeOnDisk です。リソースのサイズを表しているので、この値が大きいものを優先的に削減していきます。

上記のデータと照らし合わせたところなんとファイルサイズが20MBを超える巨大な画像リソースが複数あることがわかりました。

それらの画像はベクターPDFとして扱っていましたが、中身にKyashカードのイメージ図といった巨大なラスタデータが含まれておりこれが大幅なサイズ増の原因となっていました。

対応策としてPNGへ置き換えることにより、アプリサイズを 127MB -> 70MBまで削減することができました。

PR自動チェックによる再発防止 ✅

今後同様の問題が起きないように、ベクターPDFのサイズが3MB以上であればWarningコメントをPR上に出力するようにしました。

Kyash iOSではDanger.swiftを導入しているため、比較的簡単に仕組みを作ることができたのも良かった点です。

github.com

func checkPDFFileSize() {
    let warnThresholdBytes = 3 * 1024 * 1024 // 3MB
    let allSourceFiles = danger.git.modifiedFiles + danger.git.createdFiles
    let allPDFFiles = allSourceFiles
        .filter { $0.hasPrefix("Kyash-iOS/Assets/") && $0.hasSuffix(".pdf") }
        .compactMap { URL(fileURLWithPath: $0) }

    allPDFFiles.forEach { file in
        guard let fileAttributes = try? FileManager.default.attributesOfItem(atPath: file.relativeString),
                let fileSize = fileAttributes[.size] as? UInt64
        else {
            return
        }

        if fileSize >= warnThresholdBytes {
            let message = """
            PDF画像のファイルサイズが3MBを超えています。JPEG/PNGを利用できないか検討してください。

            ファイルパス: \(file.relativeString)
            """

            warn(message)
        }
    }
}

最後に

アプリ内リソースの削減と対策についてご紹介しました。

今回はリソースサイズをチェックするような仕組みを取り入れましたが、今後は他の要因でサイズが増えたときのことを考慮してSwiftInfoを使ったipaファイルサイズの可視化などもやっていければと考えています。

github.com

Kyashではモバイルエンジニアを募集中です!
少しでも気になりましたら是非是非カジュアル面談に応募していただけると嬉しいです 👍

www.wantedly.com

meety.net