CleanArchitectureRxSwift alternatives and similar libraries
Based on the "Code Quality" category.
Alternatively, view CleanArchitectureRxSwift alternatives based on common mentions on social networks and blogs.
-
SwiftLint
A tool to enforce Swift style and conventions. -
chisel
Chisel is a collection of LLDB commands to assist debugging iOS apps. -
SwiftFormat
A command-line tool and Xcode Extension for formatting Swift code -
MLeaksFinder
Find memory leaks in your iOS app at develop time. -
FBRetainCycleDetector
iOS library to help detecting retain cycles in runtime. -
OCLint
A static source code analysis tool to improve quality and reduce defects for C, C++ and Objective-C -
FBMemoryProfiler
iOS tool that helps with profiling iOS Memory usage. -
KZBootstrap
iOS project bootstrap aimed at high quality coding. -
HeapInspector-for-iOS
Find memory issues & leaks in your iOS app without instruments -
Dotzu
iOS app debugger while using the app. Crash report, logs, network. -
dotenv-linter
⚡️Lightning-fast linter for .env files. Written in Rust 🦀 -
spacecommander
Commit fully-formatted Objective-C as a team without even trying. -
Tailor
Cross-platform static analyzer and linter for Swift. -
IBAnalyzer
Find common xib and storyboard-related problems without running your app or writing unit tests. -
ODUIThreadGuard
A guard to help you check if you make UI changes not in main thread -
DWURecyclingAlert
Optimizing UITableViewCell For Fast Scrolling -
SwiftCop
SwiftCop is a validation library fully written in Swift and inspired by the clarity of Ruby On Rails Active Record validations. -
Warnings-xcconfig
An xcconfig (Xcode configuration) file for easily turning on a boatload of warnings in your project or its targets. -
PIDOR
Simple design pattern with the best iOS dev experience. -
GlueKit
Type-safe observable values and collections in Swift -
Marshroute
Marshroute is an iOS Library for making your Routers simple but extremely powerful -
PSTModernizer
Makes it easier to support older versions of iOS by fixing things and adding missing methods -
Trackable
Trackable is a simple analytics integration helper library. It’s especially designed for easy and comfortable integration with existing projects. -
DecouplingKit
decoupling between modules in your iOS Project. iOS模块化过程中模块间解耦方案 -
SwiftyVIPER
Swift Interaction with VIPER Architecture -
KZAsserts
Asserts on roids, test all your assumptions with ease. -
AnyLint
Lint anything by combining the power of scripts & regular expressions. -
WeakableSelf
A Swift micro-framework to easily deal with weak references to self inside closures -
SwiftLinter
Share lint rules between projects and lint changed files with SwiftLint. -
UIBaseKit
This helps make the view's configuration code, hierarchy code, and constraint code neat. -
Bugsee
In-app bug and crash reporting with video, logs, network traffic and traces.
Appwrite - The Open Source Firebase alternative introduces iOS support
* Code Quality Rankings and insights are calculated and provided by Lumnify.
They vary from L1 to L5 with "L5" being the highest.
Do you think we are missing an alternative of CleanArchitectureRxSwift or a related project?
README
Clean architecture with RxSwift
Contributions are welcome and highly appreciated!!
You can do this by:
- opening an issue to discuss the current solution, ask a question, propose your solution etc. (also English is not my native language so if you think that something can be corrected please open a PR 😊)
- opening a PR if you want to fix bugs or improve something
Installation
Dependencies in this project are provided via Cocoapods. Please install all dependecies with
pod install
You can run this script
(exec_pod_install.sh)
in 'scripts' folder that help you install all dependecies
High level overview
[](Architecture/Modules.png)
Domain
The Domain
is basically what is your App about and what it can do (Entities, UseCase etc.) It does not depend on UIKit or any persistence framework, and it doesn't have implementations apart from entities
Platform
The Platform
is a concrete implementation of the Domain
in a specific platform like iOS. It does hide all implementation details. For example Database implementation whether it is CoreData, Realm, SQLite etc.
Application
Application
is responsible for delivering information to the user and handling user input. It can be implemented with any delivery pattern e.g (MVVM, MVC, MVP). This is the place for your UIView
s and UIViewController
s. As you will see from the example app, ViewControllers
are completely independent of the Platform
. The only responsibility of a view controller is to "bind" the UI to the Domain to make things happen. In fact, in the current example we are using the same view controller for Realm and CoreData.
Detail overview
[](Architecture/ModulesDetails.png)
To enforce modularity, Domain
, Platform
and Application
are separate targets in the App, which allows us to take advantage of the internal
access layer in Swift to prevent exposing of types that we don't want to expose.
Domain
Entities are implemented as Swift value types
public struct Post {
public let uid: String
public let createDate: Date
public let updateDate: Date
public let title: String
public let content: String
}
UseCases are protocols which do one specific thing:
public protocol PostsUseCase {
func posts() -> Observable<[Post]>
func save(post: Post) -> Observable<Void>
}
UseCaseProvider
is a service locator. In the current example, it helps to hide the concrete implementation of use cases.
Platform
In some cases, we can't use Swift structs for our domain objects because of DB framework requirements (e.g. CoreData, Realm).
final class CDPost: NSManagedObject {
@NSManaged public var uid: String?
@NSManaged public var title: String?
@NSManaged public var content: String?
@NSManaged public var createDate: NSDate?
@NSManaged public var updateDate: NSDate?
}
final class RMPost: Object {
dynamic var uid: String = ""
dynamic var createDate: NSDate = NSDate()
dynamic var updateDate: NSDate = NSDate()
dynamic var title: String = ""
dynamic var content: String = ""
}
The Platform
also contains concrete implementations of your use cases, repositories or any services that are defined in the Domain
.
final class PostsUseCase: Domain.PostsUseCase {
private let repository: AbstractRepository<Post>
init(repository: AbstractRepository<Post>) {
self.repository = repository
}
func posts() -> Observable<[Post]> {
return repository.query(sortDescriptors: [Post.CoreDataType.uid.descending()])
}
func save(post: Post) -> Observable<Void> {
return repository.save(entity: post)
}
}
final class Repository<T: CoreDataRepresentable>: AbstractRepository<T> where T == T.CoreDataType.DomainType {
private let context: NSManagedObjectContext
private let scheduler: ContextScheduler
init(context: NSManagedObjectContext) {
self.context = context
self.scheduler = ContextScheduler(context: context)
}
override func query(with predicate: NSPredicate? = nil,
sortDescriptors: [NSSortDescriptor]? = nil) -> Observable<[T]> {
let request = T.CoreDataType.fetchRequest()
request.predicate = predicate
request.sortDescriptors = sortDescriptors
return context.rx.entities(fetchRequest: request)
.mapToDomain()
.subscribeOn(scheduler)
}
override func save(entity: T) -> Observable<Void> {
return entity.sync(in: context)
.mapToVoid()
.flatMapLatest(context.rx.save)
.subscribeOn(scheduler)
}
}
As you can see, concrete implementations are internal, because we don't want to expose our dependecies. The only thing that is exposed in the current example from the Platform
is a concrete implementation of the UseCaseProvider
.
public final class UseCaseProvider: Domain.UseCaseProvider {
private let coreDataStack = CoreDataStack()
private let postRepository: Repository<Post>
public init() {
postRepository = Repository<Post>(context: coreDataStack.context)
}
public func makePostsUseCase() -> Domain.PostsUseCase {
return PostsUseCase(repository: postRepository)
}
}
Application
In the current example, Application
is implemented with the MVVM pattern and heavy use of RxSwift, which makes binding very easy.
[](Architecture/MVVMPattern.png)
Where the ViewModel
performs pure transformation of a user Input
to the Output
protocol ViewModelType {
associatedtype Input
associatedtype Output
func transform(input: Input) -> Output
}
final class PostsViewModel: ViewModelType {
struct Input {
let trigger: Driver<Void>
let createPostTrigger: Driver<Void>
let selection: Driver<IndexPath>
}
struct Output {
let fetching: Driver<Bool>
let posts: Driver<[Post]>
let createPost: Driver<Void>
let selectedPost: Driver<Post>
let error: Driver<Error>
}
private let useCase: AllPostsUseCase
private let navigator: PostsNavigator
init(useCase: AllPostsUseCase, navigator: PostsNavigator) {
self.useCase = useCase
self.navigator = navigator
}
func transform(input: Input) -> Output {
......
}
A ViewModel
can be injected into a ViewController
via property injection or initializer. In the current example, this is done by Navigator
.
protocol PostsNavigator {
func toCreatePost()
func toPost(_ post: Post)
func toPosts()
}
class DefaultPostsNavigator: PostsNavigator {
private let storyBoard: UIStoryboard
private let navigationController: UINavigationController
private let services: ServiceLocator
init(services: ServiceLocator,
navigationController: UINavigationController,
storyBoard: UIStoryboard) {
self.services = services
self.navigationController = navigationController
self.storyBoard = storyBoard
}
func toPosts() {
let vc = storyBoard.instantiateViewController(ofType: PostsViewController.self)
vc.viewModel = PostsViewModel(useCase: services.getAllPostsUseCase(),
navigator: self)
navigationController.pushViewController(vc, animated: true)
}
....
}
class PostsViewController: UIViewController {
private let disposeBag = DisposeBag()
var viewModel: PostsViewModel!
...
}
Example
The example app is Post/TODOs app which uses Realm
, CoreData
and Network
at the same time as a proof of concept that the Application
level is not dependant on the Platform level implementation details.
CoreData | Realm | Network |
---|---|---|
[](Architecture/CoreData.gif) | [](Architecture/Realm.gif) | [](Architecture/Network.gif) |
Modularization
The corner stone of Clean Architecture is modularization, as you can hide implementation detail under internal
access layer. Further read of this topic here
TODO:
Links
Any questions?
- ping me on Twitter