【Swift】Swift Documentation を読んでみる

About Swift
特徴
- 型推論サポート
- 関数ポインタで統合されたクロージャ
- タプルと複数の戻り値
- ジェネリック
- 範囲またはコレクションに対する高速かつ簡潔な反復
- メソッド、拡張機能、プロトコルをサポートする構造体
- 関数型プログラミング パターン
- エラー処理機能
- do、guard、defer、およびrepeatキーワードを使用した高度な制御フロー
安全性
- デフォルトでは Swift オブジェクトは nil にできないため、コードが安全に
→ この点 Dart では型を指定しなければ dynamic になる - Optionals の使用

A Swift Tour
型推論
常に型を明示的に記述する必要はない。
定数または変数を作成するときに値を指定すると、コンパイラはその型を推論する。
配列と辞書
fruits = [] // 空の配列
let fruits: [String] = [] // 空の String の配列
occupations = [:] // 空の辞書
let occupations: [String: Float] = [:] // 空の String : Float の配列
制御フロー
let individualScores = [75, 43, 103, 87, 12]
var teamScore = 0
for score in individualScores {
if score > 50 {
teamScore += 3
} else {
teamScore += 1
}
}
print(teamScore) // 11
for A in B で B の要素一つ一つに対して参照可能
var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
greeting = "Hello, \(name)"
}
print(greeting) // Hello, John Appleseed
if let
を使うと、nil の場合とそれ以外で条件分岐できる。
var optionalName: String? = nil
として実行すると Hello!
が出力される
var total = 0
for i in 0..<4 {
total += i
}
print(total)
// Prints "6"
for i in 0..<4
の様にインデックスの範囲を指定することで、ループ内にインデックスを保持
以下のDartのコードと同様
var total = 0;
for(int i = 0; i < 4; i++) {
total += i;
}
print(total);
// Prints "6"
Function と Closures
func greet(person: String, day: String) -> String {
return "Hello \(person), today is \(day)."
}
greet(person: "Bob", day: "Tuesday")
デフォルトでは関数はパラメータ名を引数のラベルとして使用
func greet(_ person: String, on day: String) -> String {
return "Hello \(person), today is \(day)."
}
greet("John", on: "Wednesday")
引数の前に _
を記述することで、引数のラベルを省略可能
func calculateStatistics(scores: [Int]) -> (min: Int, max: Int, sum: Int) {
var min = scores[0]
var max = scores[0]
var sum = 0
for score in scores {
if score > max {
max = score
} else if score < min {
min = score
}
sum += score
}
return (min, max, sum)
}
let statistics = calculateStatistics(scores: [5, 3, 100, 3, 9])
print(statistics.sum)
// Prints "120"
print(statistics.2)
// Prints "120"
タプルを返却する。タプルの要素は名前または番号で参照可能
同時実行性
func fetchUsername(from server: String) async -> String {
let userID = await fetchUserID(from: server)
if userID == 501 {
return "John Appleseed"
}
return "Guest"
}
async / await に関しては Dart と記法が似ている
func connectUser(to server: String) async {
async let userID = fetchUserID(from: server)
async let username = fetchUsername(from: server)
let greeting = await "Hello \(username), user ID \(userID)"
print(greeting)
}
async let を使用することで、他の非同期処理コードと並行して実行可能
Task {
await connectUser(to: "primary")
}
動機コードから非同期関数を呼び出すために Task を使用
SwiftUI では Viewの後に .task { }
のように記述することで、非同期処理を実装可能
actor ServerConnection {
var server: String = "primary"
private var activeUsers: [Int] = []
func connect() async -> Int {
let userID = await fetchUserID(from: server)
// ... communicate with server ...
activeUsers.append(userID)
return userID
}
}
actor
はクラスと似ているが、異なる非同期関数が同時に同じインスタンスと連携可能である点が異なる。

基礎
複数の変数定義
var red, green, blue: Double
変数をカンマで区切って、最後の変数の後に一つ型注釈を入れることで定義できる
数値リテラル(Numeric Literals)
let decimalInteger = 17
let binaryInteger = 0b10001 // 17 は2進数表記
let octalInteger = 0o21 // 17 は8進数表記
let hexadecimalInteger = 0x11 // 17 は16進数表記
2進数、8進数、16進数で表記することができる
タイプエイリアス(Type Aliases)
typealias AudioSample = UInt16
typealias
は既存の方をコンテキストに沿ったより適切な名前で参照する際に有効
タプル(Tuples)
let http200Status = (statusCode: 200, description: "OK")
print("The status code is \(http200Status.statusCode)")
// The status code is 200
print("The status message is \(http200Status.description)")
// The status message is OK
nil
var serverResponseCode: Int? = 404
// serverResponseCode Int の値の 404 を含んでいます
serverResponseCode = nil
// serverResponseCode は 値を含んでいません
デフォルトで値を与えずにオプショナルの変数を定義した場合、その変数には自動で nil
が設定される

基本演算子(Basic Operators)
閉範囲演算子(Closed Range Operator)
for index in 1...5 {
print("\(index) × 5 is \(index * 5)")
}
a...b
は、a から b までの連続した、a も b も含んだ範囲を定義
半開範囲演算子(Half-Open Range Operator)
let names = ["Anna", "Alex", "Brian", "Jack"]
let count = names.count
for i in 0..<count {
print("人物 \(i + 1) の名前は \(names[i])")
}
// 人物 1 の名前は Anna
// 人物 2 の名前は Alex
// 人物 3 の名前は Brian
// 人物 4 の名前は Jack
a..<b
のような半開範囲演算子は、a から b まで連続しているものの b は含まない範囲を定義
片側範囲演算子(One-Sided Ranges)
let names = ["Anna", "Alex", "Brian", "Jack"]
for name in names[2...] {
print(name)
}
// Brian
// Jack
for name in names[...2] {
print(name)
}
// Anna
// Alex
// Brian
この種類の範囲指定では、片方しか値を持たないため、片側範囲演算子と呼ばれる

クロージャ(Closures)
定義
クロージャは、コード内で受け渡して使用できる、ある機能の独立したブロック
定数と変数への参照を、それらを定義したコンテキストからキャプチャして保持できる。これは、定数と変数をスコープに閉じ込めると呼ばれる。
クロージャは、次の 3 つの形式のいずれか
- グローバル関数は、名前があり、値をキャプチャしないクロージャ
- ネスト関数は、名前があり、囲んでいる関数から値を取得できるクロージャ
- クロージャ式は、周囲のコンテキストから値をキャプチャできる、軽量な構文で書かれた名前のないクロージャ
クロージャ式構文(Closure Expression Syntax)
{ (<#parameters#>) -> <#return type#> in
<#statements#>
}
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in
return s1 > s2
})

API 設計ガイドライン
基本
-
使用時の明瞭さが最も重要
メソッドやプロパティなどのエンティティは一度だけ宣言されるが、繰り返し使用される。これらの用途を明確かつ簡潔にするために API を設計する。設計を評価する場合、常にユースケースを調べて文脈の中で確認する -
簡潔さより明瞭さが重要
最小限の文字で可能な限り小さいコードにすることは目標ではない -
全ての宣言に対してドキュメントコメントを書く
ドキュメント作成によって得られる洞察が設計に大きな影響を与える可能性がある -
API の機能を簡単に説明できない場合は、間違ったAPI設計である可能性がある
ネーミング
明確な使用を促進する
-
名前が使用されているコードを読む人が曖昧にならないように、必要な全ての単語を含めつつ、不必要な言葉は省略する。読者が持っている情報を鑑みて、重複する単語は省略する必要がある。
-
変数、パラメータ、関連する型には、型の制約ではなく、それらの役割に応じて命名する
- var string = "Hello"
+ var greeting = "Hello"
スムーズな使用を目指す
- メソット名、関数名は英語の文法的なフレーズを形成するように
下のコードの方が、x に対して、y を z の位置に挿入することが一目でわかる
- x.insert(y, position: z)
+ x.insert(y, at: z)
-
機能を記述するプロトコルには、 接尾辞able、ible、またはing (例Equatable: 、ProgressReporting) を使用して名前を付ける
-
他の型、プロパティ、変数、定数の名前は名詞として読み取る
用語をうまく使う
-
略語は避ける
非標準的な略語、専門用語を使用すると、略語を正しく翻訳する手間が生じる -
前例を受け入れる
広く使用されている用語は変更せずに使用すること
慣例
- 大文字と小文字の規則に従うこと
タイプとプロトコルは UpperCamelCase
それ以外は全て lowerCamelCase
引数ラベル
- 引数を区別できない、または区別する必要がない場合は、引数ラベルを省略する
- 値を保持する型変換を実行するイニシャライザでは、最初の引数ラベルを省略
- 基本的には全ての引数にラベルを付ける