12月 21, 2025
49 分で読める

シニア iOS 開発者向け面接質問と回答

interview
career-advice
job-search
シニア iOS 開発者向け面接質問と回答
Milad Bonakdar

Milad Bonakdar

著者

Swift、SwiftUI、アーキテクチャ、並行処理、パフォーマンス、Core Data、オフライン同期、セキュリティを実践的に確認できるシニア iOS 面接対策です。


はじめに

シニア iOS 面接では、Swift や Apple フレームワークの知識を、実際のプロダクト判断につなげられるかが見られます。アーキテクチャ、状態管理、並行処理、パフォーマンス、永続化、セキュリティ、そして現実的なトレードオフを説明できることが重要です。

このガイドでは、最初に判断を述べ、避けたい失敗を説明し、Xcode のツールやコード上の具体策まで言える回答を練習できます。シニアの回答は、単なる暗記ではなく、コードレビューで信頼される具体性が必要です。


高度な Swift と言語機能 (6つの質問)

1. Swift のメモリ管理と ARC (自動参照カウント) について説明してください。

回答: ARC は、クラスインスタンスへの参照を追跡および管理することにより、メモリを自動的に管理します。

  • 仕組み: 各クラスインスタンスには参照カウントがあります。カウントがゼロになると、インスタンスは割り当て解除されます。
  • ストロング参照: デフォルト。参照カウントを増やします。
  • ウィーク参照: 参照カウントを増やしません。インスタンスが割り当て解除されると、自動的に nil になります。
  • アンオーナー参照: 参照カウントを増やしませんが、インスタンスが常に存在することを前提としています。
  • 循環参照: 2つのオブジェクトが互いにストロング参照を保持し、割り当て解除を妨げるときに発生します。
class Person {
    var name: String
    var apartment: Apartment?
    
    init(name: String) { self.name = name }
    deinit { print("\(name) is being deallocated") }
}

class Apartment {
    var unit: String
    weak var tenant: Person?  // 循環参照を解消するために weak を使用
    
    init(unit: String) { self.unit = unit }
    deinit { print("Apartment \(unit) is being deallocated") }
}

希少性: 非常に一般的 難易度: 難しい


2. Swift のジェネリクスとは何ですか?なぜ役立つのですか?

回答: ジェネリクスを使用すると、任意の型で動作する柔軟で再利用可能な関数と型を記述できます。

  • 利点: コードの再利用性、型安全性、パフォーマンス (ランタイムオーバーヘッドなし)
  • 型制約: ジェネリック型を特定のプロトコルまたはクラスに制限します
  • 関連型: プロトコルでプレースホルダー型を定義するために使用されます
// ジェネリック関数
func swap<T>(_ a: inout T, _ b: inout T) {
    let temp = a
    a = b
    b = temp
}

// ジェネリック型
struct Stack<Element> {
    private var items: [Element] = []
    
    mutating func push(_ item: Element) {
        items.append(item)
    }
    
    mutating func pop() -> Element? {
        return items.popLast()
    }
}

// 型制約
func findIndex<T: Equatable>(of valueToFind: T, in array: [T]) -> Int? {
    for (index, value) in array.enumerated() {
        if value == valueToFind {
            return index
        }
    }
    return nil
}

希少性: 一般的 難易度: 普通


3. escaping クロージャと non-escaping クロージャの違いについて説明してください。

回答:

  • Non-escaping (デフォルト): クロージャは関数が戻る前に実行されます。コンパイラはより適切に最適化できます。
  • Escaping (@escaping): クロージャは関数よりも長く生存します (プロパティに格納され、非同期的に呼び出されます)。self を明示的にキャプチャする必要があります。
class NetworkManager {
    var completionHandlers: [() -> Void] = []
    
    // Escaping クロージャ - 後で使用するために格納
    func fetchData(completion: @escaping (Data?) -> Void) {
        URLSession.shared.dataTask(with: url) { data, _, _ in
            completion(data)  // 関数が戻った後に呼び出されます
        }.resume()
    }
    
    // Non-escaping クロージャ - すぐに実行
    func processData(_ data: Data, transform: (Data) -> String) -> String {
        return transform(data)  // 関数が戻る前に呼び出されます
    }
}

希少性: 一般的 難易度: 普通


4. mapflatMapcompactMap の違いは何ですか?

回答: これらはコレクションを変換するための高階関数です。

  • map: 各要素を変換し、結果の配列を返します
  • compactMap: map と同様ですが、nil 値を除外します
  • flatMap: ネストされた配列を単一の配列に平坦化します
let numbers = [1, 2, 3, 4, 5]

// map
let doubled = numbers.map { $0 * 2 }
// [2, 4, 6, 8, 10]

// compactMap - nil を削除
let strings = ["1", "2", "three", "4"]
let validNumbers = strings.compactMap { Int($0) }
// [1, 2, 4]

// flatMap - 配列を平坦化
let nested = [[1, 2], [3, 4], [5]]
let flattened = nested.flatMap { $0 }
// [1, 2, 3, 4, 5]

希少性: 一般的 難易度: 簡単


5. Swift のプロパティラッパーについて説明してください。

回答: プロパティラッパーは、プロパティの格納方法を管理するコードと、プロパティを定義するコードの間に分離層を追加します。

  • 組み込みの例: SwiftUI の @State@Published@AppStorage
  • カスタムラッパー: 再利用可能なプロパティの動作を定義します
@propertyWrapper
struct Capitalized {
    private var value: String = ""
    
    var wrappedValue: String {
        get { value }
        set { value = newValue.capitalized }
    }
}

struct User {
    @Capitalized var firstName: String
    @Capitalized var lastName: String
}

var user = User()
user.firstName = "john"
print(user.firstName)  // "John"

// UserDefaults ラッパー
@propertyWrapper
struct UserDefault<T> {
    let key: String
    let defaultValue: T
    
    var wrappedValue: T {
        get { UserDefaults.standard.object(forKey: key) as? T ?? defaultValue }
        set { UserDefaults.standard.set(newValue, forKey: key) }
    }
}

struct Settings {
    @UserDefault(key: "username", defaultValue: "Guest")
    static var username: String
}

希少性: 普通 難易度: 難しい


6. Result 型とは何ですか?どのように使用されますか?

回答: Result は、成功または失敗を表す enum であり、エラー処理をより明示的にします。

  • 定義: enum Result<Success, Failure: Error>
  • 利点: 型安全なエラー処理、より明確な API コントラクト、非同期コードでの関数をスローするよりも優れています
enum NetworkError: Error {
    case invalidURL
    case noData
    case decodingError
}

func fetchUser(id: Int, completion: @escaping (Result<User, NetworkError>) -> Void) {
    guard let url = URL(string: "https://api.example.com/users/\(id)") else {
        completion(.failure(.invalidURL))
        return
    }
    
    URLSession.shared.dataTask(with: url) { data, response, error in
        guard let data = data else {
            completion(.failure(.noData))
            return
        }
        
        do {
            let user = try JSONDecoder().decode(User.self, from: data)
            completion(.success(user))
        } catch {
            completion(.failure(.decodingError))
        }
    }.resume()
}

// 使用法
fetchUser(id: 1) { result in
    switch result {
    case .success(let user):
        print("User: \(user.name)")
    case .failure(let error):
        print("Error: \(error)")
    }
}

希少性: 一般的 難易度: 普通


アーキテクチャパターン (5つの質問)

7. MVVM (Model-View-ViewModel) パターンについて説明してください。

回答: MVVM は、UI ロジックをビジネスロジックから分離し、コードのテストと保守を容易にします。

Loading diagram...
  • Model: データとビジネスロジック
  • View: UI (UIViewController、SwiftUI View)
  • ViewModel: プレゼンテーションロジック、View 用にモデルデータを変換
  • 利点: テスト可能 (ViewModel には UI 依存関係がありません)、再利用可能な ViewModel、関心の明確な分離
// Model
struct User {
    let id: Int
    let firstName: String
    let lastName: String
}

// ViewModel
class UserViewModel {
    private let user: User
    
    var fullName: String {
        "\(user.firstName) \(user.lastName)"
    }
    
    var displayText: String {
        "User #\(user.id): \(fullName)"
    }
    
    init(user: User) {
        self.user = user
    }
}

// View
class UserViewController: UIViewController {
    private let viewModel: UserViewModel
    private let label = UILabel()
    
    init(viewModel: UserViewModel) {
        self.viewModel = viewModel
        super.init(nibName: nil, bundle: nil)
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        label.text = viewModel.displayText
    }
}

希少性: 非常に一般的 難易度: 普通


8. Coordinator パターンとは何ですか?なぜそれを使用するのですか?

回答: Coordinator パターンは、ナビゲーションロジックをビューコントローラから分離します。

  • 問題: UI ロジックと混在したナビゲーションロジックを持つ巨大なビューコントローラ
  • 解決策: Coordinator がナビゲーションフローを処理します
  • 利点: 再利用可能なビューコントローラ、テスト可能なナビゲーション、明確なアプリフロー
protocol Coordinator {
    var navigationController: UINavigationController { get }
    func start()
}

class AppCoordinator: Coordinator {
    let navigationController: UINavigationController
    
    init(navigationController: UINavigationController) {
        self.navigationController = navigationController
    }
    
    func start() {
        showLogin()
    }
    
    func showLogin() {
        let loginVC = LoginViewController()
        loginVC.coordinator = self
        navigationController.pushViewController(loginVC, animated: true)
    }
    
    func showHome() {
        let homeVC = HomeViewController()
        homeVC.coordinator = self
        navigationController.pushViewController(homeVC, animated: true)
    }
}

希少性: 普通 難易度: 難しい


9. iOS における依存性注入について説明してください。

回答: 依存性注入は、依存関係を内部で作成するのではなく、オブジェクトに提供するデザインパターンです。

  • 利点: テスト容易性 (モックを注入)、柔軟性、疎結合
  • 種類:
    • コンストラクタ注入: イニシャライザを介して依存関係を渡します (最も一般的)
    • プロパティ注入: 初期化後に依存関係を設定します
    • メソッド注入: メソッドパラメータとして依存関係を渡します
// 抽象化のためのプロトコル
protocol NetworkService {
    func fetchData(completion: @escaping (Result<Data, Error>) -> Void)
}

// 具体的な実装
class URLSessionNetworkService: NetworkService {
    func fetchData(completion: @escaping (Result<Data, Error>) -> Void) {
        // 実際のネットワーク呼び出し
    }
}

// テスト用のモック
class MockNetworkService: NetworkService {
    func fetchData(completion: @escaping (Result<Data, Error>) -> Void) {
        completion(.success(Data()))
    }
}

// 依存性注入を備えた ViewModel
class UserViewModel {
    private let networkService: NetworkService
    
    // コンストラクタ注入
    init(networkService: NetworkService) {
        self.networkService = networkService
    }
    
    func loadUsers() {
        networkService.fetchData { result in
            // 結果を処理
        }
    }
}

// 使用法
let viewModel = UserViewModel(networkService: URLSessionNetworkService())

// テスト
let testViewModel = UserViewModel(networkService: MockNetworkService())

希少性: 一般的 難易度: 普通


10. Repository パターンとは何ですか?

回答: Repository パターンは、データアクセスロジックを抽象化し、データ操作のためのクリーンな API を提供します。

  • 利点: データロジックの一元化、データソースの切り替えが容易 (API、データベース、キャッシュ)、テスト可能
  • 実装: Repository は複数のデータソース間で連携します
protocol UserRepository {
    func getUser(id: Int) async throws -> User
    func saveUser(_ user: User) async throws
}

class UserRepositoryImpl: UserRepository {
    private let apiService: APIService
    private let cacheService: CacheService
    
    init(apiService: APIService, cacheService: CacheService) {
        self.apiService = apiService
        self.cacheService = cacheService
    }
    
    func getUser(id: Int) async throws -> User {
        // 最初にキャッシュを確認
        if let cachedUser = try? await cacheService.getUser(id: id) {
            return cachedUser
        }
        
        // API からフェッチ
        let user = try await apiService.fetchUser(id: id)
        
        // キャッシュを更新
        try? await cacheService.saveUser(user)
        
        return user
    }
    
    func saveUser(_ user: User) async throws {
        try await apiService.updateUser(user)
        try await cacheService.saveUser(user)
    }
}

希少性: 普通 難易度: 普通


11. MVC、MVP、MVVM の違いについて説明してください。

回答:

  • MVC (Model-View-Controller):
    • Apple のデフォルトパターン
    • Controller は Model と View の間を仲介します
    • 問題: 巨大なビューコントローラ
  • MVP (Model-View-Presenter):
    • Presenter がすべての UI ロジックを処理します
    • View は受動的です (データを表示するだけです)
    • MVC よりも優れたテスト容易性
  • MVVM (Model-View-ViewModel):
    • ViewModel はデータストリームを公開します
    • View は ViewModel にバインドします
    • リアクティブプログラミングに最適 (Combine、RxSwift)

希少性: 一般的 難易度: 難しい


パフォーマンスと最適化 (5つの質問)

12. テーブルビューとコレクションビューのパフォーマンスを最適化するにはどうすればよいですか?

回答: 複数の戦略により、スクロールパフォーマンスが向上します。

  1. セルの再利用: dequeueReusableCell を適切に使用します
  2. 重い処理の回避: cellForRowAt でコストのかかる計算を実行しないでください
  3. 画像の最適化:
    • 画像を表示サイズにリサイズします
    • 画像処理にバックグラウンドスレッドを使用します
    • デコードされた画像をキャッシュします
  4. プリフェッチ: UITableViewDataSourcePrefetching を実装します
  5. 高さのキャッシュ: 計算されたセルの高さをキャッシュします
  6. 透明度の回避: 不透明なビューはより速くレンダリングされます
class ImageTableViewCell: UITableViewCell {
    static let identifier = "ImageCell"
    private let customImageView = UIImageView()
    
    override func prepareForReuse() {
        super.prepareForReuse()
        customImageView.image = nil  // 古い画像をクリア
    }
}

// ビューコントローラ内
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: ImageTableViewCell.identifier, for: indexPath) as! ImageTableViewCell
    
    // 画像を非同期的にロード
    DispatchQueue.global(qos: .userInitiated).async {
        let image = self.loadAndResizeImage(at: indexPath)
        DispatchQueue.main.async {
            cell.imageView?.image = image
        }
    }
    
    return cell
}

希少性: 非常に一般的 難易度: 普通


13. Instruments と、パフォーマンスプロファイリングにどのように使用するかを説明してください。

回答: Instruments は、Xcode のパフォーマンス分析ツールです。

  • 一般的な Instruments:
    • Time Profiler: CPU を集中的に使用するコードを特定します
    • Allocations: メモリ割り当てとリークを追跡します
    • Leaks: メモリリークを検出します
    • Network: ネットワークアクティビティを監視します
    • Energy Log: バッテリーの使用状況を分析します
  • ワークフロー:
    1. アプリをプロファイル (Cmd+I)
    2. Instruments を選択
    3. アプリを記録して操作
    4. コールツリーとタイムラインを分析
    5. ボトルネックを特定

希少性: 一般的 難易度: 普通


14. メモリリークを検出して修正するにはどうすればよいですか?

回答: メモリリークは、不要になったオブジェクトが割り当て解除されない場合に発生します。

  • 一般的な原因:
    • 循環参照 (ストロング参照サイクル)
    • self を強くキャプチャするクロージャ
    • weak としてマークされていないデリゲート
  • 検出:
    • Instruments Leaks ツール
    • Xcode の Memory Graph Debugger
    • メモリ使用量の増加を監視
  • 修正:
    • デリゲートに weak または unowned を使用します
    • クロージャで [weak self] または [unowned self] を使用します
    • 循環参照を解消します
class ViewController: UIViewController {
    var closure: (() -> Void)?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // BAD - 循環参照
        closure = {
            self.view.backgroundColor = .red
        }
        
        // GOOD - weak self
        closure = { [weak self] in
            self?.view.backgroundColor = .red
        }
        
        // GOOD - unowned self (self が常に存在する場合)
        closure = { [unowned self] in
            self.view.backgroundColor = .red
        }
    }
}

希少性: 非常に一般的 難易度: 普通


15. アプリの起動を最適化するためにどのような手法を使用しますか?

回答: アプリの起動が速くなると、ユーザーエクスペリエンスが向上します。

  1. 遅延ロード: オブジェクトが必要なときにのみ初期化します
  2. Dylib ロードの削減: 動的ライブラリを最小限に抑えます
  3. application:didFinishLaunching の最適化:
    • 重要でない作業をバックグラウンドに移動します
    • 重い初期化を延期します
  4. バイナリサイズ: バイナリが小さいほどロードが速くなります
  5. 重い処理の回避: メインスレッドをブロックしないでください
  6. 測定: Instruments の App Launch テンプレートを使用します
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    
    // 重要: ウィンドウとルートビューコントローラを設定
    setupWindow()
    
    // 重要でない作業を延期
    DispatchQueue.main.async {
        self.setupAnalytics()
        self.setupCrashReporting()
    }
    
    // バックグラウンド作業
    DispatchQueue.global(qos: .background).async {
        self.preloadData()
    }
    
    return true
}

希少性: 一般的 難易度: 普通


16. 画像のキャッシュとロードをどのように処理しますか?

回答: 効率的な画像処理は、パフォーマンスにとって非常に重要です。

  • 戦略:
    • メモリキャッシュ: 高速アクセス、サイズ制限
    • ディスクキャッシュ: 永続的、大容量
    • ダウンロード: ネットワークからフェッチ
  • ライブラリ: SDWebImage、Kingfisher (キャッシュを自動的に処理)
  • カスタム実装:
class ImageCache {
    static let shared = ImageCache()
    
    private let memoryCache = NSCache<NSString, UIImage>()
    private let fileManager = FileManager.default
    private let cacheDirectory: URL
    
    private init() {
        cacheDirectory = fileManager.urls(for: .cachesDirectory, in: .userDomainMask)[0]
            .appendingPathComponent("ImageCache")
        try? fileManager.createDirectory(at: cacheDirectory, withIntermediateDirectories: true)
    }
    
    func loadImage(from url: URL, completion: @escaping (UIImage?) -> Void) {
        let key = url.absoluteString as NSString
        
        // メモリキャッシュを確認
        if let cachedImage = memoryCache.object(forKey: key) {
            completion(cachedImage)
            return
        }
        
        // ディスクキャッシュを確認
        let fileURL = cacheDirectory.appendingPathComponent(url.lastPathComponent)
        if let diskImage = UIImage(contentsOfFile: fileURL.path) {
            memoryCache.setObject(diskImage, forKey: key)
            completion(diskImage)
            return
        }
        
        // ダウンロード
        URLSession.shared.dataTask(with: url) { data, _, _ in
            guard let data = data, let image = UIImage(data: data) else {
                completion(nil)
                return
            }
            
            // キャッシュに保存
            self.memoryCache.setObject(image, forKey: key)
            try? data.write(to: fileURL)
            
            DispatchQueue.main.async {
                completion(image)
            }
        }.resume()
    }
}

希少性: 一般的 難易度: 難しい


並行処理と非同期プログラミング (4つの質問)

17. Swift の async/await について説明してください。

回答: Swift 5.5 で導入された Swift の最新の並行処理モデル。

  • 利点: 完了ハンドラよりもクリーンな構文、より簡単なエラー処理、コンパイラによるスレッド安全性の強制
  • キーワード:
    • async: 中断できる関数をマークします
    • await: 中断ポイントをマークします
    • Task: 新しい非同期コンテキストを作成します
    • actor: スレッドセーフな参照型
// 完了ハンドラを使用した古い方法
func fetchUser(id: Int, completion: @escaping (Result<User, Error>) -> Void) {
    URLSession.shared.dataTask(with: url) { data, response, error in
        // 結果を処理
    }.resume()
}

// async/await を使用した新しい方法
func fetchUser(id: Int) async throws -> User {
    let (data, _) = try await URLSession.shared.data(from: url)
    let user = try JSONDecoder().decode(User.self, from: data)
    return user
}

// 使用法
Task {
    do {
        let user = try await fetchUser(id: 1)
        print(user.name)
    } catch {
        print("Error: \(error)")
    }
}

// 並列実行
async let user1 = fetchUser(id: 1)
async let user2 = fetchUser(id: 2)
let users = try await [user1, user2]

希少性: 非常に一般的 難易度: 難しい


18. Swift の Actors とは何ですか?

回答: Actors は、可変の状態をデータ競合から保護する参照型です。

  • スレッド安全性: 一度に 1 つのタスクだけが Actor の可変の状態にアクセスできます
  • 自動同期: コンパイラは安全なアクセスを強制します
  • Main Actor: UI 更新用の特別な Actor
actor BankAccount {
    private var balance: Double = 0
    
    func deposit(amount: Double) {
        balance += amount
    }
    
    func withdraw(amount: Double) -> Bool {
        guard balance >= amount else { return false }
        balance -= amount
        return true
    }
    
    func getBalance() -> Double {
        return balance
    }
}

// 使用法
let account = BankAccount()

Task {
    await account.deposit(amount: 100)
    let balance = await account.getBalance()
    print("Balance: \(balance)")
}

// UI 更新用の Main Actor
@MainActor
class ViewModel: ObservableObject {
    @Published var data: [String] = []
    
    func loadData() async {
        let newData = await fetchDataFromAPI()
        // 自動的にメインスレッド上
        self.data = newData
    }
}

希少性: 普通 難易度: 難しい


19. Combine フレームワークについて説明してください。

回答: Combine は、Apple のリアクティブプログラミングフレームワークです。

  • コアコンセプト:
    • Publisher: 時間の経過とともに値を排出します
    • Subscriber: 値を受信します
    • Operator: 値を変換します
  • 利点: 宣言型、構成可能、組み込みの演算子
  • ユースケース: ネットワーク、ユーザー入力処理、データバインディング
import Combine

class UserViewModel: ObservableObject {
    @Published var searchText = ""
    @Published var users: [User] = []
    
    private var cancellables = Set<AnyCancellable>()
    
    init() {
        $searchText
            .debounce(for: .milliseconds(300), scheduler: RunLoop.main)
            .removeDuplicates()
            .filter { !$0.isEmpty }
            .flatMap { query in
                self.searchUsers(query: query)
            }
            .receive(on: DispatchQueue.main)
            .sink { [weak self] users in
                self?.users = users
            }
            .store(in: &cancellables)
    }
    
    func searchUsers(query: String) -> AnyPublisher<[User], Never> {
        // Publisher を返す
        URLSession.shared.dataTaskPublisher(for: url)
            .map(\.data)
            .decode(type: [User].self, decoder: JSONDecoder())
            .replaceError(with: [])
            .eraseToAnyPublisher()
    }
}

希少性: 一般的 難易度: 難しい


20. シリアルキューとコンカレントキューの違いは何ですか?

回答: ディスパッチキューは、タスクをシリアルまたはコンカレントに実行します。

  • シリアルキュー: 一度に 1 つのタスクを FIFO 順に実行します。タスクは前のタスクが完了するのを待ちます。
  • コンカレントキュー: 複数のタスクを同時に実行します。タスクは FIFO 順に開始されますが、任意の順序で完了できます。
  • メインキュー: UI 更新用の特別なシリアルキュー
// シリアルキュー
let serialQueue = DispatchQueue(label: "com.app.serial")
serialQueue.async { print("Task 1") }
serialQueue.async { print("Task 2") }
// 出力: Task 1, Task 2 (常に順番どおり)

// コンカレントキュー
let concurrentQueue = DispatchQueue(label: "com.app.concurrent", attributes: .concurrent)
concurrentQueue.async { print("Task A") }
concurrentQueue.async { print("Task B") }
// 出力: Task A, Task B または Task B, Task A (順序は保証されません)

// 書き込み操作のバリア
concurrentQueue.async(flags: .barrier) {
    // 排他的アクセス - 他のタスクは同時に実行されません
    print("Write operation")
}

希少性: 一般的 難易度: 普通


Core Data と永続化 (3つの質問)

21. Core Data アーキテクチャとその主要なコンポーネントについて説明してください。

回答: Core Data は、Apple のオブジェクトグラフおよび永続化フレームワークです。

Loading diagram...
  • NSManagedObjectModel: スキーマ定義 (エンティティ、属性、リレーションシップ)
  • NSPersistentStoreCoordinator: コンテキストとストアの間を調整します
  • NSManagedObjectContext: オブジェクトの作業メモリ (スクラッチパッドのようなもの)
  • NSPersistentStore: 実際のストレージ (SQLite、バイナリ、インメモリ)

希少性: 一般的 難易度: 普通


22. Core Data での並行処理をどのように処理しますか?

回答: Core Data コンテキストはスレッドセーフではありません。適切な並行処理パターンを使用してください。

  • コンテキストタイプ:
    • メインキューコンテキスト: UI 操作用
    • プライベートキューコンテキスト: バックグラウンド操作用
  • ベストプラクティス:
    • スレッド間で管理オブジェクトを渡さないでください
    • コンテキスト操作には perform または performAndWait を使用します
    • コンテキスト間でオブジェクト ID を渡します
class CoreDataManager {
    let persistentContainer: NSPersistentContainer
    
    var viewContext: NSManagedObjectContext {
        return persistentContainer.viewContext
    }
    
    func performBackgroundTask(_ block: @escaping (NSManagedObjectContext) -> Void) {
        persistentContainer.performBackgroundTask { context in
            block(context)
            
            if context.hasChanges {
                try? context.save()
            }
        }
    }
}

// 使用法
coreDataManager.performBackgroundTask { context in
    let user = User(context: context)
    user.name = "John"
    // コンテキストは自動的に保存されます
}

希少性: 普通 難易度: 難しい


23. NSFetchedResultsController とは何ですか?いつ使用しますか?

回答: NSFetchedResultsController は、テーブル/コレクションビューの Core Data の結果を効率的に管理します。

  • 利点:
    • 自動変更追跡
    • メモリ効率 (バッチ処理)
    • セクション管理
    • 自動 UI 更新
  • ユースケース: テーブル/コレクションビューに Core Data オブジェクトを表示する
class UsersViewController: UITableViewController, NSFetchedResultsControllerDelegate {
    var fetchedResultsController: NSFetchedResultsController<User>!
    
    func setupFetchedResultsController() {
        let fetchRequest: NSFetchRequest<User> = User.fetchRequest()
        fetchRequest.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
        
        fetchedResultsController = NSFetchedResultsController(
            fetchRequest: fetchRequest,
            managedObjectContext: context,
            sectionNameKeyPath: nil,
            cacheName: nil
        )
        
        fetchedResultsController.delegate = self
        try? fetchedResultsController.performFetch()
    }
    
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return fetchedResultsController.sections?[section].numberOfObjects ?? 0
    }
    
    func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
        tableView.reloadData()
    }
}

希少性: 普通 難易度: 普通


システム設計とベストプラクティス (2つの質問)

24. オフラインファーストのモバイルアプリをどのように設計しますか?

回答: オフラインファーストのアプリは、インターネットがなくても動作し、接続時に同期します。

  • アーキテクチャ:
    • 真実のソースとしてのローカルデータベース (Core Data、Realm)
    • サーバーと調整するための同期レイヤー
    • 競合解決戦略
  • 戦略:
    • 楽観的な UI: 変更をすぐに表示し、バックグラウンドで同期します
    • 操作のキュー: 失敗したリクエストを保存し、オンライン時に再試行します
    • タイムスタンプ/バージョン: データの鮮度を追跡します
  • 課題:
    • 競合解決 (最後の書き込みが優先、マージ、ユーザー選択)
    • データの一貫性
    • ストレージ制限

希少性: 普通 難易度: 難しい


25. iOS でのアプリのセキュリティ戦略は何ですか?

回答: 複数のレイヤーがアプリとユーザーデータを保護します。

  1. データ保護:
    • 機密データ (パスワード、トークン) のキーチェーン
    • 保存時のデータの暗号化
    • Data Protection API の使用
  2. ネットワークセキュリティ:
    • HTTPS のみ (App Transport Security)
    • 重要な API の証明書ピンニング
    • SSL 証明書の検証
  3. コードセキュリティ:
    • 機密ロジックの難読化
    • Jailbreak の検出
    • ハードコードされたシークレットの禁止
  4. 認証:
    • 生体認証 (Face ID、Touch ID)
    • 安全なトークンストレージ
    • トークン更新メカニズム
  5. 入力検証:
    • ユーザー入力をサニタイズ
    • インジェクション攻撃の防止
// キーチェーンストレージ
Newsletter subscription

実際に機能する週次のキャリアのヒント

最新の洞察をメールボックスに直接お届けします

採用担当者に目立ち、夢の仕事を手に入れよう

ATSを通過し、採用担当者を感動させるAI搭載の履歴書でキャリアを変えた数千人の仲間に加わりましょう。

今すぐ作成を開始

この投稿を共有

面接のコールバックを2倍に

求人内容に合わせて履歴書をカスタマイズする候補者は、2.5倍多くの面接を獲得します。当社のAIを使用して、すべての応募に対して即座に自動カスタマイズできます。