25 Pertanyaan Interview Swift Teratas untuk Developer iOS

Persiapkan diri untuk interview iOS dengan 25 pertanyaan Swift yang paling sering ditanyakan: optionals, closures, ARC, protocols, async/await, dan pola lanjutan.

Pertanyaan Interview Swift untuk Developer iOS

Interview teknis iOS menguji secara mendalam pemahaman Swift baik dasar maupun lanjutan. 25 pertanyaan berikut mencakup topik-topik yang paling sering ditanyakan oleh hiring manager, dari dasar bahasa hingga pola concurrency modern.

Cara menggunakan panduan ini

Setiap pertanyaan dilengkapi jawaban detail beserta contoh kode. Pertanyaan disusun berdasarkan tingkat kesulitan yang meningkat, dari dasar hingga konsep lanjutan.

Dasar-Dasar Swift

1. Apa perbedaan antara let dan var?

let mendeklarasikan konstanta yang nilainya tidak dapat diubah setelah inisialisasi, sedangkan var mendeklarasikan variabel yang bersifat mutable. Untuk tipe referensi (class), let mencegah penggantian referensi tetapi tidak mencegah modifikasi objek itu sendiri.

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

Praktik terbaik: gunakan let secara default dan hanya beralih ke var ketika mutasi benar-benar diperlukan. Hal ini membuat kode lebih mudah diprediksi dan dipahami.

2. Jelaskan optionals di Swift

Optionals merepresentasikan kemungkinan tidak adanya nilai. Sebuah optional dapat berisi nilai dari tipe yang ditentukan atau nil. Swift menggunakan optionals untuk menjamin keamanan saat kompilasi dengan memaksa penanganan eksplisit pada kasus di mana nilai mungkin tidak ada.

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. Apa perbedaan antara struct dan class?

struct adalah tipe nilai (disalin saat assignment) sedangkan class adalah tipe referensi (berbagi instance yang sama). Perbedaan fundamental ini berdampak pada performa, manajemen memori, dan perilaku kode.

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!

Kapan menggunakan yang mana:

  • Struct: data sederhana, nilai immutable, tidak memerlukan inheritance
  • Class: identitas penting, inheritance diperlukan, perilaku shared

4. Bagaimana cara kerja pattern matching dengan switch?

switch di Swift sangat powerful dan bersifat exhaustive: harus mencakup semua kemungkinan case. Mendukung pattern matching pada tipe, range, tuple, dan kondisi tambahan dengan 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. Jelaskan closures dan sintaksnya

Closures adalah blok kode mandiri yang menangkap dan menyimpan referensi ke variabel dari konteks sekitarnya. Closures setara dengan lambda atau fungsi anonim di bahasa pemrograman lain.

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
Semantik capture

Closures menangkap variabel secara referensi secara default. Untuk menangkap secara nilai, gunakan capture list: { [count] in ... }.

Manajemen Memori dan ARC

6. Bagaimana cara kerja ARC (Automatic Reference Counting)?

ARC mengelola memori secara otomatis dengan menghitung strong references ke setiap instance class. Ketika hitungan turun ke nol, instance tersebut didealokasi. Berbeda dengan garbage collection, ARC bersifat deterministik dan dapat diprediksi.

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. Apa itu retain cycle dan bagaimana cara mencegahnya?

Retain cycle terjadi ketika dua objek saling memegang strong reference satu sama lain, sehingga mencegah dealokasi. Kata kunci weak dan unowned digunakan untuk memutus siklus ini.

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. Apa perbedaan antara weak dan unowned?

Keduanya memutus retain cycle, tetapi dengan jaminan berbeda. weak bersifat opsional dan menjadi nil jika objek yang direferensikan didealokasi. unowned mengasumsikan objek selalu ada dan akan crash jika diakses setelah dealokasi.

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

Aturan: gunakan weak secara default. Gunakan unowned hanya ketika lifetime objek yang direferensikan dijamin sama atau lebih panjang.

Siap menguasai wawancara iOS Anda?

Berlatih dengan simulator interaktif, flashcards, dan tes teknis kami.

Protocols dan Generics

9. Jelaskan protocols di Swift

Protocols mendefinisikan kontrak (properti dan method yang wajib) yang harus diimplementasikan oleh tipe yang mengadopsinya. Protocols memungkinkan polimorfisme dan merupakan fondasi dari Protocol-Oriented Programming (POP) di 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. Apa itu associated type?

Associated types memungkinkan protocols mendefinisikan tipe generik yang akan ditentukan oleh tipe yang mengadopsinya. Mekanisme ini membuat protocols seperti Collection begitu fleksibel.

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. Bagaimana cara kerja generics?

Generics memungkinkan penulisan kode yang fleksibel dan dapat digunakan kembali dengan tipe apapun. Generics menghindari duplikasi kode sambil tetap menjaga keamanan tipe saat kompilasi.

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. Jelaskan protokol Codable

Codable (alias dari Encodable & Decodable) memungkinkan serialisasi otomatis tipe Swift ke dan dari format seperti JSON. Compiler menghasilkan implementasinya secara otomatis jika semua properti juga 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)

Concurrency dan async/await

13. Bagaimana cara kerja async/await di Swift?

async/await menyederhanakan kode asynchronous dengan memungkinkan operasi non-blocking ditulis secara sekuensial. Fungsi async dapat ditangguhkan tanpa memblokir thread, sehingga memungkinkan task lain berjalan.

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. Apa itu Actor?

Actors adalah tipe referensi yang melindungi state internalnya dari akses concurrent. Actors menjamin bahwa hanya satu task pada satu waktu yang dapat mengakses properti mutable-nya, sehingga mengeliminasi data races.

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. Jelaskan Task dan TaskGroup

Task membuat unit kerja asynchronous. TaskGroup memungkinkan menjalankan beberapa task secara paralel dan mengumpulkan hasilnya.

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. Bagaimana cara kerja @MainActor?

@MainActor memastikan kode berjalan di main thread. Ini sangat penting untuk pembaruan UI yang harus selalu dieksekusi di main thread.

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

Pola dan Arsitektur

17. Jelaskan pola Delegate

Pola Delegate memungkinkan sebuah objek mendelegasikan tanggung jawab tertentu ke objek lain. Pola ini sangat umum digunakan di UIKit (UITableViewDelegate, UITextFieldDelegate, dan lain-lain).

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. Apa itu pola MVVM?

MVVM (Model-View-ViewModel) memisahkan logika presentasi dari view. ViewModel mengekspos data yang dapat diobservasi yang ditampilkan oleh View, tanpa mengetahui detail 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() }
    }
}

Siap menguasai wawancara iOS Anda?

Berlatih dengan simulator interaktif, flashcards, dan tes teknis kami.

19. Jelaskan dependency injection

Dependency injection melibatkan penyediaan dependensi sebuah objek dari luar, bukan membuatnya secara internal. Pendekatan ini meningkatkan testability dan decoupling.

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. Bagaimana cara mengimplementasikan pola Singleton?

Singleton memastikan sebuah class hanya memiliki satu instance yang dapat diakses secara global. Di Swift, hal ini dilakukan dengan properti static dan initializer private.

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)
Hati-hati dengan Singleton

Singleton menciptakan state global yang mempersulit testing dan decoupling. Lebih baik gunakan dependency injection jika memungkinkan.

Konsep Lanjutan

21. Jelaskan @escaping untuk closures

Sebuah closure bersifat @escaping ketika dapat dipanggil setelah fungsi yang menerimanya selesai dieksekusi. Hal ini umum terjadi pada callback asynchronous dan penyimpanan closure.

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. Apa itu @propertyWrapper?

Property wrappers mengenkapsulasi logika penyimpanan dan akses sebuah properti. Mekanisme ini memungkinkan penggunaan kembali pola seperti validasi, logging, atau persistensi.

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. Jelaskan result builders

Result builders memungkinkan pembuatan nilai kompleks dengan sintaks deklaratif. Ini adalah mekanisme di balik sintaks 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. Bagaimana cara kerja some dan opaque types?

some mendeklarasikan opaque type: tipe yang tepat diketahui oleh compiler tetapi disembunyikan dari pemanggil. Ini sangat penting untuk protocols dengan associated types dan memungkinkan optimisasi.

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. Jelaskan Swift macros

Macros (Swift 5.9+) menghasilkan kode saat kompilasi. Macros mengurangi boilerplate sambil tetap menjaga keamanan tipe dan kemampuan debugging.

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
}
Debugging macros

Di Xcode, klik kanan pada macro lalu pilih "Expand Macro" untuk melihat kode yang dihasilkan. Berguna untuk memahami dan debugging.

Kesimpulan

25 pertanyaan ini mencakup dasar-dasar yang harus dikuasai setiap developer Swift untuk berhasil dalam interview iOS. Dari manajemen memori dengan ARC hingga pola concurrency modern, setiap konsep terintegrasi dalam ekosistem Swift.

Checklist Review

  • Kuasai optionals dan berbagai metode unwrapping-nya
  • Pahami perbedaan antara tipe nilai dan tipe referensi
  • Ketahui cara mengidentifikasi dan mengatasi retain cycles dengan weak/unowned
  • Gunakan async/await dan actors untuk concurrency
  • Implementasikan pola Delegate, MVVM, dan Dependency Injection
  • Pahami protocols, generics, dan Codable
  • Kuasai konsep lanjutan: property wrappers, result builders, macros

Sumber Belajar Tambahan

Untuk pembelajaran lebih mendalam, dokumentasi resmi Swift tetap menjadi referensi utama. Latihan rutin dengan proyek pribadi dan coding exercises membantu memantapkan pemahaman konsep-konsep ini.

Mulai berlatih!

Uji pengetahuan Anda dengan simulator wawancara dan tes teknis kami.

Tag

#swift
#ios
#interview
#career
#apple

Bagikan

Artikel terkait