Налаштування архітектури Clean Architecture для iOS-додатку

TRUETECH займається розробкою, підтримкою та обслуговуванням мобільних додатків iOS, Android, PWA. Маємо великий досвід та експертизу для публікації мобільних додатків до популярних маркетів Google Play, App Store, Amazon, AppGallery та інші.

Розробка та підтримка будь-яких видів мобільних додатків:

Інформаційні та розважальні мобільні програми
Новинки, ігри, довідники, онлайн-каталоги, погодні, фітнес та здоров'я, туристичні, освітні, соціальні мережі та месенджери, квіз, блоги та подкасти, форуми, агрегатори
Мобільні програми електронної комерції
Інтернет-магазини, B2B-додатки, маркетплейси, онлайн-обмінники, кешбек-сервіси, біржі, дропшиппінг-платформи, програми лояльності, доставка їжі та товарів, платіжні системи
Мобільні програми для управління бізнес-процесами
CRM-системи, ERP-системи, управління проектами, інструменти для команди продажів, облік фінансів, управління виробництвом, логістика та доставка, управління персоналом, системи моніторингу даних
Мобільні програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, платформи надання електронних послуг, платформи кешбеку, відеохостинги, тематичні портали, платформи онлайн-бронювання та запису, платформи онлайн-торгівлі

Це лише деякі з типів мобільних додатків, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 1735 послуг
Налаштування архітектури Clean Architecture для iOS-додатку
Складний
~3-5 днів
Часті запитання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_mobile-applications_feedme_467_0.webp
    Розробка мобільного додатка для компанії FEEDME
    792
  • image_mobile-applications_xoomer_471_0.webp
    Розробка мобільного додатку для компанії XOOMER
    671
  • image_mobile-applications_rhl_428_0.webp
    Розробка мобільного додатку для компанії RHL
    1097
  • image_mobile-applications_zippy_411_0.webp
    Розробка мобільного додатку для компанії ZIPPY
    969
  • image_mobile-applications_affhome_429_0.webp
    Розробка мобільного додатку для компанії Affhome
    914
  • image_mobile-applications_flavors_409_0.webp
    Розробка мобільного додатку для компанії FLAVORS
    495

Настройка архітектури Clean Architecture для iOS-застосунку

Коли кодова база iOS-застосунку виростає до 50–70 екранів, ViewController відповідає і за сітьові запити, і за трансформацію даних, і за навігацію. Тести писати некуди — залежності захардкожені. Новий розробник відкриває ProfileViewController.swift на 1200 рядків та закриває ноутбук.

Clean Architecture вирішує це через розділення на концентричні шари з жорсткою напрямком залежностей: внутрішні шари нічого не знають про зовнішні.

Як устроєна Clean Architecture на практиці в iOS-проекті

У класичній трактовці Боба Мартина три кільця: EntitiesUse CasesInterface Adapters. У iOS це відображається таким чином.

Domain-слой — ядро. Тут Entity-моделі: чисті Swift-структури без імпорту Foundation, тільки бізнес-дані. Поруч — протоколи UseCase та їх реалізації. Наприклад, FetchUserProfileUseCase приймає UserRepository через ін'єкцію залежностей та повертає AnyPublisher<UserProfile, DomainError>. Ніякого URLSession, ніякого CoreData. Цей слой компілюється та тестується ізольовано.

protocol UserRepository {
    func fetchProfile(id: String) -> AnyPublisher<UserProfile, DomainError>
}

final class FetchUserProfileUseCase {
    private let repository: UserRepository
    init(repository: UserRepository) { self.repository = repository }

    func execute(id: String) -> AnyPublisher<UserProfile, DomainError> {
        repository.fetchProfile(id: id)
    }
}

Data-слой — реалізації репозиторіїв. UserRepositoryImpl працює з URLSession або Alamofire, маппить DTO → доменну модель, обробляє сітьові помилки. CoreDataUserCache реалізує той же протокол для локального кешу. Вибір джерела даних — в UserRepositoryImpl через стратегію або в DI-контейнері.

Presentation-слой — тут живуть ViewModel/Presenter. В поєднанні з SwiftUI зручен ObservableObject-ViewModel: викликає UseCase, трансформує результат у @Published-стан та публікує його. ViewController або SwiftUI View займається виключно рендерингом.

Навігація: Coordinator або Router

Типова проблема — ViewController створює наступний ViewController та робить push. Це порушує Clean Architecture: presentation-слой знає про конкретні типи інших екранів. Рішення — Coordinator:

protocol ProfileCoordinator: AnyObject {
    func showEditProfile(user: UserProfile)
    func showOrders(userId: String)
}

ViewModel тримає слабку ссилку на ProfileCoordinator. Конкретний ProfileCoordinatorImpl знає про UINavigationController та про наступні екрани. ViewModel — ні.

DI: чиста ін'єкція без Service Locator

Service Locator (глобальний DIContainer.shared.resolve()) — анти-паттерн: скриває залежності та ломає тести. Використовуємо інініціалізаційну ін'єкцію в ланцюзі: SceneDelegate створює AppCoordinator, той — конкретні репозиторії та UseCase-и, передає їх у ViewModel через init. Можна підключити Swinject або Needle, але для більшості проектів вручна сборка в CompositionRoot достатньо.

Тестованість — основний виграш

Domain-слой тестується через XCTest без залежності від UIKit: створюємо MockUserRepository, підставляємо в UseCase, перевіряємо логіку. Ніяких XCTestExpectation для мережі, ніяких моків URLSession.

final class FetchUserProfileUseCaseTests: XCTestCase {
    func test_execute_returnsProfile() {
        let mock = MockUserRepository(result: .success(.stub()))
        let sut = FetchUserProfileUseCase(repository: mock)
        var received: UserProfile?
        _ = sut.execute(id: "123").sink(
            receiveCompletion: { _ in },
            receiveValue: { received = $0 }
        )
        XCTAssertEqual(received?.id, "123")
    }
}

Час збирання тестів domain-слоя — секунди, не хвилини. Це змінює культуру розробки в команді.

Типові помилки при впровадженні

Занадто тонкі UseCase. GetUsernameUseCase, який робить return user.name — бессмисловий слой. UseCase виправданий, коли інкапсулює нетривіальну логіку або оркеструє кілька репозиторіїв.

Domain-моделі з Codable. Додати Codable до доменної Entity означає протічку Data-слоя всередину. DTO — в Data-слое, маппінг — там же.

ViewModel знає про конкретний репозиторій. Якщо у ViewModel написано let repo = UserRepositoryImpl(...) — ін'єкції залежностей немає. Тільки протокол + інініціалізаційна ін'єкція.

Що входить у настройку

Аудит поточної архітектури (якщо проект існує): визначаємо, що виносяться в Domain, що залишається в Presentation, які залежності потрібно інвертувати.

Створення базової структури модулів: Domain, Data, Presentation — окремі Swift Package або targets в одному Xcode-проекті. Настройка залежностей між targets: Data залежить від Domain, Presentation залежить від Domain, не від Data.

Реалізація CompositionRoot / DI-контейнера. Настройка перших 2–3 фича-модулів як образця для команди.

Написання базових тестів domain-слоя як образця.

Терміни

Настройка архітектури з нуля на новому проекті (структура + DI + перший модуль): 3–5 днів. Рефакторинг існуючого проекту з міграцією 10–15 модулів: 2–4 тижні залежно від обсягу. Вартість розраховується після аналізу поточного кода та архітектурних рішень.