タコさんブログ

プログラミングメモと小言

RxSwift 入門 その4

Filtering Observables

RxSwift 入門 その3 - タコさんブログ の続き。
今回は、RxSwiftプレイグラウンドの Filtering Observables の項。
この項では、ソースObservableから選択的にアイテム(イベント)を送信するオペレータに関して説明してある。

以下、プレイグラウンドで説明してあるFilteringに関するオペレータ。

  • filter
  • distinctUntilChanged
  • take

RxSwiftには、この他にも以下のオペレータが利用できる:

  • debounce
  • elementAt
  • ignoreElements
  • sample
  • skip
  • takeLast

filter

filterオペレータは、述語が真となるアイテムをObservableから送信する。

let stream = (1..<10).toObservable()
_ = stream
  .filter {
    $0 % 2 == 0  // 偶数をフィルタ
  }
  .subscribe {
    print($0)
  }

この出力は、

Next(2)
Next(4)
Next(6)
Next(8)
Completed

このマーブルダイアグラムは以下のように表せられる。

-1-2-3- ... -8-9|
↓ filter { x % 2 == 0 }
---2--- ... -8--|

distinctUntilChanged

distinctUntilChangedオペレータは、Observableによって送信される重複するアイテムを除去する。

let stream = [1, 2, 3, 1, 1, 4].toObservable()
_ = stream
  .distinctUntilChanged()
  .subscribe {
    print($0)
  }

この出力は、

Next(1)
Next(2)
Next(3)
Next(1)
Next(4)

このマーブルダイアグラムは以下のように表せられる。

-1-2-3-1-1-4|
↓ distinctUntilChanged
-1-2-3-1---4|

take

takeオペレータは、Observableによって送信される最初のn個のアイテムのみを送信する。

let stream = [1, 2, 3, 1, 1, 4].toObservable()
_ = stream
  .take(2)
  .subscribe {
    print($0)
  }

この出力は、

Next(1)
Next(2)
Completed

このマーブルダイアグラムは以下のように表せられる。

-1-2-3-1-1-4|
↓ take(2)
-1-2|

参考URL

RxSwift 入門 その3

Transforming Observables

RxSwift 入門 その2 - タコさんブログの続き。
今回は、RxSwiftプレイグラウンドの Transforming Observables の項。
この項では、Observableから送信されるアイテム(イベント)を変換するオペレータについて説明してある。

変換に関するオペレータ

以下、プレイグラウンドに説明してある変換に関するオペレータ。

  • map
  • flatMap
  • scan

この他にも、以下の変換に関するオペレータがある。

  • buffer
  • window

map (a.k.a. select)

mapオペレータはObservableによって送信された各アイテム(イベント)に関数を適用することによって、アイテムを変換する。
RxJavaではSelectになっている。

下の例では、map で Int -> UnicodeScalar に変換している。

// ストリームを生成
let stream = (65..<70).toObservable()
_ = stream
  .map { x in
    UnicodeScalar(x)
  }
  .subscribe {
    print($0)
  }

この出力は、

Next(A)
Next(B)
Next(C)
Next(D)
Next(E)
Completed

このマーブルダイアグラムは以下のように表せられる。

---6---6---6---6---6|
   5   6   7   8   9|
   ↓ map
---A---B---C---D---E|

ここで、

---65---66---67---68---69|

の数字の二桁めを以下のように2段めに記述した。

---6---6---6---6---6|
   5   6   7   8   9|

flatMap

Observableによって送信されたアイテム(イベント)をObservablesに変換し、その後、それらから送信されるアイテムを1つのObservableへ変換(フラットに)する。

下の例では、1〜29のシーケンス値をAのシーケンス、Bのシーケンス、...とZまで繰り返し、27以降は空のシーケンスに変換し、これらを1つのA〜Zのシーケンスに変換している。

let stream = (1..<30).toObservable()
_ = stream
  .flatMap { (x: Int) -> Observable<UnicodeScalar> in
    if case (1..<27) = x {
      return Observable.just(UnicodeScalar(x + 64))
    } else {
      return Observable.empty()
    }
  }
  .subscribe {
    print($0)
}

この出力は、

Next(A)
Next(B)
Next(C)
Next(D)
... // 略
Next(X)
Next(Y)
Next(Z)
Completed

このマーブルダイアグラムは以下のように表せられる。

-1-2 ... -2-2-2-2|
          6 7 8 9|
 A|
  B|
...
          Z|
            |
              |
                |
↓ flatMap
-A-B- ... -Z----|

scan

Observableによって送信された各アイテム(イベント)に関数を逐次適用し、そして、それぞれの連続値を送信する。

下の例では、0をアキュムレータの初期値とし、0〜3までの値を足している。

let stream = Observable.of(0, 1, 2, 3)
_ = stream
  .scan(0) { acum, elem in
    acum + elem
  }
  .subscribe {
    print($0)
}

この出力は、

Next(0)
Next(1)
Next(3)
Next(6)
Completed

このマーブルダイアグラムは以下のように表せられる。

-0-1-2-3|
↓ scan
-0-1-3-6|

参考URL

RxSwift 入門 その2

RxSwift 入門 その1 - タコさんブログ の続き。
今回は、RxSwiftプレイグラウンドのSubjectsの項。
SubjectObserver 、また Observable の両方として機能し、Hotなオブザーバブルである。 Erik MeijerはSubjectを好んでいないようだ(Why Does E.Meijer not like Subjects?)。

Subjects

RxSwiftでは以下のSubjectクラスが利用できる。

  • PublishSubject
  • ReplaySubject
  • BehaviorSubject
  • Variable

PublishSubject

PublishSubjectはサブスクリプションの後のソースObservable(s)が送信したアイテム(イベント)のみを送信する。

// メモリ管理
let disposeBag = DisposeBag()
// PublishSubject生成
let subject = PublishSubject<String>()
// サブスクライブ
subject.subscribe({ event in
  print("Subscription: 1, event:\(event)")
}).addDisposableTo(disposeBag)
subject.on(.Next("a"))
subject.on(.Next("b"))
// "b"を送信した後に、
// 更にサブスクライブ
subject.subscribe({ event in
  print("Subscription: 2, event:\(event)")
}).addDisposableTo(disposeBag)
subject.on(.Next("c"))
subject.on(.Next("d"))

この出力は、

Subscription: 1, event:Next(a)
Subscription: 1, event:Next(b)
Subscription: 1, event:Next(c)
Subscription: 2, event:Next(c)
Subscription: 1, event:Next(d)
Subscription: 2, event:Next(d)

ReplaySubject

ReplaySubjectはオブザーバがいつサブスクライブするかに関わらずソースObservable(s)が送信した全てのアイテム(イベント)をオブザーバに送信する。

// メモリ管理
let disposeBag = DisposeBag()
// ReplaySubject生成
// bufferSizeに指定した数だけアイテムがキャッシュされる
// 全てのアイテムをキャッシュする場合は、createUnbounded を使用する
let subject = ReplaySubject<String>.create(bufferSize: 1)
// サブスクライブ
subject.subscribe({ event in
  print("Subscription: 1, event:\(event)")
}).addDisposableTo(disposeBag)
subject.on(.Next("a"))
subject.on(.Next("b"))
// "b"を送信した後に、
// 更にサブスクライブ
subject.subscribe({ event in
  print("Subscription: 2, event:\(event)")
}).addDisposableTo(disposeBag)
subject.on(.Next("c"))
subject.on(.Next("d"))

この出力は、

Subscription: 1,  event:Next(a)
Subscription: 1,  event:Next(b)
Subscription: 2,  event:Next(b)
Subscription: 1,  event:Next(c)
Subscription: 2,  event:Next(c)
Subscription: 1,  event:Next(d)
Subscription: 2,  event:Next(d)

BehaviorSubject

オブザーバがBehaviorSubjectをサブスクライブした時、ソースObservableから送信された最新のアイテム(イベント)を送信し、それから、ソースObservableから送信された他のアイテムを送信し続ける。

// メモリ管理
let disposeBag = DisposeBag()
// BehaviorSubject生成
// 初期値 "Initial value" を与える
let subject = BehaviorSubject<String>(value: "Initial  value")
// サブスクライブ
subject.subscribe({ event in
  print("Subscription: 1, event:\(event)")
}).addDisposableTo(disposeBag)
subject.on(.Next("a"))
subject.on(.Next("b"))
// "b"を送信した後に、
// 更にサブスクライブ
subject.subscribe({ event in
  print("Subscription: 2, event:\(event)")
}).addDisposableTo(disposeBag)
subject.on(.Next("c"))
subject.on(.Next("d"))
subject.on(.Completed)

この出力は、

Subscription: 1, event:Next(Initial value)
Subscription: 1, event:Next(a)
Subscription: 1, event:Next(b)
Subscription: 2, event:Next(b)
Subscription: 1, event:Next(c)
Subscription: 2, event:Next(c)
Subscription: 1, event:Next(d)
Subscription: 2, event:Next(d)
Subscription: 1, event:Completed
Subscription: 2, event:Completed

Variable

VariableはBehaviorSubjectをラップする。BehaviorSubjectに対しての利点は、Variableは明示的に complete または error になることがない。また、Variableはdeallocated時に自動的に完了する。

// Variable のdeallocate時にCompleteを見るためにdoで囲む
do {
  // メモリ管理
  let disposeBag = DisposeBag()
  // Variable生成
  // 初期値 "Initial value" を与える
  let variable = Variable("Initial value")
  variable.asObservable().subscribe({ event in
    print("Subscription: 1, event:\(event)")
  }).addDisposableTo(disposeBag)
  variable.value = "a"
  variable.value = "b"
  // valueを"b"に変更した後に、
  // 更にサブスクライブ
  variable.asObservable().subscribe({ event in
    print("Subscription: 2, event:\(event)")
  }).addDisposableTo(disposeBag)
  variable.value = "c"
  variable.value = "d"
  print("--- スコープを抜けて variable はdeallocateされる ---")
}

この出力は、

Subscription: 1, event:Next(Initial value)
Subscription: 1, event:Next(a)
Subscription: 1, event:Next(b)
Subscription: 2, event:Next(b)
Subscription: 1, event:Next(c)
Subscription: 2, event:Next(c)
Subscription: 1, event:Next(d)
Subscription: 2, event:Next(d)
--- スコープを抜けて variable はdeallocateされる ---
Subscription: 1,  event:Completed
Subscription: 2,  event:Completed

参考URL

RxSwift 入門 その1

とりあえずRxSwiftのプレイグラウンドのIntroductionに書いてあるサンプルを見ていく。Introductionには「RxSwiftを理解する鍵は、Observablesの概念を理解することである」と書いてあるとおり、主にObservableの生成について説明してある。

環境

準備

  • GitHubからReactiveX/RxSwiftをクローンする
  • Rx.xcworkspace を開いて、RxSwiftをビルド
    1. ビルドスキームを RxSwift-OSX に変更
    2. ビルド (Cmd + B)

Observables

Observables は時間とともに送信されるイベントの列(ストリーム)。

Marble Diagrams (マーブルダイアグラム)はObservables(データストリーム)を視覚化する図形で、下の図のようなもの。

---◯---△---□---|-->
  • 横線(-):Observableのタイムラインを表す
  • 丸(◯)、三角(△)、四角(□):Observableによって送信されるアイテム(データ)を表す
  • 縦線(|):Observableが正常に完了したことを表す

Observablesの生成とサブスクライブ

Observablesを生成するには、以下の関数が利用できる。

  • empty
  • just
  • of
  • toObservable
  • create
  • error
  • deffered

Observableを監視する(observe, listen, subscribeなどの呼び方がある?)には subscribe メソッドを呼ぶ。subscribeの中で、プッシュされたイベントにリアクト(反応)する。今回は単純にコンソールに出力しているだけ。

empty 関数

empty は空ストリームを生成する。
.Completed メッセージを唯一送信する。

// ストリームを生成
let emptyStream: Observable<Int> = Observable.empty()
// ストリームをサブスクライブする
let subscription = emptyStream
  .subscribe { event in
    print(event)
  }

この出力は、

Completed

emptyのマーブルダイアグラムは、

--|-->

never 関数

never はエレメントまたは、完了を一度も送信しないストリームを生成する。

let neverStream: Observable<String> = Observable.never()
let subscription = neverStream
  .subscribe { _ in
    print("このブロックは一度も呼び出されない")
  }

neverのマーブルダイアグラムは、

---->

just 関数

just は一つのイベントを含むストリームを生成し、サブスクライバーに2つのメッセージを送信する。
1つ目はイベントの値、2つ目は .Completed メッセージ。

let singleElementStream = Observable.just(32)
let subscription = singleElementStream
  .subscribe { event in
    print(event)
  }

この出力は、

Next(32)
Completed

just(32)のマーブルダイアグラムは、

--32--|-->

of 関数

Observable.of 関数は一定数のイベントのストリームを生成する。

let stream = Observable.of(0, 1, 2)
let subscription = stream
  .subscribe { event in
    print(event)
  }

この出力は、

Next(0)
Next(1)
Next(2)
Completed

Observable.of(0, 1, 2)のマーブルダイアグラムは、

--0--1--2--|-->

toObservable

toObservable はSequenceType (Array, Range とか) からシーケンスを生成する。

let streamFromArray = [0, 1, 2].toObservable()
let subscription = streamFromArray
  .subscribe { event in
    print(event)
  }

出力、マーブルダイアグラムは Observable.of(0, 1, 2)と同様。

create 関数

createクロージャからストリームを生成する。
この例はカスタム版のjust関数。

let myJust = { (singleElement: Int) -> Observable<Int> in
  return Observable.create { observer in        
    observer.on(.Next(singleElement))
    observer.on(.Completed)
    return NopDisposable.instance
  }
}

let singleElementStream = myJust(32)
let subscription = singleElementStream
  .subscribe { event in
    print(event)
  }

出力、マーブルダイアグラムはjust(32)と同様。

NopDisposableNopNo Operation の略

error 関数

error はエラーで終了するストリーム(Observable)を生成する。

let error = NSError(domain: "Error Test", code: -1, userInfo: nil)

let erroredStream: Observable<Int> = Observable.error(error)

let subscription = erroredStream
  .subscribe { event in
    print(event)
}

この出力は、

Error(Error Domain=Error Test Code=-1 "(null)")

errorのマーブルダイアグラムは、

--x-->

deffered 関数

deferred はオブザーバがサブスクライブするまで待ち、サブスクライブ後、Observableを生成する。

let deferredStream: Observable<Int> = Observable.deferred {
  print("creating")
  return Observable.create { observer in
    print("emmiting")
    observer.on(.Next(0))
    observer.on(.Next(1))
    return NopDisposable.instance
  }
}
print("firstSubscription")
let firstSubscription = deferredStream
  .subscribe { event in
    print(event)
}
print("secondSubscription")
let secondSubscription = deferredStream
  .subscribe { event in
    print(event)
}

この出力は、

firstSubscription
creating
emmiting
Next(0)
Next(1)
secondSubscription
creating
emmiting
Next(0)
Next(1)

マーブルダイアグラムは、

firstSubscriptionのストリーム
--0--1--2--|-->
secondSubscriptionのストリーム
                   --0--1--2--|-->

その他

RxCocoaには、この他にもたくさんの役に立つメソッドが用意されている。例えば、

  • KVOをラップする rx_observe
  • @IBActionをラップする rx_tap
  • NotificationCenterのイベントをラップする rx_notification

参考URL

Swift Moyaでモヤっとしているネットワークレイヤーを解決

MoyaはAlamofireを直接さわることなく、ネットワークレイヤーを抽象化してくれるライブラリ。 イメージとしては、下の図のような感じ。

f:id:tiny_wing:20151223011015p:plain

Moyaの特徴として、

  • コンパイル時に正しいAPIエンドポイントのチェック
  • Associated Enumによりエンドポイントを明確にできる
  • Unitテストが簡単になる

今回の例はiTunes Apple APIを使用して音楽のアルバムを検索する。

環境

MoyaをPodでインストール

use_frameworks!

pod 'Moya'

MoyaをインストールすればAlamofireもインストールしてくれるので、AlamofireをPodfileに書く必要がない。

API ターゲットを設定

iTunesの検索には以下のURLを使用する:
https://itunes.apple.com/search?media=music&entity=album&term=検索キーワード

enumでターゲットAPIを設定する。
今回は検索するだけなので、Searchとする。

internal enum iTunes {
  case Search(String) // 検索キーワードを付随させる
}

ターゲットをTargetTypeプロトコルに準拠させる

enumで定義したターゲット(iTunes)をTargetTypeプロトコルに準拠させ、以下の4つを実装する。

  1. var baseURL: NSURL { get }
  2. var path: String { get }
  3. var method: Moya.Method { get }
  4. var parameters: [String: AnyObject]? { get }
  5. var sampleData: NSData { get }
extension iTunes: TargetType {
  // ベースURLを文字列で定義
  private var _baseURL:String {
    return "https://itunes.apple.com"
  }
  public var baseURL: NSURL { return NSURL(string: _baseURL)! }
  // enumの値に対応したパスを指定 
  public var path: String {
    switch self {
    case .Search:
        return "/search"
    }
  }
  // enumの値に対応したHTTPメソッドを指定
  public var method: Moya.Method {
    switch self {
    case .Search:
        return .GET
    }
  }
  // enumの値に対応したパラメータを指定
  public var parameters: [String: AnyObject]? {
    switch self {
    case .Search(let term):  // 検索タームを付随させる
        return ["media" : "music", "entity" : "album", "term" : term]
    }
  }
  // スタブデータの設定
  // 便宜のためベタに書いているが
  // Moyaを使ったアプリeidolonではMainBundleからロードさせている
  public var sampleData: NSData {  
    switch self {
    case .Search:
        return "Stub data".dataUsingEncoding(NSUTF8StringEncoding)!
    }
  }
}

簡単に使う場合、Moyaの設定はこれでOK!!

リクエスト方法

Moyaを使ってリクエストする。

// providerを生成
let provider = MoyaProvider<iTunes>()
// 検索ターム"swift"を指定して、Search APIをリクエストする
provider.request(.Search("swift")) { result in
  switch result {
  case .Success(let response):
    let json = try! response.mapJSON()
    print(json)
  case .Failure(let error):
    print("request error: ", error)
  }
}

リクエストのキャンセル

リクエストのキャンセルにはリクエストした時に、Cancellableオブジェクトが戻り値として 受け取れるので、このオブジェクトを保持しておき、キャンセルする。

以下のような感じ。

var cancellable: Cancellable?

let provider = MoyaProvider<iTunes>()
cancellable = provider.request(...) // Cancellableオブジェクトを取得

// 後でキャンセルする場合
cancellable?.cancel()

感想

Moyaを使ったアプリ Artsy/eidolon のコードがGitHub上に公開されているので、高度な使用例が見れて良いと思う。

参考URL

CommonCryptoのSwift製ラッパー IDZSwiftCommonCrypto を使ってみる

IDZSwiftCommonCrypto を使ってみる

IDZSwiftCommonCryptoAppleCommonCryptoSwiftラッパーで、Realmで紹介されていたので、ちょっと試してみた。CryptoSwiftでAES暗号 (AES-256-CBC) - タコさんブログと同じように Swift側でIDZSwiftCommonCryptoを使って暗号化(AES-256-CBC)し、Ruby側で復号化する。

環境

IDZSwiftCommonCryptoで暗号化(AES-256-CBC

do {
  // 暗号化する平文
  let plainText = "Hello IDZSwiftCommonCrypto"
  // 用意した鍵(適当な256ビット長)を16進文字列に変換
  let keyStr = "BDC171111B7285F67F035497EE9A081D".utf8ToHexString()
  // 16進文字列を[UInt8]に変換
  let keyHex = arrayFromHexString(keyStr)
  // 16 byteの初期化ベクトル
  let iv = try Random.generateBytes(16)
  // Cryptorを生成
  let cryptor = Cryptor(operation:.Encrypt, algorithm:.AES, options:.PKCS7Padding, key:keyHex, iv:iv)
  // 暗号化
  if let cipheredText = cryptor.update(plainText)?.final() {
      // 使用したIV:Ts4lC0DdSZvufWOPb3uTbg==
      print(base64EncodedStringFromArray(iv))
      // 暗号化された文字列:gWTKuU8SK6g9OrNR2SLdtSxM/OCYXa2ZHVFkRvn3Qgg=
      print(base64EncodedStringFromArray(cipheredText))
  }
} catch RNGStatus.ParamError {
  print("IV Error")
} catch {
  print("error")
}

ちょっとしたエクステンション・関数

// UTF-8文字列をHex文字列に変換するエクステンション
extension String {
  func utf8ToHexString() -> String {
    return self.utf8.map { NSString(format: "%2X", $0) as String }.reduce("", combine: {$0 + $1})
  }
}

// UInt8配列をBase64文字列に変換する関数
func base64EncodedStringFromArray(array: [UInt8]) -> String {
  let nsdata = dataFromByteArray(array)
  return nsdata.toBase64EncodedString()
}

// NSDataをBase64文字列に変換するエクステンション
extension NSData {
  func toBase64EncodedString() -> String {
    return self.base64EncodedStringWithOptions(.Encoding64CharacterLineLength)
  }
}

初期化ベクトルは16バイトで良いのか調べたら、Stack Overflow (Need solution for wrong IV length in AES)にあった。

Ruby側で復号化

Ruby側の復号化についてはCryptoSwiftでAES暗号 (AES-256-CBC) - タコさんブログとほぼ同じ。

require 'base64'
require 'openssl'

key = 'BDC171111B7285F67F035497EE9A081D'
encrypted = Base64::decode64 'gWTKuU8SK6g9OrNR2SLdtSxM/OCYXa2ZHVFkRvn3Qgg='
iv = Base64::decode64 'Ts4lC0DdSZvufWOPb3uTbg=='

# Cipherインスタンスの生成
cipher = OpenSSL::Cipher.new('aes-256-cbc')
cipher.padding = 1  # パディングを有効にする
cipher.key = key    # 鍵の設定
cipher.iv = iv      # IVの設定
# 復号化
decrypted = ""
decrypted << cipher.update(encrypted)
decrypted << cipher.final

p decrypted  # 出力:Hello IDZSwiftCommonCrypto

感想

IDZSwiftCommonCryptoは、Apple製のCommonCryptoを使用したラッパーなので、ある程度信はできるんじゃないでしょうか。ただあまりソースの更新はなさそう(?)

参考URL

Xcode 7 で 実機にインストールすると Security エラーで入らない時に確認すること

久しぶりに、実機にインストールしようとしたら、Security エラーが出てインストールできなかったので、その時の解決方法

環境

エラー内容

実行した時、以下のようなエラー内容が表示されました。

Could not launch "アプリ名"
process launch failed: Security

解決方法

次の方法で解決できました:

  1. 設定 -> 一般 -> プロファイルとデバイス管理 -> デベロッパAPP にデベロッパ登録したメールアドレスが表示されているので、選択
  2. "デベロッパのメールアドレス"を信頼 を選択
  3. ポップアップでも 信頼 を選択

これでアプリはすでにインストールされている。

プロファイルとデバイス管理はiOS 9.2からでてきたのかな?