Skip to content

denissimon/iOS-MVVM-Clean-Architecture

Repository files navigation

iOS-MVVM-Clean-Architecture

Swift Version Platforms License: MIT

Example iOS app designed using MVVM-C and Clean Architecture. Uses Swift Concurrency, UIKit & SwiftUI.

The app retrieves images for any search query or tag via the Flickr API. It has three modules: ImageSearch, ImageDetails, HotTags.

Swift 6 support

Architecture concepts used here

Includes

  • Reusable and universal NetworkService based on URLSession
  • Reusable and universal SQLite wrapper around SQLite3
  • Image caching service
  • Configurable use of UIKit or SwiftUI for the same screen
  • Advanced error handling
  • Unit and integration tests for a number of components from all layers

Main layers

Presentation (MVVM): coordinators, UI elements, SwiftUI views, UIKit storyboards, ViewControllers, ViewModels

Domain: entities, use cases, services, interfaces

Data: entity repositories, APIs, API/DB interactors (or network services and persistence storages), adapters

Architecture diagram

Architecture diagram

This architecture is highly modular, has a clear separation of concerns, and is easy to modify, scale, test and debug. It can be easily modularized with SPM.

It also allows to swap the implementation of any component without making changes to the rest of the app code. For example, for the ImageDBInteractor protocol, in addition to the existing SQLiteImageDBInteractor class that uses SQLite, we can add a SwiftDataImageDBInteractor class that will use SwiftData. There will be no changes to the ImageDBInteractor protocol, nor to repositories or other layers (Domain, Presentation). Or if some 3rd-party library changes its API, all you need to do is modify its adapter, nothing on the left of the diagram will change.

Thanks to these system properties - both comprehensive test coverage and compartmentalization to isolate changes - further changes can be made with confidence and without risk of regression.

The module assembly setup occurs in DIContainer. Only those dependencies that are necessary for a specific module/component are initialized and injected into it, which in particular improves app performance and memory usage. View Models, as well as the entire Domain layer, are independent of any dependencies (with the exception of Apple's core Foundation framework).

Use case scenarios

ImageSearch module:

* searchImagesUseCase.execute(imageQuery)
* imageCachingService.cacheIfNecessary(data)
* imageCachingService.getCachedImages(searchId: searchId)

ImageDetails module:

* getBigImageUseCase.execute(for: image)

HotTags module:

* getHotTagsUseCase.execute()

Image caching service

ImageCachingService implements the logic for caching downloaded images and freeing memory. This helps keep the app's memory usage under control, since there can be a lot of downloaded images, and without caching, the app could quickly accumulate hundreds of MB of memory used. Downloaded images are cached and read from the cache automatically.

Reusable components from this project

  • SwiftEvents - the easiest way to implement data binding and notifications. Includes Event<T> and Observable<T>. Has a thread-safe version.
  • URLSessionAdapter - a Codable wrapper around URLSession for networking
  • SQLiteAdapter - a simple wrapper around SQLite3

Swift 6 support

For a Swift 6 version with SWIFT_STRICT_CONCURRENCY = complete, see the swift6 branch.

Requirements

iOS 15.0+, Xcode 13.0+, Swift 5.5+

Releases

No releases published

Packages

No packages published

Languages