- 投稿日:2019-07-10T22:22:10+09:00
iOS13ではStoryboardでもDIができる件について
はじめに
これまでのiOS13未満のUIKitのAPIでは、Storyboardで定義したUIViewControllerではinitializerでのDI(Dependency Injection|依存性の注入)ができないという課題がありました。
※initializerでのDIはコンストラクタインジェクションとも呼ばれますが、Swiftでは慣習的にコンストラクタという用語はあまり使わないので、本記事では「initializerでのDI」とします。
UIViewControllerの実装方法とinitializerでのDIの可否をまとめると以下のようになります。
※黒魔術的なライブラリなどを利用しない前提
レイアウト実装方法 initializer DI可否(〜iOS12) initializer DI可否(iOS13〜) コードベース ○ ○ XIB ○ ○ Storyboard × ○ iOS13未満での実装方法の詳細については以下の記事で説明しています。
Qiita - iOSとコードベースレイアウト勿論、プロパティインジェクションならばiOS12以下でもStoryboardで定義したUIViewControllerで可能ですが、initializerでのDIをする設計にすることで以下の恩恵が得られます。
- DIする値をプロパティで保持する場合に、varでなくletで定義できるのでより安全
- DIする値をプロパティで保持する場合に、Optional型でなく非Optional型で定義できるので無駄なUnwrapが不要
- UIViewControllerの生成を行うために、必ずDIが必要になるので、プロパティインジェクションのように設定忘れが起こり得ない
iOS13でStoryboardでイニシャライザでDIする
iOS13では地味にこんなAPIが加わっています。
instantiateInitialViewController(creator:)
func instantiateInitialViewController<ViewController>(creator: ((NSCoder) -> ViewController?)? = nil) -> ViewController? where ViewController : UIViewControllerこのAPIでは、createrというクロージャーを引数として渡すことで、これまで隠蔽されていたStoryboardからのUIViewControllerの生成過程に介入することができます。
以下のようにUIViewControllerの継承クラスで以下のようにNSCoderを引数にとるinitializerを実装します。
class ViewController: UIViewController { @IBOutlet fileprivate weak var textView: UITextView! private let dependency: Int // DI用のinitializer init?(coder: NSCoder, dependency: Int) { self.dependency = dependency super.init(coder: coder) } // 独自のinitializerを実装するときにrequiedとして実装が要求されるinitializer // 利用しないので、fatalError()として実装を省略する required init?(coder: NSCoder) { fatalError() } }上記のように定義したinitializerをcreaterのクロージャーで利用します。
let storyboard = UIStoryboard(name: "ViewController", bundle: nil) let viewController = storyboard.instantiateInitialViewController { coder in ViewController(coder: coder, dependency: 10) }以上のようにすることで、Storyboardで定義したUIViewControllerでもinitializerでのDIが可能です。
- 投稿日:2019-07-10T21:08:29+09:00
【iOS】 Cloud Functions for Firebaseを使ってアプリにサーバ日時取得処理を追加する
はじめに
ライセンスの期限チェックやゲームの時間チート対策など、端末の日時ではなくサーバの日時が欲しいケースがあります。
Google Firebaseの1機能であるCloud Functions for Firebaseを使うことで、容易にサーバ日時取得処理をアプリに追加できます。Cloud Functions for Firebaseとは
アプリなどの環境から、Googleクラウド上にデプロイしているバックエンドコードを実行することができる機能です。
毎月200万回、処理時間100万秒、トラフィック容量5GBまでであれば無料で利用できます。2019/7/10現在前提条件
この記事を利用するにあたり、次の状態を前提とします。
- 処理を追加するXcodeプロジェクトが存在する
- Google Firebase上にプロジェクトを作成し、Xcodeプロジェクトに導入している
- XcodeプロジェクトにCocoaPodsからFirebaseを導入している
- Xcodeプロジェクトで、Firebaseの初期化処理を実装しているCocoaPodsを使っていない場合など環境が異なる場合は、記事の該当部分について適宜対応してください。
環境の構築
上の項目から順番にインストールしていきます。
すでにインストール済みの場合、スキップして次の項目に移ってください。Node.jsとnpmのインストール
Node.js公式サイトにアクセスし、インストーラーをダウンロードしインストールします。
インストール後ターミナルで次のコマンドを入力し、バージョン情報を得られたらインストール完了です。$ node --version $ npm --versionFirebase CLIのインストール
ターミナルで次のコマンドを入力し、Firebase CLIをインストールします。
$ npm install -g firebase-toolsバックエンド側の実装
プロジェクトの作成
管理しやすい場所(Xcodeプロジェクトフォルダの直下など)に適当なフォルダを作成します。(以降バックエンドフォルダ)
$ mkdir FirebaseFunctionsターミナルで作成したフォルダに移動し、次のコマンドを入力します。
$ firebase init実行するといくつか選択項目が表示されるので、次の値を選択します。
- 利用する機能:Functions: Configure and deploy Cloud Functions
- デフォルトのFirebaseプロジェクト:作成したFirebaseプロジェクトを選択します
- 言語:JavaScript
- ESLintを利用するか:好みで選択します。単純な機能を実装するため、Nで問題ありません
- 今すぐ依存関係をインストールするか:Y
以上の手順でバックエンドフォルダ直下にファイル一式が作成されます。コードの記述
サーバ日時を取得するためのコードを記述します。
バックエンドフォルダ/functions/index.js に次のコードを追加します。バックエンドフォルダ/functions/index.jsexports.ServerTime = functions.https.onRequest((request, response) => { var dt = new Date(); var delta = dt.getTime(); response.send({ data: String(delta) }); });コードのデプロイ
ターミナルで次のコマンドを入力し、ログインします。
$ firebase loginログインしている状態で次のコマンドを入力し、デプロイを実行します。
$ firebase deployDeploy complete!と表示されたらデプロイ完了です。
ターミナルに出力されたProject ConsoleのURLを開き、Functionsからデプロイした関数を確認できます。XCodeプロジェクト側の実装
SDKのインストール
CocoaPodsからFirebaseFunctionsをインストールします。
podfileに次の行を追記します。pod 'Firebase/Functions'追記後ターミナルで次のコマンドを入力し、インストールを実行します。
$ pod installサーバ日時取得処理の実装
この記事ではサンプルとして、アプリ起動時にサーバ日時を取得しログ出力するよう実装します。
ViewController.swiftに次のコードを記述します。ViewController.swiftimport FirebaseFunctions class ViewController: UIViewController { override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) let functions = Functions.functions() functions.httpsCallable("ServerTime").call { result, error in if let error = error { debugPrint(error.localizedDescription) } else { if let data = result?.data as? String { let ms = UInt64(data) ?? 0 let timeInterval = TimeInterval(ms) * 0.001 let date = Date(timeIntervalSince1970: timeInterval) debugPrint("サーバ日時: \(date.description(with: Locale.current))") } else { debugPrint("受信データなし") } } } } }実行し、次のようなログが出力されれば成功です。
"サーバ日時: Wednesday, July 10, 2019 21:02:43 Japan Standard Time"メモ
サーバ日時はミリ秒で得られる為、Date型に変換する際は0.001を掛けて秒に変換する必要があります。
- 投稿日:2019-07-10T20:16:33+09:00
Swift5.0.1でAtcoder!
すみません。気が早いです。
Atcoderの言語アップデートシートにSwift5.0.1を追加しておいたので,追加されてくれ...!!!
c++のコレはSwiftでこう書くってのをまとめます。随時更新
std:cin, scanf
これないです(草)
Swiftの場合,標準入力は
readLine()
のみになります。演算子を自作して配列をタプルで返却させることで同じような書き方ができます。追記:Stringにsplitがあった
import Foundation prefix operator * prefix func * <T> (a: [T]) -> (T, T) {return (a[0], a[1])} // let (a, b) = *readLine()!.components(separatedBy: " ").map({Int($0)!}) let (a, b) = *readLine()!.split(separator: " ").map({Int($0)}) print(a, b) // 入力:1 4 // 出力:1 43つのタプル版が欲しい時は,増やしましょう
prefix func * <T> (a: [T]) -> (T, T, T) {return (a[0], a[1], a[2])}
std:cout, printf
printf
みたいにフォーマットしたい時はString
を間に挟む必要があります。let hoge = 1, huga = 2 // std:cout print(hoge) // 出力:1\n // printf print(String(format: "%03d", hoge)) // 出力:001\n print(String(format: "%03d", hoge), terminator: "") // 出力:001 print(hoge, huga) // 出力:1 2 print(hoge, huga, separator: "!") // 出力:1!2
terminator: 最後にprintする文字列
,separator: 項目間にprintする文字列
を与えることができるので,改行なしや指定文字で区切る等ができます。
if, for, while
条件は
()
で囲まなくて良いが,コードブロックは{}
で必ず囲む// if if 条件 {処理} else if 条件 {処理} // while while 条件 {処理} // do-while repeat {処理} while 条件 // for for 定数 in 式 option(where 条件式) {処理} // 0から9を表示する for i in 0..<10 {print(i)}ちなみに範囲演算子
..<
は以上未満で,…
は以上以下です。
switch
swiftにもswich-caseがあります。
※breakが要りません
配列 Array
Swiftの配列
Array
は可変長なのでvector
みたいに使えます// 宣言 var a: [Int] = [1, 2, 4, 5, 0] var b: [Int] = [] var c = Array<Int>()どれでもいい
対応は下記の通り
vector<int> cpp(5, 0); var swift = [Int](repeating: 0, count: 5) cpp[n] swift[n] cpp.size() swift.count cpp.push_back(T) swift.append(T) cpp.pop_back() swift.removeLast() cpp.front() swift.first! cpp.back() swift.last! cpp.erase(cpp.begin()+n) swift.remove(n)詳しくは↓
https://developer.apple.com/documentation/swift/array
辞書 Dictionary
var a = ["Swift": 2014, "Objective-C": 1983] var b: [String: Int] = [:] var c = Dictionary<String, Int>()
a["key"]
のようにアクセスします。ただし返り値はオプショナルになります。詳しくは↓
https://developer.apple.com/documentation/swift/dictionary
pair
Swiftにはpairがないです。そのため,自前で実装するかタプルを使います。
例題:ABC131-D
これは締め切りが早い順に片付けるのが想定解です。Atcoderの解説PDFではc++でpairを使っています。
import Foundation prefix operator * prefix func * <T> (a: [T]) -> (T, T) {return (a[0], a[1])} func readInt() -> [Int] {return readLine()!.components(separatedBy: " ").map({Int($0)!})} func main() { let N = Int(readLine()!)! var tasks: [(Int, Int)] = []// タプルを使ってタスクを持つ for _ in 0..<N { tasks.append(*readInt()) } tasks.sort(by: {return $0.1 < $1.1}) // returnは無くてもいい var isYes = true var sum = 0 for (a, b) in tasks { sum += a if sum > b { isYes = false break; } } print(isYes ? "Yes" : "No") } main()タプルのソートをする場合,クロージャでどう入れ替えるかを渡す必要があります。
True になるようにソートされるので,この場合昇順です。
あ,サンプルしか動かしてないのでわかんないです。
おわり
とりあえずこれだけわかってればABC-Cまでは解けるんじゃないですかね?
- 投稿日:2019-07-10T18:26:00+09:00
iOS 13 の Core NFC で運転免許証を読み取ろう【TRETJapanNFCReader】
WWDC19 にて発表された iOS 13 の Core NFC にて、これまでの NDEF タグの読み書きの他に FeliCa を含む NFC の読み書きができるようになりました。
そこで私は日本の NFC、FeliCa カードの読み取りを行うためのライブラリとして、treastrain/TRETJapanNFCReader を開発しています。
開発途中のため、まだまだできることは少ないですが、少しでも iOS 13 における NFC の利用方法について知ってもらいたいと思い、この記事では運転免許証を読み取るサンプルを紹介させていただきたいと思います。
なお、記事中で紹介するコードは treastrain/TRETJapanNFCReader 0.0.5 の Examples にある DriversLicenseReader にすべて含まれています。
TRETJapanNFCReader
treastrain/TRETJapanNFCReader: 日本の NFC、FeliCa カード向けリーダーライブラリ(iOS 13.0 以降) - GitHub
- Suica、PASMOなどの交通系ICカード、運転免許証の読み取り
- 日本語・英語に対応
iOS 13.0 以降で使える、日本のNFCカード向けリーダーライブラリを 開発途中ではありますが GitHub に上げました。https://t.co/ARV6y0aWYe
— treastrain / とれいん (@treastrain) 2019年6月28日
いまは運転免許証をスキャンするところまで公開しております。
今日中には運転免許証の公開情報の読み込みまでをアップする予定です。様々な方から いいね、Star をいただいており、とてもうれしく思っています!
環境
- Xcode 11 beta 3
- NFC の読み取りが可能な iOS 13.0 端末(iOS 13.0 beta 3 の iPhone X で動作確認しています)
- 運転免許証(いわゆる ICカード免許証、NFC-B (Type-B))
- treastrain/TRETJapanNFCReader 0.0.5
残念ながら SwiftUI ではありません……。
GitHub からダウンロード
まずは GitHub からコード一式を持ってきてください。
Releases から Tag 0.0.5 をダウンロードすることをおすすめします。GitHub の Examples にあるサンプルは Capabilities や Info.plist の設定が既に済んでいます。README をご確認ください。
ライブラリのインポート
以降、
DriversLicenseReader
ディレクトリ内を操作します。
すでにサンプルのための ViewController が作成されていますが、ここでは新しくViewController.swift
を作成するものとして記事を進めます。
まずはライブラリのインポートから。Examples/DriversLicenseReader/ViewController.swiftimport UIKit import CoreNFC import TRETJapanNFCReader初期化とデリゲートの継承
運転免許証の読み取りを司る
DriversLicenseReader
を初期化します。
DriversLicenseReader
は初期化の際にDriversLicenseReaderSessionDelegate
かDriversLicenseReaderSessionDelegate
を継承したUIViewController
、DriversLicenseReaderViewController
を要求します。TRETJapanNFCReader/DriversLicense/DriversLicenseReader.swiftpublic typealias DriversLicenseReaderViewController = UIViewController & DriversLicenseReaderSessionDelegate
DriversLicenseReader(viewController: )
で初期化すると、エラーが発生した場合にライブラリ内で UIAlertController を作ってviewController.present
します。エラーのときの処理をすべて自前で行う場合はDriversLicenseReader(delegate: )
で初期化してください。Examples/DriversLicenseReader/ViewController.swiftimport UIKit import CoreNFC import TRETJapanNFCReader class ViewController: UIViewController, DriversLicenseReaderSessionDelegate { var reader: DriversLicenseReader! override func viewDidLoad() { super.viewDidLoad() self.reader = DriversLicenseReader(viewController: self) } }
DriversLicenseReader.get(items: )
を呼ぶ
DriversLicenseReader
の初期化が済んだら、あとはDriversLicenseReader.get(items: )
を呼ぶだけです。引数のitems
は[DriversLicenseCardItem]
を要求します。DriversLicenseCardItem
は、記事執筆時点で以下のとおりです。TRETJapanNFCReader/DriversLicense/DriversLicenseCard.swift/// 日本の運転免許証から読み取ることができるデータの種別 public enum DriversLicenseCardItem: CaseIterable { /// MF/EF01 共通データ要素 case commonData /// MF/EF02 暗証番号(PIN)設定 case pinSetting /// DF1/EF01 記載事項(本籍除く) case matters }ここで気をつけなければいけないのは、ICカード免許証の仕様で
共通データ要素
と暗証番号(PIN)設定
以外のデータの読み取りには、ICカード免許証の発行時に各自で設定した暗証番号が必要となることです。
ICカード運転免許証には暗証番号1と暗証番号2の2つがあり、本籍
を含むデータを読み取る場合は暗証番号1と暗証番号2の両方、それ以外のデータの場合は暗証番号1が必要となっています。
また、この暗証番号は3回間違えるとロックされ、以降は警察署などでロックを解除してもらわなければ、読み取りの際に暗証番号が必要なデータを読み取ることができなくなってしまいます。よって、
items
でcommonData
とpinSetting
を指定した場合は問題ありませんが、matters
が含まれている場合(DriversLicenseCardItem.allCases
とした場合なども同様)は暗証番号1も渡す必要があります。self.reader.get(items: [.commonData, .pinSetting]) self.reader.get(items: [.matters], pin1: "暗証番号1")一度
self.reader.get(items: [.pinSetting])
で ICカード免許証を読み取り、暗証番号が設定されているかどうかを確認することもできます。読み取った結果を確認する
DriversLicenseReader.get(items: )
を呼んだ後は iOS 端末が NFC の読み取りモードに入ります。iPhone の上部(受話部)を ICカード免許証の下半分を隠すように置いて、読み取ります。
読み取りが正しく成功すると、DriversLicenseReaderSessionDelegate
によりdriversLicenseReaderSession(didRead driversLicenseCard: DriversLicenseCard)
が呼ばれます。Examples/DriversLicenseReader/ViewController.swiftfunc driversLicenseReaderSession(didRead driversLicenseCard: DriversLicenseCard) { // driversLicenseCard に読み取った運転免許証の情報が格納されている } }
driversLicenseCard
に読み取った情報が入っているので、確認してみましょう。DriversLicenseCard
は TRETJapanNFCReader/DriversLicense/DriversLicenseCard.swift で定義されています。終わりに
以上の手順で簡単に運転免許証のデータを読み取ることができたかと思います。
例えば、いわゆる IDセルフィー と運転免許証の NFC 読み取りを組み合わせれば本人確認の手段として有効に活用できるのではないかなと思います。もしよろしければ GitHub のほうに Star していただけるとありがたいです!また、指摘や意見等もお待ちしています。
- 投稿日:2019-07-10T16:24:42+09:00
[WWDC19] Xcode11新機能まとめ
はじめに
WWDC19 報告会 at Yahoo! JAPANで発表したXcode11の新機能について文章でまとめた記事です。
NDAや著作権に配慮してスクリーンショットなどは載せておりません。エディタ
エディタUIの刷新
- コミット履歴が一番右のインスペクタに統合
- 真ん中の時計アイコン(Source Controll History表示)
- AssistantとAuthorsエディタをStandardエディタに統合
- Editor Optionから表示モードが選択できるように
- Editor Only(エディタのみ)
- Editor and Canvas(SwiftUIのプレビュー用)
- Editor and Assistant(従来のアシスタントエディタ)
- Authorの表示
- Editor Option > Show Authors
- エディタを縦横に追加可能
- 縦に追加する場合はoptionを押しながら
個々のエディタで個別の表示設定が可能
- エディタを縦横に分割可能
縦に追加する場合はoptionを押しながら
フォーカスモードで一つのエディタを一時的に拡大
SwiftUIのサポート
- プロジェクト作成時にUse SwiftUIにチェック
- プレビュー表示
- Editor Option > Editor and Canvas
Mini Map | ミニマップ
- ミニマップの表示
- Editor Option > Show Minimap
- すばやく任意のセクションにジャンプ
- ホバーで関数名などを表示
- find箇所のハイライト
inline code diffs | インラインコード差分
- change barをクリックしてShow Changeをクリック
- マウスホバーで差分を強調表示
ドキュメンテーション機能の強化
パラメータを追加した時に差分だけのコメントを追加
- command + クリック > Add Documentation
typoの修正もドキュメンテーションと同期
- command + クリック > Edit All in Scope
Spell Checking | スペルチェック
- スペルチェック機能がサポートされた
- 現在開いているファイルのチェック
- Edit > Format > Spelling and Grammer > Check Document Now
- command + ;
- タイピング時に自動でチェック
- Edit > Format > Spelling and Grammer > Check Spelling While Typing
- 赤線部分を右クリック
- スペル候補が表示される
- Ignore Spelling(スペルを無視)
- Learn Spelling(スペルを学習)
cherry-pick
- git cherry-pickコマンドがGUIで操作可能に
- 別ブランチでのコミットを反映できる機能
stash
- git stashコマンドがGUIで操作可能に
- commitしていない変更を一時退避できる機能
- メッセージも任意で追加可能
source control file inspector
- インスペクタから現在のブランチでの履歴を見られる
- StoryboardやXIBも含め、全ての種類のファイルで閲覧できる
パッケージ管理
Swift Package Manager
- Swift Package Managerの機能がXcodeに統合
- GitHub、Bitbucket、GitLab、または自分のホストで公開されているSwiftパッケージを利用できる
- 依存関係分析に基づいてパッケージを自動的に取得
- Swiftパッケージの作成方法(参考:Creating Swift Packages)
- Swiftパッケージの導入方法(参考:Adopting Swift Packages in Xcode)
XCFramework
- ライブラリ、フレームワークの新しいバイナリ配布形式(.xcframework)
- SwiftとC-basedコードをサポート
- 1つのフレームワークで複数のプラットフォームで動かせる
- 複数のフレームワークがアーキテクチャ毎のディレクトリに含まれる構成
- xcframeworkを作成
- ビルド設定のBuild Options > Build Libraries for Destiribution をYesにする
xcodebuild archive -sheme XXX -destination "xxx" -destination "xxx" SKIP_INSTALL=NO xcodebuild -create-xcframework -framework [path] -framework [path] -output XXX.xcframeworkデバッグ・テスト
Device Conditions
- デバイスの発熱状態、 通信状態をシミュレートできる機能
- Window > Device and Simurators > DEVICE CONDITIONS
- Thermal State (発熱状態のシミュレート)
- 選択候補
- Fair
- Serious
- Critical
- 本当に熱くなるわけではない
- Network Link(通信状態)
- 選択候補
- 100% packet loss
- Very poor network
- Edge Network - poor
- Edge Network - average
- Edge Network - good
- Edge Network - best
- 2G Network - poor
- 2G Network - better
- 3G Network - average
- 3G Network - good
- 3G Network - best
- LTE Network
- WiFi Network
- WiFi Network(802.11ac)
- DSL Network
- High Latency DNS
Environment Overrides
- デバイスの表示設定を実行時にリアルタイムで変更できる機能
- Interface Style(ダークモードかどうか)
- Dynamic Type(ダイナミックタイプ)
- Accesibility Options(アクセシビリティでの設定)
- Increse Contrast(コントラストを上げる)
- Bold Text(文字を太くする)
- Reduce Transparency(透明度を下げる)
- Reduce Motion(示唆効果を減らす)
- On/Off Labels(オン/オフラベル)
- Button Shapes(ボタンの形)
- Grayscale(グレイスケール)
- Smart Invert(色を反転)
- Differentiate Without Color(色なしで区別)
XCTest frameworkの新しいAPI
XCTUnwrap
- nilが入ったらassert(=XCTAssertNotNill)
- nilでない場合はUnwrapして値が取り出せる
- nilの場合throwされるので、テストケースの関数をthrowsにする
func testFirstNameNotEmpty() throws { let forenames: [String] = customer.forenames let firstName = try XCTUnwrap(forenames.first) XCTAssertFalse(firstName.isEmpty) }measure(metrics:options:block:)
- measure関数でメトリクス計測できる項目が増えてCPUやメモリの使用率なども計測できる
- https://developer.apple.com/documentation/xctest/xctestcase/3194265-measure`
- XCTMetricの継承クラス
- XCTClockMetric
- XCTOSSignpostMetric
- XCTCPUMetric
- XCTMemoryMetric
- XCTStorageMetric
func testExample() { // Measures the CPU and memory impact of sorting the input list. measure(metrics: [XCTCPUMetric(), XCTMemoryMetric()]) { sortedList = qsort(list: self.fiftyNumbersFrom0to100) } }Test Plan
- 異なる設定毎にでテストを複数回実行できる機能
- ローカリゼーション(言語、地域、位置情報)
- アルファベット順、ランダム順
- サニタイザー(Address、Thread、Undefined Behavior)
- メインスレッドチェッカー
- 引数もしくは環境変数
- Memory diagnostics(MallcStackLogging、NSZombies)
- スクリーンショットの撮影
- 一箇所にテスト変数を定義し、複数のスキーム間でシェア可能
- Test Planファイル(拡張子は.xctestplan)を作成(中身はJSON形式)
- Testsタブ
- テストケースの有効/無効のチェック
- 並列実行するか
- 新しいテストケースを自動で追加するか
- Configurationsタブ
- Shared Settings(すべてのテストで共通するデフォルト設定、個々のConfigurationで上書きも可能)
- Configuration(Configurationを作成した分だけテストが実行される)
- Option + クリックで選択したConfigurationのテストを1回だけ実行することも可能
- テストナビゲーターではcontroll + クリックで同様のことが可能
- 既存のTestスキームをTest Planをコンバートすることも可能
# -testPlanオプションを複数つけることで、Configurationを複数指定 xcodebuild test -project ... -scheme ... -testPlan 'Configuration1' -testPlan 'Configuration2'Result Bundle
- ビルドとテストの結果を含む新しいファイル形式(拡張子は.xcresult)
- ビルドログ
- テストレポート
- コードカバレッジレポート
- テストアタッチメント
- 特徴
- 従来のフォーマットに比べて容量が小さい(4倍小さい)
- Xcodeで内容を閲覧可能
- プログラムでアクセス可能なコンテンツ
- xcresulttoolでアクセスする
# Result Bundleの書き出しパス指定 xcodebuild test ... -resultBundlePath /path/ResultBundle.xcresult # 結果をJSONで取得 xcrun xcresulttool get --path ResultBundle.xcresult --format json # マークダウン形式 xcrun xcresulttool formatDescription get # コードカバレッジ xcrun xccov view --report ResultBundle.xcresult # コードカバレッジレポートの比較 xcrun xccov diff --json Before.xcresult After.xcresultその他の改善
Signing and Distribution | 署名と配布
- Signing & Capabilitiesタブにまとめられた
- Configuration毎の設定が可能
- 新しい証明書タイプ
- Apple Development、Apple Distribution
- すべてのプラットフォームをサポートする
- Xcode11以降で利用可能
- 既存の証明書も利用可能だが、旧バージョンのXcodeでは新しい証明書を利用できない
Asset Catalogの新機能
- ローカライズのサポート
- ダークモードのサポート
- Darkアピアランスの設定(Any、Light、Dark)
- SF Symbols
- Symbol Configurationからサイズと太さのバリエーションを設定
- 切り取り、コピー、貼り付けが可能に
Metrics Organizer
- Window > Organizer > Metrics
- リリース済みアプリのメトリクスデータを収集する機能
- Battery(バッテリー消費)
- Launch Time(起動時間)
- Hang Rate(ハング率|レスポンス)
- Memory(メモリ消費)
- Disk Writes(ディスク書き込み)
- ユーザーの許可設定が必要(設定 > プライバシー > 解析 > Appデベロッパと共有)
シミュレータ
- Metalサポート
- 60FPSで滑らかに描画
- CPUの利用は90%減
- Warm bootが二倍早く
- シミュレータでもWatch単体で起動できるように
参考資料
- Xcode Release Notes
- WWDC19 Video - What's New in Xcode 11
- WWDC19 Video - Getting Started with Xcode
- WWDC19 Video - Creating Swift Packages
- WWDC19 Video - Adopting Swift Packages in Xcode
- WWDC19 Video - Binary Frameworks in Swift
- WWDC19 Video - Debugging in Xcode 11
- WWDC19 Video - Creating Great Localized Experiences with Xcode 11
- WWDC19 Video - Testing in Xcode
- Xcode Help - About Metrics organizer
- Xcode Help - Share crash, energy, and metrics data with developers
- 投稿日:2019-07-10T11:18:26+09:00
PINRemoteImageのキャッシュに保存された画像をロードする方法
動機
PINRemoteImageManagerでキャッシュした画像を
UIActivityViewController
を使ってシェアしたかった。
そのときにどうやってキャッシュしたUIImageを取得するかわからなかったため。方法
PINRemoteImageManagerの
downloadImage
メソッドを使う
用意されたメソッドを使うだけだが意外と調べても出てこなかったのでメモ。guard let url = url else { return } _ = PINRemoteImageManager.shared().downloadImage(with: url) { (result) in // resultはPINRemoteImageManagerResult型でimageプロパティから画像が取得できる var activityItems: [Any] = [result.image] }こそっとリファレンスを見てみる
Summary
Download or retrieve from cache the image found at the url.
All completions are called on an arbitrary callback queue unless called on the main thread and the result is in the memory cache (this is an optimization to allow synchronous results for the UI when an object is cached in memory).日本語訳
URLで見つかったイメージをダウンロードするか、キャッシュから取得します。
すべての補完は、メイン・スレッドで呼び出され、その結果がmemory cache(これは、オブジェクトがメモリにキャッシュされたときのUIの同期結果を可能にする最適化です。)にない限り、任意のcallbackで呼び出されます。キャッシュがあればキャッシュから取得、無ければダウンロードしてくれる便利メソッドでした。
Pinterest様様ですね。Declaration
downloadImageWithURL:(nonnull NSURL *)url completion:(nullable PINRemoteImageManagerImageCompletion)completion;```
- 投稿日:2019-07-10T01:09:33+09:00
[SwiftUI]ButtonやNavigationLinkで囲んだ画像や文字が青色に塗りつぶされてしまったらrenderingModeを指定しよう
SwiftUI以前の開発環境でも同じような現象ありますね。一度や二度首を捻ったこともあるのではないでしょうか?
そう、画像なら、レンダリングモードを指定しないといけないやつですね。
青くなるコード例
NavigationLink(destination: Text("NextView")) { Image("imageString") // 青く塗りつぶされる Text("To Next") // 青色文字になる }という感じでNavigationLinkで囲むと、それまで通常通り表示されていた画像が青一色に、文字色も青色になってしまいます。
SwiftUIの場合は画像に.renderingMode(.original)
を指定すればOKです。
文字も色を指定しましょう。改善策
NavigationLink(destination: Text("NextView")) { Image("imageString") .renderingMode(.original) // templateではなくoriginalを指定 Text("To Next") .color(.primary) // 好きな色を指定 }現場からは以上です!