20220113のiOSに関する記事は4件です。

SwiftUI内でUIViewControllerを使おう

概要 SwiftUIがだんだんと使われてきている印象を受けますが、まだまだ世間ではUIViewControllerが使われているのも事実です。 ただ単にUIKitベースの画面を表示したいのであれば UIViewControllerRepresentable などを使えばいいのですが、 特にサードパーティがリリースしている広告SDKなどはUIViewControllerが必要な実装が行われているので、SwiftUI内で広告を出そうとする場合、工夫が必要になります。 SwiftUI内でUIViewControllerを取得 なるべくSwiftUIっぽくViewControllerを使おうと考え、以下のような形になりました。 ContainerViewがViewControllerを返してくれて、それを使って広告など、ViewControllerが必要なビューを表示していきます。 struct SwiftUIView: View { var body: some View { ContainerView { viewController in // UIViewControllerが必要な広告を、UIViewRepresentableで実装 AdView(viewController: viewController) } } } もちろんこれはプレビューでも動作し、広告が表示されます。 ContainerViewの実装 ViewControllerを返すContainerViewの実装です。 内部はUIViewControllerRepresentableを実装しています。 struct ContainerView<Content>: UIViewControllerRepresentable where Content : View { let content: (UIViewController) -> Content func makeCoordinator() -> Coordinator { Coordinator(self) } func makeUIViewController(context: Context) -> UIViewController { let vc = UIHostingController(rootView: AnyView(EmptyView())) vc.rootView = AnyView(content(vc)) return vc } func updateUIViewController(_ viewController: UIViewController, context: Context) { viewController.rootView = AnyView(content(viewController)) } class Coordinator: NSObject { var parent: ContainerView init(_ viewController: ContainerView) { parent = viewController } } } キモとなるのは makeUIViewController(context:) の部分で、一度ベースとなるUIHostingControllerをEmptyViewで作成しておいてViewControllerインスタンスを作った後、改めてパラメータのcontentをあてがう実装になってます。 これでContainerViewの内部はcontentのUIになり、SwiftUI側にはViewControllerが返されます。 ライブラリに登録 UIViewControllerをしょっちゅう使う場合は、このContainerViewをライブラリに登録しておくと便利です。 import DeveloperToolsSupport import SwiftUI struct UIKitViewContent: LibraryContentProvider { var views: [LibraryItem] { LibraryItem(ContainerView(content: { _ in EmptyView() }) ,title: "ViewController container" ,category: .layout) } } 右上あたりの+ボタンから追加できるようになります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Firebase Remoete Configで強制アップデート

FirebaseのRemote Configを使用して強制アップデートを実装したので記事にします。 強制アップデートとは アプリの新しいバージョンをリリースした時に、ユーザに必ずアップデートをして欲しい場合があります。 例えば、以下のような場合です。 既存の機能に大幅な変更が加わった場合 致命的なバグを修正した場合 この時、ユーザがアプリを立ち上げた際、ユーザにアップデートを促しアップデートを完了させるまでアプリの使用を事実上不可能にする処理が強制アップデートです。 アプリの使用を不可能にする処理としては、以下のようなアラートを出し、アプリストアへの遷移以外できなくすることなどが挙げられます。(ストアからアップデートせずに戻ってきても再度表示される) また、強制アップデートは最初に公開した時に入れとかないと強制アップデートさせたくてもさせることができない場面が出てくるので、できるだけ最初のバージョンで入れておきたい機能です。 Firebase Remote Configとは Firebase Remote Configはアプリの動作や外見を変更するパラメータをFirebaseに持たせることができるクラウドサービスです。 アプリが通信処理を行いこのパラメータにアクセスすることにより、アプリ自身を変更することなく動作や外見を変更することができます。 今回の強制アップデートの場合、アプリの最新のバージョン情報などをFirebase Remote Configに設定しておきます。 実装 ここからは実際にどのような手順で実装するのか見ていきます。 Firebase側 まずFirebaseのコンソールから左下、「エンゲージメント」の中にある「Remote Config」を選びます。 初めての場合はRemote Configのトップページが表示されます。「構成を作成」を押すと最初のパラメータを入力することができるので、バージョン情報を入力して保存しましょう。 また、それぞれ項目は以下の通りです。 パラメータ名 アプリ側でパラメータにアクセスするためのキー データ型 パラメータのタイプ Description パラメータの説明 Default value 設定するパラメータ 保存すると以下のような画面になります。 この状態ではまだ変更が適用されていません。右上の「変更を公開」ボタンを押すと変更が適用されて、設定したパラメータをアプリで使用できるようになります。 また、新たにパラメータを追加することも可能です。 私の場合は、current_versionの他にrequire_force_update(強制アップデートさせるかどうか)とapp_store_url(app storeのURL)も追加しています。 最後に「変更を公開」ボタンを押してFirebase側の設定は完了です。 アプリ側 次はアプリ側の実装についてです。 まず全体のコードを載せてしまいます。 RemoteConfigProvider.swift import FirebaseRemoteConfig /// Firebaseのコンソールで設定したパラメータ名に対応 enum ConfigKey: String { case currentVersion = "current_version" case forceUpdateRequired = "require_force_update" case storeUrl = "app_store_url" static func makeDefaults() -> [String: Any] { [ currentVersion.rawValue: "", forceUpdateRequired.rawValue: false, storeUrl.rawValue: "" ] } } final class FirebaseRemoteConfigProvider { private let remoteConfig = RemoteConfig.remoteConfig() /// パラメータを取ってくる func fetchConfig(completion: (() -> Void)? = nil) { // 取得するパラメータのデフォルトを設定 remoteConfig.setDefaults(ConfigKey.makeDefaults() as? [String: NSObject]) // FetchとActivateを一括で行う remoteConfig.fetchAndActivate(completionHandler: { status, error in switch status { case .successUsingPreFetchedData, .successFetchedFromRemote: completion?() case .error: if let error = error { print(error.localizedDescription) } @unknown default: fatalError() } }) } /// RemoteConfigから取ってきたパラメータを取得 func getConfig(key: ConfigKey) -> RemoteConfigValue { remoteConfig.configValue(forKey: key.rawValue) } } ForceUpdateManager.swift import Combine import FirebaseRemoteConfig final class UpdateCheckManager { static let shared = UpdateCheckManager() private let remoteConfigProvider = FirebaseRemoteConfigProvider() private var cancellable: AnyCancellable? private init() {} func setup() { observeApplicationDidBecomeActive() } /// アプリがActiveになった際にアップデートチェック private func observeApplicationDidBecomeActive() { cancellable = NotificationCenter.Publisher(center: .default, name: UIApplication.didBecomeActiveNotification, object: nil) .sink(receiveValue: { [weak self] _ in FirebaseRemoteConfigProvider().fetchConfig(completion: { self?.forceUpdateIfNeeded() }) }) } /// requireForceUpdateがtrueかつ現在のバージョンが最新のバージョンと異なる場合に強制アップデート private func forceUpdateIfNeeded() { let localVersionString = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as! String let requireForceUpdate = remoteConfigProvider.getConfig(key: .forceUpdateRequired).boolValue if requireForceUpdate, let currentVersionString = remoteConfigProvider.getConfig(key: .currentVersion).stringValue, localVersionString != currentVersionString { guard let storeUrlString = remoteConfigProvider.getConfig(key: .storeUrl).stringValue, let storeUrl = URL(string: storeUrlString) else { return } // 強制アップデートのアラートを出す } } } AppDelegate.swift import UIKit @main class AppDelegate: UIResponder, UIApplicationDelegate { func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. UpdateCheckManager.shared.setup() return true } ... } 各クラスの説明は以下の通りです。 クラス 機能 UpdateCheckmanager 強制アップデートに関する処理を担当 FirebaseRemoteConfigProvider Firebase Remote Configとの通信とパラメータ取得 ConfigKey Firebase Remote Configのパラメータ名を定義 コードを見ていただくとわかりますが、UpdateCheckManagerでdidBecomeActiveを監視しています。アプリがアクティブになった際は、FirebaseRemoteConfigProviderにFirebaseのコンソールで設定したパラメータを取ってきてもらいます。 また今回は、「アプリがアクティブになった際に強制アップデートのアラートを出す」という方針を取っております。これにより、ストアに移動してアップデートせずに戻ってきた場合でも再度アラートを出すことができます。 まとめ Firebase Remote Configを使用した強制アップデートの実装についてまとめました。 強制アップデートの実装については、他にも強制アップデート用のライブラリを利用する等の方法があるようなので、作成するアプリに適した方法を使用してください。 参考文献 Firebase Remote Configを使ってみる Implementing Force Update Feature using Firebase Remote Config in iOS
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Firebase Remote Configで強制アップデート

FirebaseのRemote Configを使用して強制アップデートを実装したので記事にします。 強制アップデートとは アプリの新しいバージョンをリリースした時に、ユーザに必ずアップデートをして欲しい場合があります。 例えば、以下のような場合です。 既存の機能に大幅な変更が加わった場合 致命的なバグを修正した場合 この時、ユーザがアプリを立ち上げた際、ユーザにアップデートを促しアップデートを完了させるまでアプリの使用を事実上不可能にする処理が強制アップデートです。 アプリの使用を不可能にする処理としては、以下のようなアラートを出し、アプリストアへの遷移以外できなくすることなどが挙げられます。(ストアからアップデートせずに戻ってきても再度表示される) また、強制アップデートは最初に公開した時に入れとかないと強制アップデートさせたくてもさせることができない場面が出てくるので、できるだけ最初のバージョンで入れておきたい機能です。 Firebase Remote Configとは Firebase Remote Configはアプリの動作や外見を変更するパラメータをFirebaseに持たせることができるクラウドサービスです。 アプリが通信処理を行いこのパラメータにアクセスすることにより、アプリ自身を変更することなく動作や外見を変更することができます。 今回の強制アップデートの場合、アプリの最新のバージョン情報などをFirebase Remote Configに設定しておきます。 実装 ここからは実際にどのような手順で実装するのか見ていきます。 Firebase側 まずFirebaseのコンソールから左下、「エンゲージメント」の中にある「Remote Config」を選びます。 初めての場合はRemote Configのトップページが表示されます。「構成を作成」を押すと最初のパラメータを入力することができるので、バージョン情報を入力して保存しましょう。 また、それぞれ項目は以下の通りです。 パラメータ名 アプリ側でパラメータにアクセスするためのキー データ型 パラメータのタイプ Description パラメータの説明 Default value 設定するパラメータ 保存すると以下のような画面になります。 この状態ではまだ変更が適用されていません。右上の「変更を公開」ボタンを押すと変更が適用されて、設定したパラメータをアプリで使用できるようになります。 また、新たにパラメータを追加することも可能です。 私の場合は、current_versionの他にrequire_force_update(強制アップデートさせるかどうか)とapp_store_url(app storeのURL)も追加しています。 最後に「変更を公開」ボタンを押してFirebase側の設定は完了です。 アプリ側 次はアプリ側の実装についてです。 まず全体のコードを載せてしまいます。 RemoteConfigProvider.swift import FirebaseRemoteConfig /// Firebaseのコンソールで設定したパラメータ名に対応 enum ConfigKey: String { case currentVersion = "current_version" case forceUpdateRequired = "require_force_update" case storeUrl = "app_store_url" static func makeDefaults() -> [String: Any] { [ currentVersion.rawValue: "", forceUpdateRequired.rawValue: false, storeUrl.rawValue: "" ] } } final class FirebaseRemoteConfigProvider { private let remoteConfig = RemoteConfig.remoteConfig() /// パラメータを取ってくる func fetchConfig(completion: (() -> Void)? = nil) { // 取得するパラメータのデフォルトを設定 remoteConfig.setDefaults(ConfigKey.makeDefaults() as? [String: NSObject]) // FetchとActivateを一括で行う remoteConfig.fetchAndActivate(completionHandler: { status, error in switch status { case .successUsingPreFetchedData, .successFetchedFromRemote: completion?() case .error: if let error = error { print(error.localizedDescription) } @unknown default: fatalError() } }) } /// RemoteConfigから取ってきたパラメータを取得 func getConfig(key: ConfigKey) -> RemoteConfigValue { remoteConfig.configValue(forKey: key.rawValue) } } ForceUpdateManager.swift import Combine import FirebaseRemoteConfig final class UpdateCheckManager { static let shared = UpdateCheckManager() private let remoteConfigProvider = FirebaseRemoteConfigProvider() private var cancellable: AnyCancellable? private init() {} func setup() { observeApplicationDidBecomeActive() } /// アプリがActiveになった際にアップデートチェック private func observeApplicationDidBecomeActive() { cancellable = NotificationCenter.Publisher(center: .default, name: UIApplication.didBecomeActiveNotification, object: nil) .sink(receiveValue: { [weak self] _ in FirebaseRemoteConfigProvider().fetchConfig(completion: { self?.forceUpdateIfNeeded() }) }) } /// requireForceUpdateがtrueかつ現在のバージョンが最新のバージョンと異なる場合に強制アップデート private func forceUpdateIfNeeded() { let localVersionString = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as! String let requireForceUpdate = remoteConfigProvider.getConfig(key: .forceUpdateRequired).boolValue if requireForceUpdate, let currentVersionString = remoteConfigProvider.getConfig(key: .currentVersion).stringValue, localVersionString != currentVersionString { guard let storeUrlString = remoteConfigProvider.getConfig(key: .storeUrl).stringValue, let storeUrl = URL(string: storeUrlString) else { return } // 強制アップデートのアラートを出す } } } AppDelegate.swift import UIKit @main class AppDelegate: UIResponder, UIApplicationDelegate { func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. UpdateCheckManager.shared.setup() return true } ... } 各クラスの説明は以下の通りです。 クラス 機能 UpdateCheckmanager 強制アップデートに関する処理を担当 FirebaseRemoteConfigProvider Firebase Remote Configとの通信とパラメータ取得 ConfigKey Firebase Remote Configのパラメータ名を定義 コードを見ていただくとわかりますが、UpdateCheckManagerでdidBecomeActiveを監視しています。アプリがアクティブになった際は、FirebaseRemoteConfigProviderにFirebaseのコンソールで設定したパラメータを取ってきてもらいます。 また今回は、「アプリがアクティブになった際に強制アップデートのアラートを出す」という方針を取っております。これにより、ストアに移動してアップデートせずに戻ってきた場合でも再度アラートを出すことができます。 まとめ Firebase Remote Configを使用した強制アップデートの実装についてまとめました。 強制アップデートの実装については、他にも強制アップデート用のライブラリを利用する等の方法があるようなので、作成するアプリに適した方法を使用してください。 参考文献 Firebase Remote Configを使ってみる Implementing Force Update Feature using Firebase Remote Config in iOS
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Mac + iOS で Charles を使う

概要 AdMobが表示されない原因調査のために、Charlesを使ったので手順を残しておきます。 [Mac] Charlesをダウンロード&インストール [Mac: Charles] Proxy Settings Proxy - Proxy Settings の HTTP Proxy を確認する。必要に応じて変える。 [Mac: Charles] SSL Settings SSL - SSL Proxy Settings の SSL Proxying の include に *:* を追加する。 [Mac: Charles] Local IP Address Help - Local IP Address の IP Address を控えておく。 [iOS: Safari] 証明書ダウンロード 以下へアクセスし証明書をダウンロードする。 [iOS: 設定] 証明書インストール 設定 - 一般 - VPNとデバイス管理 ダウンロード済プロファイルにある Charles Proxy〜 を選びインストール。 [iOS: 設定] 証明書設定 設定 - 一般 - 情報 - 証明書信頼設定 で Charles Proxy〜 を有効にする。 [iOS: 設定] Wi-Fi設定 設定 - Wi-Fi - 選択 - HTTPプロキシ で 手動を選び、IP Address と ポート番号を設定。 [Mac: Charles] ほうきボタン 既につながっているのでほうきボタンでクリア [Mac: Charles] セッション保存 File - Save Session でセッションを保存 [iOS: 設定] Wi-Fi設定 設定 - Wi-Fi - 選択 - HTTPプロキシ で オフを選ぶ。 忘れないように・・・ 最後に もうちょっと何かやった気もするけど忘れてしまった・・・思い出したら追記する。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む