koogawa blog

iOS、Android、foursquareに関する話題

2016/5/25 #potatotips #29 (iOS/Android開発Tips共有会) に参加してきたよ

f:id:koogawa:20160526013231p:plain

昨日は赤坂にて開催された potatotips #29 (iOS/Android開発Tips共有会) に参加してきました。主催は bitFlyer さんです。

potatotips.connpass.com

会場は水槽や植物などもあるオシャレなスペースでした。

f:id:koogawa:20160526013730p:plain

http://t-time156.com/

いつものようにツイートもまとめておきました。

2016/5/25 #potatotips (iOS/Android開発Tips共有会) 第29回 - Togetter

以下はiOSに関する発表のメモになります。間違いなどあれば教えて下さい。


WWDC初参加の方に送る
6つのおすすめな巡り方

mogmet さんによる発表です。WWDCに初めて参加する人向けに、おすすめの現地の巡り方を紹介されていました。

mogmet さんによると、WWDCの各セッションは1Fで視聴したほうがリアルタイムに英語の字幕を見ることができるため、セッションも聞き取りやすくなるそうです。これは参加した人にしかわからない情報なのでとても嬉しい情報ですね。他にもたくさんのTIPSを共有されていました!

  1. キーノート前日の朝までには到着しよう!
  2. キーノートに参加しよう!
  3. セッションは1Fで視聴しよう!
  4. ラボに張り付きましょう!
  5. 夜はパーティにでかけよう!
  6. 帰国後WWDC勉強会に出る

Bitriseを使っています

kurikazu さんによる発表。私も愛用している Bitrise についてのお話でした。

Bitrise は iOS, Android, Xamarin に対応したCIサービスです。無料枠もあります。GUIベースで「ワークフロー」と呼ばれる一連のステップ(git clone, pod install, Unit Test など)を追加していくのが特徴です。Slackなど色々なサービスにも対応しているそうです。

Apple の TestFlight にも対応していますが、Submit for Testflight オプションには罠があり、yes にするといきなりアップルにアプリがアップロードされるので注意が必要とのこと。

Segue をもっと使いやすくするTips

hirose_yudai さんによる発表です。Segueに関するお話でした。

Segueを使いやすくしてくれるライブラリとして ResourceKit というライブラリがあります。

これを使うことにより

  • 補完が効く
  • typoを防ぐことができる
  • Replaceし忘れがなくなる

などのメリットがありますが、さらに便利な SegueAddition というライブラリがあります。

これによって

  • 値渡しが楽になる
  • より狭いスコープで完結する
  • 循環参照しない

などのメリットが得られます。

さらに、ResourceKit と SegueAddition を併用することで更に便利になるそうです。詳しくはスライドをご覧ください。

docs.google.com

AppStoreで最新バージョン以外のアプリをダウンロードする話

hikarusato さんによる発表です。AppStore からアプリをダウンロードする際のリクエストを書き換えて、過去のバージョンをダウンロードするという素晴らしいTipsでした!

あるとき、旧バージョンをビルドしようとしたら、古いXcodeと互換性がないためビルドできない!ということがあったそうです(開発者あるある)。そこで、AppStore から過去のバージョンをダウンロードしようと思ったのがきっかけなんだそうです。

用意するものは次の3つ:

AppStore には初回リリースからすべてのバージョンが保存されている(おそらく)ことも知らなかったので、非常に勉強になりました!

Introducing Anglerfish

dealforest さんによる発表。私も愛用している Xcode プラグイン Anglerfish の紹介でした。

GitHub - dealforest/Anglerfish: sort the recently used simulators

iOS シミュレータが増えてくると、プルダウンから選択するときにわけが分からなくなりますよね。それを解決してくれるのが Anglerfish です!シミュレータを使った順にソートしてくれます。

ちなみに Anglerfish という名前は「アンコウ」からきているそうです。

Swift Package Manager (SwiftPM, SPM)

神戸から参加された JPMartha さんの発表!Swift Package Manager のお話でした。

Swift Package Manager は CocoaPods や Carthage のように、ライブラリを管理するためのツールです。Apple純正の管理ツールであり、将来的にはXcodeに組み込まれるそうです。今回は Swift Package Manager の入門的な話と、Swift Package Managerを駆使したツールの話が中心でした。

懇親会でも色々と質問させて頂きました。Twitterで補足をいただき、ありがとうございます 🙇🏻

ちなみに、JPMartha さんはニューヨークで開催されている Brooklyn swift developers に登壇されるそうです!

スタイルの一貫性を保つ

今回の主催である bitFlyer の hayashi331 さんによる発表です。

人間が行動するときにかかる負荷は「運動」「視覚」「認知」の3つだと言われているそうです。そして、運動よりも視覚よりも重い認知負荷を下げるのがもっとも重要なんだそうです。そのためにも「スタイルの一貫性を保つこと」が大事であり、今回は bitFlyer で作成している

  • NSAttributedString + Builder
  • UICatalog

について紹介されていました。

所感

今回は質疑応答ありのスタイル*1でしたが、時間通りに進んでいてタイムキーピングが素晴らしいと思いました。

ちなみに、bitFlyer さんは iOS エンジニアを大募集されているそうです!

*1:個人的には大好きなスタイルだけど時間のコントロールが難しくなる

RxSwiftを使ってfoursquareのベニューを取得するメモ

2016.10.23 追記:Swift 3.0 対応版を作成しました

blog.koogawa.com


RxSwift のメリットを理解するには実際に使ってみるのが一番!ということで、とりあえず foursquare のベニューを取得するサンプルを作ってみました。

f:id:koogawa:20160515223640p:plain

※RxSwift は絶賛勉強中なので間違ったことを書いている可能性が高いです。ツッコミ歓迎です!

動作環境

  • Swift 2.2
  • RxCocoa (2.4)
  • RxSwift (2.4)
  • SwiftyJSON (2.3.2)
  • Xcode 7

APIクライアント

APIにアクセスする部分を作ります。戻り値を Observable<[Venue]> にしているのがポイントです。

func send() -> Observable<[Venue]> {
    return Observable.create{ (observer) in
        let client = FoursquareAPIClient(accessToken: "YOUR_TOKEN")
        let parameter: [String: String] = [
            "ll": "40.7,-74",
        ];
        client.requestWithPath("venues/search", parameter: parameter) {
            [weak self] (data, error) in
            let json = JSON(data: data!)
            let venues = (self?.parseVenues(json["response"]["venues"])) ?? [Venue]()
            observer.on(.Next(venues))
            observer.on(.Completed)
        }
        return AnonymousDisposable {}
    }
}

func parseVenues(venuesJSON: JSON) -> [Venue] {
    var venues = [Venue]()
    for (key: _, venueJSON: JSON) in venuesJSON {
        venues.append(Venue(json: JSON))
    }
    return venues
}

通信ライブラリは拙作 FoursquareAPIClient を使用しました。

ViewModel

ViewModel から先ほどの send を呼び出す部分です。

public func fetch() {
    self.send()
        .subscribe { [weak self] (event) -> Void in
            switch event {
            case .Next(let value):
                self?.venues.value = value
            case .Error(_):
                ()
            case .Completed:
                ()
            }
        }
        .addDisposableTo(disposeBag)
}

戻り値が Observable<[Venue]> なので、これを subscribe して、返ってくる値(ベニューリストなど)を処理しています。

ViewController

ViewController は30行程と、かなりスッキリしました。rx_itemsWithDataSource でベニューリストとTableViewをBindしているのがポイントです。ViewModel のベニューリストに変化があるとTableViewがリロードされます。

import UIKit
import RxSwift
import RxCocoa

class ViewController: UIViewController {

    @IBOutlet weak var tableView: UITableView!
    
    var viewModel = ViewModel()
    var dataSource = DataSource()
    var delegate = Delegate()
    let disposeBag = DisposeBag()

    override func viewDidLoad() {
        super.viewDidLoad()
        self.tableView.delegate = self.delegate
        self.viewModel.fetch()
        self.viewModel.venues
            .asDriver()
            .drive (
                self.tableView.rx_itemsWithDataSource(self.dataSource)
            )
            .addDisposableTo(self.disposeBag)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

完全なソースコードはこちらです。

GitHub - koogawa/RxSwiftSample: RxSwiftSample

参考にさせて頂いた記事など

地図から位置情報を選択できる「LocationPickerController」を公開しました

地図から位置情報を選択する処理を毎回書くのが面倒くさいので、ライブラリとして公開しました。 位置情報は CLLocationCoordinate2D で取得できます。

github.com

使い方

  1. プロジェクトに CoreLocation.frameworkMapKit.framework をリンクします
  2. 位置情報を使う目的を info.plist の NSLocationWhenInUseUsageDescription に記載します
  3. CoreLocationLocationPickerController をインポートします
  4. LocationPickerController を初期化します
  5. LocationPickerController を表示すると地図を表示します
  6. Doneをタップすると位置情報がクロージャで取得できます
import CoreLocation
import LocationPickerController
let viewController = LocationPickerController(success: {
    [weak self] (coordinate: CLLocationCoordinate2D) -> Void in
    self?.locationLabel.text = "".stringByAppendingFormat("%.4f, %.4f",
        coordinate.latitude, coordinate.longitude)
    },
                                              failure: nil)
let navigationController = UINavigationController(rootViewController: viewController)
self.presentViewController(navigationController, animated: true, completion: nil)

インストール方法

LocationPickerControllerCocoaPods に対応しています。Podfile に下記一行を追加して pod install してください。

pod "LocationPickerController"

動作環境

iOS 8.0 以上で動作します。

プルリク歓迎です!

まだまだ Swift 勉強中であり、なかなか Swifty な書き方ではないことを自覚しております。もっと Swift らしい書き方ができるよ!という方はぜひプルリクエストをお送りください🙇🏻

すでに [twitter:@yimajo] 様より Swiftyなプルリク を頂きました!ありがとうございます。

iOSで検知できるセンサー12項目をまとめた「iSensor」のSwift版を公開しました

輝度センサーやモーションセンサーなど、iOSで検知できる様々な項目をまとめたサンプル集 iSensorSwift Githubで公開しました。

f:id:koogawa:20160429213238p:plain

github.com

▼機能の1つである「輝度センサー」のスクリーン

f:id:koogawa:20131117122515p:plain

iSensorSwift は昔書いたこちらのサンプル集を Swift で書き直したものになります。

興味のある方は、ぜひ使ってみてください!😀

検知できる項目

  • 光・音声系
  • 位置情報系
  • 移動・動作系
    • 加速度センサー
    • 歩数・進行状況
    • 移動速度
  • その他
    • 顔検出
    • バッテリー残量

実装方法を解説した記事

【Tips】iOSの顔検出機能を使ってみる(Swift編)

数年前に書いた下記記事が古くなってきたので、Swift編 として書き直しました。

【Tips】iOSの顔検出機能を使ってみる - koogawa blog

f:id:koogawa:20160429092322p:plain


iOS 5から追加された CIDetector を使って、顔検出機能を使う方法をメモしておきます。

実装方法

まずは「CoreImage.framework」を追加します。

f:id:koogawa:20131122152458p:plain

インスタンスを生成します。

let detector = CIDetector(ofType: CIDetectorTypeFace,
                          context: nil,
                          options: [CIDetectorAccuracy: CIDetectorAccuracyHigh])

ofType には検出種類を指定します。今回は顔検出なので、CIDetectorTypeFace を指定します。

options には検出精度 CIDetectorAccuracy 等を指定できます。

  • CIDetectorAccuracyLow - 精度は低いが、パフォーマンスは良い
  • CIDetectorAccuracyHigh - 精度は高いが、パフォーマンスは悪い

次に、顔検出を実行します。

// UIImage から CGImage を作る
let cgImage = image.CGImage
// CGImage から CIImage を作る
let ciImage = CIImage(CGImage: cgImage)
// 顔検出実行
let features = detector.featuresInImage(ciImage,
                           options: [CIDetectorSmile : true])

featuresInImage の引数には、顔検出したい画像を CIImage クラスで指定します。

今回は検出された顔が笑っているかどうかも検出したいので optionsCIDetectorSmile をセットしました。

正常に顔が検出されると、検出結果が CIFaceFeature オブジェクトとして返ってきます。このオブジェクトには、検出された顔の範囲や、目と口の位置などが含まれます。

  • bounds - 顔の範囲
  • hasLeftEyePosition - 左目の位置を検出できたか
  • hasMouthPosition - 口の位置を検出できたか
  • hasRightEyePosition - 右目の位置を検出できたか
  • leftEyePosition - 左目の位置
  • mouthPosition - 口の位置
  • rightEyePosition - 右目の位置
  • hasSmile - 顔が笑っているか
  • leftEyeClosed - 左目が閉じているか
  • rightEyeClosed - 右目が閉じているか

ここで注意したいのは、CoreImage は、左下の座標が (0,0) となる点です。よって、検出元画像の上に部品を載せたい場合は、座標をUIKitの座標系に変換する必要があります。

var transform = CGAffineTransformMakeScale(1, -1);
transform = CGAffineTransformTranslate(transform,
                                       0,
                                       -self.imageView.bounds.size.height);
// UIKit座標系に変換
let faceRect = CGRectApplyAffineTransform(feature.bounds,
                                          transform)

これで顔検出ができました。

顔検出というと難しそうなイメージが有りますが、座標系のところ以外は、意外と簡単に使用できますね。

サンプル

https://github.com/koogawa/iSensorSwift/blob/master/iSensorSwift/Controller/FaceDetectionViewController.swift

f:id:koogawa:20131130175727p:plain

補足

【Tips】iOSで歩数をカウントする(Swift編)

数年前に書いた下記記事が古くなってきたので、Swift編 として書き直しました。

【Tips】iOSで歩数をカウントする - koogawa blog

f:id:koogawa:20160423122813p:plain


CoreMotionを使って、歩数カウントを取得する方法をメモしておきます。

動作環境

Xcode 8.0 + Swift 2.2

実装方法

まずは「CoreMotion.framework」を追加します。

f:id:koogawa:20131122152458p:plain

ヘッダをインポートします。

import CoreMotion

歩数をカウントする

歩数をカウントするためには CMPedometer を使います。

iOS 7で登場した CMStepCounteriOS 8 で早くも deprecated になりました。早かったですね…

まずはインスタンスを生成します。メンバ変数にしないとうまくいかないので注意です。

let pedometer = CMPedometer()

startPedometerUpdatesFromDate メソッドで歩数のカウントを開始します。

if CMPedometer.isStepCountingAvailable() {
    self.pedometer.startPedometerUpdatesFromDate(NSDate(), withHandler: {
        [weak self] (data: CMPedometerData?, error: NSError?) -> Void in
        // 歩数が更新されるたびに呼ばれる
        dispatch_async(dispatch_get_main_queue(), {
            if data != nil && error == nil {
                self?.stepLabel.text = "step: \(data!.numberOfSteps)"
            }
        })
    })
}
  • 第1引数:どの時点からのデータを呼び出すか指定(今回は現在日時を指定)
  • 第2引数:歩数更新時に呼び出す handler

第2引数で指定する handler には、計測を始めてからの累計歩数、移動距離など様々な情報が返ってきます。詳細はアップルの公式ドキュメントを参照してください。

歩数カウントが不要になったら、忘れずにカウントを停止しておきます。

self.pedometer.stopPedometerUpdates()

アクティビティの変化を取得する

また、歩数以外にもユーザのアクティビティ情報(歩いているのか、走っているか等)も取得できます。

まずはインスタンスを生成します。メンバ変数にしないとうまくいかないので注意です。

let activityManager = CMMotionActivityManager()

取得を開始します。

if CMMotionActivityManager.isActivityAvailable() {
    self.activityManager.startActivityUpdatesToQueue(NSOperationQueue.mainQueue(),
        withHandler: {
        [weak self] (data: CMMotionActivity?) -> Void in
        dispatch_async(dispatch_get_main_queue(), {
            // アクティビティが変化するたびに呼ばれる
        })
    })
}

アクティビティは CMMotionActivity というクラスで返ってきます。このクラスには次のプロパティが含まれます。

プロパティ 意味
NSDate startDate アクションが発生した時間
CMMotionActivityConfidence confidence データの精度(Low/Medium/High のいずれか)
BOOL stationary 静止状態
BOOL walking 徒歩中
BOOL running ランニング中
BOOL automotive 自動車や電車等の乗り物に乗っている状態
BOOL unknown 不明なアクティビティ

どれかひとつの動作が true になるというわけではなく、例えばautomotivestationary が同時に true になる状況もありえるようです。

最後は忘れずに停止しておきます。

self.activityManager.stopActivityUpdates()

サンプル

注意点

リンク

2016/4/20 #potatotips #28 (iOS/Android開発Tips共有会) に参加してきたよ

昨日は南青山にあるエウレカさんで開催された potatotips #28 (iOS/Android開発Tips共有会) に参加してきました。

potatotips.connpass.com

とても綺麗なオフィスでした。

f:id:koogawa:20160421004042p:plain

いつものようにツイートもまとめておきました。

2016/4/20 #potatotips (iOS/Android開発Tips共有会) 第28回 - Togetter

Androidの発表内容については、すでに id:konifar さんが素晴らしいまとめを公開されています。

konifar.hatenablog.com

以下はiOSに関する発表のメモになります。間違いなどあれば教えて下さい。


PlaygroundでカスタムUIのプロトタイピング

milkit さんによる発表。Xcode の Playground についてのお話です。

Playgroundは日々進化しており、なんとたった3行で TableViewController が表示できてしまう。そして、アニメーションや AutoLayout にも対応している。更に、Xcode 7.3からタップイベントも取れるようになったんだとか。

Debug Remote / Local Notification on watchOS

shingt さんによる発表。watchOS 上で扱う Local Notification のお話です。

watchOS における Remote Notification のテストは、Xcode に仕組みが用意されているため割と簡単。それに対し、Local Notification は仕組みがないのと、引数が UILocalNotification オブジェクトで渡ってくるので、テストがとても大変。そこで、Remote/Local Notification のラッパーを作ったという素敵なお話でした。

RxSwiftのドライバー

Motoki Narita さんによる発表。RxSwift の Driver についてのお話でした。

アカウント登録画面を例に、入力されたユーザ名がすでに使われているかどうかを Validate するサンプルを使って Driver の解説をされていました。

Swiftにわか勢な自分がMacアプリを作ってRxSwiftを使えるようになった話

y.imajo さんによる発表。Macアプリを作ってRxSwiftを使えるようになった話でした。

発表が始まった途端、スクリーンにハッシュタグ #potatotips のツイートが流れているな〜と思っていたら、実はそれ自体が今回発表するMacアプリだった!という驚きの内容でした。

今回のMacアプリのソースコードGitHubに公開されているそうです。

github.com

手を動かさないとRxSwiftは理解できない

これはその通りだなぁ、と思いました。

Repository pattern in Swift

naoty さんによる発表。リポジトリパターンのお話です。

リポジトリパターンを使うことで、オブジェクトをWebAPI経由で取得するのか?Realmから取得するのか?などを知る必要がなくなり、疎結合なコードを書くことが可能になります。今回は「レシピデータ」をリポジトリを介して取得する処理をSwiftで実装する実践的な内容でした。

github.com

tvOS tips

koutalou さんによる発表。tvOSのお話です。最近、マネーフォワードのtvOSアプリを作られたそうです!

個人的には tvOS にも TabBarController と NavigationBar の概念があったことや、WebViewが使いないことを知らなかったので色々と勉強になりました。

国際カンファレンスに登壇する - 応募編

shu223 さんこと堤さんによる発表。堤さんは国際カンファレンスである UIKonf / iOSCon に登壇されるそうです!🎉

発表の中では、応募する理由、参加することのメリット、カンファレンスの探し方など、なかなか聞くことのできない貴重なお話を聞くことができました。

スライドなどは堤さんのブログにまとめられているようです。

d.hatena.ne.jp

iOS Universal Linkについて

最近ボルダリングにハマっているという TachibanaKaoru さんによる発表。iOS 9 から使えるようになった Universal Link のお話でした。

Universal Link の仕組み自体は知っていたのですが、SFSafariViewController, WKWebView, UIWebView の中では無効になる事実や、自分でURLを入力した場合も無効になるというTipsが知れたのは良かったです。

また、Smart App Banner との違いについても触れられていました。Universal Link と Smart App Banner は目的が違うものになるため、別々に実装する必要があるとのことです。

所感

ポテチでもRxの発表が増えてきており、流行ってるんだなぁ〜というのを再認識しました。

また、発表中も程よいボリュームでBGMが流れていて良いな、と思いました。

現場からは以上です。*1

*1:こにふぁーさんをリスペクトしてみました。怒られたら消します