- 投稿日:2019-08-21T23:29:21+09:00
Swiftlintで警告がでないようにする
Swiftlintを導入すると、いろいろと警告を出してもらえるので便利な反面、エラーが出すぎるとエディタの可視性が悪くなります。
そこで警告を消す方法です。// swiftlint: disable type-body-length
- 投稿日:2019-08-21T21:04:23+09:00
ドキュメントはリリースノートもしっかり見たほうがいいよって話
経緯
初めて自作iOSアプリを作っていて、公式のドキュメントにしたがってGoogleログインを実装しているときに詰まったので備忘録。
Value of type 'GIDSignIn?' has no member 'presentViewController'ただSign inボタンを足したいだけだったのに
https://developers.google.com/identity/sign-in/ios/sign-in?ver=swift
Google先生の言いなりになって、
viewDidLoad()
内に以下のメソッドを追加しました。LoginViewController.swiftoverride func viewDidLoad() { super.viewDidLoad() GIDSignIn.sharedInstance()?.presentingViewController = self // Automatically sign in the user. GIDSignIn.sharedInstance()?.restorePreviousSignIn() // ... }怒られました
ググった
検索結果: 2件
GIDSignIn
このクラスを探してみても、
presentingViewController
はあるみたいですが、出てこず。
https://developers.google.com/identity/sign-in/ios/reference/Classes/GIDSignIn
ふと思った
あれ、そもそもなんで必要なんだっけ?
GIDSignIn.sharedInstance()?.presentingViewController = self // Automatically sign in the user. GIDSignIn.sharedInstance()?.restorePreviousSignIn()
![]()
一旦無視することにした
使われていないんですかね。
一旦このまま進めることにします。追記
見つけてしまった。
このメソッドは廃止されたようでした?
https://developers.google.com/identity/sign-in/ios/release
- 投稿日:2019-08-21T21:04:23+09:00
Value of type 'GIDSignIn?' has no member 'presentViewController'
経緯
初めて自作iOSアプリを作っていて、公式のドキュメントにしたがってGoogleログインを実装しているときに詰まったので備忘録。
ただSign inボタンを足したいだけだったのに
https://developers.google.com/identity/sign-in/ios/sign-in?ver=swift
Google先生の言いなりになって、
viewDidLoad()
内に以下のメソッドを追加しました。LoginViewController.swiftoverride func viewDidLoad() { super.viewDidLoad() GIDSignIn.sharedInstance()?.presentingViewController = self // Automatically sign in the user. GIDSignIn.sharedInstance()?.restorePreviousSignIn() // ... }怒られました
ググった
検索結果: 2件
GIDSignIn
このクラスを探してみても、
presentingViewController
はあるみたいですが、出てこず。
https://developers.google.com/identity/sign-in/ios/reference/Classes/GIDSignIn
ふと思った
あれ、そもそもなんで必要なんだっけ?
GIDSignIn.sharedInstance()?.presentingViewController = self // Automatically sign in the user. GIDSignIn.sharedInstance()?.restorePreviousSignIn()
![]()
一旦無視することにした
使われていないんですかね。
一旦このまま進めることにします。追記
見つけてしまった。
https://developers.google.com/identity/sign-in/ios/release
- 投稿日:2019-08-21T19:14:55+09:00
Xcodeを消してから入れ直したらSimulator(シミュレータ)が消えた
Xcodeを入れ直した
私はAppleのサイトから直接Xcodeをダウンロードしていた為、更新が楽な様にAppleStoreの方からダウンロードをし直しました。その後、ビルドしてSimulator(シミュレータ)を起動しようとしたらデバイスが一つも見当たらなかったのです。
Simulator(シミュレータ)が入ってない
結果としては、新しいXcodeの方にSimulator(シミュレータ)が入ってなかったのが原因でした。入れ直す方法は、Xcodeを起動し、Xcode→Preferences→Componentsとクリックしていき、ダウンロードしたいSimulator(シミュレータ)のバージョンをクリックするだけです。
ダウンロード後
Xcode側のiosのバージョンを二箇所、ダウンロードしたSimulator(シミュレータ)のバージョンに合わせれば無事に反映されます。
左上にiPhoneXsが反映されています。
ちなみに、アップデート後は変なエラーが出る可能性があるので、macを再起動した方が良いです
- 投稿日:2019-08-21T17:09:01+09:00
[Swift5]Codableソースコード解析①
Codableから説明
関連するファイルは二つあります:
- gybにユーザーに公開しているprotocolおよびCodabelを実現しているSwiftのタイプ
- JSONEncoder.swift。JSONEncoderとJSONDecoderを実現しているファイルです。
続きましては、Codableの定義はここです:
qiita.swiftpublic typealias Codable = Encodable & DecodableEncodable
Encodableのタイプはなんでしょうか?実際、
encode(to:)
メソッドを提供しているだけです。qiita.swiftpublic protocol Encodable { /// Encodes this value into the given encoder. /// /// If the value fails to encode anything, `encoder` will encode an empty /// keyed container in its place. /// /// This function throws an error if any values are invalid for the given /// encoder's format. /// /// - Parameter encoder: The encoder to write data to. func encode(to encoder: Encoder) throws }ここまで、三つの質問を思い出すと:
1.SwiftがサポートしているEncodableのタイプは全部このメソッドを実現していますか?
2.SwiftではデフォルトでEncodableをサポートしているメソッドはなんでしょうか?
3.Encoderは何ですか?質問1の答えはイエスです。Codable.swift.gybファイルにデフォルトタイプの実現があります。
例えば、Intのデフォルト実現は:qiita.swiftextension Int : Codable { public func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() try container.encode(self) } }Arrayのデフォルト実現は:
qiita.swiftextension Array : Encodable where Element : Encodable { public func encode(to encoder: Encoder) throws { var container = encoder.unkeyedContainer() for element in self { try container.encode(element) } } }なるほど、Swiftでは本当にEncodableのタイプはメソッドを実現しています。
そしたら、Swiftでは、Encodableをサポートしているタイプはどれくらいありますか?
Codable.swift.gybファイルに下記のコードがあります。qiita.swift%{ codable_types = ['Bool', 'String', 'Double', 'Float', 'Int', 'Int8', 'Int16', 'Int32', 'Int64', 'UInt', 'UInt8', 'UInt16', 'UInt32', 'UInt64'] }%上記以外、ファイルの一番下に、Array / Set / Dictionary / Optionalなどもあります。
ここまで、質問3だけ残ってます。
encodeメソッドのEncoderはなんでしょうかね。ブラックボックスとして考えれば、結果的にSwiftのオブジェクトをJSON文字列にエンコディングされています。JSONEncoder
JSONEncodeの定義:
- JSONエンコーディング結果の出力フォマット(public struct OutputFormatting)
- Dateタイプのエンコーディング方式(public enum DateEncodingStrategy)
- Dataタイプのエンコーディング方式(public enum DataEncodingStrategy)
- 例外的な浮動小数点エンコーディング方式(public enum NonConformingFloatEncodingStrategy)
- JSONのkeyのエンコーディング方式(public enum KeyEncodingStrategy)
続きましては、JSONEncoderを使用してエンコーディングする時、デフォルト使用するプロパティ:
qiita.swiftopen class JSONEncoder { open var outputFormatting: OutputFormatting = [] open var dateEncodingStrategy: DateEncodingStrategy = .deferredToDate open var dataEncodingStrategy: DataEncodingStrategy = .base64 open var nonConformingFloatEncodingStrategy: NonConformingFloatEncodingStrategy = .throw open var keyEncodingStrategy: KeyEncodingStrategy = .useDefaultKeys }デフォルトのプロパティを便利に使う為、内部タイプの_Optionsと内部プロパティのoptionsも定義されています。
qiita.swiftopen class JSONEncoder { fileprivate struct _Options { let dateEncodingStrategy: DateEncodingStrategy let dataEncodingStrategy: DataEncodingStrategy let nonConformingFloatEncodingStrategy: NonConformingFloatEncodingStrategy let keyEncodingStrategy: KeyEncodingStrategy let userInfo: [CodingUserInfoKey : Any] } fileprivate var options: _Options { return _Options(dateEncodingStrategy: dateEncodingStrategy, dataEncodingStrategy: dataEncodingStrategy, nonConformingFloatEncodingStrategy: nonConformingFloatEncodingStrategy, keyEncodingStrategy: keyEncodingStrategy, userInfo: userInfo) } }そして、JSONEncoderのコンストラクタ(only one):
qiita.swiftopen class JSONEncoder { public init() {} }最後は、我々が使用するencodeメソッド:
qiita.swiftopen func encode<T : Encodable>(_ value: T) throws -> Data__JSONEncoder
これから、JSONEncoder.encodeメソッドの実現から見てみましょう。関数定義はここです。
実行ロジック:qiita.swiftopen func encode<T : Encodable>(_ value: T) throws -> Data { let encoder = __JSONEncoder(options: self.options) guard let topLevel = try encoder.box_(value) else { /// throw exception } /// Handle invalid box value. let writingOptions = JSONSerialization.WritingOptions( rawValue: self.outputFormatting.rawValue) do { return try JSONSerialization.data( withJSONObject: topLevel, options: writingOptions) } catch { /// throw exception } }encodeの実現:
- まず、Encoderタイプの__JSONEncoderオブジェクトを作る
- そして、box_メソッドでパラメーターvalueをJSONエンコーディングできるデーターに変換する
- エラーチェックなどされたら、FoundationのJSONSerialization.dataを呼び出し、データーのエンコーディングできて、エンコーディング結果のDataオブジェクトをreturn
- 投稿日:2019-08-21T15:55:09+09:00
ウェブフロントエンドエンジニアがiOSアプリ開発をする際に知っておきたかったこと
はじめに
この記事はふだんウェブフロントエンド(javascript/html/css)の業務をしているエンジニアが、iOS開発の予習に取り組んだ際に躓いた点を共有し、これからiOS開発を始めたいエンジニアの助けになることを目的としたものです。
XcodeのバージョンとSwiftのバージョン
当然これらは固定して開発に望みたいので、以下の記事を参考にしました。
http://kimagureneet.hatenablog.com/entry/2019/04/08/125129
プロジェクト内のファイルツリーと実ファイルの関係性
プロジェクトの左側のファイルツリーにある黄色と、青色のフォルダアイコンに困惑しました。
http://kurogomapurin.hatenablog.com/entry/2014/06/05/012415
https://qiita.com/nmbakfm/items/8f2e303ee4ec71640008
https://qiita.com/yimajo/items/6cffb5cd5a5dd659edb4UI実装/レイアウト
ストーリーボードを分割する
ご存知ストーリーボード、このファイルの実体(foo.storyboard)はxmlファイルであり、Xcode上の操作が即座にこのxmlファイルに反映されます。
しかしこのファイルはヒューマンリーダブルとはなかなか言いにくいものです。
開発はGitでの管理下で行っていると思いますので、ストーリーボードのファイルが大きくなると目が潰れそうな差分が多く発生します。
そのため、ストーリーボードは初期の段階から適切な大きさに分割しておくのが良いでしょう。
私が開発に参加したアプリは一般的なタブビューのレイアウトだったので、初期の段階でタブごとにストーリーボードを分割しました。https://qiita.com/Simmon/items/2af23fab4a42ab7247ba
UINavigatorController
一般的なアプリによくある、一覧画面->詳細画面に遷移するデザインは、UINavigatorController と TableViewなどで実装します。
UINavigatorControllerを利用する場合、ストーリーボード上でコンテンツとなるviewControllerにembedする操作を行うのですが、この際のストーリーボード上での配置が直感的ではなく、最初困惑しました。
並列に配置されているこれらのviewControllerは
A navigation controller is a container view controller—that is, it embeds the content of other view controllers inside of itself.
https://developer.apple.com/documentation/uikit/uinavigationcontroller
ということで、実際には左側のNavigation Controllerはコンテナの役割を果たしているのですね。
カスタムビューの実装
ストーリーボード上のUI開発で最初に面を食らったポイントは、UIViewと言われる汎用のUIクラスはborderなど、レイアウトにかかせない要素をリアルタイムに確認する術を持たないことです。
webでUIを作る際にはcssでborder: 1px solid black
などと書いていけばカンタンに進められる作業ももう少し長くコードを書いて実装する必要があります。extentionを書いておくとストーリーボードでも確認できるようになるので、最初のうちに勉強しておくとあとがラクになります。
https://qiita.com/taji-taji/items/17ad3389b3d250fcad6e
https://qiita.com/xxminamixx/items/ee8435a4e07d31cf28fdベクターイメージ
PDF!!!
svgじゃなく!!!
PDF使うのか!!!コードとの接続
アクション/アウトレットについて
https://swift-ios.keicode.com/ios/action.php
https://swift-ios.keicode.com/ios/outlet.phpなお接続は適切に外さないと、ビルド時にエラーが起きます。
https://pg-happy.jp/xcode-delete-action-outlet-connection.html
https://qiita.com/Atsushi_/items/f7930dd00a2c2ea464cdUIStackView
cssのブロックモデルに近い感覚でレイアウトできるのがUIstackviewです。
このClassが提供されてから、レイアウトの実装はだいぶラクになっただろうとたやすく想像できる非常に便利なClassです。
積極的に使いましょう。https://qiita.com/taka1068/items/69273f05d34cfbeb3679
autoLayout
ストーリーボードでのレイアウトで、意外と気に入ったのがautoLayoutです。
触ってて嬉しかったのが、重複する制約に優先度をつけられること。cssのイメージでいうと、!important
に優先度をつけられるかんじ。1日さわっていれば慣れます。
https://qiita.com/_ha1f/items/5c292bb6a4617da60d4f
autoLayoutだけでもこういうのができる
https://blog.kishikawakatsumi.com/entry/2018/11/05/044228ちなみにautoLayoutの制約もOutletでコードと関連づけられるので、状態に応じて制御したいときに重宝します。
https://nackpan.net/blog/2015/08/16/autolayout-constraints-can-connect-code/segueによる画面遷移
https://qiita.com/fromage-blanc/items/b3cb0e7833a1d5659463
ロジック(Swift)
JSやTSでの開発と違って面白いなーと思った点
delegate
https://qiita.com/mochizukikotaro/items/a5bc60d92aa2d6fe52ca
https://qiita.com/narukun/items/326bd50a78cf34371169関数のオーバーロード
https://wp-p.info/tpl_rep.php?cat=swift-biginner&fl=r23
Optional型の安全なunwrap
https://qiita.com/maiki055/items/b24378a3707bd35a31a8
https://qiita.com/mokumoku/items/5fef496f4e97a053e53e外部ライブラリの導入
ビルド速度の低下のことを考えると、carthageで配布されているものはそちらを選んだほうが良さそうです。
cocoaPods
https://qiita.com/ShinokiRyosei/items/3090290cb72434852460
carthage
https://qiita.com/yutat93/items/97fe9bc2bf2e97da7ec1
細かな点をあげればきりがないですが、勉強を始める前に自分が知りたかったポイントとしてはこんなところでしょうか。
iOS開発ではUIKitの強力さとSwiftの表現力の高さに感銘を受けました。
ふだんやっているwebアプリ開発でもUIKitくらい強力なファウンデーションがあるとラクなんですけどねえ。
- 投稿日:2019-08-21T15:33:12+09:00
Fluxアーキテクチャのメモ
この記事は
Swift
ベースで話を進めています。
iOSを普段書いている方には記事を呼んで頂きたいですが、
それ以外が主戦場の方は別の記事でFluxについての理解を深めることを推奨します。Fluxとは
Facebookが作成したアーキテクチャパターンでJSの文化から輸入してきた物です。
(そもそもClientの設計パターンはほとんどJSの文化で生まれた物ばかりです)下の画像からわかることとして
Action
->Dispatcher
->Store
->View
のフローがあって、
その後、
View
->Action
->Dispatcher
のフローが存在します。
これによって一つのライフサイクルが作成されていることがわかります。
Fluxの設計パターンの最大の特徴はこの
Action
->Dispatcher
->Store
->View
の単一方法のデータの受け渡しが行われていることにあります。逆に言えばこのパターンが逸脱したいと思った時はFluxを採用することをやめるべきとも言えます。(そのときはMVC,MVVM,Redux等を利用することを検討することになると思います)他の設計パターンと比較する
すべてのアーキテクチャとの比較をすることは
めんどくさい時間がかかってしまうので今回はいくつかある設計パターンのうち、その中でも似ているMVVM(Model,View,ViewModel)と比較することにしましょう。
このとき、最大の違いはViewModel
をDispatcher/Store
に書き換えてみるとわかります。
図をみてわかる通り、FluxとMVVMの最大の違いは
View
が一つ前の層であるViewModel
をみることで変更をみることができるのがMVVM
View
がみることができるのは2つ前のDispatcher
(StoreとActionの中継役)をみるのがFluxです。なのでFluxでは値をもらう
Store
が中で何をしているのか全く知らない状態になるという意味です。これはMVVMに比べてFluxの方が制約が厳しいという意味に直結します。各役割の説明
なんとなく雰囲気でわかったものの、各役職がどんなことを実際に説明していなかったので説明します。
Action
最終的にView
に渡したいdata
を保持している場所。
Dispatcher
Action
とStore
の中継場所。
Viewで入力された値とかはここが受け取ることで再度Store
に渡して
View
に反映したりする。
Store
値の状態を管理する場所。
View
UIViewController
やUIView
がこれにあたる。UIViewController
の場合、ViewController
としてではなく、なるべくController
としての役割を剥いで、View
として利用する。メリット・デメリット
メリット
- 単一方向であることを制約に掲げているのでデータフローが役割ごとに分けられることで理解しやすくなる。
- MVCの設計よりもUIViewController
から状態管理のためのコードが減ってViewController
としてではなく、Viewとしての役割に近い状態になるデメリット
- そもそもFluxを採用するほどの規模のアプリは少ない。(小中規模でこれほどの制約は返って開発コストが高くつきそう)今回はこれで以上です。サンプルコードを追加した上での説明は今回は省きます(追加する予定はあります)
- 投稿日:2019-08-21T12:13:27+09:00
CombineでObservable.create/SignalProducer.initに相当するPublisherを作る
Combine には当初、クロージャを通じて複数の値の出力と成否を送る Publisher (RxSwift における
Observable.create
または ReactiveSwift におけるSignalProducer.init
) が実装されていたようなのですが、途中で削除されてしまったらしい...
どうしても使いたい場面があったため、Combineの学習がてら自分で実装してみました。こちらの投稿を参照にさせていただきました?♂️
https://qiita.com/shiz/items/58abf44b77d9da2042f1SomePublisher.swiftimport Foundation import Combine // 名前は適当です... struct SomePublisher<Output, Failure: Swift.Error>: Publisher { enum Event { case value(Output) case finished case failure(Failure) } typealias Handler = (Event) -> Void private let handler: (@escaping Handler) -> Void private class Subscription<S: Subscriber>: Combine.Subscription where S.Input == Output, S.Failure == Failure { private var subscriber: S? private let handler: (@escaping Handler) -> Void init(subscriber: S, handler: @escaping (@escaping Handler) -> Void) { self.subscriber = subscriber self.handler = handler } func request(_ demand: Subscribers.Demand) { self.handler(self.handle) } func cancel() { self.subscriber = nil } private func handle(_ event: Event) { guard let subscriber = self.subscriber else { return } switch event { case .value(let input): _ = subscriber.receive(input) case .finished: subscriber.receive(completion: .finished) case .failure(let error): subscriber.receive(completion: .failure(error)) } } } init(_ handler: @escaping (@escaping Handler) -> Void) { self.handler = handler } func receive<S>(subscriber: S) where S : Subscriber, S.Failure == Failure, S.Input == Output { let subscription = Subscription(subscriber: subscriber, handler: self.handler) subscriber.receive(subscription: subscription) } }使い方はこんな感じ。
func values(count: Int) -> SomePublisher<Int, Never> { return .init { (publisher) in // ここにストリームを実装する... for i in 0..<count { publisher(.value(i)) } publisher(.finished) } }本当はイニシャライザを以下のような仕様にしたかったのですが、
Subscriber
の管理と受け渡しがうまく実装できなかったので、Event
型を使ってストリームする実装にしています。init<S>(_ handler: @escaping (S) -> Void) where S : Subscriber, S.Failure == Failure, S.Input == Output { ... }Combineに実装が復活するといいですね。
- 投稿日:2019-08-21T00:53:55+09:00
Class 'hogeClass' has no initializers って出た時の対処法[Swift]
原因
classっていちいち
init()
せなダメだっけ?と思っていたところ原因はこれだった定義してた変数がオプショナル型でなかった
→nil
を許して!→初期化して! ということらしい対処
オプショナル型にする(
hogehogeDelegate
→hogehogeDelegate?
)class hogeClass { private var delegate: hogehogeDelegate? //private var delegate: hogehogeDelegate だとエラー出る }参考
・swift初心者:「Class 'ViewController' has no initializers」の対処方法 - Qiita
- 投稿日:2019-08-21T00:06:32+09:00
NSRangeからSwiftのRangeへの置換方法
はじめに
NSRangeはlocation(位置)とlength(長さ)の2つを持つ、範囲を表す構造体です。
SwiftではRangeとして範囲の扱いについて再定義されています。
NSを外せば置き換えられません。別物といってもいいくらいです。対比表
とりあえずNSRangeからRangeに置き換えたいときは、この対比表で置き換えてください。
NSRange Range 開始位置 location startIndex 長さ length count NSRange
文字列から取得する方法
NSString *text = @"つれづれなるままに"; NSRange rangeTest = [text rangeOfString:@"ままに"];また、
NSMakeRange
で作成することもできます。NSRange range = NSMakeRange(3, 5);参照
NSRange - Foundation | Apple Developer Documentation
Range - Swift Standard Library | Apple Developer DocumentationNSRangeの使い方 - Object for cutie
swift - How to convert Range in NSRange? - Stack Overflow
[Swift2]Stringの Range と NSRange の変換 : 永遠日誌