読者です 読者をやめる 読者になる 読者になる

koogawa log

iOS、Android、foursquareに関する話題

確定申告会場の様子をなんとなく可視化するサービスを作った

ネタ

というわけで作りました。

http://koogawa.github.io/kakutei/

「サービス」って書きましたが、Twitterの検索結果を並べただけです

なお、すべての税務署は網羅できていません。リクエストがあれば追加します💪

つくりかた

材料

手順

Twitterの検索窓から検索します。

https://twitter.com/search?f=tweets&q=確定申告&src=typd

そしたら、検索結果画面の「︙」から「この検索をサイトに埋め込む」をクリックします。

レイアウトなどを設定して「ウィジェットを作成」をクリックします。

ソースコードが吐き出されるので、それを自分のHTMLに貼り付けます。これを必要な分だけ繰り返します。

最後にHTMLをサーバーにアップして完成です🎉

所感

ツイートを見ていると、今年から記載が義務付けられたマイナンバー絡みの混乱が目につきました。これから提出する人はマイナンバーカードを忘れずに持参しましょう。マイナンバーカードを持っていない人は必要な書類を準備しましょう。

重要なお知らせ<申告手続には>:平成28年分 確定申告特集|国税庁

Swift で Foursquare の API を使う(Swift 3編)

API Swift foursquare

過去に書いた「Swift で Foursquare の API を使う - koogawa log」を Swift 3 でも動くように書き直しました。


今月からSwiftの勉強を始めているkoogawaです。

勉強も兼ねて、FoursquareAPI Client を Swift で作ってみました。

github.com

  • 通信には URLSession を利用
  • APIのパス(venues/search等)は自由に指定できる
  • レスポンスのJSONパース方法は利用者側で選べる
  • 簡単なエラーハンドリングができる

動作環境

  • Xcode 8.2
  • Swift 3.0
  • FoursquareAPIClient 3.0.0
  • CocoaPods 1.2.0

使い方

インストール

CocoaPods からインストールできるようにしました。

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
use_frameworks!

target 'HogeApp' do
    pod 'FoursquareAPIClient'
end

セットアップ

ソースコード上で FoursquareAPIClient をインポートします。

import FoursquareAPIClient

アクセストークン or クライアントID+クライアントシークレットで初期化します。

let client = FoursquareAPIClient(accessToken: “YOUR_ACCESS_TOKEN”)

or

let client = FoursquareAPIClient(clientId: "YOUR_CLIENT_ID",
    clientSecret: "YOUR_CLIENT_SECRET")

APIバージョンを指定することもできます。

// Set v=YYYYMMDD param
let client = FoursquareAPIClient(accessToken: “YOUR_ACCESS_TOKEN”,
    version: "20140723")

ベニュー検索

さっそくベニューを検索してみましょう。

必要なパラメータを Dictionary にセットします。

let parameter: [String: String] = [
    "ll": "35.702069,139.7753269", // 緯度経度
    "limit": "10", // 一度に取得する件数
];

先ほど初期化した FoursquareAPIClientrequest(path:method:parameter:) メソッドを実行します。引数にはAPIのパスとパラメータをセットします。method 引数を省略することで .get になります。

client.request(path: "venues/search", parameter: parameter) {
    result in

    switch result {

    case .success(let data):
        // 成功
        print(NSString(data:data, encoding:String.Encoding.utf8.rawValue)!)

    case .failure(.connectionError(let error)):
        // 通信エラー
        print(error)

    case .failure(.apiError(let error)):
        // 通信はできたけどAPIエラー
        print(error.errorType)   // e.g. endpoint_error
        print(error.errorDetail) // e.g. The requested path does not exist.
    }
}

レスポンスは Result<T, Error> 型で返ってきます。

public enum Result<T, Error> {
    case success(T)
    case failure(Error)

    init(value: T) {
        self = .success(value)
    }

    init(error: Error) {
        self = .failure(error)
    }
}

エラー時には連想値として error が返ってくるので、簡単なエラーハンドリングができるようになっています。

チェックイン

ベニューにチェックインすることもできます。

チェックインに必要なパラメータをセットします。

let parameter: [String: String] = [
    "venueId": "55b731a9498eecdfb3854a9”, // Venue ID
    "ll": "37.33262674912818,-122.030451055438", // 現在地
    "alt": "10”, // 標高
];

同じように request(path:method:parameter:) メソッドを実行します。今回はチェックインAPIの仕様に従い、method に .post を指定しています。

client.request(path: "checkins/add",
    method: .post,
    parameter: parameter) { result in

    switch result {

    case let .success(data):
        // チェックイン成功

    case let .failure(error):
        // チェックイン失敗
    }
}

他のAPIも path を書き換えることでリクエストすることができます。

デモ

近隣のベニューを検索するデモアプリを添付しております。

https://github.com/koogawa/FoursquareAPIClient/tree/master/Example

f:id:koogawa:20150724172338p:plain

ベニューをタップすることでチェックインもできますよ。

デモアプリのアーキテクチャ

手書きですいませんw

f:id:koogawa:20150724170326j:plain

Venue オブジェクトはベニューデータを抽象化したデータモデルです。

FoursquareAPIClient は、Foursquare API とのネットワーク通信を抽象化します。リクエストを送信して、レスポンスを Data にして返すところまでの責任を負います。

FoursquareManager は、Venue オブジェクトを管理するシングルトンオブジェクトです。ベニューリストをコントローラから取得するときはこれを介して操作します。予め accessToken プロパティにアクセストークンを設定しておく必要があります(ここは微妙かも)。

実質的に、FoursquareManager だけが FoursquareAPIClient を操作することになります。

ここはこうしたほうが良い、などあればツッコミよろしくお願いします。

リンク

RealmSwiftで簡単なGPSロガー作ってみたのでメモ(Swift 3編)

realm Swift iOS

以前書いた下記記事のソースコードが古くなっていたので、Swift 3対応版として書き直しました。

RealmSwiftで簡単なGPSロガー作ってみたのでメモ - koogawa log


以前から気になっていた Realm ですが、先日受講した id:KishikawaKatsumi さんの授業をきっかけに、実際に触ってみたくなりました。 Realm を理解するには何か作ってみるのが一番ってことで、簡単なGPSロガーを作ってみました。

f:id:koogawa:20150802150835p:plain

次のような機能があります。

  • Startボタンを押すと位置情報を記録開始
  • アプリをバックグラウンドに落としても記録し続ける
  • 位置情報が取得されると地図にもピンが立つ
  • 1日経過したデータは起動時に自動削除
  • Stopボタンを押すと位置情報の取得終了

***

以下、実装メモです。

動作環境

  • Xcode 8.3
  • Swift 3.0
  • RealmSwift 2.4.2
  • CocoaPods 1.2.0

RealmSwift インストール

次のような Podfile を用意して pod install します。

use_frameworks!

target 'GPSLogger' do
  pod 'RealmSwift'
end

post_install do |installer|
  installer.pods_project.targets.each do |target|
    target.build_configurations.each do |config|
      config.build_settings['SWIFT_VERSION'] = '3.0'
    end
  end
end

RealmSwift インポート

import RealmSwift

モデルクラス作成

位置情報を格納するためのモデルクラスを作ります。緯度、経度、作成日時のみを保持するようにしました。

class Location: Object {
    dynamic var latitude: Double = 0.0
    dynamic var longitude: Double = 0.0
    dynamic var createdAt = Date(timeIntervalSince1970: 1)
}

Realm では次の型が使用できます:Bool, Int8, Int16, Int32, Int64, Double, Float, String, NSDate, NSData

https://realm.io/docs/swift/latest/#supported-types

Startボタンを押したときの処理

位置情報の取得を開始します。

self.locationManager.startUpdatingLocation()

位置情報を永続化

取得できた位置情報(CLLocation)から Location モデルを生成し、Realmに永続化できるようにします。

let location = makeLocation(rawLocation: rowLocation)

// Get the default Realm
let realm = try! Realm()

// Add to the Realm inside a transaction
try! realm.write {
    realm.add(location)
}

テーブルビューを更新

addNotificationBlock メソッドでブロックを登録すると、Realmオブジェクトが更新されたときに通知を受けることができます。

self.token = realm.addNotificationBlock {
    [weak self] notification, realm in
    self?.tableView.reloadData()
}

ここでは通知を受け取るたび(つまり位置情報が追加されるたび)にテーブルビューを更新しています。

Stopボタンを押したときの処理

位置情報の取得を停止します。

self.locationManager.stopUpdatingLocation()

通知の停止

先ほど addNotificationBlock メソッドでブロックを登録した際に保持した NotificationToken オブジェクトの stop メソッドを呼ぶことで、通知の受け取りを停止します。

if let token = self.token {
    token.stop()
}

アプリを起動したときの処理

すでに永続化されている位置情報をロードします。

// Get the default Realm
let realm = try! Realm()

// Load recent location objects
return realm.objects(Location.self).sorted(byKeyPath: "createdAt", ascending: false)

createdAt でソートをかけています。

古いデータの削除

古いデータがいつまでも残ってしまうのを防ぐため、1日経過した位置情報ログを削除します。

DispatchQueue.global().async {
    // Get the default Realm
    let realm = try! Realm()

    // Old Locations stored in Realm
    let oldLocations = realm.objects(Location.self).filter(NSPredicate(format:"createdAt < %@", NSDate().addingTimeInterval(-86400)))

    // Delete an object with a transaction
    try! realm.write {
        realm.delete(oldLocations)
    }
}

データ量によっては時間がかかるので、バックグラウンドで実行するようにしています。

所感

CoreData を初めて使った時と比べると、かなりスムーズに導入・実装ができました。ドキュメントが充実しているのも安心ですね。

ソースコード

こちらに全てアップしてあります。Realm を使い慣れている方にとってはツッコミどころ満載だと思いますので、ご指摘など歓迎です:-)

github.com

リンク

React Native さわってみたメモ

ReactNative iOS Android

今年のはじめにこんな記事がバズった。

b.hatena.ne.jp

批判的なコメントが多かったせいか、記事自体はもう消されてしまっている。

自分も数年前に Titanium を使ったことがあり、そんときは「Nativeで頑張ったほうが良さそう」と思って使うのをやめてしまった。ただ、その頃から状況も変わっているだろうし、この人の言っていることを無下に批判する気にはなれなかった。

そんなわけで、実際に自分の手で動かしてみることにした。以下はその時の作業メモ(自分用なので読みやすさは考慮してません)

動作環境

  • react-native: 0.41.1
  • Xcode 8.3
  • Android Studio 2.2.3
    • Build #AI-145.3537739, built on December 2, 2016
    • JRE: 1.8.0_112-release-b05 x86_64
    • JVM: OpenJDK 64-Bit Server VM by JetBrains s.r.o

作ったソースコード

https://github.com/koogawa/ReactNativeSample

インストール

Getting Started に従って、Node、Watchman、React Native CLI をインストール。

brew install node
brew install watchman
npm install -g react-native-cli

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

簡単なiOSアプリとAndroidアプリを作ってみる。

iOS

react-native init AwesomeProject
cd AwesomeProject

これだけで最低限の機能を備えたiOSアプリを生成してくれる。(言語はObjective-C

吐き出されたソース

react-native run-ios

を実行すれば iOS Simulator を起動してくれる。もしくは出力された xcodeproj を Xcode で開いて自分でビルドすることもできる。

ビルド自体は本当に簡単にできた。

cmd+d でデバッグメニューが出てくる。

出力されたコードを見てみる

デバッグメニューを出すのをどうやって実現しているのか気になったのでソースを追ってみると

    RCTKeyCommands *commands = [RCTKeyCommands sharedInstance];

    // Toggle debug menu
    [commands registerKeyCommandWithInput:@"d"
                            modifierFlags:UIKeyModifierCommand
                                   action:^(__unused UIKeyCommand *command) {
                                     [weakSelf toggle];
                                   }];

こんな感じでやっていた。cmdキーを検知できるの知らなかった。

AppDelegate も読んでみる。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  NSURL *jsCodeLocation;

  jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index.ios" fallbackResource:nil];

  RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation
                                                      moduleName:@"ReactNativeSample"
                                               initialProperties:nil
                                                   launchOptions:launchOptions];
  rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];

  self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
  UIViewController *rootViewController = [UIViewController new];
  rootViewController.view = rootView;
  self.window.rootViewController = rootViewController;
  [self.window makeKeyAndVisible];
  return YES;
}

index.ios.js から RCTRootView を生成して self.window.rootViewController にセットしてる感じなのね。

ためしに index.ios.js に変更を加えて再度ビルドしてみたけど、AppDelegate に差分は出なかった。某クラスプラットフォームのように、単純に機械的なコードを吐き出してるわけでは無さそうだ。この辺はとてもシンプル。

Android

同じソースコードAndroidアプリも作ってみる。

SDK Manager を起動して、必要なパッケージをインストール。

このとき Android SDK Build-Tools 23.0.1 にチェックが入ってないとダメ。

環境変数 ANDROID_HOME にもパスをセットする必要がある。

export ANDROID_HOME=${HOME}/Library/Android/sdk
export PATH=${PATH}:${ANDROID_HOME}/tools
export PATH=${PATH}:${ANDROID_HOME}/platform-tools

最初、ここのパスを間違えて結構ハマった。

次に Android Studio から AVD Manager を起動して、エミュレータを起動しておくのがポイント。

f:id:koogawa:20170205073018p:plain

f:id:koogawa:20170205073232p:plain

AVD Managerは

android avd

のコマンドでも起動できる。

react-native run-android

でアプリを実行。

こんな感じで Android アプリもビルドできた。

タブバーを表示してみる

TabBarIOSの公式ドキュメント通り実装してみるが、何故かエラーが出る。。🤔

調べてみてわかったのだが、

var React = require('react');
var ReactNative = require('react-native');

みたいな書き方はすでに古くなっているらしい😩

Does React Native use require or import? - Stack Overflow

import React, { Component } from 'react';
import {
  AppRegistry,
  TabBarIOS,
  StyleSheet,
  Text,
  View
} from 'react-native';

のような書き方に修正することで、なんとかビルドが通った。

f:id:koogawa:20170205075010p:plain

見てるドキュメントが間違ってるのかもしれないけど、公式ドキュメントが最新になってないのはちょっと焦った😞

TabBarIOS という名前なので、Androidの場合はどうするんだろう?という疑問が湧いたが、やはり同じことを考える人がすでにいた。

How to make Tab bar for Android like TabBarIOS in react native? - Stack Overflow

こうやってiOS用のコードとAndroid用のコードを別々に管理していくことになりそう。

所感

  • ホントに簡単にアプリを作り始められる
  • 公式ドキュメント通りやっても動かないことが多々ある
    • 正しい情報を探すことになり、ここに多くの時間を割くことになりそう
  • ビルドしてみないとエラーかどうかわからないのがつらい
  • コードの補完機能がないのが地味につらい
  • パフォーマンスとかはもうちょっと使ってみないとわからない
  • iOS/Android のバージョンが上がったときに追従してくれるか不安

お世話になったリンク

2016/11/29 #potatotips 35 (iOS/Android開発Tips共有会) @トレタ に参加してきたよ

iOS 勉強会

f:id:koogawa:20161130002127j:plain

昨日はトレタさんで開催された potatotips #35 (iOS/Android開発Tips共有会) に参加してきました。

potatotips.connpass.com

とてもオシャレなオフィスでした。

f:id:koogawa:20161130002309j:plain

社内にはリラックマさんの姿も。

f:id:koogawa:20161130002331j:plain

バーカウンターの裏はこんな感じ。id:masuidrive さんにコーヒーを淹れて頂きました。ごちそうさまでしたm(_ _)m

f:id:koogawa:20161130002643j:plain

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

以下はiOSの発表メモになります。間違いなどあれば教えて下さい(遅刻してしまったため、最初の方はメモが取れておりません🙇)


目次:

トレタでのSwift 3 対応

y_koh さんによる発表(私が遅刻したためメモが取れず🙇🏻)

「来年のSwift 4では楽になるはず」

私も期待してます😇

MVVM で Protocol Oriented をやってみた話 iOS

kumapo さんによる発表(私が遅刻したためメモが取れず🙇🏻)

iOSアプリ開発者なら簡単にできる!! TouchBarにツイートを表示するTips

yimajo さんによる発表(私が遅刻したためメモが取れず🙇🏻)

yimajo さんには休憩中に Touch Bar を見せて頂きました!初めて見たので感動でした 😂

GitLab CI for iOS

ikamooon さんによる発表です。GitLab で CI をまわすお話でした。

GitLab CI を使うための Multi Runner の設定方法などを詳しく解説されていました。

docs.gitlab.com

github.com

Embedded Framework in Action

TachibanaKaoru さんによる発表です。

アプリ内のソースコードをプロセス間で共有する仕組みである Embedded Framework についてのお話です。実際に使う上でのメリット・デメリットなど、ノウハウが満載でした。

ちなみに、QAタイムでは Tachibana さんが発表に使われていたMacBook Pro の使用感に関する質問が相次ぎましたw

Popoverの実装比較

Yuta Hoshinoさんによる発表です。

PickerをPopoverスタイルでコード1行で表示できる SwiftyPickerPopover について、デモを交えながら解説されていました。

github.com

Firebaseのススメ

nafu003 さんによる Firebase のお話です。

Firebase は Crash Reporting 機能も統合しているため、クラッシュが発生したユーザがどんな行動をしていたか?なども分析することができるそうです。他にもデモを交えながら様々な機能を紹介されていました。

UIコンポーネントを磨く

Ryota Hayashi さんによる発表です。

UIコンポーネントを開発する上でUIKitと同連携させていくか、というお話でした。ピンコードを入力するUIを例として、サンプルコードを交えながら解説されていました。華やかなUIをOSSで実現するのも良いけど。。の話はとても共感できました。

所感

tokorom さんも仰っていましたが、今日は質疑応答時間がアクティブで良い感じでした!(質問がひとつも出なかった発表はなかったのでは?)司会の方の進め方も上手かったのだと思います。

また、今回は噂の新型MacBook Proで発表される方が何人かいらっしゃいました。やはりUSB-Cのディスプレイアダプタまわりで苦労されている方が多かったように思えます。普及するまでにはしばらく時間がかかりそうですね。

ローディング中に画面ごと回転するライブラリを作った話

ネタ iOS

アプリが何らかの処理中にローディング画面を出すのは一般的だと思います。

f:id:koogawa:20161112233719p:plain

SVProgressHUD

これらに対抗して、画面全体がぐるぐる回るライブラリをネタで作ってみました。

github.com

インストール

CocoaPods でインストールできます。

pod "RollingProgressHUD"

使い方

RollingProgressHUD をインポートします。

import RollingProgressHUD

ローディング開始時に

RollingProgressHUD.show()

を呼び、ローディング完了時に

RollingProgressHUD.dismiss()

を呼ぶだけです。

デモ

ローディング中に画面ごと回転します。

f:id:koogawa:20161112234359g:plain

目が回りそうですね(^q^)

エンジニア立ち居振舞い:自分のタスクよりメンバーのコードレビューを優先する

開発全般

お題「エンジニア立ち居振舞い」 ということで自分も書きます。

以前、Twitterにつぶやいたのですが

自分のタスクに着手する前に、チームメンバーのコードレビューをするように心掛けています。

コードレビューが滞ると、次のような悪影響があると思っています。

  • 他のメンバーが次のタスクに着手できない
  • プルリクエストが消化されないうちにもメインのブランチはどんどん進んでいるのでコンフリクトが起こる
    • →コンフリクトを直す手間が増える
  • 誰が何をしているか把握しにくくなる

当然、自分の作業は止まってしまいますが、それよりも上に書いた悪影響の方がまずいと思っていて、ここを解決することで全体的には開発スピードが上がると思っています。

共感した立ち居振る舞い