یک تور سریع

با تغییراتی از تور اصلی A Swift در Swift.org اقتباس شده است. محتوای اصلی توسط Apple Inc. با مجوز Creative Commons Attribution 4.0 International (CC BY 4.0) ایجاد شده است.
مشاهده در TensorFlow.org در Google Colab اجرا شود مشاهده منبع در GitHub

سنت پیشنهاد می کند که اولین برنامه به زبان جدید باید کلمات "سلام، دنیا!" روی صفحه نمایش در Swift، این کار را می توان در یک خط انجام داد:

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

اگر کدی را به زبان C یا Objective-C نوشته‌اید، این نحو برای شما آشنا به نظر می‌رسد—در سوئیفت، این خط کد یک برنامه کامل است. برای عملکردهایی مانند ورودی/خروجی یا مدیریت رشته، نیازی به وارد کردن کتابخانه جداگانه ندارید. کد نوشته شده در دامنه جهانی به عنوان نقطه ورودی برنامه استفاده می شود، بنابراین شما به تابع main() نیاز ندارید. همچنین لازم نیست در انتهای هر عبارت نقطه ویرگول بنویسید.

این تور با نشان دادن نحوه انجام انواع وظایف برنامه نویسی به شما اطلاعات کافی برای شروع نوشتن کد در سوئیفت می دهد. اگر چیزی را متوجه نشدید نگران نباشید - همه چیزهایی که در این تور معرفی شده اند در ادامه این کتاب به تفصیل توضیح داده شده است.

ارزش های ساده

let برای ایجاد یک ثابت و var برای ایجاد یک متغیر استفاده کنید. نیازی نیست که مقدار یک ثابت در زمان کامپایل مشخص شود، اما باید دقیقاً یک بار به آن مقدار اختصاص دهید. این به این معنی است که می توانید از ثابت ها برای نامگذاری مقداری استفاده کنید که یک بار تعیین می کنید اما در بسیاری از مکان ها استفاده می کنید.

var myVariable = 42
myVariable = 50
let myConstant = 42

یک ثابت یا متغیر باید دارای همان نوع مقداری باشد که می خواهید به آن اختصاص دهید. با این حال، همیشه لازم نیست نوع را به صراحت بنویسید. ارائه یک مقدار هنگام ایجاد یک ثابت یا متغیر به کامپایلر اجازه می دهد نوع آن را استنتاج کند. در مثال بالا، کامپایلر استنباط می کند که myVariable یک عدد صحیح است زیرا مقدار اولیه آن یک عدد صحیح است.

اگر مقدار اولیه اطلاعات کافی را ارائه نمی‌کند (یا اگر مقدار اولیه وجود ندارد)، نوع را با نوشتن آن بعد از متغیر، که با دو نقطه از هم جدا شده است، مشخص کنید. توجه: استفاده از Double به جای Float برای اعداد ممیز شناور دقت بیشتری به شما می دهد و نوع ممیز شناور پیش فرض در سوئیفت است.

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.

مقادیر هرگز به طور ضمنی به نوع دیگری تبدیل نمی شوند. اگر نیاز به تبدیل یک مقدار به نوع دیگری دارید، به صراحت یک نمونه از نوع مورد نظر ایجاد کنید.

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?

یک راه ساده تر هم برای گنجاندن مقادیر در رشته ها وجود دارد: مقدار را در پرانتز بنویسید و قبل از پرانتز یک اسلش (``) بنویسید. مثلا:

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.

از سه علامت نقل قول دوتایی ( """ ) برای رشته هایی که چندین خط را اشغال می کنند استفاده کنید. تورفتگی در ابتدای هر خط نقل قول حذف می شود، تا زمانی که با تورفتگی علامت های نقل قول پایانی مطابقت داشته باشد. به عنوان مثال:

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.

آرایه ها و فرهنگ لغت ها را با استفاده از براکت ( [] ) ایجاد کنید و با نوشتن فهرست یا کلید در پرانتز به عناصر آنها دسترسی پیدا کنید. کاما بعد از آخرین عنصر مجاز است.

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"]

با اضافه کردن عناصر، آرایه ها به طور خودکار رشد می کنند.

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

برای ایجاد یک آرایه یا فرهنگ لغت خالی، از دستور اولیه استفاده کنید.

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

اگر می توان اطلاعات نوع را استنباط کرد، می توانید یک آرایه خالی را به عنوان [] و یک فرهنگ لغت خالی را به عنوان [:] بنویسید - برای مثال، زمانی که یک مقدار جدید برای یک متغیر تنظیم می کنید یا یک آرگومان را به یک تابع ارسال می کنید.

shoppingList = []
occupations = [:]

کنترل جریان

برای ایجاد شرط if و switch استفاده کنید و برای ایجاد حلقه for in ، for ، 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)
11

در یک دستور if ، شرطی باید یک عبارت بولی باشد - این بدان معنی است که کدی مانند if score { ... } یک خطا است، نه مقایسه ضمنی با صفر.

می توانید از if و let together برای کار با مقادیری که ممکن است گم شده اند استفاده کنید. این مقادیر به صورت اختیاری نمایش داده می شوند. یک مقدار اختیاری یا حاوی یک مقدار است یا حاوی nil است تا نشان دهد که مقداری از دست رفته است. بعد از نوع مقدار یک علامت سوال ( ? ) بنویسید تا مقدار را به عنوان اختیاری علامت گذاری کنید.

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`.

اگر مقدار اختیاری nil باشد، شرطی false است و کد موجود در پرانتزها نادیده گرفته می شود. در غیر این صورت، مقدار اختیاری باز شده و به ثابت پس از let اختصاص داده می شود، که باعث می شود مقدار بازشده در داخل بلوک کد موجود باشد.

راه دیگر برای مدیریت مقادیر اختیاری، ارائه یک مقدار پیش فرض با استفاده از ?? اپراتور. اگر مقدار اختیاری وجود نداشته باشد، به جای آن از مقدار پیش فرض استفاده می شود.

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

سوئیچ ها از هر نوع داده و طیف گسترده ای از عملیات مقایسه پشتیبانی می کنند - آنها به اعداد صحیح و تست های برابری محدود نمی شوند.

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?

توجه کنید که چگونه let را می توان در یک الگو برای اختصاص مقداری که با آن قسمت از الگو مطابقت دارد به یک ثابت استفاده کرد.

پس از اجرای کد داخل کیس سوئیچ که مطابقت داشت، برنامه از دستور switch خارج می شود. اجرا به مورد بعدی ادامه نمی‌یابد، بنابراین نیازی به خروج صریح از سوییچ در انتهای کد هر مورد نیست.

شما for - in برای تکرار روی موارد در فرهنگ لغت با ارائه یک جفت نام برای استفاده برای هر جفت کلید-مقدار استفاده می کنید. دیکشنری ها مجموعه ای نامرتب هستند، بنابراین کلیدها و مقادیر آنها به ترتیب دلخواه تکرار می شوند.

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.

while برای تکرار یک بلوک کد تا زمانی که یک شرط تغییر کند استفاده کنید. شرط یک حلقه می تواند در انتها باشد و اطمینان حاصل شود که حلقه حداقل یک بار اجرا می شود.

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

print(n)
128

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

print(m)
128

شما می توانید یک شاخص را در یک حلقه نگه دارید—یا با استفاده از ..< برای ایجاد محدوده ای از شاخص ها یا با نوشتن یک مقدار اولیه، شرط و افزایش صریح. این دو حلقه یک کار را انجام می دهند:

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

print(total)
6

از ..< برای ایجاد محدوده ای که مقدار بالایی آن را حذف می کند و از ... برای ایجاد محدوده ای که شامل هر دو مقدار است استفاده کنید.

توابع و بسته شدن

از func برای اعلام یک تابع استفاده کنید. یک تابع را با دنبال کردن نام آن با لیستی از آرگومان های داخل پرانتز فراخوانی کنید. از -> برای جدا کردن نام و انواع پارامترها از نوع بازگشتی تابع استفاده کنید.

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.

به طور پیش فرض، توابع از نام پارامترهای خود به عنوان برچسب برای آرگومان های خود استفاده می کنند. قبل از نام پارامتر یک برچسب آرگومان سفارشی بنویسید، یا برای استفاده از برچسب _ بنویسید.

func greet(_ person: String, on day: String) -> String {
    return "Hello \(person), today is \(day)."
}
print(greet("John", on: "Wednesday"))
Hello John, today is 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)
120
120

توابع را می توان تو در تو قرار داد. توابع تو در تو به متغیرهایی که در تابع خارجی اعلان شده اند دسترسی دارند. شما می توانید از توابع تو در تو برای سازماندهی کد در یک تابع طولانی یا پیچیده استفاده کنید.

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

توابع از نوع درجه یک هستند. این بدان معنی است که یک تابع می تواند تابع دیگری را به عنوان مقدار خود برگرداند.

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

یک تابع می تواند تابع دیگری را به عنوان یکی از آرگومان های خود انتخاب کند.

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

توابع در واقع یک مورد خاص از بسته شدن هستند: بلوک هایی از کد که می توانند بعداً فراخوانی شوند. کد موجود در یک بسته به چیزهایی مانند متغیرها و توابع دسترسی دارد که در محدوده ای که بسته شدن ایجاد شده در دسترس بودند، حتی اگر بسته شدن در حین اجرای آن در محدوده دیگری باشد - شما نمونه ای از این را قبلاً با توابع تو در تو دیدید. می توانید با استفاده از کدهای اطراف با پرانتز ( {} ) یک بسته بدون نام بنویسید. in برای جدا کردن آرگومان ها و نوع بازگشت از بدنه استفاده کنید.

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.

شما چندین گزینه برای نوشتن خلاصه تر دارید. هنگامی که نوع بسته شدن از قبل شناخته شده است، مانند پاسخ تماس برای یک نماینده، می توانید نوع پارامترهای آن، نوع بازگشت آن یا هر دو را حذف کنید. بسته شدن یک بیانیه به طور ضمنی مقدار تنها عبارت خود را برمی گرداند.

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

می‌توانید به جای نام، به پارامترها بر اساس تعداد ارجاع دهید - این رویکرد به‌ویژه در بسته‌های بسیار کوتاه مفید است. بسته شدن به عنوان آخرین آرگومان یک تابع می تواند بلافاصله بعد از پرانتز ظاهر شود. هنگامی که بسته شدن تنها آرگومان یک تابع است، می توانید پرانتزها را به طور کامل حذف کنید.

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

اشیاء و کلاس ها

برای ایجاد کلاس از کلاس و به دنبال آن نام class استفاده کنید. یک اعلان ویژگی در یک کلاس مانند یک اعلان ثابت یا متغیر نوشته می شود، با این تفاوت که در متن یک کلاس است. به همین ترتیب، اعلان‌های متد و تابع به همین ترتیب نوشته می‌شوند.

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.

با قرار دادن پرانتز بعد از نام کلاس، یک نمونه از کلاس ایجاد کنید. برای دسترسی به ویژگی ها و متدهای نمونه از دستور نقطه استفاده کنید.

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

این نسخه از کلاس Shape چیزی مهم را از دست داده است: یک مقدار اولیه برای تنظیم کلاس هنگام ایجاد یک نمونه. از init برای ایجاد یکی استفاده کنید.

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

اگر نیاز به پاکسازی قبل از جابجایی شیء دارید، از deinit برای ایجاد deinitializer استفاده کنید.

زیر کلاس ها شامل نام سوپرکلاس خود بعد از نام کلاس خود می شوند که با یک دونقطه از هم جدا شده اند. هیچ الزامی برای کلاس‌ها برای زیرکلاس کردن هر کلاس ریشه استاندارد وجود ندارد، بنابراین می‌توانید در صورت نیاز یک سوپرکلاس را اضافه یا حذف کنید.

روش‌های روی یک کلاس فرعی که اجرای سوپرکلاس را نادیده می‌گیرند، با override علامت‌گذاری می‌شوند - نادیده گرفتن یک متد به‌طور تصادفی، بدون 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)."
    }
}
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.

علاوه بر خواص ساده ای که ذخیره می شوند، ویژگی ها می توانند یک گیرنده و یک تنظیم کننده داشته باشند.

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

در تنظیم کننده perimeter ، مقدار جدید دارای نام ضمنی newValue است. شما می توانید یک نام صریح در پرانتز پس از set وارد کنید.

توجه داشته باشید که مقداردهی اولیه برای کلاس EquilateralTriangle دارای سه مرحله مختلف است:

  1. تنظیم مقدار خواصی که زیر کلاس اعلام می کند.

  2. فراخوانی اولیه ساز سوپرکلاس.

  3. تغییر مقدار خواص تعریف شده توسط superclass. هر کار راه اندازی اضافی که از روش ها، گیرنده ها یا تنظیم کننده ها استفاده می کند نیز می تواند در این مرحله انجام شود.

اگر نیازی به محاسبه ویژگی ندارید اما همچنان نیاز به ارائه کدی دارید که قبل و بعد از تنظیم یک مقدار جدید اجرا شود، از 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)
    }
}
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

هنگام کار با مقادیر اختیاری، می توانید بنویسید ? قبل از عملیاتی مانند متدها، خصوصیات، و اشتراک. اگر مقدار قبل از ? nil است، همه چیز بعد از ? نادیده گرفته می شود و ارزش کل عبارت nil است. در غیر این صورت، مقدار اختیاری باز می شود و همه چیز بعد از ? بر روی ارزش بدون بسته بندی عمل می کند. در هر دو مورد، مقدار کل عبارت یک مقدار اختیاری است.

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

شمارش ها و ساختارها

از enum برای ایجاد شمارش استفاده کنید. مانند کلاس‌ها و همه انواع نام‌گذاری شده دیگر، شمارش‌ها می‌توانند متدهای مرتبط با آن‌ها را داشته باشند.

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.

به‌طور پیش‌فرض، سوئیفت مقادیر خام را که از صفر شروع می‌شود و هر بار یک افزایش می‌یابد، اختصاص می‌دهد، اما می‌توانید این رفتار را با تعیین صریح مقادیر تغییر دهید. در مثال بالا، به آس به صراحت یک مقدار خام 1 داده شده است و بقیه مقادیر خام به ترتیب تخصیص داده شده اند. همچنین می توانید از رشته ها یا اعداد ممیز شناور به عنوان نوع خام شمارش استفاده کنید. از ویژگی rawValue برای دسترسی به مقدار خام یک مورد enumeration استفاده کنید.

از مقداردهی اولیه init?(rawValue:) برای ساختن نمونه ای از شمارش از یک مقدار خام استفاده کنید. در صورتی که Rank منطبقی وجود نداشته باشد، یا مورد شمارش مطابق با مقدار خام nil برمی‌گرداند.

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

به دو روشی که در بالا به مورد Hearts در شمارش اشاره می‌شود توجه کنید: هنگام اختصاص مقداری به ثابت hearts ، مورد شمارش Suit.Hearts با نام کامل آن نامیده می‌شود زیرا ثابت نوع صریحی ندارد. در داخل سوئیچ، مورد شمارش با شکل اختصاری .Hearts ارجاع داده می شود زیرا ارزش self از قبل به عنوان یک کت و شلوار شناخته شده است. هر زمان که نوع مقدار از قبل مشخص شده باشد، می توانید از فرم اختصاری استفاده کنید.

اگر یک شمارش مقادیر خام داشته باشد، آن مقادیر به عنوان بخشی از اعلان تعیین می شوند، به این معنی که هر نمونه از یک مورد شمارش خاص همیشه مقدار خام یکسانی دارد. انتخاب دیگر برای موارد شمارش، داشتن مقادیر مرتبط با مورد است - این مقادیر زمانی تعیین می‌شوند که شما نمونه را بسازید، و می‌توانند برای هر نمونه از یک مورد شمارش متفاوت باشند. می‌توانید مقادیر مرتبط را به‌عنوان ویژگی‌های ذخیره‌شده نمونه شمارش در نظر بگیرید.

برای مثال، درخواست زمان طلوع و غروب خورشید از یک سرور را در نظر بگیرید. سرور یا با اطلاعات درخواستی پاسخ می دهد، یا با توضیحی درباره اشتباه پاسخ می دهد.

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.

توجه کنید که چگونه زمان طلوع و غروب خورشید از مقدار ServerResponse به عنوان بخشی از تطبیق مقدار با موارد سوئیچ استخراج می شود.

از struct برای ایجاد یک ساختار استفاده کنید. ساختارها از بسیاری از رفتارهای مشابه کلاس ها، از جمله متدها و اولیه سازها پشتیبانی می کنند. یکی از مهم‌ترین تفاوت‌های ساختارها و کلاس‌ها این است که ساختارها وقتی در کد شما ارسال می‌شوند همیشه کپی می‌شوند، اما کلاس‌ها با مرجع ارسال می‌شوند.

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.

پروتکل ها و برنامه های افزودنی

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()
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?

به استفاده از کلمه کلیدی mutating در اعلان SimpleStructure برای علامت گذاری روشی که ساختار را تغییر می دهد توجه کنید. اعلان SimpleClass نیازی به هیچ یک از متدهای آن ندارد که به عنوان جهش‌یافته علامت‌گذاری شوند، زیرا روش‌های روی یک کلاس همیشه می‌توانند کلاس را تغییر دهند.

از extension برای افزودن قابلیت به نوع موجود، مانند روش‌های جدید و ویژگی‌های محاسبه‌شده، استفاده کنید. می‌توانید از یک افزونه برای افزودن انطباق پروتکل به نوعی که در جای دیگر اعلان شده است یا حتی به نوعی که از یک کتابخانه یا چارچوب وارد کرده‌اید استفاده کنید.

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.

شما می توانید از یک نام پروتکل درست مانند هر نوع نامگذاری شده دیگری استفاده کنید - برای مثال، برای ایجاد مجموعه ای از اشیاء که انواع مختلفی دارند اما همه آنها با یک پروتکل منطبق هستند. وقتی با مقادیری کار می کنید که نوع آنها یک نوع پروتکل است، روش های خارج از تعریف پروتکل در دسترس نیستند.

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

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

حتی اگر متغیر protocolValue دارای یک نوع زمان اجرا از SimpleClass باشد، کامپایلر آن را به عنوان نوع داده شده ExampleProtocol در نظر می گیرد. این بدان معنی است که شما نمی توانید به طور تصادفی به متدها یا ویژگی هایی که کلاس علاوه بر انطباق پروتکل آن پیاده سازی می کند، دسترسی پیدا کنید.

رسیدگی به خطا

شما خطاها را با استفاده از هر نوع که از پروتکل Error استفاده می کند، نشان می دهید.

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

از throw برای پرتاب خطا و throws برای علامت گذاری تابعی که می تواند خطا ایجاد کند استفاده کنید. اگر در یک تابع خطایی ایجاد کنید، تابع بلافاصله برمی گردد و کدی که تابع را فراخوانی کرده است، خطا را کنترل می کند.

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

راه های مختلفی برای رسیدگی به خطاها وجود دارد. یک راه استفاده از do-catch است. در داخل بلوک do ، کدی را علامت‌گذاری می‌کنید که با نوشتن try در مقابل آن می‌تواند خطا ایجاد کند. در داخل بلوک catch ، خطا به طور خودکار به error نام داده می شود، مگر اینکه نام دیگری به آن بدهید.

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.

شما می توانید چندین بلوک catch ارائه دهید که خطاهای خاصی را مدیریت می کند. شما یک الگوی بعد از catch می نویسید درست همانطور که بعد از case در یک سوئیچ انجام می دهید.

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?

راه دیگری برای رسیدگی به خطاها استفاده از try? برای تبدیل نتیجه به اختیاری. اگر تابع یک خطا ایجاد کند، خطای خاص کنار گذاشته می شود و نتیجه nil می شود. در غیر این صورت، نتیجه یک اختیاری است که حاوی مقداری است که تابع برگردانده است.

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

از defer برای نوشتن بلوکی از کد استفاده کنید که بعد از همه کدهای دیگر در تابع، درست قبل از بازگشت تابع اجرا می شود. کد بدون توجه به اینکه آیا تابع خطا می دهد یا نه اجرا می شود. می‌توانید از defer برای نوشتن کدهای راه‌اندازی و پاک‌سازی در کنار یکدیگر استفاده کنید، حتی اگر آنها باید در زمان‌های مختلف اجرا شوند.

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

ژنریک ها

برای ایجاد یک تابع یا نوع عمومی، یک نام در داخل پرانتزهای زاویه بنویسید.

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"]

می توانید فرم های عمومی توابع و متدها و همچنین کلاس ها، شمارش ها و ساختارها را بسازید.

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

where بعد از نام نوع برای تعیین لیستی از الزامات استفاده کنید - برای مثال، برای اینکه نوع را برای پیاده‌سازی یک پروتکل، نیاز به یکسان بودن دو نوع یا نیاز به یک کلاس برای داشتن یک سوپرکلاس خاص، بخواهید.

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

نوشتن <T: Equatable> همان نوشتن <T> ... where T: Equatable .