Топ 25 запитань зі Swift на співбесідах для iOS-розробників

Підготовка до технічних співбесід з iOS: 25 найпоширеніших запитань зі Swift — optionals, closures, ARC, протоколи, async/await та просунуті патерни з прикладами коду.

Запитання зі Swift на співбесідах для iOS-розробників

Технічні співбесіди на позицію iOS-розробника ретельно перевіряють як базові, так і просунуті знання Swift. Ці 25 запитань охоплюють теми, які найчастіше піднімають на інтервʼю — від основ мови до сучасних патернів конкурентності.

Як користуватися цим гайдом

Кожне запитання містить детальну відповідь із прикладами коду. Запитання впорядковані за зростанням складності — від фундаментальних до просунутих.

Основи Swift

1. Яка різниця між let і var?

let оголошує константу, значення якої не можна змінити після ініціалізації, тоді як var оголошує змінну, яку можна модифікувати. Для посилальних типів (класів) let запобігає повторному присвоєнню посилання, але не блокує зміну самого обʼєкта.

Constants.swiftswift
// let = constant, immutable value
let maximumAttempts = 3
// maximumAttempts = 5  // ❌ Compile error

// var = variable, mutable value
var currentAttempt = 0
currentAttempt += 1  // ✅ OK

// Careful with reference types
class User {
    var name: String
    init(name: String) { self.name = name }
}

let user = User(name: "Alice")
user.name = "Bob"  // ✅ OK - modifying object, not reference
// user = User(name: "Charlie")  // ❌ Error - reassignment forbidden

Найкраща практика: за замовчуванням слід використовувати let, переходячи на var лише тоді, коли мутація справді необхідна. Це робить код більш передбачуваним і простішим для аналізу.

2. Що таке optionals у Swift?

Optionals представляють можливу відсутність значення. Optional може містити значення вказаного типу або nil. Swift використовує optionals для гарантії безпеки на етапі компіляції, вимагаючи явної обробки випадків, коли значення може бути відсутнім.

Optionals.swiftswift
// Declaring an optional with ?
var username: String? = nil  // Can hold a String or nil

// Optional binding with if let (safe unwrapping)
if let name = username {
    print("Hello, \(name)")  // Only executes if username != nil
} else {
    print("Anonymous user")
}

// Guard let for early return
func greet(user: String?) {
    guard let name = user else {
        print("No user provided")
        return  // Early exit if nil
    }
    print("Hello, \(name)")  // name is guaranteed non-nil here
}

// Nil-coalescing operator (??) for default value
let displayName = username ?? "Anonymous"

// Force unwrapping (!) - DANGEROUS, avoid this
// let forced = username!  // Crashes if nil

3. Яка різниця між struct і class?

struct — це тип-значення (копіюється при присвоєнні), тоді як class — це посилальний тип (спільний доступ до одного екземпляра). Ця фундаментальна відмінність впливає на продуктивність, управління памʼяттю та поведінку коду.

ValueVsReference.swiftswift
// Struct = value type (copy)
struct Point {
    var x: Int
    var y: Int
}

var p1 = Point(x: 10, y: 20)
var p2 = p1      // Independent copy
p2.x = 100       // Only modifies p2
print(p1.x)      // 10 - p1 unchanged

// Class = reference type (shared)
class Rectangle {
    var width: Int
    var height: Int
    init(width: Int, height: Int) {
        self.width = width
        self.height = height
    }
}

let r1 = Rectangle(width: 10, height: 20)
let r2 = r1      // Same shared instance
r2.width = 100   // Modifies the shared instance
print(r1.width)  // 100 - r1 also modified!

Коли що використовувати:

  • Struct: прості дані, незмінні значення, немає потреби в успадкуванні
  • Class: важлива ідентичність обʼєкта, потрібне успадкування, спільна поведінка

4. Як працює зіставлення зі зразком (pattern matching) у switch?

switch у Swift є потужним і вичерпним — він повинен охопити всі можливі випадки. Підтримується зіставлення на типах, діапазонах, кортежах, а також додаткові умови через where.

PatternMatching.swiftswift
enum NetworkError: Error {
    case timeout
    case serverError(code: Int)
    case noConnection
}

func handleError(_ error: NetworkError) {
    switch error {
    case .timeout:
        print("Request timed out")

    case .serverError(let code) where code >= 500:
        print("Critical server error: \(code)")

    case .serverError(let code):
        print("Server error: \(code)")

    case .noConnection:
        print("No connection")
    }
    // No default needed: all cases covered
}

// Pattern matching on ranges and tuples
let point = (x: 5, y: 10)
switch point {
case (0, 0):
    print("Origin")
case (let x, 0):
    print("On X axis at \(x)")
case (0...10, 0...10):
    print("In the 10x10 square")
default:
    print("Elsewhere")
}

5. Що таке замикання (closures) та який їхній синтаксис?

Замикання — це самодостатні блоки коду, які захоплюють і зберігають посилання на змінні з навколишнього контексту. Це еквівалент лямбда-функцій або анонімних функцій з інших мов програмування.

Closures.swiftswift
// Full syntax
let add: (Int, Int) -> Int = { (a: Int, b: Int) -> Int in
    return a + b
}

// Shorthand syntax (inferred types, implicit return)
let multiply: (Int, Int) -> Int = { $0 * $1 }

// Trailing closure syntax
let numbers = [3, 1, 4, 1, 5]
let sorted = numbers.sorted { $0 > $1 }  // [5, 4, 3, 1, 1]

// Closure capturing a variable
func makeCounter() -> () -> Int {
    var count = 0  // Captured variable
    return {
        count += 1  // Closure "closes over" count
        return count
    }
}

let counter = makeCounter()
print(counter())  // 1
print(counter())  // 2 - count remembered between calls
Семантика захоплення

Замикання за замовчуванням захоплюють змінні за посиланням. Для захоплення за значенням використовується список захоплення: { [count] in ... }.

Управління памʼяттю та ARC

6. Як працює ARC (Automatic Reference Counting)?

ARC автоматично управляє памʼяттю, підраховуючи сильні посилання на кожен екземпляр класу. Коли лічильник стає нулем, екземпляр звільняється. На відміну від збирання сміття (garbage collection), ARC є детерміністичним і передбачуваним.

ARC.swiftswift
class Person {
    let name: String

    init(name: String) {
        self.name = name
        print("\(name) is initialized")
    }

    deinit {
        print("\(name) is deallocated")
    }
}

// Demonstrating lifecycle
var person1: Person? = Person(name: "Alice")  // refCount = 1
var person2 = person1                          // refCount = 2
person1 = nil                                  // refCount = 1 (not deallocated)
person2 = nil                                  // refCount = 0 → deinit called
// Output: "Alice is deallocated"

7. Що таке цикл сильних посилань (retain cycle) і як його запобігти?

Цикл сильних посилань виникає, коли два обʼєкти утримують сильні посилання один на одного, що унеможливлює їхнє звільнення з памʼяті. Ключові слова weak та unowned розривають такі цикли.

RetainCycle.swiftswift
class Department {
    let name: String
    var manager: Employee?  // Strong reference

    init(name: String) { self.name = name }
    deinit { print("Department \(name) deallocated") }
}

class Employee {
    let name: String
    // weak prevents retain cycle - can become nil
    weak var department: Department?

    init(name: String) { self.name = name }
    deinit { print("Employee \(name) deallocated") }
}

// Without weak: retain cycle → memory leak
// With weak: proper deallocation
var dept: Department? = Department(name: "Engineering")
var emp: Employee? = Employee(name: "Bob")
dept?.manager = emp
emp?.department = dept

dept = nil  // ✅ Deallocated thanks to weak
emp = nil   // ✅ Deallocated

8. Яка різниця між weak і unowned?

Обидва модифікатори розривають цикли сильних посилань, але з різними гарантіями. weak є опціональним і стає nil, коли обʼєкт, на який він посилається, звільняється. unowned передбачає, що обʼєкт завжди існує — звернення до нього після звільнення спричинить аварійне завершення програми.

WeakVsUnowned.swiftswift
class Customer {
    let name: String
    var card: CreditCard?

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

class CreditCard {
    let number: String
    // unowned because a card always exists with its customer
    unowned let customer: Customer

    init(number: String, customer: Customer) {
        self.number = number
        self.customer = customer
    }
}

// Card cannot exist without customer
let customer = Customer(name: "Alice")
customer.card = CreditCard(number: "1234", customer: customer)
// If customer is deallocated, accessing card.customer would crash

Правило: за замовчуванням слід використовувати weak. unowned варто застосовувати лише тоді, коли час життя обʼєкта, на який посилаються, гарантовано дорівнює або перевищує час життя поточного обʼєкта.

Готовий до співбесід з iOS?

Практикуйся з нашими інтерактивними симуляторами, flashcards та технічними тестами.

Протоколи та дженерики

9. Що таке протоколи у Swift?

Протокол визначає контракт (обовʼязкові властивості та методи), який мають реалізувати відповідні типи. Протоколи забезпечують поліморфізм і є основою Protocol-Oriented Programming (POP) у Swift.

Protocols.swiftswift
// Protocol definition
protocol Drawable {
    var color: String { get set }  // Required property (read/write)
    func draw()                     // Required method
}

// Protocol extension with default implementation
extension Drawable {
    func draw() {
        print("Default drawing in \(color)")
    }
}

// Protocol conformance
struct Circle: Drawable {
    var color: String
    var radius: Double

    // draw() inherits default implementation
}

struct Square: Drawable {
    var color: String
    var side: Double

    // Override default implementation
    func draw() {
        print("Square \(color) with side \(side)")
    }
}

// Polymorphic usage
let shapes: [Drawable] = [Circle(color: "red", radius: 5), Square(color: "blue", side: 10)]
shapes.forEach { $0.draw() }

10. Що таке associated type?

Associated types дозволяють протоколам визначати узагальнені типи, які будуть конкретизовані відповідними типами. Саме цей механізм робить протоколи на кшталт Collection такими гнучкими.

AssociatedTypes.swiftswift
// Protocol with associated type
protocol Container {
    associatedtype Item  // Type defined by conformant
    var items: [Item] { get set }
    mutating func add(_ item: Item)
    func count() -> Int
}

// Implementation with Item = String
struct StringBox: Container {
    typealias Item = String  // Optional, Swift can infer
    var items: [String] = []

    mutating func add(_ item: String) {
        items.append(item)
    }

    func count() -> Int { items.count }
}

// Implementation with Item = Int
struct IntStack: Container {
    var items: [Int] = []

    mutating func add(_ item: Int) {
        items.append(item)
    }

    func count() -> Int { items.count }
}

11. Як працюють дженерики?

Дженерики дозволяють писати гнучкий, повторно використовуваний код, який працює з будь-яким типом. Вони усувають дублювання коду, зберігаючи при цьому типобезпеку на етапі компіляції.

Generics.swiftswift
// Generic function
func swap<T>(_ a: inout T, _ b: inout T) {
    let temp = a
    a = b
    b = temp
}

// Type constraint with where
func findIndex<T: Equatable>(of item: T, in array: [T]) -> Int? {
    for (index, element) in array.enumerated() {
        if element == item { return index }  // Equatable required for ==
    }
    return nil
}

// Generic struct
struct Queue<Element> {
    private var elements: [Element] = []

    mutating func enqueue(_ element: Element) {
        elements.append(element)
    }

    mutating func dequeue() -> Element? {
        guard !elements.isEmpty else { return nil }
        return elements.removeFirst()
    }
}

var intQueue = Queue<Int>()
intQueue.enqueue(1)
intQueue.enqueue(2)
print(intQueue.dequeue())  // Optional(1)

12. Що таке протокол Codable?

Codable (псевдонім для Encodable & Decodable) забезпечує автоматичну серіалізацію типів Swift у формати на кшталт JSON і навпаки. Компілятор генерує реалізацію автоматично, якщо всі властивості самі є Codable.

Codable.swiftswift
struct User: Codable {
    let id: Int
    let name: String
    let email: String
    let createdAt: Date

    // CodingKeys to map different JSON names
    enum CodingKeys: String, CodingKey {
        case id
        case name
        case email
        case createdAt = "created_at"  // snake_case → camelCase
    }
}

// JSON decoding
let json = """
{
    "id": 1,
    "name": "Alice",
    "email": "alice@example.com",
    "created_at": "2026-01-15T10:30:00Z"
}
""".data(using: .utf8)!

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601

do {
    let user = try decoder.decode(User.self, from: json)
    print(user.name)  // "Alice"
} catch {
    print("Decoding error: \(error)")
}

// Encoding to JSON
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let data = try encoder.encode(user)

Конкурентність та async/await

13. Як працює async/await у Swift?

async/await спрощує асинхронний код, дозволяючи записувати неблокуючі операції послідовно. Функція, позначена як async, може бути призупинена без блокування потоку, надаючи можливість виконуватися іншим завданням.

AsyncAwait.swiftswift
// Asynchronous function
func fetchUser(id: Int) async throws -> User {
    let url = URL(string: "https://api.example.com/users/\(id)")!

    // await suspends execution until response
    let (data, response) = try await URLSession.shared.data(from: url)

    guard let httpResponse = response as? HTTPURLResponse,
          httpResponse.statusCode == 200 else {
        throw NetworkError.invalidResponse
    }

    return try JSONDecoder().decode(User.self, from: data)
}

// Calling from async context
func loadUserProfile() async {
    do {
        let user = try await fetchUser(id: 42)
        print("User: \(user.name)")
    } catch {
        print("Error: \(error)")
    }
}

// Calling from synchronous context with Task
func buttonTapped() {
    Task {
        await loadUserProfile()
    }
}

14. Що таке Actor?

Актори — це посилальні типи, які захищають свій внутрішній стан від одночасного доступу. Вони гарантують, що лише одне завдання в будь-який момент часу може звертатися до їхніх змінних властивостей, усуваючи перегони даних.

Actors.swiftswift
// Actor protects its state automatically
actor BankAccount {
    private var balance: Double = 0

    func deposit(_ amount: Double) {
        balance += amount  // Automatic thread-safe access
    }

    func withdraw(_ amount: Double) -> Bool {
        guard balance >= amount else { return false }
        balance -= amount
        return true
    }

    func getBalance() -> Double {
        return balance
    }
}

// Usage - await required to access actor
let account = BankAccount()

Task {
    await account.deposit(100)
    let success = await account.withdraw(30)
    let balance = await account.getBalance()
    print("Balance: \(balance)")  // 70
}

15. Що таке Task і TaskGroup?

Task створює одиницю асинхронної роботи. TaskGroup дозволяє запускати кілька завдань паралельно та збирати їхні результати.

TaskGroup.swiftswift
// Simple Task
let task = Task {
    return await fetchUser(id: 1)
}
let user = try await task.value

// TaskGroup for parallelization
func fetchMultipleUsers(ids: [Int]) async throws -> [User] {
    try await withThrowingTaskGroup(of: User.self) { group in
        // Launch all requests in parallel
        for id in ids {
            group.addTask {
                try await fetchUser(id: id)
            }
        }

        // Collect results as they complete
        var users: [User] = []
        for try await user in group {
            users.append(user)
        }
        return users
    }
}

// All 3 requests run in parallel
let users = try await fetchMultipleUsers(ids: [1, 2, 3])

16. Як працює @MainActor?

@MainActor гарантує виконання коду в головному потоці. Це критично важливо для оновлення інтерфейсу користувача, яке завжди повинно відбуватися в головному потоці.

MainActor.swiftswift
// UI class annotated with @MainActor
@MainActor
class UserViewModel: ObservableObject {
    @Published var user: User?
    @Published var isLoading = false
    @Published var error: String?

    func loadUser() async {
        isLoading = true  // ✅ On main thread automatically

        do {
            // Network operation on background thread
            user = try await fetchUser(id: 42)
        } catch {
            self.error = error.localizedDescription
        }

        isLoading = false  // ✅ Automatic return to main thread
    }
}

// Or for a specific function
func updateUI() async {
    await MainActor.run {
        // This block runs on main thread
        label.text = "Updated"
    }
}

Патерни та архітектура

17. Що таке патерн Delegate?

Патерн Delegate дозволяє обʼєкту делегувати певні обовʼязки іншому обʼєкту. Цей патерн є повсюдним у UIKit (UITableViewDelegate, UITextFieldDelegate тощо).

DelegatePattern.swiftswift
// 1. Define the delegate protocol
protocol DownloadManagerDelegate: AnyObject {
    func downloadDidStart()
    func downloadDidProgress(_ progress: Double)
    func downloadDidComplete(data: Data)
    func downloadDidFail(error: Error)
}

// 2. Class that uses the delegate
class DownloadManager {
    // weak to avoid retain cycles
    weak var delegate: DownloadManagerDelegate?

    func startDownload(url: URL) {
        delegate?.downloadDidStart()

        // Simulated download
        Task {
            for progress in stride(from: 0.0, to: 1.0, by: 0.1) {
                try await Task.sleep(nanoseconds: 100_000_000)
                delegate?.downloadDidProgress(progress)
            }
            delegate?.downloadDidComplete(data: Data())
        }
    }
}

// 3. Class that implements the delegate
class ViewController: UIViewController, DownloadManagerDelegate {
    let manager = DownloadManager()

    override func viewDidLoad() {
        super.viewDidLoad()
        manager.delegate = self  // Register as delegate
    }

    func downloadDidStart() { print("Started") }
    func downloadDidProgress(_ progress: Double) { print("\(progress * 100)%") }
    func downloadDidComplete(data: Data) { print("Complete") }
    func downloadDidFail(error: Error) { print("Error: \(error)") }
}

18. Що таке патерн MVVM?

MVVM (Model-View-ViewModel) відокремлює логіку представлення від вигляду. ViewModel надає дані для спостереження, які View відображає, не знаючи деталей реалізації View.

MVVM.swiftswift
// Model
struct Article: Identifiable {
    let id: UUID
    let title: String
    let content: String
    let publishedAt: Date
}

// ViewModel
@MainActor
class ArticleListViewModel: ObservableObject {
    @Published private(set) var articles: [Article] = []
    @Published private(set) var isLoading = false
    @Published var errorMessage: String?

    private let repository: ArticleRepository

    init(repository: ArticleRepository = .shared) {
        self.repository = repository
    }

    func loadArticles() async {
        isLoading = true
        errorMessage = nil

        do {
            articles = try await repository.fetchArticles()
        } catch {
            errorMessage = "Failed to load articles"
        }

        isLoading = false
    }
}

// View (SwiftUI)
struct ArticleListView: View {
    @StateObject private var viewModel = ArticleListViewModel()

    var body: some View {
        Group {
            if viewModel.isLoading {
                ProgressView()
            } else {
                List(viewModel.articles) { article in
                    Text(article.title)
                }
            }
        }
        .task { await viewModel.loadArticles() }
    }
}

Готовий до співбесід з iOS?

Практикуйся з нашими інтерактивними симуляторами, flashcards та технічними тестами.

19. Що таке впровадження залежностей (Dependency Injection)?

Впровадження залежностей передбачає надання залежностей обʼєкту ззовні, замість їхнього створення всередині. Це покращує тестовність і зменшує звʼязність між компонентами.

DependencyInjection.swiftswift
// Protocol for abstraction
protocol UserServiceProtocol {
    func fetchUser(id: Int) async throws -> User
}

// Real implementation
class UserService: UserServiceProtocol {
    func fetchUser(id: Int) async throws -> User {
        // Real API call
        let url = URL(string: "https://api.example.com/users/\(id)")!
        let (data, _) = try await URLSession.shared.data(from: url)
        return try JSONDecoder().decode(User.self, from: data)
    }
}

// ViewModel with injection
class ProfileViewModel: ObservableObject {
    private let userService: UserServiceProtocol

    // Constructor injection
    init(userService: UserServiceProtocol = UserService()) {
        self.userService = userService
    }

    func loadProfile(id: Int) async {
        // Uses injected service
    }
}

// Mock for testing
class MockUserService: UserServiceProtocol {
    func fetchUser(id: Int) async throws -> User {
        return User(id: id, name: "Test User", email: "test@test.com")
    }
}

// In tests
let viewModel = ProfileViewModel(userService: MockUserService())

20. Як реалізувати патерн Singleton?

Singleton гарантує, що клас має лише один глобально доступний екземпляр. У Swift це реалізується через статичну властивість із приватним ініціалізатором.

Singleton.swiftswift
class NetworkManager {
    // Single globally accessible instance
    static let shared = NetworkManager()

    // Private initializer prevents creating other instances
    private init() {
        // Initial configuration
    }

    private let session = URLSession.shared

    func request<T: Decodable>(_ url: URL) async throws -> T {
        let (data, _) = try await session.data(from: url)
        return try JSONDecoder().decode(T.self, from: data)
    }
}

// Usage
let user: User = try await NetworkManager.shared.request(url)
Обережно з Singleton

Singleton створює глобальний стан, який ускладнює тестування та розділення компонентів. За можливості слід віддавати перевагу впровадженню залежностей.

Просунуті концепції

21. Що таке @escaping у контексті замикань?

Замикання позначається як @escaping, коли воно може бути викликане після повернення з функції, яка його отримала. Це типово для асинхронних зворотних викликів та зберігання замикань.

Escaping.swiftswift
class DataLoader {
    // Storage of completion handlers
    private var completionHandlers: [() -> Void] = []

    // @escaping because closure is stored and called later
    func loadData(completion: @escaping () -> Void) {
        completionHandlers.append(completion)

        DispatchQueue.global().async {
            // Async work...
            Thread.sleep(forTimeInterval: 1)

            DispatchQueue.main.async {
                // Closure called after loadData returns
                completion()
            }
        }
    }

    // Non-escaping by default: closure called before return
    func transform(data: Data, using transformer: (Data) -> String) -> String {
        return transformer(data)  // Called immediately
    }
}

// With @escaping, watch for retain cycles
class ViewController {
    var loader = DataLoader()
    var data: String?

    func load() {
        loader.loadData { [weak self] in  // [weak self] avoids retain cycle
            self?.data = "Loaded"
        }
    }
}

22. Що таке @propertyWrapper?

Property wrapper інкапсулюють логіку зберігання та доступу до властивості. Вони дозволяють повторно використовувати такі патерни, як валідація, логування чи персистентність.

PropertyWrapper.swiftswift
// Property wrapper for positive values only
@propertyWrapper
struct Positive {
    private var value: Int = 0

    var wrappedValue: Int {
        get { value }
        set { value = max(0, newValue) }  // Force positive
    }

    // Projected value accessible via $
    var projectedValue: Bool {
        value > 0
    }

    init(wrappedValue: Int) {
        self.wrappedValue = wrappedValue
    }
}

// Usage
struct Player {
    @Positive var score: Int = 0
    @Positive var health: Int = 100
}

var player = Player()
player.score = -50   // Becomes 0 (clamped)
print(player.score)  // 0
print(player.$score) // false (projectedValue)

player.score = 100
print(player.$score) // true

23. Що таке result builders?

Result builders дозволяють будувати складні значення за допомогою декларативного синтаксису. Саме цей механізм лежить в основі DSL-синтаксису SwiftUI.

ResultBuilder.swiftswift
// Result builder definition
@resultBuilder
struct StringBuilder {
    static func buildBlock(_ components: String...) -> String {
        components.joined(separator: " ")
    }

    static func buildOptional(_ component: String?) -> String {
        component ?? ""
    }

    static func buildEither(first component: String) -> String {
        component
    }

    static func buildEither(second component: String) -> String {
        component
    }
}

// Function using the builder
func buildGreeting(@StringBuilder _ content: () -> String) -> String {
    content()
}

// Usage with declarative syntax
let greeting = buildGreeting {
    "Hello"
    "and"
    "welcome"
    if Bool.random() {
        "!"
    } else {
        "."
    }
}
print(greeting)  // "Hello and welcome !" or "Hello and welcome ."

24. Як працюють some та непрозорі типи (opaque types)?

some оголошує непрозорий тип: точний тип відомий компілятору, але прихований від того, хто викликає. Це необхідно для протоколів з associated types і дозволяє виконувати оптимізації.

OpaqueTypes.swiftswift
// Without some: error because Collection has associated type
// func makeCollection() -> Collection { ... }  // ❌ Error

// With some: exact type is hidden but consistent
func makeArray() -> some Collection {
    return [1, 2, 3]  // Always returns the same concrete type
}

// Used in SwiftUI for body
struct ContentView: View {
    var body: some View {  // Exact type inferred but hidden
        VStack {
            Text("Hello")
            Text("World")
        }
    }
}

// Difference with any (existential)
func processAny(_ collection: any Collection) {
    // Can accept different types, runtime overhead
}

func processSome(_ collection: some Collection) {
    // Type fixed at compile time, no overhead
}

25. Що таке макроси у Swift?

Макроси (Swift 5.9+) генерують код на етапі компіляції. Вони зменшують шаблонний код, зберігаючи при цьому типобезпеку та можливість відлагодження.

Macros.swiftswift
// Freestanding macro: generates an expression
let (x, y) = #unwrap(optionalX, optionalY)
// Expands to: guard let x = optionalX, let y = optionalY else { ... }

// Attached macro: modifies a declaration
@Observable  // Macro that generates Observable boilerplate
class UserModel {
    var name: String = ""
    var email: String = ""
}
// Automatically generates @ObservationTracked, ObservationRegistrar, etc.

// Macro for Codable with customization
@Codable
struct Product {
    let id: Int
    @CodableKey("product_name") let name: String  // Renames JSON key
    @CodableIgnored var cache: Data?              // Excludes from coding
}

// Creating a custom macro
@attached(member, names: named(init))
public macro AutoInit() = #externalMacro(module: "MyMacros", type: "AutoInitMacro")

@AutoInit
struct Point {
    let x: Int
    let y: Int
    // init(x: Int, y: Int) generated automatically
}
Відлагодження макросів

У Xcode достатньо натиснути правою кнопкою на макрос і обрати "Expand Macro", щоб побачити згенерований код. Корисно для аналізу та відлагодження.

Підсумок

Ці 25 запитань охоплюють фундаментальні теми, якими має володіти кожен Swift-розробник для успішного проходження iOS-співбесід. Від управління памʼяттю з ARC до сучасних патернів конкурентності — кожна концепція є невідʼємною частиною екосистеми Swift.

Контрольний список для повторення

  • Опанування optionals та різних методів їхнього розгортання
  • Розуміння різниці між типами-значеннями та посилальними типами
  • Вміння ідентифікувати та розвʼязувати цикли сильних посилань за допомогою weak/unowned
  • Використання async/await та акторів для конкурентності
  • Реалізація патернів Delegate, MVVM та Dependency Injection
  • Знання протоколів, дженериків та Codable
  • Розуміння просунутих концепцій: property wrappers, result builders, макроси

Додаткові ресурси

Офіційна документація Swift залишається найкращим джерелом для поглибленого вивчення цих тем. Регулярна практика з власними проєктами та вправами з програмування допомагає закріпити здобуті знання.

Починай практикувати!

Перевір свої знання з нашими симуляторами співбесід та технічними тестами.

Теги

#swift
#ios
#interview
#career
#apple

Поділитися

Пов'язані статті