Irbisa · Шпаргалка
Архитектура iOS
MVC, MVVM, VIPER, Coordinators, TCA, Clean Swift
Что такое MVC в UIKit и почему он так часто превращается в Massive View Controller?
Что такое внедрение зависимостей и как оно делает view model тестируемой?
protocol UserAPI: Sendable { func user(id: String) async throws -> User} @MainActor @Observablefinal class ProfileViewModel {…Что такое MVVM, какую проблему он решает и как выглядит в UIKit и в SwiftUI?
@MainActor @Observablefinal class ProfileViewModel { // no import UIKit or SwiftUI private(set) var name = "" private let api: ProfileAPI init(api: ProfileAPI) { self.api = api }…Найдите баг: если быстро набрать
"iphone", в списке иногда остаются результаты для"iph".@MainActor @Observablefinal class SearchViewModel { private(set) var results: [Product] = [] private let api: any ProductAPI & Sendable init(api: any ProductAPI & Sendable) { self.api = api } func queryChanged(_ query: String) { Task { let found = (try? await api.search(query)) ?? [] results = found } }}Делегат, замыкание, NotificationCenter, KVO, Combine или AsyncSequence: как выбрать способ общения двух объектов?
// Delegate: long-lived, several messages, one known counterpartprotocol BarcodeScannerDelegate: AnyObject { func scanner(_ scanner: BarcodeScanner, didFind code: String) func scannerDidFail(_ scanner: BarcodeScanner, error: Error)}final class BarcodeScanner {…Какие классические паттерны проектирования используют UIKit и Foundation и где их видно?
Что такое принципы SOLID, с примером из iOS для каждого?
Что такое паттерн Coordinator и когда он оправдан?
@MainActor protocol Coordinator: AnyObject { // conforming classes inherit the isolation func start()} final class AppCoordinator: Coordinator { private var children: [Coordinator] = []…После каждого завершённого оформления заказа его
CheckoutCoordinatorостаётся в памяти. Что не так в этом коде?@MainActorfinal class AppCoordinator { private var children: [AnyObject] = [] private let nav: UINavigationController init(nav: UINavigationController) { self.nav = nav } func showCheckout() { let checkout = CheckoutCoordinator(nav: nav) checkout.onFinish = { self.nav.popToRootViewController(animated: true) } children.append(checkout) checkout.start() }}Пуш-уведомление должно открыть конкретный комментарий к конкретному посту. Где живёт это состояние навигации?
enum Route: Hashable { case post(id: String) case comment(postID: String, commentID: String)} @Observable…Что такое паттерн Repository и зачем держать DTO, доменные модели и состояние экрана отдельными типами?
// The wire format: pessimistic, matches the server exactlystruct PostDTO: Decodable { let id: Int let author_name: String? let created_at: String let status: String // never an enum here…Когда DI-контейнер вроде Swinject или Factory оправдан по сравнению с обычным внедрением через конструктор?
// wrong: a runtime registry; a missing line fails only when the code runslet container = Container()container.register(AuthService.self) { _ in LiveAuthService() }let auth = container.resolve(AuthService.self)! // crash in production // right: the initialiser states the requirement; the compiler enforces it…Найдите баг:
testFreshInstallShowsOnboardingпроходит отдельно на чистом симуляторе, но падает после прогона всего класса, и дальше падает всегда.@MainActor final class OnboardingViewModel { private let defaults: UserDefaults init(defaults: UserDefaults = .standard) { self.defaults = defaults } var showsOnboarding: Bool { !defaults.bool(forKey: "onboardingDone") } func finish() { defaults.set(true, forKey: "onboardingDone") }}@MainActor final class OnboardingTests: XCTestCase { func testFreshInstallShowsOnboarding() { XCTAssertTrue(OnboardingViewModel().showsOnboarding) } func testFinishHidesOnboarding() { let model = OnboardingViewModel() model.finish() XCTAssertFalse(model.showsOnboarding) }}Как VIPER и Clean Swift (VIP) делят экран и что даёт эта церемония?
// Clean Swift scene: VC -> Interactor -> Presenter -> VC, one direction only@MainActor protocol OrderBusinessLogic { func fetchOrder(request: Order.Fetch.Request) }protocol OrderPresentationLogic { func presentOrder(response: Order.Fetch.Response) }protocol OrderDisplayLogic: AnyObject { func displayOrder(viewModel: Order.Fetch.ViewModel) }@MainActor protocol OrderDataStore { var order: OrderEntity? { get } }protocol OrderLoading { func load(id: String) async throws -> OrderEntity }…Что такое feature flags и как с их помощью безопасно выкатить рискованное изменение?
Сравните MVC, MVP, MVVM, VIPER и TCA: как выбрать архитектуру для фичи или команды?
Что такое однонаправленный поток данных и какие проблемы решает с его помощью TCA?
import ComposableArchitecture struct NumberFactClient { var fetch: @Sendable (Int) async throws -> String }extension NumberFactClient: DependencyKey { static let liveValue = NumberFactClient { "\($0) is a number" }}…Что такое Clean Architecture в iOS и когда слой use case окупается?
// Domain: plain Swift, no framework importsstruct Article: Identifiable, Equatable, Sendable { let id: String let title: String}…Как структурировать большое iOS-приложение (по фичам или по слоям) и где проводить границы модулей?
// swift-tools-version: 6.0import PackageDescription let package = Package( name: "Profile", platforms: [.iOS(.v17)],…Оформление заказа из трёх шагов делит один черновик, и пользователи жалуются, что новый заказ открывается с адресом из предыдущего. Где должен жить этот черновик?
// wrong: app-wide lifetime, so the next checkout inherits the last onefinal class CheckoutDraft { static let shared = CheckoutDraft() } // right: the draft's lifetime is the flow's lifetime@Observablefinal class CheckoutDraft {…Вы добавляете SwiftUI в десятилетнее UIKit-приложение. Где проводить границу?
// UIKit pushes a SwiftUI screen; the coordinator still owns navigation@MainActorfinal class SettingsCoordinator { private let nav: UINavigationController private let session: SessionStore // @Observable, shared by both worlds…В приложении 300 тысяч строк на view controller-ах, а фичи останавливать нельзя. Как прийти к тестируемой архитектуре?
// before: untestable: the dependency is fetched, not receivedfinal class FeedViewController: UIViewController { private var posts: [Post] = [] func load() { Task { posts = (try? await NetworkManager.shared.feed()) ?? [] }…Как бы вы спроектировали конвейер загрузки и кэширования картинок для прокручиваемой ленты?
Как бы вы спроектировали offline-first синхронизацию для приложения заметок, включая разрешение конфликтов?