Skip to content
  • Home
  • Code
  • iOS & Swift
  • Combine
  • RxSwift
  • SwiftUI
  • Flutter & Dart
  • Tutorials
  • Art
  • Blog
Fx Studio
  • Home
  • Code
  • iOS & Swift
  • Combine
  • RxSwift
  • SwiftUI
  • Flutter & Dart
  • Tutorials
  • Art
  • Blog
Written by chuotfx on October 6, 2020

RxSwift vs. UIKit – Tương tác giữa các ViewController

RxSwift

Contents

  • Chuẩn bị
  • 1. Setup
    • 1.1. ViewController
    • 1.2. Observable Properties
  • 2. Emit data
    • 2.1. Vấn đề
    • 2.2. Emit
  • 3. Observing Observables
  • 4. Disposing subscriptions
  • Tạm kết

Chào bạn đến với Fx Studio. Chúng ta lại tiếp tục khám phá thêm miền bugs mới. Bài viết lần này sẽ trình bày cách tương tác dữ liệu giữa 2 ViewController trong thế giới RxSwift.

Nếu bạn đã bỏ qua bài Hello ViewController bằng RxSwift, thì có thể tham khảo lại link sau:

    • RxSwift vs. UIKit – Hello ViewController

Và mọi thứ đã ổn rồi, thì …

Bắt đầu thôi!

Chuẩn bị

    • Xcode 11
    • Swift 5.x

Chúng ta vẫn sử dụng project ở bài trước. Nếu bạn xây dựng một project ngay từ đầu thì phải thêm 1 ViewController nữa. Ngoài ra, dành cho các bạn chưa biết checkout ở đâu, thì hãy tham khảo link sau:

    • Link: checkout
    • Thư mục: /Examples/BasicRxSwift

Bạn hãy thêm một màn hình nữa, để phục vụ cho việc lựa chọn Avatar của User lúc đăng ký tài khoản. Nó có tên là ChangeAvatarViewController. Nó bao gồm:

  • 1 CollectionView với cell hiển thị ảnh
  • Select vào mỗi cell sẽ chọn ảnh

Ta edit lại một chút chỗ function changeAvatar ở RegisterViewController để hiển thị được ChangeAvatartViewController khi người dùng nhấn vào.

    @objc func changeAvatar() {
        let vc = ChangeAvatarViewController()
        self.navigationController?.pushViewController(vc, animated: true)
    }

Okay! xong phần dạo đầu. Chúng ta tiến vào phần chính.

1. Setup

1.1. ViewController

Tất nhiên, công việc đầu tiên là cài đặt cho ChangeAvatarViewController cũng có được khả năng RxSwift. Mở file ViewController đó lên và import vào:

import RxSwift
import RxCocoa

Hai thư viện hàng đầu của chúng ta. Giờ thêm tiếp Túi rác quốc dân vào nữa.

private let bag = DisposeBag()

Có thể chúng ta không dùng tới nó, nhưng để cho chắc chắn thì bạn hãy thêm nó vào. Và tập thành thói quen sau này.

1.2. Observable Properties

Tiếp tục với phần thêm các properties của ViewController là các Observable nào.

    private let selectedPhotosSubject = PublishSubject<UIImage>()

    var selectedPhotos: Observable<UIImage> {
        return selectedPhotosSubject.asObservable()
    }

Ta thấy có tới 2 Observables

  • selectedPhotosSubject là một Subject, nó sẽ emit dữ liệu và không cần thiết phải khai báo thêm giá trị ban đầu cho nó.
    • Vì lúc hiển thị ViewController lên, thì người dùng chưa có sự lựa chọn nào hết.
    • Quan trọng nhất là nó private
  • selectedPhotos đây là phát ngôn viên của ViewController.
    • Để bên ngoài (các đối tượng khác) có thể subscribe tới thuộc tính này của ViewController.
    • Thực chất là việc biến đổi subject kia thành 1 observable

Và cả 2 đều có kiểu dữ liệu Output khi emit là UIImage.

2. Emit data

2.1. Vấn đề

Vâng, chúng ta đã sử dụng được rất rất là cơ bản RxSwift trong một UIViewController rồi. Để giải quyết 2 vấn đề là:

  • Update dữ liệu lên UI Control
  • Xử lý sự kiện người dùng

Bài toán của chúng ta tiếp tục với việc tương tác với một ViewController khác. Giờ project chúng ta đã có 2 ViewController. Nhiệm vụ quan trọng trong bài này chính là:

Truyền dữ liệu giữa các ViewController

Trong việc tương tác này, chúng ta chỉ cần quan tâm tới việc truyền dữ liệu theo chiều ngược lại. Và các phương pháp trước đây được sử dụng là:

  • Protocol Delegate
  • Closure
  • Notification

Và bây giờ, chúng ta sẽ dùng RxSwift để thêm một cách nữa trong việc truyền dữ liệu này.

2.2. Emit

Sau khi đã cài đặt các Observable  (bước setup), công việc tiếp theo là phát dữ liệu đi. Lúc này, chúng ta không quan tâm là dữ liệu phát đi thì ai sẽ nhận được.

Phát dữ liệu thì trong RxSwift sẽ gọi là emit.

Với collectionview tại delegate của nó, chúng ta sẽ bắt được sự kiện người dùng chọn vào cell. Từ đó tiến hành emit dữ liệu.

    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        selectIndex = indexPath.row
        collectionView.reloadData()
        
        //emit UIImage
        if let image = UIImage(named: "avatar_\(indexPath.row + 1)") {
            selectedPhotosSubject.onNext(image)
        }
    }

Toán tử .onNext khi được Subject thực thi, thì dữ liệu truyền vào đó sẽ được gởi đi. Chỉ có vậy thôi, coi như chúng ta đã xong phần của ChangeAvatarViewController rồi.

3. Observing Observables

Đã xong việc, chúng ta có bên phát đi rồi và đối nghịch nó là bên nhận. Chúng ta lại quay về RegisterViewController để làm công việc này.

Nhận dữ liệu từ ViewController khác.

Tại function changeAvatar, tiến hành subscribe tới các Observable của ChangeAvatarViewController. Ta thêm đoạn code này vào.

    @objc func changeAvatar() {
        let vc = ChangeAvatarViewController()
        self.navigationController?.pushViewController(vc, animated: true)
        
        //subscribe
        vc.selectedPhotos
            .subscribe(onNext: { img in
                self.image.accept(img)
            }, onDisposed: {
                print("Complete changed Avatar")
            })
        .disposed(by: bag)
    }

Trong đó

  • .selectedPhotos là đối tượng Observable mà chúng ta đã tạo bên ViewController kia. Nó là phát ngôn viên của nó.
  • subscribe để nhận dữ liệu từ ViewController kia phát ra.
  • .onNext handle việc nhận dữ liệu. Từ đó, chúng ta dùng BehaviorRelay để accept dữ liệu mới lên UI của ViewController bên này.
  • .onDispose để xem việc đăng kí này có kết thúc hay là không. Điều này rất quan trọng.

Giờ bạn có thể build app và test lại mọi thứ đã hoạt động tốt hay không.

4. Disposing subscriptions

Nếu bạn để ý kĩ đoạn code này tại function changeAvatar ở RegisterViewController sẽ không bao giờ chạy.

onDisposed: {
                print("Complete changed Avatar")
            }

Đây thực sự là rất nguy hiểm.

Nguyên nhân vì sao?

  • Khi bạn đăng ký tới 1 Observable và nén nó vào disposeBag , khi đó subscription sẽ được lưu giữ lại tại disposeBag.
  • Subscription sẽ giải phóng khi nào disposeBag giải phóng.
  • Tuy nhiên, RegisterViewController là rootView của ứng dụng lúc này. Có nghĩa là nó tồn tại vĩnh viễn với thời gian.

Các subscription khi chúng ta đăng ký tới vẫn còn tồn tại. Bộ nhớ vẫn bị chiếm giữ. Mà chúng ta không làm gì được hết ở đây.

Cách giải quyết sẽ là:

Nếu disposeBag không tự kết liễu, thì sẽ cho kết thúc observable.

Vâng, quay lại ChangeAvatarViewController, thêm function này vào.

    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        
        selectedPhotosSubject.onCompleted()
    }

Với toán tử .onCompleted() thì subject sẽ kết thúc. Và không bao giờ emit ra nữa. Đồng nghĩa với các đăng ký từ các subscriber tới nó cũng bị huỷ. Test lại app, chúng ta đã thấy xuất hiện dòng print ở onDispose rồi.

 

OKAY! Tới đây thì mình xin kết thúc bài viết. Nếu có gì thắc mắc hay góp ý cho mình thì bạn có thể để lại bình luận hoặc gởi email theo trang Contact.

Tạm kết

  • Mục đích bài này là truyền dữ liệu qua lại giữa 2 ViewController
  • Dễ hiểu hơn là viết lại delegate bằng RxSwift một cách đơn giản. Ta không cần quan tâm việc tạo các protocol và implement các function đó.

Cảm ơn bạn đã đọc bài viết này!

FacebookTweetPinYummlyLinkedInPrintEmailShares23

Related Posts:

  • RxCocoa Basic – Extend UIKit
    RxCocoa Basic – Extend UIKit
  • RxSwift vs. UIKit - Hello ViewController
    RxSwift vs. UIKit - Hello ViewController
  • RxSwift vs. UIKit - Fetching Data from API
    RxSwift vs. UIKit - Fetching Data from API
  • SwiftUI - Phần 3 : Tích hợp SwiftUI và UIKit
    SwiftUI - Phần 3 : Tích hợp SwiftUI và UIKit
Tags: rxswift
Written by chuotfx

Hãy ngồi xuống, uống miếng bánh và ăn miếng trà. Chúng ta cùng nhau đàm đạo về đời, về code nhóe!

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

Donate – Buy me a coffee!

Fan page

Fx Studio

Tags

Actor Advanced Swift api AppDistribution Asynchronous autolayout basic ios tutorial blog callback ci/cd closure collectionview combine concurrency CoreData Core Location crashlytics darkmode dart dart basic dart tour Declarative decoding delegate deploy fabric fastlane firebase flavor flutter GCD iOS mapview MVVM optional protocol rxswift Swift Swift 5.5 SwiftUI SwiftUI Notes tableview testing TravisCI unittest

Recent Posts

  • Raw String trong 10 phút
  • Dispatch Semaphore trong 10 phút
  • Tổng kết năm 2022
  • KeyPath trong 10 phút – Swift
  • Make color App Flutter
  • Ứng dụng Flutter đầu tiên
  • Cài đặt Flutter SDK & Hello world
  • Coding Conventions – người hùng hay kẻ tội đồ?
  • Giới thiệu về Flutter
  • Tìm hiểu về ngôn ngữ lập trình Dart

You may also like:

  • Tích hợp UIViewController (UIKit) vào SwiftUI Project -…
    Tích hợp UIViewController (UIKit) vào SwiftUI Project - SwiftUI Notes #14
  • RxSwift vs. UIKit - Fetching Data from API
    RxSwift vs. UIKit - Fetching Data from API
  • SwiftUI - Phần 3 : Tích hợp SwiftUI và UIKit
    SwiftUI - Phần 3 : Tích hợp SwiftUI và UIKit
  • Tích hợp SwiftUI vào UIKit Project - SwiftUI Notes #13
    Tích hợp SwiftUI vào UIKit Project - SwiftUI Notes #13
  • RxSwift - Traits
    RxSwift - Traits

Archives

  • February 2023 (1)
  • January 2023 (2)
  • November 2022 (2)
  • October 2022 (1)
  • September 2022 (5)
  • August 2022 (6)
  • July 2022 (7)
  • June 2022 (8)
  • May 2022 (5)
  • April 2022 (1)
  • March 2022 (3)
  • February 2022 (5)
  • January 2022 (4)
  • December 2021 (6)
  • November 2021 (8)
  • October 2021 (8)
  • September 2021 (8)
  • August 2021 (8)
  • July 2021 (9)
  • June 2021 (8)
  • May 2021 (7)
  • April 2021 (11)
  • March 2021 (12)
  • February 2021 (3)
  • January 2021 (3)
  • December 2020 (3)
  • November 2020 (9)
  • October 2020 (7)
  • September 2020 (17)
  • August 2020 (1)
  • July 2020 (3)
  • June 2020 (1)
  • May 2020 (2)
  • April 2020 (3)
  • March 2020 (20)
  • February 2020 (5)
  • January 2020 (2)
  • December 2019 (12)
  • November 2019 (12)
  • October 2019 (19)
  • September 2019 (17)
  • August 2019 (10)

About me

Education, Mini Game, Digital Art & Life of coders
Contacts:
contacts@fxstudio.dev

Fx Studio

  • Home
  • About me
  • Contact us
  • Mail
  • Privacy Policy
  • Donate
  • Sitemap

Categories

  • Art (1)
  • Blog (22)
  • Code (4)
  • Combine (22)
  • Flutter & Dart (24)
  • iOS & Swift (86)
  • RxSwift (37)
  • SwiftUI (76)
  • Tutorials (70)

Newsletter

Stay up to date with our latest news and posts.
Loading

    Copyright © 2023 Fx Studio - All rights reserved.

    Share this ArticleLike this article? Email it to a friend!

    Email sent!