sergdort / cleanarchitecturerxswift Goto Github PK
View Code? Open in Web Editor NEWExample of Clean Architecture of iOS app using RxSwift
License: MIT License
Example of Clean Architecture of iOS app using RxSwift
License: MIT License
Hi sergdort, thanks for the excellent work! Apart from the nice concept of the architecture, I found some great ideas in some utility classes (ErrorTracker, ActivityIndicator, etc.) and would like to know if and how I can use them but I did not find any license info attached to your project. Could you provide it please?
As the title says, I have got two questions trying to figure out why it's required or what's the underlying thinking around it.
1, Why do you use do
in ViewModel
instead of using subscribe
in ViewModel
?
let dismiss = Driver.of(save, input.cancelTrigger)
.merge()
.do(onNext: navigator.toPosts)
Is there a specific reason to use do
here? I remember that I read somewhere about do
is a bad practice since it's a side effect, why not just use subscribe
to achieve the pretty much same thing?
2, I know Driver has bunch of benefit, a) main thread b) no error c) shared
struct Input {
let cancelTrigger: Driver<Void>
let saveTrigger: Driver<Void>
let title: Driver<String>
let details: Driver<String>
}
struct Output {
let dismiss: Driver<Void>
let saveEnabled: Driver<Bool>
}
But over here it makes sense to use it for Output
, but why we are using it for input?
Hi,
Could you give some advice on where to place an MFMailComposeViewController
?
In a non RxSwift and non Clean Architecture project, I would implement it in some view controller, like this:
extension ViewController: MFMailComposeViewControllerDelegate {
func presentMailComposer() {
if !MFMailComposeViewController.canSendMail() {
// TODO: - Handle error here
return
}
DispatchQueue.global(qos: DispatchQoS.QoSClass.userInitiated).async {
let mailComposeViewController = MFMailComposeViewController()
mailComposeViewController.mailComposeDelegate = self
mailComposeViewController.setToRecipients(["[email protected]"])
mailComposeViewController.setMessageBody("Message body", isHTML: false)
DispatchQueue.main.async(execute: {
self.present(mailComposeViewController, animated: true, completion: nil)
})
}
}
func mailComposeController(_ controller: MFMailComposeViewController, didFinishWith result: MFMailComposeResult, error: Error?) {
if result == MFMailComposeResult.failed {
// TODO: - Handle error here
}
}
}
Within the Clean Architecture, where would you place the Mail Composer?
Would you present this from the Navigator/Router? It is after all a "Scene", even if we don't necessarily have a Navigator/Router and a ViewModel dedicated to the MailComposer.
There are 2 distinct places where errors might occur, and I really don't think the Navigator should handle these.
Thanks!
PostsViewModel
is exposing to the view the Post mode as output: let selectedPost: Driver<Post>
My question is:
PostsViewController
knows Post
data model which means that the view knows the model.
As far as I understand MVVM architecture the UI (view) doesn't need to know the model
(Post).
What do you think? is there a place to change the selectedPost
parameter to be:
let selectedPost: Driver<PostViewModel>
?
Hello,
Thank you for this project. It was very helpful for me.
I have one question, hope you can help me. Can you explain how do you see solution of task with handling some action from unique cell. An example: we have tableView with some sections like profile, counters, account's actions, like send message or send friend request. There are unique cells. How to handle send message/friend request actions in controller's viewModel from unique cell?
im using database realm
1- using Method save1 in class Repository
2- error : Argument type '[T]' does not conform to expected type 'RealmRepresentabl
func save1(entity: [T]) -> Observable<Void> {
return Observable.deferred {
return self.realm.rx.save(entity: entity, update: true)
}.subscribeOn(scheduler)
}
Class Name
Realm+Ext.swift
Line 28 & 46
observer.onNext()
Error :
Missing argument for parameter #1 in call
When you add some param, it says
Argument passed to call that takes no arguments
Like Trap in Cycle.
Xcode version
Version 9.2 (9C40b)
RxSwift 4.0
Swift 4.0
In RxSwift, method declaration is like below. So, how you calling without parameter?
public func onNext(_ element: Self.E)
Any suggestion?
Should't PostsViewModel be an protocol and not class? I think keeping protocol oriented view models is better than enforcing generic structure with associatedtype. Input/output transform can always be enforced per viewModel protocol. Thoughts?
Hi! Thanks very much for your inspirational work on this project! It has helped me a lot in my own work.
I opened this issue to provide a tiny suggestion.:
Since the Schemes list is quite long and mostly filled with Pod-related schemes, you could hide those in Xcode's Manage Schemes window. That will just leave 5 schemes visible instead of 28.
Since the README and the workspace have a lot of information for someone to digest, it would be nice to make things as clean as possible :)
Thanks again!!
Hi guys,
first of all, let me thank you for your work, which is really great and inspiring!
I've been using MVVM in iOS since 4 years now and I've learned that everyone has its personal way to implement things with this pattern and, most of all, everyone intend a "View Model" in different way.
However, there seems to be 2 things that almost everyone agrees upon:
Taking a look at your example, in the PostsViewController, you're fetching the UITableView with the .rx
extension: inside the closure, you're directly referencing the UITableViewCells IBOutlets and directly populating with model properties, which (in my opinion) totally breaks the MVVM pattern for different reasons :
Does it make sense to you? Am I being too "strict to the pattern" for your scenario?
Thanks in advance :)
Hi, I have a question related to architecture of view model and VC.
I'm want to add tvDataSource to PostsViewModel.swift. But can't understand it will be in input or in output. Now in VC i have some like:
let tvDataSource = RxTableViewSectionedReloadDataSource<SectionOfCustomData>(configureCell: { dataSource, tableView, indexPath, _ in
switch dataSource[indexPath] {
case .customData(let customData):
let cell = tableView.dequeue(Reusable.petCell, for: indexPath)
return cell
case .string(let string):
let cell = tableView.dequeue(Reusable.addItemCell, for: indexPath)
return cell
}
})
and bind this in in func VC:
output.sections
.drive(tableView.rx.items(
dataSource: tvDataSource))
.disposed(by: rx.disposeBag)
I can't understood cell we need configure in VC, but tvDataSource in viewModel. So what i need change to configure dataSource in viewModel. Because let sections: Driver<[SectionOfCustomData]>
i have in viewModel Output.
Thanks
I have conversation service response that contain array of message. Need to added that functionality in CDModelName+Ext.swift to store chat messages.
While adding array I am getting error "Value of type '[Message]' has no member 'sync' "
Response for conversation is something like that
{
id : 1,
message : [
{id:12, message:"test1"},
{id:13, message: "test2"}
]
}
how creat framwork in swift 4 like sample
Hey! Awesome project! Your code is so inspiring!
I just have one short question, in your ViewModels transform:input
method you do use capture list to weakify self, e.g. in EditPostViewModel
let savePost = saveTrigger.withLatestFrom(post)
.flatMapLatest { post in
return self.useCase.save(post: post)
.trackError(errorTracker)
.asDriverOnErrorJustComplete()
}
You use self.useCase.save
, isn't this a retain cycle? Dont we have to use:
let savePost = saveTrigger.withLatestFrom(post)
.flatMapLatest { [weak self] post in
guard let `self` = self else { return someStuff }
return self.useCase.save(post: post)
.trackError(errorTracker)
.asDriverOnErrorJustComplete()
}
Thanks!
Hi there
I'm trying to change one UseCase Repository to first fetching data from realm and getting data from network and update the realm. something like that :
final class NewsUseCase: Domain.NewsUseCase where Repository: AbstractRepository, Repository.T == News {
private let network: NewsNetwork
private let repository: Repository
init(repository: Repository, network: NewsNetwork) {
self.repository = repository
self.network = network
}
func news() -> Observable<[News]> {
let fetchNews = repository.queryAll().asObservable()
let stored = network.fetchNews()
.flatMap {
return self.repository.save(entity: News)
.asObservable()
.map(to: [News].self)
.concat(Observable.just($0))
}
return fetchNews
but there have been couple of errors in there. for example in saving to repository I've got error like this :
Cannot convert value of type 'News.Type' to expected argument type 'News'
could me help me and fix this snippet code for me.
Thanks
Hello, thank you for great sample "how to explore CleanArch with RX using swift"!
I have few questions, I would be thankful for clarification.
If I have few flows, likes authentication and home (an example: tab bar with slide menu, where a lot use cases). Should I group use cases like authenticationUseCases which consist of AuthenticationServiceLocator which provide cases just for this flow and so on for other flows?
If answer for first question is yes, so how can I provide services to other flows? I mean, when I start application, I can configure welcome scene (as at your example), where I have authentication flow with authenticationService. After it I should redirect user to another flow, with another useCases (something like HomeServiceLocator). Where should I initialize HomeScene with HomeServiceLocator?
Hi,
First I'd like to say that this is a wonderful example, and I plan on applying the lessons learned here to our next refactoring. That being said, I think it would be valuable if this example included unit tests. From my point of view, one of the benefits of an architecture like this is increased testability (especially when compared to something like "Massive View Controller") - so it would be great to highlight that.
Thanks,
Noah
Your domain has entities... but they have absolutely no logic or critical business rules associated to them... it only defines entities and a use case provider protocol...
You have 3 redundant use cases provider implementations that are fully aware of everything and can't talk to each other.
Use cases implementation should have no knowledge about core data, realm, etc... you just write platform agnostic code... using protocols and inject adapters as dependencies.
So, my idea is to hide ViewModel implementation with protocol
something like this:
protocol PostsViewModelType: ViewModelType {}
final class PostsViewModel: PostsViewModelType {}
And then, in viewController, access the viewModel like so:
var viewModel: PostsViewModelType
But I am getting the folowing error, when accessing viewModel
Protocol 'PostsViewModelType' can only be used as a generic constraint because it has Self or associated type requirements
This happens because of associatedtype
, but unfortunately I couldn't figure out how to resolve this. Do you have any thoughts about this?
Your architecture was so good. But i have question. I need to upload 10 photos and this 10 photos must be in arrange. So, i added a numbering in request. My question is, How to retrieve that numbering so i can get the exact arrangement in my photos?
final class Repository<T: CoreDataRepresentable>: AbstractRepository where T == T.CoreDataType.DomainType
could not understand how T.CoreDataType.DomainType is returning back the Domain Type .
In case of Domain.Post, Post is conforming to CoreDataRepresentable
`extension Post: CoreDataRepresentable {
typealias CoreDataType = CDPost
....
}`
and CDPost conforms to DomainConvertibleType and Persistable
But nowhere associatedtype DomainType
is defined.
Can you please clarify?
Now in app, when showing alert with "Are you sure you want to delete this post?", when select "No" Post deleted :-( How to fix this problem? @sergdort
let deleteTrigger = deleteButton.rx.tap.flatMap {
return Observable<Void>.create { observer in
let alert = UIAlertController(title: "Delete Post",
message: "Are you sure you want to delete this post?",
preferredStyle: .alert
)
let yesAction = UIAlertAction(title: "Yes", style: .destructive, handler: { _ -> () in observer.onNext(()) })
let noAction = UIAlertAction(title: "No", style: .cancel, handler: { _ -> () in observer.onNext(()) })
alert.addAction(yesAction)
alert.addAction(noAction)
self.present(alert, animated: true, completion: nil)
return Disposables.create()
}
}
let input = EditPostViewModel.Input(
editTrigger: editButton.rx.tap.asDriver(),
deleteTrigger: deleteTrigger.asDriverOnErrorJustComplete(),
title: titleTextField.rx.text.orEmpty.asDriver(),
details: detailsTextView.rx.text.orEmpty.asDriver()
)
For Model like CDMessage,
Why func sync not called when I try to save CDMessage data in core data.
CDConversation contained CDMessage, for reference I am sharing response below:
{ id : 23,
message : {
id : 123,
text : "hello"
}
}
In CDModalName+ext.swift :
extension CDConversation {
public static var id: Attribute<String> { return Attribute("id")}
public static var last_message: Attribute<CDMessage> { return Attribute("message")}
}
extension Conversation: CoreDataRepresentable {
public typealias CoreDataType = CDConversation
func sync(in context: NSManagedObjectContext) -> Observable<CDConversation> {
let syncSelf = context.rx.sync(entity: self, update: update)
let syncLast_message = last_message!.sync(in: context).map(Optional.init)
return Observable.zip(syncSelf, syncLast_message, resultSelector: CDConversation.synced)
}
public func update(entity: CDConversation) {
entity.id = Int16(id)
}
}
Why this project use both NSObject
/ NSObject
and then in Domain uses Codeable
??
Hi,
What do you think about the introduction of BindableType protocol?
protocol BindableType {
associatedtype ViewModel: ViewModelType
var viewModel: ViewModel { get }
func bindInput() -> ViewModel.Input
func bind(output: ViewModel.Output)
}
extension BindableType {
func bindViewModel() {
let input = bindInput()
let output = viewModel.transform(input: input)
bind(output: output)
}
}
Example:
import RxSwift
import RxCocoa
class ViewModel: ViewModelType {
struct Input {
let trigger: Driver<Void>
}
struct Output {
let value: Driver<Void>
}
func transform(input: Input) -> Output {
return Output(value: input.trigger)
}
}
class ViewController: UIViewController, BindableType {
let viewModel: ViewModel
private let disposeBag = DisposeBag()
init(viewModel: ViewModel) {
self.viewModel = viewModel
super.init(nibName: nil, bundle: nil)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
bindViewModel()
}
func bindInput() -> ViewModel.Input {
return ViewModel.Input(trigger: .just(()))
}
func bind(output: ViewModel.Output) {
output.value
.drive()
.disposed(by: disposeBag)
}
}
Can you add an example for offline storage?
Like fetching data from the network then storing locally and displaying into the UI.
Hello,
Some of rx code won't work easyly with swift 4
Hi, I started to work on some side project which is based on this great project.
I facing some strange issue that when i execute a request from my view model to the server if the request failed the app crash. After debug it i found the crash is because the asserationFailure command check the error and because the error object is not nil the app crash.
This is the whole function:
func asDriverOnErrorJustComplete() -> Driver<E> { return asDriver { error in assertionFailure("Error \(error)") return Driver.empty() } }
My view model code is:
`
let login = input.loginTrigger.withLatestFrom(emailAndPassword)
.flatMapLatest({ [unowned self] (email, password) in
return self.loginUseCase.login(username: email, password: password)
.trackActivity(activityIndicator)
.trackError(errorTracker)
.asDriverOnErrorJustComplete()
})
`
As you can see i use the same best practices like you use in your project.
I also tried to change your code so network requests will fail and the asDriverOnErrorJustComplete but from some reason on your project it ignore the asserationFailure line.
Can someone advice what should i do or what i am doing wrong?
thanks!
Hi @sergdort, first of all thank you very much for your inspiring work. I am in the process of adopting some of the architectural patterns from your project and wanted to discuss with you my concern.
In PostViewController you are subscribing to createPost
and selectedPost
of the view model without any actual observer code passed. It is clear for me that this is done that the delegate style callback to the navigator hidden in the view model's do
operator would be called -- without subscription neither the do
is called. I have a kind of bad feeling about this solution because it is not clear in the view controller why one should create an empty subscription as the side effect of this subscription (i.e. calling navigator.toPost
) is hidden in the view model. Do you think there is a way to refactor this code?
Hi, Thanks for really good architecture template :)
Have you a plan to add Pull to Refresh and Infinite Scrolling features?
It's will be good for networking part.
Thanks again.
Type 'RMProduct' has no member 'build'
extension Product: RealmRepresentable {
func asRealm() -> RMProduct {
return RMProduct.build{ object in
object.name = name
}
}
}
Can someone help to understand what is wrong ?
Hi Serg,
First of all, I want to thank you for the great explanation about CleanArchitecture, but I am a bit concerned about the way the ViewModel is created. Based on this approach, if a change is made in the Input, the transform function will be called and all the outputs will be recreated again. This is not necessarily right.
extension ViewModel: ViewModelType {
struct Input {
...
}
struct Output {
...
}
func transform(input: Input) -> Output {
...
return Output(output: someOutput)
}
}
What if we create the ViewModel like this, in a protocol oriented way:
protocol ViewModelInput {
...
}
protocol ViewModelOutput {
...
}
protocol ViewModelType {
var input: ViewModelInput { get }
var output: ViewModelOutput { get }
}
extension ViewModel: ViewModelInput, ViewModelOutput, ViewModelType {
var input: ViewModelInput { return self }
var output: ViewModelOutput { return self }
...
}
The ViewModel is easier to be tested and the outputs aren't recreated for every change in Input.
What do you think?
Should viewModel import framework that exposes UIKit like RxCocoa? Maybe it's better to expose input/output as Observable's and convert them to Driver in viewController implementation where it's legit to import RxCocoa?
Hi,
I've created a User entity in my domain.
My user entity looks like the following:
public struct User {
public let username : String?
public let email : String?
public let uid: String?
public let firstName: String?
public let lastName: String?
public let password: String?
public let createdAt: Date?
public let updatedAt: Date?
public init(builder: UserBuilder) {
self.username = builder.username
self.firstName = builder.firstName
self.lastName = builder.lastName
self.uid = builder.userId
self.password = nil
self.email = builder.email
self.createdAt = builder.createdAt
self.updatedAt = builder.updatedAt
}
}
extension User : Equatable {
public static func == (lhs: User, rhs: User) -> Bool {
return lhs.uid == rhs.uid &&
lhs.firstName == rhs.firstName &&
lhs.lastName == rhs.lastName &&
lhs.createdAt == rhs.createdAt &&
lhs.updatedAt == rhs.updatedAt
}
}
Then, i started to build the Realm platform to use as offline database. There I've create the RealmUser entity and it looks like the following:
import Realm
import RealmSwift
import Domain
import QueryKit
final class RealmUser : Object {
dynamic var uid : String = ""
dynamic var email : String = ""
dynamic var firstName : String = ""
dynamic var lastName : String = ""
override class func primaryKey() -> String? {
return "uid"
}
}
extension RealmUser {
static var uid : Attribute<String> { return Attribute("uid") }
static var email : Attribute<String> { return Attribute("email") }
static var firstName : Attribute<String> { return Attribute("firstName") }
static var lastName : Attribute<String> { return Attribute("lastName") }
}
extension RealmUser : DomainConvertibleType {
func asDomain() -> User {
var builder = UserBuilder()
builder.userId = uid
builder.email = email
builder.firstName = firstName
builder.lastName = lastName
let user = User(builder: builder)
return user
}
}
extension User : RealmRepresentable {
func asRealm() -> RealmUser {
return RealmUser.build { object in
object.uid = uid!
object.email = email!
object.firstName = firstName!
object.lastName = lastName!
}
}
}
My issue is in this specific line:
extension User : RealmRepresentable
swift compiler yelling me that the:
Type 'User' does not conform to protocol 'RealmRepresentable'
I know that i can implement the
var uid: String {get}
protocol, but i looked at your code and you are not doing it and it still work for you.
Your code looks like the following:
extension User: RealmRepresentable {
func asRealm() -> RMUser {
return RMUser.build { object in
object.uid = uid
object.address = address.asRealm()
object.company = company.asRealm()
object.email = email
object.name = name
object.phone = phone
object.username = username
object.website = website
}
}
}
as you can see, you are not overriding the uid property and it still works for you...
am i missing something?
Thanks!
It's more of a question then Issue. But how would you implement OfflineFirst principle and keep for example both platforms intact and still let them be modular (networking, realm i.e). I suppose I should create another platform or layer below the domain and in the UseCase I could use both the NetworkingPlatform and RealmPlatform and just merge the results. Or is there some other way how to do it?
Hello,
in Post entry the part where Encodable do the task to decode json from internet. Is there any efficient and fast way to do this? like say you have nested object in one you gotta have several lines of code just to decode it. Thank you.
Xcode 9.1
error:dyld: Symbol not found: OBJC_CLASS$_NWPathEvaluator
seems Network.framework colliding with system framework .
rename Network target to fix it .
Dear Sergdort,
Thanks so much for sharing this good article.
But my issue is when I run pod install on Xcode 10.1 & Swift 4.2, and run project.
It appear so many error message. Please see the image below:
First error is the RxAlmofire SWIFT_VERSION is not support Swift 5.0, therefore, I add code in the end of podfile:
post_install do |installer|
installer.pods_project.targets.each do |target|
if target.name == 'RxAlamofire'
target.build_configurations.each do |config|
config.build_settings['SWIFT_VERSION'] = '4.2'
end
end
end
end
Therefore, the error is solved. But cmd+R appear too many error talking about .h is not found.
I try to change the RxSwift version to the 4.5.0, but CocoaPods told me it is not found.
Below is my current podfile all content:
# Uncomment the next line to define a global platform for your project
platform :ios, '11.0'
inhibit_all_warnings!
def rx_swift
pod 'RxSwift', '~> 5.0'
end
def rx_cocoa
pod 'RxCocoa', '~> 5.0'
end
def test_pods
pod 'RxTest'
pod 'RxBlocking'
pod 'Nimble'
end
target 'CleanArchitectureRxSwift' do
# Comment the next line if you're not using Swift and don't want to use dynamic frameworks
use_frameworks!
rx_cocoa
rx_swift
pod 'QueryKit'
target 'CleanArchitectureRxSwiftTests' do
inherit! :search_paths
test_pods
end
end
target 'CoreDataPlatform' do
# Comment the next line if you're not using Swift and don't want to use dynamic frameworks
use_frameworks!
rx_swift
pod 'QueryKit'
target 'CoreDataPlatformTests' do
inherit! :search_paths
test_pods
end
end
target 'Domain' do
# Comment the next line if you're not using Swift and don't want to use dynamic frameworks
use_frameworks!
rx_swift
target 'DomainTests' do
inherit! :search_paths
test_pods
end
end
target 'NetworkPlatform' do
# Comment the next line if you're not using Swift and don't want to use dynamic frameworks
use_frameworks!
rx_swift
pod 'Alamofire'
pod 'RxAlamofire'
target 'NetworkPlatformTests' do
inherit! :search_paths
test_pods
end
end
target 'RealmPlatform' do
# Comment the next line if you're not using Swift and don't want to use dynamic frameworks
use_frameworks!
rx_swift
pod 'RxRealm', '~> 1.0.0'
pod 'QueryKit'
pod 'RealmSwift', '~> 3.15'
pod 'Realm', '~> 3.15'
target 'RealmPlatformTests' do
inherit! :search_paths
test_pods
end
end
post_install do |installer|
installer.pods_project.targets.each do |target|
if target.name == 'RxAlamofire'
target.build_configurations.each do |config|
config.build_settings['SWIFT_VERSION'] = '4.2'
end
end
end
end
Please give me some advise (solution), thank again!
Hi @sergdort , thank you for your interesting work. We got a lot of inspiring things from that.
I would like to share one of the problem that we are trying to resolve in process of adopt Input, Output and transform method between View and ViewModel.
In our project we use RxSwift + MVVM + Coordinator. ViewModel of Cell in our UICollectionView has to implement some work for UI of cell. So when UICollectionViewDataSource calls dequeueReusableCell every time Cell calls method transform and create new output from ViewModel. It requires a little bit of time for UI so itβs visible on scroll. In our first approach, Cell subscribed on event of already created VIewModel and it worked fine (but this solution was less functional). Now we call every time transform method and it blocks UI on scroll because ViewModel should be recreated every time.
Did you ever think about similar situation or do you have some ideas how it can be resolved?
Thank you
I tried Xcode 10, it works fine, but crashes on Xcode 11 whenever you click Realm Tab.
RunLoopThreadScheduler:
private final class ThreadTarget: NSObject {
@objc fileprivate func threadEntryPoint() {
let runLoop = RunLoop.current
***CRASH: com.CleanArchitectureRxSwift.RealmPlatform.Repository (8): signal SIGABRT
runLoop.add(NSMachPort(), forMode: RunLoop.Mode.default)
runLoop.run()
}
}
2019-09-24 22:40:34.205345+1200 CleanArchitectureRxSwift[95204:325374] *** Terminating app due to uncaught exception 'RLMException', reason: 'Primary key property 'uid' does not exist on object 'RMPhoto''
Hello All
I'm new to Clean Architecture, for the last couple of days, I've been struggling with learning the basic concepts of Clean Architecture, For iOS, I have 2 main sources for learning, This repo & Raymond's implementation here.
These 2 implementations differ in the concepts of clean architecture.
1- one example is that this implementation relies on a Domain
target to store all the models for the app, All the targets that want to use one of these models has to import Domain to see them.
2- Another example is that Raymond's implementation relies on workers
to do the actual work of the interactor, why doesn't this repo make use of these workers to push the Single Responsibility Principle
further?
Any help is highly appreciated!
Hi Guys, I'm very exited with this architecture and want to thank you. While working with it I've got some questions. Could you please provide an example for the following case: I have a cell with a textfield placed on it. How should I create an Input for controller's ViewModel in this case to make it work properly with cells reusability?
Thanks.
Hello,
is it possible to make the Post Type in the PostUseCase as associatedtype so I write the required method only once:
I started to write the Code, but It dont't work.
Here is the General UseCase in Domain:
public protocol UseCase {
associatedtype E
func many() -> Observable<[E]>
func save(entry: E) -> Observable<Void>
func delete(entry: E) -> Observable<Void>
}
Here is the General UseCaseProvider in Domain:
public protocol UseCaseProvider {
associatedtype U: UseCase
func makeUseCase() -> U
}
Here is the UserCaseProvider in CoreDataPlatform:
public final class PostUseCaseProvider: Domain.UseCaseProvider {
private let coreDataStack = CoreDataStack()
private let postRepository: Repository<Post>
public init() {
postRepository = Repository<Post>(context: coreDataStack.context)
}
public func makeUseCase() -> Domain.UseCase {
return PostsUseCase(repository: postRepository)
}
}
Here get the error Type 'UserCaseProvider' does not conform to protocol 'UseCaseProvider' and Protocol 'UseCase' can only be used as a generic constraint because it has Self or associated type requirements'
Any idea?
Hi,
I have a question related to architecture of view model.
Let's assume that I have a requirement to store network requests payload in Realm in order to be able to access data even if the user is offline. Currently in the example you show how to use Network or Realm or CoreData but not all together.
If i want to fulfill the requirement I need to do the following:
I think it can work but maybe there is a better way to achieve it. Maybe by creating an abstraction layer on top of it that will do the call (either to the server or to the offline database in case the user is online/offline)
What do you think ?
Thanks!
Hey,
Have you thought about refactoring code by using Moya? Rx + Alamofire + ObjectMapper IMO asks for it :)
Great example btw, very clean and helpful one :)
Hello,
I can't wrap my head around how to communicate with a child view controller.
I have one trigger from the ParentViewController
that is needed in the ChildViewModel
and a second one from the ChildViewModel
that should trigger some UI changes in the ParentViewController
:
trigger1: ParentViewController (input)> ... (no idea)> ChildViewModel
trigger2: ChildViewModel (output)> ... (no idea)> ParentViewController
My code currently looks something like this:
final class ParentViewController: UIViewController {
var viewModel: ParentViewModel?
private let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
bindViewModel()
}
private func bindViewModel() {
guard let viewModel = self.viewModel else {
fatalError("View Model not set!")
}
let triggerToChild = rx.someTrigger
let input = ParentViewModel.Input(triggerToChild: triggerToChild)
let output = viewModel.transform(input: input)
output.triggerFromChild
.drive(rx.someProperty)
.disposed(by: disposeBag)
}
}
final class ParentViewModel: ViewModelType {
struct Input {
let triggerToChild: Driver<Void>
}
struct Output {
let triggerFromChild: Driver<Void>
}
func transform(input: Input) -> Output {
let triggerFromChild = ??? <===================
return Output(triggerFromChild: triggerFromChild)
}
}
final class ChildViewController: UIViewController {
var viewModel: ChildViewModel?
private let disposeBag = DisposeBag()
override func viewDidLoad() {
super.viewDidLoad()
bindViewModel()
}
private func bindViewModel() {
guard let viewModel = self.viewModel else {
fatalError("View Model not set!")
}
let triggerFromParent = ??? <===================
let input = ChildViewModel.Input(triggerFromParent: triggerFromParent)
let output = viewModel.transform(input: input)
output.triggerFromParent
.drive(rx.someProperty)
.disposed(by: disposeBag)
}
}
final class ChildViewModel: ViewModelType {
struct Input {
let triggerFromParent: Driver<Void>
}
struct Output {
let triggerToParent: Driver<Void>
}
func transform(input: Input) -> Output {
let triggerToParent = rx.someTrigger
return Output(triggerToParent: triggerToParent)
}
}
Maybe someone could point me in the right direction? Thank You!
Hi,
As I mentioned in the title, what is the difference between two use cases?
observeOn:
https://github.com/sergdort/CleanArchitectureRxSwift/blob/master/Network/Network/Network.swift
subscribeOn:
https://github.com/sergdort/CleanArchitectureRxSwift/blob/master/RealmPlatform/Repository/Repository.swift
Thank you. Your example is very great and it helps me a lot :)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
π Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. πππ
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google β€οΈ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.