let quotation = """
Even though there's whitespace to the left,
the actual lines aren't indented.
Except for this line.
Double quotes (") can appear without being escaped.
I still have \(apples + oranges) pieces of fruit.
"""
let vegetable = "red pepper"
switch vegetable {
case "celery":
print("Add some raisins and make ants on a log.")
case "cucumber", "watercress":
print("That would make a good tea sandwich.")
case let x where x.hasSuffix("pepper"):
print("Is it a spicy \(x)?")
default:
print("Everything tastes good in soup.")
}
// Is it a spicy red pepper?
let interestingNumbers = [
"Prime": [2, 3, 5, 7, 11, 13],
"Fibonacci": [1, 1, 2, 3, 5, 8],
"Square": [1, 4, 9, 16, 25],
]
var largest = 0
for (_, numbers) in interestingNumbers {
for number in numbers {
if number > largest {
largest = number
}
}
}
print(largest)
// 25
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)
// 120
print(statistics.2)
// 120
class NamedShape {
var numberOfSides: Int = 0
var name: String
init(name: String) {
self.name = name
}
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}
イニシャライザ内で self を使用して name プロパティと name 引数を区別していることに注目してください。インスタンスの作成時、イニシャライザの引数は関数の呼び出しと同じように渡されます。全てのプロパティは宣言時に値を設定するか(numberOfSides 参照)、イニシャライザ内で値を設定する必要があります。(name 参照)
enum Rank: Int {
case ace = 1
case two, three, four, five, six, seven, eight, nine, ten
case jack, queen, king
func simpleDescription() -> String {
switch self {
case .ace:
return "ace"
case .jack:
return "jack"
case .queen:
return "queen"
case .king:
return "king"
default:
return String(self.rawValue)
}
}
}
let ace = Rank.ace
let aceRawValue = ace.rawValue
Experiment 2つの Rank の raw value を比較して Rank を比較するメソッドを追加してみましょう。
デフォルトで、Swift は 0 から 1 つずつ増加する値を raw value に割り当てますが、明示的に値を指定してこの挙動を変更することもできます。上記の例では、Ace は明示的に 1 を raw value に指定しており、残りは、1 から順番に raw value が指定されます。文字列や浮動小数点も使用することができます。列挙型の個々のケースの raw value には、rawValue プロパティからアクセスすることができます。
Raw value から列挙型のインスタンスを生成するためには init?(rawValue:) イニシャライザを使います。これは raw value に合致したケースを返すか、合致するものがない場合は nil を返します。
if let convertedRank = Rank(rawValue: 3) {
let threeDescription = convertedRank.simpleDescription()
}
列挙型のケースの値は実在の値で、raw value を書くための別の方法ではありません。事実、意味のある raw value が存在しない場合は、raw value を提供する必要はありません。
enum Suit {
case spades, hearts, diamonds, clubs
func simpleDescription() -> String {
switch self {
case .spades:
return "spades"
case .hearts:
return "hearts"
case .diamonds:
return "diamonds"
case .clubs:
return "clubs"
}
}
}
let hearts = Suit.hearts
let heartsDescription = hearts.simpleDescription()
もし列挙型が raw value を持つ場合、これらの値は宣言の一部として決定されます。これはつまり、特定の列挙ケースの全てのインスタンスは、必ず同じ raw value を持つということです。もう 1 つの選択肢は、列挙ケースに関連値(Associated value)を持たせることです。関連値はインスタンス生成時に決定され、列挙ケースのインスタンスごとに異なる値を持つことができます。関連値は列挙ケースのインスタンスの格納プロパティのように振る舞います。例えば、サーバから日の出時刻と日の入り時刻をリクエストする場合を考えてみてください。サーバはリクエスト情報に応じたレスポンスを返すか、リクエストが間違っている場合は間違っているという内容を返します。
enum ServerResponse {
case result(String, String)
case failure(String)
}
let success = ServerResponse.result("6:00 am", "8:09 pm")
let failure = ServerResponse.failure("Out of cheese.")
switch success {
case let .result(sunrise, sunset):
print("Sunrise is at \(sunrise) and sunset is at \(sunset).")
case let .failure(message):
print("Failure... \(message)")
}
// Sunrise is at 6:00 am and sunset is at 8:09 pm.
Task {
await connectUser(to: "primary")
}
// Hello Guest, user ID 97
並行コードを構造化するために、タスクグループ(task group)を使います。
let userIDs = await withTaskGroup(of: Int.self) { group in
for server in ["primary", "secondary", "development"] {
group.addTask {
return await fetchUserID(from: server)
}
}
var results: [Int] = []
for await result in group {
results.append(result)
}
return results
}
let server = ServerConnection()
let userID = await server.connect()
プロトコルと拡張(Protocols and Extensions)
プロトコルを宣言するために protocol を使います。
protocol ExampleProtocol {
var simpleDescription: String { get }
mutating func adjust()
}
クラス、列挙型、構造体は全てプロトコルに準拠することができます。
class SimpleClass: ExampleProtocol {
var simpleDescription: String = "A very simple class."
var anotherProperty: Int = 69105
func adjust() {
simpleDescription += " Now 100% adjusted."
}
}
var a = SimpleClass()
a.adjust()
let aDescription = a.simpleDescription
struct SimpleStructure: ExampleProtocol {
var simpleDescription: String = "A simple structure"
mutating func adjust() {
simpleDescription += " (adjusted)"
}
}
var b = SimpleStructure()
b.adjust()
let bDescription = b.simpleDescription
let protocolValue: any ExampleProtocol = a
print(protocolValue.simpleDescription)
// A very simple class. Now 100% adjusted. が出力されます。
// print(protocolValue.anotherProperty) // エラーを確認したい場合はコメントアウトを外しましょう
do {
let printerResponse = try send(job: 1040, toPrinter: "Bi Sheng")
print(printerResponse)
} catch {
print(error)
}
// Job sent
Experiment プリンタの名前を "Never Has Toner" に変えてみましょう。send(job:toPrinter:) はエラーをスローします。
特定のエラーを処理するために、複数の catch ブロックを書くこともできます。switch 文の case のように catch の後ろにパターンを書きます。
do {
let printerResponse = try send(job: 1440, toPrinter: "Gutenberg")
print(printerResponse)
} catch PrinterError.onFire {
print("I'll just put this over here, with the rest of the fire.")
} catch let printerError as PrinterError {
print("Printer error: \(printerError).")
} catch {
print(error)
}
// Job sent
Experiment do ブロックの中でエラーをスローするコードを追加してみましょう。最初の catch ブロックでエラーを処理するためにはどの種類のエラーをスローする必要がありますでしょうか? 2つ目、3つ目の場合はどうでしょうか?
var fridgeIsOpen = false
let fridgeContent = ["milk", "eggs", "leftovers"]
func fridgeContains(_ food: String) -> Bool {
fridgeIsOpen = true
defer {
fridgeIsOpen = false
}
let result = fridgeContent.contains(food)
return result
}
if fridgeContains("banana") {
print("Found a banana")
}
print(fridgeIsOpen)
// false
ジェネリクス(Generics)
ジェネリックな関数や型を作成するには、山括弧(<>)の中に名前を書きます。
func makeArray<Item>(repeating item: Item, numberOfTimes: Int) -> [Item] {
var result = [Item]()
for _ in 0..<numberOfTimes {
result.append(item)
}
return result
}
makeArray(repeating: "knock", numberOfTimes: 4)
クラス、列挙型、構造体と同じように、ジェネリックな関数やメソッドも作成することができます。
// Swift 標準ライブラリのオプショナル型の再実装
enum OptionalValue<Wrapped> {
case none
case some(Wrapped)
}
var possibleInteger: OptionalValue<Int> = .none
possibleInteger = .some(100)
要件の一覧を指定するために、本文の直前に where を使います。例えば、プロトコルを実装するために型が必要な場合や、2 つの型が一致している必要がある場合、クラスが特定のスーパークラスを継承している必要がある場合などに使います。
func anyCommonElements<T: Sequence, U: Sequence>(_ lhs: T, _ rhs: U) -> Bool
where T.Element: Equatable, T.Element == U.Element
{
for lhsItem in lhs {
for rhsItem in rhs {
if lhsItem == rhsItem {
return true
}
}
}
return false
}
anyCommonElements([1, 2, 3], [3])