Tur Cepat

Diadaptasi dari A Swift Tour asli di Swift.org dengan modifikasi. Konten asli ditulis oleh Apple Inc. Berlisensi di bawah Lisensi Creative Commons Attribution 4.0 International (CC BY 4.0) .
Lihat di TensorFlow.org Jalankan di Google Colab Lihat sumber di GitHub

Tradisi menyarankan bahwa program pertama dalam bahasa baru harus mencetak kata-kata "Halo, dunia!" di layar. Di Swift, ini bisa dilakukan dalam satu baris:

print("Hello, world!")
Hello, world!

Jika Anda telah menulis kode dalam C atau Objective-C, sintaksis ini tampak familier bagi Anda—di Swift, baris kode ini adalah program yang lengkap. Anda tidak perlu mengimpor perpustakaan terpisah untuk fungsionalitas seperti input/output atau penanganan string. Kode yang ditulis dalam lingkup global digunakan sebagai titik masuk program, jadi Anda tidak memerlukan fungsi main() . Anda juga tidak perlu menulis titik koma di akhir setiap pernyataan.

Tur ini memberi Anda informasi yang cukup untuk mulai menulis kode di Swift dengan menunjukkan cara menyelesaikan berbagai tugas pemrograman. Jangan khawatir jika Anda tidak memahami sesuatu—semua yang diperkenalkan dalam tur ini dijelaskan secara rinci di sisa buku ini.

Nilai Sederhana

Gunakan let untuk membuat konstanta dan var untuk membuat variabel. Nilai konstanta tidak perlu diketahui pada waktu kompilasi, namun Anda harus menetapkan nilainya tepat satu kali. Ini berarti Anda dapat menggunakan konstanta untuk memberi nama nilai yang Anda tentukan satu kali namun digunakan di banyak tempat.

var myVariable = 42
myVariable = 50
let myConstant = 42

Konstanta atau variabel harus memiliki tipe yang sama dengan nilai yang ingin Anda tetapkan. Namun, Anda tidak selalu harus menulis tipenya secara eksplisit. Memberikan nilai saat Anda membuat konstanta atau variabel memungkinkan kompiler menyimpulkan tipenya. Pada contoh di atas, compiler menyimpulkan bahwa myVariable adalah bilangan bulat karena nilai awalnya adalah bilangan bulat.

Jika nilai awal tidak memberikan informasi yang cukup (atau jika tidak ada nilai awal), tentukan jenisnya dengan menuliskannya setelah variabel, dipisahkan dengan titik dua. Catatan: menggunakan Double daripada Float untuk angka floating point memberi Anda lebih presisi, dan merupakan tipe floating point default di Swift.

let implicitInteger = 70
let implicitDouble = 70.0
let explicitDouble: Double = 70
// Experiment:
// Create a constant with an explicit type of `Float` and a value of 4.

Nilai tidak pernah secara implisit dikonversi ke tipe lain. Jika Anda perlu mengonversi nilai ke tipe lain, buatlah instance tipe yang diinginkan secara eksplisit.

let label = "The width is "
let width = 94
print(label + String(width))
The width is 94

// Experiment:
// Try removing the conversion to `String` from the last line. What error do you get?

Ada cara yang lebih sederhana untuk memasukkan nilai dalam string: Tulis nilai dalam tanda kurung, dan tuliskan garis miring terbalik (``) sebelum tanda kurung. Misalnya:

let apples = 3
print("I have \(apples) apples.")
I have 3 apples.

let oranges = 5
print("I have \(apples + oranges) pieces of fruit.")
I have 8 pieces of fruit.

// Experiment:
// Use `\()` to include a floating-point calculation in a string and to include someone's name in a
// greeting.

Gunakan tiga tanda kutip ganda ( """ ) untuk string yang memuat beberapa baris. Indentasi di awal setiap baris yang dikutip akan dihilangkan, selama cocok dengan indentasi tanda kutip penutup. Misalnya:

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.
    """
print(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 8 pieces of fruit.

Buat array dan kamus menggunakan tanda kurung ( [] ), dan akses elemennya dengan menulis indeks atau kunci dalam tanda kurung. Tanda koma diperbolehkan setelah elemen terakhir.

var shoppingList = ["catfish", "water", "tulips", "blue paint"]
shoppingList[1] = "bottle of water"

var occupations = [
    "Malcolm": "Captain",
    "Kaylee": "Mechanic",
]
occupations["Jayne"] = "Public Relations"
print(occupations)
["Jayne": "Public Relations", "Kaylee": "Mechanic", "Malcolm": "Captain"]

Array secara otomatis bertambah saat Anda menambahkan elemen.

shoppingList.append("blue paint")
print(shoppingList)
["catfish", "bottle of water", "tulips", "blue paint", "blue paint"]

Untuk membuat array atau kamus kosong, gunakan sintaks penginisialisasi.

let emptyArray = [String]()
let emptyDictionary = [String: Float]()

Jika informasi tipe dapat disimpulkan, Anda dapat menulis array kosong sebagai [] dan kamus kosong sebagai [:] — misalnya, saat Anda menetapkan nilai baru untuk variabel atau meneruskan argumen ke suatu fungsi.

shoppingList = []
occupations = [:]

Aliran Kontrol

Gunakan if dan switch untuk membuat kondisional, dan gunakan for - in , for , while , dan repeat - while untuk membuat perulangan. Tanda kurung di sekitar kondisi atau variabel loop bersifat opsional. Diperlukan kawat gigi di sekitar tubuh.

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

Dalam pernyataan if , kondisinya harus berupa ekspresi Boolean—ini berarti kode seperti if score { ... } adalah sebuah kesalahan, bukan perbandingan implisit dengan nol.

Anda dapat menggunakan if dan let bersama-sama untuk menangani nilai-nilai yang mungkin hilang. Nilai-nilai ini direpresentasikan sebagai opsional. Nilai opsional berisi nilai atau berisi nil untuk menunjukkan bahwa ada nilai yang hilang. Tuliskan tanda tanya ( ? ) setelah jenis nilai untuk menandai nilai tersebut sebagai opsional.

var optionalString: String? = "Hello"
print(optionalString == nil)
false

var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
    greeting = "Hello, \(name)"
}
print(greeting)
Hello, John Appleseed

// Experiment:
// Change `optionalName` to `nil`. What greeting do you get?
// Add an `else` clause that sets a different greeting if `optionalName` is `nil`.

Jika nilai opsionalnya adalah nil , kondisinya false dan kode dalam kurung kurawal dilewati. Jika tidak, nilai opsional akan dibuka bungkusnya dan ditetapkan ke konstanta setelah let , yang membuat nilai yang belum dibungkus tersedia di dalam blok kode.

Cara lain untuk menangani nilai opsional adalah dengan memberikan nilai default menggunakan ?? operator. Jika nilai opsional tidak ada, nilai default akan digunakan.

let nickName: String? = nil
let fullName: String = "John Appleseed"
print("Hi \(nickName ?? fullName)")
Hi John Appleseed

Switch mendukung segala jenis data dan beragam operasi perbandingan—tidak terbatas pada bilangan bulat dan pengujian kesetaraan.

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?

// Experiment:
// Try removing the default case. What error do you get?

Perhatikan bagaimana let dapat digunakan dalam suatu pola untuk menetapkan nilai yang cocok dengan bagian pola tersebut ke sebuah konstanta.

Setelah mengeksekusi kode di dalam switch case yang cocok, program keluar dari pernyataan switch. Eksekusi tidak berlanjut ke kasus berikutnya, jadi tidak perlu keluar dari saklar secara eksplisit di akhir setiap kode kasus.

Anda menggunakan for - in untuk mengulangi item dalam kamus dengan memberikan sepasang nama yang akan digunakan untuk setiap pasangan nilai kunci. Kamus adalah kumpulan yang tidak berurutan, sehingga kunci dan nilainya diulangi dalam urutan yang berubah-ubah.

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 (kind, numbers) in interestingNumbers {
    for number in numbers {
        if number > largest {
            largest = number
        }
    }
}
print(largest)
25

// Experiment:
// Add another variable to keep track of which kind of number was the largest, as well as what that
// largest number was.

Gunakan while untuk mengulang blok kode hingga kondisi berubah. Kondisi perulangan bisa berada di akhir, memastikan bahwa perulangan dijalankan setidaknya sekali.

var n = 2
while n < 100 {
    n = n * 2
}

print(n)
128

var m = 2
repeat {
    m = m * 2
} while m < 100

print(m)
placeholder38 l10n-placeholder39
128

Anda dapat menyimpan indeks dalam satu lingkaran—baik dengan menggunakan ..< untuk membuat rentang indeks atau dengan menulis inisialisasi, kondisi, dan kenaikan secara eksplisit. Kedua loop ini melakukan hal yang sama:

var total = 0
for i in 0..<4 {
    total += i
}

print(total)
6

Gunakan ..< untuk membuat rentang yang menghilangkan nilai atasnya, dan gunakan ... untuk membuat rentang yang mencakup kedua nilai.

Fungsi dan Penutupan

Gunakan func untuk mendeklarasikan suatu fungsi. Panggil suatu fungsi dengan mengikuti namanya dengan daftar argumen dalam tanda kurung. Gunakan -> untuk memisahkan nama dan tipe parameter dari tipe kembalian fungsi.

func greet(name: String, day: String) -> String {
    return "Hello \(name), today is \(day)."
}
print(greet(name: "Bob", day: "Tuesday"))
Hello Bob, today is Tuesday.

// Experiment:
// Remove the `day` parameter. Add a parameter to include today’s lunch special in the greeting.

Secara default, fungsi menggunakan nama parameternya sebagai label untuk argumennya. Tulis label argumen khusus sebelum nama parameter, atau tulis _ untuk tidak menggunakan label argumen.

func greet(_ person: String, on day: String) -> String {
    return "Hello \(person), today is \(day)."
}
print(greet("John", on: "Wednesday"))
Hello John, today is Wednesday.

Gunakan tuple untuk membuat nilai gabungan—misalnya, untuk mengembalikan beberapa nilai dari suatu fungsi. Elemen tupel dapat dirujuk berdasarkan nama atau nomor.

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)
120
120

Fungsi dapat disarangkan. Fungsi bersarang memiliki akses ke variabel yang dideklarasikan di fungsi luar. Anda dapat menggunakan fungsi bertingkat untuk mengatur kode dalam fungsi yang panjang atau kompleks.

func returnFifteen() -> Int {
    var y = 10
    func add() {
        y += 5
    }
    add()
    return y
}
print(returnFifteen())
15

Fungsi adalah tipe kelas satu. Artinya suatu fungsi dapat mengembalikan fungsi lain sebagai nilainya.

func makeIncrementer() -> ((Int) -> Int) {
    func addOne(number: Int) -> Int {
        return 1 + number
    }
    return addOne
}
var increment = makeIncrementer()
print(increment(7))
8

Suatu fungsi dapat mengambil fungsi lain sebagai salah satu argumennya.

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))
true

Fungsi sebenarnya merupakan kasus khusus dari penutupan: blok kode yang dapat dipanggil nanti. Kode dalam penutupan memiliki akses ke hal-hal seperti variabel dan fungsi yang tersedia dalam lingkup di mana penutupan dibuat, bahkan jika penutupan berada dalam lingkup yang berbeda ketika dijalankan—Anda sudah melihat contohnya dengan fungsi bersarang. Anda bisa menulis penutupan tanpa nama dengan mengapit kode dengan tanda kurung kurawal ( {} ). Gunakan in untuk memisahkan argumen dan tipe kembalian dari isi.

let mappedNumbers = numbers.map({ (number: Int) -> Int in
    let result = 3 * number
    return result
})
print(mappedNumbers)
[60, 57, 21, 36]

// Experiment:
// Rewrite the closure to return zero for all odd numbers.

Anda memiliki beberapa opsi untuk menulis penutupan dengan lebih ringkas. Ketika tipe penutupan sudah diketahui, seperti callback untuk delegasi, Anda bisa menghilangkan tipe parameternya, tipe pengembaliannya, atau keduanya. Penutupan pernyataan tunggal secara implisit mengembalikan nilai dari satu-satunya pernyataannya.

let mappedNumbers2 = numbers.map({ number in 3 * number })
print(mappedNumbers2)
[60, 57, 21, 36]

Anda dapat merujuk ke parameter berdasarkan nomor, bukan berdasarkan nama—pendekatan ini sangat berguna dalam penutupan yang sangat singkat. Penutupan yang diteruskan sebagai argumen terakhir ke suatu fungsi dapat muncul tepat setelah tanda kurung. Jika penutupan adalah satu-satunya argumen pada suatu fungsi, Anda dapat menghilangkan tanda kurung seluruhnya.

let sortedNumbers = numbers.sorted { $0 > $1 }
print(sortedNumbers)
[20, 19, 12, 7]

Objek dan Kelas

Gunakan class diikuti dengan nama kelas untuk membuat kelas. Deklarasi properti dalam suatu kelas ditulis dengan cara yang sama seperti deklarasi konstanta atau variabel, hanya saja deklarasi tersebut berada dalam konteks suatu kelas. Demikian pula, deklarasi metode dan fungsi ditulis dengan cara yang sama.

class Shape {
    var numberOfSides = 0
    func simpleDescription() -> String {
        return "A shape with \(numberOfSides) sides."
    }
}
// Experiment:
// Add a constant property with `let`, and add another method that takes an argument.

Buat sebuah instance kelas dengan memberi tanda kurung setelah nama kelas. Gunakan sintaks titik untuk mengakses properti dan metode instance.

var shape = Shape()
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()

Versi kelas Shape ini kehilangan sesuatu yang penting: penginisialisasi untuk menyiapkan kelas ketika sebuah instance dibuat. Gunakan init untuk membuatnya.

class NamedShape {
    var numberOfSides: Int = 0
    var name: String

    init(name: String) {
        self.name = name
    }

    func simpleDescription() -> String {
        return "A shape with \(numberOfSides) sides."
    }
}

Perhatikan bagaimana self digunakan untuk membedakan properti name dari argumen name ke penginisialisasi. Argumen ke penginisialisasi diteruskan seperti pemanggilan fungsi saat Anda membuat instance kelas. Setiap properti memerlukan nilai yang ditetapkan—baik dalam deklarasinya (seperti numberOfSides ) atau dalam penginisialisasi (seperti name ).

Gunakan deinit untuk membuat deinitializer jika Anda perlu melakukan pembersihan sebelum objek dibatalkan alokasinya.

Subkelas menyertakan nama superkelasnya setelah nama kelasnya, dipisahkan dengan titik dua. Tidak ada persyaratan bagi kelas untuk membuat subkelas dari kelas akar standar apa pun, sehingga Anda dapat menyertakan atau menghilangkan superkelas sesuai kebutuhan.

Metode pada subkelas yang mengesampingkan implementasi superkelas ditandai dengan override —mengganti suatu metode secara tidak sengaja, tanpa override , terdeteksi oleh kompiler sebagai kesalahan. Kompiler juga mendeteksi metode dengan override yang sebenarnya tidak menimpa metode apa pun di superclass.

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)."
    }
}
let test = Square(sideLength: 5.2, name: "my test square")
print(test.area())
print(test.simpleDescription())
27.040000000000003
A square with sides of length 5.2.

// Experiment:
// - Make another subclass of `NamedShape` called `Circle` that takes a radius and a name as
//   arguments to its initializer.
// - Implement an `area()` and a `simpleDescription()` method on the `Circle` class.

Selain properti sederhana yang disimpan, properti dapat memiliki pengambil dan penyetel.

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)."
    }
}
var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle")
print(triangle.perimeter)
triangle.perimeter = 9.9
print(triangle.sideLength)
9.3
3.3000000000000003

Dalam penyetel perimeter , nilai baru memiliki nama implisit newValue . Anda dapat memberikan nama eksplisit dalam tanda kurung setelah set .

Perhatikan bahwa penginisialisasi untuk kelas EquilateralTriangle memiliki tiga langkah berbeda:

  1. Menetapkan nilai properti yang dideklarasikan oleh subkelas.

  2. Memanggil inisialisasi superclass.

  3. Mengubah nilai properti yang ditentukan oleh superclass. Pekerjaan penyiapan tambahan apa pun yang menggunakan metode, pengambil, atau penyetel juga dapat dilakukan pada tahap ini.

Jika Anda tidak perlu menghitung properti tetapi masih perlu menyediakan kode yang dijalankan sebelum dan sesudah menetapkan nilai baru, gunakan willSet dan didSet . Kode yang Anda berikan dijalankan setiap kali nilai berubah di luar penginisialisasi. Misalnya, kelas di bawah memastikan bahwa panjang sisi segitiga selalu sama dengan panjang sisi persegi.

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)
    }
}
var triangleAndSquare = TriangleAndSquare(size: 10, name: "another test shape")
print(triangleAndSquare.square.sideLength)
print(triangleAndSquare.triangle.sideLength)
triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
print(triangleAndSquare.triangle.sideLength)
10.0
10.0
50.0

Saat bekerja dengan nilai opsional, Anda dapat menulis ? sebelum operasi seperti metode, properti, dan berlangganan. Jika nilainya sebelum ? apakah nil , semuanya setelah itu ? diabaikan dan nilai keseluruhan ekspresi adalah nil . Jika tidak, nilai opsional akan dibuka, dan semuanya setelah ? bertindak berdasarkan nilai yang tidak dibungkus. Dalam kedua kasus tersebut, nilai seluruh ekspresi adalah nilai opsional.

let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
print(optionalSquare?.sideLength)
Optional(2.5)

Pencacahan dan Struktur

Gunakan enum untuk membuat enumerasi. Seperti kelas dan semua tipe bernama lainnya, enumerasi dapat memiliki metode yang terkait dengannya.

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
print(ace)
let aceRawValue = ace.rawValue
print(aceRawValue)
ace
1

// Experiment:
// Write a function that compares two `Rank` values by comparing their raw values.

Secara default, Swift menetapkan nilai mentah yang dimulai dari nol dan bertambah satu setiap kali, namun Anda dapat mengubah perilaku ini dengan menentukan nilai secara eksplisit. Dalam contoh di atas, Ace secara eksplisit diberi nilai mentah 1 , dan nilai mentah lainnya diberikan secara berurutan. Anda juga dapat menggunakan string atau angka floating-point sebagai tipe mentah enumerasi. Gunakan properti rawValue untuk mengakses nilai mentah kasus enumerasi.

Gunakan penginisialisasi init?(rawValue:) untuk membuat instance enumerasi dari nilai mentah. Ia mengembalikan kasus enumerasi yang cocok dengan nilai mentah atau nil jika tidak ada Rank yang cocok.

if let convertedRank = Rank(rawValue: 3) {
    let threeDescription = convertedRank.simpleDescription()
}

Nilai kasus dari suatu pencacahan adalah nilai aktual, bukan sekadar cara lain untuk menuliskan nilai mentahnya. Faktanya, jika tidak ada nilai mentah yang berarti, Anda tidak perlu memberikannya.

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()
// Experiment:
// Add a `color()` method to `Suit` that returns "black" for spades and clubs, and returns "red" for
// hearts and diamonds.

Perhatikan dua cara kasus pencacahan Hearts dirujuk di atas: Saat memberikan nilai ke konstanta hearts , kasus pencacahan Suit.Hearts disebut dengan nama lengkapnya karena konstanta tersebut tidak memiliki tipe eksplisit yang ditentukan. Di dalam saklar, kasus pencacahan disebut dengan bentuk singkatan .Hearts karena nilai self sudah diketahui sebagai jas. Anda dapat menggunakan formulir yang disingkat kapan saja tipe nilainya sudah diketahui.

Jika suatu enumerasi memiliki nilai mentah, nilai tersebut ditentukan sebagai bagian dari deklarasi, yang berarti setiap contoh kasus enumerasi tertentu selalu memiliki nilai mentah yang sama. Pilihan lain untuk kasus enumerasi adalah memiliki nilai yang dikaitkan dengan kasus tersebut—nilai ini ditentukan saat Anda membuat instance, dan nilai tersebut dapat berbeda untuk setiap instance kasus enumerasi. Anda dapat menganggap nilai terkait berperilaku seperti properti tersimpan dari contoh kasus enumerasi.

Misalnya, pertimbangkan kasus permintaan waktu matahari terbit dan terbenam dari server. Server merespons dengan informasi yang diminta, atau merespons dengan penjelasan tentang apa yang salah.

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.

// Experiment:
// Add a third case to `ServerResponse` and to the switch.

Perhatikan bagaimana waktu matahari terbit dan terbenam diekstraksi dari nilai ServerResponse sebagai bagian dari pencocokan nilai terhadap kasus saklar.

Gunakan struct untuk membuat struktur. Struktur mendukung banyak perilaku yang sama seperti kelas, termasuk metode dan inisialisasi. Salah satu perbedaan paling penting antara struktur dan kelas adalah bahwa struktur selalu disalin ketika diteruskan dalam kode Anda, namun kelas diteruskan dengan referensi.

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()
// Experiment:
// Write a function that returns an array containing a full deck of cards, with one card of each
// combination of rank and suit.

Protokol dan Ekstensi

Gunakan protocol untuk mendeklarasikan protokol.

protocol ExampleProtocol {
    var simpleDescription: String { get }
    mutating func adjust()
}

Kelas, enumerasi, dan struct semuanya dapat mengadopsi protokol.

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()
print(b.adjust())
print(b.simpleDescription)
()
A simple structure (adjusted)

// Experiment:
// Add another requirement to `ExampleProtocol`.
// What changes do you need to make to `SimpleClass` and `SimpleStructure` so that they still
// conform to the protocol?

Perhatikan penggunaan kata kunci mutating dalam deklarasi SimpleStructure untuk menandai metode yang memodifikasi struktur. Deklarasi SimpleClass tidak memerlukan metode apa pun yang ditandai sebagai bermutasi karena metode pada suatu kelas selalu dapat mengubah kelas tersebut.

Gunakan extension untuk menambahkan fungsionalitas ke tipe yang sudah ada, seperti metode baru dan properti terhitung. Anda bisa menggunakan ekstensi untuk menambahkan kesesuaian protokol ke tipe yang dideklarasikan di tempat lain, atau bahkan ke tipe yang Anda impor dari pustaka atau kerangka kerja.

extension Int: ExampleProtocol {
    public var simpleDescription: String {
        return "The number \(self)"
    }
    public mutating func adjust() {
        self += 42
    }
}
print(7.simpleDescription)
The number 7

// Experiment:
// Write an extension for the `Double` type that adds an `absoluteValue` property.

Anda dapat menggunakan nama protokol sama seperti tipe bernama lainnya—misalnya, untuk membuat kumpulan objek yang memiliki tipe berbeda namun semuanya sesuai dengan satu protokol. Saat Anda bekerja dengan nilai yang tipenya merupakan tipe protokol, metode di luar definisi protokol tidak tersedia.

let protocolValue: ExampleProtocol = a
print(protocolValue.simpleDescription)
A very simple class.  Now 100% adjusted.

// Uncomment to see the error.
// protocolValue.anotherProperty

Meskipun variabel protocolValue memiliki tipe runtime SimpleClass , kompilator memperlakukannya sebagai tipe tertentu dari ExampleProtocol . Ini berarti Anda tidak dapat secara tidak sengaja mengakses metode atau properti yang diimplementasikan oleh kelas tersebut selain kesesuaian protokolnya.

Penanganan Kesalahan

Anda mewakili kesalahan menggunakan jenis apa pun yang mengadopsi protokol Error .

enum PrinterError: Error {
    case outOfPaper
    case noToner
    case onFire
}

Gunakan throw untuk menampilkan error dan throws untuk menandai fungsi yang dapat memunculkan error. Jika Anda memunculkan kesalahan pada suatu fungsi, fungsi tersebut akan segera kembali dan kode yang memanggil fungsi tersebut akan menangani kesalahan tersebut.

func send(job: Int, toPrinter printerName: String) throws -> String {
    if printerName == "Never Has Toner" {
        throw PrinterError.noToner
    }
    return "Job sent"
}

Ada beberapa cara untuk menangani kesalahan. Salah satu caranya adalah dengan menggunakan do-catch . Di dalam blok do , Anda menandai kode yang dapat menimbulkan kesalahan dengan menulis try di depannya. Di dalam blok catch , kesalahan secara otomatis diberi nama error kecuali Anda memberinya nama yang berbeda.

do {
    let printerResponse = try send(job: 1040, toPrinter: "Bi Sheng")
    print(printerResponse)
} catch {
    print(error)
}
Job sent

// Experiment:
// Change the printer name to `"Never Has Toner"`, so that the `send(job:toPrinter:)` function
// throws an error.

Anda dapat menyediakan beberapa blok catch yang menangani kesalahan tertentu. Anda menulis pola setelah catch seperti yang Anda lakukan setelah case di switch.

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:
// Add code to throw an error inside the `do` block.
// What kind of error do you need to throw so that the error is handled by the first `catch` block?
// What about the second and third blocks?

Cara lain untuk menangani kesalahan adalah dengan menggunakan try? untuk mengubah hasilnya menjadi opsional. Jika fungsi memunculkan kesalahan, kesalahan spesifik akan dibuang dan hasilnya adalah nil . Jika tidak, hasilnya adalah opsional yang berisi nilai yang dikembalikan oleh fungsi tersebut.

let printerSuccess = try? send(job: 1884, toPrinter: "Mergenthaler")
let printerFailure = try? send(job: 1885, toPrinter: "Never Has Toner")

Gunakan defer untuk menulis blok kode yang dieksekusi setelah semua kode lain dalam fungsi, tepat sebelum fungsi kembali. Kode dijalankan terlepas dari apakah fungsinya memunculkan kesalahan. Anda dapat menggunakan defer untuk menulis kode pengaturan dan pembersihan secara bersebelahan, meskipun keduanya harus dijalankan pada waktu yang berbeda.

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
}
print(fridgeContains("banana"))
print(fridgeIsOpen)
false
false

Generik

Tulis nama di dalam tanda kurung siku untuk membuat fungsi atau tipe umum.

func makeArray<Item>(repeating item: Item, numberOfTimes: Int) -> [Item] {
    var result = [Item]()
    for _ in 0..<numberOfTimes {
        result.append(item)
    }
    return result
}
print(makeArray(repeating: "knock", numberOfTimes: 4))
["knock", "knock", "knock", "knock"]

Anda dapat membuat bentuk fungsi dan metode umum, serta kelas, enumerasi, dan struktur.

// Reimplement the Swift standard library's optional type
enum OptionalValue<Wrapped> {
    case none
    case some(Wrapped)
}
var possibleInteger: OptionalValue<Int> = .none
possibleInteger = .some(100)
print(possibleInteger)
some(100)

Gunakan where setelah nama tipe untuk menentukan daftar persyaratan — misalnya, untuk mengharuskan tipe tersebut mengimplementasikan suatu protokol, untuk mengharuskan dua tipe harus sama, atau untuk mengharuskan suatu kelas memiliki superkelas tertentu.

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
}
print(anyCommonElements([1, 2, 3], [3]))
true

Penulisan <T: Equatable> sama dengan penulisan <T> ... where T: Equatable .

,

Diadaptasi dari A Swift Tour asli di Swift.org dengan modifikasi. Konten asli ditulis oleh Apple Inc. Berlisensi di bawah Lisensi Creative Commons Attribution 4.0 International (CC BY 4.0) .
Lihat di TensorFlow.org Jalankan di Google Colab Lihat sumber di GitHub

Tradisi menyarankan bahwa program pertama dalam bahasa baru harus mencetak kata-kata "Halo, dunia!" di layar. Di Swift, ini bisa dilakukan dalam satu baris:

print("Hello, world!")
Hello, world!

Jika Anda telah menulis kode dalam C atau Objective-C, sintaksis ini tampak familier bagi Anda—di Swift, baris kode ini adalah program yang lengkap. Anda tidak perlu mengimpor perpustakaan terpisah untuk fungsionalitas seperti input/output atau penanganan string. Kode yang ditulis dalam lingkup global digunakan sebagai titik masuk program, jadi Anda tidak memerlukan fungsi main() . Anda juga tidak perlu menulis titik koma di akhir setiap pernyataan.

Tur ini memberi Anda informasi yang cukup untuk mulai menulis kode di Swift dengan menunjukkan cara menyelesaikan berbagai tugas pemrograman. Jangan khawatir jika Anda tidak memahami sesuatu—semua yang diperkenalkan dalam tur ini dijelaskan secara rinci di sisa buku ini.

Nilai Sederhana

Gunakan let untuk membuat konstanta dan var untuk membuat variabel. Nilai konstanta tidak perlu diketahui pada waktu kompilasi, namun Anda harus menetapkan nilainya tepat satu kali. Ini berarti Anda dapat menggunakan konstanta untuk memberi nama nilai yang Anda tentukan satu kali namun digunakan di banyak tempat.

var myVariable = 42
myVariable = 50
let myConstant = 42

Konstanta atau variabel harus memiliki tipe yang sama dengan nilai yang ingin Anda tetapkan. Namun, Anda tidak selalu harus menulis tipenya secara eksplisit. Memberikan nilai saat Anda membuat konstanta atau variabel memungkinkan kompiler menyimpulkan tipenya. Pada contoh di atas, compiler menyimpulkan bahwa myVariable adalah bilangan bulat karena nilai awalnya adalah bilangan bulat.

Jika nilai awal tidak memberikan informasi yang cukup (atau jika tidak ada nilai awal), tentukan jenisnya dengan menuliskannya setelah variabel, dipisahkan dengan titik dua. Catatan: menggunakan Double daripada Float untuk angka floating point memberi Anda lebih presisi, dan merupakan tipe floating point default di Swift.

let implicitInteger = 70
let implicitDouble = 70.0
let explicitDouble: Double = 70
// Experiment:
// Create a constant with an explicit type of `Float` and a value of 4.

Nilai tidak pernah secara implisit dikonversi ke tipe lain. Jika Anda perlu mengonversi nilai ke tipe lain, buatlah instance tipe yang diinginkan secara eksplisit.

let label = "The width is "
let width = 94
print(label + String(width))
The width is 94

// Experiment:
// Try removing the conversion to `String` from the last line. What error do you get?

Ada cara yang lebih sederhana untuk memasukkan nilai dalam string: Tulis nilai dalam tanda kurung, dan tuliskan garis miring terbalik (``) sebelum tanda kurung. Misalnya:

let apples = 3
print("I have \(apples) apples.")
I have 3 apples.

let oranges = 5
print("I have \(apples + oranges) pieces of fruit.")
I have 8 pieces of fruit.

// Experiment:
// Use `\()` to include a floating-point calculation in a string and to include someone's name in a
// greeting.

Gunakan tiga tanda kutip ganda ( """ ) untuk string yang memuat beberapa baris. Indentasi di awal setiap baris yang dikutip akan dihilangkan, selama cocok dengan indentasi tanda kutip penutup. Misalnya:

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.
    """
print(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 8 pieces of fruit.

Buat array dan kamus menggunakan tanda kurung ( [] ), dan akses elemennya dengan menulis indeks atau kunci dalam tanda kurung. Tanda koma diperbolehkan setelah elemen terakhir.

var shoppingList = ["catfish", "water", "tulips", "blue paint"]
shoppingList[1] = "bottle of water"

var occupations = [
    "Malcolm": "Captain",
    "Kaylee": "Mechanic",
]
occupations["Jayne"] = "Public Relations"
print(occupations)
["Jayne": "Public Relations", "Kaylee": "Mechanic", "Malcolm": "Captain"]

Array secara otomatis bertambah saat Anda menambahkan elemen.

shoppingList.append("blue paint")
print(shoppingList)
["catfish", "bottle of water", "tulips", "blue paint", "blue paint"]

Untuk membuat array atau kamus kosong, gunakan sintaks penginisialisasi.

let emptyArray = [String]()
let emptyDictionary = [String: Float]()

Jika informasi tipe dapat disimpulkan, Anda dapat menulis array kosong sebagai [] dan kamus kosong sebagai [:] — misalnya, saat Anda menetapkan nilai baru untuk variabel atau meneruskan argumen ke suatu fungsi.

shoppingList = []
occupations = [:]

Aliran Kontrol

Gunakan if dan switch untuk membuat kondisional, dan gunakan for - in , for , while , dan repeat - while untuk membuat perulangan. Tanda kurung di sekitar kondisi atau variabel loop bersifat opsional. Diperlukan kawat gigi di sekitar tubuh.

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

Dalam pernyataan if , kondisinya harus berupa ekspresi Boolean—ini berarti kode seperti if score { ... } adalah sebuah kesalahan, bukan perbandingan implisit dengan nol.

Anda dapat menggunakan if dan let bersama-sama untuk menangani nilai-nilai yang mungkin hilang. Nilai-nilai ini direpresentasikan sebagai opsional. Nilai opsional berisi nilai atau berisi nil untuk menunjukkan bahwa ada nilai yang hilang. Tuliskan tanda tanya ( ? ) setelah jenis nilai untuk menandai nilai tersebut sebagai opsional.

var optionalString: String? = "Hello"
print(optionalString == nil)
false

var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
    greeting = "Hello, \(name)"
}
print(greeting)
Hello, John Appleseed

// Experiment:
// Change `optionalName` to `nil`. What greeting do you get?
// Add an `else` clause that sets a different greeting if `optionalName` is `nil`.

Jika nilai opsionalnya adalah nil , kondisinya false dan kode dalam kurung kurawal dilewati. Jika tidak, nilai opsional akan dibuka bungkusnya dan ditetapkan ke konstanta setelah let , yang membuat nilai yang belum dibungkus tersedia di dalam blok kode.

Cara lain untuk menangani nilai opsional adalah dengan memberikan nilai default menggunakan ?? operator. Jika nilai opsional tidak ada, nilai default akan digunakan.

let nickName: String? = nil
let fullName: String = "John Appleseed"
print("Hi \(nickName ?? fullName)")
Hi John Appleseed

Switch mendukung segala jenis data dan beragam operasi perbandingan—tidak terbatas pada bilangan bulat dan pengujian kesetaraan.

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?

// Experiment:
// Try removing the default case. What error do you get?

Perhatikan bagaimana let dapat digunakan dalam suatu pola untuk menetapkan nilai yang cocok dengan bagian pola tersebut ke sebuah konstanta.

Setelah mengeksekusi kode di dalam switch case yang cocok, program keluar dari pernyataan switch. Eksekusi tidak berlanjut ke kasus berikutnya, jadi tidak perlu keluar dari saklar secara eksplisit di akhir setiap kode kasus.

Anda menggunakan for - in untuk mengulangi item dalam kamus dengan memberikan sepasang nama yang akan digunakan untuk setiap pasangan nilai kunci. Kamus adalah kumpulan yang tidak berurutan, sehingga kunci dan nilainya diulangi dalam urutan yang berubah-ubah.

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 (kind, numbers) in interestingNumbers {
    for number in numbers {
        if number > largest {
            largest = number
        }
    }
}
print(largest)
25

// Experiment:
// Add another variable to keep track of which kind of number was the largest, as well as what that
// largest number was.

Gunakan while untuk mengulang blok kode hingga kondisi berubah. Kondisi perulangan bisa berada di akhir, memastikan bahwa perulangan dijalankan setidaknya sekali.

var n = 2
while n < 100 {
    n = n * 2
}

print(n)
128

var m = 2
repeat {
    m = m * 2
} while m < 100

print(m)
placeholder38 l10n-placeholder39
128

Anda dapat menyimpan indeks dalam satu lingkaran—baik dengan menggunakan ..< untuk membuat rentang indeks atau dengan menulis inisialisasi, kondisi, dan kenaikan secara eksplisit. Kedua loop ini melakukan hal yang sama:

var total = 0
for i in 0..<4 {
    total += i
}

print(total)
6

Gunakan ..< untuk membuat rentang yang menghilangkan nilai atasnya, dan gunakan ... untuk membuat rentang yang mencakup kedua nilai.

Fungsi dan Penutupan

Gunakan func untuk mendeklarasikan suatu fungsi. Panggil suatu fungsi dengan mengikuti namanya dengan daftar argumen dalam tanda kurung. Gunakan -> untuk memisahkan nama dan tipe parameter dari tipe kembalian fungsi.

func greet(name: String, day: String) -> String {
    return "Hello \(name), today is \(day)."
}
print(greet(name: "Bob", day: "Tuesday"))
Hello Bob, today is Tuesday.

// Experiment:
// Remove the `day` parameter. Add a parameter to include today’s lunch special in the greeting.

Secara default, fungsi menggunakan nama parameternya sebagai label untuk argumennya. Tulis label argumen khusus sebelum nama parameter, atau tulis _ untuk tidak menggunakan label argumen.

func greet(_ person: String, on day: String) -> String {
    return "Hello \(person), today is \(day)."
}
print(greet("John", on: "Wednesday"))
Hello John, today is Wednesday.

Gunakan tuple untuk membuat nilai gabungan—misalnya, untuk mengembalikan beberapa nilai dari suatu fungsi. Elemen tupel dapat dirujuk berdasarkan nama atau nomor.

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)
120
120

Fungsi dapat disarangkan. Fungsi bersarang memiliki akses ke variabel yang dideklarasikan di fungsi luar. Anda dapat menggunakan fungsi bertingkat untuk mengatur kode dalam fungsi yang panjang atau kompleks.

func returnFifteen() -> Int {
    var y = 10
    func add() {
        y += 5
    }
    add()
    return y
}
print(returnFifteen())
15

Fungsi adalah tipe kelas satu. Artinya suatu fungsi dapat mengembalikan fungsi lain sebagai nilainya.

func makeIncrementer() -> ((Int) -> Int) {
    func addOne(number: Int) -> Int {
        return 1 + number
    }
    return addOne
}
var increment = makeIncrementer()
print(increment(7))
8

Suatu fungsi dapat mengambil fungsi lain sebagai salah satu argumennya.

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))
true

Fungsi sebenarnya merupakan kasus khusus dari penutupan: blok kode yang dapat dipanggil nanti. Kode dalam penutupan memiliki akses ke hal-hal seperti variabel dan fungsi yang tersedia dalam lingkup di mana penutupan dibuat, bahkan jika penutupan berada dalam lingkup yang berbeda ketika dijalankan—Anda sudah melihat contohnya dengan fungsi bersarang. Anda bisa menulis penutupan tanpa nama dengan mengapit kode dengan tanda kurung kurawal ( {} ). Gunakan in untuk memisahkan argumen dan tipe kembalian dari isi.

let mappedNumbers = numbers.map({ (number: Int) -> Int in
    let result = 3 * number
    return result
})
print(mappedNumbers)
[60, 57, 21, 36]

// Experiment:
// Rewrite the closure to return zero for all odd numbers.

Anda memiliki beberapa opsi untuk menulis penutupan dengan lebih ringkas. Ketika tipe penutupan sudah diketahui, seperti callback untuk delegasi, Anda bisa menghilangkan tipe parameternya, tipe pengembaliannya, atau keduanya. Penutupan pernyataan tunggal secara implisit mengembalikan nilai dari satu-satunya pernyataannya.

let mappedNumbers2 = numbers.map({ number in 3 * number })
print(mappedNumbers2)
[60, 57, 21, 36]

Anda dapat merujuk ke parameter berdasarkan nomor, bukan berdasarkan nama—pendekatan ini sangat berguna dalam penutupan yang sangat singkat. Penutupan yang diteruskan sebagai argumen terakhir ke suatu fungsi dapat muncul tepat setelah tanda kurung. Jika penutupan adalah satu-satunya argumen pada suatu fungsi, Anda dapat menghilangkan tanda kurung seluruhnya.

let sortedNumbers = numbers.sorted { $0 > $1 }
print(sortedNumbers)
[20, 19, 12, 7]

Objek dan Kelas

Gunakan class diikuti dengan nama kelas untuk membuat kelas. Deklarasi properti dalam suatu kelas ditulis dengan cara yang sama seperti deklarasi konstanta atau variabel, hanya saja deklarasi tersebut berada dalam konteks suatu kelas. Demikian pula, deklarasi metode dan fungsi ditulis dengan cara yang sama.

class Shape {
    var numberOfSides = 0
    func simpleDescription() -> String {
        return "A shape with \(numberOfSides) sides."
    }
}
// Experiment:
// Add a constant property with `let`, and add another method that takes an argument.

Buat sebuah instance kelas dengan memberi tanda kurung setelah nama kelas. Gunakan sintaks titik untuk mengakses properti dan metode instance.

var shape = Shape()
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()

Versi kelas Shape ini kehilangan sesuatu yang penting: penginisialisasi untuk menyiapkan kelas ketika sebuah instance dibuat. Gunakan init untuk membuatnya.

class NamedShape {
    var numberOfSides: Int = 0
    var name: String

    init(name: String) {
        self.name = name
    }

    func simpleDescription() -> String {
        return "A shape with \(numberOfSides) sides."
    }
}

Perhatikan bagaimana self digunakan untuk membedakan properti name dari argumen name ke penginisialisasi. Argumen ke penginisialisasi diteruskan seperti pemanggilan fungsi saat Anda membuat instance kelas. Setiap properti memerlukan nilai yang ditetapkan—baik dalam deklarasinya (seperti numberOfSides ) atau dalam penginisialisasi (seperti name ).

Gunakan deinit untuk membuat deinitializer jika Anda perlu melakukan pembersihan sebelum objek dibatalkan alokasinya.

Subkelas menyertakan nama superkelasnya setelah nama kelasnya, dipisahkan dengan titik dua. Tidak ada persyaratan bagi kelas untuk membuat subkelas dari kelas akar standar apa pun, sehingga Anda dapat menyertakan atau menghilangkan superkelas sesuai kebutuhan.

Metode pada subkelas yang mengesampingkan implementasi superkelas ditandai dengan override —mengganti suatu metode secara tidak sengaja, tanpa override , terdeteksi oleh kompiler sebagai kesalahan. Kompiler juga mendeteksi metode dengan override yang sebenarnya tidak menimpa metode apa pun di superclass.

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)."
    }
}
let test = Square(sideLength: 5.2, name: "my test square")
print(test.area())
print(test.simpleDescription())
27.040000000000003
A square with sides of length 5.2.

// Experiment:
// - Make another subclass of `NamedShape` called `Circle` that takes a radius and a name as
//   arguments to its initializer.
// - Implement an `area()` and a `simpleDescription()` method on the `Circle` class.

Selain properti sederhana yang disimpan, properti dapat memiliki pengambil dan penyetel.

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)."
    }
}
var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle")
print(triangle.perimeter)
triangle.perimeter = 9.9
print(triangle.sideLength)
9.3
3.3000000000000003

Dalam penyetel perimeter , nilai baru memiliki nama implisit newValue . Anda dapat memberikan nama eksplisit dalam tanda kurung setelah set .

Perhatikan bahwa penginisialisasi untuk kelas EquilateralTriangle memiliki tiga langkah berbeda:

  1. Menetapkan nilai properti yang dideklarasikan oleh subkelas.

  2. Memanggil inisialisasi superclass.

  3. Mengubah nilai properti yang ditentukan oleh superclass. Pekerjaan penyiapan tambahan apa pun yang menggunakan metode, pengambil, atau penyetel juga dapat dilakukan pada tahap ini.

Jika Anda tidak perlu menghitung properti tetapi masih perlu menyediakan kode yang dijalankan sebelum dan sesudah menetapkan nilai baru, gunakan willSet dan didSet . Kode yang Anda berikan dijalankan setiap kali nilai berubah di luar penginisialisasi. Misalnya, kelas di bawah memastikan bahwa panjang sisi segitiga selalu sama dengan panjang sisi persegi.

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)
    }
}
var triangleAndSquare = TriangleAndSquare(size: 10, name: "another test shape")
print(triangleAndSquare.square.sideLength)
print(triangleAndSquare.triangle.sideLength)
triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
print(triangleAndSquare.triangle.sideLength)
10.0
10.0
50.0

Saat bekerja dengan nilai opsional, Anda dapat menulis ? sebelum operasi seperti metode, properti, dan berlangganan. Jika nilainya sebelum ? apakah nil , semuanya setelah itu ? diabaikan dan nilai keseluruhan ekspresi adalah nil . Jika tidak, nilai opsional akan dibuka, dan semuanya setelah ? bertindak berdasarkan nilai yang tidak dibungkus. Dalam kedua kasus tersebut, nilai seluruh ekspresi adalah nilai opsional.

let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
print(optionalSquare?.sideLength)
Optional(2.5)

Pencacahan dan Struktur

Gunakan enum untuk membuat enumerasi. Seperti kelas dan semua tipe bernama lainnya, enumerasi dapat memiliki metode yang terkait dengannya.

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
print(ace)
let aceRawValue = ace.rawValue
print(aceRawValue)
ace
1

// Experiment:
// Write a function that compares two `Rank` values by comparing their raw values.

Secara default, Swift menetapkan nilai mentah yang dimulai dari nol dan bertambah satu setiap kali, namun Anda dapat mengubah perilaku ini dengan menentukan nilai secara eksplisit. Dalam contoh di atas, Ace secara eksplisit diberi nilai mentah 1 , dan nilai mentah lainnya diberikan secara berurutan. Anda juga dapat menggunakan string atau angka floating-point sebagai tipe mentah enumerasi. Gunakan properti rawValue untuk mengakses nilai mentah kasus enumerasi.

Gunakan penginisialisasi init?(rawValue:) untuk membuat instance enumerasi dari nilai mentah. Ia mengembalikan kasus enumerasi yang cocok dengan nilai mentah atau nil jika tidak ada Rank yang cocok.

if let convertedRank = Rank(rawValue: 3) {
    let threeDescription = convertedRank.simpleDescription()
}

Nilai kasus dari suatu pencacahan adalah nilai aktual, bukan sekadar cara lain untuk menuliskan nilai mentahnya. Faktanya, jika tidak ada nilai mentah yang berarti, Anda tidak perlu memberikannya.

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()
// Experiment:
// Add a `color()` method to `Suit` that returns "black" for spades and clubs, and returns "red" for
// hearts and diamonds.

Perhatikan dua cara kasus pencacahan Hearts dirujuk di atas: Saat memberikan nilai ke konstanta hearts , kasus pencacahan Suit.Hearts disebut dengan nama lengkapnya karena konstanta tersebut tidak memiliki tipe eksplisit yang ditentukan. Di dalam saklar, kasus pencacahan disebut dengan bentuk singkatan .Hearts karena nilai self sudah diketahui sebagai jas. Anda dapat menggunakan formulir yang disingkat kapan saja tipe nilainya sudah diketahui.

Jika suatu enumerasi memiliki nilai mentah, nilai tersebut ditentukan sebagai bagian dari deklarasi, yang berarti setiap contoh kasus enumerasi tertentu selalu memiliki nilai mentah yang sama. Pilihan lain untuk kasus enumerasi adalah memiliki nilai yang dikaitkan dengan kasus tersebut—nilai ini ditentukan saat Anda membuat instance, dan nilai tersebut dapat berbeda untuk setiap instance kasus enumerasi. Anda dapat menganggap nilai terkait berperilaku seperti properti tersimpan dari contoh kasus enumerasi.

Misalnya, pertimbangkan kasus permintaan waktu matahari terbit dan terbenam dari server. Server merespons dengan informasi yang diminta, atau merespons dengan penjelasan tentang apa yang salah.

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.

// Experiment:
// Add a third case to `ServerResponse` and to the switch.

Perhatikan bagaimana waktu matahari terbit dan terbenam diekstraksi dari nilai ServerResponse sebagai bagian dari pencocokan nilai terhadap kasus saklar.

Gunakan struct untuk membuat struktur. Struktur mendukung banyak perilaku yang sama seperti kelas, termasuk metode dan inisialisasi. Salah satu perbedaan paling penting antara struktur dan kelas adalah bahwa struktur selalu disalin ketika diteruskan dalam kode Anda, namun kelas diteruskan dengan referensi.

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()
// Experiment:
// Write a function that returns an array containing a full deck of cards, with one card of each
// combination of rank and suit.

Protokol dan Ekstensi

Gunakan protocol untuk mendeklarasikan protokol.

protocol ExampleProtocol {
    var simpleDescription: String { get }
    mutating func adjust()
}

Kelas, enumerasi, dan struct semuanya dapat mengadopsi protokol.

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()
print(b.adjust())
print(b.simpleDescription)
()
A simple structure (adjusted)

// Experiment:
// Add another requirement to `ExampleProtocol`.
// What changes do you need to make to `SimpleClass` and `SimpleStructure` so that they still
// conform to the protocol?

Perhatikan penggunaan kata kunci mutating dalam deklarasi SimpleStructure untuk menandai metode yang memodifikasi struktur. Deklarasi SimpleClass tidak memerlukan metode apa pun yang ditandai sebagai bermutasi karena metode pada kelas selalu dapat memodifikasi kelas.

Gunakan extension untuk menambahkan fungsionalitas ke jenis yang ada, seperti metode baru dan properti yang dihitung. Anda dapat menggunakan ekstensi untuk menambahkan kesesuaian protokol dengan jenis yang dinyatakan di tempat lain, atau bahkan ke jenis yang Anda impor dari perpustakaan atau kerangka kerja.

extension Int: ExampleProtocol {
    public var simpleDescription: String {
        return "The number \(self)"
    }
    public mutating func adjust() {
        self += 42
    }
}
print(7.simpleDescription)
The number 7

// Experiment:
// Write an extension for the `Double` type that adds an `absoluteValue` property.

Anda dapat menggunakan nama protokol seperti jenis bernama lainnya - misalnya, untuk membuat kumpulan objek yang memiliki tipe yang berbeda tetapi semuanya sesuai dengan satu protokol. Saat Anda bekerja dengan nilai -nilai yang tipenya adalah tipe protokol, metode di luar definisi protokol tidak tersedia.

let protocolValue: ExampleProtocol = a
print(protocolValue.simpleDescription)
A very simple class.  Now 100% adjusted.

// Uncomment to see the error.
// protocolValue.anotherProperty

Meskipun variabel protocolValue memiliki jenis runtime dari SimpleClass , kompiler memperlakukannya sebagai jenis ExampleProtocol yang diberikan. Ini berarti bahwa Anda tidak dapat secara tidak sengaja mengakses metode atau properti yang diterapkan kelas di samping kesesuaian protokolnya.

Penanganan Kesalahan

Anda mewakili kesalahan menggunakan jenis apa pun yang mengadopsi protokol Error .

enum PrinterError: Error {
    case outOfPaper
    case noToner
    case onFire
}

Gunakan throw untuk melempar kesalahan dan throws untuk menandai fungsi yang dapat melempar kesalahan. Jika Anda melempar kesalahan dalam suatu fungsi, fungsi segera kembali dan kode yang memanggil fungsi menangani kesalahan.

func send(job: Int, toPrinter printerName: String) throws -> String {
    if printerName == "Never Has Toner" {
        throw PrinterError.noToner
    }
    return "Job sent"
}

Ada beberapa cara untuk menangani kesalahan. Salah satu caranya adalah dengan menggunakan do-catch . Di dalam blok do , Anda menandai kode yang dapat melempar kesalahan dengan menulis mencoba di depannya. Di dalam blok catch , kesalahan secara otomatis diberikan error nama kecuali Anda memberikan nama yang berbeda.

do {
    let printerResponse = try send(job: 1040, toPrinter: "Bi Sheng")
    print(printerResponse)
} catch {
    print(error)
}
Job sent

// Experiment:
// Change the printer name to `"Never Has Toner"`, so that the `send(job:toPrinter:)` function
// throws an error.

Anda dapat memberikan beberapa blok catch yang menangani kesalahan tertentu. Anda menulis pola setelah catch seperti yang Anda lakukan setelah case dalam sakelar.

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:
// Add code to throw an error inside the `do` block.
// What kind of error do you need to throw so that the error is handled by the first `catch` block?
// What about the second and third blocks?

Cara lain untuk menangani kesalahan adalah dengan menggunakan try? untuk mengubah hasilnya menjadi opsional. Jika fungsi melempar kesalahan, kesalahan spesifik dibuang dan hasilnya nil . Jika tidak, hasilnya adalah opsional yang berisi nilai yang dikembalikan fungsi.

let printerSuccess = try? send(job: 1884, toPrinter: "Mergenthaler")
let printerFailure = try? send(job: 1885, toPrinter: "Never Has Toner")

Gunakan defer untuk menulis blok kode yang dieksekusi setelah semua kode lain dalam fungsi, tepat sebelum fungsi kembali. Kode dijalankan terlepas dari apakah fungsi tersebut melakukan kesalahan. Anda dapat menggunakan defer untuk menulis kode pengaturan dan pembersihan di sebelah satu sama lain, meskipun mereka perlu dieksekusi pada waktu yang berbeda.

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
}
print(fridgeContains("banana"))
print(fridgeIsOpen)
false
false

Generik

Tulis nama di dalam kurung sudut untuk membuat fungsi atau jenis generik.

func makeArray<Item>(repeating item: Item, numberOfTimes: Int) -> [Item] {
    var result = [Item]()
    for _ in 0..<numberOfTimes {
        result.append(item)
    }
    return result
}
print(makeArray(repeating: "knock", numberOfTimes: 4))
["knock", "knock", "knock", "knock"]

Anda dapat membuat bentuk fungsi dan metode generik, serta kelas, enumerasi, dan struktur.

// Reimplement the Swift standard library's optional type
enum OptionalValue<Wrapped> {
    case none
    case some(Wrapped)
}
var possibleInteger: OptionalValue<Int> = .none
possibleInteger = .some(100)
print(possibleInteger)
some(100)

Gunakan where setelah jenis nama untuk menentukan daftar persyaratan - misalnya, untuk meminta tipe untuk mengimplementasikan protokol, untuk meminta dua jenis menjadi sama, atau meminta kelas memiliki superclass tertentu.

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
}
print(anyCommonElements([1, 2, 3], [3]))
true

Menulis <T: Equatable> sama dengan menulis <T> ... where T: Equatable .