Clean VIP with Delegate Pattern - ios

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

Related

Prevent iOS app from loading default Storyboard entry point

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

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.

How to pass data from appdelegate to a viewcontroller?

Hello I am trying to implement a push notification feature in my IOS app. Currently I have code that will open a specific viewcontroller if the app was opened through the push notification. The way i'm doing this is by pushing the viewcontroller ontop.
What I need to do now is to pass some data to the viewcontroller from the appdelegate. I understand to pass data from viewcontroller to viewcontroller is to use prepareforsegue. I tried doing this and it did not work
I tried researching how to accompolish this task but a lot of the answers on stackoverflow were outdated swift code that I don't have the ability to convert to swift 3. can someone explain to me how would i send data from appdelegate to a viewcontroller?
heres the code to show the VC which is in didFinishLaunchingWithOptions
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let destinationViewController = storyboard.instantiateViewController(withIdentifier: "detailPin2") as! detailPinViewController
let navigationController = self.window?.rootViewController as! UINavigationController
navigationController.pushViewController(destinationViewController, animated: false)
//Declare you variable somewhere within the app delegate scope
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
var myVariable: String = "Hello Swift"
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
// some other app delegate’s methods....
}
//In your view controller
class ViewController: UIViewController {
#IBOutlet weak var myLabel: UILabel!
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let myOtherVariable = appDelegate.myVariable
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
myLabel.text = myOtherVariable
var anotherVariable: String = appDelegate.myVariable // etc...
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
OK - you've got most of the work already done...
When using segues, iOS creates your view controller and gives you access to it in prepare, where you probably have done something like this:
if let vc = segue.destination as? detailPinViewController {
vc.myVariable = "this is the data to pass"
}
In didFinishLaunchingWithOptions, you are doing the creation part, and the "presentation" part... so you can just add in the "pass the data" part:
let destinationViewController = storyboard.instantiateViewController(withIdentifier: "detailPin2") as! detailPinViewController
destinationViewController.myVariable = "this is the data to pass"
let navigationController = self.window?.rootViewController as! UINavigationController
navigationController.pushViewController(destinationViewController, animated: false)
and that should do it.

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