その後のその後

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

try! Swift Tokyo 2019の復習 #tryswiftconf

平日に戻ったら復習の機会もなくなるのでできるだけスライドとかで復習していきます。ここに書いた以外にも素晴らしいセッションがたくさんありましたが、会場で理解が追いつかなかったもの、あまりちゃんと聞けなかったものを中心に。

Keypath入門 - Introduction to Swift Keypaths

speakerdeck.com

SwiftのKeyPathを使って型の抽象化を行う話。例として、以下のような全然違う要素を持つStructを抽象化するのはProtocolだとできないけど、KeyPathを使えばできるよ、と。

f:id:shu223:20190325060340p:plain

こういうStructがあるとして、

struct User {
    var username: String
}

var player = User(username: "マリオ")

で、Swiftのキーパスではこんな感じでUserの持つusernameにアクセスできる。

player[keyPath: \User.username] = "リンク"

(この書式、全く知らなかった)

こんな感じで変数に格納することもできる。

let nameKeyPath = \User.username
player[keyPath: nameKeyPath] = "ルイージ"

で、キーパスには色々種類があるという話と、

  • KeyPath<Root, Value>
  • WritableKeyPath<Root, Value>
  • ReferenceWritableKeyPath<Root, Value>
  • PartialKeyPath
  • AnyKeyPath

合成できるという話があって、

f:id:shu223:20190325060633p:plain

これらの性質を使って、冒頭に挙げたSettingsを表す要素の全然違うStructをどう抽象化するか、という具体例について説明される。

あとKeyPathはHashableなのでDictionaryのキーにも使えますよ、とか。

Swiftにおける音の成形

スライドはGitHubにある。

github.com

このセッションは音の基礎から始まって、で、iOSではどうやるの?というところでCore Audioは難しいからAudioKit使おう、という流れ。

音声処理は興味がある。が、あんまり「音楽」の文脈では興味がなくて、どっちかというと「現実世界をセンシングする」という文脈で興味がある。このセッションは音楽寄りの話なのでそういう意味では僕の興味にストライクではなかったが、AudioKitがディレイやリバーブのような基本的な処理だけじゃなくてAKMoogLadderみたいなかなりアプリケーションよりのところまで含んでいるというのは知見だった。

試しにAudioKitのプロジェクトでvoiceでクラス群をフィルタしてみると、SamplerVoiceとかSynthVoiceとか興味深いクラスがあるし、MIDI系の実装も充実している。あとAudioKitのリポジトリはAudioKitというアカウントにあって、

github.com

サンプルとか、音をパーティクルでビジュアライズするプロジェクトもある。

Metalを使用。あとでちゃんとコードを読んでみようと思う。

f:id:shu223:20190325061041p:plain

(iPhoneでも普通に動いた)

PixarのようなグラフィックをSwiftで実現する

レイトレーシングの話。iOS 12でMetalに追加されたレイトレーシングの話かと思いきや、

developer.apple.com

シンプルなレイトレーサーをSwiftで自前実装する、という話だった。めちゃくちゃいい。自分で実装するというのは理解度が全然違う。

コードはGitHubにある。

github.com

"Pete Shirley"という人の書籍を参考に書いたコードらしい。

Much of the code is derived from Pete Shirley's Ray Tracing Minibooks, with a few added extras.

で、このリポジトリ、Swiftのファイルが2つとビルドスクリプトしか入ってない。Xcodeプロジェクトはなく、コマンドからビルドして各フレームの画像をppmで出力し、それらをffmpegに食わせてmp4として吐き出すというもののようだ。

中身を見ると、Metalを使ってないどころか、SceneKitもCore ImageもCore Graphicsも使ってない。Foundationだけ。

(ということをツイートしたら、レイトレーシングという(iOS界隈では)ニッチな話題にもかかわらず結構な反響があった)

次のようなアルゴリズムらしい。

  • Line sphere intersection
  • Path tracing
  • Lamberian scattering
  • Fresnel equations
  • Schlick approximation
  • Perlin noise
  • Rodrigues rotation formula

これはたぶんコードを読むだけでは何やってるかわからないやつだ。。まぁそれぞれのアルゴリズムの日本語名を調べて、何をやってるのか(中身の詳細はブラックボックスでもいいので)理解する、ぐらいのことはやりたい。

SwiftSyntax で便利を実現する基礎

SwiftSyntaxというSwiftの公式ライブラリがあって、それを使って構文解析したり、コードの一部を書き換えたり(修正コードを生成)する方法の紹介。

speakerdeck.com

めっちゃわかりやすいけど、会場ではボーッとしてて(スライドが日本語だったのでそこが気になって回想モードに入ってしまった)、『SwiftSyntaxは公式ライブラリである』というところを聞き逃してしまい、構文解析がどのように行われるかを解説しているのか、Swiftのコンパイラ実装の一部を取り出してツールとして利用するような話をしているのか、みたいに理解の迷子になってしまった。プレゼンを聞くときは冒頭は絶対に聞き逃してはいけない。。

Swift type metadata

SwiftのType metadataを通じてSwiftのランタイムを理解しよう、というセッション。

speakerdeck.com

let metatype: Int.Type = Int.self

このInt.Typeは「メタタイプ(Metatype)」と呼ばれる型で、クラスや構造体、列挙型、プロトコル等、型を表すための型{型名}.selfでこのメタタイプを型自体から取得できる。

で、このセッションの主役である"type metadata"は、Swiftランタイムにおいて型の情報を表すデータで、Metatypeはtype metadataへのポインタである、とのこと。

これを用いて、Method swizzlingも可能になる。

class Animal {
    func bar() { print("bar") }
    func foo() { print("foo") }
}

struct ClassMetadata {
    ...
    // VTable
    var barRef: FunctionRef
    var fooRef: FunctionRef
}

let metadata = unsafeBitCast(
    Animal.self, to: UnsafeMutablePointer<ClassMetadata>.self
)
let bar = withUnsafeMutablePointer(to: &metadata.pointee.barRef) { $0 }
let foo = withUnsafeMutablePointer(to: &metadata.pointee.fooRef) { $0 }
bar.pointee = foo.pointee
let animal = Animal()
animal.bar() // foo

これを利用したOSSがStubKitで、

github.com

Decodableに準拠している任意のクラスや構造体を1行で初期化できるようにしてくれる、というものらしい。

import StubKit

// Codable struct
struct User: Codable {
  let id: Int
  let name: String
  let sex: Sex
}

let stubUser = try Stub.make(User.self)
// User(id: 1234, name: "This is Stub String", sex: .female)

便利そう。

通常の開発ではあまり見ることのない言語の「内部」を読み解き、さらに実用面での使いどころを考え、OSSにまで落とし込む(しかもかなり普遍性がある)という一連の所業は見習いたい。

ポートレートモードを自作しよう / Making Portrait mode yourself

深度情報が付属しない2D画像、しかも現実世界の写真だけじゃなくてたとえばマンガやアニメの画像も背景をぼかしたりしたい、という試み。

speakerdeck.com

深度推定の機械学習モデル使うのかなと思ったが、違った。それだと期待した性能が出ない(機械学習なので、学習させたシーンの背景しか分離できない)ので、"GrabCut"なるアルゴリズムを採用したとのこと。

LTなのでアルゴリズムの詳細説明はなくサッと次のスライドに行ってしまったので、帰ってググってみたところ、2004年の論文で提案されたアルゴリズムのようだった。

try! Swiftの当該セッションの実装コードを見ると、OpenCVにそのものズバリな関数が用意されていて、それを使っているようだった。

cv::grabCut(sourceMat, maskMat, rectangle, bgModel, fgModel, iterationCount, cv::GC_INIT_WITH_RECT);

これはいいことを知った。デュアルカメラやTrueDepthがハイエンドiPhoneに限定されたスペックである以上、iOSが13になろうと14になろうとデプス取得手段がないデバイスで撮影された画像への対処は必要で、こういう手段もある、というのは知れてよかった。

OpenCVの公式サイトに色々とドキュメントがある。

docs.opencv.org

画像処理界隈ではよく知られたアルゴリズムのようで、"grabcut opencv"でググると日本語情報もたくさん出てくる。

つづく

復習したいセッションのリストはまだまだあるので、つづきます。