koogawa blog

iOS、Android、foursquareに関する話題

Re: 半年後の自分へ

半年ぐらい前ですが、数ヶ月後に一児の父親になる自分に宛てて質問を書いていたので、回答したいと思います。

今もコードは書けていますか?

割と書けています。

子供が生まれる前(2016年の冬ごろ)と比べるとさすがにコミット数は減りましたが、今でも気になる技術があれば簡単なサンプルコードを書いてみたり、自分が公開しているライブラリのメンテも続けてますよ。

とは言え、いつでも自由にコードを書けるわけではなく、子供が寝た後の時間を利用して書くことが多いです(この記事も子供を寝かしつけた後に書いてます)。

逆に言うと、子供が起きているうちは何もできないと思った方が良いです。

勉強会には参加できてますか?

できるだけ子供と過ごす時間を大事にしたいので、参加する勉強会はかなり厳選するようになりました。

どうしても参加したい勉強会の例としては、

  • 会場となるオフィスを一度見てみたい
  • 会ってみたいエンジニアが参加者の中にいる
  • 自分が発表メンバーである

などです。

ちなみに最近は、Twitter上で開催される勉強会があるのも嬉しいですね。

Swift Tweets

こういった勉強会が今後も増えていってほしいですな😃

情報収集の時間はありますか?

これも「コードを書く時間はあるのか?」の回答と同じで、子供が起きているうちはほぼ不可能です。

さらに、最近は自転車通勤に切り替えたため、通勤中の電車の中で情報収集することができなくなりました。

よって、

  • 朝、子供が起きる前のわずかな時間
  • 昼休み
  • 夜、子供が寝た後

に集中して情報収集する感じになっています。

その他、半年前の自分に言いたいことは?

「子供が起きてる時間は何もできない」と散々書いてきましたが、これは決してツラいわけではなく、むしろとても幸せな時間なんですぞ😊

最初の数ヶ月は子供の顔が毎日のように変わっていくので、一緒にいる時間を大切にするんじゃよ😇

【Swarm】ステッカーのアップグレードを効率よく行うための方法

こんにちは。koogawaです。最近また無職になりました😇

今日はひさびさのSwarmネタです。

皆さんはチェックインする際にステッカーを使ってますでしょうか?

f:id:koogawa:20170403170804p:plain

ステッカーを使うことで、今の自分の気持ちを表したり、食事中や仕事中などのステータスを知らせることができます。

また、ステッカーにはレベルがあり、2X〜3X にアップグレードさせることができます。例えば 2X ステッカーを使うとチェックイン時に獲得できるコイン数を2倍に増やすことができます。

今回はこのアップグレードを効率よく行うための方法を紹介します。

※コインの使い道については次の記事でとても丁寧に解説されています。

ろくデブログ - ろくじゅうどデザインのブログ

ステッカーのアップグレードを効率よく行うための方法

例えばこの「Dark & Swarmy」ステッカーを 3X にアップグレードするためには、3件の新しいカクテルバーにチェックインする必要があります。

f:id:koogawa:20170403171428p:plain

しかし、「カクテルバー」がどこにあるのかすぐにはわかりませんね。残念ながらSwarmの検索機能にもカテゴリで絞り込む機能はありません。

そこで活躍するのが拙作「Venue Map」です。

Venue Map for foursquare

Venue Map for foursquare

  • Kosuke Ogawa
  • ナビゲーション
  • 無料

Venue Mapは、世界中に存在するベニューを地図上に表示することができるアプリです。

まずはアプリを起動して、右下の設定ボタンをタップします。

f:id:koogawa:20170403171532p:plain

「By Category」から表示するカテゴリを絞り込むことができます。

f:id:koogawa:20170403172208p:plain

「夜の娯楽スポット」 → 「バー」 → 「カクテルバー」の順にドリルダウンしていきます。

f:id:koogawa:20170403171609p:plain

そして、設定画面を閉じるとカクテルバーのベニューだけが表示されます。

f:id:koogawa:20170403171624p:plain

残念ながら私の大好きな練馬にはカクテルバーが1件しかないようです😫

そこで、渋谷に移動してみましょう。

f:id:koogawa:20170403171638p:plain

さすがは渋谷。カクテルバーだけでこれだけのベニューが見つかりました!

こんな具合に、ステッカーのアップグレードに必要なカテゴリに属するベニューを効率よく見つけることができます。

良ければ活用してみてください😃

iOSアプリ内のCookie情報を覗けるライブラリをつくった👀(デバッグ用)

iOSアプリのデバッグ中にCookieの中身が見たくなったので作りました。

github.com

f:id:koogawa:20170326090809p:plain

機能

  • アプリが保存するCookie一覧を表示
  • Cookieをひとつずつ削除

インストール

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

pod 'CookieManager'
pod install

使い方

CookieManager をインポートします。

import CookieManager

UINavigationController の rootViewController として表示する例です。

let viewController = CookiesViewController()
let naviController = UINavigationController(rootViewController: viewController)
self.present(naviController, animated: true, completion: nil)

これだけです。

参考リンク

Stackoverflowの質問と回答を自由に編集できる権限をもらった

以前、こんな記事を書きました。

blog.koogawa.com

その後も地道に活動を続けた結果、ようやく 2,000 reputation に到達しました🎉

そこで今回は、この reputation level に到達するとどんな事ができるようになるのか書いていきたいと思います。

質問と回答を編集できるようになる

このレベルに達すると、Edit Questions And Answers 権限が付与され、自分以外の質問と回答を「いつでも」「自由に」編集することができるようになります。

この権限がなくとも「編集の提案」を出す事はできたのですが、reputation の高いユーザに承認してもらうまで反映されませんでした。しかし、この権限が付与されたなら、もう承認を待つ必要はありません。edit リンクから編集画面を開き、編集ボタンを押すだけで即時反映されます。

どんなときに投稿を編集するのか?

公式ドキュメントには次のような記載があります。

  • to fix grammatical or spelling mistakes
  • to clarify the meaning of a post without changing it
  • to correct minor mistakes or add addendums / updates as the post ages
  • to add related resources or hyperlinks

つまり、

  • 文法またはスペルミスを直すべきとき
  • 投稿の内容をより明確化したいとき (ただし投稿の趣旨は変えないように)
  • 細かいミスの修正や時間のたった投稿に補足や更新を追加したいとき
  • 関連する情報またはハイパーリンクを追加したいとき

に投稿を編集すべきです。

また、

Tiny, trivial edits are discouraged

とあるように、細かい些末な編集(例えば1文字直すだけ等)は推奨されません。

編集の提案も承認できるようになる

先ほどもちょっと触れましたが、このreputation levelに達していなくとも編集の提案を出す事はできました。2,000 reputationを超えると、これらの提案を承認する事ができるようになります。

Twitterにもつぶやきましたが、ネイティブによって修正された英語の内容は、見ているだけで勉強になります。

また、たくさんレビューすることによって獲得できるバッジも存在するので、積極的にレビューしていきたいと思います💪

※ただし、1日にレビューできる数には制限があるようです。

f:id:koogawa:20170306224136p:plain

さらに上の権限

2,000 reputation の上にもまだまだ権限が存在します。

Privileges - Stack Overflow

私のStackoverflow活動はこれからも続きます…

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

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

確定申告会場の混み具合をなんとなく可視化するサイト: http://koogawa.github.io/kakutei/

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

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

つくりかた

材料

手順

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

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

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

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

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

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

所感

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

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

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

過去に書いた「Swift で Foursquare の API を使う - koogawa blog」を 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編)

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

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


以前から気になっていた 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 {
    @objc dynamic var latitude: Double = 0.0
    @objc dynamic var longitude: Double = 0.0
    @objc 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.observe {
    [weak self] notification, realm in
    self?.tableView.reloadData()
}

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

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

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

self.locationManager.stopUpdatingLocation()

通知の停止

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

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

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

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

// 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

リンク