Rodhos Soft

備忘録を兼ねた技術的なメモです。Rofhos SoftではiOSアプリ開発を中心としてAndroid, Webサービス等の開発を承っております。まずはご相談下さい。

FutureパターンをSwiftで書く

Swiftで

まずリクエストプロトコルを作る。

public protocol Request {
    func getResult()->String    
}

それを実装するFurureを作る。

public class Future:Request {
    private let semaphore:DispatchSemaphore = DispatchSemaphore(value: 0)
    private var ready = false
    private var result:Result? = nil
    
    public func setResult(result:Result) {
        if ready {
            return
        }
        
        self.result = result
        self.ready = true
        
        semaphore.signal()
    }
    
    public func getResult() -> String {
        objc_sync_enter(self)
        
        while !ready {
            objc_sync_exit(self)
            semaphore.wait()
            objc_sync_enter(self)
        }
        
        objc_sync_exit(self)
        
        return self.result!.getResult()
        
    }
}

つまり、getResultすると結果がsetResultされるまで待たされるという動きになる。

Resultは即に返答する。

public class Result : Request {
    private var content:String
    
    public init(_ command:Int) {
        print("request start \(command)")
        Thread.sleep(forTimeInterval: 1.0)
        content = "result = \(command)"
        print("request end \(command)")
    }
    
    public func getResult() -> String {
        return content
    }
    
}


リクエストを受けつたマネージャはまずFurureを返し、
遅延して受け取る結果をsetResultで入れる。

public class RequestManager {
    public func request(command:Int) -> Future {
        
        let future = Future()
        
        DispatchQueue.global().async {
            let result = Result(command)
            future.setResult(result:result)
        }
        
        return future
    }
}

ソースコードは以下に
github.com

回転させたときのレイアウト変更

XCode8で回転させた場合のレウアウト変更はSizeClassを使うとできる。
拘束条件を個別にinstallで設定する。全画面にしたい場合は上下左右にマージン0で設定してやる。

サイズの変更は以下の参考に書かれているような変更で呼び出しを受ける。
iOS8以降で、どうデバイスの回転を取り扱うかまとめてみる - Qiita

Tensorflowをあらためてやってみた。

あらためてTensorflowをやってみた。以前より理解は進んでいる気がする。

#coding:UTF-8

import input_data
mnist = input_data.read_data_sets("MNIST_data/", one_hot=True)

import tensorflow as tf

## 画像は28*28ピクセル = 784ピクセル
## この画像から0,...,9という番号を判断したい。

# 入力変数定義 float32型で、(None, 784)の2階のテンソルを作る。
x = tf.placeholder(tf.float32, [None, 784])
# 重み 初期値ゼロで(784,10)の2階のテンソルを作る。
W = tf.Variable(tf.zeros([784, 10]))
# バイアス 初期値0で(10)の1階のテンソルをつくる
b = tf.Variable(tf.zeros([10]))
# ソフトマックス活性化関数
y = tf.nn.softmax(tf.matmul(x, W) + b)


# 正解
right_answer = tf.placeholder(tf.float32, [None, 10])

# クロスエントロピーの定義 (最小二乗法でも良かったが)
cross_entropy = -tf.reduce_sum(right_answer*tf.log(y))

# 勾配降下法で重みを調整する
# 学習比率 0.01
train_step = tf.train.GradientDescentOptimizer(0.01).minimize(cross_entropy)

#初期化
init = tf.initialize_all_variables()
sess = tf.Session()
sess.run(init)

# 学習中
for i in range(1000):
    # データを取得 1回で100個のデータ(バッチ)を持ってくる
    batch_xs, batch_ys = mnist.train.next_batch(100)
    # ステップを実行
    sess.run(train_step, feed_dict={x: batch_xs, right_answer: batch_ys})


## 結果表示 ##

# それぞれの分布から最も確率の高いもの同士を見比べ同じであるかを調べる。
correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(right_answer,1))

# 正答率を計算する。
accuracy = tf.reduce_mean(tf.cast(correct_prediction, "float"))

# 精度表示
print(sess.run(accuracy, feed_dict={x: mnist.test.images, right_answer: mnist.test.labels}))

StoreKitメモ

参考

以下を参考にした。
d.hatena.ne.jp

以下がデータフォーマットがあり詳しい
ameblo.jp

qiita.com


SKProductRequest

initWithProductIdentifiers:でプロダクトIDを指定しプロダクト情報を取得するリクエスト(SKProductsRequest)を作成する。
これは非同期で結果がかえる。
didReceiveResponse、didFailWithError、requestDidFinish。
SKProductsResponceに結果が入っている。

SKProductsResponce

ここからSKProductが取れる。

SKProduct

ここからlocalizedTitle,localizedDescription, price, priceLocale, productIdentifier等が取得できる。

プレゼンタに加えてドメイン層を入れる

プレゼンタを導入した後は今回はさらにドメイン層(UseCase)を取り入れたい。

プレゼンタ

プレゼンタはUseCaseにまとめられた処理を用いてビューないしUseCaseからの返事を処理する。

ユースケース

UseCaseはビジネスロジックを記述するところでそれのみで単体テストできるもの。

アイコンの表示云々のロジックはビジネス層でデータ層のモデル(エンティティ)をアイコン表示するしない情報(これがビューモデル)に変換して(この部分をtranslatorとして切り出すのも可能)プレゼンタに返す。
プレゼンタはビューにビューモデルを渡す。
ビューはビューモデルの指示通りにビューを更新する。
各境界はプロトコルを用いてやりとりする。(このプロトコルを介するのがポイント、逆にプロトコルの設計次第でうまくいくかが変わりそう。)

導入として

1. プレゼンタが行う処理をユースケースに委譲
2. ユースケースはビューモデルを作りプレゼンタに返す。ビューモデルに変換する部分はトランスケーターとして切り出す。
3. 受け取ったビューモデルはビューに渡す。
4. ビューはビューモデルの支持通りに書き換える。
5. これらのやりとりは全てプロトコル(インターフェース)を介して行うようにする。

ユースケースつまりビジネスロジック部分はUIともデータ層(ネットワーク、外部フレームワーク)とも分離され、単体テストにかけることができる。アイコンの出しわけロジック、購入処理、ログイン処理などは全てユースケースとして分離される。

Adaptive Design

UITraitCollection

バイスの情報が入っている。

  1. Size class (H,V)
  2. iphone/ipad
  3. 倍率

Size Class

以下を参考
qiita.com

各種UI部品のUITaraitCollectionでの変化

以下を参考
qiita.com

  1. UISplitViewController Master/Detailの両方が見える時、Masterだけが見える時
  2. Presentation Controller
  3. UIAppearance

show

ViewControllerの階層関係を気にせず次のvcを呼ぶメソッド
親は適宜、targetViewController(forAction 、を実装し、そのshowができるvc(例えばナビゲーションvc)を見つける。
showを実装しているvcは委譲されて実行される。

FaceBookグラフ

単に以下のようにやるだけでJSONが返却される。

                let req = FBSDKGraphRequest(graphPath: "me"
                    , parameters: ["fields":"id,email,name"])
                req?.start(completionHandler: { (con, result, error) in
                    if let error = error {
                        print("error")
                        return
                    }
                    print("\(result)")

実際に取れる内容はグラフのエクスプローラでweb上で試してみることができる。
Graph APIエクスプローラ - 開発者向けFacebook

FaceBookログイン

基本はdevelopers.facebook.com
にすべて書いてある。

フレームワークとしてFBSDKCoreKit、FBSDKLoginKitあたりをDLして入れる。ここでpodを使っても良い。
info.plistの設定でFaceBookAppIDの文字列を間違えないようにする。

FBSDKCoreKitのFBSDKApplicationDelegateへAppDelegateのメッセージを流すようにする。

ログイン処理はFBSDKLoginKitを使う。
便利UIとしてFBSDKLoginButtonがすでに用意されている。FBSDKLoginButtonDelegateでログイン結果を受け取る。

ログインで得たトークンはキーチェインに保存されるので、キーチェインの使用を許可(Capabilitiesで)しておく。

FBSDKAccessTokenのcurrentで現在のトークンがわかるのでログイン済みかは判断できる。そこからユーザIDを取得できる。

ハフマン符号

"AABABCAC"を単純にbitを割り当てるとA 00, B 01, C 10のように1文字に2ビットで、計16ビットかかる。

出現頻度を調べる。

"AABABCAC"のAは4、Bは2、Cは2で現れる。

ハフマン木をつくる。

最も低い出現頻度とその次のものを取ってくる。今は,B,C。

{B,C}

次の出現頻度のものはAなので

{A,{B,C}}

というハフマン木ができる。

根元から0,1をふっていく。

A = 0

B = 10
C = 11

よってハフマン符号によって
0 0 10 0 10 11 0 11

で、計12ビットに圧縮できた。

復号方法

"0" 0 10 0 10 11 0 11 ... ハフマン木に0*はないのでA確定

A "0" 10 0 10 11 0 11 

A A 10 0 10 11 0 11 

以下同様にハフマン木を見ていけば復号できる。
A A B A A B A B 

参考したサイト

michisugara.jp

insertion sort 挿入ソート

O(n^2)な実行時間。ほぼ整列している場合は非常に速い。

1. 先頭から整列してない数を探す。
2. その数を整列させるように差し込む。

  • 2,3,5,4,2
  • 2,3,5,"4",2
  • 2,3,"4",5,2 
  • 2,3,4,5,2
  • 2,3,4,5,"2"
  • "2",2,3,4,5

ドメイン駆動

参考にしたもの
ドメイン駆動設計 ( DDD ) をやってみよう
ドメイン駆動設計のためのオブジェクト指向入門
実践に向けたドメイン駆動設計のエッセンス



3つのレイヤー

ポイント

ドメインを隔離する。UIとデータ形式から。生のデータを使わない。結果は加工されたモデルを渡す。
計算、加工、判断のロジックを一箇所に集めること。実現手段の隠蔽。各自の型の定義(基本データ型を使わない)。値オブジェクト。一覧オブジェクト。
(if,list,string,etc)も使わない。

プレゼン層はプレゼンタからドメイン層を使うがドメイン層はプレゼン層をつかわない。
ドメイン層のユースケースリポジトリを介してデータを受け取るがそれはモデルの形にしてプレゼン層に渡す。
トランスレータはモデルの変換を行う。

業務の関心事にドメインオブジェクトを作り、公開メソッドは業務と関連するものにする。

ルールのクラス化

  • Procedure(手順) 状態を持たない
  • Policy(ポリシー) ルールの集まり。状態を持たない
  • Rule(ルール) 集まって一つのポリシーを作る

集約(アグリゲート)

一緒に使うもの部品をあつめた一つのクラス

ReSwiftの流れ

ReSwiftというSwiftのRedux実装のライブラリを触ってみました。

github.com

  1. アプリはStoreを一個持ちStoreはこちらで定義した状態(State)を持ちます。
  2. 各種のReducerというのをStoreに登録します。
  3. ReducerはActionが発行されたらどのようにStateを変更するのか各自で定義します。
  4. ボタンがタップされたなどのイベントが起きたとき、プログラマはアクションをストアに送ります(dispatch)、するとストアではReducerに今の状態とアクションを食わせて新しい状態にします。
  5. 新しい状態はそれを監視(subscribe)しているオブジェクトにnewStateとして通知されるという具合です。

擬似コードでのまとめ

State定義

Action定義

各種Reducer定義

Reducer (Action, State) -> State

Store作成

Store = {State, Reducers}

状態更新を受取りたい場合

Store.subscribe(User)

タップのイベント等が起きたことを通知

Store.dispatch(Action)

reducerによってState更新され状態変更が通知される。

ViewControllerはアクションを発行し状態変更通知を受けたら状態に基づいてUIを変更するという流れになります。

参照

簡単なコードサンプルとして
GitHub - ReSwift/CounterExample: Demo Application of Unidirectional Data Flow in Swift, Built with ReSwift

より実際的な実装例は以下にありました。
qiita.com