Swift este limbajul de programare al Apple care combină puterea Objective-C cu siguranța limbajelor moderne. Spre deosebire de multe alte limbaje, Swift oferă construcții unice: tipuri de valoare cu copiere automată, Actori cu izolare la nivel de compilator, Property Wrappers pentru reutilizarea logicii proprietăților și Programare Orientată pe Protocoale ca alternativă la moștenirea clasică. Conform Documentației Apple Swift, aceste caracteristici fac codul mai sigur și mai performant.
Puncte Cheie
În Swift, struct este un tip de valoare, iar class este un tip de referință. Această diferență fundamentală determină comportamentul în timpul atribuirii, transmiterii către funcții și gestionării memoriei. Struct este copiat integral la fiecare atribuire, class este transmis prin referință — este copiat doar pointerul către obiectul din heap.
Struct implementează automat semantica de valoare — fiecare instanță este independentă. Mutarea unei proprietăți struct este posibilă doar prin var, iar metodele care modifică proprietăți trebuie marcate ca mutating. Struct nu suportă moștenirea, dar se poate conforma protocoalelor. Biblioteca standard Swift folosește struct pentru String, Array, Dictionary, Int, Bool — toate tipurile fundamentale sunt tipuri de valoare.
struct User {
let id: Int
var name: String
mutating func updateName(_ newName: String) {
name = newName
}
}
var user1 = User(id: 1, name: "Alice")
var user2 = user1
user2.updateName("Bob")
// user1.name === "Alice", user2.name === "Bob"
// Copie completă — instanțe independente
Class este stocată în heap și suportă egalitatea prin referință. Mai multe variabile pot referi același obiect, modificările printr-o referință sunt vizibile prin toate celelalte. Class suportă moștenirea, permite suprascrierea metodelor (override), are deinit pentru curățarea resurselor. În Swift, class este folosită mai rar decât struct — Apple recomandă să începeți cu struct și să treceți la class doar când sunt necesare moștenirea sau semantica de referință.
class Car {
let model: String
var speed: Int
init(model: String, speed: Int) {
self.model = model
self.speed = speed
}
func accelerate(_ amount: Int) {
speed += amount
}
}
let car1 = Car(model: "Tesla", speed: 0)
let car2 = car1
car2.accelerate(50)
// car1.speed === 50, car2.speed === 50
// Aceeași referință — modificări vizibile peste tot
Alegerea între struct și class depinde de semantica datelor. Pentru configurări, modele de date și stări folosiți struct — copierea este mai sigură și mai previzibilă. Pentru obiecte cu identitate (sesiune utilizator, conexiune bază de date) și când este necesară moștenirea, folosiți class.
Actor este un tip de referință care își protejează starea mutabilă de accesul concurent. Compilatorul Swift garantează că variabilele actor pot fi accesate doar din același context actor — orice acces extern cauzează o eroare de compilare. Aceasta previne condițiile de cursă la nivel de limbaj, fără blocări manuale.
Toate proprietățile și metodele actor sunt izolate implicit. Apelarea unei metode actor din exterior necesită await — Swift comută execuția către actor și înapoi. Actor sincronizează accesul secvențial: cererile de la diferite sarcini sunt puse în coadă. Aceasta garantează o stare consistentă în interiorul actorului. Metodele neizolate (nonisolated) pot fi apelate fără await, dar nu pot accesa proprietăți mutabile.
actor BankAccount {
private var balance: Double
init(initialBalance: Double) {
balance = initialBalance
}
func deposit(amount: Double) {
balance += amount
}
func withdraw(amount: Double) throws {
guard balance >= amount else {
throw BankError.insufficientFunds
}
balance -= amount
}
nonisolated func accountInfo() -> String {
return "Cont Bancar"
}
}
let account = BankAccount(initialBalance: 1000)
await account.deposit(amount: 500)
// balance = 1500 — atomic, fără condiție de cursă
Actor este util pentru manageri de stare, cache-uri și servicii de rețea unde mai multe sarcini citesc și scriu date concurent. Spre deosebire de sincronizarea manuală prin NSLock sau DispatchQueue, actor este verificat la compilare — este imposibil să greșești. Erorile de izolare apar la compilare, nu la runtime.
Property Wrapper este o adnotare care adaugă comportament unei proprietăți fără a-i modifica declarația. SwiftUI și Combine oferă wrapper-uri încorporate pentru gestionarea stării vizualizărilor. Compilatorul generează cod care încapsulează accesul la proprietate, adăugând logică de observare, sincronizare sau transformare.
@Published este folosit în clase conforme cu ObservableObject pentru a publica automat modificările. Abonații Combine primesc notificări la fiecare actualizare. @State este starea locală a unei vizualizări SwiftUI. SwiftUI redesenează automat vizualizarea când @State se schimbă. @Binding creează o conexiune bidirecțională între o vizualizare părinte și copil fără a deține datele.
import SwiftUI
import Combine
@main
struct CounterApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
class CounterViewModel: ObservableObject {
@Published var count: Int = 0
func increment() {
count += 1
}
}
struct ContentView: View {
@State private var greeting: String = "Salut"
@StateObject private var viewModel = CounterViewModel()
var body: some View {
VStack {
CounterDisplay(count: $viewModel.count)
Text(greeting)
Button("Incrementează", action: viewModel.increment)
}
}
}
struct CounterDisplay: View {
@Binding var count: Int
var body: some View {
Text("Numărătoare: \(count)")
}
}
Property Wrapper-urile personalizate sunt create folosind @propertyWrapper și o structură cu un câmp wrappedValue. Aceasta permite reutilizarea logicii: validare, normalizare, stocare UserDefaults. Property Wrapper-urile reduc semnificativ codul boilerplate și fac intenția dezvoltatorului explicită prin adnotare.
@propertyWrapper
struct Clamped<T: Comparable> {
private var value: T
let range: ClosedRange<T>
init(wrappedValue: T, range: ClosedRange<T>) {
self.range = range
self.value = min(max(wrappedValue, range.lowerBound), range.upperBound)
}
var wrappedValue: T {
get { value }
set { value = min(max(newValue, range.lowerBound), range.upperBound) }
}
}
struct Settings {
@Clamped(range: 0...100) var volume: Int = 50
}
var s = Settings()
s.volume = 150
// s.volume === 100 — valoare automat limitată
Generics este un mecanism puternic al Swift pentru scrierea de cod flexibil fără a pierde siguranța tipurilor. Funcțiile și tipurile generice funcționează cu orice tip care îndeplinește constrângerile. Programarea Orientată pe Protocoale (POP) este un paradigm Swift în care protocol + extension înlocuiesc moștenirea clasică a claselor, oferind compoziție de comportament.
Swift permite constrângerea parametrilor generici cu protocoale (clauze where). Protocol cu tip asociat este echivalentul generic pentru protocoale: tipul concret este determinat la momentul conformării. Aceasta este baza pentru colecții și algoritmi type-safe. Biblioteca standard Swift folosește activ generics: Array, Dictionary, Optional — toate sunt tipuri generice.
protocol Cacheable {
associatedtype Key: Hashable
associatedtype Value
func get(_ key: Key) -> Value?
mutating func set(_ value: Value, for key: Key)
}
struct MemoryCache<K: Hashable, V>: Cacheable {
private var storage: [K: V] = [:]
typealias Key = K
typealias Value = V
func get(_ key: K) -> V? {
return storage[key]
}
mutating func set(_ value: V, for key: K) {
storage[key] = value
}
}
func firstMatch<T: Equatable>(in array: [T], target: T) -> Int? {
for (index, item) in array.enumerated() {
if item == target {
return index
}
}
return nil
}
let numbers = [10, 20, 30, 40]
let index = firstMatch(in: numbers, target: 30)
// index === 2 — o metodă generică pentru toate tipurile Equatable
Extension-ul de protocol este un mecanism cheie al POP. Într-o extensie se poate furniza o implementare implicită a metodelor protocolului. Tipurile conforme cu protocolul obțin această implementare gratuit și o pot suprascrie dacă este necesar. Aceasta permite construirea de ierarhii de comportament fără moștenire de clase — o alternativă la moștenirea multiplă, care nu există în Swift.
protocol Drivable {
var speed: Double { get set }
func drive()
}
extension Drivable {
func drive() {
if speed > 0 {
print("Conduc la \(speed) km/h")
}
}
}
struct Bicycle: Drivable {
var speed: Double = 15
}
struct Plane: Drivable {
var speed: Double = 900
func drive() {
print("Zbor la \(speed) km/h")
}
}
let bike = Bicycle()
bike.drive() // "Conduc la 15.0 km/h" — implicit
let plane = Plane()
plane.drive() // "Zbor la 900.0 km/h" — suprascris
POP cu extension permite compoziția prin multiple protocoale — un tip se poate conforma mai multor protocoale, obținând implementările lor. Aceasta este mai flexibilă decât moștenirea unică a claselor, unde întreaga ierarhie este fixă. Biblioteca standard Swift este construită pe POP: CustomStringConvertible, Equatable, Hashable, Codable — toate sunt protocoale cu extensii.
Enum în Swift este o construcție semnificativ mai puternică decât în alte limbaje. Fiecare caz poate avea valori asociate — un set arbitrar de date de orice tip. Combinat cu extension și potrivirea de modele, enum devine baza pentru modelarea stărilor, rezultatelor operațiilor și mașinilor cu stări finite.
Valorile asociate permit fiecărui caz să stocheze date unice. De exemplu, cazul loading nu conține date, success conține rezultatul, failure conține o eroare. Potrivirea modelelor prin switch extrage valorile asociate și procesează fiecare caz. Compilatorul verifică exhaustivitatea (exhaustive switch) — aceasta elimină stările uitate.
enum NetworkResult<T> {
case loading
case success(T)
case failure(Error)
}
enum MediaFile {
case image(url: URL, width: Int, height: Int)
case video(url: URL, duration: Double)
case audio(url: URL, bitrate: Int)
}
func handle(_ result: NetworkResult<String>) {
switch result {
case .loading:
print("Se încarcă...")
case .success(let data):
print("Primit: \(data)")
case .failure(let error):
print("Eroare: \(error.localizedDescription)")
}
}
let file = MediaFile.video(url: URL(string: "https://example.com/video.mp4")!, duration: 120)
if case .video(let url, let duration) = file {
print("Video \(url) durată \(duration)s")
}
Swift permite adăugarea de metode, proprietăți calculate și conformare la protocoale prin extension pentru enum. Acest lucru nu necesită modificarea declarației originale — codul rămâne curat și modular. Metodele din extension pot lucra cu valori asociate, calcula date derivate și implementa logică de afaceri.
extension MediaFile {
var fileName: String {
switch self {
case .image(let url, _, _),
.video(let url, _),
.audio(let url, _):
return url.lastPathComponent
}
}
var description: String {
switch self {
case .image(_, let w, let h):
return "Imagine \(w)x\(h)"
case .video(_, let d):
return "Video \(d)s"
case .audio(_, let b):
return "Audio \(b) kbps"
}
}
}
extension MediaFile: Equatable {
static func == (lhs: MediaFile, rhs: MediaFile) -> Bool {
return lhs.fileName == rhs.fileName
}
}
let files: [MediaFile] = [file]
files.forEach { print($0.description) }
// "Video 120.0s"
Enum cu valori asociate + extension este înlocuitorul claselor sealed din Kotlin sau al uniunilor discriminate din alte limbaje. Swift folosește această combinație pentru Result (un tip standard în biblioteca standard), Optional (Optional este un enum cu .none și .some) și gestionarea stărilor în SwiftUI. Potrivirea modelelor garantează că toate stările sunt procesate — erorile la runtime sunt imposibile.
Întrebări Frecvente
Struct — tip valoare, copiat la atribuire, nu suportă moștenirea. Class — tip referință, transmis prin referință, suportă moștenirea, deinit și verificarea identității (===). Apple recomandă să începeți cu struct și să treceți la class doar când este necesară semantica de referință.
Actor — tip referință thread-safe. Compilatorul interzice accesul concurent la starea mutabilă din contexte diferite. Folosiți Actor pentru manageri de stare, cache-uri și servicii accesate de mai multe sarcini. Apelarea metodelor actor necesită await.
Principale: @State — stare locală a vizualizării; @Binding — conexiune la date fără posesie; @Published — publicarea modificărilor în ObservableObject; @StateObject — posesia ObservableObject; @EnvironmentObject — dependență din mediu. Wrapper-urile personalizate se creează prin @propertyWrapper.
POP — un paradigm în care protocol + extension definesc comportamentul în locul moștenirii de clase. Un tip se conformează mai multor protocoale, obținând implementările lor gratuit. Aceasta este compoziție în loc de moștenire — mai flexibil și mai sigur. Biblioteca standard Swift (Equatable, Codable) este construită pe POP.
Enum în Swift suportă valori asociate — fiecare caz poate stoca date arbitrare de diferite tipuri. Combinat cu potrivirea modelelor (switch), oferă un model puternic pentru stări, erori și Design-Dominat de Domeniu. Optional și Result sunt enum-uri standard Swift cu valori asociate.
Rezumat
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.