Giter Club home page Giter Club logo

cleanarchitecturerxswift's People

Contributors

abdullahselek avatar arcovv avatar ayastrebov avatar bohdanorlov avatar cpwhidden avatar ikenox avatar istiakmorsalin avatar javalnanda avatar justindsn avatar krkapilrathore avatar manhpham90vn avatar mattfenwick avatar passerbyloo avatar piotrtorczynski avatar ranhsd avatar sergdort avatar shenyj avatar stefanomondino avatar sunshinejr avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cleanarchitecturerxswift's Issues

Please provide a license for your code

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?

Question around `do` and `Driver` for Input

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?

From where should I present an MFMailComposeViewController?

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!

MVVM Isuue

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> ?

Handling action form cell

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?

save data list in realm

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)
  }

Missing argument for parameter #1 in call

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?

Hiding of Pod Schemes

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!!

Possible wrong use of MVVM pattern

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:

  1. Views should NEVER know the existence of the "model layer"
  2. In terms of separations between layers, UIViews and UIViewControllers falls inside the "View" layer

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 :

  • The UITableViewCell (a view) is fully aware (and directly accessing) something from the Model layer.
  • The cell is configured by a single view controller, meaning that the same cell must be configured in every scenario it's going to be used (suppose you have another screen where you would like to present
  • There's no "cell view Model" concept, which could be useful to reuse the same cell (with same outlets) to display data from different models.

Does it make sense to you? Am I being too "strict to the pattern" for your scenario?
Thanks in advance :)

RxDataSources and CleanArchitecture

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

Need to add array of item in CoreData class

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"}
]
}

Retain cycle?

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!

Issue when using network call in Realm Repository

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

Providing services to another flows

Hello, thank you for great sample "how to explore CleanArch with RX using swift"!

I have few questions, I would be thankful for clarification.

  1. 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?

  2. 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?

Tests

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

You don't seem to understand Clean Architecture

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.

[Question] ViewModel protocol

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?

Tagging for API Request

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?

  • NO Api needed. I need only for displaying.

Protocol conformance

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?

Wrong Action when select "No" in Alert

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()
        )

Coredata: how to call sync method for attribute<CDModalName>

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)

}
}

Bindable protocol

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)
    }
}

Crash on asDriverOnErrorJustComplete function

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!

Avoid relying on side effects of subscription in View layer

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?

Realm Object has no member 'build'

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 ?

Issues with the current ViewModel implementation.

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?

ViewModel and UIKit

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?

Type 'User' does not conform to protocol 'RealmRepresentable'

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!

OfflineFirst principle using CleanArchitectureRxSwift

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?

How to decode json other then using codable?

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 issue

Xcode 9.1
error:dyld: Symbol not found: OBJC_CLASS$_NWPathEvaluator

seems Network.framework colliding with system framework .
rename Network target to fix it .

How can I run your demo project on Xcode 10.1 & Swift 4.2

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:

Screen Shot 2019-05-21 at 9 11 34 AM

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.

Screen Shot 2019-05-21 at 9 21 42 AM

Screen Shot 2019-05-21 at 9 21 57 AM

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!

Problem with performance in case of dequeueReusableCell

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

crash on Xcode 11 when click Realm Tab

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''

Whats the difference between this & Clean-Swift

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!

UITableViewCell/UICollectionViewCell input example

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.

Generic UseCases and UseCaseProvider

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?

Store network requests in Realm

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:

  1. Pass 2 use cases into my view model . One is the network use case and the other one is the realm use case
  2. Execute a call to the server via the Network use case and then use flat map to get the results and store them in Realm.
  3. If the user is offline i can simply fetch the data from realm use case instead of the network use case.

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!

Moya

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 :)

Communication with child view controller

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!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.