CircleCIでCarthageを利用する

追記

2015-11-11
CircleCIのXcode7.1サポートコンテナは問題があるという事でロールバックされた。Carthageが インストールされないコンテナに戻ってしまったが、Carthage自体はHomebrewでインストールできる。以下のスクリプトを利用する事で、CircleCIでCarthageをセットアップすることができる。

#!/bin/sh

brew update

brewls=`brew ls --versions carthage`

if [ "${brewls}" = "" ]; then
    echo "Install carthage"
    brew install carthage
else
    echo "Upgrade carthage"
    brew upgrade carthage 2> /dev/null
fi

CircleCIがXcode7.1とCarthageに対応した。

そそくさとCarthageをプロジェクトに導入してCircleCIでテストしてもらおうとしたら、carthage bootstrapでNo Code Signエラーが発生。導入されているCarthageのバージョンは0.8だったので

dependencies:
  pre:
    - brew update
    - brew upgrade carthage

をcircle.ymlに加えてcarthageを0.10に変更したらCarthageでビルドできた。

しかし今度は*** WARNING: Skipping build for "iphoneos" SDK because the necessary signing identity "iPhone Developer" is not installedという警告が発生した。実機用のバイナリがビルドできてないのかも知れない。テストには不要だがiTCにアップロードする時に問題になるので、解決しないといけないかも。

Reason: Incompatible library version: Foo requires version 2.0.0…

XcodeでiOSアプリを実行するとReason: Incompatible library version: Foo requires version 2.0.0 or later, but Bar provides version 1.0.0のようなエラーでアプリが起動しない場合、Clean Build Folderを行うとエラーが発生しなくなった。

CocoaPodsのバージョンが古い場合にも発生するらしいので、その場合はCocoaPodsのバージョンを上げてpod installを行っておくと良いらしい。

XcodeでClean Build Folderを実行できない場合の対処方法

XcodeでClean Build Folderを実行しようとするとYou do not have the required file permissions.というメッセージでエラーが発生してしまう場合の対処方法。

XcodeのPreferences→LocationsでDerived DataのDefaultをRelativeに変更して再度Defaultに戻す。

これでClean Build Folderを実行できるようになる。

RxSwiftでdeinitまで処理をdeferする

Swiftでdeinitまで処理をdeferする – TOKOROM BLOGという記事を見かけたので、RxSwiftで同様の事を行うコードを書いてみる。

まずdeinitで処理を行いたいクラス等にDisposeBagを持たせる。

class SampleViewController: UIViewController {
let disposeBag = DisposeBag()

ロガーを登録するところは同様に行う。

let logger = DDASLLogger.sharedInstance()
DDLog.addLogger(logger)

RxSwiftのAnonymousDisposableを作成して、disposeするときに実行するアクションを記述する。そしてそのAnonymousDisposableをdisposeBagに登録する。

let disposable = AnonymousDisposable {
DDLog.removeLogger(logger)
}
disposeBag.addDisposable(disposable)

こうする事で、SampleViewControllerのdeinitでdisposeBagがリリースされる時にAnonymousDisposableがdisposeされ、登録したアクションが実行されることにより、loggerがremoveされることになる。

すべてのコードは以下のようになる。

class SampleViewController: UIViewController {

let disposeBag = DisposeBag()

override func viewDidLoad() {
super.viewDidLoad()

let logger = DDASLLogger.sharedInstance()
DDLog.addLogger(logger)
let disposable = AnonymousDisposable {
DDLog.removeLogger(logger)
}
disposeBag.addDisposable(disposable)
}
}

で、これを簡単に書く為の関数を作成してみた。

        func actionAndDefer<T>(action: () -> T, deferAction: T -> ()) -> Disposable {
            return create { observer in
                let obj = action()

                observer.onNext("")

                let disposable = AnonymousDisposable {
                    deferAction(obj)
                }

                return disposable
                }.subscribeNext { (value: String) -> Void in }
        }

        actionAndDefer({ () -> DDLogger in
            let logger = DDASLLogger.sharedInstance()
            DDLog.addLogger(logger)
            return logger
            }, deferAction: { logger -> () in
                DDLog.removeLogger(logger)
        }).addDisposableTo(disposeBag)

RxSwiftの事をまだ把握しきれていないのでもっと良い書き方があると思うけど、とりあえずこれでactionAnDeferを実行してdisposeBagに登録すると利用できる。

[Swift] リフレクションを利用してEnumのcase名を文字列として利用する方法

*Swift2によるコードです。

SortConditionというソート条件の列挙型を考えます。ソートに使うプロパティと昇降順の情報を持たせたいので以下のようになります。

enum SortCondition {
case Name(Bool)
case Date(Bool)
}

let condition = SortCondition.Name(true)
switch condition {
case .Name(let ascending):
collection.sort(property: "name", ascending: ascending)
case .Date(let ascending):
collection.sort(property: "date", ascending: ascending)
}

“name”や”date”という情報はenum内で判断させることも出来ますが、結局switch等で値を決定する必要があることには違いありません。ですのでcaseが増えるとそのつどswitchを更新する必要があります。

そこでcase名を文字列として取得してプロパティの値に利用できれば、caseを増やしてもswitchをメンテナンスする必要がなくなります。やってみましょう。

enum SortCondition {
case name(Bool)
case date(Bool)

var property: String {
let mirror = Mirror(reflecting: self)
return mirror.children.first?.0 ?? ""
}

var ascending: Bool {
let mirror = Mirror(reflecting: self)
return (mirror.children.first?.1 as? Bool) ?? true
}
}

let condition = SortCondition.name(true)
collection.sort(property: condition.property, ascending: condition.ascending)

リフレクションを使いenumの情報を取得して利用しています。Mirrorはクラス・構造体・列挙型の情報をあらわすリフレクションのための構造体です。このようにMirrorを利用するとcase名や関連値を取得することができるので、case名を文字列として利用することができます。(case名は小文字からはじまっていますが、頭文字を大文字にしておいて小文字に変換しても良いかもしれません。)