2016-06-30 2 views
1

Я начинаю использовать ReactiveCocoa с Swift в первый раз. Я создаю приложение, показывающее список фильмов, и я использую шаблон MVVM. Моя ViewModel выглядит следующим образом:Загрузка изображения Async с помощью 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 
       } 
     } 

    } 
} 

, и я хотел бы настроить свои ячейки в UITableView, как это:

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 
} 

Является ли это правильный подход к Reactive какао? Должен ли я объявлять название и описание как Mutable или просто изображение (единственное изменение). Я думаю, что могу использовать привязку, но я не уверен, как действовать дальше.

ответ

1

, чтобы сделать это с использованием реактивных шаблонов какао + MVVM, я бы сначала переместил всю логику, чтобы сконфигурировать ячейку из ее viewmodel в сам класс ячейки. а затем удалить MutableProperties из viewModel (они фактически не изменяемы, и нам не нужны эти сигналы). и для изображения разоблачить производитель сигналу, который будет выполнять сетевой запрос для извлечения изображения при start() называется, а не беспрекословно выборкой, когда init называется на ViewModel, что дает нам что-то вроде

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) 
} 

, а затем

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) 
    } 
    } 
} 

и в ViewModel

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 
      } 
     } 
    } 
} 

 Смежные вопросы

  • Нет связанных вопросов^_^