Неожиданный ноль после добавления программного представления вместо раскадровки - PullRequest
0 голосов
/ 17 марта 2019

У меня есть приложение, написанное по шаблону MVVM-C, использующее RxSwift

. После добавления нового представления программно приложение вылетает с

. Поток 1: Неустранимыйошибка: неожиданно обнаружен ноль при развертывании необязательного значения

ошибка.Я в полной растерянности, реализация почти точно такая же, за исключением того факта, что один контроллер представления является раскадровкой, а другой - нет.

Это мой новый ViewController

import UIKit
import RxSwift
import RxCocoa

final class FeedViewController: TableViewController, ViewModelAttaching {

    var viewModel: Attachable<FeedViewModel>!
    var bindings: FeedViewModel.Bindings {
        let viewWillAppear = rx.sentMessage(#selector(UIViewController.viewWillAppear(_:)))
            .mapToVoid()
            .asDriverOnErrorJustComplete()

        let refresh = tableView.refreshControl!.rx
            .controlEvent(.valueChanged)
            .asDriver()

        return FeedViewModel.Bindings(
            fetchTrigger: Driver.merge(viewWillAppear, refresh),
            selection: tableView.rx.itemSelected.asDriver()
        )
    }

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    func bind(viewModel: FeedViewModel) -> FeedViewModel {
        viewModel.posts
            .drive(tableView.rx.items(cellIdentifier: FeedTableViewCell.reuseID, cellType: FeedTableViewCell.self)) { _, viewModel, cell in
                cell.bind(to: viewModel)
            }
            .disposed(by: disposeBag)

        viewModel.fetching
            .drive(tableView.refreshControl!.rx.isRefreshing)
            .disposed(by: disposeBag)

        viewModel.errors
            .delay(0.1)
            .map { $0.localizedDescription }
            .drive(errorAlert)
            .disposed(by: disposeBag)

        return viewModel
    }

}

Это существующий, который работает, но использует раскадровки

final class PostsListViewController: TableViewController, ViewModelAttaching {

    var viewModel: Attachable<PostsListViewModel>!
    var bindings: PostsListViewModel.Bindings {
        let viewWillAppear = rx.sentMessage(#selector(UIViewController.viewWillAppear(_:)))
            .mapToVoid()
            .asDriverOnErrorJustComplete()
        let refresh = tableView.refreshControl!.rx
            .controlEvent(.valueChanged)
            .asDriver()

        return PostsListViewModel.Bindings(
            fetchTrigger: Driver.merge(viewWillAppear, refresh),
            selection: tableView.rx.itemSelected.asDriver()
        )
    }

    // MARK: - Lifecycle

    override func viewDidLoad() {
        super.viewDidLoad()
        setupView()
    }

    // MARK: - View Methods

    private func setupView() {
        title = "Posts"
    }

    func bind(viewModel: PostsListViewModel) -> PostsListViewModel {
        viewModel.posts
            .drive(tableView.rx.items(cellIdentifier: PostTableViewCell.reuseID, cellType: PostTableViewCell.self)) { _, viewModel, cell in
                cell.bind(to: viewModel)
            }
            .disposed(by: disposeBag)

        viewModel.fetching
            .drive(tableView.refreshControl!.rx.isRefreshing)
            .disposed(by: disposeBag)

        viewModel.errors
            .delay(0.1)
            .map { $0.localizedDescription }
            .drive(errorAlert)
            .disposed(by: disposeBag)

        return viewModel
    }

}

Они в основном точно такие же.Исключение выдается в строке let refresh = tableView.refreshControl!.rx.

Рабочий координатор, использующий раскадровку:

import RxSwift

class PostsCoordinator: BaseCoordinator<Void> {
    typealias Dependencies = HasPostService

    private let navigationController: UINavigationController
    private let dependencies: Dependencies

    init(navigationController: UINavigationController, dependencies: Dependencies) {
        self.navigationController = navigationController
        self.dependencies = dependencies
    }

    override func start() -> Observable<Void> {
        let viewController = PostsListViewController.instance()
        navigationController.viewControllers = [viewController]

        let avm: Attachable<PostsListViewModel> = .detached(dependencies)
        let viewModel = viewController.attach(wrapper: avm)

        viewModel.selectedPost
            .drive(onNext: { [weak self] selection in
                self?.showDetailView(with: selection)
            })
            .disposed(by: viewController.disposeBag)

        // View will never be dismissed
        return Observable.never()
    }

    private func showDetailView(with post: Post) {
        let viewController = PostDetailViewController.instance()
        viewController.viewModel = PostDetailViewModel(post: post)
        navigationController.showDetailViewController(viewController, sender: nil)
    }

}

У меня есть расширение, позволяющее мне его также создавать,

protocol Reusable {
    static var reuseID: String { get }
}

extension Reusable {
    static var reuseID: String {
        return String(describing: self)
    }
}

// MARK: - View Controller

extension UIViewController: Reusable {
    class func instance() -> Self {
        let storyboard = UIStoryboard(name: reuseID, bundle: nil)
        return storyboard.instantiateViewController()
    }
}

extension UIStoryboard {
    func instantiateViewController<T: UIViewController>() -> T {
        guard let viewController = self.instantiateViewController(withIdentifier: T.reuseID) as? T else {
            fatalError("Unable to instantiate view controller: \(T.self)")
        }
        return viewController
    }
}

'Сломанный' координатор точно такой же, за исключением того, что я поменялся

 let viewController = PostsListViewController.instance()

на

let viewController = FeedViewController()

Я совершенно не понимаю, почему это бросок.Печатные операторы и точки останова в различных точках не приводят к нулю ни при каких значениях.

Пожалуйста, дайте мне знать, если будет проще поделиться примером приложения, так как я понимаю, что фрагменты кода могут быть не самыми очевидными.

Ответы [ 2 ]

1 голос
/ 17 марта 2019

tableView.refreshControl - это nil. Вы пытаетесь принудительно получить доступ к nil refreshControl.

Свойство Refreshing равно Enabled для UITableViewController в вашей раскадровке, которая работает. В программной версии refreshControl не создается автоматически.

0 голосов
/ 17 марта 2019

Значением по умолчанию для свойства refreshControl является ноль. Вам нужно создать экземпляр и присвоить UIRefreshControl значению self.refreshControl до его появления.

Когда вы создаете свое представление с помощью раскадровки и включаете его, об этом позаботятся за вас. Программно вам потребуется реализовать это самостоятельно.

...