관리 메뉴

김종권의 iOS 앱 개발 알아가기

[iOS - swift] RxSwift의 Timer(interval)를 이용한 매초마다 API 실행 방법 (mix-in 패턴 사용) 본문

iOS 응용 (swift)

[iOS - swift] RxSwift의 Timer(interval)를 이용한 매초마다 API 실행 방법 (mix-in 패턴 사용)

jake-kim 2022. 1. 15. 21:38

5초마다 API를 통해 이미지 조회 후, 변경될때마다 업데이트

편리함을 위한 사용한 프레임워크

pod 'RxSwift'
pod 'RxCocoa'
pod 'SnapKit'
pod 'Then'

구현 아이디어

  • PhotoService라는 컴포넌트를 만든 후 이곳에서 Timer를 가지고 있고, 일정시간마다 이미지를 요청하여 Observable로 값 방출
  • 단, 값이 바뀔때만 방출 (distinctUntilChanged)
  • 사용하는쪽에서 PhotoService를 구독하고 있다가, 값이 방출되면 업데이트

구현

  • Photo값이 변경될때만 방출되도록 해야 하므로, Hashable 성격을 갖는 Photo모델 정의
    // Photo.swift
    
    import UIKit
    
    struct Photo {
      var name: String
      var image: UIImage? {
        UIImage(named: self.name)
      }
    }
    
    extension Photo: Hashable {
      static func == (lhs: Photo, rhs: Photo) -> Bool {
        lhs.name == rhs.name
      }
    }​
  • 주의) RxSwift의 timer()연산자와 interval()연산자 구분
    • timer(_:period:scheduler:): 첫번째 방출 시간을 정할 수 있음 (첫번째인자에 대입)
    • interval(_:scheduler:): 첫번째 방출 시간을 정할 수 없음
  • Timer를 가지고 일정 시간마다 Photo를 가져오는 PhotoTratis.swift 구현
    - 기록해야할 상태가 필요없으므로 mix-in 패턴에서 Traits로 구현
    (편의상 API 연동 없이 로컬 Image를 불러오도록 구현)
    • PhotoService 정의
      * distinctUntilChanged()를 통해 변경될때만 방출하도록 설정
      // PhotoService.swift
      
      import RxSwift
      import RxCocoa
      
      // MARK: Service
      protocol PhotoServiceType {
        static func getPhotoEveryFiveSeconds() -> Observable<Photo?>
      }
      
      private enum PhotoService: PhotoServiceType {
        static func getPhotoEveryFiveSeconds() -> Observable<Photo?> {
          Observable<Int>
            .timer(.seconds(1), period: .seconds(5), scheduler: MainScheduler.asyncInstance)
            .map { _ in self.getPhoto() }
            .distinctUntilChanged()
        }
        private static func getPhoto() -> Photo? {
          let photo1 = Photo(name: "img1")
          let photo2 = Photo(name: "img2")
          let photo3 = Photo(name: "img3")
          let photos = [photo1] + [photo2] + [photo3]
          let randomPhoto = photos.randomElement()
          return randomPhoto
        }
      }
    • PhotoTraits 정의
      // PhotoTraits.swift
      
      // MARK: Traits
      protocol PhotoTraits {
        static var photoService: PhotoServiceType.Type { get }
      }
      
      extension PhotoTraits {
        static var photoService: PhotoServiceType.Type { PhotoService.self }
      }​
  • 사용하는 쪽
    • PhotoTriats를 준수
      // ViewController.swift
      
      final class ViewController: UIViewController, PhotoTraits {
      }​
    • Self.photoService로 접근하여 사용
      Self.photoService.getPhotoEveryFiveSeconds()
        .bind { [weak self] in self?.photoImageView.image = $0?.image }
        .disposed(by: self.disposeBag)​
    • timer를 멈추는 방법은 disposeBag을 초기화
      self.button.rx.tap
        .bind { self.disposeBag = DisposeBag() }
        .disposed(by: self.disposeBag)​

* 전체 코드: https://github.com/JK0369/ExTimerWithAPI

Comments