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.
Related
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'm Korean android developer and new at Swift.
I am migrating my android app to ios, but meet a problem with interface and listener. I don't know how to implement listener to communicate between a custom view and a view controller.
I have a custom view(i.e. MyView) that has two buttons and each has own function.
In Android(with Java), I usually make an listener Interface in MyView and assign two functions inside like void func1( String val1 ) and func2...
public class MyView extends RelativeLayout {
private Button b1, b2;
private String val1, val2;
public interface OnButtonListener {
void onFunc1(String val1);
void onFunc2(String val2);
}
private OnButtonListener onButtonListener;
public void setOnButtonListener( OnButtonListener onButtonListener ) {
this.onButtonListener = onButtonListener;
}
public MyView( Context context, AttributeSet attrs ) {
super( context, attrs );
b1.setOnClickListener( view -> {
if (onButtonListener != null){
onButtonListener.onFunc1( val1 );
}
} );
b2.setOnClickListener( view -> {
if (onButtonListener != null){
onButtonListener.onFunc1( val2 );
}
} );
}
}
public class MyActivity extends Activity {
MyView myView1, myView2;
#Override
protected void onCreate( #Nullable Bundle savedInstanceState ) {
super.onCreate( savedInstanceState );
myView1.setOnButtonListener( new MyView.OnButtonListener() {
#Override
public void onFunc1( String val1 ) {
Log.d(TAG, val1);
}
#Override
public void onFunc2( String val2 ) {
Log.d(TAG, val2);
}
} );
myView2.setOnButtonListener( new MyView.OnButtonListener() {
#Override
public void onFunc1( String val1 ) {
// do something1
}
#Override
public void onFunc2( String val2 ) {
// do something2
}
} );
}
}
This code works perfectly as I wanted. So I've tried to apply same pattern into Swift, but I couldn't find any way to do.
below is for swift 4
import Foundation
import UIKit
import SnapKit
protocol OnButtonListener {
func onButton1( _ val1: String )
func onButton2( _ val2: String )
}
class MyView: UIView {
var onButtonListener: OnButtonListener?
var val1 = "abc"
var val2 = "123"
override init( frame: CGRect ) {
super.init( frame: frame )
let b1 = UIButton()
let b2 = UIButton() // i'm using snapkit
b1.addTarget(self, action: #selector(onB1), for: .touchUpInside)
b2.addTarget(self, action: #selector(onB2), for: .touchUpInside)
}
#objc func onB1() {
if onButtonListener != nil {
onButtonListener!.onButton1(val1 )
}
}
#objc func onB2() {
if onButtonListener != nil {
onButtonListener!.onButton2(val2 )
}
}
}
class MyVC : UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let myView1 = MyView()
let myView2 = MyView()
myView1.onButtonListener = {
// ???
}
myView2.onButtonListener = {
// ???
}
}
}
I don't know how to implement listener in ViewContorller. I've tried same way as Kotlin but I didn't work too. Thank you for reading.
You have to set delegate in your viewcontroller and implement protocol methods in your viewcontroller
class MyVC : UIViewController, OnButtonListener {
override func viewDidLoad() {
super.viewDidLoad()
let myView1 = MyView()
myView1. onButtonListener = self
let myView2 = MyView()
myView2. onButtonListener = self
}
func onButton1( _ val1: String ) {
print(val1)
}
func onButton2( _ val2: String ) {
print(val2)
}
}
**Method 2: ** You can use block as well
class MyView: UIView {
var buttonAction : ((_ value : String) -> Void)? = nil
//.... your code
#objc func onB1() {
if let action = buttonAction {
action("abc")
}
}
#objc func onB2() {
if let action = buttonAction {
action("xyz")
}
}
}
In you ViewController
override func viewDidLoad() {
super.viewDidLoad()
let myView1 = MyView()
myView1.buttonAction = { value in
print(value)
}
}
Update your view controller code as follows:
First confirm your OnButtonListener protocol to UIViewController, and implement protocol method in your view controller.
class MyVC : UIViewController, OnButtonListener {
override func viewDidLoad() {
super.viewDidLoad()
let myView1 = MyView()
// Confirm protocol implementation in current view controller
myView1.onButtonListener = self
let myView2 = MyView()
// Confirm protocol implementation in current view controller
myView2.onButtonListener = self
}
func onButton1( _ val1: String) {
// your code
}
func onButton2( _ val2: String) {
// your code
}
}
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 {
}
How do you get a reference property to trigger a property observer?
In order to demonstrate my problem I wrote a simple MVC program with one button and one label. The button increments a counter in the model and displays the value of the counter in the label in the view controller.
The problem is that the counter increment (in the model) does not trigger the didSet observer ( in the view controller)
Here is the model file:
import Foundation
class MvcModel {
var counter: Int
var message: String
init(counter: Int, message: String) {
self.counter = counter
self.message = message
}
}
// create instance
var model = MvcModel(counter: 0, message: "" )
// counting
func incrementCounter() {
model.counter += 1
model.message = "Counter Value: \(model.counter)"
//print(model.message)
}
Here is the view controller file:
import Cocoa
class ViewController: NSViewController {
let model1 = model
var messageFromModel = model.message {
didSet {
updateDisplayCounterLabel()
}
}
// update Label
func updateDisplayCounterLabel() {
DisplayCounterLabel.stringValue = model1.message
}
// Label
#IBOutlet weak var DisplayCounterLabel: NSTextField! {
didSet {
DisplayCounterLabel.stringValue = "counter not started"
}
}
// Button
#IBAction func IncrementButton(_ sender: NSButton) {
incrementCounter()
print("IBAction: \(model1.message)")
}
}
I guess the problem is linked to reference property (as I have been able to make this program work with a model based on a struct).
I would appreciate if someone could tell me how to deal with property observers and reference property and make this kind of MVC work as I plan to use it in real programs.
You could create a delegate for MvcModel
protocol MvcModelDelegate {
func didUpdateModel(counter:Int)
}
next you add a delegate property to MvcModel
class MvcModel {
var counter: Int {
didSet {
delegate?.didUpdateModel(counter: counter)
}
}
var message: String
var delegate: MvcModelDelegate?
init(counter: Int, message: String) {
self.counter = counter
self.message = message
}
}
then you make the ViewController class conform to MvcModelDelegate and finally you set model.delegate = self into the viewDidLoad
class Controller: UIViewController, MvcModelDelegate {
let model = MvcModel(counter: 0, message: "hello")
override func viewDidLoad() {
super.viewDidLoad()
self.model.delegate = self
}
func didUpdateModel(counter: Int) {
print("new value for counter \(counter)")
}
}
In case someone is interested here is the code as suggested by Noam.
Model File:
import Foundation
protocol MvcModelDelegate {
func didUpDateModel(message: String)
}
class MvcModel {
var counter: Int
var message: String {
didSet {
delegate?.didUpDateModel(message: message)
}
}
var delegate: MvcModelDelegate?
init(counter: Int, message: String) {
self.counter = counter
self.message = message
}
}
// create instance
var model = MvcModel(counter: 0, message: "" )
// counting
func incrementCounter() {
model.counter += 1
model.message = "Counter Value: \(model.counter)"
}
}
View Controller File
import Cocoa
class ViewController: NSViewController, ModelDelegate {
// communication link to the model
var model1 = model
var messageFromModel = messageToLabel
override func viewDidLoad() {
super.viewDidLoad()
self.model1.delegate = self
}
// update display
func didUpdateModel(message: String) {
//self.Label1.stringValue = model1.message
self.Label1.stringValue = messageFromModel
}
// Label
#IBOutlet weak var Label1: NSTextField! {
didSet {
Label1.stringValue = " counter not started"
}
}
// Button
#IBAction func testButton(_ sender: NSButton) {
incrementCounter()
}
}