What am I Doing wrong in Swinject? - ios

Whenever I run this code the init() of the VCModel gets called but Swinject is not Injecting the VCModel instance to my ViewController. Can Someone Please tell me what am I doing wrong?
Error I get is:
Unexpectedly Found nil while unwrapping an optional value in
ViewController viewModel.cellModels
AppDelegate
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
container = Container() { con in
con.register(VCModeling.self) { _ in
VCModel()
}
con.storyboardInitCompleted(ViewController.self) { r, c in
c.viewModel = r.resolve(VCModeling.self)!
}
}
let window = UIWindow(frame: UIScreen.main.bounds)
window.backgroundColor = UIColor.white
window.makeKeyAndVisible()
self.window = window
let bundle = Bundle(for: ViewController.self)
let storyboard = SwinjectStoryboard.create(name: "Main", bundle: bundle, container: container)
window.rootViewController = storyboard.instantiateInitialViewController()
return true
}
ViewController
private let disposeBag = DisposeBag()
var viewModel: VCModeling!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
viewModel.cellModels
.bind(to: tableView.rx.items(cellIdentifier: "myCell", cellType: MyCellClass.self)) {
i, cellModel, cell in
cell.viewModel = cellModel
}.disposed(by: disposeBag)
}

Can you try out the following code in AppDelegate.swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
container = Container() { con in
con.register(VCModeling.self) { _ in
VCModel()
}
con.storyboardInitCompleted(ViewController.self) { r, c in
c.viewModel = r.resolve(VCModeling.self)!
let window = UIWindow(frame: UIScreen.main.bounds)
window.backgroundColor = UIColor.white
window.makeKeyAndVisible()
self.window = window
window.rootViewController = c
}
}
return true
}

Code in your AppDelegate → application:didFinishLaunchingWithOptions method does seems to work properly. I have verified it with following code:
class ViewController: UIViewController {
var viewModel: VCModeling!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
print(viewModel.uuid)
}
}
protocol VCModeling {
var uuid: UUID { get }
}
class VCModel: VCModeling {
let uuid: UUID
init() {
self.uuid = UUID()
}
}
I can not tell what your VCModel's init method looks like but looking at
...
// Do any additional setup after loading the view.
viewModel.cellModels
...
From the error you are getting:
Unexpectedly Found nil while unwrapping an optional value in ViewController viewModel.cellModels
It looks like cellModels which I assume is implicitly unwrapped property, you must initialize it VCModel's init method.

Related

Swift: Delegate is nil

I'm trying to create an AppCoordinator which holds the different ViewControllers in my app:
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
window = UIWindow(frame: UIScreen.main.bounds)
let appCoordinator = AppCoordinator()
window?.rootViewController = appCoordinator.initialTabBarController()
window?.makeKeyAndVisible()
return true
}
// ...
import SnapKit
class AppCoordinator {
private let initialTabController = TabBarController()
private let cars = CarProvider.sharedInstance.getCars()
init() {
var viewControllers = [ViewController]()
viewControllers.append(carListViewController())
initialTabController.setViewControllers(viewControllers, animated: true)
}
private func carListViewController() -> CarListViewController {
let controller = CarListViewController(cars: cars)
print(self)
controller.delegate = self
return controller
}
}
// MARK: - CarListViewControlerDelegate
extension AppCoordinator: CarListViewControlerDelegate {
func didPullToRefresh() {
print("Did pull")
}
}
My CarListViewController looks like this:
import UIKit
protocol CarListViewControlerDelegate: class {
func didPullToRefresh()
}
class CarListViewController: ViewController {
weak var delegate: CarListViewControlerDelegate?
// MARK: Interface Properties
private let tableView = TableView()
private let refreshControl = UIRefreshControl()
private var cars: [Car]?
// MARK: Initializers
init(cars: [Car]) {
super.init(nibName: nil, bundle: nil)
self.cars = cars
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
// MARK: - Actions
extension CarListViewController {
#objc private func refresh() {
delegate?.didPullToRefresh()
refreshControl.endRefreshing()
}
}
My refresh method gets called when I pull to refresh in my UITableView, but the protocol method didPullToRefresh doesn't. When I check on the delegate, it's value is nil.
You need a strong reference so make it an instance var inside AppDelegate
let appCoordinator = AppCoordinator()
OR
var appCoordinator:AppCoordinator!
self.appCoordinator = AppCoordinator()
As Sh_Khan said you need a strong reference. You should change the way you set the delegate for created CarListViewController.
I've changed your code as below. let me know if the problem solved.
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
window = UIWindow(frame: UIScreen.main.bounds)
let appCoordinator = AppCoordinator()
window?.rootViewController = appCoordinator.initialTabBarController()
window?.makeKeyAndVisible()
return true
}
// ...
import SnapKit
class AppCoordinator {
private let initialTabController = TabBarController()
private let cars = CarProvider.sharedInstance.getCars()
var controller = CarListViewController!
init() {
var viewControllers = [ViewController]()
let carListVC = CarListViewController(cars: cars)
self.controller = carListVC
self.controller.delegate = self
viewControllers.append(self.controller)
initialTabController.setViewControllers(viewControllers, animated: true)
}
}
// MARK: - CarListViewControlerDelegate
extension AppCoordinator: CarListViewControlerDelegate {
func didPullToRefresh() {
print("Did pull")
}
}

Swinject container registration fails

While trying to bind some protocols to various classes I have encountered a strange behaviour of getting nil in the protocols.
This is the dependency injector class:
class DependencyInjector: NSObject {
let container: Container
override init() {
container = Container()
super.init()
register()
}
}
extension DependencyInjector {
func register() {
container.register(NetworkProtocols.self) { _ in ApiManager() }.inObjectScope(.container)
container.register(ConnectivityManager.self) { r in
let controller = ConnectivityManager()
controller.network = r.resolve(NetworkProtocols.self)
return controller
}
container.storyboardInitCompleted(ChooseCameraViewController.self) {r,c in
c.network = r.resolve(NetworkProtocols.self)
}
container.storyboardInitCompleted(InstallationViewController.self) {r,c in
c.network = r.resolve(NetworkProtocols.self)
}
container.storyboardInitCompleted(CameraSetupViewController.self) {r,c in
c.network = r.resolve(NetworkProtocols.self)
}
container.storyboardInitCompleted(LoginViewController.self) { r,c in
c.network = r.resolve(NetworkProtocols.self)
}
}
}
This is the AppDelegate:
var dependencyInjector: DependencyInjector!
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
dependencyInjector = DependencyInjector()
let window = UIWindow(frame: UIScreen.main.bounds)
self.window = window
let bundle = Bundle(for: ViewController.self)
let storyboard = SwinjectStoryboard.create(name: "Main", bundle: bundle, container: dependencyInjector.container)
window.rootViewController = storyboard.instantiateInitialViewController()
return true
}
In the class below I get nil in the protocol property:
class ConnectivityManager: NSObject {
var network: NetworkProtocols!
func connectHotSpot() {
self.network.whoAmI(success: {
print("api succses")
}, failure: { (error) in
print(error.localizedDescription)
})
}
}
The network var is always nil while in the other classes that use storyboardInitCompleted the network var works.
What am I doing wrong?
As #JakubVano said I had to obtain an instance of ConnectivityManager:
container.register(ConnectivityManager.self) { r in
let controller = ConnectivityManager()
controller.network = r.resolve(NetworkProtocols.self)
return controller
}
container.storyboardInitCompleted(ChooseCameraViewController.self) {r,c in
c.connectivityManager = r.resolve(ConnectivityManager.self)!
}

Swift weak delegate

my code:
public func start() {
let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
guard let listVC = storyboard.instantiateViewController(withIdentifier: "ListVC") as? ListVC else { return }
let viewModel = ListViewModel(dependencies: appDependencies)
viewModel.delegate = self
listVC.listViewModel = viewModel
navigationController?.pushViewController(listVC, animated: true)
}
protocol ListViewModelDelegate: class {
func needChangeScreen(cellViewModel: UserCellViewModel)
}
final class ListViewModel {
weak var delegate: ListViewModelDelegate?
func userPressed(at index: IndexPath) {
delegate?.needChangeScreen(cellViewModel: cellViewModels[index.row])
}
}
User pressed is called from UIViewController , then i want to send callback to Coordinator to start another coordinator but delegate? is always nil. I know that delegates should be weak but in this case is not working for me. Any ideas?
Okey i have fix but i do not know is it good.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let navigationController = UINavigationController()
navigationController.setNavigationBarHidden(true, animated: false)
window?.rootViewController = navigationController
let appDependencies = configureAppDependencies()
let coordinator = AppCoordinator(navigationController: navigationController, appDependencies: appDependencies)
coordinator.start()
window?.makeKeyAndVisible()
return true
}
That was my app delegate function(In AppCoordinator start is created and pushed ListCoordinator) but when i changed let coordinator for instance var :
var coordinator: AppCoordinator?
weak delegate is not nil and everything works.

UIViewController's delegate var becomes nil

I have already read all articles but could not find a solution. I have a class to organize navigation between controllers:
class Navigator: NotesListViewControllerDelegate {
var rootController:UINavigationController!
lazy private var notesListController:NotesListViewController! = {
let controller = NotesListViewController(nibName: String(NotesListViewController), bundle: nil)
controller.title = "Notes"
return controller
}()
init() {
self.rootController = UINavigationController.init(rootViewController: self.notesListController)
self.notesListController.delegate = self
}
//MARK: NotesListController delegate
func plusButtonDidSelect() {
print("plus button did select")
}
}
My NotesListController:
protocol NotesListViewControllerDelegate:class {
func plusButtonDidSelect()
}
class NotesListViewController: UIViewController {
weak var delegate:NotesListViewControllerDelegate? {
didSet {
print("delegate = \(delegate)")
}
}
System shows in didSet what variable has setted. But in viewDidLoad self.delegate is already has nil value. What could be a problem?
EDIT: my didFinishLaunchingWithOptions method:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
self.window = UIWindow.init(frame: UIScreen.mainScreen().bounds)
let navigator = Navigator()
if let window = self.window {
window.backgroundColor = UIColor.whiteColor()
window.rootViewController = navigator.rootController
window.makeKeyAndVisible()
}
return true
}
EDIT 2 I resolved this problem by taking out navigator variable from didFinishLaunchingWithOptions method to other class variables (it becomes like strong property in obj-c). But why system not keeps it in memory when it was declared localy in didFinishLaunchingWithOptions method?
What is maintaining a reference to your Navigator instance ?
The NotesListViewController instance only has a weak reference so if nothing else keeps the Navigator alive, it will go out of scope and the delegate variable will go nil in notesListController (which I assume is being referenced elsewhere because it is being presented)

OS X app in Swift without XIB

Has anyone figured out a way to bootstrap an NSWindow-based app without using a XIB (or IB at all)? In iOS, this is fairly simple;
func application(application: UIApplication!, didFinishLaunchingWithOptions launchOptions: NSDictionary!) -> Bool {
window = UIWindow(frame: UIScreen.mainScreen().bounds)
window!.rootViewController = ViewController(nibName: nil, bundle: nil)
window!.makeKeyAndVisible()
window!.backgroundColor = UIColor.whiteColor()
return true
}
But I just can't get it to work right for a desktop app.. NSWindow doesn't have a makeKeyAndVisible() method, and NSApplicationDelegate seems to have a dozen overrides for func application().. What's the magic incantation?
import Cocoa
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
#IBOutlet weak var window: NSWindow!
let newWindow = NSWindow(contentRect: NSScreen.mainScreen()!.frame, styleMask: NSBorderlessWindowMask, backing: NSBackingStoreType.Buffered, defer: false)
func applicationDidFinishLaunching(aNotification: NSNotification) {
// Insert code here to initialize your application
newWindow.opaque = false
newWindow.movableByWindowBackground = true
newWindow.backgroundColor = NSColor.whiteColor()
newWindow.makeKeyAndOrderFront(nil)
}
func applicationWillTerminate(aNotification: NSNotification) {
// Insert code here to tear down your application
}
}

Resources