https://github.com/daisuke-t-jp/UserDefaultsAndKeychainSample/
サンプルを作った。以下メモ。
Swift の Codable を使えば、プロパティそれぞれをエンコード、デコードのコードを書く必要なく楽だ。
Keychain の場合は
- KeychainAccess が便利
- プロパティのエンコード、デコードに PropertyListEncoder / PropertyListDecoder を使う
感じ。
https://github.com/daisuke-t-jp/UserDefaultsAndKeychainSample/
サンプルを作った。以下メモ。
Swift の Codable を使えば、プロパティそれぞれをエンコード、デコードのコードを書く必要なく楽だ。
Keychain の場合は
感じ。
iOS 14 では AppTrackingTransparency Framework が追加され、ユーザのトラッキング収集はこのフレームワークを介して、ユーザに承認を得る必要がある。
もし iOS 13 以前に IDFA を利用して広告を表示していたアプリがあったとして、iOS 14 で AppTrackingTransparency を使ってユーザの承認を得ない場合は、IDFA が無効になった状態で広告が表示されることになる。 (=収益が下がることが予想される)
Info.plist にトラッキング利用の説明文を追加する
NSUserTrackingUsageDescription を Info.plist に追加する。
<key>NSUserTrackingUsageDescription</key> <string>This identifier will be used to deliver personalized ads to you.</string>
トラッキングの承認をリクエストして、その結果 Google AdMob を表示する。
import AppTrackingTransparency import AdSupport ... func requestIDFA() { ATTrackingManager.requestTrackingAuthorization(completionHandler: { status in // Tracking authorization completed. Start loading ads here. // loadAd() }) }
ユーザへのトラッキング利用の許諾アラートに説明文が表示される。
Google AdMob ではトラッキング広告は表示できない。
そういう時は、かわりに Google AdMob は SKAdNetwork を使用して、アプリインストールをアトリビューションすることができる↓
https://developers.google.com/admob/ios/ios14?hl=ja#skadnetwork
テキストから住所(地名)を得たい場合 (たとえば、"嵐山" という入力から "京都府京都市右京区" という結果が欲しい)
その用途に使用できそうな Apple の API は、以下がある。
これらの違いや使い道を確認したかったので、テキスト入力をしてそれぞれの API の結果を一覧表示するサンプルを作った。
CLGeocoder は短時間に大量リクエストを行うと失敗する。(CLError.Code.network
)
この動作はドキュメントにも明記されている。
Apps must be conscious of how they use geocoding. Geocoding requests are rate-limited for each app, so making too many requests in a short period of time may cause some of the requests to fail. (When the maximum rate is exceeded, the geocoder returns an error object with the CLError.Code.network error to the associated completion handler.) Here are some rules of thumb for using this class effectively:
ただし、以下の記載があるので、ユーザアクションに紐づいた頻度程度の API 呼び出し自体は考慮されているようだ。
• Send at most one geocoding request for any one user action.
アプリ側で呼び出すタイミングを気を付ければ、あまりエラーになることは無さそうだ。(逆に言えば、ユーザー入力など関係なく、アプリの動作として大量リクエストを送ったりする用途には使えない)
CLGeocoder とは違い、MKLocalSearch はリクエスト制限でエラーにはならない(連続リクエストをしてエラー発生を観測できなかった)
こちらは、現在地と範囲を指定して地図上にある"近場"の Placemark を検索するのが本来の用途のようだ。(LocalSearch なので)
CLGeocoder とはちがい、住所だけではなく、店舗の名前を知りたいならばこちらを使う。
ただし、MapKit の機能なので、地図表示がないアプリで住所検索をしたいために MKLocalSearch だけ使う、というのはレギュレーションに反していないのか、疑問である。
iOS に入っている Apple の天気アプリではこのように、テキストに対して住所候補が複数あれば、複数表示される。
このような動作を期待して CLGeocoder を使ってみたが、CLGeocoder はレスポンスは配列であるが、結果は常に単一になっていた。
たとえば "中央区" の場合、iOS の天気アプリのように、日本国内にある中央区を複数候補を返して欲しいが、ひとつの中央区しか CLGeocoder は返さない(現在位置から一番近い "中央区" になる?)
この CLGeocoder のレスポンスが一つしか得られない問題は StackoverFlow でも散見されるので、どうにかなる話では無さそうだ。
「候補が複数欲しい」場合は、Apple の API は使わず他のサービス(Google の API など)を使う必要があるようだ。
気になったので、オープンソースな iOS の Web ブラウザアプリで、 WKWebview
のカスタムユーザーエージェント(customUserAgent
) を設定している部分を見てみる。
ユーザーエージェントの例
Mozilla/5.0 (iPhone; CPU iPhone OS 13_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/84.0.4147.71 Mobile/15E148 Safari/604.1
UserAgent関連コードを抜粋(これがすべてではない)
std::string BuildOSCpuInfo() { std::string os_cpu; // Remove the end of the platform name. For example "iPod touch" becomes // "iPod". std::string platform = base::SysNSStringToUTF8([[UIDevice currentDevice] model]); size_t position = platform.find_first_of(" "); if (position != std::string::npos) platform = platform.substr(0, position); base::StringAppendF(&os_cpu, "%s; CPU %s %s like Mac OS X", platform.c_str(), (platform == "iPad") ? "OS" : "iPhone OS", OSVersion().c_str()); return os_cpu; } /* 中略 */ std::string BuildMobileUserAgent(const std::string& mobile_product) { std::string user_agent; base::StringAppendF(&user_agent, "Mozilla/5.0 (%s) AppleWebKit/605.1.15" " (KHTML, like Gecko) %s Mobile/15E148 Safari/604.1", BuildOSCpuInfo().c_str(), mobile_product.c_str()); return user_agent; }
ユーザーエージェントの例
Mozilla/5.0 (iPhone; CPU OS 13_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) FxiOS/28.0 Mobile/15E148 Safari/605.1.15
以下、UserAgent関連コードを抜粋(これがすべてではない)
public static let uaBitSafari = "Safari/605.1.15" public static let uaBitMobile = "Mobile/15E148" public static let uaBitFx = "FxiOS/\(AppInfo.appVersion)" public static let product = "Mozilla/5.0" public static let platform = "AppleWebKit/605.1.15" public static let platformDetails = "(KHTML, like Gecko)" /* 中略 */ public static func defaultMobileUserAgent() -> UserAgentBuilder { return UserAgentBuilder(product: UserAgent.product, systemInfo: "(\(UIDevice.current.model); CPU OS \(UIDevice.current.systemVersion.replacingOccurrences(of: ".", with: "_")) like Mac OS X)", platform: UserAgent.platform, platformDetails: UserAgent.platformDetails, extensions: "FxiOS/\(AppInfo.appVersion) \(UserAgent.uaBitMobile) \(UserAgent.uaBitSafari)") }
ユーザーエージェントの例
Mozilla/5.0 (iPhone; CPU iPhone OS 12_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.4 Mobile/15E148 DuckDuckGo/7 Safari/605.1.15
UserAgent関連コードを抜粋(これがすべてではない)
struct UserAgent { private struct Constants { // swiftlint:disable line_length static let fallbackWekKitVersion = "605.1.15" static let fallbackSafariComponent = "Safari/\(fallbackWekKitVersion)" static let fallbackDefaultAgent = "Mozilla/5.0 (iPhone; CPU iPhone OS 13_5 like Mac OS X) AppleWebKit/\(fallbackWekKitVersion) (KHTML, like Gecko) Mobile/15E148" static let desktopPrefixComponent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15)" static let fallbackVersionComponent = "Version/13.1.1" // swiftlint:enable line_length }
AppleWebKit
, Safari
のバージョンもコード内で固定値で記述されている
Xcode 12.0 beta 2 (12A6163b) の iOS platform の Frameworks の中をみると
$ cd /Applications/Xcode-beta.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks $ ls ARKit.framework AVFoundation.framework AVKit.framework Accelerate.framework Accessibility.framework Accounts.framework AdSupport.framework AddressBook.framework AddressBookUI.framework AppClip.framework AppTrackingTransparency.framework AssetsLibrary.framework AudioToolbox.framework AudioUnit.framework AuthenticationServices.framework AutomaticAssessmentConfiguration.framework BackgroundTasks.framework BusinessChat.framework CFNetwork.framework CallKit.framework CarPlay.framework ClassKit.framework ClockKit.framework CloudKit.framework Combine.framework Contacts.framework ContactsUI.framework CoreAudio.framework CoreAudioKit.framework CoreAudioTypes.framework CoreBluetooth.framework CoreData.framework CoreFoundation.framework CoreGraphics.framework CoreHaptics.framework CoreImage.framework CoreLocation.framework CoreMIDI.framework CoreML.framework CoreMedia.framework CoreMotion.framework CoreNFC.framework CoreServices.framework CoreSpotlight.framework CoreTelephony.framework CoreText.framework CoreVideo.framework CryptoKit.framework CryptoTokenKit.framework DeveloperToolsSupport.framework DeviceCheck.framework EventKit.framework EventKitUI.framework ExposureNotification.framework ExternalAccessory.framework FileProvider.framework FileProviderUI.framework Foundation.framework GLKit.framework GSS.framework GameController.framework GameKit.framework GameplayKit.framework HealthKit.framework HealthKitUI.framework HomeKit.framework IOKit.framework IOSurface.framework IdentityLookup.framework IdentityLookupUI.framework ImageCaptureCore.framework ImageIO.framework Intents.framework IntentsUI.framework JavaScriptCore.framework LinkPresentation.framework LocalAuthentication.framework MLCompute.framework MapKit.framework MediaAccessibility.framework MediaPlayer.framework MediaSetup.framework MediaToolbox.framework MessageUI.framework Messages.framework Metal.framework MetalKit.framework MetalPerformanceShaders.framework MetalPerformanceShadersGraph.framework MetricKit.framework MobileCoreServices.framework ModelIO.framework MultipeerConnectivity.framework NaturalLanguage.framework NearbyInteraction.framework Network.framework NetworkExtension.framework NewsstandKit.framework NotificationCenter.framework OSLog.framework OpenAL.framework OpenGLES.framework PDFKit.framework PassKit.framework PencilKit.framework Photos.framework PhotosUI.framework PushKit.framework QuartzCore.framework QuickLook.framework QuickLookThumbnailing.framework RealityKit.framework ReplayKit.framework SafariServices.framework SceneKit.framework ScreenTime.framework Security.framework SensorKit.framework Social.framework SoundAnalysis.framework Speech.framework SpriteKit.framework StoreKit.framework SwiftUI.framework SystemConfiguration.framework Twitter.framework UIKit.framework UniformTypeIdentifiers.framework UserNotifications.framework UserNotificationsUI.framework VideoSubscriberAccount.framework VideoToolbox.framework Vision.framework VisionKit.framework WatchConnectivity.framework WebKit.framework WidgetKit.framework _AVKit_SwiftUI.framework _HomeKit_SwiftUI.framework _MapKit_SwiftUI.framework _QuickLook_SwiftUI.framework _SceneKit_SwiftUI.framework _SpriteKit_SwiftUI.framework iAd.framework
という感じだった。
AppClips や ScreenTime など iOS 14 のあたらしい framework は含まれているが、Apple Archive はなかった。
macOS Big Sur の framework はこんな感じ。こちらも AppleArchive はない。
$ cd /Applications/Xcode-beta.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/iOSSupport/System/Library/Frameworks $ ls ARKit.framework AVKit.framework AddressBook.framework AddressBookUI.framework AssetsLibrary.framework AuthenticationServices.framework BusinessChat.framework CarPlay.framework ContactsUI.framework CoreAudioKit.framework CoreNFC.framework EventKitUI.framework GameController.framework GameKit.framework GameplayKit.framework HealthKit.framework HealthKitUI.framework HomeKit.framework IdentityLookupUI.framework IntentsUI.framework JavaScriptCore.framework LinkPresentation.framework MapKit.framework MediaPlayer.framework MessageUI.framework Messages.framework MetalKit.framework MobileCoreServices.framework MultipeerConnectivity.framework NetworkExtensioniOSSupport.framework NewsstandKit.framework OpenAL.framework PDFKit.framework PassKit.framework PencilKit.framework PhotosUI.framework QuickLook.framework RealityKit.framework ReplayKit.framework SafariServices.framework SceneKit.framework ScreenTime.framework Social.framework SpriteKit.framework StoreKit.framework SwiftUI.framework Twitter.framework UIKit.framework UserNotificationsUI.framework VisionKit.framework WatchConnectivity.framework WebKit.framework WidgetKit.framework _AVKit_SwiftUI.framework _MapKit_SwiftUI.framework _SpriteKit_SwiftUI.framework iAd.framework
リーダブルコード ―より良いコードを書くためのシンプルで実践的なテクニック (Theory in practice)
原題は
The Art of Readable Code: Simple and Practical Techniques for Writing Better Code
である。
邦題では「リーダブルコード」であるが、原題では「The Art of Readable Codes」なので本来は Art の意味もタイトルに含まれている。
その言葉の意味を考えると、これは内容的には、コーディングに関しての Art, 芸術に関する本である。
基本的なところだと、たとえば命名や改行の仕方で、そのコードの読みやすさというは変わる。(命名が適切であり、役割を簡潔に示せているか? 改行された位置により処理が一つのブロックであることを示せているか?)
残されたコードが読みやすく、理解しやすく、あとで読む人(それはすっかりコーディングした当時のことを忘れた自分かもしれない)がストレスなく開発できるかどうか?
そういったコードを通した情報伝達の精度をいかによくするか、という芸術だ。
逆に、この情報伝達がうまくできていないと、コードというダンジョンを、頼りない松明片手に探索する考古学をしなければならない。 (そして運が悪ければダンジョンから出れずに、壁に「yyyy/MM/dd 私は解析に失敗した。ここに眠る ◯◯」とコメントする羽目になる)
本は 200 ページくらいで読みやすい。
「こうしたらコードが読みやすくなる(Readable)」という tips は、自分でコーディングしたり、ほかの人のコードを読んだりして無意識に獲得していくものも多いが (本を読んで、これはやっているなぁ、、という部分もある)、文書にして明文化したことにより、それらが共通知識として残るので価値がある。
個人的には範囲を示すときに start/stop は包括的で、begin/end は終端が排他的なものに使うというのが、なるほどなあ、と思った。
たとえば
2020/01/01 の1日を範囲としたい場合
ではなく
とできるし、後者の方が自然である。
前者だと厳密に1日の終わりを示すことは難しい。なぜなら 59 秒は1日の終わりでなく、まだ1秒あるからだ。(さらに 999... とミリ秒単位の指定を続けると精度はあげることはできるが、、)
免除される暗号のみを使用しているのに App Store Connect でアップロードごとに毎回、暗号に関する質問を答えるのが面倒。
その場合は、
Info.plist に以下を追加する。
<key>ITSAppUsesNonExemptEncryption</key> <false/>
これでアプリには免除されていない暗号を使用していない、とマークされるので暗号に関する質問をスキップできる。
(もし、true にした場合はこの逆で、免除されていない暗号を"使用している"、とマークされる)
そもそもなぜアプリを公開する時に、暗号に関する確認があるのか?
暗号利用環境に関する動向調査(独立行政法人 情報処理推進機構)
アメリカに限らずに「暗号」というは、国家の安全保障・戦略にかかわるものなので、その技術は国外に出て(輸出)は困る、ということらしい。(もちろん、普及している標準的な暗号技術はその対象にはならない)
そして、AppStore のアプリはアメリカからの配信(輸出)になるので、米国輸出管理法の規制の対象になる、ということみたい。