# code layout thoughts ## view definitions - `UIView` - basic element to build UI, buttons, labels, layouts etc. - rectangle area - can contain other `UIView` objects - _similar to "View" on android_ - `UIViewController` - contains at least one `UIView` - _similar to "Acticity" on android_ - `UITabBarController` - manages a number of `UIViewController` accessible via a button bar (abottom) - typically not subclassed, says apple - `UINavigationController` - manage a stack of `UIViewController` and a navigation bar (atop) - performs horizontal view transitions for pushed and popped views (via `pushViewController()`, `popViewController()`) - typically subclasses, says apple - "delegate" - a "delegate" is a set of functions, defined by a protocol, to receive events as `didSelect()` [2] - the functions in the "delegate" are called by the "delegator" - both, "delegator" and "delegate" may be view controllers, eg. ViewControllerA ("delegator") is sending an event to ViewControllerB ("delegate") - in practise, subclasses just override functions defined by "delegate" protocol, as `didSelect()` - used view controllers the system provides - "coordinator" - convention, no requirement from UIKit - defined as an empty protocol class from where other derive - they typically take a `UINavigationController` as argument on construction and call eg. `pushViewController()` to navigate to other view controllers - stored as a member of the view controller - everything done there could also be done in a view controller directly - when derived from an abstract base class, in theory, this helps on making things more reusable by removing the navigation from the view, see next point - "coordinator protocol" - convention to define a base for coordinators having the same interface - eg. ContactDetailCoordinatorProtocol ## app definitions - `UIApplication` - every app has exactly one instance of `UIApplication` [1] - typically not subclassed - created by calling `UIApplicationMain()` or by adding the attribute `@UIApplicationMain` to a class derived from `UIApplicationDelegate` - main entry point is `application(_:didFinishLaunchingWithOptions:)` [4] - _similar to "Application" on android_ - `UIApplicationDelegate` - a "delegate" (see "view delegate" for definition) for events on application-level as `applicationWillEnterForeground()` - we need to derive from `UIApplicationDelegate` and pass this to `UIApplicationMain` - `UIApplication.shared.delegate` will keep a reference to our delegate - we can store "app globals" here, as DcContext - `UIWindow` - backdrop for the ui, dispatches events, derived from `UIView` - stored in `UIApplication.shared.delegate.window`, - created in `application(_:didFinishLaunchingWithOptions:)`; in pure iOS13 apps set in SceneDelegate - an app typicaly has only one window - `window.rootViewController` is the anchor of all view controllers ## holding references a tricky part (see eg. [3]) seems to be to hold the correct type of rerences to the UIViewControllers. - at least one "strong" (normal) reference is needed somewhere. - only "weak" or "unowned" references are not sufficient ("weak" always needs unwrap and may be come nil at any time, "unowned" is kind of always-unwrapped "weak"). - currently, we hold strong references to the coordinators which hold strong references to the view controllers ## what is needed? what adds unneeded complexity? - "delegates" are needed to get events from the system - we need some dead-simple way to persist the objects that need persistance - do we need our own delegates? or just call funcions directly as needed? what is the current state? - is the overhead of "coordinator" really needed and helpful? - why not just call eg. `pushViewController()` and make sure it is persisted as needed (coordinators would not help on that imu anyway, see bug at [3]) - TODO: `AppCoordinator` is also derived from NSObject - is there a know reason for that? - seem not to help on making code readable - esp. as all coordinators are in a separate file - i think the "coordinator" as they are used now are of very limited use, - however, the coordinator get the UINavigationContoller as argument, so there is some use - remove "coordinator protocol" and do not use? - in general, for some minor adaptions, a simple flag eg. hiding some elements appear much more readable to me that having several levels of abstraction - when we really come to the point where we need to reuse things, we can add these things as needed (or use parameters on creation "flag"), i think, in the current state, coordinators create more harm than use. - the tearing up of navigator/coordinator into two files is unfortunate, in swiftUI, this is re-combined to a single file, child-class and a makeCoordinator() called by SwiftUI: https://developer.apple.com/tutorials/swiftui/interfacing-with-uikit ## notification system - `NotificationCenter.default.post()` post events to all observers _synchronously_ on the same thread; this may not be the thread the observer registered itself [5]. Observers may receive the event in any random thread therefore. - best practise [6] would be that the observers assume, they receive the notification in any thread and call `DispatchQueue.main.async` where actually needed. for new code, we should use that approach, it won't hurt. - however, existing code might assume that the notification arrives in main thread; therefore, we also call `DispatchQueue.main.async` for sending events out. - using always main thread for sending/receiving and adding/remove observers also avoids other multithreading-issues of NotificationCenter itself, see https://lapcatsoftware.com/articles/nsnotificationcenter-is-threadsafe-not.html for details. tl;dr: post from main thread and also add/remove observers from main thread. on receiving, assume, things will block. ## notification system: adding and removing observers - for the `addObserver()` variant returning an object (overview at https://developer.apple.com/documentation/foundation/notificationcenter ) always save the object as `NSObjectProtocol` not as `Any?` - otherwise you risk to mix addObserver() functions as also the non-return variant can be saved as `Any?` (Swift allows saving "no return type" as `Any?`) - call `removeObserver(self)` only in deinit, otherwise use `removeObserver(self, name, obj)` or `removeObserver(obj)` ## some sources [1] https://developer.apple.com/documentation/uikit/uiapplication [2] delegator/delegate: https://stackoverflow.com/questions/7052926/what-is-the-purpose-of-an-ios-delegate https://developer.apple.com/library/archive/documentation/General/Conceptual/DevPedia-CocoaCore/Delegation.html [3] weak coordinator bugs: https://github.com/deltachat/deltachat-ios/issues/675, https://github.com/deltachat/deltachat-ios/issues/323 [4] https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622921-application [5] https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Notifications/Articles/Threading.html#//apple_ref/doc/uid/20001289-CEGJFDFG [6] https://medium.com/@hadhi631/myths-and-facts-about-nsnotifcation-posting-and-receiving-df7f5729b19f