---
title: Swifti kirjutamine
description: Kuidas kirjutada kaasaegset Swifti hästi: väärtustüübid, Swift 6 andmevõistluste turvalisus ja konkurentsus.
category: Design
sublabel: Disainiinseneeria
author: Emil Kowalski
tags: 
license: MIT
source: https://github.com/emilkowalski/skills
---

# Kirjuta Swifti

## Esmane vastus

Kui see oskus esimest korda ilma konkreetse küsimuseta käivitatakse, vasta ainult:

> Olen valmis aitama sul kirjutada kaasaegset Swifti nii, nagu see keel tahab olla kirjutatud.

Ära anna mingit muud teavet enne, kui kasutaja küsimuse esitab.

Kuidas kirjutada Swifti nii, nagu keel tahab olla kirjutatud, ajakohane kuni Swift 6.4-ni.

**Tööriistaahela lähtetase: Swift 6.3** (praegune väljalase 2026. aasta augusti seisuga). Kõik siin kompileerub versioonis 6.3, välja arvatud ⚠-ga märgitu, mis tähistab avaldamata Swift 6.4 funktsioone. Samaaegsuse juhised eeldavad Swift 6.2 mudelit — kui projekt on versioonil 6.1 või vanemal, ei kehti §3 reeglid `async` ja `@concurrent` kohta.

Läbiv mõte: **Swift on järkjärgulise avalikustamise keel. Alusta kõige lihtsamast, kõige staatilisemast ja kõige ühelõimelisemast lahendusest, mis töötab, ning osta dünaamilisust — samaaegsust, viitesemantikat, eksistentsiaale, ebaturvalisi viitasid — ainult seal, kus saad põhjusele osutada.** Iga allpool toodud reegel on selle rakendus.

Modelleeri see vaikevalikute hierarhia. Liigu tasemel allapoole ainult siis, kui oskad põhjuse sõnastada:

| Vajadus | Kasuta | Liigu allapoole ainult siis, kui |
| ------------ | ----------------------- | ---------------------------------------------------------- |
| Andmed | `struct` / `enum` | vajad identiteeti, jagamist või pärilust |
| Abstraktsioon | konkreetne tüüp | sul on tüüpide vahel korduvat koodi |
| Polümorfism | `some P` (geneeriline) | vajad heterogeenset salvestust → `any P` |
| Täitmine | peaaktor, sünkroonne | profileerimine näitab hangumist → `async` → `@concurrent` → `actor` |
| Mälu | `Array`, `String` | profileerimine näitab kulu → `InlineArray`, `Span` |
| Ohutus | turvaline API | C-koostöö või mõõdetud kuum rada → `Unsafe*` |

---

## 1. Modelleeri andmeid väärtustüüpidega

Väärtustüübid on Swiftis vaikimisi valik, mitte erijuhtum.

- **Kasuta vaikimisi `struct`-i ja `enum`-it. Kasuta `class`-i ainult identiteedi, jagatud muudetava oleku, päriluse või ressursi eluea jaoks.** Aknal, andmebaasiühendusel, renderdusmootoris hoitaval olemil on identiteet. `Point`-il, `Drink`-il, `Material`-il ei ole.
- **Vaikimisi `let`; `var` ainult siis, kui muudad.** See on sama distsipliin nagu `some` enne `any`-t ja väärtus enne viidet: alusta kitsalt, laienda põhjusega.
- **Struct, millel on muudetav viitetüüpi omadus, pole ei väärtus ega viide.** Koopiad jagavad objekti; muudatused lekivad koopiate vahel. Kas hoia viidatud tüüp muutumatuna, eksponeeri ainult arvutatud omadusi, mis talle edasi suunavad, või tee sellest `private` salvestatud omadus copy-on-write'i taga.
- **Copy-on-write on viis, kuidas saada välisvaramu _ja_ väärtussemantika.** Mähi final-klass structi sisse ja kontrolli enne muutmist `isKnownUniquelyReferenced(&storage)`; kui see pole unikaalne, kopeeri esmalt. Täpselt nii töötavad `Array`, `String` ja `Dictionary`.
- **Enumid on tööriist „kindla hulga asjade“ ja üksteist välistavate olekute jaoks.** Kui asendad hunniku valikulisi salvestatud omadusi (`isSharing`, `selectedRows`, `shareTarget`) ühe `enum State`-iga, muutuvad kehtetud kombinatsioonid väljendamatuks ja olekumuutus atomaarseks, mitte omaduste kirjutamise jadaks, mille lõpetamine võib ununeda.
- **Väärtuste koostamine annab väärtuse.** Struct, mille kõik salvestatud omadused on väärtustüübid, saab väärtussemantika tasuta — just see teeb tagasivõtmisest, erinevuste leidmisest ja oleku taastamisest üheainsa koodiraja, mitte eraldi raja iga omaduse jaoks.

```swift
struct Material {                       // value semantics preserved
  var roughness: Double
  private var _texture: Texture         // a class

  var color: Color {
    get { _texture.color }
    set {
      if !isKnownUniquelyReferenced(&_texture) { _texture = Texture(copying: _texture) }
      _texture.color = newValue
    }
  }
}
```

**Mittekopeeritavad tüübid** (`~Copyable`) väljendavad ainuomandit: failideskriptor, pangaülekanne, avatud ressurss. Kopeerimise keelamine muudab „seda ei tohi kaks korda käivitada“ väitest kompileerimisveaks ja annab structi `deinit`-ile tähenduse. Märgi lõpetav meetod `consuming`-uks, et kompilaator tõestaks, et see on viimane kasutus. Parameetrite omand muutub selgesõnaliseks: `borrowing` (ainult lugemine, vaikimisi), `consuming` (võtab ära), `inout`/`mutating` (ajutine kirjutusõigus).

---

## 2. Vead ja valikulised väärtused — tee ebaõnnestumise rajad nähtavaks

Swifti veakäsitlus toetub kolmele punktile: veaallikad on märgistatud, et need sind ei üllataks; vead kannavad piisavalt konteksti, et nende põhjal tegutseda; ja **taastatavad vead erinevad programmeerija vigadest**.

- **Taastatav → `throw`. Programmeerija viga → `precondition`/`fatalError`.** Ebaõnnestunud võrgupäring hoiab programmi töös. Piiridest väljas indeks tähendab, et kood on vale ja peab peatuma enne, kui viga turvaprobleemiks muutub.
- **Seotud väärtustega enumid on parimad veatüübid.** `case duplicateFriend(String)` on parem kui `case duplicateFriend` — kontekst ongi kogu mõte.
- **`guard` veatingimuste jaoks**, sest see sunnib väljumisraja peale. `if let` tavalise lahtipakkimise jaoks.
- **Tüübitud viskamine (`throws(MyError)`) on mõeldud sisemistele funktsioonidele, vigu edasi suunavale geneerilisele koodile ja piiratud keskkondadele**, kus `any Error`-i karpimine on liiga kulukas. Avaliku API puhul säilitab tüübitamata `throws` sinu vabaduse veatüüpi hiljem muuta. Pane tähele ühtlustust: `throws` on `throws(any Error)` ja mitteviskav on `throws(Never)` — see võimaldabki `map`-il abstraheerida mõlema üle.
- **Sundlahtipakkimist kasuta ainult seal, kus oskad invariandi sõnastada**, ja eelista paljale `!`-le teatega ebaõnnestuvat `#require`/`precondition`-it.

---

## 3. Samaaegsus: püsi ühelõimelisena, kuni profileerimine ütleb muud

Selle jaotise teevad agendid kõige sagedamini valesti, sest mudel muutus Swift 6.2-s.

Alusta iga rakendust täielikult põhilõimel. Ühelõimeline kood viib kaugele ja enamik rakendusi ei pea sellest kunagi lahkuma.

**Edenemine järjekorras. Ära jäta samme vahele.**

1. **Ühelõimeline peaaktoril.** Samaaegsust üldse pole. Sobib enamikule rakendustele.
2. **`async`/`await`** latentsuse varjamiseks (võrk, ketas). Ikka veel pole sul omaenda samaaegsust — SDK API-d nagu `URLSession.data(from:)` tõstavad töö sinu eest mujale.
3. **`@concurrent`**, et tõsta _sinu_ kulukas töö põhilõimelt ära — ainult pärast seda, kui Instruments näitab hangumist.
4. **`actor`**, et tõsta _olek_ peaaktorilt ära — ainult siis, kui liiga palju peaaktori olekut sunnib ülesandeid pidevalt tagasi hüppama.

**Lülita esmalt sisse õiged ehitusseaded.** Lülita igas projektis sisse **Approachable Concurrency**. Rakendusmoodulite ja kasutajaliidesega seotud moodulite puhul määra ka **Default Actor Isolation** väärtuseks **MainActor** — see on Xcode 26-s uute rakendusprojektide vaikeväärtus ja kustutab enamiku sinu `@MainActor` annotatsioonidest. Paketis: `swiftSettings: [.defaultIsolation(MainActor.self)]`. **Ära määra main-actor-by-default'i üldotstarbelisele teegile** — teegid peaksid tarnima `nonisolated` API-sid ja laskma klientidel otsustada, kus töö jookseb.

### Reegel, mis muutus

**Swift 6.2-s ei vii funktsiooni märkimine `async`-iks seda praeguselt aktorilt _ära_.** See jookseb seal, kust see välja kutsuti. Just see teeb „kõige loomulikumalt kirjutatava koodi“ vaikimisi andmevõidujooksudest vabaks.

- **`@concurrent`** — lülitub alati samaaegsele lõimekogumile. Kasuta seda _oma_ protsessorimahuka töö jaoks.
- **`nonisolated`** — jookseb seal, kust see välja kutsutakse. **See on teegi API-de jaoks õige vaikevalik**, sest otsustab väljakutsuja. `nonisolated` tüübil muudab kõik selle liikmed nonisolated'iks (Swift 6.1+).
- Kumbki mitte — jääb väljakutsuja aktorile.

```swift
nonisolated struct PhotoProcessor {          // decoupled from the main actor
  @concurrent                                // guaranteed to run in the background
  func process(_ data: Data) async -> ProcessedPhoto {
    async let sticker = extractSticker(data)  // two independent jobs, in parallel
    async let colors  = extractColors(data)
    return await ProcessedPhoto(sticker: sticker, colors: colors)
  }
}
```

- **Profileeri enne, kui tööd mujale tõstad.** Kasuta Instrumentsi (Time Profiler, hangumised). Kui koodi saab ilma samaaegsuseta kiiremaks teha, tee alati esmalt seda. Samaaegsusel on tegelik hind — ülesannete eraldamine, ajastamine ja mõtlemiskoormus.
- **Ära käivita ülesannet tühise töö jaoks.** Alamülesanne `UserDefaults` väärtuse lugemiseks maksab rohkem, kui see säästab.
- **Üks ülesanne otsast lõpuni toimingu kohta.** Töö, mis peab toimuma järjekorras, läheb _ühte_ ülesandesse; sõltumatud toimingud saavad eraldi ülesanded, et käituskeskkond saaks neid vaheldumisi täita.
- **`await` on peatamispunkt ja see lõhub atomaarsuse.** Olek võib peatatud olekus olles muutuda ja võid jätkata teisel lõimel. Kontrolli eeldusi pärast iga `await`-i uuesti. Ära kunagi hoia lukku üle selle. Ära kunagi toetu üle selle lõimelokaalsele salvestusele.

### Aktori taassisenemine

Aktorid tagavad vastastikuse välistamise, mitte tehingud. Kahe `await`-i vahel samal aktoril jookseb muu töö.

- **Muuda aktori olekut sünkroonsetes meetodites.** Sünkroonne kood aktoril jookseb katkestamatult lõpuni — see on sinu tehingupiir.
- **Hoia asünkroonsed aktorimeetodid õhukesed**, koosnevana sünkroonsetest tehingulistest toimingutest, ja jäta aktor iga `await`-i juures järjepidevasse olekusse.
- Klassikaline viga: kontrolli vahemälu → `await` allalaadimine → kirjuta vahemällu. Kaks ülesannet mõlemad ei leia, mõlemad laadivad alla, teine kirjutab esimese üle. Kontrolli pärast `await`-i uuesti või dedubleeri pooleliolev töö.
- **Aktorid ei ole FIFO.** Nad täidavad esmalt kõrgeima prioriteediga tööd, just selleks, et vältida prioriteedi ümberpööramist. Kui vajad järjestust, kasuta ülesannet (mis jookseb algusest lõpuni) või `AsyncStream`-i, mitte aktorit.

---

## 4. Sendable ja andmete jagamine

`Sendable` märgib tüübi, mida on ohutu isolatsioonidomeenide vahel jagada. Kompilaator kontrollib seda iga ülesande ja aktori piiril.

- **Väärtustüübid on `Sendable`, kui nende salvestus on seda** — mitteavalike tüüpide puhul tuletatakse see automaatselt. **Avalikele tüüpidele ei tuletata sendability't kunagi**: avaliku tüübi märkimine `Sendable`-iks on lubadus sinu klientidele, seega paneb Swift sind seda ise kirjutama.
- **Aktorid ja `@MainActor` klassid on kaudselt `Sendable`**, sest nende olek on isoleeritud.
- **Enamik mudeliklasse ei peaks olema ei `@MainActor` ega `Sendable`.** Hoia need teadlikult mitte-`Sendable`-ina — see hoiab ära olukorra, kus pool mudelist muudetakse põhilõimel ja teine pool taustal. Kui need peavad peaaktorilt lahkuma, tee need `nonisolated`-iks, mitte `Sendable`-iks.
- **Mitte-`Sendable` objekti saab domeenide vahel siiski _saata_**, kui saatja lõpetab selle kasutamise. Tee kõik muudatused _enne_ üleandmist; selle puudutamine pärast seda ongi viga.
- Ka sulundid haaravad olekut. Märgi funktsioonitüüp `@Sendable`-iks ainult siis, kui see tõesti domeene ületab.
- **`@unchecked Sendable` on lubadus, mida kompilaator kontrollida ei saa.** Jäta see tüüpidele, millel on tõeline sisemine sünkroniseerimine (`Mutex`, lukk). Sama kehtib `nonisolated(unsafe)` kohta globaalsel muutujal — viimane abinõu, mitte hoiatuste vaigistaja.

**Kui saad andmevõidujooksu vea, liigu läbi see loend:**

1. **Ära jaga seda.** Tõsta jagatud objekt lokaalsesse muutujasse, et iga samaaegne töö saaks oma eksemplari. (See on lahendus valdavale enamikule tegelikest vigadest.)
2. **Tee sellest `Sendable` väärtustüüp**, nii et „jagamine“ on tegelikult kopeerimine.
3. **Isoleeri see aktorile** — peaaktorile või omaenda aktorile.
4. Alles siis võta kasutusele `Mutex`/`Atomic` moodulist `Synchronization` (salvesta need `let`-omadustesse) või `@unchecked Sendable`.

**Globaalsed ja staatilised muutujad on kõige levinum veaallikas.** Eelistuse järjekorras: tee sellest `let`; pane see `@MainActor`-ile; mähi see `Mutex`-isse; `nonisolated(unsafe)`. Pane tähele, et Swiftis initsialiseeritakse globaalsed muutujad laisalt _ja_ atomaarselt — erinevalt C-st.

**Vanade tagasikutse-API-de sildamine:** annoteeri delegaatprotokollid `@MainActor`-iga, kui need on sinu omad. Kui ei ole, märgi meetod `nonisolated`-iks ja kasuta `MainActor.assumeIsolated { }` — see kinnitab, mitte ei hüppa, nii et see kukub valjult kokku, selle asemel et vaikselt võidu joosta. `@preconcurrency` vastavusel on sama asja lühivorm. Kasuta `@preconcurrency import`-i, et ajutiselt vaigistada sendability hoiatusi moodulist, mis pole veel migreeritud; hoiatused tulevad — õigustatult — tagasi, kui see on tehtud.

---

## 5. Struktureeritud samaaegsus

Eelista alati struktureeritud ülesandeid.

Struktureeritud ülesannetel (`async let`, ülesandegrupid) on lokaalsete muutujate sarnane skoop: need ei saa plokist kauem elada, neid oodatakse automaatselt ja need pärivad ülesandepuu kaudu tühistamise, prioriteedi ja ülesandelokaalsed väärtused. Struktureerimata ülesanded (`Task { }`, `Task.detached`) ei anna sulle midagi sellest automaatselt.

- **`async let`** fikseeritud, staatiliselt teadaoleva arvu samaaegsete alamülesannete jaoks.
- **`withTaskGroup`**, kui arv on dünaamiline. Ülesandegrupid vastavad `AsyncSequence`-ile — itereeri tulemusi nende saabumise järjekorras. Kasuta **`withDiscardingTaskGroup`**-i, kui alamülesanded ei tagasta midagi: see vabastab iga alamülesande ressursid kohe ja tühistab esimese vea korral õdeülesanded.
- **`Task { }`** ainult siis, kui töö eluiga ei mahu skoobi sisse — reageerimine delegaadi tagasikutsele, nupuvajutusele, vaate ilmumisele. See pärib aktori isolatsiooni ja prioriteedi; tühistamist pead ise haldama.
- **`Task.detached`** peaaegu mitte kunagi. See ei päri midagi — ei isolatsiooni, prioriteeti ega ülesandelokaalseid väärtusi. Kui vajad lahtiühendatud juurt, pane ülesandegrupp selle _sisse_, selle asemel et korduvalt lahti ühendada.

**Tühistamine on koostööpõhine.** Tühistamine seab lipu; see ei peata midagi. Kontrolli `Task.isCancelled` või `try Task.checkCancellation()` **enne kuluka töö alustamist**, ka sünkroonsetes abifunktsioonides. Töö puhul, mis on pigem peatatud kui jookseb (`AsyncSequence`'i `next()`), kasuta `withTaskCancellationHandler`-it — ja pea meeles, et käsitleja jookseb kohe ja kehaga samaaegselt, nii et olek, mida see puudutab, vajab tõelist sünkroniseerimist (atomaarset muutujat või lukku, mitte aktorit — aktoril ei saa järjestust tagada).

**Piira oma samaaegsust.** Ära hargne piiramatu loendi peal iga elemendi kohta üheks alamülesandeks. Käivita N alamülesannet ja lisa uus iga kord, kui mõni lõpetab.

**Ülesandelokaalsed väärtused** (`@TaskLocal`) levitavad konteksti — päringu ID-d, jälituslõiku — ülesandepuus allapoole, ilma et peaksid parameetrit läbi iga signatuuri vedama. Tee need valikuliseks, et sidumata lugemistel oleks mõistlik vaikeväärtus.

**Tagasikutsete sildamine:** `withCheckedContinuation` / `withCheckedThrowingContinuation`. Leping on **jätka täpselt üks kord igal rajal** — jätkamata jätmine paneb väljakutsuja igaveseks ootama; kaks korda jätkamine on fataalne viga. Hiljem käivituvate delegaat-API-de puhul salvesta jätk ja sea see jätkamisel nil-iks. (Swift 6.4 — avaldamata — lisab tüübi `Continuation`, mis kontrollib ühekordset jätkamist kompileerimise ajal.)

**`AsyncSequence`:** itereeri `for await` / `for try await` abil. Kohanda olemasolev käsitleja- või delegaadipõhine API `AsyncStream` / `AsyncThrowingStream` abil — loo allikas sulundi sees, tee käsitlejast `yield` ja korista `onTermination`-is.

---

## 6. Samaaegsus SwiftUI-s

- **`View` on `@MainActor`-isoleeritud** ja nii on ka kõik, mida see sisaldab, sealhulgas sinu `@State`. Peaaegu kunagi pole vaja kirjutada `@MainActor` vaatele või vaatemudelile — ja main-actor-by-default'iga saad olemasolevad kustutada.
- **SwiftUI käivitab teadlikult osa sinu koodist põhilõimest väljaspool**, et kaadrid odavad püsiksid. Signaal on `@Sendable` API signatuuris: `visualEffect`, `Shape.path(in:)`, `Layout` nõuded, `onGeometryChange`. Kui saad mõne sellise sulundi sees isolatsioonivea, **ära saada `self`-i — kopeeri üks vajalik väärtus sulundi haaramisloendisse.**

```swift
.visualEffect { [pulse] effect, proxy in    // copy the Bool, don't capture self
  effect.blur(radius: pulse ? 2 : 0)
}
```

- **SwiftUI tegevuste tagasikutsed on meelega sünkroonsed.** Ajatundlikud kasutajaliidese uuendused — animatsiooni käivitamine vastuseks žestile või kerimissündmusele — peavad toimuma sündmusega samas kaadris. Pane `withAnimation` olekumuutus sünkroonsesse tagasikutsesse; ava `Task` ainult sellele järgneva pikaajalise töö jaoks.
- **Pane olekutükk kasutajaliidese ja asünkroonse töö vahelisele õmblusele.** Vaade käivitab ülesande; asünkroonne kiht teeb lõpetamisel sünkroonse muudatuse; kasutajaliides reageerib. See hoiab vaateloogika sünkroonsena ja teeb asünkroonse loogika testitavaks ilma SwiftUI-d importimata.

---

## 7. Protokollid ja geneerikud

Ära alusta klassist. **Ära alusta ka protokollist.**

Töövoog: **kirjuta konkreetsed tüübid → märka nendevahelist korduvat koodi → tõsta ühine võimekus protokolli → kirjuta selle vastu geneeriline kood.** Peaaegu identsete kehadega ülelaadimised on signaal, et on aeg üldistada.

- **Protokoll ilma tüübipõhise kohandamiseta on raisatud protokoll.** Kui iga vastavus kasutaks sama vaikeimplementatsiooni, kirjuta selle asemel olemasolevale protokollile piiratud laiendus. Keerukad protokollihierarhiad („tüübizooloogia“) maksavad kompileerimisaega ja binaarfaili suurust ega anna midagi.
- **Eelista „omab“ suhet „on“ suhtele.** Kui vaid osa protokolli toimingutest on sinu tüübi jaoks mõttekad, ära seda täpsusta — mähi see geneerilisse structi ja eksponeeri täpselt see API, mida mõtled. (`GeometricVector<Storage: SIMD>`, mitte `GeometricVector: SIMD`.)
- **Protokolli nõue on kohanduspunkt** — see saadetakse dünaamiliselt ja vastava tüübi implementatsioon võidab kõikjal. **Ainult laienduses olev meetod saadetakse staatiliselt**, nii et vastava tüübi versioon _varjutab_ seda, mitte ei kirjuta üle, ja kood, mis teab ainult `any P`-d, kutsub välja laienduse oma. Kui tüüp peaks saama midagi kohandada, tee sellest nõue.
- **Kompositsioon päriluse asemel.** Klassipärilus on monoliitne (üks ülemklass), pealetükkiv (pärid salvestatud omadused ja initsialiseerija keerukuse) ja jätab kirjutamata lepingud selle kohta, mida tohib üle kirjutada ja millal kutsuda super. Koosta selle asemel väikestest väärtustest.
- **Sunnitud allateisendus on koodilõhn** — tavaliselt tähendab see, et tüübisuhe läks klassihierarhia või eksistentsiaali tõttu kaduma.

### `some` vs `any`

- **Kirjuta vaikimisi `some P`. Muuda see `any P`-ks, kui pead salvestama suvalisi tüüpe.** Sama distsipliin nagu `let` enne `var`-i.
- `some P` — üks fikseeritud aluseks olev tüüp skoobi kohta. Säilitad kõik tüübisuhted, sealhulgas seotud tüübid, ja kompilaator saab spetsialiseerida.
- `any P` — tüübikustutatud karp, dünaamiline tüüp varieerub käitusajal. Vajalik heterogeensete kogumite, aluseks oleva tüübi valikulisuse ja abstraktsiooni täielikuks peitmiseks. Selle eest maksad: seotud tüüpide suhted kustutatakse nende ülempiirideni ja väljakutsed on optimeerijale läbipaistmatud.
- **`any P` peal ei saa välja kutsuda meetodit, mis võtab seotud tüübi.** Kustutamine töötab tootvas positsioonis (tulemus kustutatakse ülempiirini), aga mitte tarbivas. Lahendus on anda eksistentsiaal edasi funktsioonile, mis võtab `some P` — kompilaator pakib selle lahti ja selles skoobis on tüüp jälle fikseeritud.
- **Piiratud eksistentsiaalid ja läbipaistmatud tüübid** — `some Collection<Element>`, `any Collection<any Animal>` — lasevad peita `LazyFilterSequence<[Animal]>`-i, eksponeerides samas elemendi tüüpi. Deklareeri oma protokollidele esmased seotud tüübid (`protocol Container<Item>`) tüübi jaoks, mille väljakutsujad tegelikult annavad, mitte implementatsiooni detailide jaoks nagu `Iterator`.
- **Sama-tüübi nõuded `where`-klauslites** on viis, kuidas protokollide vahelisi suhteid kinnitada (`where Self.CropType.FeedType == Self`). Ilma nendeta ei läbi „kasvata, siis lõika“ tüübikontrolli ja valed vastavused kompileeruvad.

---

## 8. API disain — selgus kasutuskohas

Selgus kasutuskohas on eesmärk, mis on siin kõigist teistest tähtsam.

- **Ainult Swifti API-des ei kasutata tüübieesliiteid.** Moodulid eristavad. Hoia eesliiteid ainult seal, kus API peegeldab Objective-C oma. Kuid väldi konkreetsetest raamistikest pärit väga üldisi nimesid — need loevad kontekstist väljas halvasti ja sunnivad käsitsi eristama.
- **Jäta ära eesolev `get`** asünkroonsetest alternatiividest ja kõigest, mis tagastab tulemuse otse. `persistentPosts`, mitte `getPersistentPosts`.
- **Juurdepääsukontroll on dokumentatsioon.** `private` (fail), `internal` (moodul ja vaikimisi), `package`, `public`. Piiril selgesõnaline olemine sunnib tegema ülaltoodud sendability ja API arengu otsused.
- **Disaini mudel nii, et lubamatuid olekuid ei saaks välja kirjutada.** Privaatsed setterid koos valideeriva muteeriva meetodiga; enumid suletud hulkade jaoks; tugevalt tüübitud `UUID` `String`-i asemel.
- **Omadusemähised** tõstavad välja _ligipääsupoliitika_ (`@Argument`, `@Published`, kaitsev kopeerimine, laisk, lõimelokaalne), nii et deklaratsioonikoht väljendab poliitikat ühe sõnaga. Kombineeri `@dynamicMemberLookup`-iga võtmeteel, et projitseerida läbi mähise (nii töötab `$binding.title`).
- **Tulemuseehitajad** deklaratiivsete DSL-ide jaoks. **Makrod**, kui šabloonkood on kood, mille kompilaator oleks võinud ise kirjutada (§12).

---

## 9. Jõudlus — mõõda, siis vali

Madala taseme Swifti jõudlust domineerivad neli kulu. Tea, millist neist maksad.

1. **Funktsioonikutsed** — argumentide koopiad, staatiline vs dünaamiline saatmine, kutsekaadri eraldamine ja blokeeritud optimeerimine.
2. **Mälupaigutus** — reasisene vs välisvaramu; dünaamilise suurusega tüübid.
3. **Eraldamine** — globaalne (tasuta), pinu (odav: üks lahutamine), kuhi (kallis: otsing pluss lukustamine).
4. **Koopiad** — retain'id/release'id ja rekursiivsed structi koopiad.

**Aga tee esmalt algoritmiline töö.** Iga kord, kui kirjutad tsükli, proovi see asendada algoritmi väljakutsega. Suurimad võidud pole peaaegu kunagi mikrooptimeerimised:

- **Tea, milline on väljakutsutava keerukus.** `Array.remove(at:)` on O(n); selle tsüklis väljakutsumine on O(n²). `removeAll(where:)` on kokku O(n). `Data` koostamine baidi kaupa ümberlõikamise teel on O(n²); `popFirst()` on O(1). Mõlemad olid 100×+ regressioonid, mis peitusid puhta välimusega koodi taga.
- **Aheldatud `map`/`flatMap`/`filter` eraldavad iga etapi jaoks massiivi.** Elegantne ≠ kiire. Kui konveier jookseb kuumas tsüklis iga piksli või elemendi kohta, määra väljundi suurus üks kord ja kirjuta sellesse.
- **Seejärel profileeri.** Instrumentsi Time Profiler ja Allocations, käivitatuna _testi_ vastu (paremklõps testi käivitusnupul → Profile), et mõõdaksid täpselt seda koodi, mis sulle oluline on. Kui leekgraafikut domineerib `platform_memmove`, tähendab see juhuslikku kopeerimist; miljon lühiajalist eraldust tähendab vahemassiive; `swift_beginAccess` tähendab käitusaegseid eksklusiivsuse kontrolle; `swift_retain`/`swift_release` tähendab viiteloenduse liiklust.

**Konkreetsed hoovad, umbes selle järjekorras, mida need annavad:**

- **`final` klassidel, mida sa ei kavatse alamklassida**, muudab dünaamilise saatmise staatiliseks ja võimaldab sisseehitamist (inlining). Terve mooduli optimeerimine laseb kompilaatoril paljudel juhtudel seda sinu eest tõestada — ja võimaldab geneerilist spetsialiseerimist, mille puhul geneerikud lakkavad midagi maksmast.
- **Structi salvestus on reasisene; klassi salvestus on välisvaramus.** Väikesed structid on tasuta; suur struct kolme viitetüüpi väljaga maksab kolm retain'i _koopia kohta_, klass aga ühe. Kui kopeerid seda palju, kasuta copy-on-write'i.
- **`any P` eksistentsiaalil on 3-sõnaline reasisene puhver.** Väärtused, mis sinna mahuvad, elavad reasiseselt; suuremad eraldatakse iga koopia jaoks kuhjas. Kehtib sama tehnika: anna suurele tüübile kaudne salvestus copy-on-write'iga ja see mahub jälle puhvrisse.
- **Homogeenne `[MyModel]` on parem kui `[any Model]`** — tihedalt pakitud, tüübiinfo antakse üks kord, spetsialiseeritav. `[any Model]` on paindlik, kuid läbipaistmatu valik; kasuta seda, kui seda vajad.
- **Geneerilise parameetri piiramine klassiga** (`T: AnyObject`) annab kompilaatorile teadaoleva esituse ka ilma spetsialiseerimiseta.
- **`InlineArray<N, T>`** (Swift 6.2) fikseeritud suurusega salvestuse jaoks: elemendid salvestatakse reasiseselt, suurus on tüübis väärtusgeneerikute kaudu, kuhjaeraldust pole, viiteloendust pole, unikaalsuse ega eksklusiivsuse kontrolle pole. Vale valik, kui seda kopeeritakse või jagatakse.
- **`Span` / `RawSpan` / `OutputSpan`** (Swift 6.2) asendavad `withUnsafeBufferPointer`-i otseseks ligipääsuks järjestikusele salvestusele. Need on mittepõgenevad, nii et kompilaator seob nende eluea konteineriga — saad viida jõudluse ilma elueavigadeta ja retain'id/release'id kaovad.
- **Salvestatud omaduste tõstmine pesastatud klassist vanem-structi** eemaldab käitusaegsed eksklusiivsuse kontrollid.
- Tarnitud Swift 6.3-s, kui oled vajaduse mõõtnud: `@inline(always)` (paari meetoditel `final`-iga) ja `@specialized(where T == ...)` (SE-0460), et geneerilist eelnevalt kuumade konkreetsete tüüpide jaoks spetsialiseerida.
- Saabumas Swift 6.4-s (**avaldamata** — vaata märkust §15 all): `borrow`/`mutate` aksessorid `get`/`set` asemel suurte salvestatud väärtuste jaoks, `UniqueArray`/`UniqueBox` ning `Ref`/`MutableRef`, et korduv otsing tsüklist välja tõsta.

**Asünkroonsed funktsioonid** hoiavad oma olekut ülesandepõhises plokieraldajas, mitte C-pinus, ja jagunevad igas peatamispunktis osafunktsioonideks. Kuluprofiil sarnaneb sünkroonsete funktsioonidega, kutse lisakulu on veidi suurem — mis on veel üks põhjus mitte teha `async`-iks midagi, millel pole midagi oodata.

**Hüpped peaaktorile ja sealt tagasi maksavad tõelise kontekstivahetuse.** Koonda: lükka tsükkel funktsioonide `loadArticles`/`updateUI` _sisse_, nii et need võtavad massiive, selle asemel et iga iteratsiooni kohta kaks korda hüpata.

---

## 10. ARC ja objekti eluiga

- **Objekti garanteeritud eluiga lõpeb viimase kasutusega, mitte sulgeva looksuluga.** Täheldatud elueaid kujundab optimeerija ja need _muutuvad_. Kood, mis sõltub sellest, millal `deinit` jookseb, on varjatud viga.
- **`weak`/`unowned` on viitetsüklite katkestamiseks — mitte millekski muuks.** `weak` viite lugemine pärast tugeva omaniku viimast kasutust võib õigustatult anda `nil`. Valikuline sidumine on seal _halvem_ kui sundlahtipakkimine: see muudab valju krahhi vaikseks valeks vastuseks.
- **Parem kui `weak`: ära ehita tsüklit.** Tõsta jagatud andmed kolmandasse tüüpi, millele mõlemad pooled viitavad, muutes tsükli puuks.
- **Järgmine parim: disaini API ümber**, nii et objekt on kättesaadav ainult tugeva viite kaudu. `withExtendedLifetime` töötab, kuid paneb korrektsuse sinu kanda ja levib koodibaasis — käsitle seda paiga, mitte disainina.
- **Hoia `deinit`-i kõrvalmõjud lokaalsed.** Mõõdikute avaldamine või globaalse efekti käivitamine `deinit`-ist järjestub optimeerija otsuste vastu. Kasuta selle asemel kutsekohas `defer`-it ja jäta `deinit` kontrollimiseks.
- Xcode'i ehitusseade **Optimize Object Lifetimes** lühendab täheldatud elueaid garanteeritud miinimumi suunas ja toob esile just need vead.

---

## 11. Testimine — vaikimisi Swift Testing

Kasuta uute testide jaoks **Swift Testingut**. XCTest on endiselt vajalik täpselt kolme asja jaoks: kasutajaliidese automatiseerimine (`XCUIApplication`), jõudlusmõõdikud (`XCTMetric`) ja testid, mis peavad olema kirjutatud Objective-C-s või mis püüavad Objective-C erandeid.

- **`@Test` mis tahes funktsioonil** — globaalne, staatiline või eksemplari oma; `async`, `throws` ja globaalse aktoriga isoleeritud töötavad kõik.
- **`#expect(...)` võtab tavalisi avaldisi.** Pole `XCTAssertEqual`-stiilis funktsioonide perekonda, mida pähe õppida — `#expect(a == b)`, `#expect(list.isEmpty)`, `#expect(!x.contains(y))` kõik haaravad ja kuvavad ebaõnnestumisel alamavaldiste väärtusi.
- **`try #require(...)`**, et peatada test ebaõnnestumisel ja pakkida valikuline väärtus ohutult lahti. See asendab `continueAfterFailure = false` ja laseb valida ootuse kaupa.
- **Komplektid on `struct`-id.** Iga testfunktsiooni jaoks luuakse uus eksemplar, nii et olek ei saa testide vahel lekkida. Kasuta seadistuseks `init`-i; kasuta `class`-i/`actor`-it ainult siis, kui vajad koristamiseks `deinit`-i. Pesasta komplekte rühmitamiseks.
- **Parameetrista kopeerimise või tsükli asemel.** `@Test(arguments: [...])` käivitab iga juhtumi sõltumatult, paralleelselt, eraldi uuesti käivitatavana, ja tulemustes on ebaõnnestunud argument nimetatud. Kaks argumendikogumit annavad täieliku ristkorrutise — kasuta `zip()`-i, kui soovid selle asemel sobitatud paare.
- **Tunnused kannavad kavatsust:** `.enabled(if:)` / `.disabled("reason")` tingimuste jaoks (ära kunagi kommenteeri testi välja — keelatud test ikka kompileerub), `.bug(url)` jälgimiseks, `.tags(...)` testide seostamiseks failide ja sihtmärkide vahel, `.timeLimit`, `.serialized`, kui test tõesti ei saa paralleelselt joosta. Kasuta käitusaegse `#available` kontrolli asemel `@available`-it, et testimisteek sellest teaks.
- **`withKnownIssue { }`** testile, mis ebaõnnestub millegi tõttu, mis pole sinu kontrolli all — see jätkab kompileerimist ja jooksmist ning annab teada, kui probleem on parandatud, erinevalt `.disabled`-ist.
- **`confirmation`** tagasikutsete jaoks, mis käivituvad N korda; `withCheckedContinuation` ühekordsete tagasikutsete jaoks, millel pole asünkroonset ülelaadimist.
- **Testid jooksevad vaikimisi paralleelselt ja juhuslikus järjekorras.** See on omadus: see toob esile varjatud testidevahelised sõltuvused. Refaktoreeri, selle asemel et haarata `.serialized` järele.
- **Väljumistestid** — `#expect(processExitsWith: .failure) { ... }` — katavad `precondition`/`fatalError` rajad isoleeritud alamprotsessis. Ainult macOS, Linux, FreeBSD, Windows.
- Migreerimine: mõlemad raamistikud eksisteerivad koos ühes sihtmärgis, nii et migreeri järk-järgult ja kirjuta uued testid juba täna Swift Testingus. **Testiraamistike koostalitlus** (swift-testing ST-0021; kontrolli saadavust oma Xcode'i versioonis) võimaldab `XCTFail`-i mähkivaid abifunktsioone välja kutsuda Swift Testingu testidest ja vastupidi; määra režiimiks **complete** või **strict** (mitte **limited** ega kunagi **none**), et raamistikeülesed probleemid jääksid vigadeks ja suunaksid sind `Issue.record` asenduse juurde.

---

## 12. Makrod

Kasuta makrot, kui kirjutad koodi, mille kompilaator saaks tuletada — ja ainult siis.

- **Makrode tüübid kontrollitakse enne laiendamist.** Argumente kontrollitakse makro deklareeritud signatuuri vastu, nii et väärkasutus on puhas viga kutsekohas, mitte segadus genereeritud koodi sees.
- **Vabaltseisvad (`#foo`)** toodavad avaldise või deklaratsiooni. **Kinnitatud (`@Foo`)** täiendavad deklaratsiooni ühes viiest rollist: member, peer, accessor, member-attribute, conformance. Rollid on kombineeritavad — `@Observable` on member + member-attribute + conformance.
- **Testi makrosid puhaste süntaksipuu teisendustena** `assertMacroExpansion`-iga. See on kiireim tsükkel ja nii väldid vigu koodis, mida keegi ei loe. Sea `expansion`-isse murdepunkt ja tee süntaksisõlmele `po`, et selle kuju tundma õppida.
- **Väljasta tõelised diagnostikad, kui makro ei kohaldu.** Viska viga või kasuta `context.addDiagnostic`-ut hoiatuste ja parandusettepanekute (fix-it) jaoks konkreetses kohas. Ära kunagi lase makrol vaikselt genereerida koodi, mis ei kompileeru.
- Laiendatud kood on tavaline Swift: uuritav („Expand Macro“), silutav, sammhaaval läbitav.

---

## 13. Logimine ja silumine

- **`Logger` moodulist `os`, mitte `print`.** Loo üks iga alamsüsteemi ja kategooria kohta. Sõnumeid salvestatakse optimeeritud kujul ja renderdatakse ainult kuvamisel, nii et logimine on piisavalt odav, et see sisse jätta.
- **Mittenumbrilised interpolatsioonid on vaikimisi varjatud.** Luba väärtuse kaupa `privacy: .public` abil ainult andmete puhul, mis tõesti pole isiklikud. Kasuta `.private(mask: .hash)`, kui pead väärtusi seostama neid paljastamata.
- **Tasemed määravad püsivuse ja kulu:** `debug` (ei salvestata kunagi, kiireim — sõnumi koostamine optimeeritakse täielikult välja, kui voogedastust pole), `info`, `notice` (vaikimisi), `error`, `fault` (kõige püsivam, aeglaseim). Logi tasemel `error`/`fault` asju, mida tahad veaaruandes näha.
- **Logi korrelatsiooni ID** (ülesande või päringu UUID) ja saad terve tõrke ajaloo seadme logiarhiivist välja filtreerida ilma seda taasesitamata. `log collect --device --start ...`, seejärel filtreeri Console'is alamsüsteemi järgi.
- `format:` ja `align:` on tasuta — kasuta neid, et logid oleksid loetavad ja veergude kaupa valitavad.
- LLDB mõistab Swifti ülesandeid: see astub `await`-ist üle lõimede läbi, `swift task info` näitab prioriteeti ja alamülesandeid ning nimega ülesanded ilmuvad nii silurisse kui ka Instrumentsi Swift Concurrency malli.

---

## 14. Ebaturvaline kood ja koostalitlus

- **„Unsafe“ tähendab, et API ei suuda oma sisendit täielikult valideerida, nii et selle eeltingimuste rikkumine on määratlemata käitumine** — mitte seda, et see kukub kokku. Turvalised API-d _kukuvadki_ teadlikult kokku; puhas fataalne viga on ohutu tulemus.
- **Eelista `Span`-i `Unsafe*Pointer`-ile.** Alates Swift 6.2-st on olemas turvaline, mittepõgenev ja sama kiire viis järjestikusele salvestusele ligi pääseda. Jäta toorviidad C-koostöö jaoks.
- Kui pead viitasid kasutama: hoia ebaturvaline piirkond võimalikult väike, kasuta **puhvri**viitasid (aadress + arv) paljaste viitade asemel, et piire jälgitaks, ära kunagi lase viidal põgeneda sulundist, mis selle väljastab, ja käivita **Address Sanitizer**.
- Lülita turvakriitilistes moodulites sisse **range mäluohutus** — see sunnib iga ebaturvalist kasutust lähtekoodis tunnistama, mis teebki auditi võimalikuks. Swift 6.4 atribuut `@diagnose` (avaldamata) võimaldab selle üksikute funktsioonide jaoks sisse lülitada.
- **Koostalitlus on kahesuunaline ja järkjärguline.** C, Objective-C ja C++ tüübid kaarduvad otse Swifti (sealhulgas C++ väärtussemantika, konteinerid Swifti kogumitena ja ainult teisaldatavad tüübid `~Copyable`-ina). Swift 6.3 atribuut `@c` eksponeerib Swifti funktsioonid tagasi C-le (koos `@implementation`-iga, kui deklaratsioon on juba päisfailis olemas). Võta Swift kasutusele üks fail korraga; ära kirjuta ümber.

---

## 15. Kaasaegne süntaks, mida peaksid kasutama

Agendid kirjutavad tavapäraselt kõigi nende vanemat ja pikemat vormi.

**⚠-ga märgitud read on Swift 6.4, mis pole veel välja tulnud.** Praegune väljalase on 6.3.x. Nende ettepanekud on vastu võetud ja põhiharus implementeeritud, nii et nende ümber on ohutu planeerida, aga täna on ebaturvaline neid kirjutada — kontrolli enne kasutamist projekti tööriistaahelat ja eelista vanemat vormi, kui sihtversioon on 6.3 või vanem.

| Selle asemel | Kirjuta | Alates |
| --------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | ----- |
| Pesastatud ternaarsed operaatorid; kohe väljakutsutud sulund `let`-i initsialiseerimiseks | `if`/`switch` **avaldised** | 5.9 |
| Ülelaadimised 1, 2, 3… argumendi jaoks | **parameetripakid** (`each T`) ja `for` üle paki | 5.9 |
| `ObservableObject` + `@Published` igal omadusel | **`@Observable`** | 5.9 |
| Objekti küsitlemine muudatuste suhtes | **`Observations { ... }`** — tehinguliste uuenduste `AsyncSequence` | 6.2 |
| `NotificationCenter` stringipõhise `userInfo`-ga | konkreetsed teavitustüübid (`MainActorMessage` / `AsyncMessage`) | 6.2 |
| `Process` + torud skriptimiseks | pakett **Subprocess** (`AsyncBufferSequence.strings()` reakaupa väljundi jaoks; 1.0 saabub koos 6.4-ga) | 6.2+ |
| Käsitsi tehtud stringiindeksi arvutus | **Swift Regex** — literaalid lühiduse jaoks, `RegexBuilder` struktuuri jaoks | 5.7 |
| Fikseeritud suurusega `[String]` kuumal rajal | **`InlineArray<N, T>`** | 6.2 |
| `withUnsafeBufferPointer` | **`.span`** / **`.bytes`** (`RawSpan`) / `OutputSpan` | 6.2 |
| Käsitsi `Task.isCancelled` žongleerimine kirjutamise lõpetamiseks | `Task` **tühistamiskilp** (SE-0504) | 6.4 ⚠ |
| Sõnastiku käsitsi ümberehitamine võtme kasutamiseks | **`mapKeyedValues`** | 6.4 ⚠ |
| `@available(iOS ..., macOS ..., tvOS ..., watchOS ..., visionOS ...)` | **`@available(anyAppleOS ...)`** | 6.4 ⚠ |
| `Rocket.SaturnV`, kui tüüp varjutab moodulit | **moodulivalija** `Rocket::SaturnV` | 6.3 |
| Üldine „hoiatused vigadeks“ | **`@diagnose`** deklaratsiooni / hoiatusgrupi kaupa | 6.4 ⚠ |
| `@unchecked Sendable` `weak var`-i tõttu | `weak let`; või väljenda mitte-saadetavust **`~Sendable`**-iga | 6.4 ⚠ |
| Binaarvormingute käsitsi parsimine viitadega | **Swift Binary Parsing** (`ParserSpan`, ületäitumist kontrollivad parsimisinitsialiseerijad) | 6.2 |
| Kohmakad testfunktsioonide nimed | **toorsed identifikaatorid**: `` @Test func `fruits have a tropical climate`() `` | 6.0 |

Veel tasub teada: **Swift Regexi parserid on kombineeritavad Foundationi päris parseritega** (`.date(...)`, `.currency(...)`) — ära kunagi kirjuta kuupäeva või numbri parsimist regexi sees käsitsi. Määra lokaat selgesõnaliselt, selle asemel et süsteemi oma pärida. Ja kasuta `NegativeLookahead`-i või `Local`-it (atomaarsed grupid), et takistada mustril kogu sisendi ulatuses tagasi otsimast.

---

## 16. Olemasoleva koodibaasi migreerimine Swift 6-le

Järjekord on oluline ja sammude segamine on see, mis migratsioonid seisma paneb.

1. **Ehita esmalt uue kompilaatoriga.** Lähtekoodi ühilduvus tähendab, et see peaks Swift 5 režiimis lihtsalt töötama.
2. **Lülita iga sihtmärgi kaupa sisse täielik samaaegsuse kontroll** (Swift 5 režiim + kõik Swift 6 hoiatused). Alusta **kasutajaliidese/rakenduse kihist**, mitte selle all olevatest raamistikest — suur osa sellest on SDK poolt juba peaaktoriga annoteeritud, nii et parandamise määr on kõrge.
3. **Paranda hoiatused, odavaimad enne.** Oota sadu hoiatusi käputäiest algpõhjusest: `var` globaalsed muutujad, mis peaksid olema `let`, vabad funktsioonid, mis kuuluvad `@MainActor`-ile, üks avalik struct, mis vajab `: Sendable`. Üksainus rida võib kõrvaldada kümneid.
4. **Lülita sihtmärk Swift 6 keelerežiimi**, et töö lukustada.
5. **Liigu järgmise sihtmärgi juurde ja korda.**
6. **Refaktoreeri hiljem, eraldi.** Ära kunagi kombineeri olulist refaktoreerimist andmevõidujooksude ohutuse sisselülitamisega — pead mõlemad tagasi võtma.

Võid range kontrolli uuesti välja lülitada ja tarnida; iga tehtud parandus on tõeline täiustus, mis jääb alles. Lülita **Approachable Concurrency** ja rakendusmoodulite puhul main-actor-by-default sisse _enne_ alustamist — mõlemad vähendavad dramaatiliselt nähtavate vigade arvu ja Xcode tarnib migratsioonitööriistu, mis rakendavad paljud muudatused sinu eest (swift.org/migration).

---

## Kiirteatmik

| Vajadus | Kasuta | Mitte |
| ---------------------------------- | ------------------------------- | ----------------------------------------- |
| Andmetüüp | `struct` / `enum` | `class` ilma identiteedi või jagamiseta |
| Jagatud muudetav olek | `actor` või `@MainActor` klass | `class` + lukk, mida pead meeles pidama |
| Töö tõstmine põhilõimelt ära | `@concurrent func … async` | `Task.detached`, `DispatchQueue.global()` |
| Teegi API isolatsioon | `nonisolated` | `@MainActor`, `@concurrent` |
| Fikseeritud arv paralleelseid töid | `async let` | N struktureerimata `Task`-i |
| Dünaamiline arv paralleelseid töid | `withTaskGroup` (piiratud) | üks ülesanne elemendi kohta, piiramatult |
| Alamülesanded, mis ei tagasta midagi | `withDiscardingTaskGroup` | `withTaskGroup`, mida sa kunagi ei tühjenda |
| Kasutajaliidese sündmusega seotud töö | `Task { }` tagasikutse sees | tagasikutse muutmine `async`-iks |
| Andmevõidujooksu parandamine | lõpeta objekti jagamine | `@unchecked Sendable` |
| Jagatud mudeliklass | mitte-`Sendable` või `@MainActor` | `Sendable` + käsitsi lukustamine |
| Blokeeriv primitiiv üle `await`-i | mitte midagi — struktureeri ümber | `DispatchSemaphore`, `NSCondition` |
| Polümorfism | `some P` | `any P`, kui sa ei vaja salvestust |
| Heterogeenne kogum | `[any P]` | klassihierarhia |
| Jagatud käitumine, kohandamiseta | piiratud `extension` | uus protokoll |
| Kohanduspunkt | protokolli **nõue** | ainult laienduses olev meetod |
| Viitetsükli katkestamine | struktureeri ümber puuks | `weak` + `withExtendedLifetime` |
| Sobivate elementide eemaldamine | `removeAll(where:)` — O(n) | `remove(at:)` tsüklis — O(n²) |
| Otsene ligipääs järjestikusele mälule | `.span` | `withUnsafeBufferPointer` |
| Fikseeritud suurusega puhver kuumal rajal | `InlineArray<N, T>` | `Array` |
| Uus test | `@Test` + `#expect` | `XCTestCase` + `XCTAssertEqual` |
| Sama test paljude sisenditega | `@Test(arguments:)` | `for`-tsükkel või kopeerimine |
| Testi peatamine ebaõnnestumisel | `try #require` | `continueAfterFailure = false` |
| Ajutiselt katkine test | `withKnownIssue` | `.disabled` või väljakommenteerimine |
| Diagnostika tarnitavas koodis | `Logger` + korrelatsiooni ID | `print` |
| Optimeerimise otsustamine | Instruments profileeritud testil | intuitsioon |