この記事を書こうと思ったきっかけ
Swiftを学ぶ際に「公式ドキュメントを見るのが間違いない!」と思って見たら英文🥹
Google Chromeのページ翻訳機能を使うが、翻訳の仕方や英文と日本語の並びがおかしかったりでわかりにくい🙄
ということから他の翻訳ツールを使ってまとめることにしました📝
翻訳したページ
・About Swift
・Version Compatibility
・A Swift Tour
以下、翻訳です。
Swiftについて
言語の高レベルな目標を理解する。
Swiftは、携帯電話、タブレット、デスクトップ、サーバー、その他コードを実行するあらゆるものにソフトウェアを書くための素晴らしい方法です。安全で高速なプログラミング言語であり、現代の言語思考の最良の部分と、多様なオープンソースコミュニティの知恵を組み合わせています。
Swiftは、経験豊富なプログラマーが必要とする力と柔軟性を犠牲にすることなく、新しいプログラマーにも親しみやすい言語です。スクリプト言語のように表現力が高く楽しめる、工業品質のプログラミング言語です。コンパイラはパフォーマンス向けに最適化され、言語は開発向けに最適化されており、どちらも妥協することはありません。
Swiftは、現代的なプログラミングパターンを採用することで、一般的なプログラミングエラーの大部分を排除しています。
変数は常に使用前に初期化される。
配列のインデックスは、範囲外エラーをチェックする。
整数はオーバーフローをチェックする。
オプショナルは、nil値が明示的に処理されることを保証する。
メモリは自動的に管理される。
エラー処理により、予期しない障害からの制御された回復が可能になる。
Swiftコードは、現代のハードウェアを最大限に活用するためにコンパイルおよび最適化されます。構文と標準ライブラリは、コードを書く上で明らかな方法が最高のパフォーマンスを発揮するという指針に基づいて設計されています。安全性と速度を兼ね備えたSwiftは、"Hello, world!"から完全なオペレーティングシステムまで、あらゆるものに最適な選択肢です。
Swiftは、他の一般的な言語から来た開発者にとって親しみやすい、現代的で軽量な構文と、型推論やパターンマッチングのような強力な機能を組み合わせており、複雑なアイデアを明確かつ簡潔に表現することができます。その結果、コードの読み書きや保守が容易になります。
Swiftは、思慮深い新機能と強力な機能で進化し続けています。Swiftの目標は野心的です。あなたがSwiftで何を作るのか、楽しみにしています。
バージョンの互換性
古い言語モードで利用可能な機能について説明します。
この本では、Xcode 15.3に含まれているデフォルトのSwiftバージョンであるSwift 5.10について説明しています。Xcode 15.3を使用して、Swift 5.10、Swift 4.2、またはSwift 4で書かれたターゲットをビルドできます。
Xcode 15.3を使用してSwift 4とSwift 4.2のコードをビルドする場合、ほとんどのSwift 5.10の機能が利用可能です。ただし、以下の変更は、Swift 5.10以降を使用するコードでのみ利用可能です。
不透明な型を返す関数には、Swift 5.1ランタイムが必要です。
try?式は、すでにオプショナルを返す式に余分なオプショナル性を導入しません。
大きな整数リテラルの初期化式は、正しい整数型であると推論されます。たとえば、UInt64(0xffff_ffff_ffff_ffff)は、オーバーフローするのではなく、正しい値に評価されます。
並行処理には、Swift 5.10以降と、対応する並行処理型を提供するバージョンのSwift標準ライブラリが必要です。Appleプラットフォームでは、少なくともiOS 13、macOS 10.15、tvOS 13、またはwatchOS 6のデプロイメントターゲットを設定します。
Swift 5.10で書かれたターゲットは、Swift 4.2またはSwift 4で書かれたターゲットに依存することができ、その逆も可能です。つまり、複数のフレームワークに分割された大規模なプロジェクトがある場合、一度に1つのフレームワークずつSwift 4からSwift 5.10にコードを移行できます。
A Swift Tour
シンプルな値
定数をlet
で宣言し、変数をvar
で宣言します。定数の値は、コンパイル時に知る必要はありませんが、コードで正確に1回割り当てる必要があります。これは、1つの値を多くの場所で使用できることを意味しますが、意図せずに変更することはできません。
var myVariable = 42
myVariable = 50
let myConstant = 42
定数または変数に明示的な型を指定する必要はありませんが、明示的な型を指定すると、コードの意図を明確にすることができます。値の型は、コンパイラが型を推測できるようにする必要があります。
let implicitInteger = 70
let implicitDouble = 70.0
let explicitDouble: Double = 70
値は、他の型に変換されることはありません。明示的に変換する必要があります。
let label = "The width is "
let width = 94
let widthLabel = label + String(width)
文字列に値を含めるより簡単な方法は、バックスラッシュを使用して、値を括弧で囲むことです。
let apples = 3
let oranges = 5
let appleSummary = "I have \(apples) apples."
let fruitSummary = "I have \(apples + oranges) pieces of fruit."
複数行の文字列は、3 つの二重引用符を使用します。行頭のインデントは、終了の引用符と一致するように削除されます。
let quotation = """
I said "I have \(apples) apples."
And then I said "I have \(apples + oranges) pieces of fruit."
配列とディクショナリを作成するには、角括弧を使用し、インデックスまたはキーを角括弧内に記述してそれらの要素にアクセスします。最後の要素の後にコンマを置くことができます。
var shoppingList = ["catfish", "water", "tulips"]
shoppingList[1] = "bottle of water"
var occupations = [
"Malcolm": "Captain",
"Kaylee": "Mechanic",
]
occupations["Jayne"] = "Public Relations"
空の配列またはディクショナリを作成するには、初期化構文を使用します。
let emptyArray: [String] = []
let emptyDictionary: [String: Float] = [:]
型が推測可能な場合は、空の配列を [] として、空のディクショナリを [:]として記述できます。例えば、変数に新しい値を設定する場合や、関数に引数を渡す場合などです。
swiftCopy codeshoppingList = []
occupations = [:]
制御フロー
if
およびswitch
を使用して条件を作成し、for-in
、while
、およびrepeat-while
を使用してループを作成します。条件またはループ変数の周囲の括弧は任意です。本文の周りの中括弧は必須です。
let individualScores = [75, 43, 103, 87, 12]
var teamScore = 0
for score in individualScores {
if score > 50 {
teamScore += 3
} else {
teamScore += 1
}
}
print(teamScore)
if
ステートメントでは、条件は常にブール式でなければなりません。つまり、if score { ... }
は、暗黙の非ゼロとの比較ではなく、エラーです。
オプショナル値の後に疑問符(?
)を置くことで、nil でない場合はラップされた値にアクセスし、nil の場合はnil
を返すことができます。どちらの場合も、結果の型はオプショナルです。ラップされた値にアクセスする別の方法は、オプショナル バインディングを使用することです。
let optionalString: String? = "Hello"
print(optionalString == nil)
var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
greeting = "Hello, \(name)"
}
オプショナル値が nil の場合に使用するデフォルト値を提供する場合は、??
演算子の後に記述します。
let nickname: String? = nil
let fullName: String = "John Appleseed"
let informalGreeting = "Hi \(nickname ?? fullName)"
nil 合体演算子を使用して条件付きでオプショナル値にアクセスできます。オプショナルがnil
の場合、条件はfalse
で、コードはスキップされます。それ以外の場合、オプショナルはラップが解除され、if
コードブロック内でlet
定数として使用可能になります。
if let nickname {
print("Hey, \(nickname)")
}
switch はあらゆるデータ型の値に対して機能し、さまざまな比較操作を実行できます。スイッチ ケースは空にできません。ファイルスルーまたは明示的なbreak
ステートメントは必要ありません。
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.")
}
キーワードlet
を使用すると、ケース本文で使用できる定数にケースの値をバインドできます。
タプルの要素を個別の定数または変数にバインドすることで、タプルの一致を確認することもできます。
let somePoint = (1, 1)
switch somePoint {
case (0, 0):
print("\(somePoint) is at the origin")
case (_, 0):
print("\(somePoint) is on the x-axis")
case (0, _):
print("\(somePoint) is on the y-axis")
case (-2...2, -2...2):
print("\(somePoint) is inside the box")
default:
print("\(somePoint) is outside of the box")
}
for-in
を使用してディクショナリのキーと値のペアをイテレートできます。ディクショナリは順序付けられていないコレクションであるため、キーと値は任意の順序でイテレートされます。ディクショナリのキーはkeys
プロパティを使用して配列として取得することもできます。
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)
while
を使用してループを作成することができ、ループを完了する前に条件をテストします。条件は、ブロック本体の最後で条件をテストするように、ループの最後に置くこともできます。
var n = 2
while n < 100 {
n *= 2
}
print(n)
var m = 2
repeat {
m *= 2
} while m < 100
print(m)
..<を使用して、インデックスの範囲を作成できます。
var total = 0
for i in 0..<4 {
total += i
}
print(total)
半開範囲を使用して、上限値を省略します。
var totalHalf = 0
for i in 0..<4 {
totalHalf += i
}
print(totalHalf)
配列内のインデックスを省略する必要がない場合は、_ を使用してインデックスを無視することもできます。
let base = 3
let power = 10
var answer = 1
for _ in 1...power {
answer *= base
}
print("\(base) to the power of \(power) is \(answer)")
関数とクロージャ
func
を使用して関数を宣言し、関数の引数と戻り値の型を指定するには->
を使用します。関数の呼び出しでは、カンマで区切った引数のリストを使用することができます。
func greet(person: String, day: String) -> String {
return "Hello \(person), today is \(day)."
}
print(greet(person: "Bob", day: "Tuesday"))
関数のパラメーター名は関数内の局所名であり、関数を呼び出すときには外部での使用は任意です。関数宣言で外部パラメーター名を指定することで、パラメーターに名前を付けることができます。
func greet(_ person: String, on day: String) -> String {
return "Hello \(person), today is \(day)."
}
print(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)
print(statistics.2)
関数は、入れ子にすることができます。ネストされた関数は、外側の関数の変数にアクセスすることができます。関数は、入れ子のレベルを使用して整理することができます。
swiftCopy codefunc returnFifteen() -> Int {
var y = 10
func add() {
y += 5
}
add()
return y
}
print(returnFifteen())
関数は第一級の型です。つまり、関数は別の関数の戻り値として返すことができます。
func makeIncrementer() -> ((Int) -> Int) {
func addOne(number: Int) -> Int {
return 1 + number
}
return addOne
}
var increment = makeIncrementer()
print(increment(7))
関数は引数として別の関数を受け取ることができます。
func hasAnyMatches(list: [Int], condition: (Int) -> Bool) -> Bool {
for item in list {
if condition(item) {
return true
}
}
return false
}
func lessThanTen(number: Int) -> Bool {
return number < 10
}
var numbers = [20, 19, 7, 12]
print(hasAnyMatches(list: numbers, condition: lessThanTen))
関数は、実際は特殊なクロージャの一種です。{ }を使用して、関数の一部としてインラインクロージャを記述できます。クロージャの引数はinキーワードの後に記述します。
numbers.map({ (number: Int) -> Int in
let result = 3 * number
return result
})
複数のクロージャ引数を持つ関数を呼び出すときは、最後のクロージャ引数の後にトレイリングクロージャを記述できます。トレイリングクロージャを使用するときは、関数呼び出しの()を省略できます。
let mappedNumbers = numbers.map { number in 3 * number }
print(mappedNumbers)
関数のように、クロージャは参照型です。クロージャを変数または定数に割り当てると、クロージャへの参照を作成することになります。関数とは異なり、クロージャは周囲のコードからキャプチャすることができます。
let incrementByTen = makeIncrementer(forIncrement: 10)
print(incrementByTen())
print(incrementByTen())
print(incrementByTen())
let incrementBySeven = makeIncrementer(forIncrement: 7)
print(incrementBySeven())
オブジェクトとクラス
class
の後にクラス名を記述することで、クラスを作成します。クラスのプロパティ宣言は、クラス内の定数または変数宣言と同じように記述します。同様に、メソッドおよびイニシャライザの宣言は、クラス内の関数宣言と同じように記述します。
swiftCopy codeclass Shape {
var numberOfSides = 0
func simpleDescription() -> String {
return "A shape with (numberOfSides) sides."
}
}
Shape
のインスタンスを作成するには、クラス名の後に括弧を記述します。クラスのプロパティとメソッドにアクセスするには、ドット構文を使用します。
swiftCopy codevar shape = Shape()
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()
このShape
クラスのバージョンには不足しているものがあります。初期化時にクラスのnumberOfSides
を設定する必要があります。これをイニシャライザで記述します。
class NamedShape {
var numberOfSides: Int = 0
var name: String
init(name: String) {
self.name = name
}
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}
クラス内でイニシャライザ パラメータ name と、name プロパティを区別するために、self を使用することに注意してください。イニシャライザのパラメータには、クラスのプロパティと同じ名前を付けるのが一般的です。新しいプロパティを初期化するイニシャライザ パラメータの前にself
を使用します。
サブクラスでは、クラス名の後にコロンとスーパークラス名を記述します。クラスは標準のルートクラスから継承する必要はないため、必要に応じてスーパークラスを含めたり省略したりできます。
スーパークラスのメソッドをオーバーライドするメソッドは、override
とマークされています。オーバーライドせずにスーパークラスのメソッドをマークすると、コンパイラがエラーを検出します。コンパイラは、スーパークラスにないメソッドにはoverride
を挿入することもできます。
class Square: NamedShape {
var sideLength: Double
init(sideLength: Double, name: String) {
self.sideLength = sideLength
super.init(name: name)
numberOfSides = 4
}
func area() -> Double {
return sideLength * sideLength
}
override func simpleDescription() -> String {
return "A square with sides of length \(sideLength)."
}
}
プロパティには、ゲッターとセッターがあります。
class EquilateralTriangle: NamedShape {
var sideLength: Double = 0.0
init(sideLength: Double, name: String) {
self.sideLength = sideLength
super.init(name: name)
numberOfSides = 3
}
var perimeter: Double {
get {
return 3.0 * sideLength
}
set {
sideLength = newValue / 3.0
}
}
override func simpleDescription() -> String {
return "An equilateral triangle with sides of length \(sideLength)."
}
}
セッターのパラメータ名を指定しない場合、デフォルトの名前newValue
が指定されます。計算プロパティとは対照的に、イニシャライザの後または使用時に値が設定される、willSet
およびdidSet
を持つプロパティを記述できます。詳しくは、プロパティを参照してください。
class TriangleAndSquare {
var triangle: EquilateralTriangle {
willSet {
square.sideLength = newValue.sideLength
}
}
var square: Square {
willSet {
triangle.sideLength = newValue.sideLength
}
}
init(size: Double, name: String) {
square = Square(sideLength: size, name: name)
triangle = EquilateralTriangle(sideLength: size, name: name)
}
}
オプショナル値を使用すると、定数およびプロパティを保持または解放する前に条件を確認できます。オプショナル値は疑問符 (?
) で示され、値が存在しない可能性があることを示します。オプショナル バインディングを使用して、現在値が存在するかどうかを示します。
let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare?.sideLength
列挙型とストラクチャ
enum
を使用して列挙型を作成します。列挙型のメソッドの 1 つの一般的なクラスは、異なるケースに関連付けられた値を取得するイニシャライザです。
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
デフォルトでは、Swift は最初のケースから始まる 0 の生の値を割り当てますが、明示的な値を指定することでこの動作を変更できます。上記の例では、Ace
に明示的な生の値1
が与えられ、残りのケースには順番に生の値が割り当てられます。列挙型の生の値の型を文字列や浮動小数点数などに変更することもできます。rawValue
プロパティを使用して、ケースの生の値にアクセスします。
init?(rawValue:)
イニシャライザを使用して、生の値から列挙型のインスタンスを作成します。
if let convertedRank = Rank(rawValue: 3) {
let threeDescription = convertedRank.simpleDescription()
}
列挙型のケースの値は実際の値であり、生の値へのエイリアスではありません。実際、生の値が意味をなさない場合は、提供する必要はありません。
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()
struct
を使用してストラクチャを作成します。ストラクチャは、クラスと多くの同じ動作をサポートしています。これには、メソッドやイニシャライザが含まれます。クラスとストラクチャの最も重要な違いの1つは、ストラクチャはコードの間で値として渡されるのに対し、クラスは参照によって渡されることです。
struct Card {
var rank: Rank
var suit: Suit
func simpleDescription() -> String {
return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
}
}
let threeOfSpades = Card(rank: .three, suit: .spades)
let threeOfSpadesDescription = threeOfSpades.simpleDescription()
列挙型は、プロトコルを採用することができます。
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)")
}
プロトコルとエクステンション
rotocol
を使用してプロトコルを宣言します。
swiftCopy codeprotocol 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
extension
を使用して、既存の型に機能を追加することができます。拡張機能は、ソースコードへのアクセスがなくても、他の場所で宣言されたクラス、列挙型、ストラクチャ、およびプロトコルに新しい機能を追加するために使用できます。
swiftCopy codeextension Int: ExampleProtocol {
var simpleDescription: String {
return "The number \(self)"
}
mutating func adjust() {
self += 42
}
}
print(7.simpleDescription)
型の拡張機能を使用して、プロトコルに準拠することができますが、既存のインスタンスを変更することはできません。
let protocolValue: ExampleProtocol = a
print(protocolValue.simpleDescription)
// print(protocolValue.anotherProperty) // Uncomment to see the error
拡張機能を使用して、プロトコルに実装を提供できますが、準拠する型がすでに実装を提供している場合は、その実装が優先されます。
extension Double {
var absoluteValue: Double {
if self < 0 {
return -self
} else {
return self
}
}
}
エラー処理
Error
プロトコルに準拠する型を使用してエラーを表すことができます。
enum PrinterError: Error {
case outOfPaper
case noToner
case onFire
}
throw
を使用してエラーをスローし、throws
を使用してエラーをスローする可能性のある関数をマークします。関数がエラーをスローする場合、関数は即座に戻ります。エラーをキャッチするコードは、do-catchブロック
を使用して記述する必要があります。
func send(job: Int, toPrinter printerName: String) throws -> String {
if printerName == "Never Has Toner" {
throw PrinterError.noToner
}
return "Job sent"
}
do {
let printerResponse = try send(job: 1040, toPrinter: "Bi Sheng")
print(printerResponse)
} catch {
print(error)
}
複数のcatch
ブロックを提供して、特定のエラーを処理することができます。パターンと同様に、catch
の後にlet
を記述して、エラーに名前を付けることができます。
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)
}
一連のエラー処理を実行した後、defer
ブロックを使用してコードを記述することができます。これにより、関数の最後で実行する必要があるすべてのコードを、関数の終了前に1か所にまとめることができます。
ジェネリクス
角括弧を使用して、ジェネリック関数やジェネリック型を作成します。
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)
ジェネリック形式を持つ関数、メソッド、クラス、列挙型、およびストラクチャを作成できます。
// Reimplement the Swift standard library's optional type
enum OptionalValue<Wrapped> {
case none
case some(Wrapped)
}
var possibleInteger: OptionalValue<Int> = .none
possibleInteger = .some(100)
型制約を指定すると、ジェネリック関数やジェネリック型の要件をより制限できます。例えば、型パラメーターが特定のプロトコルに準拠する必要がある、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])
where
句を使用して制約を指定し、ジェネリック関数の本文の直前またはジェネリック型のメンバーの直前に置きます。
まとめ
以上が、Swiftプログラミング言語のガイドツアーの翻訳内容となります。
あとで自分で見直しながら更新していけたらと思っております。
もしおかしな翻訳などがありましたらコメント頂けますと幸いです🙇♂️