Irbisa · Шпаргалка
System Design на iOS
Раунд проектирования: лента, чат, трекинг в реальном времени, загрузки, пагинация, кэш, идемпотентность, server-driven UI, аналитика, суперприложения
У вас от 45 до 60 минут, чтобы спроектировать фичу iOS-приложения на доске. Как распределить время и что на самом деле оценивает интервьюер?
REST, GraphQL или gRPC: какой стиль API вы предложите для iOS-клиента и чего каждый из них стоит приложению?
Почему offset-пагинация показывает дубли и пропуски в ленте, которая меняется, пока её листают, и что исправляет курсор?
-- Offset: "skip 40 rows". One post inserted at the top shifts every row down,-- so page 3 starts with the last post of page 2. A deleted post is skipped the same way.SELECT id, created_at FROM postsORDER BY created_at DESC, id DESCLIMIT 20 OFFSET 40;…Каждый экран должен быть «свежим», поэтому на холодном старте приложение шлёт сорок запросов. Какие данные вы кэшируете, на сколько и где устаревшие данные допустимы?
Главный экран банка показывает шесть виджетов из шести сервисов и остаётся пустым, пока не ответит самый медленный. Что вы поменяете на клиенте и на бэкенде?
enum Section: CaseIterable, Sendable { case accounts, cards, loans, savings, cashback, offers }enum SectionState: Sendable { case loading, loaded(SectionData), failed } @MainActor @Observablefinal class HomeModel { private(set) var sections: [Section: SectionState] = [:] // filled from the disk cache first…Найдите баг: на слабой сети быстро поставьте лайк и тут же снимите его: сердечко остаётся закрашенным, хотя на сервере лайка нет.
@MainActor final class PostCellModel { private(set) var post: Post private let api: any LikeAPI init(post: Post, api: any LikeAPI) { self.post = post; self.api = api } func likeTapped() { post.isLiked.toggle() // optimistic: the heart fills at once post.likeCount += post.isLiked ? 1 : -1 Task { do { try await api.toggleLike(postID: post.id) } catch { post.isLiked.toggle() // roll back post.likeCount += post.isLiked ? 1 : -1 } } }}Запрос на оплату отвалился по таймауту, и пользователь снова нажимает «Оплатить». Как клиент и API гарантируют, что карту спишут только один раз?
// The key names the payment, not the HTTP requeststruct PaymentIntent: Codable, Sendable { let key: UUID let orderID: String let amount: Decimal}…Распродажа должна закончиться в полночь, но часть пользователей открывает её и на следующее утро. Каким часам доверяет приложение и кто обеспечивает окончание?
// The API sends absolute instants: { "serverTime": "...Z", "offers": [{ "endsAt": "...Z" }] }struct Offer: Decodable, Sendable { let id: String let endsAt: Date // an instant in UTC, never "until midnight"}…Спроектируйте ленту приложения вроде Instagram: поток данных, пагинация, предзагрузка, медиа и возврат пользователя к тому же посту после перезапуска.
Спроектируйте просмотр сторис как в Instagram: что вы предзагружаете, сколько видеоплееров держите живыми и каков бюджет памяти?
Спроектируйте публикацию объявления на маркетплейсе с десятью фото: сжатие, очередь загрузки, повторы и черновик, который переживает убийство приложения.
Спроектируйте клиент мессенджера вроде Telegram: транспорт, статусы сообщений, локальное хранилище, порядок и что происходит офлайн или в фоне.
Спроектируйте отслеживание курьера в реальном времени: как его координаты попадают на экран клиента и что меняется, когда приложение в фоне или телефон заблокирован?
Как бы вы построили собственный слой аналитики приложения: пакетная отправка событий, хранение офлайн, сэмплирование, учёт согласия и смена вендора без правок в фичах?
Когда server-driven UI того стоит и как старая версия приложения справляется с компонентом, которого никогда не видела?
// { "type": "stories", "props": { ... },// "fallback": { "type": "banner", "props": { "title": "New: stories", "url": "app://stories" } } }indirect enum Component: Decodable, Sendable { case banner(Banner) case productGrid(ProductGrid) case unsupported(fallback: Component?) // a type this build has never seen…Дюжина команд выпускает банк, такси и еду в одном iOS-супераппе. Как его устроить, чтобы ни одна команда не замедляла запуск и чужие релизы?