koogawa blog

iOS、Android、foursquareに関する話題

【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

リンク

React Native さわってみたメモ

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

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

インストール

Introduction · React Native に従って、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 のバージョンが上がったときに追従してくれるか不安

お世話になったリンク