RxSwift紹介と基本の使い方
こんにちは、かずきです!
今回の記事で一旦Swiftはおしまいにしようかと思います。
そんでもってReactiveExtension(Rx)の布教記事ですw
プログラミング初心者にはオススメしません...
Reactive Extensionsとは
非同期処理で非常に扱いやすくするライブラリで、
主にIObservableインタフェースとIObserverインタフェースから成り立ちます。
学習コストは高いものの、他の言語のRxライブラリでもほぼ使い方が変わらないため学習効率は良いと思います。
Observerから発火される通知をメッセージと言い、メッセージ外部に流すのがObservableとなり、
通知を受け取る側IObservableを実装したクラスをSubscribe(購読)する事になります。
Observer -> Observable -> (Operator) -> Subscriber
の流れをストリームと言い、
Operatorはメッセージの加工や通知タイミングの変更・フィルタリング、ストリームの合成などを行えます。
Reactive Extensionsで出来る事
非同期
Observerからの通知をトリガーに動作しているので、当然の事ながら非同期を扱う事が出来ます。
時間
何秒後or毎に通知を送るObservable、Timer・Intervalが用意されていたり、
通知を遅らせるDelayや一定時間通知が無かった場合に購読を終了するTimeoutなどのOperatorがあります。
合成
複数のObservableから送られてくるストリームを合成し新しいストリームを作成する事が出来ます。
単純にストリーム合成したストリームを作成したいならMarge
それぞれのObservableから1つづつメッセージが流れ来た時に通知を受け取りるストリームを作成する場合はZipなど...etc
いくつか説明しましたが、他にも色々あるので残りは割愛。
ざっくり言うと通知を受け取って色々と処理を走らせる事がとても便利なライブラリって事ですw
スマホアプリなどではユーザの操作があった際に処理をしたいが多々あると思います。
そんな時に常に監視せずに実装できるのがReactive Extensionsって訳です。
(もちろんライブラリ内部では監視してるけど監視タイミングなどが集中管理されているので、個別監で視処理書くよりパフォーマンスが良買ったりする)
RxSwiftとは
Reactive ExtensionのSwift向けライブラリです(RxはC#(.Net)で最初に登場した)以上!
CocoaPodsの導入
RxSwiftをプロジェクトに追加するためには、CocoaPodsを利用します(他の方法もありますがコレが1番楽)
まずmacのLanchPadなどからターミナル
を開き次のコマンドを叩きます。
sudo gem install cocoapods
次にCocoaPodsセットアップのコマンドを叩きます。
pod setup
導入は以上!
RxSwiftの導入
まだターミナルからは逃れられません!
cd
でプロジェクトのディレクトリまで移動して次のコマンドを叩きます。
pod init
Podfileというファイルが出来ているはずなので編集します。
# Uncomment the next line to define a global platform for your project # platform :ios, '9.0' target 'プロジェクト名' do # Comment the next line if you're not using Swift and don't want to use dynamic frameworks use_frameworks! # Pods for ProjectName pod 'RxSwift' end
編集し終わったら、ターミナルでコマンドを叩きます。
pod install
これで導入終了!
CocoaPodsでインストールしたライブラリを利用する場合は拡張子が.xcworkspace
のファイルでプロジェクトを開いてください!!
プロジェクトでRxSwiftを利用する
RxSwiftを利用するソースコードの上部で
import RxSwift
と記述する事で利用できます。
ObserverとObservable両方の機能を持つSubject
IObserverインタフェースとIObservableインタフェースの両方を1つクラスで実装しているものをSubject
と言い、
メッセージの発火・購読の両方を1つのクラスで行う事が出来ます。
最初の内はSubjectを利用する事が多くなるかと思います。
SubjectはPublishSubject
BehaviourSubject
ReplaySubject
3種類あります。
PublishSubject
1番単純な機能のSubjectです。 購読を開始してから発火されたメッセージを受け取る事が出来ます。
var publishSubject = PublishSubject<T>() // Tはメッセージで送るデータの型
BehaviorSubject
購読開始時に購読直前に発火されたのメッセージの内容を受け取る事が出来ます。
(購読開始時にまだ何も発火されていない状態だった場合は初期値が通知されます)
var behaviorSubject = BehaviorSubject<T>(value: default) // defaultは初期値
ReplaySubject
BehaviorSubject
では購読開始時に購読直前のメッセージ内容を1つだけ受け取れましたが、
ReplaySubject
では指定したバッファサイズ分過去のメッセージを受け取る事ができ、
初期値の通知などはありません。
// ReplaySubjectのみcreateメソッドで作成する var replaySubject = ReplaySubject<T>.create(bufferSize: size)
RxCocoaとは
UIKitを扱うためのRxSwift向けの拡張ライブラリで、
ボタンのタップや、テキストフィールドへの入力内容の変化をメッセージとして受け取れます。
インストールはRxSwiftと同様CocoaPodsを利用して、インストールします。 Podfileを編集します。
# Uncomment the next line to define a global platform for your project # platform :ios, '9.0' target 'プロジェクト名' do # Comment the next line if you're not using Swift and don't want to use dynamic frameworks use_frameworks! # Pods for ProjectName pod 'RxSwift' pod 'RxCocoa' # <- これを追加する end
追加したので、コマンドを叩いてインストールする
pod install
以上で導入完了です!
Rx初心者にはとってRxCocoaはわかりやすく使いやすいものだと思いますので、
RxSwiftと一緒にRxCocoaも導入する事をオススメします!
まとめ
- Reactive Extensionは非同期とか時間とか扱える便利なヤツ!
- RxSwift・RxCocoaはCocoaPodsを使ってインストール
- RxCocoaはタップなどのUIのイベントを取って処理を書ける
これでSwiftに関する記事は一旦終了です。
ありがとうございましたm( )m
次回は備忘録的にUnityとかの記事書こうかなって思ってます!
SwiftのProtocol(プロトコル)とextension(エクステンション)
こんにちは!かずきです。
今回はProtocolとExtensionについて書いていこうと思います。
※今回の記事内のプロパティはコンピューテッド・プロパティ を指します。
Protocol(プロトコル)
Protocolとは
多言語ではよくインタフェースと呼ばれるものとほぼ同じものです。
継承したクラスで実装すべき、プロパティやメソッドを宣言する為に利用します。
継承先でのプロパティ・メソッドの存在保証
プロトコルで宣言したもは継承クラスで実装されていないとエラーが出るので、
継承クラスでのプロパティ・メソッドが利用可能な事が保証されます。
また、他のクラスと同じように継承先クラスは継承元であるプロトコルの型の変数で保持する事が出来ます。
実装例
protocol HogeHogeProtocol { var Fuga:String {get set} func FugaFuga() -> Void }
このようなプロトコルを用意して、
class HogeHogeHoge : HogeHogeProtocol { var fuga:String! // 実装しないとエラーになる var Fuga: String { get { return self.fuga } set { self.fuga = newValue } } // 実装しないとエラーになる func FugaFuga() -> Void{ print(self.Fuga) } }
このように継承・プロトコル実装します。
var hogehoge:HogeHogeProtocol = HogeHogeHoge() hogehoge.Fuga = "ほげほげ" hogehoge.FugaFuga() // ほげほげ
そうするとプロトコルで宣言されたメソッドを継承クラスで利用出来ます。
extension(エクステンション)
extensionとは
extension(拡張)の意味の通り、クラス・データ型・構造体などの機能を拡張する機能です。 プロパティとメソッドを実装する事ができます。
今回はデータ型とクラスの拡張を紹介します。
データ型の拡張
データを処理するプロパティ
このようなプロパティを書くと
extension Int { // パーセントから割合に変換 var Rate: Float { return (Float)(self) / 100.0 } } print(80.Rate) // 0.8
このようにデータ型に格納している値を処理追加する事が出来ます。
データ型そのものに機能を追加するプロパティ
プロパティの前にstatic
をつける事で、
extension Int { static var Zero : Int { return 0 } } print(Int.Zero) // 0
データ型そのものを拡張する事ができます。
データの処理をするメソッド
引数が必要な処理や保持しているデータ内容を変更したい場合などはプロパティでは実装出来ないので、メソッドを使って拡張します。
extension Int { func Square(x : Int) -> Int64 { var result:Int64 = Int64(self) for _ in 0...x { result = result * Int64(self) } return result } } print(12.Square(x: 10)) // 8916100448256
クラスの拡張
クラスの拡張もデータ型と同じように行えますが、
self
で取得できる物がデータでは無く、クラスのインスタンスになります。
※基本的にプロトコル実装以外での自作クラスの拡張は処理が分散して分かりにくくなるので、
クラス内に実装する事をおすすめします。(出番はライブラリなどの拡張のみ)
またエクステンションはクラス外の扱いになるので、private
の変数やメソッドにはアクセス出来ません。
// HogeHoge.swift class HogeHoge { private var hogeStr = "HogeHoge" }
// HogeHogeExtension.swift extension HogeHoge { public var HogeStr:String { get { return self.hogeStr // エラー } } }
プロトコル実装などをクラス外に書いて整理したい場合にも使えます。
この際、クラスとプロトコル実装のエクステンションを同一ファイル内に書く場合は
fileprivate
という同一ファイル内でのみアクセス可能なアクセス指定子を活用する事が出来ます。
extensionでプロトコル実装をする場合は以下のように記述します。
extension 元のクラス : 実装するプロトコル
fileprivate
を活用して実装するとこんな感じになります。
// HogeStrProperty.swift protocol HogeStrProperty { var HogeStr:String{ get } }
// HogeHoge.swift class HogeHoge { fileprivate let hogeStr = "HogeHoge" } extension HogeHoge:HogeStrProperty { public var HogeStr:String { get { return self.hogeStr } } }
これを他ファイルから呼び出すと
// FugaFuga.swift class FugaFuga { //---省略--- func PrintHogeHoge() -> Void { let hogeHoge = HogeHoge() print(hogeHoge.HogeStr) // HogeHoge } // ---省略--- }
HogeHogeクラスのhogeStrの内容が表示されます。
まとめ
プロトコルは動作(鳴く・動くetc)は同じだけど、
クラス毎にデータ(鳴き声・速度etc)が違ったり、
処理(動き方etc)など違う物を宣言しておき、
継承クラスで確実に利用したい場合に利用するエクステンションはデータ型にプロジェクト内でよく使う処理などの追加や、
ライブラリの拡張・プロトコル実装などに利用する
上述のように使うと便利かと思います。
他にこんな使い方あるよ
いう方。ぜひコメントなどで教えて下さい!!
Swiftのプロパティ監視willset/didset
こんにちは!かずきです。 今回はSwiftの特徴的機能の一つ、プロパティ監視のdidset/willsetについて書いていきたいと思います。 前回に続き、Swift入門者向けの記事です。
プロパティとは
private var hoge: Int = 0 public var Hoge: Int{ set{ self.hoge = newValue } get{ return self.hoge } }
読み書きそれぞれ別の範囲でのアクセス制限を掛けたい場合などのアクセス制御に使用するものです。
上のようにsetで囲んで値を設定するsetter
とget囲んで値を渡すgetter
の2種類があります。
class HogeHoge { private var hoge: Int = 0 public var Hoge: Int{ get{ return self.hoge } } }
このようなプロパティを持つクラスがあるとして、次のようにアクセスすると。。。
let hogehoge = HogeHoge() print("\(hogehoge.Hoge)") // "0" // エラー hogehoge.Hoge = 1
値の取得には成功しますが、代入は失敗してしまいます。
このように外部からは取得しか出来ず、書き換えさせない場合や複数の変数の計算結果などを返したい場合によく利用されます。
プロパティとプロパティ監視の違い
プロパティは変数のアクセス制御
などに使用するのに対し、プロパティ監視は変数の変更を監視
するものになります。
なぜ変数の監視
なのにプロパティ監視
なのかと言うと、Swiftでは変数もプロパティの1種
として扱われているからです。
先程説明したプロパティと変数としてのプロパティの違いは次に説明します。
2種類のプロパティ
Swiftにコンピューテッドプロパティ
とストアドプロパティ
の2種類のプロパティがあります。
コンピューテッドプロパティ
先ほど紹介したものはこちらです。
プロパティそのものには値を格納出来ません
。
よくプロパティ
と呼ばれるものはこちらになります。ストアドプロパティ
一般的に変数
と呼ばれるのもです。
getter
setter
は利用出来ませんが、 こちらのプロパティには値を格納出来ます
。
Swiftではこのように2種類のプロパティとして扱われています。
説明はしましたが、Swiftでは変数もプロパティの一種くらいの認識で大丈夫だと思います。
プロパティ監視(willset / didset)
プロパティ監視は先程説明した通り変数の変更を監視する
ものです。
willset
・・・変数の値の変更前
に通知されるdidset
・・・変数の値の変更後
に通知される
var hoge:Int = 0 { willSet(newValue){ hoge = 0 // 値は変わらない } didSet(oldValue){ if(hoge < oldValue) { print("元の値より小さい値は代入できません。\(oldValue) -> \(hoge)") hoge = oldValue // 値は変わる(この場合willset / didsetには通知されない) } } }
willset
で自身の変数に代入しても値は変化しません
が、didset
内で代入した場合は値が変化します
。
didset
内で自身の変数を変化させた場合willset
didset
には通知されません。
プロパティ監視の使い所
先程の説明で処理の場合、setter
でも同じ内容の事をする事が出来ます。
主なプロパティ監視の使いどころしては、値が変化された場合に特定のメソッドを呼ぶ
イベントで通知する
などです。
まとめ
ここまで説明してきましたが、基本的にwillset / didset
で出来る処理はgetter / setter
でも出来ます。
ですが。。。
読み書きそれぞれ
別の範囲でのアクセス制限
を掛けたい場合はgetter
setter
で処理を記述する。値が変更された場合に処理を行いたい場合
willset
didset
で処理を記述する。
このような使い分けで利用する事により、
変数(ストアドプロパティ)プロパティ(コンピューテッドプロパティ)両方にアクセス可能な場所で
setterに必要な処理
を書いているので変数に代入
してしまって正しい処理がされないなどのリスクを回避する事が出来ます。
他にも使い分け方があれば是非教えて下さい!
Swiftの!と?の違いと使い方
初めまして! かずきです。
この頃Unityばかり触っているので、Swiftを忘れかけている。。。 という事でSwift入門者向けの記事を何回に分けて書いていこうと思います!
オプショナル型とは
オプショナル型は変数宣言時にデータ型名の後ろに?
をつけることで利用出来るnil許容型
です。
var hoge : Int? var hogehoge : int!
!
については後ほど説明致します。
オプショナル型のアンラップ
そのままオプショナル型の変数を利用しようとするとOptional()
に囲まれた状態で中に入った値を利用できません。
var hoge : Int? = 114514 print(hoge) // => Optional(114514)
強制アンラップ
名前の通り強制的にオプショナル型をアンラップする方法です。
オプショナル型変数の値がnil
の場合エラー
が発生します。
var hogehoge : String? = nil print(hogehoge!) // エラー hogehoge = "屋上あんだけど焼いてかない?" print(hogehoge!) // => 屋上あんだけど焼いてかない?
nilを許容しない
場面でアンラップする際に使用します。
オプショナルバインディング
オプショナルバインディングは、if文などの条件式として利用します。
オプショナル型変数の値がnil以外
場合、条件式左辺で宣言した変数にアンラップされた値が代入されます。
if let unwrapfuga = fuga { // fugaが`nil以外`の場合(アンラップされた値がunwrapfugaに代入されている) } else { // fugaがnilの場合(unwrapfugaは利用不可) }
nilを許容する
場面で、変数の値がnilでない
場合のみ処理を行いたい場合などに使用します。
オプショナルチェイニング
オプショナル型変数に含まれるメソッドやプロパティを利用する際に使用します。
オプショナルチェイニングを使用してメソッドやプロパティにアクセスした場合、戻り値は全てオプショナル型
となります。
※例えばこのようなクラスがあったとします。
class HogeHoge{ private var name : String = "yajuu" public var Name : String { get { return self.name } } public func IceTea() -> String { return "おまたせ!アイスティーしかなかったけどいいかな?" } }
変数hogehgeに値が入っている場合はオプショナル型の戻り値が帰ってきます。
var hogehoge : HogeHoge? = HogeHoge() print(hogehoge?.Name) // => Optional("yajuu") print(hogehoge?.IceTea()) // => Optional("おまたせ!アイスティーしかなかったけどいいかな?")
変数hogehogeがnil
の場合は戻り値もnil
になります。
var hogehoge : HogeHoge? = nil print(hogehoge?.Name) // => nil print(hogehoge?.IceTea()) // => nil
戻り値を強制アンラップ
かオプショナルバインディング
して使用します。
暗黙的アンラップ型
暗黙的アンラップ型はオプショナル型の一種で、使用時に自動で強制アンラップを行います。
// オプショナル型 var a : Int? = 10 var b : Int = 20 var c = a! + b // cc = 30 var a : Int? = 10 var b : Int = 20 var c = a + b // エラー // 暗黙的アンラップ型 var aa : Int! = 10 var bb : Int = 20 var cc = aa + bb // 自動的に強制アンラップされるので、ccは30になる
主に初期化時以外でのnilが許容されない場面で使用します。