# iOS開発者向けSwift面接質問トップ25 > iOS面接で最も頻出するSwift質問25問を網羅的に解説。オプショナル、クロージャ、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. Swiftのオプショナルについて説明してください オプショナルは値が存在しない可能性を表現する型である。指定された型の値か`nil`のどちらかを保持できる。Swiftはオプショナルを使用して、値が欠落する可能性のあるケースの明示的な処理をコンパイル時に強制し、安全性を保証する。 ```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. `switch`でのパターンマッチングはどのように動作しますか? Swiftの`switch`は強力かつ網羅的であり、すべての可能なケースをカバーする必要がある。型、範囲、タプルに対するパターンマッチングに加え、`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. クロージャとその構文について説明してください クロージャは、周囲のコンテキストから変数への参照をキャプチャして保持する自己完結型のコードブロックである。他の言語におけるラムダや無名関数に相当する。 ```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(自動参照カウント)はどのように動作しますか? ARCは各クラスインスタンスへの強参照をカウントすることで、メモリを自動的に管理する。カウントがゼロになるとインスタンスは解放される。ガベージコレクションとは異なり、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. 循環参照(リテインサイクル)とは何ですか?どのように防ぎますか? 循環参照は、2つのオブジェクトが互いに強参照を保持し、解放を妨げる状態である。`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のプロトコルについて説明してください プロトコルは、準拠する型が実装すべきプロパティやメソッドの契約(インターフェース)を定義する。ポリモーフィズムを実現し、SwiftにおけるProtocol-Oriented Programming(POP)の基盤となっている。 ```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. アソシエイテッドタイプとは何ですか? アソシエイテッドタイプにより、プロトコルは準拠する型によって指定されるジェネリック型を定義できる。この仕組みが`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. Swiftのasync/awaitはどのように動作しますか? `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とは何ですか? Actorは、内部状態を並行アクセスから保護する参照型である。一度に1つのタスクのみが可変プロパティにアクセスできることを保証し、データ競合を排除する。 ```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`はコードがメインスレッドで実行されることを保証する。常にメインスレッドで実行される必要があるUI更新において不可欠な仕組みである。 ```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. デリゲートパターンについて説明してください デリゲートパターンは、あるオブジェクトが特定の責務を別のオブジェクトに委譲する設計パターンである。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はビューが表示する監視可能なデータを公開し、ビューの詳細を知る必要がない。 ```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. 依存性注入について説明してください 依存性注入とは、オブジェクトの依存関係を内部で生成するのではなく、外部から提供する設計手法である。テスタビリティと疎結合性が向上する。 ```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. シングルトンパターンはどのように実装しますか? シングルトンは、クラスがグローバルにアクセス可能な唯一のインスタンスを持つことを保証するパターンである。Swiftでは、staticプロパティとprivateイニシャライザを使用して実装する。 ```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) ``` > **シングルトンの注意点** > > シングルトンはテストと疎結合を困難にするグローバル状態を生成する。可能な限り依存性注入を優先すべきである。 ## 高度な概念 ### 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`とは何ですか? プロパティラッパーは、プロパティのストレージとアクセスロジックをカプセル化する仕組みである。バリデーション、ロギング、永続化などのパターンを再利用可能にする。 ```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. リザルトビルダーについて説明してください リザルトビルダーは、宣言的な構文で複雑な値を構築する機能である。SwiftUIのDSL構文を支える仕組みとなっている。 ```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 Type(不透明型)はどのように動作しますか? `some`はOpaque Typeを宣言する。正確な型はコンパイラに認識されるが、呼び出し側からは隠蔽される。アソシエイテッドタイプを持つプロトコルに不可欠であり、最適化を可能にする。 ```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の質問は、iOS面接で成功するためにすべてのSwift開発者が習得すべき基礎を網羅している。ARCによるメモリ管理からモダンな並行処理パターンまで、各概念はSwiftエコシステムに統合されている。 ### 復習チェックリスト - オプショナルと各種アンラップ方法を習得する - 値型と参照型の違いを理解する - weak/unownedによる循環参照の検出と解決方法を把握する - async/awaitとActorを使用した並行処理を実践する - デリゲート、MVVM、依存性注入パターンを実装できるようにする - プロトコル、ジェネリクス、Codableを理解する - 高度な概念を把握する: プロパティラッパー、リザルトビルダー、マクロ ### 追加リソース より深い学習には、Apple公式のSwiftドキュメントが最も信頼できるリファレンスである。個人プロジェクトやコーディング演習での定期的な実践が、これらの概念の定着に役立つ。 --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/ja/blog/ios/top-25-swift-interview-questions