Pebble Coding

ソフトウェアエンジニアによるIT技術、数学の備忘録

Rx メモ

RxSwiftとRxCocoaの区別がわかりづらいのでメモ。
RxCocoaはUIKit,NSFoundationなどに特化したiOS/macOS環境特有の機能。

RxSwift

  • combileLatest

仕様:複数の変数のいずれかが変更された場合にそれぞれの最新の値をまとめて受け取る
使い道:2つのUITextFieldに入力した値の合計をUILabelに表示する場合など

extension Observable {
    public static func combineLatest<O1: ObservableType, O2: ObservableType>
        (_ source1: O1, _ source2: O2, resultSelector: @escaping (O1.E, O2.E) throws -> E)
            -> Observable<E>
}
  • Variable

仕様:BehaviorSubject のラッパー
使い道:

public final class Variable<Element>

RxCocoa

  • orEmpty

仕様:String?型をString型に変換する。nilの場合は"“に変換される。
使い道:UILabelのタイトルなどString型のプロパティに設定する場合など

public protocol ControlPropertyType : ObservableType, ObserverType {
    func asControlProperty() -> ControlProperty<E>
}

extension ControlPropertyType where E == String? {
    public var orEmpty: ControlProperty<String>
}
  • bindTo

仕様:
使い道:

extension ObservableType {    
    public func bindTo<O: ObserverType>(_ observer: O) -> Disposable where O.E == E
}
  • drive

仕様:
使い道:

extension SharedSequenceConvertibleType where SharingStrategy == DriverSharingStrategy {
    public func drive<O: ObserverType>(_ observer: O) -> Disposable where O.E == E
}

Android Studio2.3 から外部エディタvimを開く(macOS)

Android Studio2.3で開いているファイルを行番号を指定し外部エディタvimで開く方法です。 macOSの場合です。

1) Android Studio - Preferences - Tools - External Tools に gvim を登録します。

内部console画面が開かないように Open console のチェックは外します。

f:id:pebble8888:20170325095630p:plain

2) Android Studio - Preferences - Keymap で追加したツール gvim にショートカットキーを割り当てます。 ここでは、Ctrl + Enter に割り当てています。 このショートカットは他のコマンドとコンフリクトしているため、登録時に他のコマンドを破棄するかどうか聞かれます。

f:id:pebble8888:20170325100009p:plain

C# Microsoft純正ライブラリでJSONをdeserializeする

C#JSONをserialize/deserializeする方法として、Microsoftが提供するライブラリを使う場合、2種類の方法がある。

1) DataContractJsonSerializer

Namespace: System.Runtime.Serialization.Json
AssemblySystem.Runtime.Serialization

2) JavaScriptSerializer

Namespace: System.Web.Script.Serialization
Assembly: System.Web.Extensions

1)の方はJSONに対応するモデルクラスを経由して変換する方法。
2)の方は1) の方法に加えて、モデルクラスを経由せずにダイナミックな変数を経由して変換する方法が使える。

この記事ではモデルクラスを作らずにdeselializeする方法を紹介する。

var jsonstring = "{cast:[{id:1, name:\"harry\"},{id:2, name:\"hermione\"}]}";
Debug.Print(jsonstring);
var serializer = new JavaScriptSerializer();
dynamic json = serializer.DeserializeObject(jsonstring);
dynamic cast = json["cast"];
foreach (var obj in cast)
{
    if (obj.ContainsKey("unknowntag"))
    {
        Debug.Assert(false); 
    } else
    {
        Debug.Assert(true);
    }
    if (obj.ContainsKey("name"))
    {
        Debug.Assert(true);
    } else
    {
        Debug.Assert(false);
    }
    int id = obj["id"];
    Debug.Print("{0}", id);
    string s = obj["name"];
    Debug.Print("{0}", s);
}
{cast:[{id:1, name:"harry"},{id:2, name:"hermione"}]}
1
harry
2
hermione

以上である。
注意するのは、型が確定していない部分では、varではなくdynamicで受けるという点である。
dynamicというのはC#で動的に型が決定されるという恐ろしい代物であるが(導入されたのは2010年頃)、
jsonとはすこぶる相性がいい。というか、これがないとJavaScript, Ruby, Swiftと勝負できない。
ContainsKeyの呼び出しによって、keyがふくまれるかどうかも判定することができる。
もちろん、不定な動作をしたときは例外が発生するので、例外のキャプチャは必須であろう。

RxSwift 超入門その3

class GeolocationViewController: ViewController {
    
    @IBOutlet weak private var noGeolocationView: UIView!
    @IBOutlet weak private var button: UIButton!
    @IBOutlet weak private var button2: UIButton!
    @IBOutlet weak var label: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // No GeoLocation Viewを追加する
        view.addSubview(noGeolocationView)
        
        let geolocationService = GeolocationService.instance
        
        // GeoLocation が利用できる場合は No GeoLocation Viewを隠す
        geolocationService.authorized
            .drive(noGeolocationView.rx.isHidden)
            .disposed(by: disposeBag)
        
        // ロケーションが変化したら、座標ラベルを更新する
        geolocationService.location
            .drive(label.rx.coordinates)
            .disposed(by: disposeBag)
        
        // No GeoLocation Viewにあるプリファレンスオープンボタン
        button.rx.tap
            .bindNext { [weak self] in
                self?.openAppPreferences()
            }
            .disposed(by: disposeBag)
        
        // GeoLocation Viewにあるプリファレンスオープンボタン
        button2.rx.tap
            .bindNext { [weak self] in
                self?.openAppPreferences()
            }
            .disposed(by: disposeBag)
    }
    
    private func openAppPreferences() {
        UIApplication.shared.openURL(URL(string: UIApplicationOpenSettingsURLString)!)
    }
}