2016-06-30 1 views
1

초보자 인 Swift와 ReactiveCocoa을 처음 사용했습니다. 나는 영화 목록을 보여주는 앱을 만들고 MVVM 패턴을 사용하고있다. 내 뷰 모델은 다음과 같습니다ReactiveCocoa (4.2.1) 및 Swift를 사용한 비동기 이미지로드

class HomeViewModel { 

    let title:MutableProperty<String> = MutableProperty("") 
    let description:MutableProperty<String> = MutableProperty("") 
    var image:MutableProperty<UIImage?> = MutableProperty(nil) 

    private var movie:Movie 

    init (withMovie movie:Movie) { 

     self.movie = movie 

     title.value = movie.headline 
     description.value = movie.description 

     Alamofire.request(.GET, movie.pictureURL) 
      .responseImage { response in 

       if let image = response.result.value { 
        print("image downloaded: \(image)") 
        self.image.value = image 
       } 
     } 

    } 
} 

와 나는이 같은 jQuery과 내 세포를 구성하고 싶습니다 :

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 

    let cell = tableView.dequeueReusableCellWithIdentifier("MovieCell", forIndexPath: indexPath) as! MovieCell 
    let movie:Movie = movieList[indexPath.row] 
    let vm = HomeViewModel(withMovie: movie) 

    // fill cell with data 
    vm.title.producer.startWithNext { (newValue) in 
     cell.titleLabel.text = newValue 
    } 

    vm.description.producer.startWithNext { (newValue) in 
     cell.descriptioLabel.text = newValue 
    } 

    vm.image.producer.startWithNext { (newValue) in 
     if let newValue = newValue { 
      cell.imageView?.image = newValue as UIImage 
     } 
    } 

    return cell 
} 

이 반응성 코코아에 대한 올바른 접근 방식인가? Title과 Description을 Mutable 또는 그냥 이미지로 선언해야합니다 (단 하나만 변경됨). 나는 바인딩을 사용할 수 있다고 생각하지만 진행 방법을 모르겠습니다.

답변

1

Reactive Cocoa + MVVM 패턴을 사용하여이 작업을 수행하려면 먼저 모든 논리를 이동하여 뷰 모델에서 셀 클래스 자체로 셀을 구성해야합니다. 그런 다음 viewModel에서 MutableProperties를 제거하십시오 (실제로 변경할 수 없으며 해당 신호가 필요하지 않습니다). 이미지에 대한

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { 
    let cell = tableView.dequeueReusableCellWithIdentifier("MovieCell", forIndexPath: indexPath) as! MovieCell 
    cell.viewModel = self.viewModelForIndexPath(indexPath) 
    return cell 
} 

private func viewModelForIndexPath(indexPath: NSIndexPath) -> MovieCellViewModel { 
    let movie: Movie = movieList[indexPath.row] 
    return HomeViewModel(movie: movie) 
} 

다음

처럼 우리에게 무언가를주는 start()가 호출 될 때 이미지를 가져 오기보다는 암시 적으로 뷰 모델에 호출 될 때 init를 가져 오는하기 위해 네트워크 요청을 수행하는 신호 프로듀서 노출
class MovieCell: UITableViewCell 
    @IBOutlet weak var titleLabel: UILabel 
    @IBOutlet weak var descriptionLabel: UILabel 
    @IBOutlet weak var imageView: UIImageView 

    var viewModel: MovieCellViewModel { 
    didSet { 
     self.configureFromViewModel() 
    } 
    } 

    private func configureFromViewModel() { 
    self.titleLabel.text = viewModel.title 
    self.descriptionLabel.text = viewModel.description 
    viewModel.fetchImageSignal() 
     .takeUntil(self.prepareForReuseSignal()) //stop fetching if cell gets reused 
     .startWithNext { [weak self] image in 
     self?.imageView.image = image 
     } 
    } 

    //this could also go in a UITableViewCell extension if you want to use it other places 
    private func prepareForReuseSignal() -> Signal<(), NoError> { 
    return Signal { observer in 
     self.rac_prepareForReuseSignal // reactivecocoa builtin function 
     .toSignalProducer() // obj-c RACSignal -> swift SignalProducer 
     .map { _ in() } // AnyObject? -> Void 
     .flatMapError { _ in .empty } // NSError -> NoError 
     .start(observer) 
    } 
    } 
} 

와 뷰 모델

struct HomeViewModel { 
    private var movie: Movie 

    var title: String { 
    return movie.headline 
    } 

    var description: String { 
    return movie.description 
    } 

    func fetchImageSignal() -> SignalProducer<UIImage, NSError> { 
    return SignalProducer { observer, disposable in 
     Alamofire.request(.GET, movie.pictureURL) 
     .responseImage { response in 
      if let image = response.result.value { 
      print("image downloaded: \(image)") 
      observer.sendNext(image) //send the fetched image on the signal 
      observer.sendCompleted() 
      } else { 
      observer.sendFailed(NSError(domain: "", code: 0, userInfo: .None)) //send your error 
      } 
     } 
    } 
} 
에서