user14215604
user14215604

Reputation:

Presenter gives nil error in the VIPER Project

In the project I made with Viper, the presenter in ViewController gives a nil error. What is the reason of this? And how can I solve this problem?

AppDelegate

let wireframe = MovieWireframe()
let movies = wireframe.createMovieController()
window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = UINavigationController(rootViewController: movies)
window?.makeKeyAndVisible()

ViewController

Presenter here gives a nil error.

var presenter: IMoviePresenter? //Nil
   
override func viewDidLoad() {
  super.viewDidLoad()

  presenter?.startFetchingMovies() //Nil 
}

Presenter

StartFetchingMovies here doesn't work because presenter is nil.

class MoviePresenter: IMoviePresenter {
    
    var view: IMovieView?
    var interactor: IMovieInteractor
    var router: IMovieWireframe
    var movies: MoviesResult?
    
    init(view: IMovieView?, interactor: IMovieInteractor, router: IMovieWireframe) {
        self.view = view
        self.interactor = interactor
        self.router = router

    }
    

    func startFetchingMovies() {
        interactor.fetchMovies()

    }
    
    func showMovieController(navigationController: UINavigationController) {
        router.pushToMovieDetailScreen(navigationController: navigationController)
    }
}

extension MoviePresenter: IMovieInteractorDelegate {
    func movieInteractorDidSuccessToFetchMovies(movies: MoviesResult) {
        self.movies = movies
        view?.showMovie(movies: movies)
    }
    
    func movieInteractorDidFailToFetchMovies() {
        print("error")
    }
}

Router

View.presenter here is not nil.

class MovieWireframe: IMovieWireframe {
    
    func createMovieController() -> UIViewController {
        print("createMovieController")
        let view = MovieVC()
        let movieService = APIClient()
        let interactor = MovieInteractor(movieService: movieService)
        let presenter = MoviePresenter(
            view: view,
            interactor: interactor,
            router: self
        )
        
        view.presenter = presenter
        print("presenter: \(presenter)")
        interactor.delegate = presenter

        return view
    }
    
    func pushToMovieDetailScreen(navigationController: UINavigationController) {
        
        
    }
}

Upvotes: 3

Views: 597

Answers (1)

Andreas ZUERCHER
Andreas ZUERCHER

Reputation: 902

ViewController's init completes prior to createMovieController being invoked between the 1st & 2nd lines here:

let wireframe = MovieWireframe()
let movies = wireframe.createMovieController()

For that brief interim of time, the view loaded (due to creating the wireframe) with

var presenter: IMoviePresenter?

as nil (hence why momentarily the need for the question mark there) and caused viewDidLoad to be invoked ‘prematurely’ (i.e., prior to the 2nd line being invoked). Therefore, eliminate the 2-part construction so that it is not done piecemeal 1) without MovieController then 2) finished as a 2nd step outside of the MovieWireframe construction. 1-part construction not 2-part.

Upvotes: 0

Related Questions