Duplicate codes in Controller - ios

I am currently working on one project. It may potentially have duplicated codes in multiple controllers like below.
Controller A
class A: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
// about 50~70 lines of codes
#IBAction func scanButtonTapped {
// used self (as AVCaptureMetadataOutputObjectsDelegate)
// used view
// called presentViewController(...), which is a func in UIViewController
}
}
Controller B
class B: UIViewController, AVCaptureMetadataOutputObjectsDelegate {
#IBAction func scanButtonTapped {
// will need same logic as in Controller A
}
}
My current solution is have another class C, and move the duplicated codes into it. However, if I do so, controller can cast to AVCaptureMetadataOutputObjectsDelegate, but not to UIViewController.
class C {
func btnTapped (view: UIView, controller: AnyClass) {
// logic is here
// controller can cast to AVCaptureMetadataOutputObjectsDelegate
// but controller cannot cast to UIViewController
}
}
so A and B will have
class A {
#IBAction func scanButtonTapped {
let c = C()
c.btnTapped(view, self)
}
}
My question is if it is possible to cast controller into UIViewController. OR is there another way to refactor the codes properly?

What about extend AVCaptureMetadataOutputObjectsDelegate protocol and create default implementation by protocol extension (POP approach)?
protocol ScanButtonClickable: AVCaptureMetadataOutputObjectsDelegate {
func btnTapped() // this line is optional
}
extension Clickable where Self: UIViewController {
func btnTapped() {
// logic is here
}
}
class A: UIViewController, ButtonClickable {
...
}
class B: UIViewController, ButtonClickable {
...
}

Try this:
//declare your default method to be used across classes
protocol MyProtocol {
func myFunc()
}
//provide the implementation of your default behavior here in myFunc()
extension MyProtocol {
func myFunc() {
print("Default behavior")
}
}
class A: MyProtocol {
}
class B: MyProtocol {
}
let a = A()
a.myFunc()
let b = B()
b.myFunc()
//prints
Default behavior
Default behavior

Related

How to notify a change from a class to multiple ViewControllers?

I'm writing because I'd like to know what's the best method to notify a change from a class to multiple ViewControllers. At the moment I'm using delegate method but I'm sure It's not the best one for this purpose. I created a class where I receive data and after a bit of processing I need to send the processed message to some ViewControllers (any one shows a piece of that message.). At the moment I have a singleton for the class and I assign its delegate to a different ViewController when I load it through a menu. What's your suggestion to do this job?
Here is an example of my actual code:
import Foundation
protocol MyClassDelegate {
func receivedData(_ sender: MyClass)
}
class MyClass: NSObject {
// create the var for delegate
var delegate: MyClassDelegate?
// save the single instance
static private var instance: MyClass {
return sharedInstance
}
private let sharedInstance = MyClass()
static func getInstance() -> MyClass {
return instance
}
func processData() {
// at the end of the process
delegate?.receivedData(self)
}
}
class Menu: UIViewController {
private var containerView: UIView!
private let myClass = Myclass.getInstance()
private var vcOne = VcOne()
private var vcTwo = VcTwo()
override func viewDidLoad() {
super.viewDidLoad()
containerView = UIVIew()
// set containerView position and dimensions
}
func selectViewController(previous: UIViewController, next: UIViewController) {
// remove actual loaded ViewController
previous.willMove(toParent: nil)
previous.view.removeFromSuperView()
previous.removeFromParent()
// assign the delegate
myClass.delegate = next
// add the new ViewController
self.addChild(next)
slef.addSubView(next.view)
next.didMove(toParent: self)
}
}
class VcOne: UIViewController, MyClassDelegate {
func receivedData(_ sender: MyClass) {
// data received
}
}
class VcTwo: UIViewController, MyClassDelegate {
func receivedData(_ sender: MyClass) {
// data received
}
}
You can use NotificationCenter https://developer.apple.com/documentation/foundation/notificationcenter to broadcast a message. or use KVO. Generally I consider notification center much easier.
simple example:
https://www.hackingwithswift.com/example-code/system/how-to-post-messages-using-notificationcenter

Unable to call another class method using delegate in swift

I'm trying to call method of Class B from class A on the button tap event. But it does not work and below is my code.
// Viewcontroller
class ViewController: UIViewController {
#IBAction func btnClicked(_ sender: Any) {
var objA = A()
objA.delegate?.TestA()
}
}
// ClassA.swift
protocol TestA {
func TestA()
}
class A {
var delegate: TestA?
}
// ClassB.swift
class B : TestA {
func TestA() {
print(" Function A from b")
}
}
When tapping a button, function TestA() does not invoke.
I even tried the code below, but it also didn't work:
var objB = B()
var objA = A()
objA.delegate = objB
Because you instantiate instance of Class A using
var objA = A()
Clearly you haven't initialised delegate property in A because its optional its default value is nil
Now when you call
objA.delegate?.TestA()
delegate is nil hence function TestA will not be called
Suggestion
Always use camelCasing for declaring names of functions. So TestA() is incorrect rather use testA()
EDIT 1:
Tested this
#IBAction func btnClicked(_ sender: Any) {
let objA = A()
let objB = B()
objA.delegate = objB
objA.delegate?.TestA()
}
This is working fine what is the issue?
The objA.delegate is never assigned to an object, so it has an initial value of nil. The ? operator avoids calling a function on a nil object.
The answer by Sandeep Bhandari is right.
Some information for better understanding of Protocol and Delegates.
TestA is a protocol and an optional var delegate is defined in class A. This setup is right. The idea behind this setup is that any user of class A, in this case class B which conforms to protocol TestA gets a callback from class A. You need to call the delegate.testA() function from within class A.
The current implementation of ViewController is not at all benefiting from defining Protocol and Delegates.
To achieve proper usage, the class A cab be modified as follows:
protocol TestA {
func testA()
}
class A {
var delegate: TestA?
func process() {
// Do something and call delegate function to report it.
delegate?.testA()
}
}
And modify ViewController as follows (copied class B for completeness):
class ViewController: UIViewController {
#IBAction func btnClicked(_ sender: Any) {
var objA = A()
var objB = B()
objA.delegate = objB
objA.process()
}
}
// ClassB.swift
class B : TestA {
func TestA() {
print(" Function A from b")
}
}
Now function implemented in class B to conform to protocol TestA will be called when process() function on objA of class A is called.
This is better use of Protocol and Delegate. Hope this helps.

method in protocol extension gets called instead of method implementation in View Controller

so I have a viewController which holds a custom view,
and that viewController class conforms to ViewProtocol
I expect when someAction method triggered in someCustomizedView
it will print " method in otherCustomizedClass called "
but it prints (" method in extension Called") instead.
The theNotOptionalMethod works just fine but not the optional method.
Is there anything that I misunderstand of protocol extension ?
Please help, been struggling for hours, thanks
protocol ViewDelegate: class {
func theNOTOptionalMethod()
}
extension ViewDelegate {
func theOptionalMethod(){
print (" method in extension Called")
}
}
class someCustomizedView: UIView {
weak var deleage: ViewDelegate?
#IBAction func someAction(sender: UIButton) {
deleage?.theOptionalMethod()
}
}
class someCustomizedVC: UIViewController, ViewDelegate {
lazy var someView: someCustomizedView = {
var v = someCustomizedView()
v.deleage = self
return v
}()
//...... someView added to controller
func theNOTOptionalMethod() {
// do nothing
}
func theOptionalMethod() {
print (" method in otherCustomizedClass called ")
}
}
That is how methods in extensions work. They hide the implementations in a class.
To create a protocol with optional methods, you need to put the optional method in the protocol definition:
protocol ViewDelegate: class {
func theNOTOptionalMethod()
func theOptionalMethod()
}
Alternatively, you can use #objc and optional modifiers:
#objc protocol MyDelegate : class{
func notOptionalMethod()
#objc optional func optionalMethod()
}
When you call optionalMethod, you need to unwrap the optional:
delegate.optionalMethod?()

Swift protocol defines an init that doesn't work for UIViewController

Here is a simple protocol:
protocol StringsInitiable {
init(strings: [String])
}
Trying to use the initializer in an extension works when constraint to NSObject...
extension StringsInitiable where Self: NSObject {
func test() {
let _ = Self(strings: [])
}
}
...but not when constraint to UIViewController. It then complains that the initializer should be labeled 'coder', referring to the mandatory initializer from NSCoding.
extension StringsInitiable where Self: UIViewController {
func test() {
let _ = Self(strings: []) // error label 'strings:' expected 'coder:'
}
}
Is there a way to use the initializer declared in the protocol even when being a UIViewController subclass?
EDIT
It seems to be working when constraining the extension to a base class (NSObject or any Swift class that doesn't inherit from anything) but not when constraining the extension to a child class.
I'm not entirely convinced, but this smells like a bug. Have a look at this example which doesn't compile:
protocol P {
init(n: Int)
}
class A {}
class B : A {}
extension P where Self : B {
func f() -> Self {
return Self(n: 3) // Error
}
}
But this compiles:
extension P where Self : A {
func f() -> Self {
return Self(n: 3)
}
}
Probably you don't want a protocol for that anyways, since you even named it StringsViewController. You should probably subclass UIViewController:
class StringsViewController : UIViewController {
convenience init(strings: [String]) {
self.init()
}
}
extension StringsViewController {
func test() {
let _ = StringsViewController(strings: [])
}
}
Or if you really want a protocol you can do something like this:
protocol HasView {
var view : UIView! { get }
}
protocol StringsInitable {
init(strings: [String])
}
extension UIViewController : HasView {}
extension HasView where Self : StringsInitable {
func test() {
let n = Self(strings: [])
print(n.view)
}
}
UIViewController doesn't have such an initialiser, because you haven't implemented the StringsViewController protocol. You would not be able to implement this protocol for UIViewController, because you cannot declare a designed initialiser into an extension. On the other hand you need a designated initialiser in order to conform to a init requirement of a protocol.

how to inject a delegate in swift using typhoon?

Using typhoon I'm trying to inject the type of "worker" in my view controller. my "Worker" requires a delegate so that when the work is done, it calls this method. I need to set my view controller to be the delegate of the worker class that was injected. in other words a circular dependency.
Updated question with source:
//my typhoon assembly class
import Typhoon
class Assembly : TyphoonAssembly {
public dynamic func viewController() -> AnyObject {
return TyphoonDefinition.withClass(ViewController.self) {
(definition) in
definition.injectProperty("worker", with: self.foo())
definition.scope = TyphoonScope.Singleton
}
}
public dynamic func foo() -> AnyObject {
return TyphoonDefinition.withClass(Foo.self) {
(definition) in
definition.injectProperty("delegate", with: self.viewController())
}
}
}
Foo is where the work done, it implements WorkHandler protocol and has a delegate of type SomeProtocol to call when work has finished:
import Foundation
#objc
protocol SomeProtocol: class {
optional func hasFinishedWork(value: Bool)
}
protocol WorkHandler : class {
func doSomething()
}
class Foo: WorkHandler{
//how will this get set?
var delegate:SomeProtocol?
func doSomething(){
print("doing the work")
delegate?.hasFinishedWork!(true)
}
}
And my ViewController conforms to SomeProtocol like so:
import UIKit
class ViewController: UIViewController, SomeProtocol{
var worker:WorkHandler?
override func viewDidLoad() {
super.viewDidLoad()
worker?.doSomething();
}
#objc func hasFinishedWork(value: Bool){
print("The work was all done")
}
}
above code gives the following error when it runs:
2016-02-29 20:25:43.250 TestApp[30604:5316415] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Subclass of NSProxy or NSObject is required.'
Is anyone able to help with this?
Turns out i had to make my protocol to inherit from NSObject:
#objc
protocol SomeProtocol: class {
optional func hasFinishedWork(value: Bool)
}
#objc
protocol WorkHandler : class {
func doSomething()
}
class Foo: NSObject, WorkHandler{
//how will this get set?
var delegate:SomeProtocol?
#objc func doSomething(){
print("doing the work")
delegate?.hasFinishedWork!(true)
}
}
Now it works as expected.

Resources