[iOS | RxSwift] Transforming Operator

minji0801·2022년 1월 25일
0

RxSwift

목록 보기
6/6
post-thumbnail

개요

RxSwift 연산자 중에서 가장 중요한 Transforming Operator(변환 연산자)에 대해서 알아보자.


Transforming Operator

  • toArray
    toArray는 각 요소를 배열로 만들어준다. Observable의 just를 한 결과와 같다.
let disposeBag = DisposeBag()
Observable.of("A", "B", "C")    // Observable.just(["A", "B", "C"])와 같다.
    .toArray()
    .subscribe(onSuccess: {
        print($0)
    })
    .disposed(by: disposeBag)
    
// print
// ["A", "B", "C"]
  • map
    map은 Observable에서 사용된다는 것을 제외하고 기본 map과 동일하다.
Observable.of(Date())
    .map { date -> String in
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd"
        dateFormatter.locale = Locale(identifier: "ko_KR")
        return dateFormatter.string(from: date)
    }
    .subscribe(onNext: {
        print($0)
    })
    .disposed(by: disposeBag)
    
// print
// 2022-01-25
  • flatMap
    flatMap은 중첩된 Observable에서 element를 뽑아낼 수 있다.
protocol student {
    var score : BehaviorSubject<String> { get }
}

struct grade1: student {
    var score: BehaviorSubject<String>
}

let Meri = grade1(score: BehaviorSubject<String>(value: "A"))   // 초기값을 가진 상태
let Jhon = grade1(score: BehaviorSubject<String>(value: "B+"))   // 초기값을 가진 상태

let exam = PublishSubject<student>()

exam
    .flatMap { student in
        student.score
    }
    .subscribe(onNext: {
        print($0)
    })
    .disposed(by: disposeBag)

exam.onNext(Meri)       // 초기값 "A" 방출
Meri.score.onNext("A+")

exam.onNext(Jhon)       // 초기값 "B+" 방출
Meri.score.onNext("B+")
Jhon.score.onNext("A")
    
// print
// A
// A+
// B+
// B+
// A
  • flatMapLatest
    flatMapLatest는 가장 최근의 시퀀스의 값만 가져온다.
    아래의 예제를 살펴보면 final.onNext(Ria) 구문에서 초깃값 "B"가 방출되고, Ria.score.onNext("B+") 구문에서 "B+"이 방출된다.
    그리고 나서 final.onNext(Jey) 구문에서 해당 시퀀스가 가장 최신 시퀀스로 바뀌었기 때문에 Ria와 관련된 시퀀스는 무시한다.
    따라서 그 다음 줄인 Ria.score.onNext("C") 구문이 무시되어 "C"가 방출되지 않은 것이다.
struct grade2: student {
    var score: BehaviorSubject<String>
}

let Ria = grade1(score: BehaviorSubject<String>(value: "B"))   // 초기값을 가진 상태
let Jey = grade1(score: BehaviorSubject<String>(value: "A+"))   // 초기값을 가진 상태

let final = PublishSubject<student>()

final
    .flatMapLatest { student in
        student.score
    }
    .subscribe(onNext: {
        print($0)
    })
    .disposed(by: disposeBag)

final.onNext(Ria)       // 초기값 "B" 방출
Ria.score.onNext("B+")

final.onNext(Jey)       // 초기값 "A+" 방출
Ria.score.onNext("C")
Jey.score.onNext("A")
    
// print
// B
// B+
// A+
// A
  • materialize and dematerialize
    materialize는 요소에 이벤트를 감싸서 방출하고, 반대로 dematerialize는 해당 요소만 방출한다.
    아래의 예제에서 dematerialize()를 주석 처리했을 때와 주석 처리하지 않았을 때는 print 되는 내용이 다른 것을 확인할 수 있다.
enum error: Error {
    case stop
}

struct run: student {
    var score: BehaviorSubject<String>
}

let Anne = run(score: BehaviorSubject<String>(value: "10"))
let Ven = run(score: BehaviorSubject<String>(value: "7"))

let evaluation = BehaviorSubject<student>(value: Anne)

evaluation
    .flatMapLatest { student in
        student.score
            .materialize()  // 값에 이벤트를 감싸서 함께 표시
    }
    .filter {
        guard let error = $0.error else {
            return true
        }
        print(error)
        return false
    }
    .dematerialize()    // 값만 표시
    .subscribe(onNext: {
        print($0)
    })
    .disposed(by: disposeBag)

Anne.score.onNext("8")
Anne.score.onError(error.stop)
Anne.score.onNext("5")

evaluation.onNext(Ven)
    
// print(materialize)
// next(10)
// next(8)
// stop
// next(7)

// print(dematerialize)
// 10
// 8
// stop
// 7

전화번호 예시

앞서 배운 Filtering OperatorTransforming Operator를 사용해서 전화번호를 입력하면 010-0000-0000 형태로 보여주는 코드를 작성해 볼 것이다.

let input = PublishSubject<Int?>()
let list: [Int] = [1]

input
    .flatMap { $0 == nil ? Observable.empty() : Observable.just($0) }   // nil 제외
    .map { $0! }    // 옵셔널 처리
    .skip(while:  { $0 != 0 })  // 전화번호는 0으로 시작하니까, 0이 아닐때까지 건너띄기
    .take(11)       // 총 11자리 가져오기
    .toArray()      // 배열로
    .asObservable() // toArray() -> Single로 되니까 다시 Observalble로 바꾸기
    .map {
        $0.map {"\($0)"}    // Int to String
    }
    .map { numbers in
        var numberList = numbers
        numberList.insert("-", at: 3)   // 010-
        numberList.insert("-", at: 8)   // 010-0000-
        let number = numberList.reduce(" ", +)
        return number
    }
    .subscribe(onNext: {
        print($0)
    })
    .disposed(by: disposeBag)

input.onNext(10)
input.onNext(0)
input.onNext(nil)
input.onNext(1)
input.onNext(0)
input.onNext(1)
input.onNext(2)
input.onNext(nil)
input.onNext(3)
input.onNext(4)
input.onNext(5)
input.onNext(6)
input.onNext(7)
input.onNext(8)

// print
// 010-1234-5678

마무리

Transforming Operator에 대해서 알아보았다.
연산자의 종류가 다양하고 처음 접해봐서 어떻게 사용해야 할지 감이 잘 안 잡힌다.
하지만, 마지막 전화번호 예시처럼 각 연산자의 기능을 잘 조합하면 효율적인 코드를 작성할 수 있겠다.

profile
iOS Developer

0개의 댓글