kazuki’s blog

新人エンジニアのブログ

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 ReplaySubject3種類あります。

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が許容されない場面で使用します。

まとめ

  • オプショナル型?nilを許容する場合、!は初期化時のみnilを許容する場合に使用する
  • 強制アンラップ!はオプショナル型?でもnilを許容しない場面で使用する
  • オプショナルバインディングnilでない場合とnilの処理を分ける為に使用する
  • オプショナルチェイニングは戻り値がオプショナル型になる為、強制アンラップ オプショナルバインディングを使用してアンラップする
  • オプショナル型!暗黙的アンラップと言い、使用時に自動的に強制アンラップされる