
Outline
動機
flatMap
の振る舞いって難しいですよね…。特にArray
のflatMap
。
どのような値を返すのか突き止めるべく、あらゆるパターンの呼び出しを試して個別にその挙動を確認し、
自分独自の概念もこねくり回しながら、どうにか法則を抽出しようと試みる。
だけど、どうしてもその挙動を言葉に落とし込むことができなくて、涙で枕をぬらす夜が続きました。
ところがある日、そのソースコードを読んでみると、
あれだけ内部で複雑な動きをしているかに見えたflatMap
の振る舞いが、
(Array
の場合は)2つのオーバーロードと、意外なほど簡潔なコードだけで実現されていたことがわかったのです。
個別の現象からその内部構造を推測するトップダウン型
だけではなく、ソースコードに当たってその振る舞いを読み解くボトムアップ型
のアプローチも取り入れた結果、
これらのメソッドの振る舞いを、より多角的に理解できた気がしました。
で、これらに関連してcompactMap
なるメソッドが導入された新しいSwiftも正式リリースされて間もないことですし、そちらの話題もちょっぴり絡めつつ、まとめてみました。
この記事を読むことで、
Optional
だったり、複雑なArray
オブジェクトに対するmap
・flatMap
呼び出し、おまけにcompactMap
までも、その挙動が手に取るように把握できるようになります。
その結果、これらのメソッドに対する自信が深まり、Swiftがさらに楽しくなります。
他方、そもそもmap
やflatMap
とは何か、Swiftではどのように呼び出すのか…などといった事柄は、他の方の優れた解説に多くを任せようと思います。
この記事では、実際のソースコードを参照しながらflatMap
の挙動を解剖していくことで、
これまでとは別の角度からこのメソッドの理解を促進することに挑戦したいと思います。
Array
のflatMap
とcompactMap
が混在する世界線の話ができたら理想的なので、
よければSwift 4.1以降をご用意のうえ、お読みくださると嬉しいです。
環境
Xcode 9.3
Swift 4.1
用語・あるとよさげな知識
記事中で用いる用語の語義と、もし知っていればこの記事が遥かに読みやすくなると思われる知識について、あらかじめ共有させてください。
レシーバ
メソッドを呼び出す主体となるオブジェクトのこと。
メソッド呼び出しの記法 オブジェクト.メソッド()
の、.(ドット)
の左側。
let str = "HELLO"
str.lowercased() // レシーバは `str`であり、"HELLO"
let dog = Dog()
dog.bark() // レシーバは `dog`であり、Dog
let array = [1,2,3,4,5]
array.map{ $0 * $0 } // レシーバは `array`であり、[1,2,3,4,5]
あるとよさげな知識
SwiftOptional四方山話、
理論から入門するswift/lib/Sema – わいわいswiftc #1などにあるように、
オプショナル型とその非オプショナル型の間には、サブタイプ関係がある(T is T?)
// T is T?
let optionalInt: Int? = 1 // `T?`型の変数に、`T`型のオブジェクトを代入できている
また、Swiftの型の深淵をのぞくなどにあるように、
オプショナル型を戻り値にした関数オブジェクトとその非オプショナル型を戻り値にした関数オブジェクトの間には、サブタイプ関係がある(() -> T is () -> T?)
// () -> T is () -> T?
func bar() -> Int { return 1 }
let barOptional: () -> Int? = bar // `() -> Int?`型の変数に、`() -> Int` 型の関数オブジェクトを代入できている
ということを知っていると、特にOptional#map
、Optional#flatMap
周りの説明などが読みやすくなると思います。
もうひとつ、SwiftのOptional型を極めるの中などで用いられている、「オプショナルとは箱である」というアナロジーを、この記事を通して借用しています。もしリンク先をまだ読んだことがない場合、ぜひ読んでみてください。
またこの記事全編に渡り、上記の記事を参照いたしました。
Optional#flatMap(_:)の挙動を追う
標準ライブラリで定義された型のうち、flatMap
を持つ型はいくつかあると思いますが、
実際の開発などで最もよく使うものは、Optional
とArray
のそれではないかと思います。
そのため、本記事ではこの2つの型に属するflatMap
に絞って書いていきます。
では、まずはOptional
におけるflatMap
から。
Array
が持つflatMap
に比べると複雑度は低いと思われますが、
それでも深い理解を求めるのであれば、丁寧に見ていく必要があると考えます。
map
・flatMap
のきほん
そもそもmap
って、flatMap
ってどんな感じの処理なんだ、ってお話を簡単に。
map
map
・・・すなわちマッピング。
一般的なIT用語としてのマッピングなら、1
ある集合の個々の構成要素に対して、別の集合の要素を規則に従って機械的に対応付けたり割り当てたりすること。また、何かの分布や配置などを地図に重ね合わせて図示すること。
とりわけ、プログラミングにおけるマッピングなら、
あるオブジェクトをもとに、指定した変換ルールに基づき、新しいオブジェクトを生成すること。
こちらは引用というより、いま私が考えました。
変換ルールをクロージャとして指定してmap
に渡してあげることで、
その変換ルールに基づいて新たにオブジェクトを生成、返却してくれます。
たとえば、ある整数をもとに、その整数に1を足した整数を得たいなら、こんな感じ。
let num: Int? = 42
let newNum: Int? = number.map{ $0 + 1 }
print(newNum) // Optional("43")
flatMap
さて、そんな感じで使えるmap
メソッドなのですが、困ったこともあります。
例えばこんなケースだとどうでしょう。
標準ライブラリInt
には、文字列から整数を生成するイニシャライザが定義されています。2
public init?(_ description: String) {
self.init(description, radix: 10)
}
このイニシャライザはその定義にinit?
とあるように、
文字列から整数を生成できた場合、その整数を返しますが、
反対に生成できない場合、nil
を返します(失敗可能イニシャライザ: Failable Initializer
)。
つまり、このイニシャライザが返す値の型は、Int?
になります。
let num1: Int? = Int("11") // Optional(11)
let num2: Int? = Int("VA-11") // nil
// オプショナル型を返す処理を、非オプショナル型の変数で受けることはできない
let num3: Int = Int("11") // コンパイルエラー
最後の行にあるように、
オプショナル型を返す処理(メソッドや失敗可能イニシャライザ、他にもOptional Chaining
など)の結果は、必ずオプショナル型の変数で受ける必要がある(非オプショナル型の変数で受けることはできない)
という点は、一見当たり前のようで、何気にこのあと重要になるので、ぜひ知っておくとハッピーです。
(なお、そのようなルールになっている理由として、
もしオプショナル型を返す処理を非オプショナル型の変数で受けられるとしたら、
仮にその処理がnil
を返した場合、本来nil
を入れられない変数にnil
を格納できてしまい、
null安全が崩壊する…といった説明ができるのでは、と思います。)
話を戻して、このイニシャライザを用いてmap
を呼び出してみましょう。
let a: String? = "1"
let result: Int?? = a.map{ Int($0) }
print(result) // Optional(Optional(1))
ありゃ。map
を実行した結果、一重のOptional
ではなく、二重のOptional
で返ってきてしまいました。
このように、map
に渡したいクロージャが、その返り値にオプショナル型の値を返すクロージャだった場合、
map
の代わりにflatMap
を用いると、二重の箱をフラット(一重)にして結果を得られ、上手く対処できます。
let a: String? = "1"
let result: Int? = a.flatMap{ Int($0) }
print(result) // Optional(1)
map
の処理に加えて、フラットにする(Optional
という「箱」を、1個分ぺしゃんこにする)処理まで施してくれるのがflatMap
なのですね。
実装を見る
ではその実装を見てみましょう。
また、Optional
のflatMap
はmap
と対称性があり、比較しながら見ていくといっそう理解しやすくなるため、こちらも合わせて引用します(必要な箇所を抽出・編集しています)。
public enum Optional<Wrapped> {
case none
case some(Wrapped)
public func map<U>(
_ transform: (Wrapped) throws -> U // ※1 ここ同士が違う
) rethrows -> U? {
switch self {
case .some(let y):
return .some(try transform(y)) // ※2 ここ同士も違う
case .none:
return .none
}
}
public func flatMap<U>(
_ transform: (Wrapped) throws -> U? // ※1 ここ同士が違う
) rethrows -> U? {
switch self {
case .some(let y):
return try transform(y) // ※2 ここ同士も違う
case .none:
return .none
}
}
}
map
とflatMap
、交互ににらめっこしていると、
- 引数のクロージャの返り値 (※1)
- switch-case文の
case .some(let y)
内、return している箇所 (※2) ← 特に注目
だけが少し異なり、それ以外は全く同じつくりであることがわかります。
シグネチャこそ2つのジェネリクス型(Optional
に付随するジェネリクス型Wrapped
と、メソッド内で使われるジェネリクス型U
)が絡み、とっつきにくいのは確かなのですが、
より着目すべきはその本体({ }
の中身)であり、そこでなされている処理は意外なほどシンプルであることが見て取れます。
ではそのflatMap
の処理の中身を見てみましょう。
func flatMap<U>(
_ transform: (Wrapped) throws -> U?
) rethrows -> U? {
switch self {
case .some(let y):
return try transform(y)
case .none:
return .none
}
}
flatMap
を呼び出した主体(レシーバ)の値がnil
かどうかを判定し(switch self
)、
もしnil
であれば、nil
を返す。
一方nil
でなければ、値バインディングパターン(value-binding pattern)
のパターンマッチを用い(case .some(let y)
)、
列挙体Optional
のアソシエイト値として設定されている値(=「オプショナルという箱の中身」)を取り出して(let y
)、その値を引数で渡されたクロージャに適用する(try transform(y)
)。
最後に、クロージャの実行結果をflatMap
の結果として返す。
また、map
は処理のほぼすべてがflatMap
と同じなのですが、
1点だけ、クロージャの実行結果をオプショナルでラップした上で返す、という点だけが異なります。
func map<U>(
_ transform: (Wrapped) throws -> U
) rethrows -> U? {
switch self {
case .some(let y):
return .some(try transform(y)) // オプショナル型にラップした上で返す
case .none:
return .none
}
}
こうして見ると、「flatMap
がなんらかのフラットにする処理をしている」というよりも、
「map
が処理のあと、あえて値をオプショナルに包んでいる(.some(...)
)。flatMap
の”flat”とは、その対としての”flat”。」と考えていたほうが、おさまりが良いかもしれません。
「値をオプショナルでラップする」という余計なひと手間をわざわざ施しているぶん、
なんだかflatMap
よりもmap
のほうが特殊なメソッドのように感じられてきますね。
この動作を知るためには、その実装を覗くことなく到達するのはなかなか難しかったのでは…と思います。
注意点
さて、ここまであえて触れずにきたものの、気に掛けておかないといけない点があります。
上で見たように、flatMap
の場合map
と違って、
クロージャの実行結果がオプショナルに包まれることはありませんでした。
case .some(let y):
return .some(try transform(y)) // こちらは、クロージャの実行結果をオプショナル型でラップしているが...
...
case .some(let y):
return try transform(y) // こちらは、クロージャの実行結果を素直に返しているだけ
...
そのため、例えばこのflatMap
全体の戻り値の型は、非オプショナルな型となってもよさそうなものです。
let a: Int? = 1
let result = a.flatMap{ $0 + 1 } // 戻り値の型は `Int`、値は2かな...🤔
ですが、実際はその戻り値の型は、オプショナル型であるInt?
になってしまいます。
また、これを無理やりInt
型の変数で受けることはできません。
let a: Int? = 1
let result: Int? = a.flatMap{ $0 + 1 } // 実際は、型は `Int?`、値はOptional(2)になってしまった😯
let result: Int = a.flatMap{ $0 + 1 } // これはコンパイルエラー
どうしてこのような結果になってしまうのでしょう。
もう一度flatMap
のシグネチャに着目してみると、
引数として取るクロージャの定義に_ transform: (Wrapped) throws -> U?
とありますね。
public func flatMap<U>(
_ transform: (Wrapped) throws -> U? // ← ここに注目
) rethrows -> U? {
...
}
クロージャtransform
の戻り値は「U?」。
つまり、このクロージャを実行した結果返される値の型は、その定義にひきずられる形で、
オプショナル型であると解釈されてしまう(たとえ実際には「非」オプショナル型の値を返していたとしても)、というわけです。
そしてflatMap
がreturn
するのはこのオプショナル型の値なので、そうなるとそれを受け取る変数もオプショナル型でなければいけないのですね。
let a: Int? = 1
// `flatMap`に渡されたクロージャが返している値の型は、
// { return $0 + 1 } ...すなわち実際には `Int` 型のオブジェクト(今回は2)なのだが、
// クロージャの戻り値が `U?` になっているせいで、
// このクロージャを実行したとき返される値の型は `Int?`であると解釈されてしまう
let result: Int? = a.flatMap{ $0 + 1 }
また、よしんばtransform
の戻り値が「U?」
ではなく、「U」
、つまり非オプショナル型だったと仮定してみましょう。
public func flatMap<U>(
_ transform: (Wrapped) throws -> U // ← 実際はこうではなく、あくまで仮定です
) rethrows -> U? {
...
}
ですが仮にそうだったとしても、rethrows -> U?
とあるように、そもそもflatMap
全体の戻り値も U?
、すなわちオプショナル型となっているため、結局はこのflatMap
の実行結果はオプショナル型の変数で受け取らないといけません。
public func flatMap<U>(
_ transform: (Wrapped) throws -> U // ← この戻り値が仮に `U` だったとしても...
) rethrows -> U? { // ← そもそも`flatMap`全体の戻り値が `U?`
...
}
これは、このようなコードを書くと確かめることができます。
// クロージャを受け取って実行し、その結果を返す関数
func myFunc<U>(
_ completion: () throws -> U // クロージャの戻り値の型は、非オプショナル型の `U` だが...
) rethrows -> U? { // この関数全体の戻り値の型は、オプショナル型の `U?`。なので...
return try completion()
}
// クロージャの戻り値が非オプショナル型だろうが、
// `myFunc`の戻り値が `U?` とオプショナル型になっているので、
// 結局このメソッドの結果は、オプショナル型の変数で受けないといけなくなる
let result1: Int? = myFunc{ return 1 }
// 当然非オプショナル型の変数で受けることはできず、コンパイルエラーになる
let result2: Int = myFunc{ return 1 } // error
そのため、どのみちflatMap
実行結果の戻り値はオプショナル型と解釈される、というわけなのでした。
flatMap
やmap
の結果がOptional
で返ってくるのは、
簡単に言うと「Optional
という箱の中身(値)に対して、それを箱から取り出すことなく(入れたまま)演算を行う」ことができると都合がよいから。
これにより、箱の中身(値)を外の世界に触れさせることなく、後続の処理につなげたりできるようになっています。
となると当然、返される値はOptional
という箱に入ったまま、というわけですね。
挙動のまとめ
・・・存外長い説明となってしまいました。
いま一度、Optional
のflatMap
の挙動をまとめておきます。
Optional#flatMap
・レシーバがnil
ならば、nil
を返す。
・レシーバがnil
でないならば、レシーバの”箱の中身”に対して引数で渡したクロージャを適用し、
その結果を返す。
(・加えて、その型定義に引きずられる形で、その結果はオプショナル型の変数で受ける必要がある。)
ついでに、Optional
のmap
の挙動は、こう説明できます。
Optional#map
・レシーバがnil
ならば、nil
を返す。
・レシーバがnil
でないならば、レシーバの”箱の中身”に対して引数で渡したクロージャを適用し、
結果をオプショナルに包んで、その結果を返す。
(・加えて、その型定義に引きずられる形で、その結果はオプショナル型の変数で受ける必要がある。
すなわち、クロージャ適用の結果得た値がオプショナル型であれば、二重のオプショナル型になる。)
以上、ソースコードを参照しながら、Optional
のflatMap
(と、map
)の仕組みについて考察しました。
(補足) Optional#map
・Optional#flatMap
に関する補足
…と言いつつ、この項ではOptional#map
・Optional#flatMap
にまつわる、
「さらに探求したかったので調べてみたけど、本筋からは外れる話題」について、補足としてまとめました。
次のArray#flatMap(_:)の挙動を追う(その1)
までスキップでも一切支障ございません。
Q1. (Wrapped) throws -> U?
に対し、なぜ(Wrapped) -> U
を渡せるのか?
Optional#flatMap
が引数として取るクロージャの型は (Wrapped) -> U?
です。
public func flatMap<U>(
_ transform: (Wrapped) throws -> U? // クロージャ`transform`の型は `(Wrapped) -> U?`
) rethrows -> U? {
...
}
にもかかわらず、このようなコードが書けてしまうのです。
let a: Int? = 1
let result: Int? = a.flatMap{ $0 + 1 } // `flatMap`に渡されたクロージャの返り値の型は`Int`
print(result) // Optional(2)
(Wrapped) -> U?
、つまりオプショナル型の返り値を返すクロージャを渡せ、と要求されているにもかかわらず、
(Wrapped) -> U
、すなわち非オプショナル型の返り値を返すクロージャを渡せています。
(Wrapped) -> U?
と(Wrapped) -> U
、2つの型はそれぞれ別のものだというのに、
なぜこのようなコードが許されるのでしょうか?
どうやらSwiftにおいて、() -> T
型の関数オブジェクトは、() -> T?
型のサブタイプとして振る舞うことが可能なルールになっています。
() -> T is () -> T?
そのため、() -> Int
型の関数オブジェクトを、flatMap
へ渡せていた、というわけです。
let a: Int? = 1
// `() -> Int` is `() -> Int?`なので、{ $0 + 1 } を`flatMap`に渡せていた!
let result: Int? = a.flatMap{ $0 + 1 }
前に見たように、map
ではなくあえてflatMap
を使うべき場面とは、「クロージャの返り値の型がオプショナル型」の場合です。
そのため、「クロージャの返り値の型がオプショナル型」ではない場合、flatMap
は本来使えるべきではなく、理想的にはコンパイルが通らないような仕組みになっているのが望ましいですが…。
このルールのせいで、実際にはコンパイルが通り、実行できてしまいます。
このことが後で触れるcompactMap
が新設された遠因になっていたりもするようで、その経緯についてはSwift 4.1で導入されたcompactMapとその背景について、こちらの記事がProposalをベースに簡潔に解説されていて、たいへん勉強になりました。
なお、Swiftにおいて2つの型の間にサブタイプ関係がある場合に実現可能になることは、
本記事の終盤に、「サブタイプ関係がある場合にできること」という見出しで、補足としてまとめてあります。
Q2. (Wrapped) -> U
に対し、なぜ(Wrapped) -> U?
を渡せる(ように見えてしまった)のか?
Optional#map
が引数として取るクロージャの型は (Wrapped) -> U
です。
public func map<U>(
_ transform: (Wrapped) throws -> U // クロージャ`transform`の型は `(Wrapped) -> U`
) rethrows -> U? {
...
}
にもかかわらず、このようなコードが書けてしまうのです。
let a: String? = "1"
let result = a.map{ Int($0) } // ...これ、`map`に渡されたクロージャの型は `(Wrapped) -> U?`。なんで渡せるんだ...?🤨
これが許されるのはちょっと奇妙な気がします。
Q1.
で既に見たように、引数の型 (Wrapped) -> Int?
に対し (Wrapped) -> Int
を渡せるのはわかりました。
が、ここで起こっていることはその反対で、引数の型 (Wrapped) -> Int
に対して、 (Wrapped) -> Int?
のクロージャを渡せているかのように見えます。
ある2つの型がサブタイプ関係にある場合において、サブタイプになっている型に対しスーパータイプの型を渡すことは、本来できないはずなのでは…?
なぜこのようなコードが許されるのでしょうか?
…結論から言えば、当然私の考え方が間違っていたのです。
私の思考の誤ちを開陳させていただきたく思います。
今回のミソは、クロージャの戻り値が( Int
のような一般的な型ではなく)U
というジェネリクス型であり、コンパイル時に具体的な型に置き換えられる、という点にありました。
上で見たように、今回map
に渡されたクロージャの型は(String) -> Int?
です。
let a: String? = "1"
let result = a.map{ Int($0) } // クロージャの型は`(String) -> Int?`
するとその結果、map
が持つ型パラメータ U
が Int?
に決定(特殊化)されることになります
(map
のシグネチャに出現するU
の箇所に着目して型パズルを解くと、論理的にInt?
に定まることがわかります)。
そのときの状態はこうなります。
// もともと`U`があった場所を`Int?`で置換すると、こうなる。
// 論旨とは全く関係ないが、`Wrapped`も`String`で特殊化された。
public func map<Int?>(
_ transform: (String) throws -> Int?
) rethrows -> Int?? {
...
}
クロージャtransform
が返す値の型はInt?
、実際のコードでmap
に渡されたクロージャが返す値の型もInt?
。(Int($0)
)
よって、サブタイピング関係がどうのなんて話は初めから存在せず、map
に渡されるクロージャにおいて型の整合性は保たれていたので、問題なくコンパイルできていた、というわけだったんですね。
「クロージャが返す値の型は U
って書いてあって、U?
のように?
が付いていないのだから、このジェネリクス型は絶対にオプショナル型にはならない!」と思い込んでしまったが故に陥ったミスでした。
また、U?
= Optional<U>
という定義を柔軟に取り出せなかったことも、ドツボにはまっていった原因でした。
ここまでで判明したように、実際にはジェネリクス型に?
が付いていなくても、それがオプショナル型で特殊化されることはありえます。
今回U
が特殊化された型はInt
ではなく、Int?(Optional<Int>)
だったのです。
Q3. U?
を返すメソッドで、なぜ U??
を返せる(ように見えてしまった)のか?
Q2.
の問題を考えていた際、同時に陥っていた問題がこちらです。
こちらもQ2.
の解決と同時に解決しましたが、また忘れないように、文章にして残しておきます。
Optional#map
が返す値の型は U?
です。
public func map<U>(
_ transform: (Wrapped) throws -> U
) rethrows -> U? { // `map`が返す値の型は `U?`
...
}
にもかかわらず、このようなコードが書けてしまうのです。
let a: String? = "1"
// `map`が返す値の型は`Int??`.
// これって`map`の返す値の型は`U?`ということを考えると許容されないのでは...?🤨
let result: Int?? = a.map{ Int($0) }
print(result) // Optional(Optional(1))
map
の戻り値の型 U?
にもかかわらず、Int??
という二重のオプショナルの値を返せている?
通常のサブタイプ関係を鑑みるなら、戻り値 U??
に対し、実際にはU?
を返せるのはOKだけど、ここではその逆が成立している。
これはおかしいのではないか?と考えてしまいました。
…この勘違いも、Q2.
の疑問が解けたと同時に払拭できました。
今回の場合、map
が持つジェネリクス型 U
が、Int?
で解決(特殊化)されているので、map
はこうなっていたのでした(上記コードの再掲)。
// もともと`U`があった場所を`Int?`で置換すると、こうなる。
public func map<Int?>(
_ transform: (String) throws -> Int?
) rethrows -> Int?? {
...
}
map
の戻り値の型はInt??
、実際の呼び出しの結果も Int??
。
型の整合性はなんの問題もなく保たれていましたね。
let a: String? = "1"
let result: Int?? = a.map{ Int($0) } // 私の勘違い。実際は型の整合性は保たれていた😅
print(result) // Optional(Optional(1))
Q2
・Q3
の教訓として、ジェネリクス型は当然Optional<Wrapped>
型としても特殊化される場合がある。シグネチャの?
の有無だけに捕らわれるな!と自身に言い聞かせておきます。これでもう間違えるまい。
Array#flatMap(_:)の挙動を追う(その1)
本当はこちらが本日のメインディッシュです。
Optional
に続き、Array
のflatMap
についてもその挙動を追っていきます。
実装を見る
さっそくソースコードを確認してみましょう。3
Array
のflatMap
はArray
に直接書かれているのではなく、Sequence
のプロトコル拡張として定義されているようです。
(必要な箇所を抽出・編集しています。)
extension Sequence {
...
public func flatMap<SegmentOfResult : Sequence>(
_ transform: (Element) throws -> SegmentOfResult
) rethrows -> [SegmentOfResult.Element] {
var result: [SegmentOfResult.Element] = []
for element in self {
result.append(contentsOf: try transform(element))
}
return result
}
...
}
extension Sequence {
...
// @available(swift, deprecated: 4.1, renamed: "compactMap(_:)",
// message: "Please use compactMap(_:) for the case where closure returns an optional value")
public func flatMap<ElementOfResult>(
_ transform: (Element) throws -> ElementOfResult?
) rethrows -> [ElementOfResult] {
return try _compactMap(transform)
}
...
}
ここからわかるように、ArrayのflatMap
には2種類が存在し、
その呼び出され方によって、どちらが使われるかが決定される、というわけだったのですね。
flatMap
の呼び出し方によって、使われているflatMap
の種類がそもそも違っていたため、
自分にとってその挙動を見抜くことが困難を極めていたのも、1つにはこの点が原因だったのでした。
どのような場合に、どちらのflatMap
が呼ばれるのか?
その決定方法については後で改めて触れるとして、
まずは前者のflatMap<SegmentOfResult : Sequence>
の挙動を追ってみたいと思います。
public func flatMap<SegmentOfResult : Sequence>(
_ transform: (Element) throws -> SegmentOfResult
) rethrows -> [SegmentOfResult.Element] {
var result: [SegmentOfResult.Element] = []
for element in self {
result.append(contentsOf: try transform(element))
}
return result
}
Optional#flatMap
と同様、こちらもシグネチャの圧がやや強めですが、
そちらは一旦無視で実装のみに着目すると、その中身はわずか5行、かつその意図は非常に明確です。
あれだけクールなメソッドがたった5行で実装されていたなんて・・・ますますクールだ。
public func flatMap<SegmentOfResult : Sequence>( ... ) {
// その中身は、たった5行だけ
var result: [SegmentOfResult.Element] = []
for element in self {
result.append(contentsOf: try transform(element))
}
return result
}
その中身について読み解いていきましょう。
配列の各要素を取り出して(
for element in self
)、引数で渡されたクロージャに適用し(try transform(element)
)、その結果を一時変数に格納する(result.append(...)
)。
これをすべての要素に対して繰り返し、最後にその一時変数(新しい配列)を返す。
関数型プログラミングの香りを併せ持つとも称されるであろうこのメソッドですが、
その内部をひも解いていけば、実はバリバリ手続き型チックな記述で構成されていたこともわかりますね。
以下の例は、2次元配列([[Int]]
)をもとに、その中の要素([Int]
)をフラットにした新しい配列を返しています。
let result = [[1,2,3],[4,5]].flatMap{ $0 }
print(result) // [1,2,3,4,5]
以降、当記事ではこのflatMap
を、便宜上「A) 配列をフラットにするflatMap
」と呼びます。
フラットにするのはappend(contentsOf:)
の仕事
さて、このflatMap
はその内部でappend(contentsOf:)
というメソッドを使用していたのでした。
func flatMap<SegmentOfResult : Sequence>(
_ transform: (Element) throws -> SegmentOfResult
) rethrows -> [SegmentOfResult.Element] {
var result: [SegmentOfResult.Element] = []
for element in self {
result.append(contentsOf: try transform(element)) // ←ここ
}
return result
}
append(contentsOf:)
は配列を引数に取り、その要素をレシーバに追加するメソッドです。4
ここでなにげに重要なのが、append(contentsOf:)
の引数として渡された配列は、レシーバにそのまま加わるのではなく、
(配列という)その「箱」がつぶされた上で加わる、という点です。
var array = [1,2,3]
array.append(contentsOf: [4,5])
// [1,2,3,[4,5]] ではなく、[4,5]を格納する箱がつぶされ(フラットにされ)た上で、レシーバに追加される
print(array) // [1,2,3,4,5]
ひるがえって、このようなflatMap
呼び出しを追ってみると・・・
let array = [[1,2,3],[4,5]] // このような二次元配列があったとき・・・
let result = array.flatMap{ $0 } // それをそのままフラットにした新しい値を得たい
print(result) // ???
レシーバarray
の要素は[1,2,3]
と[4,5]
の2つなので、flatMap
内部のforループは2回転。
1回転めが終わった時点の状態をトレースすると、
append(contentsOf:)
の働きにより、内部の一時変数result
はこんな感じで・・・
func flatMap() {
var result: [SegmentOfResult.Element] // [1,2,3]
for element in self {
result.append(contentsOf: ...)
}
...
}
2回転めが終わった時点だと、こんな感じ。
func flatMap() {
var result: [SegmentOfResult.Element] // [1,2,3,4,5]
for element in self {
result.append(contentsOf: ...)
}
...
}
よって、最終的な結果はこうなります。
let array = [[1,2,3],[4,5]]
let result = array.flatMap{ $0 }
print(result) // [1,2,3,4,5]
つまり、こちらのflatMap
が持つ「要素をフラットにした上で結果を返す」という性質は、
このappend(contentsOf:)
こそが担っていた、というわけですね。
Array#append
には、append(contentsOf:)
に加え、append(_:)
という同名のメソッドが存在します。5
そしてこちらのappend(_:)
には、引数に取った配列をフラットにした上でレシーバに加えるという機能はありません。
var array = [[1,2,3]]
array.append([4,5]) // `append(contentsOf:)`ではなく、`append(_:)`を呼ぶ
print(array) // [[1,2,3],4,5] ではなく、[[1,2,3], [4,5]]
後述するもう一方のflatMap
が内部で使用しているのは、このappend(_:)
のほうです。
そしてこの違いこそが、2つのflatMap
の挙動の違いを生んでいたのです。
また後ほどArray#flatMap、Array#compactMapの注意点
で言及します。
注意点
後述したいと思います。
挙動のまとめ
改めて、こちらのflatMap
の挙動をまとめます。
Array#flatMap<SegmentOfResult:Sequence
>
配列の各要素を取り出して、引数で渡されたクロージャに適用し、その結果を一時変数に格納する。
これをすべての要素に対して繰り返し、最後にその一時変数(新しい配列)を返す。
Array#flatMap(_:)の挙動を追う(その2)
さて、Array
が持つflatMap
は、同名で別のものがありました。
なので、次はそちらのflatMap<ElementOfResult>
の挙動を追ってみたいと思います。
(あらかじめ言ってしまうと、Swift 4.1でcompactMap
にリネームされたflatMap
のことです。)
実装を見る
// @available(swift, deprecated: 4.1, renamed: "compactMap(_:)",
// message: "Please use compactMap(_:) for the case where closure returns an optional value")
public func flatMap<ElementOfResult>(
_ transform: (Element) throws -> ElementOfResult?
) rethrows -> [ElementOfResult] {
return try _compactMap(transform)
}
中身はたった1行、(アンダーバー)compactMap
という別のメソッドに、その仕事を完全に横流ししています。
気を取り直して、(アンダーバー)compactMap
の定義を見てみると…
func _compactMap<ElementOfResult>(
_ transform: (Element) throws -> ElementOfResult?
) rethrows -> [ElementOfResult] {
var result: [ElementOfResult] = []
for element in self {
if let newElement = try transform(element) {
result.append(newElement)
}
}
return result
}
どうやらこちらが実質的な本体のようですね。
といっても、こちらも1つ前に見た「(A) フラットにするflatMap
」と非常に似ています。
// 前回見たやつ
func flatMap<SegmentOfResult : Sequence>( ... ) {
var result: [SegmentOfResult.Element] = []
for element in self {
result.append(contentsOf: try transform(element))
}
return result
}
// 今回のやつ
public func flatMap<ElementOfResult>( ... ) {
return try _compactMap(transform)
}
func _compactMap<ElementOfResult>( ... ) {
var result: [ElementOfResult] = []
for element in self {
if let newElement = try transform(element) {
result.append(newElement)
}
}
return result
}
詳細を追ってみると、前回見たflatMap
と異なる点は、
・
append
されるのは「クロージャを適用した結果得た値がnil
でない場合のみ」という条件付きになったこと
・append
の方法がappend(contentsOf:)
ではなくappend(_:)
になったこと
のみですね。とりあえず使ってみましょうか。
以下の例は、
「整数の文字列が格納された配列をもとに、それを整数に変換した新しい配列を取得したい。
ただし、整数への変換に失敗した場合は、その要素をドロップしたい(含めたくない)”。」
と考えた人が書いたコードとなります。
let result = ["1", "two", "3"].flatMap{ Int($0) }
print(result) // [1, 3]
またこちらの例は、
「名字が格納された配列をもとに、それぞれの名字に”Kun(くん)”が付加された新しい配列を取得したい。
ただし、名字が特定の文字から始まっていた場合は、その要素をドロップしたい(含めたくない)”。」
と思い立ち、書いたコードとなります。
let result = ["Amemiya", "Sakamoto", "Kitagawa"].flatMap{
guard !$0.hasPrefix("A") else { return nil }
return $0 + " Kun"
}
print(result) // ["Sakamoto Kun", "Kitagawa Kun"] ← "A"から始まる名字はドロップされている
これらの例から察するに、こちらのflatMap
は、
クロージャ実行の結果がnil
になった場合、それをドロップした上で新しい配列を返すflatMap
、といった感じになっています。
またこれを別の角度から見ると、特定の条件下でドロップしたい要素がある場合は、
その条件のときにnil
を返すようにクロージャ内を記述してあげるのが使い方のコツといえそうです。
以降、当記事ではこのflatMap
を、便宜上「B) nil
をドロップするflatMap
」と呼称します。
また、上記で引用したコード定義内にあるコメントや、その定義の中身で誘導されるように、
こちらのflatMap
は、Swift 4.1で非推奨となり、compactMap
にリネームされました。
“compact”とはすなわち、nil
をドロップすることのようです。
注意点
後述したいと思います。
挙動のまとめ
というわけで、こちらのflatMap
(compactMap
)の挙動をまとめます。
Array#flatMap<ElementOfResult
> (compactMap
)
配列の各要素を取り出して、引数で渡されたクロージャに適用する。
その結果がnil
ではなかった場合に限り、その結果を一時変数に格納する。
(逆に言うと、結果がnil
だった場合、これはドロップされる。ここがミソ)
これをすべての要素に対して繰り返し、最後にその一時変数(新しい配列)を返す。
Array#flatMap
、Array#compactMap
の注意点
A) 配列をフラットにするflatMap
の注意点
・「A) 配列をフラットにするflatMap
」は、要素の要素がnil
だった場合、それをドロップしてくれるわけではありません。
「(B)」の方の挙動が頭に残っている場合、それにひきずられないようご注意。
// (A)
let result = [[1, nil], [3]].flatMap{ $0 }
print(result) // [Optional(1), nil, Optional(3)] ← `nil`がドロップされていない
B) nil
をドロップするflatMap
(=compactMap
)の注意点
・また反対に、「(B) nilをドロップするflatMap
(= compactMap
)」には、
「A) 配列をフラットにするflatMap
」が持つ、要素をフラットにした上で返す という性質は持ち合わせていません。
「(A)」の方の挙動が頭に残っている場合、それにひきずられないようご注意。
// (B)
let result = [1, 2, [3]].compactMap{ $0 }
print(result) // [1,2,[3]] ← [3] という「箱」が、フラットになっていない
こちらについては前に触れたように、Array#append
はオーバーロードによって2種類が存在し、それぞれその挙動が違うから、ですね。
(A) のflatMap
が内部で使用しているappend(contentsOf:)
は、引数の値をフラットにした上で内部の一時変数に追加するのに対し、
(B) のflatMap
(=compactMap
)が内部で使用しているappend(_:)
には、そのような効果はないのでした。
というわけで、ここまでソースコードを読んできた私たちにとっては、これらの挙動も実は知っての通りでしたね。
(ウソです。私はこの点しこたま混乱し倒した上、この先また忘れそうだから私のために書きました)
どのような場合に、どちらのflatMap
が呼ばれるのか
Array
オブジェクトに対しflatMap
を呼び出した際、どちらのflatMap
が呼び出されるのか、
その決定方法を考察してみましょう。
(Xcode
のサポートを受けられるため、開発中これらの事柄に思考を巡らす必要は一切ありません。)
「A) 配列をフラットにするflatMap
」に決定されるための条件
まず、「A) 配列をフラットにするflatMap
」になるための条件から考えてみましょう。
その定義に型パラメータ<SegmentOfResult : Sequence
> とあるので、これがキーになってきそうです。
func flatMap<SegmentOfResult : Sequence>( ← ここ
_ transform: (Element) throws -> SegmentOfResult
) rethrows -> [SegmentOfResult.Element] {
...
}
まず、SegmentOfResult : Sequence
なので、SegmentOfResult
になる型がSequence
に適合していないといけないことが確定します。
それに加え、この型パラメータがどこで出現してどう使われているのか、定義とにらめっこしてみると、
flatMap
が取るクロージャtransform
の返り値に、SegmentOfResult
が出現していることがわかります。
func flatMap<SegmentOfResult : Sequence>(
_ transform: (Element) throws -> SegmentOfResult ← ここ
) rethrows -> [SegmentOfResult.Element] {
...
}
この2つを組み合わせて考えると、
flatMap
が取るクロージャの戻り値がSequence
に適合している場合、
こちらの「A) 配列をフラットにするflatMap
」に決定する、ということになりますね。
例えば、これなんかがそうです。6
let numbers = [1, 2, 3]
let flatMapped = numbers.flatMap { Array(repeating: $0, count: $0) }
print(flatMapped) // [1, 2, 2, 3, 3, 3]
クロージャの戻り値の型はArray
、とうぜんSequence
に適合してるので、
「A) 配列をフラットにするflatMap
」のほうが呼び出されています。
(コードを見ただけでは確証が持てない場合でも、
alt
+クリックでヘルプを表示し、どちらが呼び出されているか確認すれば大丈夫です。)
「B) nil
をドロップするflatMap
(=compactMap
)」に決定されるための条件
一方、「B) nil
をドロップするflatMap
(=compactMap
)」になる場合ですが…こちらはここまで来たら簡単です。
すなわち、A)の場合と反対に、flatMap
(=compactMap
)が取るクロージャの戻り値がSequence
に適合していない場合、
こちらのflatMap
(=compactMap
)が呼ばれることになります。
例えば、これなんかがそうです。
// 'flatMap' is deprecated: Please use compactMap(_:) for the case where closure returns an optional value
let flatMapped = ["1", "two", "3"].flatMap{ Int($0) }
print(flatMapped) // [1, 3]
クロージャの戻り値の型はInt?
、Sequence
には適合していません。
そのため A) ではなく、「B) nil
をドロップするflatMap
(=compactMap
)」のほうが呼び出されています。
もちろんこの場合、flatMap
じゃなくてcompactMap
を使え、と警告されます。
将来的にはエラーになりそうだし、素直に従っておきます。
let compactMapped = ["1", "two", "3"].compactMap{ Int($0) } // ←`compactMap`に変更
print(compactMapped) // [1, 3] ← 結果はもちろん同じです
以上、どんな場合に、どちらのflatMap
が使われるか、を考察してきました。
最初にお伝えしたように、通常こんなことを毎回考える必要は全くなくて、
上の例にあるようにcompactMap
を使うべきシーンでflatMap
と書いた場合、
Xcode
がcompactMap
を使うように警告を出してくれて、簡単にリネームできるようになっています。
(補足) サブタイプ関係がある場合にできること
前項までで本筋の内容は終了しました。おわりに
までスキップでも一切支障ございません。
また、この項は後日、独立した記事としてくくり出したいと思います。
Swiftにおいて、2つの型のあいだにサブタイプ関係
がある場合に可能になることをまとめてみました。
それぞれの「できること」について、
A) 以下のような継承関係を持つ独自型 (Dog is Animal
)
class Animal {}
class Dog: Animal {}
print(Dog() is Animal) // true
と、
B) ある非オプショナル型は、そのオプショナル型のサブタイプ(T is T?
)
print(1 is Int?) // true
という、2つのサブタイプ関係を例にとって見ていきます。
1: スーパータイプの変数に、サブタイプのインスタンスを代入できる
// 【Animal - Dog】パターン
let animal: Animal = Dog()
// 【T? - T】パターン
let optionalInt: Int? = 1
2: 関数がスーパータイプの引数を要求するとき、サブタイプのインスタンスを渡せる
// 【Animal - Dog】パターン
func takeAnimal(_ animal: Animal) { }
takeAnimal(Dog())
// 【T? - T】パターン
func takeOptional(_ optional: Int?) { }
takeOptional(1)
3: 関数の戻り値の型がスーパータイプのとき、サブタイプのインスタンスを返せる
// 【Animal - Dog】パターン
func getAnimal() -> Animal {
// 関数の返り値は`Animal`だが、サブクラスである`Dog`オブジェクトを返せる
return Dog()
}
// 関数が返した`Dog`オブジェクトは、代入時に関数の戻り値の型へ型強制(暗黙的なアップキャスト)される
let animal: Animal = getAnimal()
// 変数に格納されたオブジェクトの型は`Dog`なので、もちろんダウンキャストには成功する
print(animal as! Dog) // 成功
//【T? - T】パターン
func getOptional() -> Int? {
// 関数の返り値は`Optional<Int>`だが、サブタイプである`Int`オブジェクトを返せる
return 1
}
// 関数が返した`Int`オブジェクトは、代入時に関数の戻り値の型へ型強制(暗黙的なアップキャスト)される
let optionalInt: Int? = getOptional()
// 変数に格納されたオブジェクトの型は`Int`なので、もちろんダウンキャストには成功する
print(optionalInt as! Int) // 成功
4: 親クラスのメソッドの「スーパータイプの戻り値」を、子クラスのメソッド定義内で「サブタイプの戻り値」を返すようにオーバーライドできる
// 【Animal - Dog】パターン
class SuperClass1 {
func foo() -> Animal { return Animal() }
var bar: Animal { return Animal() }
var baz: () -> Animal {
return { return Animal() }
}
}
class SubClass1: SuperClass1 {
// サブタイプの戻り値を返すようにオーバーライドできる(Dog is Animal)
override func foo() -> Dog { return Dog() }
// コンピューテッドプロパティでも成立する
override var bar: Dog { return Dog() }
// もしや、と思ったら...こう書ける。マジかw
override var baz: () -> Dog {
return { return Dog() }
}
}
// 【T? - T】パターン
class SuperClass2 {
func foo() -> Int? { return Optional.some(1) }
var bar: Int? { return Optional.some(1) }
var baz: () -> Int? {
return { return Optional.some(1) }
}
}
class SubClass2: SuperClass2 {
// サブクラスの型を返すようにオーバーライドできる(Int is Optional<Int>)
override func foo() -> Int { return 1 }
// コンピューテッドプロパティでも成立する
override var bar: Int { return 1 }
// もしや、と思ったら...こう書ける。マジかw
override var baz: () -> Int {
return { return 1 }
}
}
また、もし2つの型の間にサブタイピング関係があるなら…
if T is Optional<T>
if Dog is Animal
それらの型を戻り値に置いた関数オブジェクトにおいても、サブクラス関係が成立する、といえます。
then () -> T is () -> Optional<T>
then () -> Dog is () -> Animal
よって、以下のようなコードが成立します。
(もしかすると「類型5」としてではなく「類型1や2や3の亜種」と考えた方が自然に感じられるかもしれません。)
5-1: () -> スーパータイプ
の変数に、() -> サブタイプ
の関数オブジェクトを代入できる
(類型1の亜種とも考えられる)
// 【Animal - Dog】パターン
func foo() -> Dog { return Dog() }
let fooAnimal: () -> Animal = foo // `() -> Animal`型の変数に、`() -> Dog` 型の関数オブジェクトを代入できている
// 【T? - T】パターン
func bar() -> Int { return 1 }
let barOptional: () -> Int? = bar // `() -> Int?`型の変数に、`() -> Int` 型の関数オブジェクトを代入できている
5-2: () -> スーパータイプ
を要求する引数に対し、() -> サブタイプ
の関数オブジェクトを渡せる
(類型2の亜種とも考えられる)
// 【Animal - Dog】パターン
func takeClosure(_ completion: () -> Animal) { }
func getDog() -> Dog { return Dog() }
takeClosure(getDog) // `() -> Animal` 型の引数に対し、`() -> Dog` の関数オブジェクトを渡せている
takeClosure{ Dog() } // ↑の2行を手短に書くとこう。やってることは同じ
// 【T? - T】パターン
func takeClosure(_ completion: () -> Int?) { }
func getOne() -> Int { return 1 }
takeClosure(getOne) // `() -> Int?` 型の引数に対し、`() -> Int` の関数オブジェクトを渡せている
takeClosure{ 1 } // ↑の2行を手短に書くとこう。やってることは同じ
5-3: () -> スーパータイプ
の関数オブジェクトを返す関数で、() -> サブタイプ
の関数オブジェクトを返せる
(類型3の亜種とも考えられる)
// 【Animal - Dog】パターン
func returnClosure() -> () -> Animal {
return { Dog() } // `() -> Animal`の関数オブジェクトを返す関数で、`() -> Dog`の関数オブジェクトを返せている
}
// 【T? - T】パターン
func returnClosure() -> () -> Int? {
return { 1 } // `() -> Int?`の関数オブジェクトを返す関数で、`() -> Int`の関数オブジェクトを返せている
}
この他にもできることがあれば、ご教示いただけると嬉しいです。
おわりに
さまざまなflatMap
の実体に、そのソースコードを読むことで迫ってきました。
もしもこの先、flatMap
が一見不可解な値を返してきた場面に遭遇したとしても、
ソースコードという「ここに立ち返れば大丈夫!」という道しるべを手にできたのは私にとって大きかったです。
そしてそれは、心の平穏ももたらしてくれるはずです。
一行一行、論理の整合性を踏みしめながら進んでいきたい私のような俗物にとって、型にうるさめのSwiftは本当にぴったりな言語だと感じています。
「この挙動はなぜ?」という事象に出くわしても、これまで集めてきた知識を組み合わせ、粘り強く考えていくことで解答にたどり着ける確率が非常に高いのは、Swiftの抗いがたい魅力です。
Array
のflatMap
の一部がcompactMap
へと切り分けられ、ジェネリクスの機能も漸進していく中、残る関心事は・・・flatMap
がプロトコルMonad
で抽象されたり、そのために必要な記述力をSwiftが獲得するーそんな未来はいつの日か訪れるのでしょうか?
・・・それに思いを馳せるには、自分自身さらなる精進が必要なので、今日はこれにて。
links
-
なお、
Array#map
に関してはこの記事では取り扱いません。思いのほか長尺になってしまったもののOptional#map
についてこれまで言及してきたのも、元を辿ればOptional#flatMap
をより理解したいと思ったが故でした。決してArray#map
の実装の意図を把握しきれなかったので取り上げることを放棄したといった話ではないと思います。それはさておいても、やはりArray
の実装についても徐々に知識を深めていかないとなと考えています。 ↩ -
この
flatMap
の使用例はソースコード内の文書化コメントからただパクってきたものなのですが、この記事を書いている際、ふとそのヘルプメッセージ(コメント)が古いまま残っていたことに気づきました。「なぜflatMap
ともあろうメジャーなAPIのコメントが2年前から放置されているんや…」と訝しみながらもPRを出してみたところ、12時間ほどでマージされてしまい(ただのコメント修正だからここまで短かったに違いない)、図らずとも自身初めてのSwiftへのコミット体験となりました。また、どう見積もっても書くのに1分とかからなかった2行の修正コードが、結果としてこれまで書いてきた中で最もインパクトを持つコードになりました。Contributionなんて言うのも憚れるミクロな修正でしたが、当の本人にとってはありえん嬉しみが深かったです。世界で一番好きな技術のコミュニティに受容される、これほど自己承認欲が充足され、また幸せなことは他にありません。この気持ちを忘れずに、次こそはコアライブラリ、そして標準ライブラリ・・・と手を入れられる開発者になれるよう、成長していきたいと思います。 ↩
- FirebaseのデータをObjectMapperを用いてデータ構造を管理する方法
- Metal逆引きレシピ
- 1年遅れのWWDC 2015 Tour
- Swiftの静的コード解析ツールTailorの使い方
- Swift3でFacebook SDKを使う
- UITableViewのデリゲートメソッドまとめ
- iOS コーディングガイドライン
- Notification的にRxSwiftをクラス間の通知に使う
- React NativeでObjective-C/SwiftのAPIを扱う(Native Component編)
- Firebase Notificationを使用してiOS端末にPush通知を送信する
- Metalの恩恵は受けつつCore Imageで「手軽に」画像処理
- Swiftでのローカル通知・リモート通知の実装メモ
- UINavigationControllerと座標ズレの小ネタ
- [swift] Firebase Realtime Databaseのリスナーについて
- モジュール結合度について
- FacebookやTwitterのアプリで気になった表現を自分なりにトレースした際の実装ポイントまとめ(タイルレイアウトがサムネイル画像の枚数に応じて変わる表現)
- Widgets(Today Extension)のまとめ
- iOSアプリに審査なしでパッチを当てるライブラリを作ってみた
- Swift Evolution の Commonly Proposed がすごい勉強になる
- AndroidとiOSの実装を徹底比較する
- CALayer概要
- [Swift]0から作るXmasカウントダウンアプリ
- 猿でもできるKituraでJSONを生成する方法
- Visual Format Languageを使う【Swift3.0】
- User登録画面で活用できそうなProtocolを活用したDesignPattern3選
- RxSwift スレッドクイズ (解答・解説編)
- Kickstarter-iOSのViewModelの作り方がウマかった
- Swift で 2つの線分の交点を求める
- Metalでカメラからの動画入力をリアルタイム処理する
- iOSアプリ開発でアニメーションするなら押さえておきたい基礎
- iOS標準の写真アプリのように画像をズームしながら遷移させる
- プロトコル指向言語としてのSwift – OOPからPOPへのパラダイムシフトと注意点
- Actionを使って快適なViewModel生活を🏄
- 【Swift】最前面のUIViewControllerを取得する方法
- UIFeedbackGeneratorをiOS10未満対応アプリで楽に書けるUtility
- ViewControllerをObservableとして考える
- 【swift】イラストで分かる!具体的なDelegateの使い方。
- iPod20台を通信/制御/連携してみた【Max × Swift × OSC】
- iOSのMPSCNNによる手書き数字認識のサンプルを読む – 前編
- swift3でCGFloat.min, CGFloat.maxを利用したい
- Objective-CからSwiftへの移行でバグりやすいポイント
- コードレビューをチームにリクエストするSlack BotをSwift実装したお話🚢
- 誰でもわかるプログラミング入門を目指し隊〜Swift編〜
- Metal で 三角形を組み合わせて 2D の線を描く
- Swift中間言語の、ひとまず入り口手前まで
- MetalでiOSアプリに宿る生命
- Xcode 8.2, Swift 3.0でTwitterの認証を通してタイムラインを取得するまで
- Swift 3 以降の NotificationCenter の正しい使い方
- Windows 10でSwift開発環境構築 with Atom
- Observable.just()からはじめるRxSwift
- `as AnyObject` で何が起こるのか
- まだiOS Clean Architecture で消耗してるの? 爆速開発ツールを作ったのでご紹介
- Swiftにおけるアニメーションの世界観を掴む
- [Swift] [iOS] Watson Visual Recognitionを使って顔解析アプリを作ってみた
- 【Swift】いまさらですがiOS10でプッシュ通知を実装したサンプルアプリを作ってみた
- 【Swift3】日本語を含むURLをNSURLに
- スワイプすると出てくるメニューをSwiftで作る
- 脱・文字列ハードコーディング
- Objective-C の循環的複雑度を計測する
- 続:いろんなところでsushiを流す話
- iOSのMetalで畳み込みニューラルネットワーク – MPSCNNを用いた手書き数字認識の実装
- [iOS10] カメラを触ってみる
- ビルドで待たないための、Simulator 上で実行中の view をいじる方法
- Swift での UI テストの雑なまとめ
- iOSの新規プロジェクトでClean Architecture + Application Coordinatorの構成にした理由と感触
- [Swift] バリデーションチェックは、正規表現で!
- MkMapViewの使い方(swift版)
- UIKitにある機能でWebで見かけるようなUI達を作る
- Swift 3でSwiftBondを使ってMVVMしちゃおう
- [Swift] サンプルを見ながら、リファクタリングを勉強する
- Swift製のコマンドラインツールをbrewでインストールできるようにする
- 【Swift】アプリからメーラーを起動する方法
- Swiftで日記アプリを作ろう 〜その1 カレンダー表示編〜
- Carthageを使っていて”$(SRCROOT)/Carthage/Build/iOS/*.framework”を打ち飽きた方へ
- AWS + Nginx + Node.js + iOS(Swift) でリアルタイムチャットアプリを作ろう
- 【Xcode 8対応版】Xcodeプラグイン Alcatrazを導入しよう
- SwiftのAddress/Thread Sanitizer
- iOSで指先またはペン先の動きを記録
- Swiftで日記アプリを作ろう 〜その2 Realm導入編〜
- IBM OpenWhiskというサーバーレス実行環境を用いて、Swiftでおみくじ🎍
- 初心者のためのSprite Kit入門
- 【Swift】Realm BrowserでRealm Mobile Databaseの中身を確認する
- Swiftで日記アプリを作ろう 〜その3 Realm活用編〜
- iOSプロジェクトのテストバンドルに存在するファイルを取得する
- Swift3でDocumentディレクトリのファイルにアクセスする
- RxSwiftでUITextFiledのtextのオブザーバーがSwift3から変わっていた
- 【Swift】UITextFieldのカーソル非表示&コピー・ペーストを不可にする方法
- Alamofire, URLSessionの通信処理をMethod Swizzlingでスタブに置き換える
- 冬休みの8日間を使って、ダンス動画アプリを作った
- Swift で計算して Bezier 曲線を描く
- Swiftで小学生の頃ハマった定規でぐるぐるするやつを描く
- iOSアプリが何回もリジェクトされた後にAppleからフォローアップされる件
- ジェスチャーやカスタムトランジションを利用して入力時やコンテンツ表示時に一工夫を加えたUIの実装ポイントまとめ
- Xcodeの旧バージョンをインストールする方法
- Share Extensionでデータを共有する
- 【iOS10】Firebaseでサイレント通知を行う
- iOSで音声/ビデオ通話をWebRTCを使って実現する(調査編)
- iOSアプリ向け通信スタブの使い方
- Swift3.0対応 CoreGraphicsでPieを描く
- 【iOS】Tinder的アニメーション
- Instagramライクな画像フィルタライブラリをCocoaPodsで公開してみた
- SceneKitで扱える3Dモデルのフォーマット/アニメーションつき3DモデルをSceneKitで使う
- UITabBarにcustomなバッジをつける
- SwiftとJavascriptの変数宣言の比較
- RxSwiftをサクッと勉強してみた
- そういえばMacでAndroidアプリつくるために必要な準備ってなんだっけ?
- 吹き出しのようなViewを作ってみる
- Swiftのエラー4分類が素晴らしすぎるのでみんなに知ってほしい
- Swiftの型の限界を超える
- SwiftのString(文字列) APIとの付き合い方
- 恐怖!忍び寄るライブラリへのロックイン
- 関数型プログラミングの実際のところ
- Never GiveUp というタイトルで Swift Tweets で発表しました。 #swtws
- Clean Architecture 開発ツールの話
- iOSにおけるクリーンアーキテクチャよもやま話 #swtws
- 型推論のビルドが遅いらしいから調べてみる
- iOSエンジニアの正規表現入門
- UIViewPropertyAnimatorの基本まとめ①
- Swift3での日時に関する処理
- プッシュ通知を究める!その①〜普通のプッシュ通知の実装の仕方〜
- プッシュ通知を究める!その②〜リッチ通知(メディア付き)の実装の仕方〜
- KickStarter iOSアプリのStoryboardのenumでの管理のしかたに感動した
- iOSアプリからFirebase Cloud Messaging経由でプッシュ通知を送る
- Swift で Elixir のパイプ演算子を実装してみる
- Swiftの値型と参照型、値渡しと参照渡し
- Swiftのクロージャにおける循環参照
- [Swift] Validatorというライブラリを使ってValidationする
- KituraでWebSocketを動かす
- iOSアプリ開発基礎ハンズオン
- 素晴らしいSwiftのポインタ型の解説
- Swiftのメモリレイアウトを調べる
- Storyboard を使わずコードだけで画面を生成、遷移をしてみる
- DispatchQueueでthrottle/debounceを実現する
- Swiftのenumのメモリレイアウトの最適化が凄い
- RxSwiftでの実装練習の記録ノート(前編:Observerパターンの例とUITableViewの例)
- [Swift3]SegmentedControlでViewを切り替える
- SwiftLintのバージョンを固定する方法
- iOS 10.3で追加されたアプリレビューを投稿できるSKStoreReviewControllerを試してみた
- UIViewControllerAnimatedTransitioning によるカスタムトランジション
- CFSocket を使って iOS で socket 通信した話
- 画像をダブルタップとピンチイン・ピンチアウトで拡大・縮小する Swift3編
-
iOS 10.3からアプリ内レーティングが可能に!- SKStoreReviewController – - Swift3による自動更新購読のアプリ内課金(In-App Purchase)の実装 for 月額課金
- 【Swift】AppStoreのレビューフォームを開くパラメーターが新登場 – action=write-review –
- Swift EvolutionのAcceptedステータスまとめ (2017.1.27) – Protocol-oriented integers, Permit where clauses to constrain associated types, …etc
- 【Swift3.0】xibを使ったカスタムビューの作り方
- iOSの審査に関するまとめ
- Apple Watch に Core Motion を使って色々なデータを取得して表示させてみる
- [macOS]macOS Cocoaプログラミングをはじめる
- 値渡しと参照渡しと参照の値渡しと
- 【iOS】UILabelにPaddingをつける(Swift3対応)
- 【AutoLayout】UILabelの幅を文字列の長さに合わせて可変にする方法
- 【Swift3】ToDoアプリを作る【CoreData】
- Cloud9で始めるServer Side Swift (Vapor)
- UIViewPropertyAnimatorの基本まとめ②
- #Swift fastlaneの次に来る? Sourcery🔮でメタプログラミングする
- iOSのMetal Performance Shadersでニューラルネットを実行する際のモデルの渡し方
- iOSアプリ開発時に使う小ネタ
- 2点間の距離計算 (C, Clojure, Go, Haskell, Java, LOGO, OCaml, Ruby, Rust, Scratch, Swift)
- SwiftでBase64 Encode / Decodeする
- Today Extensionについて
- [Swift 3.0] ViewController単位で画面の向きを制御する
- RxSwiftでの実装練習の記録ノート(後編:DriverパターンとAPIへの通信を伴うMVVM構成のサンプル例)
- swiftのguardを使ってみる
- 乾電池型IoT”MaBeee”を使って簡単なおもちゃ制御アプリを作ってみる
- ReactiveSwift 基本要素の使い方 Signal / SignalProducer編
- [Swift|Kotlin]+Rxで作ったネイティブアプリのコードの違い
- 1行で全てのUIButton(UIView)の同時押しを無効にする
- ありそうでなかったSwiftでのPostリクエストの投げ方
- Swiftによるシリーズアプリの共通部分を切り出して一括管理する
- [iOS] Google VR SDK の代替案としての MetalScope
- iOS アプリの Unit Test – Swift 編
- Swift で Quadratic Bezier 曲線の長さを計算する
- 脳波でスカートをめくるP-WAVEを開発した話
- 今更聞けない?Struct と Class の使い分け方(補足)
- わーい、すごーい
- Swiftで「サクッと使える」通信ライブラリ【RapidFire】
- Swift GPUImage2で画像の平均色を抽出する
- 【Swift x PHP】iOS端末とPHPサーバでHTTP通信
- Auto Layout を使わずにわかりやすいコードでレイアウトが組めるフレームワーク NotAutoLayout
- Carthageで管理しているライブラリのライセンスをまとめてくれるスクリプト
- 【Swift 3】処理の完了を待ってから後続処理を行う
- 【Swift x Ruby】iOS端末とRuby on RailsサーバでHTTP通信
- Swift:UIColorを16進数カラーコードで初期化する
- SwiftLintのRules全まとめ
- Xcode 不使用リソースを抽出する
- ios10 シャッタースピードやホワイトバランスなどiPhoneカメラに特殊機能を実装する方法
- RxSwiftとAlamofireとObject Mapperで
- UITextViewにタップ可能なリンクを挿入する
- web制作者にもわかる、Swift 3が++と–を削除した理由
- iOSのMPSCNNに渡すモデルパラメータのフォーマット / TensorFlowからの書き出し
- [Swift] 三角形のボタンを作る
- [Swift]segueを使った画面遷移、segueを使わない画面遷移
- SwiftにおけるMethod Dispatchについて
- ZIG SIMが送信するセンサの種類とデータ構造について
- TwitterAPIとSwiftを使ってiOSアプリを作ろう! – 前編 – #dotsgirls
- TwitterAPIとSwiftを使ってiOSアプリを作ろう! – 後編 – #dotsgirls
- TwitterAPIとSwiftを使ってiOSアプリを作ろう! – カスタマイズ編 – #dotsgirls
- Google画像検索APIをiOSで利用する
- Apple公式のiPhoneアプリチュートリアルやってみた①(Build a Basic UI)
- Swift3対応をこれからする人が役立ちそうなこと!
- tableView(_:didSelectRowAt:)にて、modalなSegueをperformSegueすると描画が遅い件は、DispatchQueue.main.asyncで解決できる
- Custom URL Schemeでアプリ内の任意のページを表示する
- Swiftの強力な機能であるstaticメソッド制約の紹介と、Kotlin, TypeScript, Java, Scala, C++との比較
- [iOS] Swift + Kannaでtableをスクレイピング
- 激安NSAttributedString
- lazy var の遅延参照?という面白い動き
- UIButtonの画像をUIButtonに対してAspectFitする方法
- iOS の時間関数の精度
- [Swift] count == 0 より isEmpty を使うべき理由
- [swift] InterfaceBuilderで多角形のボタンを作る
- Swift3で消費型アプリ内課金(In-App-Purchase)を実装してみた
- UIStackViewを使って詳細ページの実装をシンプルにする
- Swift 3のUIScrollViewでカルーセルUI(ページング/画像などをスワイプで行き来できるView)をつくる
- iOS(Swift)でアラート画面のようにViewの背景透過をする
- Concourse CIでiOSアプリのCIを行う
- VO, DTO, POSO, DAO, Entity の違い
- Swift の文字列の長さ
- モックオブジェクトをより便利にする (Try! Swift2017)
- よくわかるUIGestureRecognizerDelegate
- Swift,iOS,Xcodeの設計についてメモ
- サーバサイドSwiftの現状考察 #tryswiftconf
- Try! Swift 2017のまとめ
- TableViewで複数セルを一気に複数削除する
- 新しいアプリを作るときによく使うSwift Extension集
- ユニットテストにも可読性を持たせる (Four Phase Test)
- try! Swift 2017 Tokyo 参加レポート
- UIImageのリサイズ方法と注意点
- 始めて見よう! SwiftでWebアプリ開発(基礎編)
- try! Swift Tokyo 2017 に参加した&リンク集
- 2017年におけるObjective-Cコミュニティの動向
- SpriteKitの衝突処理について(categoryBitMask collisionBitMask contactTestBitMask 使い方)
- 猿がようやく理解したSwiftのdeferの使いどころ
- [Swift3.0][iOS][SpriteKit]SpriteKitでテキストを落としたり、ぶっ飛ばしたりした!
- SwiftはどのようにJavaの検査例外を改善したか
- iOS 10以降のNotificationの基本
- 猿がついに理解できたSwiftのthrow・do・try・catchの意味
- print()やNSLogを書かずにconsoleにメッセージを出力する方法
- 猿がもがきまくって理解したSwiftのデリゲート(Delegate)という仕組み
- iOS Test Night_#3に参加できず悲しいのでまとめた
- iOS Test Night #3
- Firebase Cloud Messagingで画像つきプッシュ通知を送信する
- TypeというiOS用のMarkdownエディタを作った
- iOSのCIにモバイル向けCI、Bitriseを導入する(CocoaPods, GitHub Privateレポジトリ対応版)
- RxSwift 3.3.0で追加された3つのUnit(Single, Maybe, Completable)
- CAMPFIRE iOS #1 参加レポート
- 【iOS】ViewControllerを汚さずにUITableViewの下部からCellを追加する実装
- [Swift3]文字列が人名かどうかをバリデーションする方法
- [Swift] シンプルなカウントアップでSequenceに強くなる
- LINQライブラリまとめ
- iOSのBackground Transferがよくわからない人用に整理した
- [iOS][Swift]UITableViewのヘッダーとフッターを設定する方法
- 【Git】 project.pbxprojのコンフリクトの直し方
- Swiftのmutatingキーワードについて
- アプリにApple Payを導入する – 商品購入のハードルを下げる –
- Swiftでasync/awaitな書き方もできるPromiseライブラリHydra
- Playground で Carthage ライブラリを import する
- キリスト教の牧師(見習い)からWebエンジニアに転職した話
- [Swift 3.0]NSLayoutAnchorを用いたコードによるAuto Layout
- 俺の俺による俺のための iOS プログラム設計
- 和暦に関するメモ
- Interface Builderとソースコードで共通のカラーパターンを利用する方法
- SwiftyBeaver でアプリケーションログをメール送信しデバッグしやすい環境を作る
- [MVVM] kickstarter/ios-oss での画面遷移のやり方
-
図解 MemoryLayout
で解き明かす型のメモリー構成 - iOSのビルド高速化 7つの方法
- LINE NEWS風のUI実装
- カメラロールのようなシンプルなイメージピッカーの作成
- swiftLint 導入
- Instagramライクなパン/ピンチ操作できるイメージビューの作成
- 【iOS】アプリ内からレビューを依頼する 10.3未満も対応
- How to write basic UnitTestsを発表した際の参考資料
- Swiftのstatic、classキーワードの違いについて
- 心がHomebrewで旧バージョンのパッケージを入れたがってるんだ
- ただ画像を右側に表示したいだけなんだ・・・ – UIButtonの画像を右寄せにする
- 制約の修正なしで縦方向に要素を追加可能なビューの作成
- Storyboard上で置いてるUIVisualEffectViewのブラーのかかり具合を調節、アニメーションさせる [iOS10]
- DI(Dependency Injection)の概要
- SwiftRater を使って好きなタイミングで SKStoreReviewController を表示する
- Swift 3.1で死んだコードまとめ
- クラスや変数の名前とかを安易に英語にしたい
- Swiftのprint()をファイル名、行数、関数名を出力して分かりやすくする
- iPhoneのSFSpeechRecognizerとAVSpeechSynthesizerと発泡スチロールでボスっぽいなにかを作る
- [Swift] 順序付き辞書、DictionaryLiteral
- iOS/Androidアプリを突貫工事で開発するときにやって良かった!悪かった!こと まとめ
- APIKitでRequestのイニシャライザに渡すのはValueObjectやEntityじゃないほうが良い
- iOS 実装サンプルアプリ集
- iOSアプリを新規開発するときに最低限守っていること
- 日付関連クラスのまとめ(Swift3)
- Foundation.Operationの並列オペレーションがよくわからない人向けの説明
- Swiftで複数のフラグを管理するためにOptionSetを使うと便利だった
- Swiftで連番画像のコマ送りアニメーション
- Swift3でJSONパースを行う
- Swift3.0 NavigationBarをhidden(隠す)方法
- iOS 10 での通知処理について
- 【Swift3】RxSwift + APIKit + Himotokiで作るAPIクライアント
- Swiftでいい感じのViewModelを作るためのメモ
- IGListKitとRxSwiftを掛け合わせてみた🤔
- UIFeedbackGeneratorの使い方と便利に使えるライブラリ
- カスタムViewをNibから初期化の最新版
- 【iOS】ステータスバーのカスタマイズ【Swift3.0】
- carthageのバージョンを今すぐ上げよう
- SwiftでContainerViewを使ってみる
- Swift の struct の stored property は var にしよう
- アイコンを申請なしで変える(iOS10.3)
- SwiftのOptionalのベストプラクティス
- Vapor as a web framework
- モックを「差し込む方法」を考える
- Swift4で何がかわりそうなのか
- Swift Standard Library相関図を作ってみた・眺めてみた #swtws
- iOS(Swift)から3ステップでMastodonに投稿を行う
- なぜDelegateをプロパティに持つとweakを指定しなければいけないの?
- SlideMenuControllerSwiftの使い方とカスタマイズ
- あとで読むQiitaリーダーアプリをリリースしました
- null安全な言語でも、バグ検知を怠れば安心ではない
- 【ネタ】Swift で Sleep Sort
- [Swift] enumerated()はindexを返さない
- [SiriKit]Siriから料金の支払いをする
- 2017年版Realmのエレガントな使い方
- Swiftのプロパティとinout参照を組み合わせたときの挙動が面白い
- swiftのenumでStringがカスタムなときでもenumを諦めない
- 【iOS】Fluxを利用して画面遷移を制御する
- Swift3.0 で CoreGraphicsを使って見る
- Debug Memory Graph で Memory Leak を調査する
- Apple公式のiOS開発チュートリアルをやってみた(Andoridエンジニア視点)
- Swift 4の新しいreduceが素晴らしいので紹介する
- Alert,ActionSheetの表示処理がスッキリかけるUIAlertControllerのラッパーライブラリを作りました
- 体系的なSwift言語学習
- AppのIconからVersionやCommitを判別して混乱を防ぐ
- IB上で範囲の確認ができるUIButtonのタップエリアを拡大する実装
- RxSwift 用語解説
- 【Swift3.0】Alamofireで画像&パラメータを送信
- Swift Package Manager(SwiftPM)で作ったコマンドラインツールをHomebrewに登録する方法
- SwiftではバージョンはStructにして演算子オーバーロードで比較したらどうでしょう
- Swift3ではKVOにkeyPath()式を使っていくのが便利
- やはりお前らのboundingRectWithSizeは間違っている
- SwiftでTestコード(on Xcode)
- `typealias My` をススメようと思ったけどやめた件 あるいはstatic methodをどうやって呼ぶか
- Firebase iOS SDKが刷新されましたよっていう話し
- [2017年版]RxSwift + Alamofire + ObjectMapper + RealmのSwift実装について
- BluetoothをもちいたiOS同士の通信
- Rails 5 Action CableチャットアプリのiOSクライアント側を作る
- SpeakerDeckのスライドをPDF形式で表示できるiOSアプリを作った
- SwiftでArrayがnilか0件の場合に共通の処理を動作させたい時はnil結合演算子を使う
- fastlaneを導入してビルドを楽にする
- 10年間の iOS 機能のまとめ with WWDC
- 書評:Swiftの各機能が「なぜ」存在し「いつ」使うべきかを解説した技術書 – Swift実践入門
- iOS SwiftでBLEのサンプルを動かしてみる
- Swift3で日付(Date)の比較が超簡単になっていた件
- AppleWatchKitとSpriteKitでスペースシューティングゲームを作ってみた
- 様々な言語でMap, Filter, Reduceを実現してみた(1)
- アニメーション付きのボタンを実装するためのテクニック
- Setを使いこなしたい(願望)
- AVFoundationで動画のリアルタイム合成
- Swiftガイドライン的な
- 位置情報アプリ開発者必見!Energy Efficiency Guide for iOS AppsのReduce Location Accuracy and Durationを読んでみた
- Instagramのような画面UIを簡単に作れるPastelViewを試してみた
- Swift API デザインガイドライン
- インタフェースと型クラス、どちらでもできること・どちらかでしかできないこと
- サーバーレスサーバーサイドSwiftとHexaville
- RxSwiftのExamplesにしれっと入ってる双方向データバインディングの演算子がイケてた
- Swift3対応をしてハマった不具合
- iOSでアプリ間でデータをやり取りするためのNの試行
- Embedded framework使用時の肥大化問題
- Swiftならメモ化も最高にスッキリ書けます
- 詳解! ios-Charts
- Instagramのログイン画面みたいなグラデーションのアニメーションを自分で作る
- Swiftのmap, filter, reduce, etc…は(あたりまえだけど)for, if, switch, whileからできている
- iOS11で新しく導入されたFramework
- pageViewController(_: viewControllerBefore:) および pageViewController(_: viewControllerAfter:) が呼び出されるタイミングについて(UIPageViewController)
- What’s New in iOS11まとめ (Metal2以外)
- Xcode による iOS 開発で秘匿したい情報をどう管理するか
- iOS11のCoreNFCを使う
- 公式ドキュメントを追いながらARKitを試してみよう
- Swiftでクラス名や関数名等をログ出力する
- 【iOS 11】開発者ドキュメントから見る iOS 11 の新機能 #WWDC17
- WWDC2017で更新されたサンプルコードまとめ
- Xcode8のDebug Memory GraphでCFArrayのメモリリークの原因を探る
- ARKitを触ってみよう 〜第1話〜
- Swift4.0 で追加される Codable
- 引っ張って閉じることができるモーダルを実装する (UINavigationControllerの場合)
- iOS11 Swipe Actions
- [iOS][Swift3] ニュース系アプリのユーザインタフェース PageMenuKit の実装
- Swift4 CodableでJSONが扱いやすくなる?
- Swift4のCodableでフラットなJSONからネストしたオブジェクトにデコードする
- Swift4のCodableでISO8601の日付をデコードする
- 【iOS 11】【Core ML】pip install coremltools でエラーになった場合の対処法
- Swift4のJSONDecorderは、Date等のパース方法をカスタマイズできるみたい
- ReactorKit(Flux + Reactive Programming)を学ぶ1 入門編
- ReactorKit(Flux + Reactive Programming)を学ぶ2 基礎編
- Swift4のCodableに対応した、独自のDecoder(CSVDecoder)を実装してみよう
- 個人開発アプリでSwift 4に一足先に対応しました
- [Xcode 8] Swiftのドキュメントコメントについての簡潔なまとめ
- init の名は。
- iOS11で追加されたDeviceCheckについて
- SwiftでWebRTC実装ハンズオン 事前準備編
- iOS11で発表されたMusicKitについて
- iOS 11 WKWebView 3大新機能 (WWDC 2017)
- NSURLSessionがメモリリークしてしまうのをなんとかした
- Array や Dictionary にもモダンでオシャレな extension を実装する
- 99%くらいのSwiftプログラマーが使わないであろう演算子の話
- UIKitのView表示ライフサイクルを理解する
- 【まとめ】What’s New in Testing【WWDC 2017】
- Swift PlaygroundsでXcode projectのコードを動かしてみよう
- WWDC17で新しく発表された画像フォーマットHEIFを使ってみた
- iOSでの各回転検知方法とその結果
- SwiftでWebRTC実装ハンズオン 本編
- [Swift4] privateにextensionからアクセスできる
- iOS – CrashlyticsのrecordErrorでカスタムログを送る
- コードを一行も書かずにHello Worldする方法
- 画像を表示する際にモアレ(干渉縞)を抑制する方法
- APIKitとCodableでAPIクライアントを作る
- CodingKeyで、case名のcamelCase ⇄ stringValueのsnake_case を自動で変換する
- 【まとめ】Engineering for Testability (前半)【WWDC 2017】
- 【まとめ】Engineering for Testability (後半)【WWDC 2017】
- 【Swift】iOSで放置型育成ゲームを作りたい(0)
- [Swift]初心者のためのSwiftチートシート
- Swift Package Manager (SwiftPM) Version 4 概要
- ARKitで豆腐作り
- Swiftで複数の非同期処理の完了時に処理を行う
- 【Swift】hogehoge.delegate = self は何をしているのか。
- cmdshelfによるスクリプト運用のご提案
- mbed × BLE × iOSでとりあえず通信したい人のための記事
- iOS LINEでログイン(Line SDK)
- Swift:UserDefaultsで初期値を設定する方法
- ios, androidのローカライズファイルを共通管理して幸せになった話
- 型システムの理論からみるSwiftの存在型(Existential Type)
- Swift4.0でDictionaryが大幅にパワーアップした
- Swift4で変更されたStringAPIをいくつか試してみた
- NO MORE ビルド時間泥棒 ☕️❌【RxSwift編】
- 新規アプリサービスのためのログ実装とサービス選定
- SwiftのSelfキーワードの使い方まとめ
- [Swift]WinでやるC#erの為のSwift基本文法覚書
- Decoder, DecodingContainerの、デコード先の型を推論させたい!明示的に指定したくない!
- XCode9で追加されたAsset catalogsのNamed colors supportについて
- Swiftのattributeまとめ[Swift4対応]
- SwiftでもKotlinのif式を使いたい
- iOSでBeaconの振る舞いを確認する
- はじめてのSwiftアプリ制作4: StoryboardとAuto Layoutその1
- Swift4でのSingletonを用いた共通データの値渡し
- ルートが配列のJSONをCoadableでカスタムモデルにマッピングする
- iOSで楽にデバッグメニューをつける
- 【Swift】TextFieldのキーボードを閉じる方法3選
- SwiftとKotlinの文法を比較してみた(基礎パート)
- [コピペで使える]swift3/swift4でリアルタイム顔認識をする方法
- Swift4 [SE-160 Limiting @objc inference] 概要
- SwiftのExtensionによるクラス分割
- Xcodeのビルド待ちで消耗してたので見直したら50%以上削減できた話
- Swiftコンパイラ開発環境構築
- (初心者向け)Flickr Apiを使って画像を引っ張ってくる(1)
- Equatableとは?(swift) ~ object同士を比較できるようにしよう〜
- Swift4のCodableが内部で何をやっているか確認する
- SwiftでアプリのCPU使用率とメモリ使用量を取得する
- できるだけプログラムっぽくないプログラミングへの挑戦(Swift編)
- MVVMをベースにしつつCleanArchitectureを取り入れてみた
- GCD(Grand Central Dispatch)でキューの順番とスピードを制御する方法(1/2)
- GCD(Grand Central Dispatch)でキューの順番とスピードを制御する方法(2/2)
- Swiftコンパイラのテスト環境
- できるだけプログラムっぽくないプログラミングへの挑戦(Objective-C編)
- 特定のアプリがインストール済みかチェックする
- 「PythonとSwiftは結構似ている」説の検証
- ARKitでタップした座標を検出する方法
- Aspect Fill, Aspect Fit, Scale to Fillの違い
- APIKitとCodableとの連携
- ストーリーボードでUIを綺麗にレイアウトするネタ集
- Swift コンパイラのアーキテクチャ
- 最近Swift書いていて可読性を上げるために意識していること
- Swiftで静的DI(Mixin-Injection)
- UIImageView で cornerRadius と Shadow を同時に使いたい
- Enigmaの実装
- iOS開発でClean Architectureを採用した際のイイ感じのディレクトリ構成とは
- iOS開発で導入しているライブラリの一言説明
- 【メモ】Xcode9ビルドでCarthage経由で導入したライブラリに関してswift version errorが発生
- インクリメンタルサーチ【RxSwift/RxCocoa編】
- CotEditor を Swift に移行する
- Swift3.0でアニメーション1 ~ Animate()メソッド編~
- Swiftに息づくstructural types(構造的型)
- メソッドのhookは正しいタイミングで行おう【RxSwift/RxCocoa】
- 実践Swiftコンパイラ #swtws
- 純粋値型Swift
- Mac: 開発向け厳選ツール群(18/6/23更新)
- Swift でアニメーションの連続実行をしてみる話
- SwiftでiOS脱獄チェック
- iOSの機械学習フレームワークの比較 – Core ML / Metal Performance Shaders (MPSCNN) / BNNS
- 【初心者向け】Core Dataの使い方と説明swift3.0
- RxSwiftのshare*の早見表
- Swiftの有名画像キャッシュライブラリを比較してみた
- indexがArrayの範囲内かチェックする色々な書き方
- 【iOS】fastlaneでipaファイルを作成して、fabric crashlyticsでベータ版を配布し、Slackで完了通知を行う
- iOSアプリでMockを使ってUnitTestを書く
- iOSアプリでCIを始めようとサービスを調べた
- StubとSpyを使ってiOSのUnitテストを書いてみた(Clean architecture)
- RSKImageCropperの使い方とカスタマイズ
- アプリ内での Touch ID を利用したユーザ認証
- Automatically manage signingとxcconigで超効率化
- Instagram APIでOAuth認証する (Swift3版)
- [Swift] MainThreadで処理を実行する
- Carthage updateとCarthage bootstrapの違い
- iOSのクラッシュログをSymbolicate(復元)して解析する
- やさしいSwift単体テスト~テスト可能なクラス設計・前編~
- やさしいSwift単体テスト~テスト可能なクラス設計・後編~
- [iOS][Swift 4] CodableでJSONのパース
- [Swift] Dictionaryをこねくり回すネタ集
- Screen Recordingの録画開始・停止を取得する
- TDD ✕ Property-based Testing (SwiftCheck) で数学パズルを検証してみる
- 超効率化外伝: xcconfigの便利なところ&設定例
- Swiftで花火を作った話
- SwiftでHigher Kinded Polymorphismを実現する
- 絶対にやってはいけない「Apple IDをテストで13歳未満にすること・・」
- Spajam2017優秀賞「嫌われAIの命名」の発想プロセスからiOSアプリ実装まで
- RxSwift `a.withLatestFrom(a)` 同じ上流元の同期的合流問題
- Range が Codable に適合してなかったので後付けで適合させてみる話と、Codable のエラーハンドリングについて
- Swiftのfinalについて
- 【Swift】Dateの王道 【日付】
- サーバーレスとiOSアプリの連携 〜IBM Cloud Functionsを使ってサーバーサイドSwiftで試してみる
- ARKitで立方体の6面それぞれに異なるテクスチャを貼る方法
- iOSでlottie-iosを使ってリッチなアニメーションを簡単に実現してみる
- Swift の class の mutating func とは何か
- 余計なOptionalはやめてくれ
- 【プッシュ通知】Y Combinatorも投資するOneSignalがFirebaseより便利で素晴らしかった
- 【swiftエラー】clang: error: linker command failed with exit code 1 (use -vto see invocation)
- RxSwiftでwithLatestFromが最新じゃなくなることがあるorz
- RxSwiftについてようやく理解できてきたのでまとめることにした(1)
- RxSwiftについてようやく理解できてきたのでまとめることにした(2)
- RxSwiftについてようやく理解できてきたのでまとめることにした(3)
- RxSwiftについてようやく理解できてきたのでまとめることにした(4)
- iOSの消耗型課金のサーバーサイドTipsまとめ
- iOS 11 WKWebViewで広告などのコンテンツブロックをする
- タブスワイプで画面を切り替えるメニューUI
- 【MacOS】スクリーンレコーディング 【Swift】
- Codableについて色々まとめた[Swift4.x]
- FirebaseStorageの画像をSDWebImageで表示しようとして詰まった話
- [Swift] classにEquatableを実装するのは一筋縄ではいかない(ことがある)点に注意。
- ニュースアプリでAPIの記事をRealmにキャッシュして有効期限内だったらそれを表示する
- Swiftで参照型の値から生ポインタを作る方法
- Swift の protocol における Interface と Method の違いを理解しよう
- ARKitを扱う際の心構えとTips
- 脱Storyboardのすすめ
- Firebaseでアプリを開発するならClient Side Joinを前提にすること
- UILabelの文字色をグラデーションさせる
- [Swift3]ローカル通知の実装方法
- [Swift]iOSのデフォルトの関数を活用した Validation String Extension集
- Closureって美味しいの?
- [Swift3]アプリ内でレビューを依頼する
- プッシュ通知設定画面へ遷移させる為の実装
- iOS9, 10 WKWebView – Cookie操作
- iPad対応アプリを開発するときに、UI周りで気をつけることをまとめてみた
- Swift 4 で「プラマイ」範囲を作る
- ぼくのやっているVIPER(のようなもの)
- MVVMを勉強するときに参考になったリンク集 & 概要まとめ
- Xcode8でiOS11beta端末の動作確認がしたい(iOS11実機ビルドしたい)Could not locate device support files.エラー
- iOS11のバグ修正を行うに当たって気になったレイアウト関連の変更点(contentInsetAdjustmentBehavior, SafeAreaLayoutGuide)
- iOS 11 UITableViewでcontentOffsetを使ったスクロールが上手くいかない
- iOS 11 の Safe Area は Auto Layout だけでなくコードベースでも取れる
- iOS向けfastlaneアクションまとめ
- iPhone Xをネイティブ解像度から判定する
- Swift4 Stringのsubstring周りが変わっていた
- iPhone X Human Interface Guidelinesの要点
- iOSDC 2017 まとめ
- RxSwift の Observable とは何か
- Swaggerで始めるAPI定義管理とコードジェネレート
- iOS11で追加されるScreen Recordingについて
- iOS11 カメラとCoreML(Vision)で画像検出
- Swift初心者が3ヶ月でiOSアプリを公開するまでにやったこと、ハマったこと。
- iOSアプリ開発の全体像
- ARKitのコードによく出てくる4次元行列transformについて
- Swiftの @escaping と weak/unowned の理解
- iOSDC Japan 2017で「Auto Layoutのアルゴリズム」について発表しました
- Setは遅いのか
- iOSDC 2017 でさらっと出てきた Phantom Type さらっとやった話
- 超朗報。Xcode 9でやっとSwiftのリファクタリングが可能に
- iOS11から搭載されるスクリーンレコーディングでの録画を検知する方法
- Swift 4 マイグレーション、またはXcode9対応 メモ
- iOSと人工知能(AI) -GPU並列演算の仕組みと機械学習- というタイトルで、iOSDC2017に登壇しました
- 今度のiPhone Xは我々開発者をどれほど苦しめるのか #okamoba
- ARKitのサンプルコード集「ARKit-Sampler」
- iOS11のTwitter投稿対応(Social.framework → TwitterKit)
- iOS11で Grouped UITableView のセクションヘッダーに余分な高さが出る問題について
- [Swift] UserDefaults に画像を保存するとフリーズした
- iOS11 + Xcode9.0でedgesForExtendedLayoutの値を空にしていると、UITableViewのドリルダウンでアニメーションが崩れる
- 【iOSDC2017】MVC→MVP→MVVM→Fluxの実装の違いを比較してみる
- [Swift4]ARKitで球体をランダムに描画する
- 【Xcode9】ファイルヘッダーコメントをカスタマイズする
- [Swift] CharacterSetはCharacterのsetではありませんよ?
- iOS11のVision.frameworkを使ってみる
- iOS11からViewの一部だけを角丸にすることが簡単になった
- Swift 4の魅力の一面を3行で表す
- [Swift] 読み上げ機能、簡単に使えるライブラリつくったよ
- iOS11.0でUINavigationControllerのTitleViewのタッチイベントが呼ばれない現象について
- PDFKit を使ってみた
- Xcode の Debug Memory Graph が便利
- iOSアプリで紙吹雪を降らして画面を賑やかにする
- iOS 11ファイルAppにDocumentsフォルダを表示して他のアプリと共有する方法
- 【Swift】 画像を3種類も書き出したくないでござる
- Swift4 全予約語 (98語) の解説
- ライブラリを使わずにMV*の話(iOS)~ViewとModelの役割〜
- ライブラリを使わずにMV*の話(iOS)〜MVC, MVP, MVVM〜
- .ipa file を実機にインストールする方法(iTunes 12.7)
- UILabelをNSAttributedStringで文字装飾(Swift 4対応)
- AutoLayoutでiPhoneXのedge-to-edge対応

Androidに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Pythonに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Swiftに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。

Unityに関する現役のエンジニアのノウハウ・トレンドのトピックなど技術的な情報を提供しています。コード・プログラムの丁寧な解説をはじめ、初心者にもわかりやすいように写真や動画を多く使用しています。