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)
Related
I am new to Clean VIP Architecture and I am struggling with its entry point.
(I am only putting some bit of code)
ViewController
protocol Delegate: class {
func execute()
}
class TitlesViewController:UIViewController {
weak var delegate: Delegate?
func viewDidLoad() {
super.viewDidLoad()
delegate.execute()
}
}
Configurator
class TitlesConfigurator {
static func configureModule(viewController: TitlesViewController) {
let interactor = Interaction()
viewController.delegate = interactor
}
}
In AppDelegate
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let titlesViewController = TitlesViewController()
let navigationController = UINavigationController(rootViewController: titlesViewController)
TitlesConfigurator.configureModule(viewController: titlesViewController)
window = UIWindow()
window?.rootViewController = navigationController
window?.makeKeyAndVisible()
return true
}
Now Issue that I am facing is that there is no reference of interactor outside of TilesConfigurator and delegate is weak which means its total arc is 0. It results in delegate = nil inside viewDidLoad
How can I improve or fix this issue in my architecture.
P.S: I don't think its good practice to make a strong reference of delegate inside ViewController
Delegate shouldn't be weak here
var delegate: Delegate?
As there is one part that's weak which is let interactor = Interaction() so no retain cycles will occur
I am following this tutorial on Coordinator pattern, and so far I have it going through creating the coordinator and running the start() from app delegate. But then calling a function on that coordinator doesn't work, because suddenly the coordinator var is nil. It seems that the initial view controller that is shown is not the one coming from the coordinator.start() but rather the one from the storyboard entry point. I did disable the Main in the main interface of the projects target.
AppDelegate:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
if let registry = DependencyResolver.shared as? DependencyRegistry {
DependencyGraph.setup(for: registry)
}
let navController = UINavigationController()
coordinator = MainCoordinator(navigationController: navController)
coordinator?.start()
window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = navController
window?.makeKeyAndVisible()
return true
}
Main coordinator:
class MainCoordinator: Coordinator {
var navigationController: UINavigationController
var childCoordinators = [Coordinator]()
init(navigationController: UINavigationController) {
self.navigationController = navigationController
}
func start() {
let vc = InitViewController.instantiate()
vc.coordinator = self. //!!I do hit this breakpoint
navigationController.pushViewController(vc, animated: false)
}
}
Init view controller (the one that's initial in the storyboard, but I'm showing it with coordinator):
class InitViewController: UIViewController, Storyboarded {
private var cameraViewModel: CameraViewModel!
weak var coordinator: MainCoordinator?
#IBAction func openCameraView(_ sender: Any) {
coordinator?.openCameraView() //!!here coordinator is nil
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
Storyboarded protocol - used to get view controller out of the storyboard by id:
protocol Storyboarded {
static func instantiate() -> Self
}
extension Storyboarded where Self: UIViewController {
static func instantiate() -> Self {
let fullName = NSStringFromClass(self)
let className = fullName.components(separatedBy: ".")[1]
let storyboard = UIStoryboard(name: "Main", bundle: Bundle.main)
let leController = storyboard.instantiateViewController(withIdentifier: className) as! Self
return leController
}
}
The problem is merely that the tutorial you're looking at is too old for current conditions. There is no Single View App template any more, and the App Delegate no longer contains the window. If you create a project in Xcode 11 or Xcode 12, the window is owned by the scene delegate. Implement the scene delegate's willConnect method to do the work that the app delegate was doing in the tutorial, and everything will spring to life.
Also the mechanism for preventing Main.storyboard from trying to load automatically has changed; you have to remove it from Application Scene Manifest in the Info.plist (editing by hand — there is no simple interface).
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.
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.
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
}
}