Quantcast
Channel: その後のその後
Viewing all 317 articles
Browse latest View live

[watchOS][iOS][WatchKit][Swift]watchOS 2 の新機能のサンプルコード集『watchOS-2-Sampler』を公開しました

$
0
0

先週の WWDC15 にて watchOS 2 が発表され、ついに待望のネイティブ動作する Apple Watch アプリがつくれるようになりました。それに伴い、WatchKit には多くの機能が追加され、ClockKit や WatchConnectivity 等の新しいフレームワークも登場しました。さらにいえば、従来フレームワークもウォッチ側で動作するようになったことから、これらもある意味新APIであるといえます。*1


そんなわけで、数が少なすぎてびっくりした 初代 watchOS の発表時とは打って変わって、今回は遥かに多くの「新機能」があります。どんな機能が追加されたのかは既に多くのキーノートのまとめ記事などで紹介されてはいますが、新しいAPIはどう使うのか、実際に何がどこまでできるのか、快適に動作するのか、といった具体的なところが、英語のドキュメントや動画や記事をながめているだけだと正直あまりよくわかりません。やはり実際にコード書いて動かしてみるのが一番わかりやすい、ということで、


watchOS 2 の新機能のサンプルコード集『watchOS-2-Sampler


をつくりました。オープンソースなのでどなたでも GitHub から clone してお試しいただけます。


このアプリで、とあるアメリカのハッカソンにて賞をいただきました!それについては最後に。


Contents

今のところ14個のサンプルが入っています。まだまだ少ないですが、随時追加していく予定です。あと今回から Swift で書いています。機能追加のプルリク大歓迎です!


アイコンデザインはいつものように okazu 氏にお願いしました!


f:id:shu223:20150614131615j:image:w242



各サンプルのスクリーンショットはAppleの正式リリースまで控えます。代替として、WWDCの動画などAppleによる公開資料からの画像を一部用いています)

Accelerometer

Core Motion を用いて加速度センサの値を取得するサンプル。


Gyroscope

Core Motion を用いてジャイロスコープの値を取得するサンプル。


Pedometer

CMPedometer を用いて歩数や距離、階の昇り降り情報を取得するサンプル。


Heart Rate

HealthKit を用いて心拍数を取得するサンプル。


Table Animations

WKInterfaceTable のセル(row)をアニメーション付きで insert / remove するサンプル。`insertRowsAtIndexes:withRowType:`, `removeRowsAtIndexes:` メソッドを利用。


Animated Properties

`animateWithDuration:animations:` メソッドを利用して、アニメーションで拡大縮小やフェードイン/アウト、移動(実際には Alignment)を行うサンプル。


Audio Rec & Play

`presentAudioRecordingControllerWithOutputURL:preset:maximumDuration:actionTitle:completion:` メソッドを利用して音声を録音する UI を表示し、


f:id:shu223:20150614134536j:image:w400

(WWDCのセッション動画より)

録音したファイルを `presentMediaPlayerControllerWithURL:options:completion:` メソッドを利用して再生するサンプル。


f:id:shu223:20150614134537j:image:w400

(WWDCのセッション動画より)


参考記事:watchOS 2 のオーディオ録音、再生機能を利用する - Qiita


Picker Styles

WKInterfacePicker の全スタイル(List, Sequence, Stack)を試せるサンプル。


f:id:shu223:20150614163013j:image:w600

(WWDCのセッションスライドより)


Taptic Engine

WKInterfaceDevice の `playHaptic` メソッドに指定できる全タイプ(WKHapticType)を試せるサンプル。


ちなみに WKHapticType は次のように定義されています。


enum WKHapticType : Int {
    
    case Notification
    case DirectionUp
    case DirectionDown
    case Success
    case Failure
    case Retry
    case Start
    case Stop
    case Click
}

Alert

`presentAlertControllerWithTitle:message:preferredStyle:actions:` メソッドで指定できる全アラートスタイル(WKAlertControllerStyle)を試せるサンプル。


ちなみに WKAlertControllerStyle は次のように定義されています。


enum WKAlertControllerStyle : Int {
    
    case Alert
    case SideBySideButtonsAlert
    case ActionSheet
}

Animation with Digital Crown

デジタルクラウンの回転に合わせ、WKInterfacePicker にセットしたアニメーションを表示するサンプル。`setCoordinatedAnimations` メソッドを利用。


f:id:shu223:20150614134538j:image:w350

(WWDCのセッションスライドより)


Interactive Messaging

iPhone と Apple Watch で相互にメッセージをやりとりするサンプル。Watch Connectivity フレームワークを利用。


f:id:shu223:20150616165131j:image:w350


Open System URL

WKExtension の `openSystemURL:` メソッドを利用して、電話およびSMSアプリを開くサンプル。


Audio File Player

WKAudioFilePlayer を利用してオーディオファイルを再生するサンプル。


参考記事:watchOS 2 のオーディオ録音、再生機能を利用する - Qiita


Known Issues

ハッカソンでガッと仕上げたこともあり、既に入っているサンプルも以下の点がまだ未完成です。

  • Audio Rec & Play
    • 実機での録音ができない。シミュレータでは可
    • 録音音声をshared containerに保存する必要がある。そうするとOSSとして試すハードルが上がってしまうのが悩みどころ。。
  • HeartRate
    • 完全に未完成。いつかやります
  • Open System URL
    • Phoneを押しても電話アプリが起動しない
    • ドキュメント通りにやってるつもりだけど・・・?
    • `sms:` は同様のやり方で動作している

あと、ジャイロは実機でもとれてませんが、実装は正しいと思ってるので一応サンプルとしては成立してるのかなと(実装間違ってたらプルリクください!)


ClockKit、WatchConnectivity は試してサンプルを入れる予定です。(2015.6.16追記:Watch Connectivity はひとつ追加しましたが、引き続きファイル送信等も試して追加する予定)


サンフランシスコで開催された  Watch Hackathon で入賞しました!

WWDC会期中にドキュメント読みつつちまちまサンプル実装はしてたのですが、WWDC終了直後の週末に Apple Watch のハッカソン がタイミングよくサンフランシスコで開催されていたので、そこでいったん仕上げて GitHub で公開し、緊張しつつ英語でプレゼンしたところ・・・なんと!賞をいただいてしまいました!!!!


f:id:shu223:20150614131616j:image:w320


コミュニティへの貢献を評価していただけたようです。英語力はプレゼンター随一の下手さだったと思いますが、ちゃんと伝わったようで、プレゼン直後、いろんな人が寄ってきてくれて「あんた、ナイスだ!」「7、8の頃から見てるよ、会えて嬉しいよ」みたいなことを言っていただけたのがとても嬉しかったです。



こんなときの挨拶用にと iOS Sampler ステッカーをつくって持っていったのが役立ちました。


f:id:shu223:20150614131617j:image:w320


あと少し残ってるので、欲しい方はお声がけください!(iOS 9 が出るまでしか使えないデザインなのでw)


関連記事:


FAQ: iOS-9-Sampler も出すの?

今回はたまたまハッカソンがあったのと、watchOS 2 のサンプルは基本的なものが多いので、早く出すことにこそ意味があるだろう、ということで即時公開しましたが、iOS-9-Sampler はいつも通り正式リリースに合わせて公開する予定です。




そんなわけで、引き続き watchOS-2-Sampler をよろしくお願いします!



こちらもどうぞ


*1:たとえば watchOS 1 から WatchKit Extension では Core Motion は使えましたが、それは単にiPhone側の機能を利用するだけのものなので「新機能」であるとはいえません。が、watchOS 2 ではウォッチ側で動作するようになったので、watchOS にとっては「新機能」です。実際、WWDC 以降、iOS Developer Library とは別に watchOS Developer Library がオープンし、watchOS 側で利用できるフレームワーク群のドキュメント類が別途管理されるようになっています。(Mac。には以前からあった Core Image が、iOS では 5 から新機能として加わった、みたいなもの。・・・というたとえは余計わかりにくいでしょうか・・・?)


[watchOS][iOS][Swift][WatchKit]watchOS 2 新機能の細かい話5つ #potatotips

$
0
0

本日、potatotips #18 という iOS / Android の開発Tips共有会(勉強会)で標題の発表をしてきました。



概要

つい先日ブログに書いた、watchOS 2 のサンプルコード集「watchOS-2-Sampler」


f:id:shu223:20150614131615j:image:w200


を実装するにあたって、気付いた注意点や、調べてみてわかったこと等、本当に細かい話の寄せ集めです。


その「細かい話」の内訳は以下の通り。

1. 2つのアセットカタログの 使い分け

watchOS 2 でも、Watch Extension と App Extension それぞれに Assets.xcassets が生成されますが、ネイティブだったらどっちもウォッチ側にあるんだからどっちに置いてもよかったりするの?という話。(結論としては、ダメです)


2. メディアデータの 保存場所

presentAudioRecordingController〜 で表示されるマイク録音UIに渡す、オーディオデータの出力先の話。


3. WKAudioFilePlayer

WKAudioFilePlayer が期待したものとちょっと違った、という話と、実装の注意点。


4. ウォッチ側 Bluetooth の (直接)利用

Core Bluetooth は使って、ウォッチ側の BLE を直接的に利用できるのか?という話。


5. Watch Connectivity のメッセージ送信可否の条件

WCSession を利用した、Phone - Watch 間の相互メッセージ送信の Reachablity の話


おわりに

そもそも先週のWWDCで発表されたてホヤホヤの機能ばかりなので、「細かい話」をいきなりするよりも、その機能自体の紹介や、実装方法を紹介した方がいいかともは思ったのですが、それぞれの機能の実装方法自体はサンプルコードの通りなのと、NDAの都合でスクショやデモを見せられないので、こういう形をとりました。


いずれも単体で記事を書くほどのことではないものの、同じように疑問に思っている人やハマる人はいると思うので、こうしてまとめて発表できる機会があってよかったです。


主催の BizReach さん、発表者・参加者のみなさまどうもありがとうございました!


[watchOS][iOS]UI/UX に影響の大きい watchOS 2 の新機能 3つ #uicrunch

$
0
0

本日開催された、『UI Crunch #5 スマートウォッチUIデザインの今』というイベントで、標題の発表をしてきました。



イベントタイトルからも察していただけるかもしれませんが、今回は勝手知ったる「iOSエンジニアの技術勉強会」ではなく、デザイナーさんやディレクターさんも集まる会なので、発表内容は相当に迷いました。タイミングとしてWWDC直後なので、そこで発表されたことについて話そう、ということだけは決めてはいたものの、具体的な実装の話をしてもポカーンとしてしまう方も多いだろうし、新機能をつらつら紹介しても退屈だろうし。。


というわけで、数ある新機能の中から、特に Apple Watch アプリの UI/UX にインパクトが大きいであろう(と僕は考えている)機能3つを抜粋して紹介しました。


具体的には以下の3つ。各内容についてはスライドをご覧いただければと。

  1. Complication (ClockKit)
  2. Watch Connectivity
  3. センサへのアクセス


他の登壇者の方々はがっつりつくりこんだウォッチアプリの話や、UIの考え方の話をされていて、非常に勉強になりました。お誘いいただいた主催のグッドパッチさん、DeNA さん、発表者・参加者のみなさまどうもありがとうございました!


[iOS][画像処理]【iOS9】Core Image の新機能:文字認識/追加フィルタ47種

$
0
0

先日、Gunosy さん主催の勉強会「WWDC Afterparty Roppongi」にて標題の発表をさせていただきました。



タイトルの通り、iOS 9 の Core Image の新機能について紹介&デモ *1 しました。


概要

大きく分けて、文字認識、新フィルタの紹介の2つ。


文字認識

WWDC初日に書いた下記記事でもわりと反響の大きかった機能。


で、OCR的なものを期待していたわけですが、リファレンスで CIFeatureTypeText を見ると、認識結果の文字列が入ってくるプロパティはないので、文字の「内容」を認識するのではなく、文字の「領域」を検出するものでした、という話。

class CITextFeature : CIFeature {
    
    var bounds: CGRect { get }
    var topLeft: CGPoint { get }
    var topRight: CGPoint { get }
    var bottomLeft: CGPoint { get }
    var bottomRight: CGPoint { get }
}

いわゆる「顔認識」も、「誰の顔か」を認識するのではなくて、顔の「領域」を検出するものがほとんどなのと同様ですね。


それはそれとしてOCRの前処理とかに便利だと思います。実装は非常に簡単で、顔認識とかとほぼ同じです。

let image  = CIImage(CGImage: imageView.image!.CGImage!)

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

let features = detector.featuresInImage(image)

for feature in features as! [CITextFeature] {
    // 認識結果の矩形を描画する処理
}

NDAのため iOS 9 での実行結果スクショを貼るのは自粛しますが、デモでは以下の2種類の画像について試し、


f:id:shu223:20150622065519p:image:w450



f:id:shu223:20150622065439j:image:w320


  • 前者は文字領域を完璧に検出
  • 後者はGitHub マークのある黒地に白文字の "shu223" というものしか認識されず

という結果になりました。わずかなパースや陰影(後者のような「写真」画像は、人間には均一に見える部分でも、二値化してみると結構色に偏りがあったりする)で認識精度がガクッと落ちるようです。


新フィルタ

CIFilter は

CIFilter(name: filterName, withInputParameters: params)

っていう感じでフィルタ名を文字列で指定する形式なので、API Diffs とかには新規追加フィルタは表記されない(Filter Reference というドキュメントもあるけど更新が遅い)ため、次のようにビルトインフィルタ名を全部出力しておいて、

let filterNames = CIFilter.filterNamesInCategory(kCICategoryBuiltIn)
for filterName in filterNames {
    print(filterName)
}

そのあと `diff` コマンドで新規追加分を抽出する、ということを毎年 iOS がメジャーバージョンアップするたびにやっています。


で、今年はフィルタ数の変化をグラフにしてみたのですが、


f:id:shu223:20150622071620j:image:w425


ひさびさにフィルタ数がグッと増えていて、ちょっと盛り上がりを感じます。


例によってNDAによってフィルタをかけた結果のスクショ掲載は控えますが、以下に新フィルタ名47種の一覧を載せておくので、名前から効果を察していただけると。

  • CIAreaAverage
  • CIAreaMaximum
  • CIAreaMaximumAlpha
  • CIAreaMinimum
  • CIAreaMinimumAlpha
  • CIBoxBlur
  • CICircularWrap
  • CICMYKHalftone
  • CIColumnAverage
  • CIComicEffect
  • CIConvolution7X7
  • CICrystallize
  • CIDepthOfField
  • CIDiscBlur
  • CIDisplacementDistortion
  • CIDroste
  • CIEdges
  • CIEdgeWork
  • CIGlassLozenge
  • CIHeightFieldFromMask
  • CIHexagonalPixellate
  • CIKaleidoscope
  • CILenticularHaloGenerator
  • CILineOverlay
  • CIMedianFilter
  • CIMotionBlur
  • CINoiseReduction
  • CIOpTile
  • CIPageCurlTransition
  • CIPageCurlWithShadowTransition
  • CIParallelogramTile
  • CIPDF417BarcodeGenerator
  • CIPointillize
  • CIRippleTransition
  • CIRowAverage
  • CIShadedMaterial
  • CISpotColor
  • CISpotLight
  • CIStretchCrop
  • CISunbeamsGenerator
  • CITorusLensDistortion
  • CITriangleTile
  • CIZoomBlur

会場ではシミュレータでデモしたのですが、GPUが効くので、実機でやるとチョッパヤです。



ちなみにちょっと余談ですが、会場で Yahoo の佐野さんから飛び出した「Apple は何のためにそんなにがんばってフィルタを追加しているのか?」という質問が印象的でした。


確かに、CIDetector 系は実用的だし、ボックスブラーとかノイズリダクションとか、色のブレンド系etcは写真加工系に幅広く使えそうなのでわかるとしても、CICrystallize とかの飛び道具系って実際には使う人いなそうだなぁと。そして僕はそういえば案件で Core Image 使ったことなんて記憶に無いのになぜ毎年やってるのだろう、と・・・


おわりに

他の発表者のみなさんもかぶりがないように幅広い内容で発表されていて、人数もちょうどよく和やかな雰囲気で質疑応答も盛り上がり、大変有意義な時間を過ごさせていただきました。主催のグノシーさん、発表者・参加者のみなさまどうもありがとうございました!


次回は「WWDC2015報告共有会@ネクスト」にて、Audio Unit Extensions について話させていただく予定です。


*1:参加者は Apple Developer アカウントを持っている人のみ

[iOS][音声処理][Swift]【iOS9】Audio Unit Extensions 〜オーディオエフェクトのアプリ間共有〜

$
0
0

昨日開催された「WWDC2015報告共有会@ネクスト」にて、iOS 9 で追加された新しい Extension Point のひとつ、「Audio Unit Extensions」についてLTをさせていただきました。



概要

Audio Unit とは?
  • Core Audio においてもっとも低レベル(ハードより)に位置するフレームワーク
  • 低遅延での音声処理が可能
  • ユニット同士を繋げて複雑なオーディオ処理を実現可能
  • iOS 8 より AVFoundation に AVAudioEngine が追加され、Audio Unit の利用ハードルがグッと下がった

Audio Unit Extensions とは?
  • iOS 9 で追加された Extension Point のひとつ
  • Audio Unit をアプリ間で共有できる

→ つまり自作エフェクトや他社製エフェクトを繋げてこういうこともできるようになる!


f:id:shu223:20150624072335j:image:w600

(※写真はイメージです)


  • Audio Unit にはエフェクト以外にもさまざまなタイプがある
    • GarageBand では Audio Unit Extensions の楽器を利用できるようになるらしい。
var kAudioUnitType_Output: UInt32 { get }
var kAudioUnitType_MusicDevice: UInt32 { get }
var kAudioUnitType_MusicEffect: UInt32 { get }
var kAudioUnitType_FormatConverter: UInt32 { get }
var kAudioUnitType_Effect: UInt32 { get }
var kAudioUnitType_Mixer: UInt32 { get }
var kAudioUnitType_Panner: UInt32 { get }
var kAudioUnitType_Generator: UInt32 { get }
var kAudioUnitType_OfflineEffect: UInt32 { get }
var kAudioUnitType_MIDIProcessor: UInt32 { get }

Audio Unit Extensions の利用方法

WWDC のサンプルコード "AudioUnitV3Example" に入っている "FilterDemo" というアプリには、Audio Unit Extensions が含まれています。まずはこのアプリをインストールして、「エクステンション利用側」を実装してみます。


利用可能なユニットのリストを取得する

iOS 9 で追加されたクラス AVAudioUnitComponentManager の `componentsMatchingDescription:` メソッドを呼ぶと、

var anyEffectDescription = AudioComponentDescription()
anyEffectDescription.componentType = kAudioUnitType_Effect
anyEffectDescription.componentSubType = 0
anyEffectDescription.componentManufacturer = 0
anyEffectDescription.componentFlags = 0
anyEffectDescription.componentFlagsMask = 0

availableEffects = AVAudioUnitComponentManager.sharedAudioUnitComponentManager()
    .componentsMatchingDescription(anyEffectDescription)

利用可能な Audio Unit の AVAudioUnitComponent (ユニットのタイプや制作者などの情報が入っているクラス)のリストが得られます。


この中に、インストールした"FilterDemo"サンプルが contain しているユニット "FilterDemo" も入ってきます。


ユニットを適用する

以下の手順で、所望の Audio Unit をサウンドに適用します。

  1. AVAudioUnitComponent の AudioComponentDescription (構造体)を取得する
  2. AVAudioUnit を `instantiateWithComponentDescription:options:` で生成する(このメソッドは iOS 9 で新規追加)
  3. AVAudioEngine に attachNodeする
  4. エフェクトノードと、プレイヤー・ミキサー等のノードを `connect:to:format:` で接続する

※AVAudioEngine を用いて Audio Unit のエフェクトをかける基本的なサンプルは、iOS8-Sampler にも入っているので、よろしければご参考に。


f:id:shu223:20150624072630j:image:w200

(iOS8-Sampler の "Audio Effects" サンプル)


Audio Unit Extensions の作成方法

なんと、Xcode 7 beta には Audio Unit Extensions を作成するためのテンプレートがまだありません(という話は WWDC セッション内でも話されています)。とりあえず現状では "FilterDemo" からコピペして使ってくれ、とのこと。


Audio Unit の関連記事


[雑記][iOS][海外就職]WWDC15のチケットは外れたけどサンフランシスコに行ってきたメモ

$
0
0

LTするまでがWWDC、ということで諸々の勉強会が終わってからブログ書こうと思っていたら7月に入ってしまい、完全にタイミングを逸してしまいました。。が、昨年のも書いといてよかったと思うので、やっぱり書いておこうと思います。


タイトルの通り、今年はWWDCのチケットは残念ながら外れてしまったのですが、初参加した昨年を振り返ってみると

  • セッションを(英語力と理解力と集中力の点で)リアルタイムで理解できない
  • → セッション中にドキュメント等で勉強しようとする
  • → セッション会場は(椅子間の距離が)狭いし、ネットも遅い
  • 1Fのネットコーナーに入り浸る

という、日本でもできる、というかむしろ日本での方が快適にできる過ごし方をしていたわけで、良かったのは何よりもその街に充満する「iOSエンジニアの祭典」的空気感なわけで、じゃあチケットなくてもサンフランシスコに行ってもいいんじゃ、という気がして航空券購入に踏み切りました。旅程は 6/6 〜 6/15 の 8泊10日。


やったこと

ヨセミテ旅行

WWDCが始まる前の土日を利用して、iOSエンジニア4人でヨセミテへ行ってきました。シリコンバレーには何度か来ているものの、なかなか観光をする機会がなかったので、今回が初。


空港で集合し、レンタカーでそのままヨセミテ方面へ。宿はヨセミテ内ではなく、そこから車で1時間ぐらいの、別荘地的なところ。


f:id:shu223:20150703034503j:image:w400


Airbnbで(Tonnyさんが)予約してくれたところなのですが、雰囲気があって超よかったです。



ヨセミテも想像以上に楽しかった。


f:id:shu223:20150607100935j:image:w400

(川遊び)


f:id:shu223:20150703042256j:image:w600

(某El Capitan)


f:id:shu223:20150703034635j:image:w400

(なぜか乗せてもらえた本場のハーレー!)


日々もくもく開発@Realm オフィス

サンフランシスコの Realm オフィスのモニタで、みんなで基調講演のストリーミングを見ました。


f:id:shu223:20150703034810j:image:w400

(広くて綺麗で快適でした)


Realmオフィスへは基調講演以降もほぼ毎日お邪魔し、もくもくドキュメント見ては手を動かして新機能を試してました。


f:id:shu223:20150703034918j:image:w400

(ランチ風景)


f:id:shu223:20150703035002j:image:w400

(念願のTシャツもいただきました。超愛用してます)


StackOverflow デビュー

StackOverflow でいくつかの質問に回答し、Reputation もいくらかつき、ついに自分も vote up やコメントができるようになりました。

今まで、「なんて素晴らしい回答だ!ありがとう!」と思っても vote up する権利すらない自分の無力さを何度歯痒く思ったことか。。


StackOverflow の Reputation は GitHub 同様に世界で通用する客観的&定量的評価だし、英語の練習になるし、人の役にも立つ、ということで自分の中でずっと重要TODOだったのですが、日々の仕事を言い訳にずっと後回しにしてきたことなので、非常に達成感があります。


HomeKit 対応デバイス購入

iOS 8 で発表された HomeKit、IoT の文脈から注目はされていたものの、対応製品がない、という状況がずっと続いていました。で、最近ぽつぽつと製品が出始めている、ということでサンフランシスコの Apple Store に行ってゲットしてきました。


f:id:shu223:20150703035336j:image:w400


Lutron社の「Caséta Wireless」という製品です。229ドルもしました。とりあえず HomeKit を体験したいだけなのでもっと安いのを・・・と店内の製品パッケージをくまなく探してみましたが、残念ながらこれしか置いてないようなので購入。


後日またブログかLT等で使用&実装報告したいと思います。


WHILL HQ 訪問、野球観戦など

昨年のTechHouse内オフィスから移転したWHILLの米国本社や、とある企業のオフィス等に遊びに行きました。


f:id:shu223:20150703035436j:image:w400



あと人生初の野球観戦。


f:id:shu223:20150703035511j:image:w400


ホットドッグ&ビール&野球という経験ができてよかったです。


Apple Watchハッカソン参戦

最終日、ちょうどSFでAppleWatchハッカソンが開催されてたので参戦。


なんと、賞をいただいてしまいました!!!!


f:id:shu223:20150614131616j:image:w400


つくったのは watchOS-2-Sampler。コミュニティへの貢献を評価していただけたようです。めっちゃ嬉しかったです。


ちなみに英語プレゼンはアドリブでは絶対にグダグダになる自信があったので、いったん話したいことを全部書き出して、流れだけ頭に入れつつ反復練習して、ハック中に話しかけてくれた優しそうな現地の人に聞いてもらったりもしました。


後日談として、こういうこともありました。


やっぱ海外でハッカソンに参加するのは楽しい。


費用

気になるお値段ですが、

  • 飛行機代:¥125,140
  • 宿代:¥44,860($45 × 8 × 124.6)

で、基本費用は17万円程度。40万オーバーだった昨年と比べて、大幅にコストダウンしました。WWDCチケット代がかからなかったのと、宿を全泊TechHouseにしたのが効いたな―と。(TechHouseについては昨年の記事をご参照ください)


あとは日々の食費、ヨセミテ旅行費(レンタカー+宿。手渡しだったのでいくらか忘れた。。)、野球観戦のチケット代(確か8000円ちょい)がかかってるので、総旅行費用としては20万円ちょいかと。


LT

せっかくSFの空気に触れて高まった意識も、帰国して日々の仕事の波にさらされるとあっという間に消えてなくなるので、「人前で発表しないといけないプレッシャー」ドリブンで勉強を進めるべく、WWDC関連の勉強会にはできるだけLT枠で申し込みをするようにしました。


6/16 「potatotips #18」


ブログ:watchOS 2 新機能の細かい話5つ #potatotips - Over&Out その後


6/17 「UI Crunch #5 スマートウォッチUIデザインの今」


ブログ:UI/UX に影響の大きい watchOS 2 の新機能 3つ #uicrunch - Over&Out その後


6/19 「WWDC Afterparty Roppongi」


ブログ:【iOS9】Core Image の新機能:文字認識/追加フィルタ47種 - Over&Out その後


6/24 「WWDC2015報告共有会@ネクスト」


ブログ:【iOS9】Audio Unit Extensions 〜オーディオエフェクトのアプリ間共有〜 - Over&Out その後


所感

チケットなしWWDC、ものすごく行ってよかったです。ドキュメント見るなら日本でもできる、とはいえ日本にいたら絶対仕事を入れてしまってそうなるとドキュメント見てる暇はないし、そうするとLTすることもないのでそれに向けた調べ物もしない、というループになったかと思います。


あと、WWDCのチケットがあると、高いチケット代払ってるので朝ちゃんと起きてフルに会場にいないといけないという気がどうしてもしてしまってなかなかしんどい(とくに後半)のですが、そういうプレッシャーもなく健やかな気持ちで日々を過ごせました。結局毎日ドキュメント見てコード書いてたわけですが、義務感があるのとないのとでは健やか度がだいぶ違うなと。


それと、Realmオフィス。Realm の中の人をはじめ、CocoaPods の中の人や RestKit の中の人など、真のスーパーハッカーな方々が集う場で、なんというかそこにいるだけで「もっと精進せねば・・・」と意識が高まりました。


そんなこんなで、来年もチケット外れてもまた行きたい所存です。現地でお世話になった皆様方、ありがとうございました!


[watchOS][WatchKit][iOS][Swift]watchOS 2 の Core Graphics は何ができて何ができないのか #potatotips

$
0
0

本日開催された、「【第19回】potatotips」にて、watchOS 2 における Core Graphics について発表をさせていただきました。



概要

watchOS 1 では Core Graphics が使えなかったので、次のような UI を実現するにあたって、


f:id:shu223:20150214162301p:image:w600


Appleのサンプル「Lister」では、360個の連番pngを使用するという相当な力技でやっていたことは記憶に新しいところです。


f:id:shu223:20150714210052p:image:w425


で、watchOS 2 でついに Core Graphics が使えるようになったわけですが、発表されて1ヶ月経過した今も、(自分の探し方が悪いのか、)10本ぐらいあるAppleのWWDC動画にもサンプルにも、GitHub や StackOverflow にも未だに具体的なコードは見当たりませんでした


単にまだ誰も書いてないというだけかもしれないし、何か重要なクラスが watchOS 上で使えないとかで結局使えないのかもしれない、実際に試してみないとわからない・・・ということで何ができて何ができないのか、検証コードを書いて確認してみました。


ポイント

詳細はスライドを見ていただくとして、ここではポイントだけ。

  • watchOS では `UIGraphicsGetCurrentContext()` でグラフィックス コンテキストを取得できない
  • ビットマップコンテキストを作成し、そこに描画 → ビットマップコンテキストから UIImage を生成して WKInterfaceImage や WKInterfaceGroup に表示すればOK
// Create a graphics context
let size = CGSizeMake(100, 100)
UIGraphicsBeginImageContext(size)
let context = createBitmapContext(size)

// Setup for the path appearance
CGContextSetStrokeColorWithColor(context, UIColor.whiteColor().CGColor)
CGContextSetLineWidth(context, 4.0)

// Draw lines
CGContextBeginPath (context);
CGContextMoveToPoint(context, 0, 0);
CGContextAddLineToPoint(context, 100, 100);
CGContextMoveToPoint(context, 0, 100);
CGContextAddLineToPoint(context, 100, 0);
CGContextStrokePath(context);

// Convert to UIImage
let cgimage = CGBitmapContextCreateImage(context);
let uiimage = UIImage(CGImage: cgimage!)

// End the graphics context
UIGraphicsEndImageContext()

image.setImage(uiimage)

うまくいったこと
  • CGPath を用いたパス描画
  • UIBezierPath を用いたパス描画
  • SVG ファイルからのパス描画
  • グラデーション描画

(描画の話なのに、NDAにつきまだスクショ貼れないのが残念。。)


watchOS の Core Graphics でできないこと

グラフィックスコンテキストの問題が解決できたことで、結構いろいろなことができることがわかったわけですが、基本的に CALayer や UIView の API を使う必要がある処理は実現できません。スライドでは例として下記3つを挙げています。

  • スクリーンキャプチャ
    • UIView または CALayer の内容をコンテキストに描画する必要がある
  • キーパスアニメーション
    • AddAnimation: しようにも CALayer オブジェクトにアクセスできない
  • 手書き
    • タッチイベントを取るAPIがまだ開放されていない

キーパスアニメーションができないということは、冒頭に挙げたような Circular Progress 的な UI の実現も結局難しい、ということになりますね。。(アニメーションの各フレームを UIImage として生成することもできますが、非現実的な方法かと)


おわりに

最初 UIGraphicsGetCurrentContext() でグラフィックスコンテキストが取得できず、ググってもあまり情報が見当たらなかったときは「もしかして何もできないのか・・・!?」と思いましたが、上述のようにそこは解決して、パス描画やグラデーション描画などなど、結構いろんなことができました。


ただ最後に書いたように、タッチイベントが取れない、キーパスアニメーションが使えない、という大きな制約もまだあるので、「Apple製アプリではできてサードパーティにはできない」ことというのは色々とありそうです。手書きの筆跡を共有する 「Digital Touch」的なこととか、Circular Progress的なUIを動的に描画するとか。



[watchOS][iOS][Swift]iOS 9、watchOS 2 技術記事のまとめ

$
0
0

今年はWWDC後の勉強会も多く開催され、正式リリースを前にして iOS 9、watchOS 2 の技術情報が既に多く出てきています。あとでキャッチアップしよう、と思ってたらいつの間にかかなりの記事がたまってきたので、ここらへんでいったん整理しておこうと思います。


iOS 9

f:id:shu223:20150804031922j:image:w600


Search API

Search API は既存アプリへのメリットもはっきりしているためか、注目度が高く、発表スライドや詳細な解説記事を多く見かける印象があります。




Universal Links

App Thinning
  • WWDC - App Thinning メモ - Qiita
    • 「App Thinning」「App Slicing」「On-Demand Resources」「Bitcode」など、どれが何だか区別がつかなくなる各用語について解説してくれています。

SFSafariViewController

UIWebView や WKWebView と同様に、アプリ内ブラウザとして使えるビューコントローラ。


コンテンツブロッカー(Content Blocker Extension)


UIStackView




LLDB


Core Location


MapKit
  • iOS 9で強化されたMapKit - Qiita
    • ピンアノテーションに任意の色をセットできるようになったりとか、コールアウトの詳細部分に任意のUIViewサブクラスをセットできるようになったりとか、見た目のカスタマイズ性が向上したようです。
    • Transit 機能の開放、Flyover のタイプ追加等、結構うれしい機能追加があります。

Audio Unit Extension


Metal
  • WWDC 2015 METAL
    • あまり見かけない、貴重な Metal 情報。WWDC 2015 の4つの Metal 関連セッションの内容をかいつまんで紹介してくれています。


Core Image


ReplayKit

ReplayKit は、画面を録画するフレームワーク。ゲーム実況にしろ、ユーザビリティテスト用途にしろ、それをメインプロダクトとしてがっつり取り組んでいる企業がいくつかある分野なので、あまり個人的には直接は利用しなそうと思っていたのですが、下記スライドに載っているサンプルを見ると、導入が簡単そうなので、ちょっと試してみようかと。




その他
  • iOS9でカスタムURLスキームの遷移に失敗するときの注意点 - Qiita
    • iOS 9 ではプライバシーへの配慮により、Info.plist に LSApplicationQueriesSchemes キーに対して追加してあるカスタムURLスキームについてのみ、 `canOpenURL:` で正しく結果を返してくれるようになった
    • (Info.plist に追加してないと、対象アプリがインストールされていて遷移可能でも、canOpenURL で `NO` が返ってくる)









watchOS 2

f:id:shu223:20150804031921j:image:w600


Watch Connectivity



Complication
  • 簡単!Complication!!
    • Complication の実装手順について、用語説明からダウンロードサンプルまであって、すごく丁寧に解説してくれているスライド



HealthKit
  • How to make workout app for watch os 2
    • HKWorkoutSession クラスを用いて、ワークアウト機能を利用したアプリを実装するための情報がひと通り書いてある大変ありがたいスライド


Core Graphics


その他

watchOS 1.0のアーキテクチャはいったい何のために用意されたのだろうか。ネイティブ化されるまでの半年間はいったい何のために必要だったのだろうか。Apple Watchに搭載されている一部のApple製のアプリは最初からネイティブで動作しているはずだが、はじめからその方法を解放できなかったのはなぜなのか。

    • という、開発者みんなが思った疑問についての、Bitcode やプロセッサを絡めての考察。





[フリーランス][watchOS][iOS]GitHub経由で海外から仕事が来た話

$
0
0

はじめて海外から(フリーランスとして)仕事をいただく、という貴重な経験ができたので、その経緯などを書いてみたいと思います。


きっかけ

7月末のある日、知らないメールアドレスから英語のメールが来ました。内容を一部だけ抜粋すると、

We are looking for someone to develop a very simple apple watch app and a companion apple phone app.


というわけで、Apple Watch アプリをつくって欲しい、とのこと。内容を読むと加速度センサとジャイロを使いたいそうで、必然的に watchOS 2 案件になりそうです。


メールには明記されてませんでしたが、GitHub で公開している watchOS-2-Sampler を見て連絡くれたのかなと。(※もちろん面識はなく、共通の知り合いもいないので、これ以外にわざわざ海外にいる僕に依頼してくる理由が考えづらい)


f:id:shu223:20150815125651j:image:w500



会社名も書かれていたのでググってみると、イスラエルの会社のようです。メンバーもLinkedInのプロフィールを見ると某世界的大企業の出身で、学位も持ってて発表した論文とかも載ってるので、怪しい感じではない、というかむしろすごいレベル高そう。


実はちょうど(フリーランスとしての今後を考えると)海外からの仕事もやりたいと思っていた & watchOS 2 を案件でやりたかったのもあって、前のめりに食いつきました。

I'm interesting for the work to build an watchOS 2 app.

(2015.8.16追記:はてブコメントで「そこは "I'm interested in" でしょ」ってツッコミを何件かいただきました。。恥ずかしいですがこんな英語でも全然やりとりできたという事実にも価値があると思うのでそのままにしておきます)


受注するまでのやりとり

工数や金額の提示

相手からきた最初のメールに(簡単に)こういうものをつくってほしい、ということが書いてあったので、僕も最初の返信から「こういう内容で、日単価○ドルで、○日でやります」と明記しました。

My freelance fee is $xxx / day. I can develop the watchOS 2 app with following features in 2 days:

  • ・・・・(機能1)
  • ・・・・(機能2)

単価は、普段日本でやってるのと同等の価格を提示しました。イスラエルの相場とかよくわからないので。それと安く済ませるためのオフショア、というよりは watchOS 2 実装がちゃんとできそうな人を海外からわざわざ探してきた、という位置付けなので、安売りする必要もないかなと。(とはいえシリコンバレー相場からすると安い)


また工数の見積もりは、だいたい1日もかからずできそうとは思ったのですが、watchOS 2 はまだベータで不安定なのでそのあたりで色々ハマるかも、というのと、

  • 加速度センサの値をインターバル x [ms] で取得し、それを iPhone 側に n 秒ごとに送る
  • データ(加速度センサ値)のロストは NG

という要件があり、取得インターバルがかなりギリギリ(短い)で、こういうのは実際にやってみるとウォッチ側の処理性能やBLEの通信速度的な部分やでどうのこうのありそうだなーというのも加味して2人日としました。


要件確認

あとちょっと要件的に気になる部分について確認しました。ジャイロの値は今のところフレームワーク的には用意されてるけど、実機でやってみると取れないですよ、とか、前述した加速度センサ値の取得インターバルや、Watch <-> iPhone 間の通信速度に関する懸念とか。

However we can't get gyroscope data from Apple Watch for now. You can try with my OSS (https://github.com/shu223/watchOS-2-Sampler). In addition, ・・・(他の要件についての懸念点)

このへんはいち開発者ではどうにもできない場合もあるので。。


以上が僕からの最初の返信メール。


条件の調整

で、それについての先方の返信があり、会社の詳細・ビジョン・プロダクトの説明とか、もっと具体的な要件とかが書いてありました。その具体的な要件が、最初のメールよりも増えてたので、「それなら4人日でやります」という旨の回答をしました。


そしたら、「なぜ2倍になったんだ?どの要件を減らせばどれぐらい減るの?」という質問が。


どれがどう、というよりベータ版での開発に携わる際の直観的なものしか根拠としてないので、その旨を正直に伝えました。

Both 2 days and 4 days, they depends on my rough feeling. It may takes more or less.

(2日にしろ4日にしろ、なんとなくの感覚なので、もっと伸びるかもしれないし、縮まるかもしれない。・・・と言ってるつもり)


さらに、次のように提案しました。

  • 3日で終わったら、3日分だけ請求します
  • 3日で終わらなかったら、その時点での状況と、あと何がどれぐらいかかりそうか、を報告するので、引き続き作業を進めるか、やめるかを選択してください
    • それ以上進めないことを選択した場合、その時点までの成果を受け取るかどうかを選択してください
      • 成果を受け取ることを選択した場合、その3日分を請求します
      • 成果を受け取らないことを選択した場合、一切請求しません

こちらとしては「やってみないとわからない」ことがほとんどなので、こういうやりとりで平行線をたどるよりは早く手を動かしたいし、最悪お金にならなくても watchOS 2 の知見は得られる(そしてブログとかに書ける)ので数日ぐらいならアリかなと。


だいぶ変則的ですが、先方の返答は、

We would like to hire you to develop these apps (iphone and apple watch).

The conditions you stated sound fair.

フェアだね、よし、任せよう


というわけで無事受注することができました(実際は技術面でもっと細かいやりとりがあったのですが、長くなるので省略)。


ここまでメールは5往復。


開発

その3日間について、いつといつといつでやります、ということは明示してましたが、当日に先方から「ちゃんとやってるか〜?」とか聞いてきたり、スカイプで何か話したりとかはなく、完全に任せてくれてました。ただ向こうも心配はあると思うので、「そろそろ作業始めます」とか、1日目の終わりにもちょっとメール送ったりとかはしました。


本当のところは、3日分の作業を1日で終えて、「もう終わったよ、請求は1日分でいいよ」「まじか!すげーな。次も頼むよ」みたいな展開を狙ってたのですが、watchOS 2 beta のあれやこれやにハマって(watchOS 2 の技術的なことはまた別記事で書こうと思います)、結局2日かかって完了。


作業報告

実装的には要件は満たしたのですが、どう考えても watchOS 2 beta 自体のバグとしか思えない挙動 *1 があり、それにより実機ではある要件を満たせないという部分がありました。そこをどう説明し、納得してもらうか、はけっこう悩みました。


前述したような条件なので、Xcodeプロジェクトをいきなり送ってしまうと相手がその成果に満足しない場合に何ももらえなくなってしまうので、まず、実装自体はできていることを証明するため、シミュレータではきちんと動いている動画を撮影し(シミュレータでは加速度センサ使えないので、そこはダミーで値を生成するモードを実装しておいた)、その上で、実機ではどういう問題が起きているのか、それについてどんな検証を行い、なぜAppleの問題(watchOS の問題)と考えるのか、どんな対策が考えられるかといったことを整理して書きました。


代替実装も行っておき、その動画も撮影して送りました。


プラス、僕としては初めてのお客さんなので、早く最初の入金までこぎつけたく、

  • まだ不足がある、追加要望がある等あれば、8月○日にもう1日作業できます
  • これで十分であれば、Xcodeプロジェクト一式を送り、2日分だけ請求します

と書きました。


そんなこんなで作業報告メールはレポートはこの記事よりも長くなり、2〜3時間はかかったわけですが、先方は「言ってることは筋が通ってる」と実機では要件を満たせていない件について理解してくれ、代替実装案についても同意してくれました。


で、まずこの2日分は入金した上で、プラス今後も作業をお願いしたい、と言ってくれました。


その後、あれこれ送金方法の調整があったのち、すみやかに送金していただけました。


所感

一連の流れから汲んでいただけると思いますが、すごく理解があり、信頼できる良い方たちでした。分野的にも watchOS 2 は興味のあるところなので、ぜひ今後も一緒にやっていきたいと思っています。


ただ、数人日の小さい案件なので、比較的ざっくりした感じで進められたものの、もっと大きい案件だとこうもいかないかもしれません。そういった場合に、

この記事に書いたみたいに、見積もり型ではなく、(ある程度の信頼関係を築いた上で)先方のオフィスまで行って、2週間とか一緒に作業して帰る、みたいなやり方ができたら理想的だなぁと思っているのですが、それも色々な面で難しいところはありそうです。


とにかく今後も海外からもお仕事をいただけるよう、GitHub活動やStackOverflow活動をがんばろう、と思った次第です。


制作実績を3ヶ月ごとにまとめています:[制作実績]記事一覧 - Over&Out その後



[海外][iOS]もくもく会@ベルリン

$
0
0

世界を旅するプログラマやデザイナーの集団『Hacker Paradise』というコミュニティに参加し、19日間の旅程でドイツはベルリンに滞在しております。今日で4日間が経過したところなので、ファーストインプレッション的なものを書いてみたいと思います。


"Hacker Paradise"とは?

もともと、主催の2人*1がノマド的に仕事をしながら旅をしていて、楽しいけど毎回場所を変わる度にコミュニティを離れるのが寂しいと感じて、それなら自分たちでコミュニティをつくろう、ってことで今の形になったそうです。



滞在する国は4週間ごとに移ります。今はドイツ、前回はバルセロナ、その前はエストニア、、、といった感じで。(僕はドイツの4週間のうち、後ろの2週間から参加したかたちになります)


参加は有料です*2。渡航費、宿代という普通に旅行に必要なものはここには含まれていなくて、彼らが提供するのは以下の4つ。


f:id:shu223:20150826092850j:image:w600


というわけでざっくりいうと、彼らが異国の地に作業場所を用意してくれて、そこに集う仲間に入れてもらえる、というわけです。


基本的には皆何かしら取り組むことを決めて、それぞれもくもくと作業します。それを週始めに宣言したり、週の終わりに発表したりといったしくみもあるので、僕的にはこれは「旅するもくもく会」みたいなものかなと思っています。


参加目的

理由は大きく2つありまして、ひとつめは、先日チケットもないのにWWDCの会期に合わせてサンフランシスコに行ったらすごく良かったので、定期的にこうして仕事とは関係なくリフレッシュして新しい技術の勉強とかをする機会を持ちたいなーと。


もうひとつは、先日書いた記事みたいに、海外からもフリーランスとして仕事をとれるようになりたくて、その足がかりを何かつかめないかなと。ベルリン滞在中に無料でちょっと手伝ったりして、その後もiOSの仕事があればお呼びがかかるようになるのが理想だけど、それが難しければ海外から仕事を受けるにあたって何が不足しているかの感覚だけでもつかめればと思っています。


スカイプ面接

参加にあたっては、20分〜30分ぐらいの簡単なスカイプ面接があります。次のようなことを聞かれました。

  • これまでどんなプロジェクトに取り組んできたのか
  • iOS を始めたきっかけは?
  • Hacker Paradise に参加したらどんなことをやる予定か
  • どの期間に参加する予定か(ベルリン4週間なのか、2週間なのかとか)

最初から僕が iOS エンジニアで、ということが前提で話が進んだので、こちらがフォームに入力したプロフィールとかはあらかじめ目を通してくれていたようでした。


準備

前述のような参加目的が既にあったので、ベルリン滞在中は日本でのお客さん仕事は一時停止させていただくべく、面接合格のメールが来てすぐに、お客さんへの連絡・調整を行いました。


あとは普通の渡航と同様、航空券購入とAirbnbでアパート借りたぐらい。


ドイツは行ったことがなく土地勘もないので、事前にHacker Paradiseの中の人にワークスペースの住所を聞いて、そこから近いところを借りました。


(築100年ぐらいの渋いアパート)


航空券はボーッと数週間放置してたら当初14万円ぐらいだったのが24万円ぐらいに値上ってました。ドイツの都市の中でも特にベルリンは経由便しかないのでこういうことになりやすいので要注意です。


ベルリンに来てからの4日間でやったこと

Hackday

ベルリンに着いてすぐ、Hacker Paradise がやっていた Hackday というイベントに参加しました。ハッカソン的な名前がついてますが、チーム分けとか賞とかはないので、これももくもく会的なものでした。


僕は Interface 4月号にあった「生体センシング入門」を読んで信号処理をまたちゃんとやりたいと思っていたので、



「Apple Watch(watchOS 2)の加速度センサデータをiPhone側で受け取ってリアルタイムにFFTかけてスペクトル表示する」というものをつくり、プレゼンしました。


(こういうプレゼン風景でした)


つくったもの自体は、英語力不足によりイマイチ伝わらなかった感があったのですが、

I came from Japan yesterday, so it's my first day for Hacker Paradise!

みたいなことを冒頭にいったらみんなワーッと拍手してくれて、何ていい人たちなんだ、と思いました。


Group Lunch

イベント名から察するにグループに分かれるのかなとか思いましたが、全員でのランチでした。


f:id:shu223:20150826153127j:image:w400


一応この日が正規の初日(Hackdayは休日開催)だったので、ここでも改めて自己紹介させてもらい、またワーッと拍手してくれました。みんな超親切。


で、ひとりひとり順番に「今週やること」を話していきます。僕は "I'll work on my open source project." といって、iOS-9-Sampler の説明をしました。誰も iOS エンジニアじゃないのであまり伝わってない気がするのですが、みんな「ワオ、クールだね!」と言ってくれます。


他の人達は、Python で機械学習に取り組む人、フロントエンドのフレームワークをつくってる人、Arduino で何かやってる人、普通にリモートワークしてる人など色々。


意外にも、リモートでどこかの誰かと一緒に仕事してる人は多いけど、わりとHacker Paradiseのメンバー同士でコラボしてるケースは少なそうな印象を受けました。


もくもく開発

iOS 9 の新機能について調べつつ、iOS-9-Sampler を絶賛開発・・・といいたいところですが、実際のところまだ半日弱ぐらいしか作業できてなくて、既存サンプルの修正(適当にやってるところをちゃんと公開できるようリファクタしたり)とREADMEとかの整備をして、Privateリポジトリにpushするところまで。


他に音声処理(DSP)や画像処理周りの勉強とかもやりたかったのですが、手がまわらず。


Coffee Chat

あまり普段話さない人と話すきっかけになるように、主催側が(以前とかぶらないように)ペアを決めて、コーヒー代あげるのでお茶してきてください、みたいな催しです。僕はアメリカからきたプログラマのLanaとペアになって、小一時間コーヒー飲みつつしゃべりました。


僕は大勢だと全然その会話スピードについていけなくなるので、1対1で話すお膳立てをしれくれるのはほんと助かります。しかもLanaは超ゆっくりしゃべってくれて、とても話しやすかったです。


ごはん

昼や夜にワークスペースにいると、だいたいごはんに誘ってくれます。


(主催の Alexey (左)と、コロンビアからきたデータサイエンティストの Juan (右))


ドイツに来たからにはビールだ!クラブだ!という感じではなく、仕事がまだ残ってれば水、ビール飲むとしても1杯、とか結構みんなそんな感じです。(クラブに行ったりがっつり飲んだりしてる人たちもいるかもしれません)


Goodpatch ベルリンオフィス訪問

現地の会社のオフィスを見学させていただきつつ、あわよくばフリーとしての仕事のチャンスを探るべく、ツテを辿ってGoodpatchさんのベルリンオフィスを訪問させていただきました。


f:id:shu223:20150827194328j:image:w400


とある案件の見積もりについて話し合いました。まだどうなるかわかりませんが、もしかすると仕事につながるかもしれません。とにかく最高に親切な方々でした。いつでもオフィスに来て作業していいよ、といっていただけたのでまたお邪魔しようと思います。


湖へドライブ

色々縁あって、ドイツに住んで働いている日本人の方々とつながり、ベルリンの北の方にある湖へ連れて行っていただきました。



すごい水がきれいでみんな泳いでてうらやましかったので、僕も(パンツで)ひと泳ぎしました。そのあとはドイツ肉をかこみつつビール。みなさん魅力的な方々ばかりでとても楽しかったです。


おわりに

Hacker Paradise が契約してくれたコワーキングスペース「Betahaus」のこととか、費用的なこととか、他にも書こうと思っていたことがたくさんあるのですが、長くなりすぎたのでまたの機会に。引き続き有意義な滞在になるよう楽しみつつ精進します!


*1:今はOrganizerは4人

*2:サイトでは公開してないようなので控えておきます

[海外][iBeacon][iOS]海外のiBeaconハッカソンで1位になりました!

$
0
0

一昨日・昨日と2日間に渡ってドイツはベルリンにて開催されたハッカソン『Travel Hackathon with Beacons by Deutsche Telekom & Lufthansa』にて、オーディエンス投票で1位をいただきました!


(一緒に写ってる方はスポンサーの会社の偉い人)


賞品としてドローンを3つ(!)いただきました。


つくったもの

ハッカソンのタイトルの通り、iBeacon を使い、空港における何らかの問題を解決する(※スポンサーがルフトハンザ航空)、あるいは新しい体験を提供する、というのがお題でした。


で、僕は

  • 出発までの待ち時間が長い
  • → インターネットがありさえすればOK(仕事もできるしだらだらSNS見たりもできる)
  • → 空港にはだいたい無料Wi-Fiがある
  • → しかし、著しく通信が遅いときがある

と考え、「空港内の地図上に、各iBeacon設置ポイントにおけるWi-Fi通信速度をマッピングして表示してくれる」というアプリをつくりました。


現在値もiBeaconにより特定して表示するので、このWi-Fi通信速度マップを見れば、より通信品質の高い場所に移動して快適にネットできる、というものです。


Wi-Fi通信速度マップをつくるしくみ

ここが作品のキモなのですが、Wi-Fi通信品質は、次のようなしくみで行っています。

  • 本アプリのユーザーがビーコンの近くを通ると、アプリはバックグラウンドでそのビーコンを検知する
  • ビーコン検知をトリガとして、バックグラウンドで通信速度計測を行う
    • 所定のURLにある画像をダウンロードするHTTPリクエスト *1 を発行
    • 画像のデータサイズ・レスポンスを受け取るまでの所要時間から通信速度を計算
  • 計算した通信速度を、検知したビーコンの major, minor とセットにしてクラウドサーバーに送信する

これらの処理はすべてバックグラウンドで行われるので、ユーザーは通信速度計測に関しては意識する必要がない、というのがポイントです。


で、クラウド上に集められた通信速度データを対応する各ビーコンごとに(つまり場所ごとに)平均化したものを、空港内地図にマッピングして表示する、というしくみになっています。


チームを抜けて独りになった経緯

他のチームはだいたい4人か5人(MAX5人というルールがあった)だったのですが、僕は開発もプレゼンも1人で行いました。




ただ、もともとは違うチームに入っていました。インターナショナルなチームで、英語でコミュニケーションしながら開発を行う経験をする、というのもハッカソン参加の目的の一つだったので。


チームに入った経緯はFacebookの投稿に書いたのでそこから引用します。

  • 行く途中のバスの中でけっこういいアイデア思いついて、しかもひとりでつくれそうなのでいつものように一人でもくもくやろうと考える
  • 会場でなんとなく話してるうちにいつの間にかチームに入ってることになってしまった
  • いろんな国から来た人たちと英語でものづくりをする、というのも参加目的のひとつではあったので、それなら、ということで自分のアイデアは捨てて、そのチームのアイデアに乗っかろうと考えを改める
  • 自分のアイデアをシェアしたら、それもいいね、って話になる
  • 議論が聞き取れてなかったのだけど、気づけば3つのアイデアを同梱するアプリをつくる話になっていた!ひとつひとつが勝てるアイデアだったとしても、3つ合わさると勝てない。何の問題を解決するかがぼやける。メンバーはハッカソン未経験者が多く、このへんの塩梅がわかってないのかと
  • ただ僕としては議論をいつまでも続けるよりは、早くつくりたい。僕の想像もつかなかったような統一感の見せ方がBizDevの人たちには見えてるのかもしれない。そのへんはプレゼンを見るまでわからない。ということでまずは作り始める
  • という感じで、いつものハッカソンとは違う展開。これはこれでおもしろい。

で、雲行きが怪しくなってきたのが翌朝。


プレゼンのストーリーのドラフト見たけど、やっぱり全然融合してなくて、僕がやってる部分は切り出した方がいいような。。今からチーム抜けて1人でやるか。。?


ビジネスデベロップメント(日本で言う「ディレクター」)が5人のうち2人いて、その1人が書いてきた「プレゼンのストーリー」の中に、WiFiの話がまったく入ってない。いい感じに融合させるアイデアどころか、あんまりその辺考えてないんじゃ。。と不安に思ってきて、しばらく迷った結果、チームに提案してみました。


I read the story. How will the wifi quality map be referred in this story? I think if we refer also to the wifi quality, it may be too much for hackathon presentation.

If everyone thinks so, how do you think that I become independent as another team?


(Wi-Fi品質マップの話はどんな風に出てくるんでしょう?Wi-Fi品質に言及したとしても、ハッカソンのプレゼンにしては盛り込み過ぎだと思います。みんなもそう思うなら、別チームとして独立しようと思うけどどう思いますか?・・・と書いたつもり)


そしたら、その場にいた2人から、「そんなことない、大丈夫だ、プレゼンの内容はもっと揉むから」と説得されたので、チームのみんなが納得してるならそれでいいかと思い、いったんはとどまることにしました。


が、そのあと修正されてきたプレゼンのストーリーを見ても、結局「空港のいろんな場面で便利なアプリ」「あれもできるし、これもできます」にしかなってないじゃん、と思い、やっぱり独立します、と伝えました。

Thanks, I read. This story makes sense, however I feel there are some features which solve different problems even now.

So I decided to be independent. Thanks so much.


そんなこんなで、ぼっちに。すでに残り2時間、みたいなタイミングだったので急いでプレゼン資料づくりを開始しました(実装はだいたいできてた)。


せっかく入れてもらえたチームを離れるのは寂しかったですが、インターナショナルなチームで開発する、という経験もできたし、自分がやりたいアイデアにフォーカスして発表できたし、そのチームも審査員賞を受賞していたしで、結果的には諸々よかったです。


おわりに

今回の約2週間のベルリン滞在のほぼ最後のイベントにあたるわけですが、インターナショナルなチームで開発する、という良い経験もできたし、賞もいただけたし、というわけで本当に出て良かったです。プレゼンが終わったあといろんな人が話しかけてきてくれて、まだまだ英語に苦手意識がある僕としてはやはりミートアップとかよりハッカソンは合ってるなと。


Q&Aで僕が審査員からの質問が聞き取れないのでヘルプに入ってもらったヒロさん、チームを抜けて弱冠へこんでいた僕の話し相手になってくれたハジメさん、(日本語なので読んでないと思いますが)他の参加者のみなさま、主催者のみなさま、どうもありがとうございました!


*1:バックグラウンドなので NSURLSession を利用

[海外][フリーランス]フリーランスiOSエンジニアの海外武者修行その1:ドイツ編

$
0
0

フリーの仕事をいったんお休みして、ドイツはベルリンに17泊19日の旅程で行ってきました。いよいよ今日が最終日なので、諸々ふりかえりたいと思います。


そもそもの目的

ドイツ行きを決めた動機は、「Hacker Paradise への参加」です。Hacker Paradise というのは「旅をしながら働く」人たちのコミュニティで、世界の国々を4週間ごとに移りながら活動しています。


で、そもそもの Hacker Paradise 参加の目的は、以下のようなものでした。


1. 仕事ではできない技術的な勉強をする時間を確保する


2. 英語で話さざるを得ない環境に身をおく

  • シリコンバレーから出戻って以降のここ2年ぐらい、まずは技術力と実績を積み上げようということで、(重要だと思いつつも)「英語苦手問題」は無視してきた
  • IoT/BLE周りで、いろんな実績もつくれたし、書籍も出せたことだし、そろそろまた英語/海外に向き合うべきときが来つつある、と思うようになってきた

そして Hacker Paradise の次の行き先がちょうど行ってみたかったドイツだった、というのがベルリンに行くことになった理由です。


後付けの目的

そんなわけで「ベルリン/ドイツ」という選択自体には何か強い理由があったわけではなかったのですが、行くことを決めてから、そういえば何人かの知り合いから「ベルリンのスタートアップ界隈が熱いらしい」という話を聞いたことを思い出し、


それに加え、ドイツ行き直前ぐらいにフリーランスとして初の海外案件をいただく、ということがあり、


そんなこんなで完全に後付けですが、


3. フリーランスとしてベルリンで案件を獲得する(もしくはその可能性の模索)


という目的が加わったのでした。



これらの目的に対してどうだったのか、それぞれ振り返ってみます。


1. 技術の勉強:達成度10%

iOS 9 の勉強(と実装)をやれたのはたぶん2日ぐらい。音声処理の勉強も2日ぐらい。


目的が複数あり、これが一番優先度的に低く考えていたので、達成度が低いのもある程度想定内。(正月休みにアレコレやろうと思っても大抵10%ぐらいしかできないのと同じパターン)


2. 英語コミュニケーション:達成度100%

この「100%」は「英語コミュニケーションが完璧にできた!」という意味ではなくて、ビビリつつもちゃんと外国人に囲まれる環境に身を投じることができたという意味です。


  • 初日から英語でピッチ
  • 全然伝わらなくて、たまたま近くにいた初対面の日本人の方に泣きついて通訳していただいた

f:id:shu223:20150826153127j:image:w400

  • 2日目のランチ
  • この写真では笑顔だけど、この後みんなが英語で会話し始めると、まったく聞き取れないのでひたすらもくもくとカレーを食べ続ける

  • 遅くまでコワーキングスペースに残っていると晩ごはんに誘ってくれた
  • 自分以外に2人までなら相手も会話に入れてくれるし何とかやっていける、という成功体験ができた

f:id:shu223:20150827194328j:image:w400

  • Goodpatchベルリンオフィスにて今後の仕事の可能性についてミーティング
  • これも自分以外に2人だけなのと、あと会話に目的(ゴール)があると話せる、という成功体験ができた

  • 食材持ち寄りパーティ *2 にて
  • みんなの談笑にまったくついていけず、料理もできないので、ただただ相槌だけを打っていた
  • ちなみにこのヘルメットは「全天球から音が聴こえるようになる」というアタッチメントをつけたもの。みんなが順番に試していて、順番なのでかぶらせてもらえた

  • 帰り道、たまたまハッカーパラダイスの2人に会ってそのまま深夜まで橋の上で飲んだ
  • 相手が自分以外に2人まではOK、の法則発動、しかもこの2人はすごくゆっくり、わかりやすく話してくれるので楽しかった!

その他にも、

  • Hacker Paradise のメンバーで、iOS を勉強中の人に色々と質問されるようになり、iOS SDK や Swift、Xcode について教える
  • ハッカソンでチームに入って共同制作
  • 朝食・昼食に一緒に行く

等々、(もくもくプログラムを書くことに逃げがちな)自分にしてはよくやったなぁと思っています。


まあ、上のコメントにちょくちょく書いてある通り、みんなが英語で普通に(自分向けにゆっくり話してくれるモードじゃなく)話し始めると、まったくついていけない。文脈自体を見失って、情報を95%ぐらいロストするので、とてもじゃないけどチームで仕事できる気がしない(大事なミーティングで何も理解してない、ってことになる)。要努力。


3. 現地フリーランス案件獲得:達成度30%

上の写真の通り、Goodpatchさんのベルリンオフィスでは今後の仕事の可能性についてミーティングさせてもらえたのですが、ほんのすこしでもベルリン案件の可能性の種ができたとすればこれぐらい。


ただ達成度30%としたのは、多少行動だけはしたかなと。


こっちで教えてもらった BERLIN Startup Jobs っていうサイトに載ってるiOSエンジニアの募集10件ぐらいに対して応募メールを送ってみたのですが、「欲しいのはフルタイムで、フリーランスは募集してないんだよね」「このポジションはもう決まっちゃったよ」という返事だけが返ってきました。


ちなみに送ったのはこういうメール。(募集内容に応じて多少変えたりはしました)

My name is Shuichi, I'm an iOS developer working in Japan as a freelancer.

I'm interested in the position: http://berlinstartupjobs.com/xxxx


My GitHub accoun has almost 10,000 stars and it's ranked #14 all over the world in ObjC category:

Of course I can write with Swift.


Please check my achievements in my LinkedIn account:

I have many experiences in IoT field using Bluetooth Low Energy (BTLE/BLE). In addition, I'm the author of 2 books and both of them won #1 sales in Programming category of Amazon.


I'm visiting Berlin and stay until Sep 6th. I'm happy to meet you here.


せめて会うところまででも漕ぎ着けられたら、少なくとも英語ミーティングの経験は増えるし、ダメならダメで何が足りないのかの感覚がつかめるだけでもいいかと思ってたのですが、それすらも成らず。。*3


他にもミートアップ的なイベントにも参加したりもしました。が、「英語が下手でフルタイムで入れない外国人フリーランスでもいいからiOSエンジニアが欲しい」という人にそんなにうまいこと会えるわけでもなく。。


ただ、ハッカソンに出てオーディエンス投票1位 を獲ったあとは、結構いろんな人が声をかけてきてくれました。仕事を頼みたいっぽい人もいました。



やっぱり、ミートアップより「つくって力を示せる」ハッカソンの方が営業の場としては自分に向いてるなと。


住環境

ベルリンに来るは初めてで、土地勘もないので、2箇所ぐらいは住んでみよう、ということで、前半の1週間はここに、



後半の約10日間はここに住みました。



結局どっちも「クロイツベルク」というエリアなのですが、前半の方はクラブストリートど真ん中にあって、週末は四つ打ちが一晩中ドンドンと鳴り響く場所にある築100年ぐらいの古いアパート(中は綺麗)で、


f:id:shu223:20150907154817j:image:w400


後半は川沿いにある、いかにもアーティストの家、って感じのアパートでした。


f:id:shu223:20150907154844j:image:w400



ちなみに前半はホストファミリーが住んでるところの一部屋、後半はアパートまるっと貸しでHacker Paradiseメンバーとの同居(寝室は別々にある)でした。


コワーキングスペース

「betahaus」(ベータハウス)という、5階建てのコワーキングスペースのフルタイムメンバーになって、滞在中はほぼ毎日通いました。


(このカフェスペースの奥にも部屋があり、上にさらにフロアがある)


ベルリンで知り合った方のつてで Factory Berlin というところでも1日だけ作業させてもらいましたが、betahaus はちょっと安くてアーリーステージ(という用語が適切かは不明)のスタートアップとか個人が多く、Factory の方がもうちょっと会社として回り始めたスタートアップが多い、という印象でした。


食事

ビールは安いし、料理は肉がドーンという感じでどれも美味しかったです。


f:id:shu223:20150907155556j:image:w400


たとえば右奥の「シュニッツェル」はでかいトンカツに大量のポテトサラダがついてくるみたいな料理で、とても美味しいのですがこれでさらにビールも飲むと確実に太りそうです。


費用

  • 航空券15万円弱
  • 宿代10万円強
  • ハッカーパラダイス参加費(非公開)
  • モバイルWi-Fiルータレンタル1.5万円ぐらい
  • 食費・交通費など滞在中の費用

超ざっくりですが35万円〜40万円ぐらいかなと。この間は仕事してないので(※1日だけやった)、その機会損失分を考慮するとなかなかの投資。でも言わずもがなでこういう経験はプライスレスだと思っているので、今後もどんどんやっていきたい所存。


おわりに

達成度とか書いてしまうとあまり満足してなかったように読めてしまうかもしれませんが、自分としては100%来てよかったなと感じています。あまり時間がとれなかったとはいえ「仕事ではできない(技術の)仕込み」も多少はできたし、英語コミュニケーションの経験もいくらかできたし、ベルリン案件獲得についてもいろいろと学びがありました。あと現地で働いている日本人の方々にもたくさんお会いして、飲みに行ったり観光に連れて行っていただいたりもしました。日本にいて仕事に没頭していると「いつの間にか」過ぎてしまっている2週間が、こうも濃密なものに変わり得るのか、という感じです。


頻繁にできることではないけど、こういうチャレンジは続けていきたいと思っています(という意味を込めてのタイトル)。ベルリンでお世話になったみなさま、ありがとうございました!



*1:近日公開予定

*2:potluck と呼ぶらしい

*3:Goodpatchは日本本社の方につないでいただきました

[Swift][iOS][Xcode]iOS 9 の新機能のサンプルコード集『iOS-9-Sampler』を公開しました

$
0
0

iOS 9 でも大量の新機能が追加されましたが、新しいAPIはどう使うのか、実際に何がどこまでできるのか、といった具体的なところが、英語のドキュメントや動画をながめているだけだと正直あまりピンときません。やはり実際にコードを書いて動かしてみるのが一番わかりやすい・・・ということで今年もつくりました!


iOS-9-Sampler


f:id:shu223:20150917065537j:image:w600


恒例の iOS 9 新機能のサンプルコード寄せ集めアプリです。ソースコードは GitHub に置いてあるので、ご自由にご活用いただけると幸いです。


使い方は Xcode 7 でビルドするだけ なので、デザイナーさんやディレクターさんもぜひお近くのエンジニアにビルドしてもらってください。


f:id:shu223:20150917073141j:image:w240


Swift のバージョン 2.0 やオープンソース化、ついにネイティブ対応する watchOS 2、といったわかりやすい進化に対し、かなり成熟してしまったからかあまり期待の声を聞かない(気がする) iOS 9 ですが、こうやってサンプルをつくりはじめてみると、「Search APIs」や「Content Blocker」、「3D Touch」といった話題に登りやすい機能以外にも、意外とおもしろい新機能がたくさんあると思うようになりました。


今のところ15個のサンプルが入っています。随時追加していきます。機能追加プルリク大歓迎です!


以下各サンプルの紹介です。


Map Customizations

マップタイプで新たに指定できるようになった Flyover や、交通渋滞情報を表示する `showsTraffic` 等のサンプルです。



特に Flyover は、Appleの「Maps」アプリでは以前から登場していたものの、サードパーティ製アプリで使えるようになったのは今回からで、下記のようにプロパティをセットするだけで、

mapView.mapType = MKMapType.SatelliteFlyover
mapView.mapType = MKMapType.HybridFlyover

こんな感じのインパクトのある3Dマップ表示が可能となります。


Flyover Map for iOS 9.


Text Detector

CIDetector で新たに指定できるようになった `CIDetectorTypeText` を用いた文字認識のサンプル。



緑色の枠が検出した文字領域を示していて、(見づらいですが)黄色の枠は個々の文字領域を示しています。


2種類の画像への処理結果を比較してみると、同じステッカーを認識させるにしても、下の画像のようにパースがかかって陰影もある場合はグッと精度が落ちる(台形補正や二値化等の前処理をする必要がある)ことがわかります。


本機能については下記記事でも解説しています。


New Image Filters

CIFilter に追加された新しいフィルタのうち、静止画に対してかけられるものを試せるサンプル。



※今のところ29のフィルタを試せますが、`CIEdgeWork` と `CISpotLight` については正常に機能していません。filterAttributes 等も確認したのですが改善方法は不明。プルリクお待ちしております。


先日のスライド にも書いたとおり、今回のアップデートは Core Image (CIFilter)にとっては久々の大幅アップデートなので、このサンプルも見応えがあります。


Audio Unit Component Manager

新クラス AudioUnitComponentManager を使用して利用可能な Audio Unit Extension Component のリストを抽出し、サウンドに動的に適用するサンプル。



iOSデバイスに Audio Unit Extensions が入っていれば、それらも抽出されます。


右上の「ShowAUVC」ボタンは、選択中の Audio Unit Extension Component が画面(`AUViewController` を継承したクラス)を持っている場合に有効になります。


Speech Voices

「私は寿司が好きです」という文を世界各国の言語の合成音声でしゃべらせるサンプル。



CASpringAnimation

CASpringAnimation を用いたばね風アニメーションのサンプル。



下記記事を参考にさせていただきました。


UIStackView

UIStackView によりビューを追加しても自動的にレイアウト調整される、というサンプル。



Selfies & Screenshots

Photos フレームワークに追加されたサブタイプ、`PHAssetCollectionSubtype.SmartAlbumSelfPortraits` と`PHAssetMediaSubtype.PhotoScreenshot` を利用し、フォトライブラリからセルフィー(自撮り)画像とスクリーンショットを抽出するサンプル。



下記記事を参考にさせていただきました。


String Transform

地味だけどちょっとおもしろい新機能 "String transliteration"(字訳、音訳という意味らしい)のサンプル。



上はひらがなをカタカナに、また全角を半角に変換するサンプルで、下は "Hello, world!" をハングルやアラビア文字等の各種言語の文字に変換するサンプルです。


後者の言語(文字)の置き換えですが、翻訳ではなくあくまで字訳なので、たとえば `NSStringTransformLatinToHiragana` を指定した場合、"Hello, world!" の変換結果は 「へっろ、をるるで!」になります。実用的なのか何なのかよくわからない機能ですが、海外に行くと意味はわからずともとにかく読みたい、という場面もある(メニュー名とか)ので、需要はあるのかもしれません。


下記記事を参考にさせていただきました。


Search APIs

Search APIs のサンプル。NSUserActivity を使うものと、Core Spotlight を使うものの2種類が実装してあります。



Content Blockers

Content Blocker Extensions のサンプル。



下記記事を参考にさせていただきました。


SFSafariViewController

SFSafariViewController で WEB ページを開くシンプルなサンプル。



Attributes of New Filters

iOS 9 から、CIFilter の filterAttributes で取得できる属性情報のキーに `kCIAttributeFilterAvailable_iOS` というものが加わりました。このフィルタは iOS 8 から使えるようになったよ、とかそういう情報を示す属性です。本サンプルではこの属性を用いて iOS 9 で新規追加されたフィルタ 41 種類 を抽出し、その属性情報を表示しています。



CIFilter の新しいフィルタはすぐにはドキュメントに反映されなかったりするので、そういう場合にこの一覧が役立ちます。


関連記事:


Low Power Mode

iOS 9 から加わった「Low Power Mode」設定。これをアプリからon/offすることはできないのですが、設定変更を検知することはできます。そのサンプル(地味なのでスクショはなし)


New Fonts

iOS 9 で新規追加されたフォント 31 種類のサンプル。



iOS 8 のときは新規追加フォントはたったの7種類だったので、ひさびさの大幅追加ではないでしょうか。


おわりに

watchOS 2 のサンプル集はこちら!正式リリースされたらスクリーンショットを貼っていきます。



[watchOS][iOS][WatchKit][Swift]【正式リリース】watchOS 2 の新機能のサンプルコード集『watchOS-2-Sampler』を公開しました

$
0
0

昨日、ついに watchOS 2 が正式リリースされました。開発者待望のネイティブ動作するウォッチアプリ対応バージョンです。このアップデートに伴い、WatchKit には多くの機能が追加され、ClockKit や WatchConnectivity 等の新しいフレームワークも登場しました。さらに、iOSでは従来から使えたフレームワーク群(記事末尾にまとめました)もウォッチ側で使えるようになったことから、これらもwatchOSにとっては新APIであるといえます。


新しいAPIはどう実装するのか、実際に何がどこまでできるのか、快適に動作するのか、といった具体的なところを実際にコードを書いて動かして理解すべく、watchOS 2 の新機能のサンプルコード寄せ集めアプリ


watchOS-2-Sampler


f:id:shu223:20150614131615j:image:w242


をつくりました。ソースコードは GitHub に置いてあります。


実はこのアプリは WWDC15 での発表があったその週につくって公開したのですが、NDA中のためスクショは貼れないし、悪名高い watchOS 2 ベータ版を入れてウォッチが文鎮化するのを恐れ、アップデートを避ける賢明な開発者の方々も多かった(ので本サンプルを試してくれる人も少なかった)ようなので、今回READMEを整備したり未完成のサンプルを除外する等の整理を行い、改めて正式リリースとした次第です。


Contents

今のところ17個のサンプルが入っています。随時追加していきます。機能追加のプルリク大歓迎です!


Animated Properties

`animateWithDuration:animations:` メソッドを利用して、アニメーションで拡大縮小やフェードイン/アウト、移動(実際には Alignment)を行うサンプル。



Table Animations

WKInterfaceTable のセル(row)をアニメーション付きで insert / remove するサンプル。`insertRowsAtIndexes:withRowType:`, `removeRowsAtIndexes:` メソッドを利用。



Picker Styles

WKInterfacePicker の全スタイル(List, Sequence, Stack)を試せるサンプル。



Taptic Engine

WKInterfaceDevice の `playHaptic` メソッドに指定できる全タイプ(WKHapticType)を試せるサンプル。



ちなみに WKHapticType は次のように定義されています。


enum WKHapticType : Int {
    
    case Notification
    case DirectionUp
    case DirectionDown
    case Success
    case Failure
    case Retry
    case Start
    case Stop
    case Click
}

Audio Rec & Play

`presentAudioRecordingControllerWithOutputURL:preset:maximumDuration:actionTitle:completion:` メソッドを利用して音声を録音する UI を表示し、録音したファイルを `presentMediaPlayerControllerWithURL:options:completion:` メソッドを利用して再生するサンプル。



参考記事:watchOS 2 のオーディオ録音、再生機能を利用する - Qiita


Animation with Digital Crown

デジタルクラウンの回転に合わせ、WKInterfacePicker にセットしたアニメーションを表示するサンプル。`setCoordinatedAnimations` メソッドを利用。



Draw Paths

Core Graphics を用いてパスを描画するサンプル。



Gradations

Core Graphics を用いてグラデーションを描画するサンプル。



※Core Graphics を用いたパス描画やグラデーション描画についての詳細は、こちらのスライド・記事もご参照ください。



Accelerometer

Core Motion を用いて加速度センサの値を取得するサンプル。



Gyroscope

Core Motion を用いてジャイロスコープの値を取得するサンプル。



※現行 Apple Watch では CMMotionManager の `gyroAvailable` が必ず `false` を返してくるので、今のところ実質的に利用できません。(API的には利用可能であるためサンプルに残してあります)


Device Motion

Core Motion を用いてデバイスのモーションデータを取得するサンプル。



※現行 Apple Watch では CMMotionManager の `deviceMotionAvailable` が必ず `false` を返してくるので、今のところ実質的に利用できません。(API的には利用可能であるためサンプルに残してあります)


Pedometer

CMPedometer を用いて歩数や距離、階の昇り降り情報を取得するサンプル。



Alert

`presentAlertControllerWithTitle:message:preferredStyle:actions:` メソッドで指定できる全アラートスタイル(WKAlertControllerStyle)を試せるサンプル。



ちなみに WKAlertControllerStyle は次のように定義されています。


enum WKAlertControllerStyle : Int {
    
    case Alert
    case SideBySideButtonsAlert
    case ActionSheet
}

Interactive Messaging

iPhone と Apple Watch で相互にメッセージをやりとりするサンプル。Watch Connectivity フレームワークを利用。



Audio File Player

WKAudioFilePlayer を利用してオーディオファイルを再生するサンプル。



※要Bluetoothヘッドセット


Open System URL

WKExtension の `openSystemURL:` メソッドを利用して、電話およびSMSアプリを開くサンプル。



Network Access

NSURLSession を用いてネットワーク経由で画像データを取得するサンプル。KAMEDAkyosuke さんより pull request いただきました。



おわりに

iOS 9 のサンプル集はこちら!





おまけ:watchOS 2 で使えるようになった従来フレームワークのリスト

  • MapKit
  • UIKit
  • WatchKit
  • CoreGraphics
  • ImageIO
  • CFNetwork
  • Contacts
  • CoreData
  • CoreFoundation
  • CoreLocation
  • CoreMotion
  • EventKit
  • Foundation
  • HealthKit
  • HomeKit
  • MobileCoreServices
  • PassKit
  • SystemConfiguration
  • Core OS Layer
  • Security

※このへんからウォッチでの挙動が気になるものを随時追加していく予定


[画像処理][iOS][Swift][Objective-C]【iOS9】Core Imageを用いたリッチな画面遷移アニメーション #cm_ios9

$
0
0

昨日、クラスメソッドさん主催の勉強会『iOS 9 週連続 Bootcamp!2週目』にて登壇させていただきました。



タイトルにある "Tips&Tricks" はWWDCのセッション名でもたびたび使われている用語で、Tricksというのは「コツ」とか「うまいやり方」みたいな意味らしいです。


こういうタイトルにしたのは、(資料冒頭にもある通り、)新しく追加されたフィルタを紹介したところで *1カメラアプリとかつくってる人でもないとあまり使う機会がないわけで、定員150名という大きい勉強会で話す内容じゃないよなぁ。。と悩み、なるべく多くのアプリ開発において普遍的に役立つような内容を、とアレコレ考えた結果、次のような切り口で話すことにした次第です。

  • Apple も UI で多用する「ブラー」の話
  • Core Image を画面遷移のカスタムアニメーションに使う話
  • ヌルッとしたアニメーションを実現するための Metal の話

今回は画像処理を使ったアニメーションの話が中心なので、スライド内ではふんだんに動画を使ったのですが、残念ながらslideshareでは動画が再生できません。デモのアニメーションGIFだけこちらに載せておきます。


Box Blur

Core Image の CIBoxBlur (iOS 9 で追加)を用いたカスタム画面遷移アニメーション


f:id:shu223:20151008085228g:image


だんだんボケていきつつ次のシーンに遷移する、というアニメーションで、Apple も 3D Touch を用いた Quick Actions のショートカットメニュー表示時や、Live Photo の再生時にこういった表現を使用しています。


Motion Blur

Core Image の CIMotionBlur (iOS 8.3 で追加)を用いたカスタム画面遷移アニメーション


f:id:shu223:20151008085257g:image


遷移方向に、遷移スピードに応じてボケさせることで、少しだけスピード感を演出する、という使い方です。

(※モーションブラーは、WWDC2014 で CIKernel を用いた実装が紹介され、OSSも出ましたが、その後ビルトインフィルタとして利用できるようになりました)


CIFilter Transitions

Core Image の CITransitionCategory にあるトランジションエフェクトを用いたカスタム画面遷移アニメーション


f:id:shu223:20151008081300g:image



サンプルはすべてGitHubにアップしてあるので、URL等スライドにてご確認ください。


補足

Metal 対応デバイス一覧については下記記事でまとめています。


スライド内で触れている Core Image のトランジションエフェクトについては、下記記事に解説があります。


また画面遷移時のカスタムアニメーションについては、下記記事をご参照ください。


f:id:shu223:20140416100040g:image


今回は割愛した iOS 9 で追加された Core Image の新フィルタや、文字領域抽出(CITextDetector)については下記記事で書いています。


ボツネタ集

20名ぐらいの規模だと、とくに悩むこともなく自分の興味のあるネタを調べて発表するのですが、100名を超える規模の大きい勉強会だと、ニッチすぎる話だったり、調べたてホヤホヤで自分の中でまだあまり自信のないことについて話すのは気が引けるので、いつも何について話すかあーでもないこーでもないと悩んでしまいます。以下その過程で出てきたボツネタ集。


ボツネタ1.「iOS GPU入門」

iPhone 6s のトピックとして 3D Touch や Live Photos 以外にも、GPUが6コアになったという話も聞きかじったので、画像処理とも絡められるし、機械学習とかとも絡められるかもしれないし、個人的にも勉強したい、ということでiOSのGPUまわりについて色々調べて発表したい、というのが当初ありました。


ボツ理由:ちょっと調査はしてみたのですが、この分野に関してまだまだ素人すぎるので、こういう規模の大きい勉強会で話す段階ではないなと。BLEを勉強し始めたときのように、まずはポツポツとブログに書いていきます。


ボツネタ2.「Core Image × OpenCV」

iOS 9 もいいですが、OpenCV は今年実に6年ぶりのメジャーアップデートである 3.0 がリリースされたところで、個人的にはかなり熱いと思っています。


Core Image もだいぶ機能が豊富になり、ビルトインフィルタは内部では OpenGL や Metal による GPU Acceleration が効いていて、実装の手軽さもあるのですが、やはりまだまだ OpenCV にあって Core Image や vImage、GPUImage にもない機能というのは数多くあります。動画内の物体追跡とか、画像の自動修復(補完)とか、「誰々の顔である」という本来の意味での顔認識とか。(詳しくは下記記事にて)

iOS 9 で追加された CIDetector の文字領域抽出から OpenCV の文字認識につなげたり、といった連携アイデアも色々と考えられます。


ボツ理由:やれること/やりたいことが多すぎて、収集つかなくなりそうなのでやめました。こちらも地道にブログでやっていきます。


ボツネタ3.「iOS 9.1 の新機能」

iOS 9.1 では 3D Touch(ForceTouch)関連の機能がいろいろと強化されてたり、Live Photo を表示する PHLivePhotoView とかもあったりするので、そのへんの話もおもしろいかなと。


ボツ理由:話題のホットさ、という点でいいと思うのですが、時期が来れば色んな人がわかりやすい記事を書いてくれそうな分野なので、やめました。


おわりに

お声がけいただいたクラスメソッドさん、会場のdots.さん、参加者のみなさま、貴重な機会をいただきどうもありがとうございました!本勉強会は9週連続ということであと7回もありますが、次回以降も watchOS 2 や tvOS、Accelerateフレームワークの新機能の話など、個人的に興味深い回がたくさんあるので、今後もちょくちょく参加させていただきます!


f:id:shu223:20151008084805j:image

(会場の様子)


*1:今回久しぶりに大量のフィルタが追加されました。詳しくは こちらをご参照ください


[制作実績][IoT][BLE][iOS]【制作実績】ウェアラブルトランシーバー「BONX」のiOSアプリ開発をお手伝いしました

$
0
0

年初からずーーっと水面下で開発をお手伝してきたプロダクトがついに本日発表になりました!スポーツシーンで複数の仲間とコミュニケーションを取るためのデバイス&アプリ、


『BONX』


f:id:shu223:20151015095946j:image:w600


です!本日より「GREEN FUNDING Lab」にてクラウドファンディング開始してます。


BONXって何?

たとえばスノーボードのように、「みんなで行くものの、いざ滑り始めるとなかなかコミュニケーション取りづらい、でもしゃべりながら遊べたら楽しいのにねー」みたいな場面で使えるデバイス&アプリです。アプリがVoIP機能を持っていて、複数人でリモートでしゃべりながらアウトドアスポーツを楽しめるというものです。


f:id:shu223:20151015083344j:image:w600


僕をご存知の方は「あなたスポーツしないでしょ。。」と心の中でつっこんでいただいたかもしれませんが、僕みたいなインドア派でも、たとえば奥さんと二人で自転車で出かけたときに、呼びかけたいけどけっこう離れてしまってたりとか、話したいけど並んで走ると危ないとか、そういう「BONXがあれば」的なシーンはいくらでも思い当たったので、アイデアを聞いた段階から普通に個人的にも欲しいと思ってました。


f:id:shu223:20151015083416j:image:w600


そんなわけで、「激しいアウトドアスポーツ」に限らず、普通のお出かけ的サイクリングとか、釣りとか、サバゲーとか、カンファレンス等のイベントでのコミュニケーション等々にも役立つんじゃないかと。(※先に書いたようにVoIPなので距離の制限はなく、オンラインであれば地球の裏側とでも会話できます)



ちなみにCEOの宮坂さんに初めて会ったとき(2014年春。まだアイデアだけで、チームも何もなかった)、トランシーバーの GoPro 的ポジションを狙いたい、というようなことを言っててそれが僕の中ではこのプロダクトの方向性がすごくハラオチした言葉でした。


どのへんが「スポーツシーン向け」なのか?

動画を見てもらうのが一番わかりやすいかもしれません。



動画を見れない方のために特徴を列挙すると、


  • デュアルマイクによる騒音・風切り音対策
  • 水や衝撃に強い
  • グローブつけたままでもさくっと着脱できて、激しく動いても転んでも外れにくく、ずっと着けていても痛くならない形状
  • 動いてる最中でも操作しやすいデザイン
    • 全体が大きな二つのボタンになっているので、簡単にミュートにしたり音量を調節したりできる

f:id:shu223:20151015083536p:image:w600


・・・等々ハードウェア面でもいろいろありますが、アプリ開発者としてがんばったところでいうと、

  • 電波の弱い環境でも話せる!
  • アプリ起動からワンタップでグループ通話開始!
  • バッテリーにもやさしい

といったところが売りです。マイクが拾う音声を全部送りつづけると通信量も大きく、通信による電力消費も大きいので、「人間の声」だけをフィルタリングして通信量と電力消費を抑制したり、その他いろいろと細かい努力をしております。(今も鋭意努力中です。)


f:id:shu223:20151015083047j:image:w600


欲しいけど、クラウドファンディングはちょっと・・・

KickStarter は買い物じゃないよ、というのはよく言われる話です。「買い物」じゃなくて「支援/出資」ですよ、と。で、ときにはモノが届かないこともある、と。。


ただ、BONX は買い物です。100% 手に入ります。


今回のクラウドファンディングで資金を調達できたらつくりはじめるぞー!ではなくて、もうずっと開発し続けていて、アプリは詰めの段階に入っており、ハードの方も量産体制まで確立できています。


余談:関わらせていただいた経緯

前述しましたが、CEO 宮坂さんとは2014年春に一度お会いして、話を伺う機会がありました。(ユカイ工学 CEO の青木さんの紹介)


そのときに既にプロダクトのアイデアにも宮坂さんにも魅力は感じていたのですが、これからメンバー集めと資金調達、という段階だったので、純粋に手を動かすところだけをやっていきたい僕としては、開発フェーズになったらまたお声がけください、とだけお伝えしました。


で、WHILLやったりMoffやったり真鍋大度さんとのプロジェクトをやったりして「今年はまじで楽しかった、来年もおもしろい仕事ができるといいなぁ」「宮坂さんのあの件、どうなったかな。あのプロダクトのアイデアはよかったなー」とまさに思っていた2014年の暮れ、宮坂さんからひさびさに電話があり、「来た!」ということで2015年の年明けからお手伝いを開始させていただいたのでした。


CEO宮坂さん、CTO楢崎さん、プロダクトデザイナーの百崎さんをはじめ、メンバーみなさんが仕事もバリバリできて人間としてもいい人達ばかりで、オフィスも駒沢オリンピック公園から徒歩10秒(隣接)と楽しい場所にあります。


ちなみに、サーバーサイドは僕がカヤック時代から何かとお世話になっている Typester こと村瀬氏と、同じカヤック時代からすごかった若手のきっつーこと北原氏の会社Make It Realが担当。スーパー実力者。そういう点でもこのお仕事は魅力的でした。


そんなわけで

興味が湧いたみなさま、ぜひクラウドファンディングのページにてポチッと応援いただけると嬉しいです!(ちなみに超早期限定コースは超格安設定です。僕も2個セットをポチりました!)



他のフリーランスとしての制作実績もブログにまとめています:[制作実績] - Over&Out その後


[iBeacon][BLE][Swift]Eddystone と iOS - その2: 実装編

$
0
0

その1 では iOS アプリ開発者から見た、Eddystone を採用するメリット・デメリットについて書きました。本記事は実装編として、Core Bluetooth を用いた Eddystone 検出機能の実装方法 をポイントをかいつまんで紹介したいと思います。


f:id:shu223:20151201110208p:image



なお、本記事では Core Bluetooth や Bluetooth Low Energy の基礎的なことは省略します。よろしければ下記書籍を参考にしてください。


iOS×BLE Core Bluetoothプログラミング
堤 修一 松村 礼央
ソシム
売り上げランキング: 898

電子書籍版 もあります)


Eddystone を発見する

普通に CBCentralManager でスキャンを開始するだけです。

let services = [CBUUID(string: "FEAA")]
centralManager.scanForPeripheralsWithServices(services, options: nil)

上のコードでは Eddystone ビーコンだけを発見対象とするよう、Eddystone Service UUID `FEAA` を指定しています。


アドバタイズメントデータから Eddystone フレームを取り出す

スキャンしてペリフェラル(ここではEddystoneビーコン)を発見すると、didDiscover〜が呼ばれるわけですが、この引数に入ってくるアドバタイズメントデータから Eddystone frame を取り出します。

func centralManager(
    central: CBCentralManager,
    didDiscoverPeripheral peripheral: CBPeripheral,
    advertisementData: [String : AnyObject],
    RSSI: NSNumber)
{
    let serviceData = advertisementData[CBAdvertisementDataServiceDataKey] as? [CBUUID : NSData]
    
    if let serviceData = serviceData {
        let eddystoneServiceData = serviceData[CBUUID(string: "FEAA")]
    }
}

こんな感じで、アドバタイズメントデータから `CBAdvertisementDataServiceDataKey` をキーとしてサービスデータを取り出し、その中からさらに Eddystone Service UUID をキーとして Eddystone フレームデータが取り出せます。


フレームタイプを判別する

Eddystone フレームの先頭バイトが Eddystone のフレームタイプを示します。

class func frameTypeForEddystoneServiceData(data: NSData) -> EddystoneFrameType {
    
    var bytes = [UInt8](count: data.length, repeatedValue: 0)
    data.getBytes(&bytes, length: data.length)
    let firstByte = bytes[0]
    
    if firstByte == 0x00 {
        return .UID
    }
    else if firstByte == 0x10 {
        return .URL
    }
    else if firstByte == 0x20 {
        return .TLM
    }
    return .Unknown
}

(Enum定義は省略)

こんな感じで、UID / URL / TLM を判別します。


各フレームデータのパース

ここは細かい話になるので、詳細は割愛しますが、実装にあたっては下記のソースコードを参考にしました。

  • Google のサンプルコード
    • 本家のコードなので仕様の解釈に困ったらこちらが指針になる
    • あくまでサンプル。あまり綺麗に、使い回しやすくは書かれていない。。
    • Eddystone-URLフレームタイプは非サポートPull Request 送信済み

距離推定(近接度推定/レンジング)

iBeacon と同等に使おうと思うと、アドバタイズメントデータ受信時に呼ばれる `centralManager:didDiscoverPeripheral:advertisementData:RSSI:` の第4引数に入ってくる RSSI 値と、UID あるいは URL フレームに入っている TxPower より距離を推定するなり、RSSI 値を閾値処理して近い/遠い(iBeacon でいう Proximity)を判別するなりする必要があります。


これについては色々とあるのでまた別記事に書きたいと思います。


バックグラウンドサポート

Eddystone向けの特別な手順は必要ありません。普通のセントラル側のバックグラウンド対応と同様です。詳しくは冒頭で紹介した参考書籍等をご参照ください。


TLM / URL フレームの取り扱い

TLM / URL フレームは UID フレームと違って UUID/Major/Minor に相当するような「ビーコンを特定するデータ」を持っていません。が、たとえばTLMフレームからメンテナンス用の情報を取得できたとして、「このTLMデータはどのビーコンのものなの?」というのを解決しないと意味がありません。


ではどうするのかというと、ペリフェラルの UUID を用いて UID フレームと紐付けます。


TLM フレームは Frame Specification に書かれている通り、UID や URL フレームの合間にアドバタイズされるものなので、


  • UIDフレームデータを保持するモデルクラスに、TLMデータを保持できるようプロパティを用意しておく
@interface EddystoneUIDBeacon : EddystoneBeacon

@property (nonatomic) NSNumber *txPower;
@property (nonatomic) NSString *namespaceId;
@property (nonatomic) NSString *instanceId;

@property (nonatomic) EddystoneTLMBeacon *tlmBeacon;

@end
  • UIDフレームが検出されたら、ペリフェラルUUIDをキーとして、UIDフレームデータをDictionaryに保持する
beaconDic[beacon.identifier] = foundBeacon
  • TLMフレームが検出されたら、ペリフェラルUUIDをキーとして上記Dictionaryの値を取り出し、値(=UIDフレームデータ)があればそのプロパティにTLMデータをセットする
foundBeacon = beaconDic[beacon.identifier]
foundBeacon.tlmBeacon = beacon

といった感じで実装できます。


じゃあURLの場合はどうするのかというと、Googleのサンプルには実装されていなかったので、正式なところはよくわかりません。URLにID的なものを持たせられる、という考え方なのかもしれません。


が、自分が今回試していた kontakt.io のビーコンでは、UID/URL/TLMの3パケットを順番にアドバタイズする(設定切り替えによってではなく)という挙動だったので、TLM同様ペリフェラルUUIDを用いてUIDフレームと紐付ける、という実装を行いました。(Google に送った Pull Request もそういう実装)


まとめ

今回は実装編として、Eddystone ビーコンを iOS アプリから検出する際の実装方法をポイントをかいつまんで紹介しました。Eddystone は Bluetooth SIG に承認されないと使用できない16ビットUUID を持っているとか、TLMはUID/URLフレームの合間にアドバタイズされるものであるとか、TLMフレームはUIDフレームと紐付けて用いるとか、(今回は書きませんでしたが)URLを20バイト以内(実質18バイト以内)にどう収めているか等々、個人的には実装してみるまでは見過ごしていた仕様が多々ありました。今回割愛した、「RSSIからの距離あるいは近接度推定」についてはiBeaconと共通の話でもあるので、また別記事で書きたいと思います。


(追記)書きました: RSSI と TxPower からビーコンとの距離および近接度(Proximity)を推定する - Over&Out その後


[iBeacon][BLE][iOS][Swift]RSSI と TxPower からビーコンとの距離および近接度(Proximity)を推定する

$
0
0

先日、Eddystone の使いどころについて書いた記事で、iBeaconと比較した場合の利点のうちのひとつは「カスタマイズ性」(=自前実装が可能)であると書きました。 *1


この場合、Core Bluetooth を使って、

  1. Eddystone ビーコンをスキャン
  2. 発見時に得られるアドバタイズメントデータを解析

という処理を実装することになるわけですが、

その際に Core Location の iBeacon 実装と同等のことをしたければ、つまり「近接度(Proximity)」を知りたければ、 didDiscoverPeripheral〜 メソッドの引数に渡されてくる RSSI と、ビーコンの TxPower から推定距離を算出する 必要があります。 *2


TxPower と RSSI について

TxPower とは?

ビーコンが発する信号の強さを示していて、単位は dBm (デシベルメートル)です。iBeacon の場合は 1m 離れた地点での受信信号強度を利用しますが、Eddystone の場合は 0m 地点での強度を利用する、という違いがあります。

Note that this is different from other beacon protocol specifications that require the Tx power to be measured at 1 m.


1m 地点の受信信号強度に 41dBm を足すと 0m 地点での受信信号強度に なります。

Note to developers: the best way to determine the precise value to put into this field is to measure the actual output of your beacon from 1 meter away and then add 41dBm to that. 41dBm is the signal loss that occurs over 1 meter.



iBeacon の場合はこの値は Proximity UUID や Major, Minor が入っている Manufacturer Data の領域の最後に入っています。



Eddystone の場合は、この値は Eddystone-UID フレーム や、Eddystone-URL フレーム より取得できます。



ちなみに iBeacon の場合は Core Location 内部で Proximity を計算するので、この値を Core Location の API から直接取得することはできません。が、iOS をペリフェラル(ビーコン側)としてふるまわせたい場合に利用する `peripheralDataWithMeasuredPower:` メソッドの引数に `nil` を指定するとデフォルト値が設定されるのですが、その値は -59dBm となります。


RSSI とは?

Received Signal Strength Indication の略で、ここでは iOS デバイスが受信した電波の強度を示します。単位は同じく dBm です。


RSSI と TxPower から推定距離を計算する

自由空間では受信信号強度は距離の二乗に反比例して減衰していく(フリスの伝達公式)ので、RSSI と TxPower と距離(d)の関係は次のようになります。

RSSI = TxPower - 20 * lg(d)

(lg は底を 10 とする常用対数)


というわけで、距離 d の計算式は以下のようになります。

d = 10 ^ ((TxPower - RSSI) / 20)

Swift のコードだとこんな感じです。

let d = pow(10.0, (TxPower - RSSI) / 20.0)

ちなみに上で「自由空間」と書きましたが、自由空間というのは「障害物のない理想空間」を意味していて、実際には障害物の有無などで電波の受信強度というのは変わってきます


これを考慮したい場合には次のように距離に係る部分を変数にします。

RSSI = TxPower - 10 * n * lg(d)

この係数は

  • n = 2.0 : 障害物のない理想空間
  • n < 2.0 : 電波が反射しながら伝搬する空間
  • n > 2.0 : 障害物に吸収され減衰しながら伝搬する空間

という意味になります。



(参考書籍、ページ)

  • iBeacon ハンドブック 2.3 「ビーコンまでの距離推定」

iBeacon ハンドブック
iBeacon ハンドブック
posted with amazlet at 14.12.08
(2014-03-25)
売り上げランキング: 11,647

近接度(Proximity)への変換

Core Location の実装ではビーコンとの距離を、CLBeacon の `proximity` プロパティから、以下の3段階で得られるようになっています。

  • CLProximityImmediate (非常に近い)
  • CLProximityNear (近い)
  • CLProximityFar (遠い)

上に載せた『ビーコンハンドブック』によると、それぞれ 〜20cm、1〜2m、それ以上、とされています。 *3


単純な閾値による判定が行われているのか、その閾値はいくつなのか、というのはわかりませんが、上記を目安に近づけることは可能です。


またレンジングの間隔は1秒であること、CLBeacon の `accuracy` プロパティは "one sigma horizontal accuracy in meters" を示していることから、何回分かのRSSIから計算した推定距離を平均化して近接度の判定に用いていると推測されます。


accuracy(精度)の計算

CLBeacon の accuracy について散見される誤解

ちょっと話はずれるのですが、CLBeacon の `accuracy` プロパティについて、「ビーコンとの推定距離をメートル単位で示したもの」という記述 を結構みかけます。(日本語記事だけじゃなくて、StackOverflowとかでも見かける)


ただAppleのリファレンス によるとこの accuracy は

one sigma horizontal accuracy in meters

とあり、この "one sigma" は

標準偏差によって定義される範囲は測定の68.3%(1σ)の信頼区間である。標準偏差を正確に求めるために十分な回数の測定が行われ、測定の誤差に偏りがなければ、測定のうち68.3%は 1σ の範囲にあり、95.4%は 2σの範囲にあり、99.7%は 3σ の範囲にあることになる。(正確度と精度 - Wikipedia

f:id:shu223:20151203074506p:image:226]

の意味での "1σ"(シグマ)です。


なので、`accuracy` は 推定距離ということではなく、推定距離のばらつきを示す値です。


というわけでリファレンスでも、

Use this property to differentiate between beacons with the same proximity value. Do not use it to identify a precise location for the beacon.

と、「同じ proximity であるビーコン同士の比較に使いましょう」としているわけですね。


accuracy の計算

というわけで CLBeacon の accuracy 相当の値が欲しい場合には、推定距離の標準偏差を計算します。


まとめ

RSSI と TxPower から距離を推定する方法について書きました。個人的には Eddystone を iBeacon の代替として用いる際にこの実装が必要になりましたが、iBeacon を使う場合でも、(ラップされているとはいえ、)このあたりを理解しておくとトラブルシューティングの際などにも見通しがよくなるのではないかと思います。


*1:iOSでは、iBeacon仕様のビーコンを検出し Proximity UUID / Major / Minor を取得するということを Core Bluetooth で自前で実装することができないようになっている。詳しくは こちらの記事 を参照

*2:推定距離を算出せず、RSSIの値をそのまま平均して閾値処理にかけて近接度を判定するという実装もみかけます。RSSIは対数ベースの値だし、あまり適切ではないと思うのですが、どうなんでしょうね。。

*3:Appleのリファレンス、プログラミングガイドではこの記述を発見できず。WWDCの資料とか、他のドキュメントに書いてある?

[Bluetooth][iOS][BLE][Swift][音声処理]Classic Bluetooth について iOS アプリ開発者ができること

$
0
0

Bluetooth Low Energy については Core Bluetooth で色々と制御できますが、Classic Bluetooth(以降クラシックBT)については基本的に開発者は制御できません*1


f:id:shu223:20151205180842p:image:w200


そう、確かにアプリ内からクラシックBTデバイスと接続したり、データを送るとか送らないとか制御したり、通信を切断したり、といったことはできないのですが、そんな中でもアプリ側から一切クラシックBTデバイスの存在を感知することができないのかというとそうでもなく、いくつかの「口」はあります。


というわけでそういう「口」を集めてみました。他にもあればぜひ教えてください!


ボタン操作イベントの取得

BTイヤフォンの再生・停止等のボタン操作イベントを取得するには、次のように取得開始を宣言し、かつファーストレスポンダになります。

UIApplication.sharedApplication().beginReceivingRemoteControlEvents()
self.becomeFirstResponder()

イベントが飛んで来ると、`remoteControlReceivedWithEvent:` が呼ばれます。

override func remoteControlReceivedWithEvent(event: UIEvent?) {
    guard event?.type == .RemoteControl else { return }
    
    if let event = event {

        switch event.subtype {
            
        case .RemoteControlPlay:
            print("Play")
        case .RemoteControlPause:
            print("Pause")
        case .RemoteControlStop:
            print("Stop")
        case .RemoteControlTogglePlayPause:
            print("TogglePlayPause")
        case .RemoteControlNextTrack:
            print("NextTrack")
        case .RemoteControlPreviousTrack:
            print("PreviousTrack")
        case .RemoteControlBeginSeekingBackward:
            print("BeginSeekingBackward")
        case .RemoteControlEndSeekingBackward:
            print("EndSeekingBackward")
        case .RemoteControlBeginSeekingForward:
            print("BeginSeekingForward")
        case .RemoteControlEndSeekingForward:
            print("EndSeekingForward")
        default:
            print("Others")
        }
    }
}

ただし、オーディオセッションカテゴリによっては、イベントがアプリまで飛んでこなくなります(OSでは拾っているが、アプリまで渡してくれない)。試したところでは、`AVAudioSessionCategoryPlayback` では取得可、`AVAudioSessionCategoryPlayAndRecord` では取得不可。


MPRemoteCommandCenter

上記の方法は今でも deprecated になってはいないものの、リファレンスによると、

In iOS 7.1 and later, use the shared MPRemoteCommandCenter object to register for remote control events. You do not need to call this method when using the shared command center object.

と、iOS 7.1 以降では `MPRemoteCommandCenter` を使うように書かれています。


MPRemoteCommandCenter *commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
[commandCenter.playCommand addTargetUsingBlock:^(MPRemoteCommandEvent *event) {
    // Begin playing the current track.
    [[MyPlayer sharedPlayer] play];
}

参考: Remote Control Events - Event Handling Guide for iOS


要注意!

記事を書きながら、念のため手元でシンプルなデモをつくって試したところこれがなぜか動作しない・・・!(BTイヤフォンのボタンイベントが飛んでこない)以前、試したときは動いたのに・・・


もちろんファーストレスポンダになるのを忘れる、というよくあるハマりポイントはクリアしています。過去に試したときと条件を合わせるため、同じBTデバイスを使い、iOS 8 で、同じオーディオセッションカテゴリ(Playback)を用いてもダメ。


で、ふと気付きました。


システムの音楽プレイヤーがボタンイベントを捕まえている状況だとアプリまでイベントが飛んで来ないのではないかと。ここでいう「システムの音楽プレイヤー」とは、iOS標準のミュージックアプリおよび `[MPMusicPlayerController systemMusicPlayer]` でアクセスできるプレイヤーのことです。


そこで AVAudioPlayer を使ってアプリ内で音楽を再生しつつクラシックBTデバイスのボタンを操作してみたところ・・・動作しました!


というわけで上述の仮説で合っているようです。


接続/切断をフック

これもイヤフォンやヘッドセット等のオーディオデバイス限定にはなりますが、そういったデバイスが接続/切断されるとき、オーディオの入力・出力ルート(iOS の世界では Audio Route と呼ばれる)の変更通知 `AVAudioSessionRouteChangeNotification` が飛んできます。


というわけでこれを監視しておき、

NSNotificationCenter.defaultCenter().addObserverForName(
    AVAudioSessionRouteChangeNotification,
    object: nil,
    queue: nil) { (notification) -> Void in
        
        // 通知を受け取ったときの処理
}

この状態でイヤフォンやヘッドセットをつなぐと、オーディオルート変更通知が飛んでくるので、そこで今のオーディオルートの入力ポートと出力ポートがそれぞれ何なのか、ということを知ることができます。

let route  = AVAudioSession.sharedInstance().currentRoute
let inPort  = route.inputs.first
let outPort = route.outputs.first

ここで、`AVAudioSessionPortDescription` の `portType` からそのポートのタイプを取得でき、たとえば A2DP プロファイルのBTイヤフォンを繋いだ場合は、`portType` は `AVAudioSessionPortBluetoothA2DP` (中身の文字列は `BluetoothA2DPOutput`)に、HFP プロファイルのBTヘッドセットを繋いだ場合は `AVAudioSessionPortBluetoothHFP` になるので、これを用いればクラシックBTデバイスの接続・切断をフックできることになります。


ただ、この方法も万能ではなくて、音楽再生中とか、録音中とか、オーディオデバイスが機能している状態じゃないと変更通知が飛んできません。(詳細な条件は要調査)


いつでもどうしても確実にフックしたい場合は `currentRoute` をポーリングするとかになるのかと思います(が、やったことないのでこれも何か思わぬ落とし穴があるかもしれません)。


接続デバイス名の取得

前項にて現在のオーディオルート情報 `AVAudioSessionRouteDescription` を取得し、そこから入力/出力ポートの情報を保持する `AVAudioSessionPortDescription` オブジェクトを取り出しました。


この `portName` プロパティから、デバイス名を取得できます。

let route  = AVAudioSession.sharedInstance().currentRoute
let outPort = route.outputs.first
if let outPort = outPort {
    print("out port name:\(outPort.portName)")
}

(BTイヤフォン接続時の実行結果)

out port name:Jabra Rox Wireless v2.5.4

(これ以外に、`UID` というプロパティもありますが、`xx:xx:xx:xx:xx:xx-tacl` とMACアドレスらしきものが入っている場合があったり、単に `Wired Headphones` と入っている場合があったりで、統一性がなく何かの判別には使いづらいかなと感じました)


入力・出力デバイスの選択

`AVAudioSessionRouteDescription` は `inputs`、 `outputs` と、複数の入出力ポートを持てるようになっています。AVAudioSession に `setPreferredInput:` というメソッドがあるので、入力デバイスは明示的に選択できそうです。

/* Select a preferred input port for audio routing. If the input port is already part of the current audio route, this will have no effect.
   Otherwise, selecting an input port for routing will initiate a route change to use the preferred input port, provided that the application's
   session controls audio routing. Setting a nil value will clear the preference. */

public func setPreferredInput(inPort: AVAudioSessionPortDescription?) throws

ただ `setPreferredOutput:` というメソッドは見当たらないので、出力デバイスが複数ある場合はどうするんでしょうか。inputと連動するのか、別のメソッドがあるのか。まだこのあたりは試したことないので、今度挙動を確かめてみます。


つづく

AVAudioSession のヘッダを見ているとまだありそうなので、引き続き何か分かり次第追記していきます。


*1:MFiプログラム締結時(External Accessory framework 利用可能時)は例外

[iBeacon][BLE][iOS]Googleリポジトリのコントリビュータになりたくて19日間粘った話

$
0
0

「Google リポジトリのコントリビュータです」って言えたらかっこいいなぁというミーハー精神からプルリクを送り、却下されそうになりつつも粘ってマージしてもらって CONTRIBUTORS に名前も入れてもらえた(まだ世界で9人しかいない!)、という経緯について、嬉しいので書いておこうと思います。


google/eddystone リポジトリ

Google が提唱しているビーコン規格、「Eddystone」というものがありまして、これは Apple の iBeacon の対抗規格となるわけですが、その売りのひとつとして、「オープン」である、という点があります。


そんなわけで、Google社のリポジトリ google/eddystone にて、その規格やサンプルコード、関連ツール等が公開されています。


最初のプルリク

先日の海外案件にて iBeacon をいじっていたときに、とある「クローズドの壁」にぶつかり、Eddybeacon も試してみるかー、という話になったのが発端です。


で、下記記事にも書いたのですが、Google の Eddystone リポジトリに置いてある iOS 向けのサンプルが不完全で、肝心の Eddystone-URL フレームタイプがサポートされていない、という状態でした。


Eddystone の仕様について勉強しつつ Google のコードを読んでいるうちに、「自分が実装するならこのへんにこう書くな」というのが思い浮かんできて、それと冒頭に書いたようなミーハー心も手伝って、コードを修正してプルリクを送ってみました。


Travis CI のテストが通らない・・・

こんな感じで failed になってしまいました。

さっくりあきらめようかとも思いましたが、ログをよく見ると自分がやった修正と全然関係ないところで引っかかっていて、他のプルリクの自動テスト結果を見ると同じログが出ていたところもあったので、コミットログからコミッタらしき人に連絡をとってみました。

Hi, my pull request could not pass the check by Travis CI, however this error seem not to be caused by my changes. Please confirm my changes don't cause errors and merge. Thanks.

(このエラーって僕の修正のせいじゃないと思うんだけど見てもらえますか?)


で、このコミッタの mashbridge さんがいろいろな人に連絡をとってくれて、g-ortuno さんという人が Travis を直してくれたのでした。


フィードバック→修正の繰り返し

プルリクの修正の中身については、marcwan さんという人がフィードバックをくれました。


「インデントは4スペースじゃなくて2スペースを使うように」とか「1行あたり100文字を超えないように」とか以外の大きいフィードバックとしては、

This pull request basically adds support for an Eddystone beacon sending out both URL and UID frames. It would be nice if we could also support just scanning and reporting for URL frames as well, even if the underlying hardware isn't broadcasting UID frames.

For this, I had thought to add a new didFindURL:(NSUUID *) method to the delegate class. IF a beacon has URL + UID, then they'll get the didFindBeacon: as they currently do, but whenever we see a URL, we could also fire a didFindURL: delegate as well (provided people are listening to it).

Should we fold in duplicates of a given URL within a certain timeframe? Probably. Maybe don't report a URL more than once a minute?

I can do this new change, or if you're feeling super energetic, you're welcome to contribute that too ^_^

というものがあり、修正イメージがすぐに湧いたしそんなに大変じゃなさそうだったので対応してpushしました。


(Eddystoneの仕様や実装の細かい話については、下記記事もご参照ください)


CONTRIBUTORS に!

そんなこんなで

Awesome, Played around with it and it seems great, thanks so much!

とマージしてくれそうな雰囲気になり、


Do you want to add yourself to the CONTRIBUTORS file too?

と、あちらから CONTRIBUTORS ファイルへ僕の名前を追加することを申し出てくれたのでした。

中身を見るとまだ9名しかいない(2015年12月現在)のでこれはかなり嬉しかったです。



おわりに

そんなこんなで、最初のプルリクから19日後に、google/eddystone リポジトリに自分のソースコードをマージしてもらうことができました。といってもほとんどは待ち時間で、そんなにすごいやりとりがあったわけではないのですが、Travis CI のよくわからないエラーで fail になったときに面倒ながらも問い合わせコメントを書いてみたところが分岐点だったなと。確か、いったんはあきらめたのですが、思い直してダメ元でコメント書いてみてよかったです。せっかくなので LinkedIn にも書いておこうと思います!


余談:その他の著名リポジトリへのContributions

あまり書く機会もないので、過去にマージしてもらえた著名リポジトリへのプルリクもここで掘り起こしてみたいと思います。


GPUImage

iOSにおける画像処理ライブラリのデファクトスタンダード。

バイラテラルフィルタのシェーダのパラメータの修正。修正量は少ないですが、GPUImage のシェーダのソースをいろいろと読み、バイラテラルフィルタの特性をちゃんと理解しての修正なので、ある程度の貢献ではあると思います。


appledoc

Apple風ドキュメントを生成してくれるツール。

僕の環境で落ちて使えなかったので、判定を入れただけ。あまり本質的な修正でもないので、それほど誇れる話ではないです。


LGBluetooth

Core Bluetooth のラッパーライブラリ。

LGBluetooth は元のリポジトリが消えて、別のところに移されたので、残念ながらプルリクのログは残っていないようです。が、コミットログには残っています。当時は案件でも使っていたので自分の必要な機能を追加してはプルリクを送っていました。


facebook/pop

Facebook の Paper が話題になった頃に公開され、同アプリで使われているということでこちらも話題になったアニメーションエンジン。


ソースの規模が他のアニメーション系ライブラリと比べて大きく、何をやっているんだろう、とソースを読んでみようとして、

どのクラスがどういう役割なのかを把握しようと思ったところ無駄に依存し合ってる部分が多くあって設計指針が把握しづらいと感じたので、依存関係を整理してプルリクを送ったのでした。


f:id:shu223:20151212160336p:image:w500



他にも大小あります。不具合修正や、他の人も必要になるであろう機能追加であればなるべくpull requestを送るようにしています。



Viewing all 317 articles
Browse latest View live