koogawa blog

iOS、Android、foursquareに関する話題

#yidev 第24回横浜iPhone勉強会に参加してきたよ

f:id:koogawa:20160702235648p:plain

今日は第24回横浜iPhone勉強会に参加してきました。

https://atnd.org/events/78243atnd.org

今回も発表枠はあっという間に埋まり、参加者枠もキャンセル待ちが出るほどの盛況ぶりでした。

参加者のアイコンを並べたスライドが今回も大活躍😃

f:id:koogawa:20160702235824p:plain

また、岸川さんより try! Swift の参加証を頂きました!ありがとうございます。

f:id:koogawa:20160702235910p:plain

当日のツイートはTogetterにまとめています。

***

そんなわけで以下、発表内容の簡単なメモになります。(発表者の皆さま、間違い等あればご指摘ください)

Xcode 8 + Server

noriri_ さんによる発表です。Xcode 8でパワーアップしたServerについてのお話でした。

  • Xcode Server
    • メリット
      • バイスでテストできる
      • Betaをテストできる
      • パフォーマンスがよい
    • デメリット
      • セットアップが難しい(.travis.yml みたいに簡単にいかない)
      • 他のサービスとの連携がしづらい
      • Github の clone にめっちゃ時間かかる
  • Server 5.2 + Xcode 8 でいろいろ改善されている
    • 好きなユーザでビルドできるようになった

CoreData 大容量データ マイグレーション

yaso_san さんによる発表です。大量にメモリを消費するCoreDataのマイグレーションについて、その理由と改善策を解説されていました。Instruments を使ったデモがわかりやすかったです。

  • CoreDataのマイグレーションは大量にメモリを消費する
  • マイグレーションの仕組み
    • Lightwight Migration - おまかせ
    • Custom Migration - 細かくカスタマイズ可
    • の2種類が存在する
  • Custom Migration
    • Stage 1: オブジェクトをSourceからDestinationに新しい形式でコピー
    • Stage 2: 関連をセット
    • Stage 3: 検証
  • 何故メモリを消費するのか?
    • データを引き継ぐために全てのオブジェクトをメモリに載せてるから
  • マイグレーション分割の壁
      1. 分割する数だけMapping Modelが必要
      1. SourceとDestinationのオブジェクトの結びつきは分割すると引き継げない
      1. 分割した数だけ時間がかかる
  • マイグレーション分割の改善
      1. オブジェクトに一意の値を持たせる
      1. グループ分けできる値を持たせて1つのエンティティ内で分割する
      1. Mapping Modelをコードで分ける
      1. 関連先のエンティティのマイグレーション順を前にする
  • まとめ
    • メモリと時間はトレードオフ
    • エンティティ毎のデータ量によって分け方をかえる
    • 処理の順番を決めて無駄な関連付けをしない
    • 必要のないデータは引き継がない

Xcode 8 移行に備える

田畑浩平 さんによる発表。みんな気になる Swift 2.3 / 3 移行のお話でした。

  • ATS必須化
    • すべての通信においてHTTPSが要求される
    • それ以外で使う場合は設定が必要
  • Swift 2.3/3 への移行
    • Swift 3 : Xcode 8 がメインでサポート
    • Swift 2.3 : Swift 3 への移行準備ができていない開発者が最新のSDKを使うためのもの
  • 移行ToDo
    • まずはSwift 2.3移行
    • Swift 2.3でエラーを吐くライブラリにはPR
    • 2.3 での動作が担保されたら3への移行を試す
    • Xcodeにバグが有る場合はApple Bug Reportからバグ報告
    • 使用しているライブラリがすべて3をサポートした時点でSwift 3へ移行

マルチプラットフォーム展開アプリに音声インタラクションを追加するお話!

k2-ikeda さんによる発表です。SiriKIt、OK Google、コルタナのデモもありましたよ!

  • SiriKit
    • iOS 10以降
    • 日本語が使える
    • 特定ドメインへの対応アプリが作れる
    • 対応シーンは限定的
  • OK Google
    • アラーム、電話をかける、フィットネス、乗車予約などが可能
    • 日本語が使える、と書いてあるが実際は不安定
  • コルタナ(Windows
    • Windows 10〜
    • 応答パターン用のxmlを書かないといけない
  • まとめ
    • 最近の環境であればどのプラットフォームも対応している
    • SiriKit対応は他のプラットフォームのことはあまり気にしないで行えばよいのでは
    • 対応するドメイン領域もそうだけど、「どのプラットフォーム版のアプリでも同様のUXを提供」みたいなのが要件にのってくるアプリには特に日本語環境では向かない

React-native について!

yuumi3 さんによる発表。Facebookが公開している React-native のお話でした。

  • React-native
  • React.js とは
    • *.js 系は色々出てきたけど、これが勝者!
    • シンプルなプログラミングモデルと性能を両立
    • メンテナンス性の高いコードが書ける
  • ES2015(ES6)
    • class 構文が導入された!
    • すべてのブラウザがサポートしているわけではない

iOS 10 Extensions ざっくり確認!

tmokita さんによる発表です。iOS 10で追加された Extensions について、みんなでおさらいしていくスタイルがとても良い感じでした。

  • Map Extensions
    • 使おうとしたけどできなかった
  • Extensions ざっくり確認
    • Xcode 8 増えたもの
      • Broadcast UI
      • Broadcast Upload
      • Call Directory
      • Intents
      • Intents UI
      • Message
      • Notification Content
      • Notification Service
      • Sticker Pack

なお、Map Extensions については次の順番で調べていけばよいのでは、と id:KishikawaKatsumi さんがおっしゃっていました。

さいごに

id:niwatako さんがとても良いことを言っていました。

これは本当に同感で、yidevは発表者でなくとも、勉強会が終わった後に不思議な充実感が得られるのです。

yidev には発表中にも普通に参加者から質問が投げられ、そのままディスカッションが始まる文化があります。これは他の勉強会ではあまり見られない光景だと思っています。きっと「講習会ではないですからね :D」の文化が根付いているんでしょうね😃

yidev、次回も楽しみです!

ブログとQiitaをどう使い分けるか

日本語でiOS開発の話をするSlackで話題になっていたので、自分の考えをまとめてみたいと思います。

ブログに書くもの

1. 比較的長くなりそうな内容

1つのテーマに対して深く掘り下げる内容はブログに書きます。逆に短いメモなどはQiitaにサクッと投稿します。

書評や勉強会レポートもじっくり書きたいので、ブログに書きます。

2. ポエム的な内容

「なぜプログラムを書くのか」「エンジニアのキャリアについて」など 考え方 について書く場合はブログに書きます。本記事もブログが適していると思います。

ちなみに、Qiitaのガイドライン

Qiitaは、プログラミングに関する知識を記録・共有するためのサービスです。

とあるので、ポエムをQiitaに書いたらダメですね😅

Qiitaに書くもの

1. 比較的短い内容

比較的短いメモやTips的なものはQiitaにサクッと投稿します。 だいたい1時間ぐらいで書き上げます。

2. あとから編集・追記しそうな内容

外的要因により、定期的に記事の内容を書き換える必要がある記事はQiitaの方が適していると思います。

Qiitaには記事を編集した際に、ストックした人全員に通知を送れる機能があるので便利ですね。

宣伝

日本語でiOS開発の話をするSlackのチームは現在、参加者募集中だそうです!気になった方は [twitter:@tomzoh] さんの下記記事からぜひ参加してみてはいかがでしょうか😄

www.hasegawa-tomoki.com

Travis + Swift + Carthage でハマったメモ

これでハマってたんだけど、エラーログを見たら答えが書いてあった。

$ brew install carthage
carthage: A full installation of Xcode.app 7.3 is required to compile this software.
Installing just the Command Line Tools is not sufficient.
Xcode can be installed from the App Store.
Error: An unsatisfied requirement failed this build.
The command "brew install carthage" failed and exited with 1 during .
Your build has been stopped.

つまり、Xcode.app 7.3 が必要なのである。ログを詳しく見たら確かに Xcode のバージョンが低い。

Xcode 6.1
Build version 6A1052c

でも、.travis.yml ではちゃんと 7.3 を指定しているのに何故??

language: swift
osx_image: xcode7.3

そこでこの記事に助けられた。

Swift の CI ですが language は objective-c で大丈夫です

どうやら language: swift って書いてたのが原因だったらしい。Swift使ってても language: objective-c って書かないといけないのね..。

再度試したところ、無事 Xcode 7.3.1 でビルドされ、carthage もインストールできた。

▼最終的な .travis.yml

language: objective-c
osx_image: xcode7.3

before_install:
- brew update
- brew install carthage
- carthage update --platform ios

script:
- xctool -project MyApp.xcodeproj -scheme MyApp -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO

branches:
  only:
  - master
  - develop

notifications:
  slack:
    secure: XXXXXX

APIKit + Himotoki + foursquare API でベニューを取得する

これは何

[twitter:@ishkawa] さん作の APIKit と、 id:ikesyo さん作の Himotoki を組み合わせて、foursquare API でベニューを取得したときのメモです。

実行環境

  • Xcode 7.3.1
  • Swift 2.2
  • Carthage 0.11.0
  • APIKit 2.0.1
  • Himotoki 2.0.1

準備

Carthage

ライブラリをインストールするために Carthage を使います。未インストールの場合は Homebrew でインストールします。

$ brew install carthage

アクセストークン取得

foursquare API にアクセスするためのアクセストークを取得します。

一番早いのは API Explorer にアクセスして、そこに表示される oauth_token をコピーする方法です。(本番リリースするアプリには使っちゃダメよ)

https://developer.foursquare.com/docs/explore#req=venues/search%3Fll%3D40.7,-74

image

https://api.foursquare.com/v2/venues/search?ll=40.7,-74&oauth_token=(YOUR_ACCESS_TOKEN)&v=20160529

でベニュー一覧が取得できることを確認します。

実装

サンプルプロジェクト作成

今回は 4sqAPIKitHimotoki という名前にしました。

f:id:koogawa:20160529142148p:plain

APIKit + Himotoki インストール

.xcodeproj ファイルがあるディレクトリに Cartfile を作成します。

github "ishkawa/APIKit" == 2.0.1
github "ikesyo/Himotoki" == 2.0.1

carthage update を実行します。(ちょっと時間がかかる)

$ carthage update
*** Fetching Himotoki
*** Fetching APIKit
*** Fetching Result
*** Downloading Result.framework binary at "2.0.0: A Portable Result"
*** Checking out APIKit at "2.0.1"
*** Checking out Himotoki at "2.0.1"
...

このとき、一緒に Result.framework もダウンロードされます。

update が終わったら、プロジェクトに3つのライブラリを追加します。

f:id:koogawa:20160529142225p:plain

Carthage の使い方は [twitter:@yutat93] さんの記事がとてもわかりやすいです。

データモデル定義

今回使用する /venues/search API のレスポンスは次のようなJSONになっています。

f:id:koogawa:20160529142259p:plain

ベニューのリスト response > venues だけ欲しいので、次の2つのデータモデルを定義することにします。

  • Resp(Response にしたかったが、APIKit の Response クラスと名前が重複するのでこの名前にした)
  • Venue

JSON デコードには Himotoki を使っています。Decodable プロトタイプで必須となる decode メソッドで各プロパティの初期化を行っています。演算子 <| の意味などは Himotokiのドキュメントを参照してください。

Resp.swift

import Himotoki

struct Resp {
    var venues: [Venue]
}

extension Resp: Decodable {
    static func decode(e: Extractor) throws -> Resp {
        return try Resp (
            venues: e <|| "venues"
        )
    }
}

Venue.swift

import Himotoki

struct Venue {
    var id: String
    var name: String
}

extension Venue: Decodable {
    static func decode(e: Extractor) throws -> Venue {
        return try Venue (
            id: e <| "id",
            name: e <| "name"
        )
    }
}

APIリクエスト用の型作成

最初にRequestType プロトコルを継承したプロトコルを作成します。

FoursquareRequestType.swift

protocol FoursquareRequestType: RequestType {

}

RequestType プロトコルに準拠するためには次の5項目を実装する必要があります。

  • typealias Response
  • var baseURL: NSURL
  • var method: HTTPMethod
  • var path: String
  • func responseFromObject(object: AnyObject, URLResponse: NSHTTPURLResponse) throws -> Response

まず、ベース URL を返す実装をプロトコル拡張で持たせます。

extension FoursquareRequestType {
    var baseURL: NSURL {
        return NSURL(string: "https://api.foursquare.com")!
    }
}

v2/venues/search をリクエストするための型を作成します。

struct GetVenueList : FoursquareRequestType {
    typealias Response = Resp

    var method: HTTPMethod {
        return .GET
    }

    var path: String {
        return "/v2/venues/search"
    }

    var parameters: AnyObject? {
        return [
            "ll": "40.7,-74",
            "oauth_token": "(YOUR_OAUTH_TOKEN)",
            "v": "20160529",
        ]
    }

    func responseFromObject(object: AnyObject, URLResponse: NSHTTPURLResponse) throws -> Response {
        return try decodeValue(object, rootKeyPath: "response")
    }
}

これで RequestType プロトコルに準拠できました。

リクエス

実際に foursquare API にアクセスしてみます。

let request = GetVenueList()

Session.sendRequest(request) { response in
    switch response {

    case .Success(let responses):
        self.venues = responses.venues

    case .Failure(let error):
        print(error)
    }
}

リクエストが成功すると response.Success になります。

f:id:koogawa:20160529142525p:plain

所感

最初 Himotoki 独特の演算子に少し戸惑いましたが、慣れてしまうとかなり便利に感じられるようになりました。

APIKit も活発にアップデートが続けられており、作者である ishkawaさんが APIKit の使い方を積極的に発信されているのが嬉しいです。

APIKit + Himotoki とても良さそうです😃

サンプルコード

github.com

リンク

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なプルリク を頂きました!ありがとうございます。