iOS開発者向けSwift面接質問トップ25

iOS面接で最も頻出するSwift質問25問を網羅的に解説。オプショナル、クロージャ、ARC、プロトコル、async/awaitから高度なパターンまで、コード例付きで徹底対策。

iOS開発者向けSwift面接質問ガイド

iOS技術面接では、Swiftの基礎から高度な概念まで幅広く問われる。本ガイドでは、採用担当者が最も頻繁に出題する25の質問を、言語の基礎からモダンな並行処理パターンまで体系的にカバーする。

本ガイドの活用法

各質問にはコード例付きの詳細な解答が含まれている。質問は基礎から高度な概念へと難易度順に構成されている。

Swiftの基礎

1. letvarの違いは何ですか?

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. Swiftのオプショナルについて説明してください

オプショナルは値が存在しない可能性を表現する型である。指定された型の値かnilのどちらかを保持できる。Swiftはオプショナルを使用して、値が欠落する可能性のあるケースの明示的な処理をコンパイル時に強制し、安全性を保証する。

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. structclassの違いは何ですか?

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. switchでのパターンマッチングはどのように動作しますか?

Swiftのswitchは強力かつ網羅的であり、すべての可能なケースをカバーする必要がある。型、範囲、タプルに対するパターンマッチングに加え、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.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(自動参照カウント)はどのように動作しますか?

ARCは各クラスインスタンスへの強参照をカウントすることで、メモリを自動的に管理する。カウントがゼロになるとインスタンスは解放される。ガベージコレクションとは異なり、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. 循環参照(リテインサイクル)とは何ですか?どのように防ぎますか?

循環参照は、2つのオブジェクトが互いに強参照を保持し、解放を妨げる状態である。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. weakunownedの違いは何ですか?

どちらも循環参照を断ち切るが、保証の内容が異なる。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のプロトコルについて説明してください

プロトコルは、準拠する型が実装すべきプロパティやメソッドの契約(インターフェース)を定義する。ポリモーフィズムを実現し、SwiftにおけるProtocol-Oriented Programming(POP)の基盤となっている。

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. アソシエイテッドタイプとは何ですか?

アソシエイテッドタイプにより、プロトコルは準拠する型によって指定されるジェネリック型を定義できる。この仕組みが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プロトコルについて説明してください

CodableEncodable & 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. Swiftのasync/awaitはどのように動作しますか?

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とは何ですか?

Actorは、内部状態を並行アクセスから保護する参照型である。一度に1つのタスクのみが可変プロパティにアクセスできることを保証し、データ競合を排除する。

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. TaskTaskGroupについて説明してください

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はコードがメインスレッドで実行されることを保証する。常にメインスレッドで実行される必要があるUI更新において不可欠な仕組みである。

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. デリゲートパターンについて説明してください

デリゲートパターンは、あるオブジェクトが特定の責務を別のオブジェクトに委譲する設計パターンである。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はビューが表示する監視可能なデータを公開し、ビューの詳細を知る必要がない。

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. 依存性注入について説明してください

依存性注入とは、オブジェクトの依存関係を内部で生成するのではなく、外部から提供する設計手法である。テスタビリティと疎結合性が向上する。

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. シングルトンパターンはどのように実装しますか?

シングルトンは、クラスがグローバルにアクセス可能な唯一のインスタンスを持つことを保証するパターンである。Swiftでは、staticプロパティと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)
シングルトンの注意点

シングルトンはテストと疎結合を困難にするグローバル状態を生成する。可能な限り依存性注入を優先すべきである。

高度な概念

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とは何ですか?

プロパティラッパーは、プロパティのストレージとアクセスロジックをカプセル化する仕組みである。バリデーション、ロギング、永続化などのパターンを再利用可能にする。

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. リザルトビルダーについて説明してください

リザルトビルダーは、宣言的な構文で複雑な値を構築する機能である。SwiftUIのDSL構文を支える仕組みとなっている。

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 Type(不透明型)はどのように動作しますか?

someはOpaque Typeを宣言する。正確な型はコンパイラに認識されるが、呼び出し側からは隠蔽される。アソシエイテッドタイプを持つプロトコルに不可欠であり、最適化を可能にする。

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の質問は、iOS面接で成功するためにすべてのSwift開発者が習得すべき基礎を網羅している。ARCによるメモリ管理からモダンな並行処理パターンまで、各概念はSwiftエコシステムに統合されている。

復習チェックリスト

  • オプショナルと各種アンラップ方法を習得する
  • 値型と参照型の違いを理解する
  • weak/unownedによる循環参照の検出と解決方法を把握する
  • async/awaitとActorを使用した並行処理を実践する
  • デリゲート、MVVM、依存性注入パターンを実装できるようにする
  • プロトコル、ジェネリクス、Codableを理解する
  • 高度な概念を把握する: プロパティラッパー、リザルトビルダー、マクロ

追加リソース

より深い学習には、Apple公式のSwiftドキュメントが最も信頼できるリファレンスである。個人プロジェクトやコーディング演習での定期的な実践が、これらの概念の定着に役立つ。

今すぐ練習を始めましょう!

面接シミュレーターと技術テストで知識をテストしましょう。

タグ

#swift
#ios
#interview
#career
#apple

共有

関連記事