Skip to main content

Dùng thư viện RXSWIFT để cải tiến hiệu suất cho dự án iOS của bạn — phần 2


RxSwift
  1. Observable :
    - Là một sequence , nó bắn các event qua các khoảng thời gian khác nhau. Event có thể chứa value Int ,String , Custom type hay là một gesture, 1 tap khi nhấn button.

- Life cycle của nó như sau : khi emit 1 event đó là next, Khi complete or error là kết thúc 1 stream.

Obserable Cycle
-Observable vs observer
// OBSERVABLE //
let observable = Observable<String>.create { (observer) -> Disposable in
    DispatchQueue.global(qos: .default).async {
        Thread.sleep(forTimeInterval: 10)
        observer.onNext("Hello dummy 🐣")
        observer.onCompleted()
    }
    return Disposables.create()
}  
      
// OBSERVER //
observable.subscribe(onNext: { (element) in
    print(element)
}).addDisposableTo(disposeBag)
Ví dụ trên bạn có thể thấy observable là cái sẽ emit ra event, còn observer là cái nhận event.
-Tạo một observable
// ========1.Tạo từ những giá trị cho trước======= //
Observable.of(1, 2, 3, 4, 5, 6)let observable = Observable<String>.create { (observer) ->  Disposable in
    observer.onNext("merge : 🎁")
    return Disposables.create()
}// =========2.Khi get API dùng URLSession ===========//func getRepositories(githubId:String) -> Observable<[Repository]> {
    guard let url = NSURL(string: "https://api.github.com/users/\(githubId)/repos")
        else { return Observable.just([]) }
    return URLSession.sharedSession
        .rx_JSON(NSURLRequest(URL: url))
        .retry(3)
        .map {
            var data = [Repository]()if let items = $0 as? [[String: AnyObject]] {
                items.forEach {
                    guard let name = $0["name"] as? String,
                        let url = $0["url"] as? String
                        else { return }data.append(Repository(name: name, url: url))
                }
            }return data
    }
}//==============3.Từ Subject vs Variable========== //
struct Student {
   var score: Variable<Int>
}
let student = PublishSubject<Student>()
student.asObservable()
.flatMap {
        $0.score.asObservable()
    }
.subscribe(onNext: {
        print($0)
    })
.addDisposableTo(disposeBag)
-Subscribing : dùng hàm subscribe để đăng kí nhận event
//Subscribe tất cả event
observable.subscribe { event in
  print(event)
}//Subscribe on next
observable.subscribe(onNext: { element in
  print(element)
})//Subscribe on next vs complete
observable.subscribe(onNext: { element in
      print(element)
},onCompleted: {
      print("Completed")
})//Subscribe với next,complete,error
.subscribe(
    onNext: {
        print($0) },
    onError: {
        print($0) },
    onCompleted: {
        print("Completed") 
    }
)
-Dispose : dùng để clean up, giải phóng 1 observable
Dispose Bag : trả về 1 loại giống ARC , tự giải phóng khi Owner đang subscribe vào hàm deinit giải phóng bộ nhớ.
Dispose Bag
// Chúng ta sẽ dispose bằng hàm, khi nào chúng ta cần dừng
subscription.dispose()// Dùng 1 instance DisposeBag, sẽ tự động gọi dispose khi class hiện tại đang cần subscribe đã deinitlet disposeBag = DisposeBag()
Observable.of("A", "B", "C")
.subscribe { 
   print($0) 
}
.disposed(by: disposeBag)
-Với các observable dùng hàm "create" để tạo sẽ return 1 dispose
return Disposables.create(with: {
    //Clean up
    connection.close()
    database.closeImportantSomething()
    cache.clear()
})Observable<String>.create { observer in
    observer.onNext("1")
    return Disposables.create()
}
.subscribe(
    onNext: {
       print($0) },
    onError: {
       print($0) },
    onCompleted: {
       print("Completed") },
    onDisposed: {
       print("Disposed") }
)
//.disposed(by: disposeBag)
+ Nhưng với dispose kiểu này thì phải gọi complete or error nó mới dispose => leak memory
+ Còn dùng .disposed(by: disposeBag) => Sẽ luôn luôn được giải phóng và không gây leak memory
-Chúng ta có 2 loại observable là HOT vs COLD
+ HOT : observable chỉ bắt đầu thực hiện khi có 1 observer subscribe.
Dùng trong các loại : Variables / properties / constants, tap coordinates, mouse coordinates, UI control values, current time.
+ COLD : observable luôn luôn thực hiện bất kể ko có 1 observer subscribe nào.
Dùng trong các loại : Async operations, HTTP Connections, TCP connections, streams.
2.SubjectsLà một loại đặc biệt có thể là 1 observable(output) và cũng là 1 observer(input) . Có 4 loại subjects :
+ PublicSubject : bắt đầu rỗng và chỉ bắn ra event khi có subscribe.
+ BehaviorSubject : bắt đầu với giá trị khởi tạo và sẽ bắn kèm cho subscribe mới với 1 event gần nhất.
+ ReplySubject : giống behavior, và có thể para : buffersize để xác định số lập lại.
+ Variable : wrap một BehaviorSubject và lưu trữ giá trị như một trạng thái(state).Để observer thì bạn dùng từ khoá asObservable.
Sơ đồ sau sẽ giải thích cho các bạn rõ sự khác nhau.
PublicSubject vs BehaviorSubject vs ReplySubject
-PublicSubject : bắt đầu rỗng và chỉ bắn ra event khi có subscribe, nó bỏ qua các phần tử đã bắn trc đó trước khi nhận 1 subscibe
//Khởi tạo 1 PublishSubject  
let subject = PublishSubject<String>()//Chuyển PublishSubject -> Observable
let observable : Observable<String> = subject //Chưa có 1 subscribe nào -> onNext ko có tác động
 subject.onNext("bỏ qua") //Bắt đầy subscribe 
 observable
 .subscribe(onNext: { text in
         print(text)
     })
 .addDisposableTo(disposeBag) // Khi trong hàng đợi subscribe có ít nhất 1 cái
 subject.onNext("Start!")
-BehaviorSubject : bắt đầu với giá trị khởi tạo và sẽ bắn kèm cho subscribe mới với 1 event gần nhất.Chỉ nhận mới nhất và mới nhất gần nhất
let subjectBehavior = BehaviorSubject<String>(value: "Gia tri ban dau")
let observable : Observable<String> = subjectBehaviorsubjectBehavior.onNext("event1")
subjectBehavior.onNext("event2")
subjectBehavior.onNext("event3") // mới nhất gần nhấtobservable
.subscribe(onNext: { text in
        print(text)
    })
.addDisposableTo(disposeBag)subjectBehavior.onNext("event4") // mới nhất
 
// event3 
// event4
-ReplySubject : giống behavior, và có thể para : buffersize để xác định số lập lại ngoài cái mới nhất
// Định nghĩa ReplaySubject với số lần lặp lại là 2
let subjectReply = ReplaySubject<String>().create(bufferSize: 2)
let observable : Observable<String> = subjectReplysubjectReply.onNext("event1")
subjectReply.onNext("event2") // mới nhất lần 2
subjectReply.onNext("event3") // mới nhất lần 1observable
.subscribe(onNext: { text in
        print(text)
})
.addDisposableTo(disposeBag)subjectReply.onNext("event4") // mới nhất// event 2
// event 3
// event 4
-Variable : wrap một BehaviorSubject, nó giống BehviorSubject ở chỗ sẽ bắt các event mới nhất và gần mới nhất , lưu trữ giá trị như một trạng thái(state).Variable có property value có thể gắn giá trị vào đó.Để observer thì bạn dùng từ khoá asObservable.
let variable = Variable<Int>(0)
print("Before first subscription ---")let variable1 = variable.asObservable()
    .subscribe(onNext: { n in
        print("First \(n)")
    }, onCompleted: {
        print("Completed 1")
})print("Before send 1")
variable.value = 1//Before first subscription ---
//First 0
//Before send 1
//First 1
Ok , phần này mình đã trình bày cho các bạn về Observable vs Subject trong RXSwift. Trong phần sau minh sẽ nói về các toán tử (operator), giúp kết hợp các observable theo các chức năng khác nhau.

Comments

Popular posts from this blog

Swift GCD part 1: Thread safe singletons

Preview Singletons are entities, referenced to the same instance of a class from everywhere in your code. It doesn't matter if you like them or not, you will definitely meet them, so it's better to understand how they work. Constructing and handling a set of data doesn't seem to be a big challenge at first glance. The problems appear when you try to optimise the user experience with background work and your app starts acting weird. ??‍♂️ After decades of watching your display mostly with a blank face, you finally realize that your data isn't handled consistently by the manager because you're accessing it (running tasks on it) from multiple threads at the same time. So you really do have to deal with making your singletons thread safe. This article series is dedicated to thread handling using Swift. In the first part below you will get a comprehensive insight into som...

RxSwift - Giới thiệu

Bạn có nghe qua về Reactive Programming chưa, đây là một khái niệm lập trình hoàn toàn mới. “Reactive Programming là lập trình theo hướng bất đồng bộ các dòng chảy dữ liệu(data streams)” Sau khi đọc khái niệm các bạn có lẽ rất mơ hồ, vậy các data streams là gì. Vậy để mình ví dụ cho các bạn: - Trong lập trình bình thường ( imperative programming ) a = b + c , tức a được gắn với kết quả cộng của b và c .Khi b với c thay đổi thì tất nhiên sẽ không ảnh hưởng tới a, nhưng trong Reactive Programming này a sẽ tự động thay đổi khi b hoặc c thay đổi, kết quả của a sẽ lập tức thay đổi theo. Vậy tại sao nên dùng RXSwift: + Giúp ứng dụng của bạn hoạt động 1 cách như là real time update ui khi có thay đổi data flow, mang đến trải nghiệm tuyệt vời cho User. + Dễ dàng lập trình bất đồng bộ (Async) thay vì phải dùng : callbacks, delegates, notifications, kvo… Bạn phải dùng nhiều thứ gây ra ứng dụng của bạn phức tạp, khó kiểm soát. + Chuyển đổi các thread khi thực hiện 1 cách...

Different between Map and FlatMap in RX

In short, the map applies a function to each emitted element and returns its result. Observable<Integer> observable = Observable . just (1,2,3) .map(x -> 10 * x); observable.subscribe(System. out ::println); Result: 10 20 30 So FlatMap applies a function to each emitted element, but this function returns the type of the Observable. I.e. 1 emitted by an element through a flatMap to generate a set of emitted elements or not one. Observable<String> observable = Observable . just ( "A" , "B" , "C" ) .flatMap(s -> { System. out .println(); return Observable. just (s + "1" , s + "2" , s + "3" ); }); observable.subscribe(s -> System. out .print(s + " " )); Result: A1 A2 A3 B1 B2 B3 C1 C2 C3