All Projects → GeekTree0101 → RxMVVM-Texture

GeekTree0101 / RxMVVM-Texture

Licence: other
RxSwift MVVM pattern best practice built on Texture(AsyncDisplayKit) and written in Swift

Programming Languages

swift
15916 projects
ruby
36898 projects - #4 most used programming language

Projects that are alternatives of or similar to RxMVVM-Texture

RxCocoa-Texture
RxCocoa Extension Library for Texture.
Stars: ✭ 98 (+16.67%)
Mutual labels:  rxswift, texture, asyncdisplaykit
Rxasdatasources
RxDataSource for AsyncDisplayKit/Texture
Stars: ✭ 114 (+35.71%)
Mutual labels:  rxswift, texture
Rxdatasources Texture
ASTable and ASCollection Data Sources for RxSwift (Texture)
Stars: ✭ 25 (-70.24%)
Mutual labels:  rxswift, texture
IGListKit-AsyncDisplayKit-Example
IGListKit be used with AsyncDisplayKit in Swift
Stars: ✭ 34 (-59.52%)
Mutual labels:  texture, asyncdisplaykit
Texture-KR-Wiki
Texture (AsyncDisplayKit) Wiki - 한국어
Stars: ✭ 42 (-50%)
Mutual labels:  texture, asyncdisplaykit
Texture
Smooth asynchronous user interfaces for iOS apps.
Stars: ✭ 7,512 (+8842.86%)
Mutual labels:  texture, asyncdisplaykit
Mp3ID3Tagger
🎶🎵A macOS application to edit the ID3 tag of your mp3 files. Developed with RxSwift and RxCocoa. 🎸🎼
Stars: ✭ 17 (-79.76%)
Mutual labels:  rxswift
MGCleanArchitecture
Clean Architecture with RxSwift & MVVM - Templates and Solutions
Stars: ✭ 156 (+85.71%)
Mutual labels:  rxswift
RxEureka
This library is a small RxSwift wrapper around Eureka
Stars: ✭ 37 (-55.95%)
Mutual labels:  rxswift
RxTask
An RxSwift implementation of a command line runner.
Stars: ✭ 14 (-83.33%)
Mutual labels:  rxswift
DailyNews
Daily News is a news app with good looking user interface ! Apps architecture is MVVM and used RxSwift for binding.
Stars: ✭ 31 (-63.1%)
Mutual labels:  rxswift
TVToday
iOS TV Shows app with TMDb Api. RxSwift, MVVM, Clean Architecture. Tuist + Swift Package Manager
Stars: ✭ 27 (-67.86%)
Mutual labels:  rxswift
mvvm-with-rxswift-mentoring-008
Project and source code of the iOS Dev Mentoring #008 - Test-driven MVVM with RxSwift
Stars: ✭ 45 (-46.43%)
Mutual labels:  rxswift
ITKTextureFeatures
Fast, Texture Feature Maps from N-Dimensional Images
Stars: ✭ 16 (-80.95%)
Mutual labels:  texture
RxResponderChain
RxResponderChain
Stars: ✭ 18 (-78.57%)
Mutual labels:  rxswift
RxSwiftDemo
RxSwift Demo
Stars: ✭ 19 (-77.38%)
Mutual labels:  rxswift
mvcvm-swift-file-templates
Swift file templates for boosting mobile app development.
Stars: ✭ 16 (-80.95%)
Mutual labels:  rxswift
texture-morpher
A solution to panorama transition problem (with a tool to make morphable texture) 解决全景过渡不自然的问题
Stars: ✭ 16 (-80.95%)
Mutual labels:  texture
Cathay
an iOS project for demonstration of Reactive Programming
Stars: ✭ 21 (-75%)
Mutual labels:  rxswift
Cycle.swift
An experiment in unidirectional architecture inspired by Cycle.js. https://cycle.js.org
Stars: ✭ 24 (-71.43%)
Mutual labels:  rxswift

RxMVVM-Texture best practice

RxSwift MVVM pattern best practice built on Texture(AsyncDisplayKit) and written in Swift

alt text

[ Model ]

class Repository: Decodable {
    var id: Int
    var user: User?
    var repositoryName: String?
    var desc: String?
    var isPrivate: Bool = false
    var isForked: Bool = false

    enum CodingKeys: String, CodingKey {
        case id = "id"
        case user = "owner"
        case repositoryName = "full_name"
        case desc = "description"
        case isPrivate = "private"
        case isForked = "fork"
    }

    func merge(_ repo: Repository?) {
        guard let repo = repo else { return }
        user?.merge(repo.user)
        repositoryName = repo.repositoryName
        desc = repo.desc
        isPrivate = repo.isPrivate
        isForked = repo.isForked
    }

[ ViewModel ]

class RepositoryViewModel {

    // @INPUT
    let didTapUserProfile = PublishRelay<Void>()
    let updateRepository = PublishRelay<Repository>()
    let updateUsername = PublishRelay<String?>()
    let updateDescription = PublishRelay<String?>()
    
    // @OUTPUT
    var openUserProfile: Observable<Void>
    var username: Driver<String?>
    var profileURL: Driver<URL?>
    var desc: Driver<String?>
    var status: Driver<String?>
    
    let id: Int
    
    private let disposeBag = DisposeBag()
    
    deinit {
        // release Model from DataProvider
        RepoProvider.release(id: id)
    }
    
    init(repository: Repository) {
        self.id = repository.id
        
        // retain Model to DataProvider
        RepoProvider.addAndUpdate(repository)
        
        // load Model Observer from ModelProvider
        let repoObserver = RepoProvider.observable(id: id)
            .asObservable()
            .share(replay: 1, scope: .whileConnected)
        
        self.username = repoObserver
            .map { $0?.user?.username }
            .asDriver(onErrorJustReturn: nil)
        
        self.profileURL = repoObserver
            .map { $0?.user?.profileURL }
            .asDriver(onErrorJustReturn: nil)
        
        self.desc = repoObserver
            .map { $0?.desc }
            .asDriver(onErrorJustReturn: nil)

[ View ]

class RepositoryListCellNode: ASCellNode {

    init(viewModel: RepositoryViewModel) {

        ... 

        // ViewModel Binding

        userProfileNode.rx
            .tap(to: viewModel.didTapUserProfile)
            .disposed(by: disposeBag)
        
        viewModel.profileURL.asObservable()
            .bind(to: userProfileNode.rx.url)
            .disposed(by: disposeBag)
        
        viewModel.username.asObservable()
            .bind(to: usernameNode.rx.text(Node.usernameAttributes),
                  setNeedsLayout: self)
            .disposed(by: disposeBag)
        
        viewModel.desc.asObservable()
            .bind(to: descriptionNode.rx.text(Node.descAttributes),
                  setNeedsLayout: self)
            .disposed(by: disposeBag)
        
        viewModel.status.asObservable()
            .bind(to: statusNode.rx.text(Node.statusAttributes),
                  setNeedsLayout: self)
            .disposed(by: disposeBag)
    }
    

alt text

Open Profile

class RepositoryListCellNode: ASCellNode {

    ...

    init(viewModel: RepositoryViewModel) {

        ... 

        // HERE!
        userProfileNode.rx
            .tap(to: viewModel.didTapUserProfile)
            .disposed(by: disposeBag)
    }
    
class RepositoryViewModel {
    // @INPUT
    let didTapUserProfile = PublishRelay<Void>()
    
    // @OUTPUT
    var openUserProfile: Observable<Void>

    ... 

    init(repository: Repository) {

        ... 

        // HERE!
        self.openUserProfile = self.didTapUserProfile.asObservable()   
    }

}
class RepositoryViewController: ASViewController<ASTableNode> {

    ...

    func tableNode(_ tableNode: ASTableNode, nodeBlockForRowAt indexPath: IndexPath) 
    -> ASCellNodeBlock {
        return {
            guard self.items.count > indexPath.row else { return ASCellNode() }
            let viewModel = self.items[indexPath.row]
            let cellNode = RepositoryListCellNode(viewModel: viewModel)
            
            // HERE!
            viewModel.openUserProfile
                .observeOn(MainScheduler.asyncInstance)
                .subscribe(onNext: { [weak self] _ in
                    self?.openUserProfile(indexPath: indexPath)
                }).disposed(by: self.disposeBag)
            
            return cellNode
        }
    }

}

alt text

Update description

class UserProfileViewController: ASViewController<ASDisplayNode> {
    
    ...


    init(viewModel: ...) {
        
        ... 


        // HERE!
        self.descriptionNode.textView.rx.text
            .bind(to: self.viewModel.updateDescription,
                  setNeedsLayout: self.node)
            .disposed(by: self.disposeBag)
    }
}
class RepositoryViewModel {

    // @INPUT
    let updateDescription = PublishRelay<String?>()
    
    // @OUTPUT
    var desc: Driver<String?>
    
    init( ... ) {

        ...

        let repoObserver = RepoProvider.observable(id: id)
            .asObservable()
            .share(replay: 1, scope: .whileConnected)

        self.desc = repoObserver
            .map { $0?.desc }
            .asDriver(onErrorJustReturn: nil)

        updateDescription.withLatestFrom(repoObserver) { ($0, $1) }
            .subscribe(onNext: { text, repo in
                guard let repo = repo else { return }
                repo.desc = text
                RepoProvider.update(repo)
            }).disposed(by: disposeBag)
    }
}

alt text

Example Video

Example Video Link

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].