TestsTested | ✓ |
LangLanguage | SwiftSwift |
License | MIT |
ReleasedLast Release | Feb 2018 |
SwiftSwift Version | 4.0 |
SPMSupports SPM | ✗ |
Maintained by pjechris.
Akane is a iOS framework that helps you building better native apps by adopting an MVVM design pattern.
Main Goals | |
---|---|
Safety: minimize bad coding practices as much as possible | |
Feature-Oriented: adding and maintaining features is easy and, yes, safe | |
Component-Oriented: each visual feature you want to add to your app is a Component (smart or dumb) | |
fine-grained Separation of Concerns, which means: | |
Much less merge conflicts | |
A better understanding of your code |
Smart components are composed of:
ComponentViewController
ComponentViewModel
ComponentDisplayable
(New in 0.19!) Dumb components are simply composed of a Displayable
and do not have an associated ViewModel
.
iOS developers tend to write all their code into a unique and dedicated ViewController class. While this may have been OK some years ago, today's app codebases grow bigger and bigger. Maintaining a single, huge, ViewController file is a dangerous operation which often results in unpredictable side effects.
Akane makes you split your code into small components which are composed of multiple classes.
The Model is the layer containing the classes that model your application business.
Songs, Movies, Books: all those classes
or struct
s belong to this layer. They should contain no reference to any UIKit
or Akane
component.
struct User {
enum Title: String {
case sir
case master
}
let username: String
let title: Title
}
Dumb component is not bound to a specific state but can still be updated with raw data.
It is represented by Displayable
protocol.
class UserView: UIView, Displayable {
@IBOutlet var title: UILabel!
func bindings(_ observer: ViewObserver, props user: User) {
self.title = UserFullNameConverter().convert(user)
}
}
Smart component represents a component who has a state defining its rendering:
ComponentViewModel
.ComponentDisplayable
.The ViewModel is where all your business logic should be implemented.
Please, Keep it agnostic: no reference to any View or ViewController should be present in your ViewModel. Also, Prefer ViewModel composition over inheritance: split your code into multiple ViewModel, each one dealing with one business case and then create another ViewModel to aggregate all those logics.
import Akane
class UserViewModel : ComponentViewModel {
let user: Observable<User>?
var disconnect: Command! = nil
init(user: User) {
self.user = Observable(user)
self.disconnect = RelayCommand() { [unowned self] in
self.user.next(nil)
}
}
func isConnected() -> Bool {
return self.user != nil
}
}
Data flow between a ViewModel and its ComponentDisplayable is bidirectional:
import Akane
class LoggedUserView : UIView, ComponentDisplayable {
@IBOutlet var userView: UserView!
@IBOutlet var buttonDisconnect: UIButton!
func bindings(observer: ViewObserver, viewModel: UserViewModel) {
// New in 0.19! Bind a author with a dumb view
observer.observe(viewModel.author).bind(to: self.authorView)
// bind 'disconnect' command with 'buttonDisconnect'
observer.observe(viewModel.disconnect)
.bind(to: self.buttonDisconnect)
}
}
ViewController, through ComponentController
protocol, makes the link between ComponentViewModel
and ComponentDisplayable
.
Just pass your ComponentViewModel
to your ViewController to bind it to its view.
application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool {
let rootViewController = self.window.rootViewController as! ComponentViewController
let user = User(username: "Bruce", title: .master)
rootViewController.viewModel = UserViewModel(user: user)
return true
}
You can even define your custom ViewControllers if you need to:
extension LoggedUserView {
static func componentControllerClass() -> AnyComponentController.Type {
return LoggedUserViewController.self
}
}
class LoggedUserViewController : UIViewController, ComponentController {
func viewDidLoad() {
super.viewDidLoad()
print("User component view loaded")
}
}
Akane supports displaying collections of objects in UITableViews
and UICollectionViews
.
Please read the Collections.md documentation to know more.
Akane supports installation via CocoaPods and Carthage.
pod 'Akane'
Akane builds on top of Bond for managing bindings. If you do want to use your own library (like RxSwift), you can use Akane core only:
pod 'Akane/Core'
Add github "akane/Akane"
to your Cartfile
.
In order to use Akane Bindings and Akane Collections, you should also append github "ReactiveKit/Bond"
.
Akane works great by itself but is even better when combined with our other tools:
This project was first developed by Xebia IT Architects and has been open-sourced since. We are committed to keeping on working and investing our time in Akane.
We encourage the community to contribute to the project by opening tickets and/or pull requests.
Akane is released under the MIT License. Please see the LICENSE file for details.