# Топ 25 запитань зі Swift на співбесідах для iOS-розробників > Підготовка до технічних співбесід з iOS: 25 найпоширеніших запитань зі Swift — optionals, closures, ARC, протоколи, async/await та просунуті патерни з прикладами коду. - Published: 2026-01-29 - Updated: 2026-04-12 - Author: SharpSkill - Tags: swift, ios, interview, career, apple - Reading time: 15 min --- Технічні співбесіди на позицію iOS-розробника ретельно перевіряють як базові, так і просунуті знання Swift. Ці 25 запитань охоплюють теми, які найчастіше піднімають на інтервʼю — від основ мови до сучасних патернів конкурентності. > **Як користуватися цим гайдом** > > Кожне запитання містить детальну відповідь із прикладами коду. Запитання впорядковані за зростанням складності — від фундаментальних до просунутих. ## Основи Swift ### 1. Яка різниця між `let` і `var`? `let` оголошує константу, значення якої не можна змінити після ініціалізації, тоді як `var` оголошує змінну, яку можна модифікувати. Для посилальних типів (класів) `let` запобігає повторному присвоєнню посилання, але не блокує зміну самого обʼєкта. ```swift // Constants.swift // 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 для гарантії безпеки на етапі компіляції, вимагаючи явної обробки випадків, коли значення може бути відсутнім. ```swift // Optionals.swift // 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` — це посилальний тип (спільний доступ до одного екземпляра). Ця фундаментальна відмінність впливає на продуктивність, управління памʼяттю та поведінку коду. ```swift // ValueVsReference.swift // 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`. ```swift // PatternMatching.swift 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) та який їхній синтаксис? Замикання — це самодостатні блоки коду, які захоплюють і зберігають посилання на змінні з навколишнього контексту. Це еквівалент лямбда-функцій або анонімних функцій з інших мов програмування. ```swift // Closures.swift // 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 є детерміністичним і передбачуваним. ```swift // ARC.swift 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` розривають такі цикли. ```swift // RetainCycle.swift 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` передбачає, що обʼєкт завжди існує — звернення до нього після звільнення спричинить аварійне завершення програми. ```swift // WeakVsUnowned.swift 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` варто застосовувати лише тоді, коли час життя обʼєкта, на який посилаються, гарантовано дорівнює або перевищує час життя поточного обʼєкта. ## Протоколи та дженерики ### 9. Що таке протоколи у Swift? Протокол визначає контракт (обовʼязкові властивості та методи), який мають реалізувати відповідні типи. Протоколи забезпечують поліморфізм і є основою Protocol-Oriented Programming (POP) у Swift. ```swift // Protocols.swift // 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` такими гнучкими. ```swift // AssociatedTypes.swift // 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. Як працюють дженерики? Дженерики дозволяють писати гнучкий, повторно використовуваний код, який працює з будь-яким типом. Вони усувають дублювання коду, зберігаючи при цьому типобезпеку на етапі компіляції. ```swift // Generics.swift // Generic function func swap(_ a: inout T, _ b: inout T) { let temp = a a = b b = temp } // Type constraint with where func findIndex(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 { 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() intQueue.enqueue(1) intQueue.enqueue(2) print(intQueue.dequeue()) // Optional(1) ``` ### 12. Що таке протокол `Codable`? `Codable` (псевдонім для `Encodable & Decodable`) забезпечує автоматичну серіалізацію типів Swift у формати на кшталт JSON і навпаки. Компілятор генерує реалізацію автоматично, якщо всі властивості самі є Codable. ```swift // Codable.swift 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`, може бути призупинена без блокування потоку, надаючи можливість виконуватися іншим завданням. ```swift // AsyncAwait.swift // 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? Актори — це посилальні типи, які захищають свій внутрішній стан від одночасного доступу. Вони гарантують, що лише одне завдання в будь-який момент часу може звертатися до їхніх змінних властивостей, усуваючи перегони даних. ```swift // Actors.swift // 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` дозволяє запускати кілька завдань паралельно та збирати їхні результати. ```swift // TaskGroup.swift // 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` гарантує виконання коду в головному потоці. Це критично важливо для оновлення інтерфейсу користувача, яке завжди повинно відбуватися в головному потоці. ```swift // MainActor.swift // 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 тощо). ```swift // DelegatePattern.swift // 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. ```swift // MVVM.swift // 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() } } } ``` ### 19. Що таке впровадження залежностей (Dependency Injection)? Впровадження залежностей передбачає надання залежностей обʼєкту ззовні, замість їхнього створення всередині. Це покращує тестовність і зменшує звʼязність між компонентами. ```swift // DependencyInjection.swift // 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 це реалізується через статичну властивість із приватним ініціалізатором. ```swift // Singleton.swift 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(_ 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`, коли воно може бути викликане після повернення з функції, яка його отримала. Це типово для асинхронних зворотних викликів та зберігання замикань. ```swift // Escaping.swift 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 інкапсулюють логіку зберігання та доступу до властивості. Вони дозволяють повторно використовувати такі патерни, як валідація, логування чи персистентність. ```swift // PropertyWrapper.swift // 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. ```swift // ResultBuilder.swift // 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 і дозволяє виконувати оптимізації. ```swift // OpaqueTypes.swift // 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+) генерують код на етапі компіляції. Вони зменшують шаблонний код, зберігаючи при цьому типобезпеку та можливість відлагодження. ```swift // Macros.swift // 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 залишається найкращим джерелом для поглибленого вивчення цих тем. Регулярна практика з власними проєктами та вправами з програмування допомагає закріпити здобуті знання. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/uk/blog/ios/top-25-swift-interview-questions