The idea is to implement MVP structure with base protocols and classes which hold reference to generic view and presenter
// --- Base --- //
protocol BaseViewProtocol: class {
associatedtype P: BasePresenterProtocol
var presenter: P? { get set }
}
class BaseView<P: BasePresenterProtocol>: UIView, BaseViewProtocol {
var presenter: P?
}
protocol BasePresenterProtocol {
associatedtype V: BaseViewProtocol
weak var view: V? { get set }
}
class BasePresenter<V: BaseViewProtocol>: BasePresenterProtocol {
weak var view: V?
}
// --- Current --- //
protocol CurrentViewProtocol: BaseViewProtocol {
}
class CurrentView<P: CurrentPresenterProtocol>: BaseView<P>, CurrentViewProtocol {
}
protocol CurrentPresenterProtocol: BasePresenterProtocol {
}
class CurrentPresenter<V: CurrentViewProtocol>: BasePresenter<V>, CurrentPresenterProtocol {
init(currentView: V) {
super.init()
self.view = currentView
}
}
The question is how to instantiate concrete implementation of all these classes, since both View and Presenter are generic classes and depend on each other
Not sure this is best way but i had done similar thing in this way
protocol Presentable {
associatedtype View: ViewAble
weak var view: View? {get set}
init(with view: View)
func onAttach(view: View)
func onDetach()
var isAttached: Bool {get}
}
extension Presentable {
var isAttached: Bool {
return view != nil
}
}
class Presenter: Presentable {
weak var view: ViewAble? {
didSet {
if let view = view {
onAttach(view: view)
} else {
onDetach()
}
}
}
required init(with view: ViewAble) {
self.view = view
}
func onAttach(view: View) {
//pre set up on construction
}
func onDetach() {
//release some resource on destroying view
}
}
#objc protocol ViewAble: class {
#objc optional func showError(_ message: String, _ callBack: (() -> Void)?)
}
extension ViewAble where Self: UIViewController {
func showAlert(_ message: String?, _ callBack: (() -> Void)? = nil) {
let alertController = UIAlertController(title: nil, message: message, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default) { action in
callBack?()
})
self.present(alertController, animated: true, completion: callBack)
}
func showLoading() {
//show default Loading here and override if want custom
}
func stopLoading() {
//stop default Loading
}
}
class ViewController: ViewAble {
}
Related
I created a custom class with function delegation. Is it possible to call the didPlaneUpdate function in ViewController1 by calling the addToPlane () function from ViewController2 ? Below is my code:
// CustomClass.swift:
protocol PlaneDelegate: class {
func didPlaneUpdate()
}
class Plane {
static let shared = Plane()
weak var delegate: PlaneDelegate?
public init() { }
public func addToPlane() {
updatePlane()
}
public func updatePlane() {
delegate?.didPlaneUpdate()
}
}
// ViewController1.swift:
class ViewControllerPlane: UIViewController, PlaneDelegate {
var plane = Plane()
override func viewDidLoad() {
super.viewDidLoad()
plane.delegate = self
}
func didPlaneUpdate() {
print("test updated")
}
// ViewController2.swift:
var plane = Plane()
plane.addToPlane()
or
// ViewController2.swift:
Plane.shared.addToPlane()
It doesn't work.
why does typealias closure not transmit data and output nothing to the console? How to fix it?
class viewModel: NSObject {
var abc = ["123", "456", "789"]
typealias type = ([String]) -> Void
var send: type?
func createCharts(_ dataPoints: [String]) {
var dataEntry: [String] = []
for item in dataPoints {
dataEntry.append(item)
}
send?(dataEntry)
}
override init() {
super.init()
self.createCharts(abc)
}
}
class ViewController: UIViewController {
var viewModel: viewModel = viewModel()
func type() {
viewModel.send = { item in
print(item)
}
}
override func viewDidLoad() {
super.viewDidLoad()
print("hello")
type()
}
}
I have a project in which a similar design works, but I can not repeat it
The pattern is fine, but the timing is off.
You’re calling createCharts during the init of the view model. But the view controller is setting the send closure after the init of the view model is done.
Bottom line, you probably don’t want to call createCharts during the init of the view model.
Possible solution is to create custom initializer:
class viewModel: NSObject {
...
init(send: type?) {
self.send = send
self.createCharts(abc)
}
}
class ViewController: UIViewController {
var viewModel: viewModel = viewModel(send: { print($0) })
...
}
In our app, we have a service that helps us decide which Modal UIVIewController should we present next. Every ModalVIewController has common function such as dismiss() but also a specific function it implements. So that's what we tried:
The base protocol that is common to all VC's base functions.
protocol ModalScreenDelegate: AnyObject {
func modalScreenWantsToDissmiss(_ modalScreen: ModalScreen)
}
A base protocol that every UIViewController implements
protocol ModalScreen: UIViewController {
var delegate: ModalScreenDelegate? { get set }
}
Now we create a protocol with specific-implementation of ModalScreenDelegate base protocol like so:
protocol ShareToFacebookDelegate: ModalScreenDelegate {
func someCustomMethod()
}
And assign it to:
class ShareToFacebookViewController: UIViewController, ModalScreen {
weak var delegate: ModalScreenDelegate? // **WORKS**
weak var delegate: ShareToFacebookDelegate? // **DOESN'T WORKS**
}
If I'm trying to use ShareToFacebookDelegate to instead of ModalScreenDelegate the compiler throws an IDE error saying I have to change it back to ModalScreenDelegate.
Why wouldn't it work? It's ShareToFacebookDelegate conforms to ModalScreenDelegate.
Any help would be highly appreciated.
Thank you!
UPDATE Based on Alexandr Kolesnik:
Your method works. But when I try to "fetch" the correct VC within the service under one method like so:
func fetchModal<T: ModalScreen & UIViewController>() -> T? {
return AddInstagramViewController.create() as? T
}
And then have a coordinator that wants to get this vc:
guard let currentModalViewController vc = modalScreenSupplierService.fetchModal() else {
return
}
I'm getting:
Generic parameter 'T' could not be inferred
And I can't really say what T will be, all I know that it's going to conform to UIViewController & ModalScreen. Is it solvable?
If I understood you correctly you can use generic types to manage the problem. Look through the code below. Hope it helps
protocol ModalScreenDelegate: AnyObject {
typealias T = ModalScreenDelegate
func modalScreenWantsToDissmiss(_ modalScreen: T)
}
protocol ShareToFacebookDelegate: ModalScreenDelegate {
func someCustomMethod()
}
protocol ModalScreen: UIViewController {
associatedtype T
var delegate: T? { get set }
}
class ShareToFacebookViewController: UIViewController, ModalScreen {
typealias T = ShareToFacebookDelegate
weak var delegate: T?
override func viewDidLoad() {
super.viewDidLoad()
delegate?.someCustomMethod()
}
}
UPDATE:
class AddInstagramViewController: SuperVC {
typealias T = ShareToFacebookDelegate
private var instaDelegate: ShareToFacebookDelegate?
override var delegate: ModalScreenDelegate? {
set {
instaDelegate = newValue as? ShareToFacebookDelegate
}
get {
return instaDelegate
}
}
static func create() -> AddInstagramViewController {
return AddInstagramViewController()
}
}
class SuperVC: UIViewController, ModalScreen {
typealias T = ModalScreenDelegate
var delegate: T?
}
class Supplier {
func fetchModal<M: ModalScreen>() -> M? { return AddInstagramViewController.create() as? M }
}
class SupplierImpl {
let modalScreenSupplierService: Supplier? = nil
func goto() {
guard
let vc: SuperVC = modalScreenSupplierService?.fetchModal()
else {
return
}
}
}
This solution:
protocol ModalScreenDelegate: AnyObject {
func modalScreenWantsToDissmiss(_ modalScreen: ModalScreen)
}
protocol ModalScreen: UIViewController {
var delegate: (ModalScreenDelegate & ShareToFacebookDelegate)? { get set }
}
protocol ShareToFacebookDelegate: ModalScreenDelegate {
func someCustomMethod()
}
class ShareToFacebookViewController: UIViewController, ModalScreen {
weak var delegate: (ModalScreenDelegate & ShareToFacebookDelegate)?
}
or inheritance:
protocol ModalScreenDelegate: AnyObject {
func modalScreenWantsToDissmiss(_ modalScreen: ModalScreen)
}
protocol ModalScreen: ShareToFacebookDelegate where Self: UIViewController {
var delegate: ModalScreenDelegate? { get set }
}
protocol ShareToFacebookDelegate: ModalScreenDelegate {
func someCustomMethod()
}
class ShareToFacebookViewController: UIViewController, ModalScreen {
func someCustomMethod() {
}
func modalScreenWantsToDissmiss(_ modalScreen: ModalScreen) {
}
weak var delegate: ModalScreenDelegate? // **WORKS**
}
I am struggling with understanding how protocols work. I have 2 files and want to use protocol to pass data... Here's what I am doing:
In ViewController.swift
protocol workingProtocol { func myFunc(strValue: String)}
class ViewController: UIViewController {
var interactor = workingProtocol
#objc func doneBtn() {
interactor.myFunc(strValue: "str")
}
}
In Interactor.swift
class Interactor {
func myFunc(strValue: String) {
print(strValue)
}
}
The data is not printing from Interactor.swift
Unfortunately I can't see how you inject interaction class, also your code has some problem with syntax. Here is how it should look:
protocol WorkingProtocol: AnyObject {
func myFunc(strValue: String)
}
final class ViewController: UIViewController {
var interactor: WorkingProtocol
#objc func doneBtn() {
interactor.myFunc(strValue: "str")
}
}
final class Interactor: WorkingProtocol {
func myFunc(strValue: String) {
print(strValue)
}
}
And how to use:
let interactor: WorkingProtocol = Interactor()
let vc = ViewController(interactor: interactor)
vc.doneBtn()
Protocols defines a blueprint of methods, properties and other requirements that suite a piece of functionality.
This is an example about how it works based on your code
protocol ProtocolName {
func functionName(strValue: String)
}
class ViewController {
var interactor: ProtocolName? = nil
#objc
fileprivate func doneBtn() {
interactor?.functionName(strValue: "Passing data to interactor using protocols")
}
}
class Interactor: ProtocolName {
func functionName(strValue: String) {
print("Showing value\n", strValue)
}
}
let interactor = Interactor()
let viewController = ViewController()
viewController.interactor = interactor
viewController.doneBtn()
Another example:
protocol ProtocolName {
func functionName(strValue: String)
}
class ViewController1 {
let interactor = Interactor1()
/// Init or viewDidLoad() if you're using ViewController classes.
init() {
interactor.delegate = self
}
}
extension ViewController1: ProtocolName {
func functionName(strValue: String) {
print("Printing the value: \(strValue)")
}
}
class Interactor1 {
var delegate: ProtocolName?
func someAction() {
delegate?.functionName(strValue: "Executed action in interactor.")
}
}
let vc = ViewController1()
vc.interactor.someAction()
I have two classes that ideally would have code in their inits and deinits, e.g.:
class Tappable {
init() { Registry.register(tappable: self) }
deinit { Registry.deregister(tappable: self) }
}
class Resizable {
init() { Registry.register(resizable: self) }
deinit { Registry.deregister(resizable: self) }
}
Ideally I would inherit from both, e.g.:
class UIElement: Tappable, Resizable {}
But of course I can't in Swift. My current solution is to make one a protocol and put a note in to remind me to write init and deinit with calls to the Registry, e.g.:
//: Classes that implememt `Resizable` must call `Registry.register(resizable: self)` in all `init`s and have `deinit { Registry.deregister(resizable: self) }`.
protocol Resizable {}
class UIElement: Tappable, Resizable {
override init() {
super.init()
Registry.register(resizable: self)
}
deinit { Registry.deregister(resizable: self) }
}
Is there a better way?
You could create a composite class and store your Registry classes as variables, it could look something like this:
protocol Register {
init(_ target: UIElement)
func deregister(target: UIElement)
}
class Tappable: Register {
required init(_ target: UIElement) { Registry.register(tappable: target) }
func deregister(target: UIElement) { Registry.deregister(tappable: target) }
}
class Resizable: Register {
required init(_ target: UIElement) { Registry.register(resizable: target) }
func deregister(target: UIElement) { Registry.deregister(resizable: target) }
}
class UIElement {
var traits: [Register]!
override init() {
self.traits = [Tappable(self), Resizable(self)]
}
deinit {
self.traits.forEach { $0.deregister(self) }
}
}
This way, when deinit is called on the UIElement object, all of the traits of UIElement will be deregistered.
You can test this out in a Swift Playground by adding the following at the bottom. This will create the UIElement class, have it register for the traits, and then deallocate it and have it deregister!
var test: UIElement! = UIElement()
test = nil
You could have each protocol define a required initializer:
protocol Tappable {
init(r:Registry)
}
Then any class that inherits the protocol will have to implement that initializer, which you'd hope would remind you what needed to happen there.
That doesn't work particularly-well for UIView subclasses, which need to implement UIView's designated initializers, also.
Here's another solution, which replaces your two superclasses with a single superclass, and an OptionSet. Obviously, this gets a bit unwieldy if you need to do a lot of case-specific initialization and de-initialization, but it works okay for the example given.
class Registry {
class func register(resizeable: Any) {
}
class func register(tappable: Any) {
}
}
struct ViewTraits: OptionSet {
let rawValue: Int
init(rawValue: Int) { self.rawValue = rawValue }
static let Tappable = ViewTraits(rawValue: 1)
static let Resizeable = ViewTraits(rawValue: 2)
}
protocol Traits {
var traits:ViewTraits { get }
}
class TraitedView: NSView, Traits {
var traits:ViewTraits {
get {
fatalError("Must implement a getter for Traits")
}
}
private func register() {
if (traits.contains(.Tappable)) {
Registry.register(tappable: self)
}
if (traits.contains(.Resizeable)) {
Registry.register(resizeable: self)
}
}
override init(frame:NSRect) {
super.init(frame: frame)
register()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
register()
}
}
class MyView: TraitedView {
override var traits: ViewTraits {
get {
return [ViewTraits.Resizeable, ViewTraits.Tappable]
}
}
}
I have pinched everyones ideas in the playground below. Thanks.
var sequence = ""
enum Registry {
static func register(tappable _: Tappable) { sequence += "reg. tap.\n" }
static func deregister(tappable _: Tappable) { sequence += "dereg. tap.\n" }
static func register(resizable _: Resizable) { sequence += "reg. res.\n" }
static func deregister(resizable _: Resizable) { sequence += "dereg. res.\n" }
}
class Registrar {
init() {
if let tappable = self as? Tappable {
Registry.register(tappable: tappable)
}
if let resizable = self as? Resizable {
Registry.register(resizable: resizable)
}
}
deinit {
if let tappable = self as? Tappable {
Registry.deregister(tappable: tappable)
}
if let resizable = self as? Resizable {
Registry.deregister(resizable: resizable)
}
}
}
protocol Tappable {
func tap()
}
extension Tappable {
func tap() { sequence += "tap\n" }
}
protocol Resizable {
func resize()
}
extension Resizable {
func resize() { sequence += "resize\n" }
}
class UIElement: Registrar, Tappable, Resizable {
}
var uie: UIElement! = UIElement()
uie.tap()
uie.resize()
uie = nil
sequence // "reg. tap.\nreg. res.\ntap\nresize\ndereg. tap.\ndereg. res.\n"