Open12
Swiftの機能
ピン留めされたアイテム
Dynamic Member Lookup
- Swift 4.2で導入
- コンパイル時に存在しないプロパティに対して
.
シンタックスを使用してアクセスできる機能 -
@dynamicMemberLookup
をつけてsubscript(dynamicMember key: String) -> Any
を実装すると使える - 別言語の関数呼び出しやJSONのようにキーがあるかないか静的解決できない場面で使える
@dynamicMemberLookup class SomeClass {
subscript(dynamicMember key: String) -> Any {
if key == "piyo" {
return 123
} else if key == "meu" {
return "hello"
}
}
piyo
やmeu
はプロパティとしては存在していないけれど、アクセス可能
let someClass = SomeClass()
print(someClass.piyo)
print(someClass.meu)
KeyPath
- Swift 4で導入
-
インスタンス[keyPath: ]
で動的にプロパティにアクセスできる- アクセスするプロパティを動的に切り替えられる
-
\.
の記法でプロパティを指定する -
WritableKeyPath
は値型なのでvar
で定義されたstruct
のプロパティを上書きできる -
ReferenceWritableKeyPath
は参照型なのでlet
で定義されたclass
のプロパティを上書きできる
struct Dog {
var breed: String
var age: Int
func bark() {
print("Bow Wow")
}
}
var dog = Dog(breed: "Beagle", age: 10)
let anyKeyPath: AnyKeyPath = \Dog.breed
print(dog[keyPath: anyKeyPath]) // Optional("Beagle")
let partialKeyPath: PartialKeyPath<Dog> = \.breed
print(dog[keyPath: partialKeyPath]) // Beagle
let _keyPath: KeyPath<Dog, String> = \.breed
print(dog[keyPath: _keyPath]) // Beagle
let writableKeyPath: WritableKeyPath<Dog, String> = \.breed
dog[keyPath: writableKeyPath] = "Retriever"
print(dog.breed) // Retriever
class Dog {
var breed: String
var age: Int
init(breed: String, age: Int) {
self.breed = breed
self.age = age
}
func bark() {
print("Bow Wow")
}
}
let dog = Dog(breed: "Beagle", age: 10)
let referenceWritableKeyPath: ReferenceWritableKeyPath<Dog, String> = \.breed
dog[keyPath: referenceWritableKeyPath] = "Retriever"
print(dog.breed) // Retriever
ネストしたプロパティへのアクセス
struct Physical {
var color: String
var legs: String
var tail: Bool
}
struct Dog {
var breed: String
var age: Int
var physical: Physical
func bark() {
print("Bow Wow")
}
}
let physical = Physical(color: "multi", legs: "middle", tail: true)
let dog = Dog(breed: "Beagle", age: 10, physical: physical)
print(dog[keyPath: \.physical.tail]) // true
let physicalKeyPath = \Dog.physical
print(dog[keyPath: physicalKeyPath.appending(path: \.tail)]) // true
ファンクションのように扱う
struct Dog {
var breed: String
var age: Int
func bark() {
print("Bow Wow")
}
}
let dogs: [Dog] = [
Dog(breed: "Beagle", age: 10),
Dog(breed: "Retriever", age: 6),
Dog(breed: "Hound", age: 13)
]
let breeds = dogs.map(\.breed)
print(breeds) // ["Beagle", "Retriever", "Hound"]
つまり、dogs.map { $0.breed }
のように書かなくていい。
String Interpolation
文字列中に定数、変数、リテラル、式などを含めて新しい文字列を構築する。
CustomStringConvertible
に準拠してdescription
を実装していると、それが展開される。
let name = "John"
print("Hello \(name)")
Multiline String Literals
複数行に渡った文字列を定義できる。行末に\
をつけなければ改行が反映され、\
をつけると改行なしとなる。
let summary = """
Hello World!
Swift is a programming language. \
Multiline String Literals
"""
Extended String Delimiters
エスケープしないといけない特殊文字がそのまま文字として利用できる。
let text = #"backslash is \"#
willSet didSet
defer
return の省略
Optional
guard let, case let, if let, catch let
switchのcaseで毎回break書かなくていいやつ
_で引数ラベル省略できるやつ
$0で引数名省略できるやつ
,で&&や||の意味になるやつ
defer
- スコープを抜ける前にさせたい処理の予約ができる
func some() { defer { print("1") } // 中略 print("2") } some() // 2 // 1
-
defer
を複数定義した場合は、定義された逆順で処理されるfunc some() { defer { print("1") } defer { print("2") } defer { print("3") } return } some() // 3 // 2 // 1
Constants and Variables
let
やvar
の話
Declaring Constants and Variables
実は,
で区切りで1行の中に複数の定数や変数を定義できる
var x = 0, y = 1, z = 2
Type Annotations
実は1行の中で同じ型の変数を,
区切りで定義できる
この場合、最後の変数の後に型注釈をつける
var red, green, blue: Double
Nested comments
実はコメントはネストできる
/* これは最初の複数行コメントの開始です
/* これはネストした2番目の複数行コメントの開始です
これはネストした2番目の複数行コメントの終わりです */
これは最初の複数行コメントの終わりです */
Type Safety and Type Inference
- コンパイル時に型チェックが行われるのでエラーに素早く気づける
- コンパイラ時に式の型を自動で推論してくれるため、比較的型の宣言を明示的にする必要がない
- 型推論の仕組みを知ることで計算コストを抑えることが可能
- 部分的に推測した型の比較が多いとコストがかかるため、例えば以下のような場合は型注釈をしない方が良い
let hoge: Double = Double(100) // 左辺と右辺の比較が必要 let hoge: Double = 100 // 左辺と右辺の比較が必要 let hoge = Double(100) // 左辺と右辺の比較が不要
- 部分的に推測した型の比較が多いとコストがかかるため、例えば以下のような場合は型注釈をしない方が良い
Numeric Literals
- プレフィックスなしだと10進数
-
0b
プレフィックスありだと2進数(例0b11010
) -
0o
プレフィックスありだと8進数(例0o32
) -
0x
プレフィックスありだと16進数(例0x1A
) - 10進数で
e2
サフィックスをつけると×10²
(例1.23e2
) - 10進数で
e-2
サフィックスをつけると×10⁻²
(例1.23e-2
) - 16進数で
p2
サフィックスをつけると×2²
(例0x1Ap2
) - 16進数で
p-2
サフィックスをつけると×2⁻²
(例0x1Ap-2
) - 読みやすくするために
0
や_
を含めることが可能(例000123.456
、1_000_000
)
Type Aliases
typealias FloatingPoint = UnsafeMutablePointer<Float>
Tuples
- 複数の値を 1 つのまとまりにグループ化できる
- タプル内の値にはどんな型も入れることができ、全ての型を同じにする必要はない
- タプルを展開する時、アンダースコアで無視することが可能
- 各値へのアクセス方法として、デフォルトでは0から始まるインデックスを使用する
- 各値に名前付けができる
let http404Error = (404, "Not Found")
let (statusCode, _) = http404Error
print("The status code is \(http404Error.0)")
let http200Status = (statusCode: 200, description: "OK")
print("The status message is \(http200Status.description)")
Optionals
-
Optional
はenum
で定義されている
let a: Optional<Int> = .some(2)
let b: Int? = .none
print(a == 2) // true
print(b == nil) // true
Optional Binding
-
if
やguard
、while
文の 1 つのアクションで、オプショナル値に値が存在することを証明し、定数や変数にその内部の値を設定することを、まとめて行うことができる
if let constantName = someOptional {
// 処理
}
guard let constantName = someOptional else {
return
}
while let constantName = someOptional {
// 処理
}
Providing a Fallback Value
-
nil
結合演算子(??
)を用いてnil
だった場合にデフォルト値を提供できる
let name: String? = nil
let greeting = "Hello, " + (name ?? "friend") + "!"
print(greeting)
Force Unwrapping
-
!
を用いた強制アンラップは実質的にfatalError(_:file:line:)
の短縮系である
Implicitly Unwrapped Optionals
- オプショナルに一度値が設定された後は必ず値が存在するということが明らかな場合、定義時に
!
を型の後ろに書くことで、暗黙的アンラップオプショナルを書ける
Error Handling
func someProcess() throws {}
Assertions and Preconditions
- アサーションを用いれば、続くコードが実行される前に必要な条件が満たされているかどうかを確かめられる
- アサーションはプロダクションビルドでは実行されない
- プリコンディションはプロダクションビルドでも実行される
let text = "Hello World"
assert(!text.isEmpty, "text must not be empty.")
if text.isEmpty {
assertionFailure("text must not be empty.")
} else {
}
precondition(!text.isEmpty, "text must not be empty.")