プログラマ英語学習日記

プログラミングと英語学習のまとめなど

WWDC 2017 Customized Loading in WKWebView まとめ

WKWebViewの新機能に関する話

動画はこちら

Customized Loading in WKWebView

  • WebContentsを表示するにはいくつかの方法がある

    • WKWebView
    • SFSafariController
  • SFSafariControllerを使えばボタン等をOS側で処理してくれて楽

    • ただし画面の一部に組み込みたいときには使えない
    • その場合は WKWebView
  • WKWebViewのプロセスはAppと分離される

    • セキュリティ上の理由など
    • それによる制約も強かった
  • 今回は開発者から要望の多かった機能を3つ実装した

    • Manage Cookies
    • Filter unwatned content
    • Provide custom resources

Manage Cookies

  • WKHttpCookieStore
    • クッキーを個別に追加/削除できる
    • すべてのクッキーにアクセス可能
    • クッキーの変更を監視可能
//GET
let cookieStore = webView.configuration.websiteDataStore.httpCookieStore;
cookieStore.getAllCookies() {(cookies) in 
}

//ADD
let cookie = HTTPCookie(properties: [
   HTTPCookiePropertyKey.domain: "canineschool.org", HTTPCookiePropertyKey.path: "/",
   HTTPCookiePropertyKey.secure: true,
   HTTPCookiePropertyKey.name: "LoginSessionID", HTTPCookiePropertyKey.value: "5bd9d8cabc46041579a311230539b8d1"])
cookieStore.setCookie(cookie!) {
    webView.load(loggedInURLRequest)
}

//remove
cookieStore.delete(cookie!) {
    webView.load(loggedOutURLRequest)
}

Filtering unwanted content

  • 見せたくないコンテンツをブロックできる

  • WKContentRuleList

    • SafariAdBlock系Extensionと同じ書き方
      • 読み込みのブロック
      • 見えなくする
      • SSLSSLに切り替える
  • JSONで記述、コード中でロードしWebViewに適用させる

//json-file in Bundle
[{
  "trigger": {
  "url-filter": ".*" },
  "action": {
  "type": "make-https"
} }]

//swift-code
//compile
let jsonString = loadJSONFromBundle()
WKContentRuleListStore.default().compileContentRuleList(
    forIdentifier: "ContentBlockingRules",
    encodedContentRuleList: jsonString
  ) { (contentRuleList, error) in
     if let error = error {
      return
     }
    createWebViewWithContentRuleList(ruleList!)
  }

//access rules
WKContentRuleListStore.default()
  .lookUpContentRuleList(
    forIdentifier: "ContentBlockingRules") { (contentRuleList, error) in
      //取得した情報を使う部分
   }

//apply
let configuration = WKWebViewConfiguration()
configuration.userContentController.add(contentRuleList)

Provide custom resources

  • リソース読み込みをアプリで処理できるようになる
  • ただしカスタムURLスキームに限る?)
  • 将来的にも問題ないようにカスタムスキームを設定するように

    • Bad : local
    • Good : [AppName]-local
  • WKURLSchemeHandler を使う

class MyCustomSchemeHandler : NSObject, WKURLSchemeHandler {
  func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask) {}
  func webView(_ webView: WKWebView, stop urlSchemeTask: WKURLSchemeTask) {}
}

let configuration = WKWebViewConfiguration()
configuration.setURLSchemeHandler(MyCustomSchemeHandler(), forURLScheme: “custom-scheme")
  • WKURLSchemeTask に結果を渡す
func webView(_ webView: WKWebView, start urlSchemeTask: WKURLSchemeTask) {
  //Bundleからデータを読み込み、対応するResponseクラスを作成し渡す
  let resourceData : Data = loadDataFromBundle()

  let response = URLResponse(
    url: urlSchemeTask.request.url,
    mimeType: "text/html",  //mimeType必須
    expectedContentLength : resourceData.count,
    textEncodingName : nil
  )

  urlSchemeTask.didReceive(response)
  urlSchemeTask.didReceive(resourceData)
  urlSchemeTask.didFinish()
}

Kotlin-AltJSの初心者向けTips&インプレ

AltJS-Kotlin

Android公式言語採用で一気にメジャーになったKotlinですが、実は JavaScriptに変換できます。いつからかは知りませんが、かなり当初からできたはず(1.0より前からあったはず)。

しかしマイナーすぎてあまり使っている人を見たことがありません。ネットの記事も少なめ。

軽くですが触った範囲でのTips&を記述していきます。

Tips

最初にコツ&ハマりポイントを。あまり出回ってないと思いますので

イベントハンドラ

DOMのクリックイベント等をWindowにイベントハンドラを追加したいことはよくあると思います。こんなやつです

<script>
function onLinkClick(param) {
}
</script>
<a href="javascript:onLinkClick('PARAMETER')">LINK</a>

で、このonLinkClick相当をKotlinにしたい。コイツが地味にkotlinだと面倒、というより最初やり方がわかりませんでした。結論からいうと asDynamic() を使います

fun main(args: Array<String>) {
   window.asDynamic().onLinkClick = {param : String -> 
      //do anything
   }
}

しかもmain関数内。最初はグローバル関数として記述すると思ってました。

jsの感覚で window.onLinkClick = と書くとビルド通らないので注意してください。

console.log

コイツも地味にはまりました。 print ですね。

一番ハマったのが、 改行がくるまでログに出力されない ことです。

このため、 print 単体では出力されません。そのあとの println で一気に出力されます。

これ結構ハマったので注意してください。

もいっこ、Object詳細をログに出してくれません。以下のようなパラメータをjs/kotlinに渡してログ出力させると…?

<a href="javascript:onLinkClick({text: 'I love Kotlin!'})">LINK</a>

f:id:nexus1:20170803230930p:plain

上がconsole.log、下がkotlinのprintlnです。見ての通り、オブジェクトの詳細がわかりません

なので kotlinでも console.log を使ったほうがいい です。printを使うメリットが分かりませんでした。記法が少し短い程度でしょうか? 基本はconsole.logがいいと思います。

js連携

jsのコードと連携する場合、external で定義ファイルを記述します。Typescriptでいう、 .d.ts ですね。

ためしに以下のようなjs用をKotlinで使おうとすると…?

//myMathをモジュール的に使ってるイメージ
myMath = {}
//モジュール内関数
myMath.sin = function(degree) {
    return Math.sin(degree * Math.PI / 180.0)
}
//クラス
myMath.Point = function(x, y) {
    self = this;
    this.x = x;
    this.y = y;
    this.abs = function() {
        return Math.sqrt(self.x * self.x + self.y * self.y);
    }
}

Kotlin用の定義ファイルはこうなります。

//モジュールはclassで包む。もっといい方法あるかも
external class myMath {
    //myMath.sinはstatic関数相当なので、companion objectで記述
    companion object {
        fun sin(degree: Double) : Double
    }
    //内部クラスはそのまま。ただしコンストラクタは constructor に限定される模様
    class Point {
        constructor(x: Double, y: Double)
        fun abs() : Double
    }
}
//ためしに使う
fun main() {
   println(myMath.sin(90.0)) //1
   val p = myMath.Point(1.0, 2.0)
   println(p.abs());  //2.23...
}

こんな感じ。正直自分も把握しきってないです(触り始めたばかりですし)

適当に書いたあと出力JSファイルをみて、それが意図した結果になってるか確認してることが多いです。.d.ts があるならそれベースでコンバートするのが楽でしょう。

小数はDoubleで

個人的にですが、小数は全部Doubleで扱ったほうが楽でした。Floatだといちいち末尾に f が必要でちょっと面倒。またKotlinはこの辺結構厳格なので、Double/Floatが入り乱れるとキャストが面倒です。

その上、jsにコンバートされたら全部number です。Java変換なら意味ありますがjs変換ではDouble/Floatを使い分けるメリットがありません。そんなわけで記法が楽な Doubleに統一がベスト と判断しました。


IntとLongについてもほぼ同等です…が、こっちは気分で分けています。

ユーザIDなど64bitなものは明示的にLongにして、それ以外はIntとしてます。 普段からLongは逆に面倒 なので。

またLongはID程度でしか使わないので加減算などをしないんですよね。よってLongだから扱いが面倒ということはないです(時刻はDoubleで扱う)

こちらもコンバートされた結果はIntだろうがLongだろうが一緒のはず。

ここは地味にKotlinToJSで使いづらい部分ですね。Typescriptと違って整数と小数を分けれるのはいいと思うのですが、それ以上は過剰機能という印象です。

一致しないオブジェクトを渡したとき

たとえば次のような定義ファイルを記述したとします。

external class User {
   var id : Long
   var name : String
}

idとnameがあるユーザ、よくあるパターンですね。これに {name: "NAME"} というオブジェクト、idが不足してるオブジェクトを渡した場合…?

//htmlが、href='javascript:onClick({name: "NAME"})'

window.asDynamyc().onClick(user : User) {
   println(user.id) //undefined
   println(user.name) //NAME
}

と、undefinedになります。当然といえば当然ですが。このへんは型に厳しいKotlinといえど注意する必要があるので気をつけましょう。

定義ファイルを全部をNullableとして定義するのも手ですが、コードがかなりめんどくさくなるのがネックです。 このへんはバランスをみて、になるかと思います。

インプレッション

まだ軽く触り始めたばかりですが、インプレッションを

Cons

  • 型があるのはやはりいい。TypeScriptより強力!
  • 言語仕様はTypeScriptより組みやすい気がする
  • IntelliJが賢い。VSCodeより補完がきく印象
  • IntelliJが一つのJSファイルとして出力してくれるのでwebpack等を記述する必要がない。楽。
    • どうもmain関数がある部分をエントリーポイントとして出力する模様

Pros

  • HTML/JSとの連携が難しい。ここはTypeScriptに軍配があがる
  • 型定義ファイルがほとんどない。自作する覚悟が必要。
  • 標準クラスの設計がJava基準で、JSの標準APIとは相性が悪い
  • 規模が大きい場合フレームワークをどうするか。
    • React/Angular/Vueのどれとも相性悪そう
    • フルスクラッチでいく覚悟が必要かもしれない

個人的印象ですが、 Kotlin内で閉じれるならすごくいい。閉じれない(外部JSと連携必須)ならTypeScript ということろでしょうか。

もちろんKotlin用外部ライブラリも少ないです。Androidのものなんて当然使いまわせません。

それに加え規模が大きくなるとReactやAngularは使いたくなるでしょう…ここは鬼門ですね。 小規模なプロジェクト以外は導入が難しそうなのに、小規模なプロジェクトで厳しい型チェックをするメリットは薄いというジレンマ。主力SPA系フレームワークとの相性悪そうなのは痛い。かろうじてAngular?

ちょっと面白いので趣味開発はありですが、業務採用となるとかなりハードルたかそうです。

こんな記事を書いておいてなんですが、個人的にはKotlinToJSよりELMのほうが面白いと思いますw

【書評】Androidを支える技術(I)

Androidを支える技術(I)

なにかと話題の書籍、「Androidを支える技術(I)」をようやく読み終えたのでちょっとしたレビューでも。

結論からいいますと、 ここ数年読んだAndroid書籍では最高に面白かったです

TL;DR

  • Androidアプリを作るための本ではない
  • 同様により良いアプリの作り方を示す本でもない
  • 内部構造がメイン
  • かなり深いところまで追っており読み応えバツグン
  • 面白かったが、仕事の役に立つかというと…?

レビュー

冒頭にも書いたとおりAndroidの内部構造、一巻は特にイベントと描画周りに焦点をしばった内容です。これがディープなことディープなこと。

「はじめに」からすでにおかしくて、

「執筆中にNougatが出て、バイトコード実行環境が変わり加筆修正を余儀なくされた」

とか他の本ではありえないことが書かれています。この時点でいい意味でやばい本を買ったという予感と期待に胸が膨らみました。

(逆に上記文言で特になにも感じないなら、おそらくこの本は合いません)


第1章はAndroid全体の概要。Androidアプリを開発したこたがない組み込み系エンジニア向けの解説というところです。ここからすでに「何らのGUIシステムでコーディングしたことがある人」前提で話が進んでいきます。UIスレッドとか当然のように出てきます。

そして2章でいきなりLinuxカーネルLinuxカーネルからのイベントを受け取り、Windowに配布する部分の解説ですからんね…カーネル周りの知識はほとんどないですがざっくり概要ぐらいは掴めたのと同時に「こんな仕組みだったのか!」という驚きを感じました。

ここはAndroidエンジニア以外の人の方が楽しいかもしれませんね。少なくともLinuxの知識があったほうが楽しめるのではないかと思います。

中盤がレイアウトや描画周りの解説。ここは(OpenGLを除き)Javaコード部分の解説が主体です。Viewツリーの構築と描画・イベント処理ですね。

ここについてはそこそこコードを追ったことがあるので、把握してた知識で問題なかったことを確認したぐらいです。dispatchTouch/dispatchDraw/onMeasureなどですね。このレベルぐらいなら自分のように、内部構造をちょっと覗いたことがあるという人は多いのではないでしょうか?

もちろん自分が追ってた範囲よりは深い部分の解説があって面白かったですが、アプリを作るぶんに困らない範囲の知識はもっていたのでそこまで新しい知識というわけでもなかったです。

逆に上記のメソッドをみてピンとこないなら 今すぐ注文していいと思います。より自作Viewが作りやすくなるはずです。


そして一番面白かったのが最終章「バイトコード実行環境」

なぜdexがレジスタ型なのかという考察、いかにネイティブコンパイルするか、さらにそれをAndroidバージョンごとにどのように変わっていったか、およびその理由の考察…

本当の読んでて興奮しました。個人的にこの本で一番面白かったのがこの章です。

バイスの進化、ユーザ要求の変化、それらに合わせてAndroidはどのように変わるべきか…著者の異常とも言えるAndroid愛を感じる章です。

本当にここまでAndroidの構造を把握してる人は日本にそうそういないのではないでしょうか?Androidの複雑で膨大な内部構造のコードを読み・理解しているあたり、著者の力量の高さも伺えます。


当然ですが、内容はそこらのAndroidの書籍より数段ハード です。理解が追いつかず、同じ場所を何度も読み返したので読破にも時間がかかりました(それでも理解しきれてない部分が多い…)

でも面白いんですよねぇ。自分の知らない層の話なので本当に面白い。

個人的にですが以下のような人におすすめです。

  • Android開発を年単位で経験している
  • よくライブラリや自作Viewを作る
  • よくView周りのソースを読む

まずある程度Androidの知識がないと厳しいです。昨日今日触り始めました、ではおそらく難しい。

「昨日今日の人」が読むのを止めはしませんが、この本より先に読むべき本はあると思います。本を読む代わりにとにかくコードを書くでもいいです。

逆にある程度なれた人にとってはより最適化されたアプリを作る手助けになるでしょう。


もっともこの本が最適化手法を提示してくれるわけではありません。

ただし、よりAndroidのソースを追いやすくなるのは間違いないです

その結果、より高速で効率のいいアプリを作れるようになる可能性はあると思います。

どの程度直接的に役立つのかはまだ自分もよくわかりません。

しかしいい刺激になるのは間違いないので、「今読む本がない / 次何を読もう?」というAndroidエンジニアには強くオススメしたい本でした。二巻も買います!


ちなみに所々でコラムがあるのですが、Android3.x系ひどい と何度も書いてて笑えましたw 確かに当時のタブレットはいい出来とは言い難かったですからね…

WWDC Cocoa Development Tips まとめ

Cocoa Development Tips

  • 「あなたが知らないCocoaの29のこと」という面白そうな副題がついてたので見てみました。

  • Internationalization

    • Xcode Scheme Editor > Options から言語を設定できる
  • UserDefaults

    • 4つの階層がある
      • Argument Domain / Application Domain / Global Domain / Registration Domain
      • 読み込み時は、先頭から順に問い合わせる
      • 書き込み時はそれぞれの階層で手法が異なる
        • Registration Domain > UserDefaults.standard.register([“key” : value])
        • ↑これは永続化されない
        • Global Domain > defaults write GlobalDomain key -bool value
        • コマンドラインから叩く。コードではない
        • Application Domain > UserDefaults.standard.set(key, value)
        • ↑よく使うもの
        • Argument Domain > 起動時に設定。書き込みはない
  • 役立つUserDefaults

    • -NSViewLayoutFeedbackLoopDebug YES
      • 繰り返し発生しているレイアウトを追跡しやすくなる
    • -NSApplicationCrashOnException YES
      • クラッシュ時の例外をキャッチして表示してくれる
  • UserDefaults + KVO

    • UserDefaultsはKVOで監視できる
//新機能
extension UserDefaulst {
  @objc dynamic var showAtLaunch : Bool {
    return self.value(forKey: "showAtLaunch") as? Bool ?? false
  }
}

let observation = UserDefaults.standard.observe(\.showAtLaunch) { observed, change in 
  //処理
}

  • Base64

    • Data(base64Encoding:) で楽にBase64文字列を変換できる
    • data.base64EncodedStringBase64文字列化可能
  • Asset Catalogs

    • 画像でも LayoutDirection がサポートされている(去年から?)
      • LayoutDirectionごとに画像を出し分けることができる
    • 色域(Gamut)もサポート。sRGBとP3で画像の出し分けが可能
    • iOS11(HighSierra)からは Color もサポート
      • Androidの colors.xml のイメージ
      • 定義した色を Xib で使える他、 UIColor.init?(named:) で取得可能
  • Unit Test

    • XcodeのNewFileからUnitTestを選択することでテンプレートが生成される
  • NSBox

    • NSBoxを拡張して角丸にする
    • boxTypeをseparatorにして区切り線にするなど
    • Mac専用APIなので正直よくわからず…
  • Restorable State

    • こちらもMacOS専用
    • NSResponderに状態を保持させ、再起動で復元させる?
    • restorableStateKeyPathsプロパティを使えば楽になる?
  • CoreData

    • ※もはやRealmの時代だとは思いますが
    • NSPersistentContainerを使えば厄介なCoreData初期化で楽できる
    • 配列を保存したいときは、RelationShip で ToMany を選択する
      • 必要に応じorderedにもチェックをいれる
    • Modelの新バージョンを作り、あとはCoreData側がマイグレーションしてくれる
    • エラーは適切にハンドリングする
      • 特に追加時
  • NSError

    • NSErrorには各種メッセージがあるので、適切にユーザに表示する
    • 自分でエラーを発生させるときも適切にメッセージを設定するように
    • CocoaErrorを使えば楽できる(新規)
    • 専用のドメインを作えばハンドルしやすくなる?
  • SharedKeySets

    • 頻出するキーを渡しておけば高速化できる
    • NSDictionary.sharedKeySet と NSMutableDicitonary.init(sharedKeySet:) をペアで使う
let set = NSDictionary.sharedKeySet(forKeys:["id", "name"])
let dict = NSMutableDictionary(sharedKeySet: set)
dict["id"] = ...
dict["name"] = ...
  • Accessibility

    • VoiceOverはIBから簡単に設定できる
    • Appを1024x640でテストしておく
    • DeveloperToolのAccessibilityInspectorをうまく使う
  • Documents

    • ユーザに保存させる文章にNSDocumentを使えばVersionControlなどいいようにやってくれる?
    • CocoaAPIは使ったことがないのでイマイチつかめず
  • Reporting Exceptions

    • NSApplication reportExceptionをオーバーライドすれば例外をロギングしやすくなる
  • Debugging

    • XcodeのDebug領域をうまく使う
    • View hierarchy, Debug memory graph, simulate location
  • Sound

    • NSButtonにSoundを設定できる

※セッション一覧ページにはiOS/Mac/watchOS/tvOSとあったのですが、思ったよりMacでした。CocoaTipsなので当然かもしれませんが。

【書評】関数プログラミング実践入門

関数プログラミング実践入門

1年ちょっと前に買ったものですが、読み直しのついでにレビューをしてみます。

※残念ながら改訂版ではありません..もちろん個人の主観に基づくレビューですので、そこはご了承ください。

購入動機

  • SwiftやScala/Java8等、関数型の側面が入った言語を触ることが多くなった
  • それに伴い、関数型の知識が欲しかった。より関数型らしく組むには が欲しかった知識
  • 特定の関数型言語の知識は求めていない
  • 上記理由により、汎用的に関数型の知識を扱っていそうなこの本を選定

TL;DR

  • 全体的に

    • 思ったよりHaskell入門本だがやや内容が少ない
    • 6章の数独が見どころ
  • 長所

    • Haskell含む、関数型言語に本格的に興味を持てた (後のelmに繋がる)
    • 6章の「関数型らしい設計方法」は非常にためになった
  • 短所

    • 他言語との実装比較が冗長に感じられた(1章)
    • 各種サンプルが、関数型であるメリットをもっと感じる内容だとより良かった

レビュー

思ったよりHaskellの本だった、というのが正直なところです。タイトルから期待する内容と実際の内容にかなりのズレがあります(個人的印象ですけど)。タイトルが「Haskell実践入門」だったらまた違ったでしょう。(買わなかった可能性は大きいですが)

タイトルに沿った内容らしいのは、

  • 「型付け」などプログラミング全般の話題に触れる0章
  • 関数型らしい設計手法を提示した6章

ぐらいでしょうか?


とはいえ実はこの0章が最も関数型のメリットを解説しているのではないかと思います。

関数型言語の特徴、参照透過性と並行処理の相性の良さ、テストのしやすさなどなど関数型のメリットが分かりやすく提示されています。(関数型言語の、というよりHaskellの、というべきかも)

また、6章も「数独」という入門本にしてはかなり複雑な題材を取りあつかい、どのように設計していくかが述べられておりここも面白かったです。

この手の入門書としては相当高難度な題材といえるのではないでしょうか?しかももろに「アルゴリズム」の話で関数型とも相性がいいです。DBやHTTPが不要な分、完全にロジックに集中できます。

ここは本当に見ごたえがありました。ソースコードも豊富で当時は初Haskellでしたがなんとかなりました。


一方で 残りの章は普通のHaskell解説本です

特に1章が厳しい。いくつかのよくある実装パターンでHaskellとの比較を行っているのですが、「相手言語の苦手な分野と比較している」のは残念な印象でした。

たとえば

  • Rubyには型がないから型のあるHaskellいいでしょ?

    • 前のページで取り扱ってたJavaには型あるんだけど…
  • CだとTuple返せないから複数の値を返したい時不便でしょ?

    • 次のページのJSならできるのでは…? いや、Cこそやりたい放題な気が。

というイメージ。Haskellの優位性をよく分からない理由付けで解説してるだけに感じてしまいました。


残りの章は主にHaskellの文法解説+αです。

Haskell入門書としては「FunctorやApplicativeなど厄介な部分を除き」良書という印象。

翻訳書籍でないので変な日本語に悩まされることもありませんし、各単語の説明もきっちりしてて読みやすいです。

問題はそのへんのつまづきやすい部分をさらっと流してること

この説明だけでIOモナドを扱える人はほぼいないのではなかろうか…? その辺は他の書籍で補完しないと厳しいです。

数独のためのHaskell解説」と割り切ってる感はあります。確かに数独ではモナドを意識して使わないので数独部分を理解するには十分ではありました。(List/Maybeは他言語にもあり、モナドと意識せず扱えるから困らない)


ちょっと厳しい感想になってしまいましたが、読んでよかったと思ったのは間違いありません。これを読んでいなかったらelmは理解できなかった、触ろうとも思わなかったはずです。

ただしHaskell入門としては明らかに足りていません。特にモナド周り。ここは注意してください。

しかし6章の数独はそれをひっくり返すぐらい面白かった。先ほども述べたとおり、1-5章はこのための準備と割り切る必要はあると思います。

こういう内容が豊富でもっと具体的な設計手法を見れたら/試せたらより良かったと思います。


この数独を経験することでかなりオブジェクト指向での組み方が変わりました。具体的には「参照透過性のあるメソッド」「ないメソッド」を分離したくなる癖が付きました。(参照透過性のあるstaticメソッドがすごい増えた)

Androidでの簡単な例だと以下のようなイメージです。

//before
if (state.isActive && !state.isLoading) {
  button.setText("送信");
} else {
  button.setText("通信中");
}


//after
static String getButtonText(State state) {
    return (state.isActive && !state.isLoading) ? "送信" : "通信中";
}
String text = getButtonText(state); //なぜか一旦変数に格納したくなる症状も出た
button.setText(text);

とにかくsetTextは1箇所に限定したくなる癖がつきました

コードとしては若干面倒になってますが、副作用を伴う操作は極力減らしたい思考に変わりました。


冗長な面やHaskell入門として足りない面はありますが、このような考え方をかえるきっかけとなった本なので良かったです。

特にelmにつながったのは大きい。(少なくともこの本で得た知識おかげである程度なんとかなったのは事実)

ターゲットとする読者層は狭い気はしますけど、上記のような考えかたに触れてみたいのでしたらオススメです。

逆に本格的にHaskell(含む他の関数型言語)に触れたい、商用投入したい/してるプロダクトに触れる必要がある、というのでしたらその言語に特化した他の本を購入されたほうがいいかと思います。

WWDC2017 Introducing ARKit まとめ

iOS11の大きなウリの一つ、ARKitのセッションです。

動画はこちら

Introducing ARKit

ARとは?

  • Virtualな物体をPhysicalな世界に配置すること
  • ポケモンGOを始めとするデモ

ARKit

  • 概要

    • Mobile用AR Platform
    • High level API
    • A9以降搭載のiOSバイスに対応(6s以降)
  • 3つの機能がある

    • Tracking
      • 実世界の座標追跡
      • カメラ画像からの移動変化量
      • 特殊なセットアップ不要
    • Scene Understanding
    • Rendering
      • 楽に組み込める
      • ARViewを使えば、SceneKit/SpriteKitから作成できる(?)
      • Metalによるカスタムレンダリング
  • Unityに組み込まれている

API概要

  • ProcessingとRenderingに分かれる

    • RenderingがMetalやSceneKit
    • ProcessingがAR-Kit
      • 内部でAVFoundationやCoreMotionを用いる
  • ARKit

    • Session-based API
    • ARSessionConfiguration → ARSession と作成する
    • ARSession が ARFrame を生成
    • ARFrame からレンダリングに必要な情報を取得する
  • ARSessionConfiguration

    • ラッキングしたい情報を設定する
    • ARSessionConfigurationは回転情報のみ
    • Subclassに ARWorldTrackingSessionConfiguration
      • 座標移動も検出可能
    • バイスによるサポート情報も取得できるので切り分ける
if ARWorldTrackingSessionConfiguration.isSupported {
  configuration = ARWorldTrackingSessionConfiguration()
} else {
  configuration = ARSessionConfiguration()
}

** ARSession * ARプロセスの管理 * run / pause など * ARSessionDelegate で更新された ARFrame を受け取る

** ARFrame * ARレンダリングに必要な情報がある * カメラ画像 * トラッキング情報 * Scene情報 * ARAnchor * 実世界のポジション * ARSessionに追加/削除する

Traking

  • ARKitは座標だけでなく、「実世界の距離」も計測する

    • セッション開始時点からの位置になる
    • カメラから複数の特徴点を抽出、その位置差分で位置を出す
      • 加速度センサも活用している
  • サンプルコード

let session = ARSession()
session.delegate = self

let configuration = ARWorldTrackingSessionConfiguration()
sesison.run(configuration)
  • ARCamera
    • 仮想的なカメラ情報を提供するオブジェクト
    • Transform/TrackingState/Camera intrinsics

Demo

  • デモで気になったクラスをピックアップ

    • ARSCNView : ARシーンレンダリングの便利クラス
      • SceneKitをはめることができる
      • arScnView.scene = scnView
    • UITapGestureでタップ判定可能
  • タップでスクリーンキャプチャを画像としてSceneに追加するサンプル

func handleTap(...) {
    guard let currentFrame = sceneView.session.currentFrame else {
        return
    }
    //キャプチャした平面Object作成
    let imagePlane = SCNPlane(width: sceneView.bounds.width / 6000,
              height: sceneView.bounds.height / 6000)
    imgPlane.firstMaterial?.diffuse.contents = sceneView.snapshot()
    imgPlane.firstMaterial?.lightingModel = .constant

    //シーンに追加
    let planeNode = SCNNode(geometry: imagePlane)
    sceneView.scene.rootNode.addChildNode(planeNode)

    //カメラの10cm先に配置
    var translation = matrix_identity_float4x4
    translation.column.3.z = -0.1
    planeNode.simdTransform = matrix_multiply(currentFrame.camera.transform, translation)
}

Trackingの品質

  • カメラの環境に大きく左右される

    • カメラに何もうつっていないとトラッキングが中止
    • 「品質のいい」適度に複雑なカメラ画像が必要
    • 動きのあるシーンに弱い
      • 子供が動き回るなど
  • Trackingの状態

    • Not available : 開始直後はコレ。トラッキングの情報が不足してる
    • Normal : トラッキング可能
    • Limited : 真っ白壁など、一部情報がうまく取れない
    • Delegateで検出可能
func session(.., cameraDidChangeTrackingState camera: ARCamera) {
    if case .limited(let reason) = camera.trackingState {
        ...
    }
}
  • Trackingの中断
    • アプリがBackgroundに移動、iPadのMulti-Taskingで中断する
    • Delegateで検出し、表示を切り替える
func sessionWasInterrupted(...) {
    showOverlay()
}
func sessionInterruptionEnded(...) {
    hideOverlay()
    //必要に応じAR処理を再開
}

Scene Understanding

  • バーチャルな物体を配置するときに必要な情報のこと

    • 平面検出
    • ヒットテスト
    • 光量推定
  • 平面検出

    • テーブルのような水平面を検出できる
    • 複数のフレーム情報から計算する
    • 広さを測定できる
    • 面をマージできる
    • ARWorldTrackingSessionConfigurationの planeDetection を使う
      • 現状、 .horizontal しか値がない模様
  • ARPlaneAnchor

    • 平面が検出されるとDelegateが呼び出される?
    • 3軸回転、中央、広さを保持している
  • HitTest

    • カメラからのみた特定の直線が実世界平面のどこに位置するかを調べる
    • 4つの種類がある
      • Existing Plane Using Extent: 平面の位置/広さを考慮したヒット位置
      • Existing Plane : 平面の広さが無限とした場合のヒット位置
      • Estimated Plane : まだ平面が検出されてないときに使う
      • Feature Point : 面が小さいときに用いる(イレギュラーなケースらしい)
    • 画面の座標ベースでテスト可能(0,0が左上、1,1が右下)
  • 光量推定

  • 画像ベースの光量推定
  • 1000ルーメンがデフォルト
  • Configurationで設定する

構成

  • SceneKit/SpriteKit/Metalのいずれか

  • SceneKit

    • ARSCNView
    • カメラ画像を表示する
    • SCNCameraが人にあわせ移動する
    • 自動でLightingされる
    • SCNNode が ARAncher にマップされる
  • SpriteKit

    • ARSKView
    • SKNode が ARAnchor にマップされる
    • SpriteKitは2D描画用
    • 物理計算にテーブルを使うなどが出来る(?)
  • Metal


SceneKitで配置するだけで動くのは良さそうですね。3Dはあまり経験がないので厳しいと思っていたのですが、結構簡単にできそうでちょっと興味わいてきました