こんにちは!スマートバンクでアプリエンジニアをしているkanekoです。 先日リリースしたB/43のバージョン12.6.0からiOS版限定でアプリアイコンの変更機能が利用できるようになりました!
今回はアプリアイコン変更機能について、実装方法やリリースまでの流れについて紹介します。/#B43 のアプリアイコンが変更できるようになりました🐈
— 家計簿プリカ B/43(ビーヨンサン) (@B43jp) 2023年11月29日
\
iOSアプリでB/43アプリアイコンを自分好みに変えられるようになりました!アプリを最新バージョンにアップデートしてご利用ください😊
ミント・スカイ以外のアイコンは、B/43プラスに加入した本人のみ設定が可能になります💡 pic.twitter.com/3YpSoaEdLb
アイコン変更実装
iOSでのアイコン変更機能はiOS 10.3から利用可能な UIApplication.setAlternateIconName
を利用しています。以下で具体的な実装の手順を紹介します。
変更対象のアイコンをプロジェクトに追加
変更対象のアイコンをApp Iconとして Assets
に追加します。ImageSetではなくAppIconとして追加しないとアイコン設定時にエラーが発生するので注意が必要です。
Build Settingsに差し替え対象にするリソース名を追加
上で追加したAsset名をBuild Settingsの Asset Catalog Compiler
> Alternate App Icon Sets
で指定します。
アプリ上で選択されたアイコンに変更する処理を実装
Build Settingsに登録後は選択されたアイコンへ切り替える処理を実装します。
切り替えは UIApplication.setAlternateIconName
でBuild Settingsに追加したAsset名文字列(デフォルトアイコン時はnil)に指定することで可能になります。
特定のアイコン(ミントアイコン)を設定する際
UIApplication.shared.setAlternateIconName("AppIconMint")
デフォルトアイコンに戻す際
UIApplication.shared.setAlternateIconName(nil)
setAlternateIconName(_:completionHandler:) | Apple Developer Documentation
具体的な実装例
これだけでは味気ないので簡易なサンプルでアイコン変更画面のB/43での実装例をご紹介します。
以下のようなB/43のような簡易的なアイコン変更画面を実装しています。
まずは変更可能なアプリアイコンを表すenumを定義します。
ここではアイコン変更のために必要な iconName
を定義し上のステップでBuild Settingsに追加した文字列を返すようにしています。また、アイコン選択画面で変更後のアイコン画像のプレビュー・アイコン名というUI表示に必要なプロパティもここで定義しています。
enum AppIcon: String, CaseIterable, Identifiable { case `default` = "AppIcon" case mint = "AppIconMint" case sky = "AppIconSky" var id: String { rawValue } // setAlternateIconNameで指定する文字列 var iconName: String? { return switch self { case .default: // 通常のアイコンに戻す際はnilを指定する nil case .mint, .sky: // その他アイコンはBuild Settingsで追加した文字列を指定する rawValue } } // リストで表示する文字列 var description: String { return switch self { case .default: "デフォルト" case .mint: "ミント" case .sky: "スカイ" } } // リストで表示する画像リソース名 var imageName: String { return rawValue } }
次に定義したアプリアイコンを変更するView側の実装をしてみます。
設定済みのアイコン名は UIApplication.shared.alternateIconName
で取得可能です。これを利用して現在設定中のアイコン情報を保持する selectedIcon
を定義します。
struct IconSelectedView: View { @State var selectedIcon: AppIcon init() { if let alternateIconName = UIApplication.shared.alternateIconName, let appIcon = AppIcon(rawValue: alternateIconName) { selectedIcon = appIcon } else { selectedIcon = .default } } // ... }
次にアイコンの更新処理を実装します。
先に書いた通りUIApplication.shared.setAlternateIconName
を使ってアイコンの更新をしますが、このメソッドを呼び出すとアイコンを変更した旨のOSダイアログが表示されます。これは呼び出し元のアプリ側の実装では非表示に出来ないうえ、現在設定中のアイコンを再選択した際も表示されてしまいます。そのため、現在設定中のアイコンと選択したアイコンの比較後に必要に応じてアイコンの更新を行うようにしています。
また、 setAlternateIconName
で発生する例外は設定したアイコンのリソースが存在しない際など限定的なものしか確認出来なかったため選択状態のリセットのみを実装しています。
struct IconSelectedView: View { // ... func updateAppIcon(appIcon: AppIcon) { // 選択したアイコンが設定済みの際にOSダイアログを表示させないようにアイコンの比較 if selectedIcon == appIcon { return } let previousIcon = selectedIcon selectedIcon = appIcon Task { @MainActor in do { try await UIApplication.shared.setAlternateIconName(appIcon.iconName) } catch { self.selectedIcon = previousIcon } } } }
最終的なViewの実装は以下のようになります。
struct IconSelectedView: View { @State var selectedIcon: AppIcon init() { if let alternateIconName = UIApplication.shared.alternateIconName, let appIcon = AppIcon(rawValue: alternateIconName) { selectedIcon = appIcon } else { selectedIcon = .default } } var body: some View { ScrollView { VStack { ForEach(AppIcon.allCases) { appIcon in Button(action: { updateAppIcon(appIcon: appIcon) }, label: { HStack(content: { Image(uiImage: UIImage(named: appIcon.imageName)!) .resizable() .frame(width: 48, height: 48) Text(appIcon.description) Spacer() if selectedIcon == appIcon { Image(systemName: "checkmark") } }) }) } } } .padding() } func updateAppIcon(appIcon: AppIcon) { if selectedIcon == appIcon { return } let previousIcon = selectedIcon selectedIcon = appIcon Task { @MainActor in do { try await UIApplication.shared.setAlternateIconName(appIcon.iconName) } catch { self.selectedIcon = previousIcon } } } }
アイコン変更の実装裏話
きっかけ
今回のアイコン変更機能の実装ですが、アプリチームの自由研究の一環で企画がスタートしました。
自由研究とは(エンジニア自由研究活動の発表会レポートより)
自由研究部は、その名前の通りエンジニアがテーマを選んで、そのテーマにそった研究に取り組む部活動のことです。 スマートバンクのエンジニア陣は各プロジェクトチームにて、業務を行うことが多いので、必然的に業務時間のほとんどは自分が担当するプロジェクトに関する仕事がメインになります。 そういった開発を進める中で、B/43を改善するアイディアの実現や普段着手できていない技術的なTryをしてみたいという要望が度々あり、また他メンバーとオフラインでコラボレーションして開発する機会が減っていたことから、そういったものを研究テーマとして扱い、エンジニアがワイワイ集まりながら取り組む機会を作ろうと試験的に始まったのが自由研究活動です。
過去の自由研究での発表結果は以下で確認できます。
blog.smartbank.co.jp blog.smartbank.co.jp
アイコン種類について
当初の企画ではB/43で発行可能なカード種別に紐づいたアイコンに変更可能にする予定でした。 しかし社内にプロトタイプ版を配布したところデザインチームからアイコンを追加しても問題ないか?との打診が…!! そこからデザインチームにアイコンの案だしと社内アンケートを主導していただき今のアイコンの一覧が決定しました。
アイコン候補の中でもひときわ異彩を放つ犬アイコンは社内の公募で決定しました。
リリース後
実装開始当初は知る人ぞ知る機能になれば良いなと思って実装をしていましたが、多くのメンバーの協力のおかげでアイコンのクオリティも上がり多くの方に認知いただける機能になりました。 まだ未設定の方がいらっしゃったらこの機会に是非使ってみていただけると幸いです。
参考までに現時点でのアイコン設定数ランキングも掲載して置きます。
最後に
以上がiOSにおけるアイコン変更機能の実装方法とリリースまでの歩みでした。
実装内容的にそこまで難しい内容では無いと思いますが、社内の雰囲気含め参考になったら幸いです。
最後に、スマートバンクでは一緒に B/43 を作り上げていくメンバーを募集しています!カジュアル面談も受け付けていますので、お気軽にご応募ください !!