アプリをiTunes Connectでサブミットするときに以下のように毎回「輸出コンプライアンス」についての質問に回答する必要がある。
少しの手間だが面倒なのでこの入力をスキップする方法を調べた。
暗号化機能を含まない場合はInfo.plist
にITSAppUsesNonExemptEncryption
というキーでNO
を設定しておくことで「輸出コンプライアンス」の項目が表示されなくなる。
環境: Swift3
UIWebViewのリクエストにUserAgentを設定するには、リクエスト前にUserDefaultsのregisterメソッドでキー名UserAgent
で値をセットする必要がある。
UserDefaults.standard.register(defaults: ["UserAgent" : "hoge"])
この時、
// これだと設定されない UserDefaults.standard.set("hoge", forKey: "UserAgent")
のようにUserDefaultsに普通に保存してもダメでregisterメソッドでデフォルト値として設定すること。
// これを使う open func register(defaults registrationDictionary: [String : Any])
setメソッドとregisterメソッドの違いは以下参照。
xyk.hatenablog.com
ハマリポイントがあったのでメモ。
UIWebViewで使われるUserAgentを取得する方法は以下。
let ua: String? = webView.stringByEvaluatingJavaScript(from: "navigator.userAgent")
取得結果は
Mozilla/5.0 (iPhone; CPU iPhone OS 10_2 like Mac OS X) AppleWebKit/602.3.12 (KHTML, like Gecko) Version/10.0 Mobile/14C92 Safari/602.1
となった。
この結果の末尾に独自の文字列(ここでは例としてhoge
)を追加して
Mozilla/5.0 (iPhone; CPU iPhone OS 10_2 like Mac OS X) AppleWebKit/602.3.12 (KHTML, like Gecko) Version/10.0 Mobile/14C92 Safari/602.1 hoge
これを新たなUserAgentとして更新しようとしたがなぜか更新できなかった。
どうやら使用するUIWebViewのインスタンスに対してwebView.stringByEvaluatingJavaScript(from: "navigator.userAgent")
を呼び出すとそれ以降、UserAgentを設定しても更新できないようだ。
なので、以下のようにした。
class WebViewController: UIViewController { @IBOutlet weak var webView: UIWebView? override func loadView() { self.updateUserAgent() super.loadView() } func updateUserAgent() { guard let currentUserAgent = UIWebView().stringByEvaluatingJavaScript(from: "navigator.userAgent"), !currentUserAgent.contains("hoge") else { return } let newUserAgent = "\(currentUserAgent) hoge" let dic = ["UserAgent" : newUserAgent] UserDefaults.standard.register(defaults: dic) } }
実際に使用するself.webView
には触れず、またself.webView
を使用する前に、新たなUIWebViewインスタンスを作って、そこから元のUserAgentを取り出して更新するようにした。
一度更新すればアプリ内では引き継がれるのでapplication:didFinishLaunchingWithOptions
でやってしまっても良いと思う。
環境: Swift3
UserDefaultsのregisterDefaultsメソッドについて勘違いしていたのでメモ。
// Swift3 で registerDefaults() から register(defaults: ) に変更になった open func register(defaults registrationDictionary: [String : Any])
このregisterメソッドを使って登録したDictionary(以降RegistrationDictionaryと呼ぶ)はデフォルト値として使う用でUserDefaultsのデータとしてファイルに書き込まれるわけではない。
あるキーで読み込みした時にそのキーがまだUserDefaultsに存在せず、RegistrationDictionaryに存在すれば、RegistrationDictionaryの値をデフォルト値として返す。
既にキーがUserDefaultsに登録されていた場合、またはその後、そのキーでUserDefaultsに登録された場合は、UserDefaultsの値を返す。
let defaults = UserDefaults.standard let foo1 = defaults.string(forKey: "Foo") print("foo1:", foo1) // nil defaults.register(defaults: ["Foo": "Bar"]) let foo2 = defaults.string(forKey: "Foo") print("foo2:", foo2) // Optional("Bar") UserDefaults.standard.set("Hoge", forKey: "Foo") defaults.synchronize() let foo3 = defaults.string(forKey: "Foo") print("foo3:", foo3) // Optional("Hoge")
環境:Swift3
UserDefaults に保存されているデータをすべて表示する
for (key, value) in UserDefaults.standard.dictionaryRepresentation().sorted(by: { $0.0 < $1.0 }) { print("- \(key) => \(value)") }
または
if let appDomain = Bundle.main.bundleIdentifier, let dic = UserDefaults.standard.persistentDomain(forName: appDomain) { for (key, value) in dic.sorted(by: { $0.0 < $1.0 }) { print("- \(key) => \(value)") } }
キーの辞書順にソートしてから表示している。
環境: iOS9
Firebaseのプッシュ通知機能を使い、iOS端末に向けてのプッシュ通知をしたところ、送信は問題なくできたのだが、管理画面から確認できる既読数が0にままだったので原因を調べた。
で原因だが以下ドキュメント
Receive Messages in an iOS App | Firebase
https://firebase.google.com/docs/cloud-messaging/ios/receive
のHandling messages with method swizzling disabled
の項にちゃんと書いてあるのだが Method swizzling をオフにした場合にはFIRMessaging.messaging().appDidReceiveMessage(userInfo)
を実装する必要があるが、これを実装していなかった。
func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) { // Let FCM know about the message for analytics etc. FIRMessaging.messaging().appDidReceiveMessage(userInfo) // handle your message }
これを実装したところ、既読数も計測されるようになった。
ちなみにプッシュ通知を受信した時に呼ばれるメソッドは以下記事書いたように2種類あるのだが
fetchCompletionHandler 付きのメソッドの方はアプリの状態によらず必ず呼ばれるのでこちらでappDidReceiveMessage(userInfo)
を実装すること。
あと、管理画面上に計測結果が反映されるには、少し時間がかかるので注意。
まず送信数の方が数十分〜数時間後に反映され、それからさらに時間をおいて既読数の方が反映される。
また、iOS10ではプッシュ通知周りの実装が少し変わったので以下を参考に今後修正予定。
(以前の実装のままでもiOS10で動作はする)
quickstart-ios/AppDelegate.swift at master · firebase/quickstart-ios
https://github.com/firebase/quickstart-ios/blob/master/messaging/FCMSwift/AppDelegate.swift
環境: Swift2.3
前提となるCapabilities
の設定
- Push Notifications -> ON - Background Modes -> OFF
プッシュ通知受信時に呼び出されるメソッドに
1. application:didReceiveRemoteNotification:
と
2. application:didReceiveRemoteNotification:fetchCompletionHandler:
という似たメソッドが存在するがこの違いについてメモしておく。
// 1 func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) { // userInfo の処理 } // 2 func application(application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) { // userInfo の処理 }
基本的には、この2つのメソッドのどちらか片方のみ実装することになる。
両方とも実装していた場合は
2. application:didReceiveRemoteNotification:fetchCompletionHandler:
の方が優先されて呼び出され、
1. application:didReceiveRemoteNotification:
の方は呼び出されないので注意。
application:didReceiveRemoteNotification:
を実装してプッシュ通知を受信画面上部に通知表示。
通知をタップすると
application:didFinishLaunchingWithOptions:
のみ呼び出される。
application:didReceiveRemoteNotification:
は呼ばれない。
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { if let userInfo = launchOptions?[UIApplicationLaunchOptionsRemoteNotificationKey] { // userInfo の処理 } }
通知表示はない。
application:didReceiveRemoteNotification:
のみ呼び出される
画面上部に通知表示。
通知をタップするとapplication:didReceiveRemoteNotification:
のみ呼び出される
application:didReceiveRemoteNotification:fetchCompletionHandler:
を実装してプッシュ通知を受信画面上部に通知表示。
通知をタップすると
application:didFinishLaunchingWithOptions:
が呼び出される。
その後
application:didReceiveRemoteNotification:fetchCompletionHandler:
が呼び出される。
ここが1とは違い必ず呼び出される。
通知表示はない。
application:didReceiveRemoteNotification:fetchCompletionHandler:
のみ呼び出される
画面上部に通知表示。
通知をタップするとapplication:didReceiveRemoteNotification:fetchCompletionHandler:
のみ呼び出される
ターミナルで以下コマンドswift -v
を実行する。
$ /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift -v Apple Swift version 3.0.1 (swiftlang-800.0.58.6 clang-800.0.42.1) Target: x86_64-apple-macosx10.9 /Applications/Xcode.app/Contents/Developer/usr/bin/lldb "--repl=-target x86_64-apple-macosx10.9 -enable-objc-interop -sdk /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk -color-diagnostics" Welcome to Apple Swift version 3.0.1 (swiftlang-800.0.58.6 clang-800.0.42.1). Type :help for assistance. 1> :q
REPLが起動するので:q
で終了する。
Window -> Devices and Simulators からiPhoneデバイスを選択、identifier
の部分が UDIDになる。
初期表示はシリアル番号
となっているがその辺りをクリックすると表示項目が切り替わり、UDID
が現れる。
シリアル番号
-> UDID
-> ECID
-> 機種ID
の順に切り替わる。
右クリックからコピーできる。
ターミナルから以下コマンドを実行。Xcodeをインストールしていれば使える
$ instruments -s devices
追記:
instruments コマンドがいつの間にか deprecated になっていた。
`instruments` is now deprecated in favor of 'xcrun xctrace' (see `man xctrace` for more information on its replacement)
代わりに以下コマンドを使う。
$ xcrun xctrace list devices
環境:
Swift2.2
iOS8以降対象
いつも設定している Appearance のコピペ用メモ。
以下を AppDelegate で呼ぶ。
mainColor は extension で独自に設定したもの。
private func setupAppearance() { // アプリケーション全体のtintColor設定 self.window?.tintColor = UIColor.mainColor // ステータスバーの文字色を白に。 // プラス`Info.plist`に`View controller-based status bar appearance = NO`を追加 UIApplication.sharedApplication().statusBarStyle = .LightContent // ナビゲーションバーの色 UINavigationBar.appearance().barTintColor = UIColor.mainColor // ナビゲーションバーボタンの色を白に。 UINavigationBar.appearance().tintColor = UIColor.whiteColor() // ナビゲーションバーのタイトル文字色を白に。 UINavigationBar.appearance().titleTextAttributes = [ NSForegroundColorAttributeName: UIColor.whiteColor() ] }
こんな感じに。