その後のその後

iOSエンジニア 堤 修一のブログ github.com/shu223

[iOS 12]Siri Shortcutsの最小実装 - NSUserActivity編

先日のWWDC18で発表された、iOS 12の新機能 "Siri Shortcuts"。「よくやる手順を声で呼び出せる」というのはもちろん嬉しいことですが、それよりも、

「ロックスクリーンから呼び出せる」

というところに、プラットフォームの制約の中で取捨選択するしかないいちアプリ開発者としては圧倒的魅力を感じざるを得ません。

f:id:shu223:20180613205618p:plain

「何がどこまでできるのか、どう実装するのか」を掴むべく、まずはその最小実装を確認してみました。

ちなみに参考資料はWWDC 2018のセッション番号211「Introduction to Siri Shortcuts」とサンプルコード「SoupChef」です。1

実装方法は2通り

Siri Shortcutsの実装方法としては、以下の2種類があります。

  • NSUserActivityを利用する方法
  • Intentsを利用する方法

本記事では、タイトル通り、NSUserActivityを用いたSiri Shortcutsの最小実装だけを紹介します。

Appleのサンプル「SoupChef」は両方の実装が入っており、それはそれでありがたいのですが、初めて挑む人にはどのコードがどっち用なのか、たとえばIntentsを使わない場合はどれを省けるのかといったことがわかりづらいと思うので、そのあたりを本記事で紐解ければと。

3ステップ

NSUserActivityを用いたSiri Shortcutsの実装は、次の3つの手順で行います。

  1. ショートカットを定義する
  2. ショートカットを提供する(donate) 2
  3. ショートカットをハンドルする

「アプリの画面Bを開く」というショートカットをSiri Shortcutsを実現してみます。

1. ショートカットを定義する

Info.plistに次のようにNSUserActivityTypesを定義します。

<key>NSUserActivityTypes</key>
<array>
    <string>com.myapp.name.my-activity-type</string>
</array>

2. ショートカットを提供する

Intentsをインポートして、

import Intents

次のようにNSUserActivityを用意します。

extension NSUserActivity {
    
    public static let myActivityType = "com.myapp.name.my-activity-type"

    public static var myActivity: NSUserActivity {
        let userActivity = NSUserActivity(activityType: myActivityType)
        
        userActivity.isEligibleForSearch = true
        userActivity.isEligibleForPrediction = true
        userActivity.title = "My First Activity"
        userActivity.suggestedInvocationPhrase = "Let's do it"
                
        return userActivity
    }
}

一見複雑に見えますが、Siri Shortcutsに関係するポイントとしては、

  • isEligibleForPredictionプロパティにtrueをセットする
  • suggestedInvocationPhraseプロパティをセットする
    • ここにセットしたフレーズがショートカットの音声コマンドをユーザーに録音してもらう画面でサジェストされる

これぐらいです。

Appleのサンプルには、次のようにCSSearchableItemAttributeSetを作成してcontentAttributeSetにセットする実装も入っていますが、

// 省略可
let attributes = CSSearchableItemAttributeSet(itemContentType: "hoge")
attributes.thumbnailData = UIImage(named: "filename")!.pngData()
attributes.keywords = ["foo", "bar"]
attributes.displayName = "My First Activity"
attributes.contentDescription = "Subtitle"
userActivity.contentAttributeSet = attributes

私が試したところではSiri Shortcutsを行うだけであれば省略可能でした3。検索窓(Spotlight)からのテキストによる検索にも対応させたい場合に必要なのではないでしょうか。

作成したNSUserActivityオブジェクトを、ショートカットを提供するUIViewControllleruserActivityプロパティ(正確にはUIResponderのプロパティ)にセットします。

userActivity = NSUserActivity.myActivity

ここでもAppleのサンプルでは次のようにupdateUserActivityState(_)をオーバーライドしてNSUserActivityaddUserInfoEntries(from:)メソッドでuserInfoのデータを供給する実装が入っていましたが、これもなくても動作しました。

// 省略可
override func updateUserActivityState(_ activity: NSUserActivity) {
    let userInfo: [String: Any] =  [NSUserActivity.ActivityKeys.menuItems: menuItems.map { $0.itemNameKey },
                                         NSUserActivity.ActivityKeys.segueId: "Soup Menu"]

    activity.addUserInfoEntries(from: userInfo)
}

ショートカットでアプリが起動する際に、アプリの状態を復元するために必要なデータ(userInfo)がある場合に実装すべきものと思われます。

なお、ここに出てくるNSUserActivityCSSearchableItemAttributeSetが初見の方は、「iOS 9 の新機能のサンプルコード集」の「Search APIs」サンプルを実行してみつつコードを読んでみると非常にわかりやすいです。NSUserActivity を使うものと、Core Spotlight を使うものの2種類が実装してあり、コードがシンプルなのでどのプロパティが何に対応してるのかがわかりやすいです。

3. ショートカットをハンドルする

ショートカットが呼び出されてアプリが起動する際、UIApplicationDelegateapplication(_:continue:restorationHandler:)が呼び出されるので、そこで渡されてくるアクティビティ(NSUserActivity)をハンドルします。

func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
    
    if userActivity.activityType == NSUserActivity.myActivityType {
        // Restore state for userActivity and userInfo
        guard let window = window,
            let rootViewController = window.rootViewController as? UINavigationController,
            let vc = rootViewController.viewControllers.first as? ViewController else {
                os_log("Failed to access ViewController.")
                return false
        }
        vc.performSegue(withIdentifier: "B", sender: nil)
        return true
    }
    
    return false
}

ここではactivityTypeでどのアクティビティかを判定し、あとは愚直にperformSegueで画面Bに遷移させているだけです。

完成品の挙動

NDA期間中のため完成品の挙動をキャプチャしてアップすることができないのですが、

  • 設定から当該Siri Shortcutを登録
  • ロックスクリーンから登録したフレーズで呼び出す

これでアプリが起動し、画面Bまで自動的に遷移しました。

アプリはバックグラウンドで生きている必要があるのか?

気になったのが、UIViewControlleruserActivityプロパティに当該NSUserActivityオブジェクトをセットした点です。これってこのView Controllerがショートカットのdonatorであり、アプリが生きてないと呼び出せないのか?と。

というわけでアプリをkillしてもNSUserActivityベースのSiri Shortcutは動作するか試してみました。

結果 → 動作しました。

良かったです。

次回

  • Intentsを用いる場合の最小実装
  • 両者をどう使い分けるか
  • Siri Shortcutsで呼び出せない機能はたとえばどんなものがある?

といったあたりについて書きます。(書けたらいいなと思っています)


  1. 本記事はNDAに配慮し、Xcode 10やiOS 12のスクショは使わず、公開情報のみで構成しています。

  2. 「機能を提供する」ということをiOSで表現する場合にはよく"provide"という動詞が使われることが多い気がします。ここでAppleがあえて「寄付する」「寄贈する」といった意味の"donate"を利用しているのはどういう気持ちがこもっているのでしょう?

  3. このへん、キャッシュが残るような挙動があり、動作検証結果に100%確信がありません。

ドラッグ&ドロップで機械学習のモデルがつくれる「Create ML」の使い方

iOS 12の気になる新機能のAPIを見ていくシリーズ。昨日はARKit 2の永続化・共有機能や3D物体検出機能について書きました。

本記事ではCreate MLについて。1

Create ML

Create MLは、Core MLのモデルを作成するためのmacOSの新フレームワークです。

f:id:shu223:20180606070639p:plain

昨日のState of the Unionにてデモがありましたが、なんと、学習用データが入ったフォルダをドラッグ&ドロップするだけで作成できます。

ちなみにmacOS 10.14 Mojaveです。

MLImageClassifierBuilder

まだMojaveにアップデートしていないので試せていない2のですが、丁寧なチュートリアル記事が出ていて、作業手順を図付きで確認できます。

PlaygroundsでMLImageClassifierBuildeを初期化してshowInLiveViewを呼ぶコードを書き、

import CreateMLUI

let builder = MLImageClassifierBuilder()
builder.showInLiveView()

実行するとGUIが表れ、そこにデータフォルダをドラッグ&ドロップすると学習が開始されます。

f:id:shu223:20180606071137p:plain

f:id:shu223:20180606071232p:plain

テスト(モデルの評価)もドラッグ&ドロップ。

f:id:shu223:20180606071320p:plain

いかがでしょうか。コードは正味3行書いただけ機械学習ができてしまいました。

Create MLでつくった.mlmodelフォーマットのモデルファイルを使ったアプリの実装方法はこちらの記事をどうぞ。

Core ML+Visionを用いた物体認識の最小実装 - Qiita

学習用データについて

ドラッグ&ドロップでできます」とはいえ、そのためのデータセットを用意する必要はあります。

どういうデータを用意するかですが、同チュートリアル記事によると、

  • ラベルごとに最低でも10枚の画像
  • ラベルごとに枚数のバランスをとること(チーターは10枚、ゾウは1000枚、みたいなことをしない)

Use at least 10 images per label for the training set, but more is always better. Also, balance the number of images for each label. For example, don’t use 10 images for Cheetah and 1000 images for Elephant.

  • JPEGPNG等、画像フォーマットはなんでもいい(UTIがpublic.imageに適合していれば)
  • サイズは揃ってなくてもOK
  • サイズもなんでもいいが、最低でも299x299ピクセルはあった方が良い

The images can be in any format whose uniform type identifer conforms to public.image. This includes common formats like JPEG and PNG. The images don’t have to be the same size as each other, nor do they have to be any particular size, although it’s best to use images that are at least 299x299 pixels.

あとは実際に推論を行うのと同じ状況で学習データも収集した方がいいとか、いろんな角度、ライティングの状況のデータがあった方がいい、ということが書かれています。

If possible, train with images collected in a way that’s similar to how images will be collected for prediction.

Provide images with variety. For example, use images that show animals from many different angles and in different lighting conditions. A classifier trained on nearly identical images for a given label tends to have poorer performance than one trained on a more diverse image set.

ラベルをフォルダ名にし、その配下にトレーニングデータ、テストデータを配置します。

Next, create a folder called Training Data, and another called Testing Data. In each folder, create subfolders using your labels as names. Then sort the images into the appropriate subfolders for each data set.

f:id:shu223:20180606071547p:plain

オープンなデータセット

ちなみに独自のデータセットを作成するのは非常に大変ですが、公開されているものもたくさんあります。

Mojaveを入れたら、このあたりのデータからいろいろなモデルをつくって試してみたいなと思っています。

こちらもどうぞ

shu223.hatenablog.com


  1. なお、本記事はNDAに配慮し、Xcode 10やiOS 12のスクショは使わず、公開情報のみで構成しています。

  2. High SierraXcode 10を入れて試してみましたが、import CreateMLUIでエラーになりました。

API Diffsから見るiOS 12の新機能 - ARKit 2 #WWDC18

今年のWWDCの基調講演は、エンドユーザ向けの新機能の話に終止した感が強く、デベロッパ的にはあまりピンと来ない2時間だったように思います。が、State of the Unionやドキュメントを見ると、試してみたい新APIが目白押しです。例年通り、気になったものを列挙していきます 1

全部書いてると時間かかりそうなので、まずは本記事では ARKit 2 について。

Multiuser and Persistent AR

自分が見ているARの世界を共有・永続化できるようになったというのは本当に大きいです。Appleのデモは「ゲームで対決できます」みたいな派手なものでしたが、たとえば奥さんの机に花を置いておくとかみたいなささやかなものでもいいですし、ソーシャルコミュニケーションな何かでもいいですし、教育的な何かでも使えそうですし、とにかく「自分しか見れない」という従来の制約がなくなるだけでARを利用したアプリのアイデアはめちゃくちゃ広がると思います。

f:id:shu223:20180605114618p:plain

API的には、ARWorldMapというクラスが追加されていて、これが「世界」の情報で、これを保存したりシェアしたりするようです。

ARMultiuserというサンプルが公開されていて、送受信のあたりのコードはこんな感じです。

(送る側)

sceneView.session.getCurrentWorldMap { worldMap, error in
    guard let map = worldMap
        else { print("Error: \(error!.localizedDescription)"); return }
    guard let data = try? NSKeyedArchiver.archivedData(withRootObject: map, requiringSecureCoding: true)
        else { fatalError("can't encode map") }
    self.multipeerSession.sendToAllPeers(data)
}

(受け取り側)

if let unarchived = try? NSKeyedUnarchiver.unarchivedObject(of: ARWorldMap.classForKeyedUnarchiver(), from: data),
    let worldMap = unarchived as? ARWorldMap {
    
    // Run the session with the received world map.
    let configuration = ARWorldTrackingConfiguration()
    configuration.planeDetection = .horizontal
    configuration.initialWorldMap = worldMap
    sceneView.session.run(configuration, options: [.resetTracking, .removeExistingAnchors])
    
    // Remember who provided the map for showing UI feedback.
    mapProvider = peer
}

ここで面白いのは、このサンプルでは送受信に Multipeer Connectivity を使っていて、データとしては普通にNSKeyedArchiverアーカイブ/アンアーカイブしたデータを送り合っている点です。

つまり、送受信や保存の方法はARKitに依存しておらず、なんでもOKということです。データをサーバに送ってもいいわけですが、バックエンドを用意せずとも、サクッとUserDefaultsに保存してもいいし、Realmに保存してもいいし、Core Bluetooth(BLE)を利用しても良いわけです。([PR]その際、こういう本が役に立つかもしれません)

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

3D Object Detection

「3D物体検出」は従来の(2D)物体検出と何が違うかというと、「正面から見た状態」だけでなく、横からでも、後ろからでも、上からでも、その物体を認識できるということです。この機能により、現実空間の物体(銅像とか)をマーカーとして仮想コンテンツを置く、といったことが可能になります。

ARKit 2には、3D物体を「スキャン」して「リファレンスオブジェクト」を生成する機能と、そのリファレンスオブジェクトと同じ特徴を持つ物体を現実空間から検出する機能とが追加されています。

f:id:shu223:20180605140338p:plain

(サンプルに同梱されている画像。スキャン〜検出の流れが示されている)

このわりと複雑なスキャンフロー、出来合いのやつがあるのかなと思いきや、ソースを見てみるとがっつりアプリ側で実装されています。3Dバウンディングボックスを順番に埋めていくのとかも全部。まぁ、ここはUIUXとして工夫のしがいがあるところなので、この方が良いですよね。

スキャンする際は、コンフィギュレーションとしてARObjectScanningConfigurationを使用します。

let configuration = ARObjectScanningConfiguration()

で、スキャン中にどういうことをするかですが・・・非常に泥臭い実装がされています。ここでは省略しますが、ARSessionDelegateの各デリゲートメソッドから実装を追ってみてください。

リファレンスオブジェクトを表すARReferenceObjectを作成するには、ARSessioncreateReferenceObject(transform:center:extent:completionHandler:)メソッドを呼びます。この引数に、スキャン対象のバウンディングボックスのtransform, center, extentを渡します。

sceneView.session.createReferenceObject(
    transform: boundingBox.simdWorldTransform,
    center: float3(), extent: boundingBox.extent,
    completionHandler: { object, error in
        if let referenceObject = object {
            // Adjust the object's origin with the user-provided transform.
            ...
        } else {
            print("Error: Failed to create reference object. \(error!.localizedDescription)")
            ...
        }
})

ARKitの新規追加API

APIを眺めるだけでもいろいろと察することはできるので、ざーっと載せておきます。

  • ARWorldMap

The space-mapping state and set of anchors from a world-tracking AR session.

  • AREnvironmentProbeAnchor

An object that provides environmental lighting information for a specific area of space in a world-tracking AR session.

  • ARReferenceObject

A 3D object to be recognized in the real-world environment during a world-tracking AR session.

  • ARObjectAnchor

Information about the position and orientation of a real-world 3D object detected in a world-tracking AR session.

  • ARObjectScanningConfiguration

A configuration that uses the back-facing camera to collect high-fidelity spatial data for use in scanning 3D objects for later detection.

  • ARFaceAnchor - 目の動きのトラッキング

    • var leftEyeTransform: simd_float4x4

      A transform matrix indicating the position and orientation of the face's left eye.

    • var rightEyeTransform: simd_float4x4

      A transform matrix indicating the position and orientation of the face's right eye.

    • var lookAtPoint: simd_float3

      A position in face coordinate space estimating the direction of the face's gaze.

  • ARImageTrackingConfiguration

A configuration that uses the back-facing camera to detect and track known images.

こちらもどうぞ

shu223.hatenablog.com


  1. なお、本記事はNDAに配慮し、Xcode 10やiOS 12のスクショは使わず、公開情報のみで構成しています。

フリーランスを再開して2ヶ月のお仕事まとめ

アメリカでのパートタイム会社員と日本でのフリーランスの2足のわらじを履き始めてから約2ヶ月が経ちました。今月からまた1ヶ月ほど会社員に戻るので、この2ヶ月にやったこと・つくったものをまとめておきます。1

f:id:shu223:20180506214619j:plain
Alexaを持ち出して外で遊ぶ

Core NFCを使ったゲームアプリ

これはまだ未公開なのですが、Core NFCを使ったゲームアプリをつくりました。ブルーパドル社でのお仕事。既に何種類かのゲームが遊べるのですが、手前味噌ながらかなり楽しいです。早く公開したいですがもうちょっと先になりそうです。

Core MLを使った写真の自動分類機能

とあるIoTスタートアップにて。モデル自体は社内の機械学習エンジニアの方がつくったものが既にあり、それをCore MLで使えるように変換するところからスタート。精度やパフォーマンスの検証、どういうUXに落とし込むのがいいかの検討を経て、当該企業のiOSアプリのいち機能として実装しました。社内βテストののち、近いうちにリリースされる見込みです。

ARKit、Metalを使った機能

GraffityというAR落書きSNSアプリ(最近出た2.0ではARビデオ通話アプリに)の開発をお手伝いしました。ARKitをプロダクトとしてがっつりやっている会社というのは(ゲームを除いて)たぶんまだ多くはなく、しかもMetalシェーダを書くという今の興味にがっちりはまるお仕事でした。2

技術顧問

最近立ち上がったとある会社に技術顧問として関わらせていただいてます。優秀な方々が開発していて、正直なところ技術を顧問できてるかは疑問ですが、年齢的にボードメンバーと開発メンバーのちょうど中間だったりするので、開発陣と経営陣のコミュニケーションの橋渡し役をしたり、色んな現場を見てきたならではの視点からプロダクト全体の方向性について意見を言ったりといったところではお役に立ててるのかなと。立ち上がったばかりのスタートアップで状況は常に動き続けているので、自分がどこで役立てるのか、慎重に見定めて行動していきたい所存です。

その他

Alexaスキル

すごく簡単なものですが、Alexaスキルをつくり、ブルーパドルメンバーで遊んでみたりしました。これ自体はストアで公開するほどのものではないのですが、「音声インターフェースアプリケーションはこんな感じでつくる」というのが掴めてよかったです。

ウェブのマークアップ

ブルーパドル社ではマークアップも経験しました。htmlとcssの概念ぐらいはもちろん理解してましたが、gulp、pug、scssというような最近の書き方はまったく知らなかったし、レスポンシブ/スマホ対応というのはそういうことなのねというのを知れたのも良かったです。残念ながらJSまでは時間的に手が回らず。

AV Foundationを使うiPadアプリ

GitHubの僕のとあるリポジトリを見て、海外の会社より依頼をいただきました。AV Foundationの最近さわってないAPIが関わるところだったので、自分のローカルコードベースを再整備しておく意味でもこれはやっておきたかったのですが、タイミング的に一番忙しい時期で、「次の休日に1日だけやってみて、間に合いそうならやる」と先方に伝え、1日やってみた感触では保ち時間では到底終わりそうにないためゴメンナサイしました。

Fyusion社のお仕事

アメリカでの雇い主であるFyusionより、ちょくちょくタスクが来てました。フリーの仕事で手一杯で機能開発までは手が回りませんでしたが、僕が実装した部分について質問に答えたり、日本のクライアント関連の対応をしたり。

個人活動

はじめて技術書を個人で出版

iOS/macOSGPUインターフェースであるMetalの入門書を書き、「技術書典4」にて販売しました。

shu223.hatenablog.com

総括

実績がぼやけている?

この2ヶ月、夜も休日も仕事をしていてものすごく忙しかった気がするのですが、こうやって列挙してみると、不思議なもので「もうちょっとできたのでは」という気がしてきます。

大小のプロジェクトが順不同で入り混じっているので、工数順に並べ替えてみます。

Metal入門執筆(9人日) > ARKit+Metalを使った機能開発 > Core MLを使った機能開発 > 技術顧問 > Webマークアップ > Core NFCを使ったアプリ開発 > その他(それぞれ1日かそれ未満)

左端と右端以外はあまり大差なく、のべ5〜8人日ぐらい。こうして個々の工数を意識してみると、要は分散してしまってるのだなと。

これはなかなか悩ましい問題です。いまのところは、AlexaやWebをやってみるといった「寄り道」は、これまでiOSにフォーカスしすぎた自分には必要なもので、逆に「プロフィールに書いたときにわかりやすく目立つ実績」(例: 有名なプロダクトをほぼまるっと実装、みたいなの)は、いま仕事の依頼が増えても手が増えるわけでもないので、こだわりすぎるところではないかなと考えています。

iOSエンジニアとしてのリハビリ

1年半にわたってひとつの会社にフルコミットしてきたのち、ひさびさにフリーランスとして色々な現場に行ってみるとまず気付いたのは、iOSエンジニアとしての総合力が落ちているということでした。Fyusion社では機械学習・3Dプログラミング・GPU制御といった新しいフィールドを開拓できた一方で、そういった技術を利用する機能の開発に関わることにフォーカスしていたためか、UIKitの細かい挙動とか、DBまわりとか、Swiftの最近のデファクト系ライブラリとか、そういう今までは普通にキャッチアップできていたところが疎くなっていて、浦島太郎になったような心持ちでした。

専業iOSエンジニアが書いたさまざまな現場のコードに触れ、ひさびさに日々の仕事でSwiftを書き、フルスクラッチでアプリもつくり・・・という中で、だいぶ勘を取り戻せた(キャッチアップできた)実感のある2ヶ月でした。

興味のある技術分野のお仕事

MetalやARKitといった最近興味を持っている分野のお仕事や、Core MLやCore NFCといった新しいフレームワークを実プロダクトで使う機会にも恵まれ、ただただ楽しかったです。

心残りは得られた技術ノウハウ3をアウトプットできてない点。たとえばCore NFCとか、ARKitとか、実プロダクトで使ってみてこそ得られる細かい知見がいろいろとありますが、今の所ローカルメモの中で埋もれています。

技術書の個人出版という新しい可能性

前身ブログ、QiitaGitHubと、たくさん「技術情報発信」を行ってきましたが、基本的にそれら自体は無収益でした。書籍を書くこともありましたが、フルコミットで2ヶ月とかかかって、印税は「3日受託開発した方が稼げる」ぐらい。

自分に学びがあるし、発信した内容からお仕事に繋がることもあるのでここでご飯を食べられる必要はないのですが、問題は、フリーとしての仕事(しかもおもしろい)がたくさんあるとき、それらを断ってまで(=自分の時間を1日n万円で買ってまで)新しい技術を勉強する選択がなかなかできない、というところにありました。

そんな折、今回「Metal入門」というニッチな技術書を執筆し、出版社を通すことなく個人で販売してみたところ、なんと簡単に収益が商業本の印税を超えてしまいました。

執筆に使った時間をカウントしてみると、のべ9人日ぐらい。この内訳としても、半分ぐらいは本としての体裁を整えるためにRe:Viewと格闘する時間だったり、印刷所を選んだり入稿方法について調べたりする時間だったりしたので、2回目、3回目とやっていくうちにもっと短縮できるはずです。そうすると、いずれ(下地となるブログ記事等が既にある前提で)技術情報発信の時間単価が受託開発の時間単価に並ぶというのも可能性としては考えられ、これは個人的には非常にエポックメイキングな出来事でした。

ブルーパドル社での非常勤メンバーとしての関わり

ブルーパドル社では毎週「ハッカソン」と称し、なにか簡単なものをつくってそれをベースにいろいろ遊んでみつつブレストする、ということをやってます。これがめっちゃ良くて、自分でアイデアを出す脳みそをまた久々に使うようになったし、家で眠ってるガジェットを掘り起こして触ってみる機会にもなってます。(上述したAlexaスキルはそのうちのひとつ)

また僕が普段フリーランスとしてお手伝いしているスタートアップの世界と、ブルーパドル社の主な事業領域である広告の世界、すぐ隣にあるようでまったく別の世界である点もおもしろいです。同じようなプログラミング言語フレームワークを使っていても、優先するポイントや考え方が違う。それに付随してスピード感とかも違ってくるし、それぞれの業界にいる人達の興味やトレンドも違う。

いろんな面でとにかく新鮮で、毎日多くの学びがあります。

今後の展望

冒頭に書きましたが、フリーランス活動は一区切りし、5月はひさびさにサンフランシスコに戻ってFyusion社にフルコミットします。6月頭はWWDCで、中旬ぐらいからまたフリーランスを再開する予定です。


  1. 許可をいただいた会社だけ社名を書いています。自分が関わったプロダクトや機能がまだ出ていない会社については、出たら聞こう、と思ってたのでまだ伏せています。

  2. ちなみにアメリカで所属しているFyusion社と事業領域がかぶるところがあるといえなくもない(AR)ので、そこらへんはあらかじめあちらのボードメンバーに了承をとりました。

  3. もちろんその会社固有の技術情報ではなくAPIの使い方とかハマりどころとか、あくまで一般的な話

[書評]iOS/Androidアプリ開発の総合解説書 - モバイルアプリ開発エキスパート養成読本

著者のひとり、森本さん(@dealforest)および版元の技術評論社様よりモバイルアプリ開発AndroidiOS)を解説する技術書「モバイルアプリ開発エキスパート養成読本」をご恵贈いただきました。

f:id:shu223:20170530170038j:plain

この表紙に見覚えがある方もいらっしゃるかもしれません。そう、本書の刊行は1年前、2017年4月24日です。Swift 3、iOS 10Xcode 8とメジャーバージョンにして最新からひとつ前になってしまった数字を見るにつけ記事を書くのが遅れたことを申し訳なく思う気持ちを抱きつつも、いや、バージョンが変わってしまって注目度が下がっているかもしれない今こそこの良書を改めて紹介するタイミングなのではと。

たとえば「Swift 3入門」という記事がありますが、この章では3からの大きい変更である「命名規則」について詳しく解説されています。これはSwift.orgのAPI Design Guidelinesに書かれている1内容で、これはSwift 4になった今でも重要ですし、これを日本語で解説してくれているまとまった情報源というのは貴重ではないでしょうか。

またiOS 10の新機能/新API群も、iOS 12がそろそろ発表されるであろう今こそ、これからiOS 9を切り、いよいよiOS 10APIを積極的に使っていくぞ、という時期なのではないでしょうか。User Notificationsフレームワークによって新たに可能になったリッチなpush通知や、Haptic Feedback等は多くのアプリにとって導入を検討する価値があります。

(User Notifiationsを利用したリッチなプッシュ通知 2

ちなみにiOS 10からの機能ではありませんが、日本では同時期から利用可能になったということで、本書ではApple Payの実装についても詳しく解説されているのも貴重なポイントです。

Xcode 8の章も、(当時の新機能である)Memory Graph DebuggerとInstrumentsを両方活用してメモリリークデバッグする方法や、PlaygroundでCocoaPods/Carthageを使う方法など、さらっと実践的なTipsが書かれていたりします。他にも「なぜXcode Extensionが導入されたのか?」「Automatic Signingで困るケース」等々、とにかく開発者視点だなと。

他の解説書にはない、実際の開発現場に即した内容

iOSアプリ開発やSwiftの入門書は数多出ています。いずれの書籍にも重要なことが書かれていますし、アプリをつくれるようにはなるでしょう。しかし、実際に会社に入り、ひとつのサービス/プロダクトを継続的に開発・運用していく場合、それら既存の本に書かれていることとはまた別の知識が多く必要になります。

本書ではリアクティブプログラミング(RxSwift)、設計手法、テスト、運用ツール(Fastlane等)、mBaaS(Firebase)といった、他の書籍ではなかなか扱わないであろう、しかし昨今の開発現場では必須ともいえるテーマについても書かれています。

目次

目次と、それぞれの著者を載せておきます。現場でバリバリやってるあの人やこの人が書いてます。また本記事はiOSエンジニア視点で書いてますが、Androidについても同じテーマで書かれています。

第1章: モバイルアプリ開発の基礎知識

  • 1-1 モバイルアプリ開発を取り巻く最近の動向…… 黒川 洋 坂田 晃一

第2章: Android開発最前線

  • 2-1 快適な開発を実現! Kotlin徹底入門…… 藤田 琢磨
  • 2-2 最新Android Studio入門…… 山戸 茂樹
  • 2-3 アプリの使い方を大きく変えるAndroid 7.0/7.1の新機能…… 山田 航

第3章: iOS開発最前線

  • 3-1 Swift 3.0入門…… 田坂 和暢
  • 3-2 iOS 10入門…… 熊谷 知子
  • 3-3 Xcode 8入門…… 森本 利博

第4章: リアクティブプログラミング入門

  • 4-1 リアクティブプログラミングとRx…… 黒川 洋
  • 4-2 RxJava実践入門…… 黒川 洋
  • 4-3 RxSwift実践入門…… 坂本 和大

第5章: 現場で役立つモバイルアプリ設計・開発

  • 5-1 メンテナビリティに優れたAndroidアプリを作るための実践ガイド…… 小形 昌樹
  • 5-2 タイプセーフでモダンなiOSアプリの設計…… 鈴木 大貴

第6章: 現場で役立つテスト

  • 6-1 モバイルアプリのテスト事始め…… 山戸 茂樹
  • 6-2 Androidアプリのテスト…… 藤田 琢磨
  • 6-3 iOSアプリのテスト…… 森本 利博

第7章: 運用に役立つツールの使い方

  • 7-1 モバイルアプリの運用に役立つツール…… 志甫 侑紀
  • 7-2 Androidアプリの運用に役立つツール…… 志甫 侑紀
  • 7-3 iOSアプリの運用に役立つツール…… 坂田 晃一

第8章: 今注目の「統合アプリプラットフォーム」Firebase活用入門

  • 8-1 今注目の「統合アプリプラットフォーム」Firebaseとは…… 山田 航
  • 8-2 Firebaseをアプリに導入しよう…… 山田 航
  • 8-3 導入したFirebaseを運用してみよう…… 山田 航

まとめ

そんなわけで、「モバイルアプリ開発エキスパート養成読本」、他の書籍には書かれていない「実際の開発現場に即した実践的な知見」にあふれていて、非常に良い本なので気になった方はぜひ。Kindle版もあります。

モバイルアプリ開発エキスパート養成読本 (Software Design plus)
山戸 茂樹 坂田 晃一 黒川 洋 藤田 琢磨 山田 航 田坂 和暢 熊谷 知子 森本 利博 坂本 和大 小形 昌樹 鈴木 大貴 志甫 侑紀
技術評論社
売り上げランキング: 231,722

  1. 本書はSwift Evolutionのproposalをベースに書かれています。

  2. 画像はiOS 10 Samplerより

ニッチな技術書を書いて #技術書典 に出展してみたら想像以上に需要があった話

昨日「技術書典4」にて、iOS/macOSGPUインターフェースであるMetalの入門書を販売してきました。初出展、初参加なので雰囲気を想像するのがなかなか難しく、ニッチな技術書なのできっと大量に余るだろうなと想像してたのですが、ありがたいことに用意した100部が完売。こういう本でもこれだけ需要があるということに驚き、感動した1日でした。

以下振り返りになります。

本の出来が最高だった

印刷は「日光企画」さんにお願いしました。

僕は学生時代に製本工場で日雇いのバイトをしたことが何回かあって、そこはちょっとしたことですぐに怒鳴るおじさんとかいて非常に緊張感のある職場でした1

というわけで印刷所=怖いというイメージしかなかったのと、サイトをみると情報量が多すぎてどうすればいいのかわからなくてビビっていたのですが2、開催一週間前になってやっと勇気を出して日光企画さんに電話してみたところこれがまぁめちゃくちゃ親切で、質問に丁寧に答えてくれるし、入稿したときも午前中にすぐに確認の電話をくれて、ああ印刷所って怖くないんだ、とイメージを改めた所存です。

そして開催当日の朝、会場のブースに届いていた箱を開けてみると、

f:id:shu223:20180422101931j:plain

最高。

A5というコンパクトさと130ページのちょっとした厚み、表紙のマットPP加工、透明度15%の薄いロゴ。僕は技術書が好きで電子版隆盛の今でも装丁によっては紙の本を買ったりしますが、これは普通に自分でも欲しいと思える出来に仕上がっていました。

売上

書籍(製本版)は1200円、電子版(PDF)とのセットで1500円。4分の3ぐらいの方が電子版セットの方を選択されました。プラス「普及版」という廉価バージョンと、BOOTHで前日に出していた電子版がイベント当日にもポツポツ売れたのもあり、この日の売上は約18万円でした。

今後も電子版や再販、コンテンツの横展開で収益がありえること、そしてそもそも商業誌だと絶対に企画が通らなそうな(≒初版2000部は売れなさそうな)ニッチな分野で本を書いて、それなりに稼げたということに非常に可能性を感じております。

とはいえそれもこれも、マニアックな技術書を買いに6000人もの人達が集まる技術書典というイベントあってのこと。本当にありがたい限りです。

もっと部数を増やすべきだったか?

  • 100部完売したのが14時過ぎ。ペース的に150部だと完売はしなかったと思われる
  • それにしてもわざわざ買いに来てくれた人のぶんがなかったのは申し訳無さの極みなので、次は余らせるぐらいに刷ります
  • とはいえ今回の100部も「絶対余る」と思った数なので難しい。。

その他反省

  • 技術書典のシステムへ情報を登録していなかったため、後払いシステム(QRでの購入)が電子版セットで利用できなかった
  • 同じく、一緒に出ていた多賀谷さんがそのへんのシステムを利用できなくて申し訳なかった(システムに価格を入力したのが前日)
  • 値段体系が複雑になってしまった
    • 「普及版」の存在が話をややこしくしてしまった
    • よくわからないとお客さんは帰る
    • 後半は説明の仕方がこなれてきた。言い方大事。
  • 売り子として未熟すぎた
    • ダウンロードコードの紙をチョキチョキ切る作業をやってたり
    • 「どうぞ見てってください」のひとことは大事
    • って家に帰ってから思った

その他の学び

  • 印刷所の入稿期限は想像以上にギリギリまで行ける(=ギリギリまで執筆できる)
    • 開催前週の火曜深夜に入稿したが、実際のところまだ1日ぐらい猶予があった

  • おつりは6万円ぶん用意していったが、使わなかった

  • テーブルを覆う布はあってもなくてもよかったかも
  • ブースを構える側の気持ちがわかったのは良い副産物だった

    • いままでこういうブース形式のところに近寄るのが苦手だった
    • 手にとって、買わなくても全然気にならない。手に取ってくれるだけでも嬉しい
    • というわけで完売後に他のブースまわるの楽しかった
  • 本としての佇まいをカッコよくすることは大事

    • 自分で会場を周ったときも、かっこいい本は遠目からでも気になった

今後の展望

  • これでMetal人口増えたら嬉しい
  • Kindle版も出したい
  • 2冊目3冊目も出したい
    • 技術書典5も通ったらvol.2出したい

おわりに

技術書典4、最高でした。運営のみなさま、参加者のみなさま、お買い上げいただいたみなさま、本当にありがとうございました!技術書典5ももちろん応募します!

Metal入門、電子版は4/24まで1500円のところ1200円で販売しております。

booth.pm


  1. 深夜〜朝まで働いて1.5万円ぐらいもらえる当時(20年前)としては破格の仕事だった

  2. こういう理由で、当初「製本直送.com」というシンプルな1部から印刷可能なサービスで刷ろうかなと思っていました。が、製本直送.comだと印刷原価が一冊1500円以上になることがわかって最終的に断念

#技術書典 4で「Metal入門」という本を販売します

iOS/macOSのGPUインターフェースMetalの入門書を書きました。その名も「Metal入門」です。4月22日に開催される技術書の同人誌イベント、技術書典4向けに書いたもので、初めて出版社を通さず個人で書いた書籍になります。ちなみに日本語としては初のMetal解説書1でもあります。

本の詳細

A5版(技術評論社の実践入門シリーズのサイズ)で全128ページ。一時150ページに達したのですがどうにかこうにか切り詰めてここまで収めたので、ギュッと詰まってます。小さくて(普通の技術書よりは)薄いので、カバンに入れておいても邪魔にならないサイズです。

f:id:shu223:20180418104931p:plain

ちなみに「GPUと戯れてみたいけどまずはほんのさわりでいい」という方のために、全48ページの「普及版」も用意しております。

GPUを操ることに興味のある方、GPUの気持ちをもっとわかりたい方はぜひ!

価格

無料サンプル

まえがき・目次・各章のさわりを試し読みできる無料サンプルをこちらからダウンロードしていただけます!

booth.pm

通常版

(製本仕様)128ページ/平綴じ/マットPP

  • 製本版のみ: 1200円(会場限定・・・の予定)
  • 電子版のみ: 1200円(4/25以降は1500円)
  • 電子版とのセット: 1500円(会場限定)

普及版

(製本仕様)48ページ/中綴じ/表紙なし

  • 製本版のみ: 500円(会場限定の予定)
  • 電子版のみ: 検討中
  • 電子版(通常版※)とのセット: 検討中
    • ※普及版の電子書籍版は作成しない予定です

目次

  • はじめに
  • 第1章 Metal の概要
    • 1.1 Metal とは
    • 1.2 Metal の用途
      • UIKitでもMetalが利用されている
      • 増え続けるMetal関連機能
    • 1.3 Metal を構成するフレームワーク群とシェーディング言語
  • 第2章 Metal の基礎
    • 2.1 Metalの「最初のハードル」
    • 2.2 「背景にある概念」を理解する
    • 2.3 Metalの基本クラスとプロトコル
      • MTLDevice
      • MTLCommandBuffer
      • MTLCommandQueue
      • MTLCommandEncoder
      • MTLBuffer,MTLTexture
    • 2.4 MetalKit
      • MTKView
      • MTKViewDelegate
      • MTKTextureLoader
  • 第3章 入門その1 - 画像を描画する
    • 3.1 描画処理のためのセットアップを行う
    • 3.2 画像をテクスチャとしてロードする
    • 3.3 描画処理を実行する
      • ドローアブル
      • MTKViewのcurrentDrawable
      • ブリットコマンドエンコーダ
      • 描画処理の全体
  • 第4章 入門その2 - シェーダを利用する
    • 4.1 Metalシェーダの基礎
      • シェーダとは
      • MetalShadingLanguage(MSL)
      • Metal シェーダファイルと Metal ライブラリファイル
      • MTLLibraryMTLFunction
    • 4.2 「画面全体を一色に塗る」シェーダの実装
      • 1.Metalシェーダファイルの作成
      • 2.頂点シェーダ関数の実装
      • 3.フラグメントシェーダ関数の実装
    • 4.3 CPUプログラム側の実装
      • シェーダにデータを渡すためのバッファを準備する
      • レンダリングパイプラインを用意する
      • MTLRenderPipelineStateの生成タイミング
      • レンダリングコマンドをエンコードする
  • 第5章 入門その3 - シェーダでテクスチャを描画する
    • 5.1 テクスチャを扱うシェーダの実装
    • 5.2 テクスチャ座標データをシェーダに渡す
    • 5.3 テクスチャをシェーダに渡す
    • 5.4 ピクセルフォーマットを合わせる- 第6章 Metal のハードウェア要件
    • 6.1 歴代iOSデバイスのMetalサポート状況
    • 6.2 MTLFeatureSet
    • 6.3 Metal非対応デバイスの判定- 第7章 GPGPU プログラミング入門
    • 7.1 コンピュートシェーダ - コンピュートシェーダとカーネル関数 - 頂点シェーダ+フラグメントシェーダとの違い.
    • 7.2 スレッドとスレッドグループ
      • スレッドグループのサイズと数を指定する
      • シェーダ側でスレッドの位置を取得する
    • 7.3 GPGPUの実装例 - パススルーコンピュートシェーダ - CPUプログラム側の実装
  • 第8章 MSL 入門
    • 8.1 Metal のグラフィックスレンダリングパイプライン
    • 8.2 .metalファイルと.metallibファイル - デフォルトライブラリ - コマンドラインユーティリティを用いた Metal ライブラリのビルド
      • 実行時にMSLをコンパイルする
    • 8.3 FunctionQualifier(関数修飾子)
    • 8.4 AttributeQualifier(属性修飾子)
      • [[position]]
      • [[vertex_id]]
      • [[stage_in]]
      • [[buffer(n)]]
      • [[texture(n)]]
      • [[sampler(n)]]
      • [[threadpositionin_grid]]
      • [[threadgrouppositioningrid]], [[ threadsper_threadgroup ]]
    • 8.5 Address Space Qualifiers(アドレス空間修飾子)
    • 8.6 型
      • テクスチャ型
      • サンプラー型
    • 8.7 GLSLをMSLに移植する
      • 型の違い
      • 戻り値の違い
      • プロトタイプ宣言
      • 作例
  • 第9章 Metal Performance Shaders
    • 9.1 デバイスがMPSをサポートしているかを確認する - 9.2 ガウシアンブラー - 9.3 MPSUnaryImageKernelを継承する他の画像処理カーネルクラス
    • 9.4 画像のリサイズ - 9.5 画像の転置(行と列の入れ替え)
  • 第10章 Metal の最適化
    • 10.1 スレッドグループサイズの最適化
      • スレッドグループサイズの決定方法 - 不均一スレッドグループサイズを利用して最適化する - 10.2 ArgumentBuffers
      • ArgumentBuffersの概要 - ArgumentBuffersの実装 - 第 11 章 Metal のデバッグ
    • 11.1 GPUフレームキャプチャを有効にする
    • 11.2 Xcode上でGPUの負荷を確認する
    • 11.3 GPUフレームデバッガ
      • シェーダにバインドされているリソースの一覧を見る
      • パイプラインの統計情報
      • パフォーマンス改善のヒント
      • issueを見る
      • シェーダの処理毎のパフォーマンス確認
    • 11.4 GPUフレームキャプチャをプログラムから制御
    • 11.5 InstrumentsのMetalSystemTrace
    • 11.6 ラベル
    • 11.7 デバッググループ
    • 11.8 GPUで利用中のメモリサイズを調べる
  • 第 12 章 Core Image × Metal
    • 12.1 Core ImageとMetalのシームレスな統合
    • 12.2 Core Imageのフィルタ結果をMetalで描画する
      • MetalによるCIImageのレンダリング
      • 実装例
    • 12.3 CoreImageのカスタムフィルタをMetalで書く
    • Metalでカーネルを書く利点
    • MSLによるカスタムカーネルの実装
    • Metalカスタムカーネルを利用するためのビルド設定
  • 第 13 章 SceneKit × Metal

    • 13.1 Metal で SceneKit のマテリアルを描画する
      • SCNProgramの実装
      • SCNProgramで利用するシェーダの実装
      • SCNProgramで用いるシェーダに値を渡す
    • 13.2 他のSceneKitとMetalの連携機能
      • SCNTechnique
      • shaderModifiers
      • SCNRenderer
  • 第14章 ARKit × Metal

    • 14.1 マテリアルをMetalで描画する
    • 14.2 MetalによるARKitのカスタムレンダリング
  • 第15章 Core Video × Metal
    • 15.1 Metalでリアルタイム動画処理
      • CVImageBufferからMTLTextureを作成する - CIContextを利用
      • CVImageBufferからMTLTextureを作成する - CVMetalTextureCacheを利用

余談:技術書典で本を出す理由

僕はフリーランスエンジニアでして、1日n万円の単価でお客さんに技術力を提供して生計を立てております。ありがたいことに今のところは食べていけております。

しかしお客さん仕事は基本的に「僕がプロとして間違いなく貢献できる領域」についてご依頼いただけるものなので、それだけでスケジュールを埋めてしまうと、

  • 仕事で必要なわけではないが単に興味がある
  • 今は素人だけど学んでいきたい

というような、「今は仕事になってない技術」を開拓していく時間が減ってしまいます。

なので理想的にはスケジュールをびっちり埋めずに、興味のある技術を勉強したり、それを発信したり、オープンソースで公開したり、という時間を確保したいという気持ちはあるのですが、まぁでもお客さんの仕事もおもしろいし勉強になるし、お金ももらえるし・・・ということでどうしても仕事で埋めてしまいがちです。

そこでよく思うのが、技術の勉強自体をマネタイズできれば、「既にスキルがあって、稼げる」フリーの仕事とバランスとってやれるようになるのになぁ、ということです。興味があってしかも稼げるなら、単純に天秤にかけるときに重みがかかる。

幸運にも、「興味のある技術を勉強しては発信してたらお仕事につながった」という例も過去にはあります(BLEとか)。しかしブログやOSSを見て仕事の依頼をいただける、というのは自分ではコントロールできないので(要は「たまたま」なので)その結果をあらかじめ織り込むことはできない。やっぱり技術情報発信そのものでマネタイズできたら理想的だなと。実際に可能かどうかは別にして、手段は探っていきたい。

その手段の候補のひとつが個人での書籍の出版です。出版社を通してちゃんとした技術書を出すのはめちゃめちゃ大変だしあまり収入にもなりませんが、個人で出すならもっとライトに出せるんじゃないの、と。ページ数が230ページ以上じゃないといけない、ということもないし、技術書っていうジャンルはプロによる超美しい装丁じゃなくてもいいと思うし、売れなそうな企画でも自分さえやりたければGOできる。

これである程度収益になるとしたら、

勉強→ブログ→ある程度たまったら本

というサイクルができたらいいな、と。そういうサイクルをまわしているうちにランダムイベントとしての「お仕事の依頼」もあるかもしれない。

で、そんなことを思い続けて案の定一文字も書くことなく4年程経ち、そこに技術書典という超素晴らしいイベントが出てきたので乗っからせていただいた次第です2。たとえあんまり売れなくても、今回技術書典ドリブンでついに個人で本を書き上げることができたというだけでも、自分的にはかなりの達成感があります。

まとめ

余談が長くなってしまいましたが、つまりいろいろ思うところがあり個人で本を書いて技術書典で販売することになりました!

Swinjectの多賀谷さんと共同ブースです(執筆はそれぞれ)。

techbookfest.org

4月22日、「き04」でお待ちしております!


  1. 章単位でいえば、PEAKS社刊行「iOS 11 Programming」のMetalの章(私が執筆)が初となります

  2. 技術書展が終わったら、noteで単発の技術チュートリアル記事販売とかも試してみたいと思ってます。