Assigning Functions from other Classes in Swift - ios

Is it possible to access and run a specific method/function from another class that can change dynamically as the app is run?
I’ll try to simplify the problem as much as possible.
SelectionPage.swift
Choose which class needs to be selected and accessed using an UIPickerView - 10 possible selections (Class1, Class2, Class3,…, Class10).
Class1.swift, Class2.swift, … Class10.swift
Each of the 10 classes has a single method that has exactly the same name but is programmed differently:
func runOnUpdate() { }
GameSceneViewController.swift
When a selection is made on the SelectionPage, the app segues to a GameSceneViewController where the specific selected function is run every time the update function is run:
override func update(currentTime: CFTimeInterval)
{
// run runOnUpdate() function here from selected class
}
Inside the update function, I would like to execute the runOnUpdate( ) function depending on which class was selected on the SelectionPage. Is this possible? Ideally I'd like to be able to assign the specific class/method in the:
override func didMoveToView(view: SKView)
so that I can access in other functions as well.
I’ve looked into lazy instantiation, creating delegates for each of the classes, #objc(Class1), arrays of [AnyClass], typealias, global variables in structs, singletons etc. but I’m unable to figure out how to make this work.
It seems like a fairly common problem so any help would be greatly appreciated! Thank you in advance!

You were correct in trying delegates as this is a case where you should make a protocol and a delegate. The protocol requires the function. From there you set the delegate property to an instance of a class that conforms to that protocol and then you call delegate?.someFunction() to call the function on the given object.
class ViewController: UIViewController {
var delegate: Updatable?
override func viewDidLoad() {
super.viewDidLoad()
let foo = Foo()
delegate = foo
delegate?.runOnUpdate() // prints do something
}
}
protocol Updatable {
func runOnUpdate()
}
class Foo: NSObject, Updatable {
func runOnUpdate() {
println("do something")
}
}

Related

How to handle not used functions from delegates in view controllers

I have a very general view that is created and used by multiple view controllers with 2 buttons, one of them sometimes is hidden depending on the needs.
This view delegates the tap of the two buttons.
protocol TheViewsDelegate: class {
func button1Tapped()
func button2Tapped()
}
Let's put that ViewControllerA creates this view and needs both buttons, this view controller will have to implement both delegate functions and do something inside it.
Now let's say that ViewControllerB creates the same view but just needs one of the buttons. This view controller will have to still implement button2Tapped() even though it will never be called and used.
Is there a way to handle this nicely? I imagine there's a nice solution where I don't need to implement this button2Tapped() if I don't need it.
I thought about making it optional by giving a default implementation but I don't like this solution, I like (and I think it's a good practice) the compiler giving me an error when a method it's not implement. Someone can jump into the project and not realising that he/she hasn't implement button2Tapped when needs to be implemented.
Note: This is a very simple example just to illustrate my question, but the question is more broad as in what to do when a function in a delegate is defined by controller that don't need to implement it.
I believe you want to use:
optional func
There are a couple of ways of declaring a protocol method as optional, one is using optional func which requires using #objc syntax, which a lot of programmers apparently don't like, and the other requires declaring an empty body in the extension of a protocol (which makes it optional by default).
protocol TheViewsDelegate: AnyObject {
func button1Tapped()
}
extension TheViewsDelegate {
func button2Tapped() {}
}
class SomeViewController: UIViewController, TheViewsDelegate {
func button1Tapped() {
// implement
}
}
By giving the protocol an empty body inside an extension of the protocol, that method is optional and does not need to be implemented by conforming objects.
For comparison, the alternative:
#objc protocol TheViewsDelegate: AnyObject {
func button1Tapped()
#objc optional func button2Tapped()
}
class SomeViewController: UIViewController, TheViewsDelegate {
func button1Tapped() {
// implement
}
}

How to call the same function on multiple classes?

I have an UIViewController with 4 UIButtons. A user can tap any of those UIButtons and an UIView pops up. I want to add an didAppear() and didDisappear() function on the classes which are holding the UIViews depending on the users action. How can I call didDisappear() without the use of an enum, for example:
func didDisappear(view: EnumViews){
switch view{
case view0: myClassWithView0.didDisappear()
case view1: myClassWithView1.didDisappear()
case view2: myClassWithView2.didDisappear()
case view3: myClassWithView3.didDisappear()
}
}
Now I get 4 times duplicate data. I know that function exists for my class with a UIView, but how to call it? I made a protocol:
protocol ViewProtocol{
func didAppear()
func didDisappear()
}
I made the classes which are holding the UIView's conform to that protocol. However I do not know how to use it, when I create the class I get the error:
'myClassWithUIView' cannot be constructed because it has no accessible
initializers
The classes are all in an array and I can identify which UIView needs to pop up from the sender.tag. Ideally, I want to have something like this:
#IBAction func bringNewUIView(_ sender: UIButton) {
let newView = myArrayOfClassesWithUIView[sender.tag]
newView.didAppear()
}
You've got many things going on here. I'll start with the easy one.
'myClassWithUIView' cannot be constructed because it has no accessible initializers
This just means you don't have an initializer for your class. So inside your myClassWithUIView implementation you need to have init. I can't really help you with building the init because I don't know how that class is structured, but I will assume this is something you know how to do anyway.
Your #IBAction seems fine. Once you have an array of your classes that seems like it should work. Edit your post if that is not the case.
Finally, for your didDisappear question, you can do something like this:
func didDisappear(view: EnumViews) {
//Check to see if this view conforms to your ViewProtocol (that's not a good name, btw)
if let myClass = view as? ViewProtocol {
//Since it does conform to ViewProtocol you can call didDisappear on it
myClass.didDisappear()
}
}
Alternatively, if you already know that the didDisappear function is always passing in a view that conforms to ViewProtocol why not just change the argument and make that easier?
func didDisappear(view: ViewProtocol) {
view.didDisappear()
}

Override function

I am taking a iOS course online provided by a famous university. I don't understand why the following code use override and it is legal.
According to the official definition, we use override to override superclass' methods. Where is the subclass and superclass in the following code?
What's been override and by what?
public override var description: String {
return "\(url.absoluteString) (aspect ratio = \(aspectRatio))"
}
Here is an example:
Your original class:
class Person {
func walk() {
//do something
}
}
Your subclass:
class Runner: Person {
override func walk() {
//do something that is different from Person's walk
}
}
In the Runner class, there is an override with the function walk. That is because it is a subclass of Person, and it can override Person's walk function. So If you instantiate a Runner:
var usainBolt = Runner()
And you call the walk function:
usainBolt.walk()
Then that will call the overriden function that you wrote in the Runner class. If you don't override it, it will call the walk function that you wrote in Person.
According to the official definition, we use override to override superclass' methods.
That's correct. The superclass in your example is the class that encloses the override of description property. This could be NSObject, some other class derived from it (directly or indirectly), or some class unrelated to NSObject that has var description: String property.
description is a property that Swift classes commonly have as a way to present themselves as a string, because description provides conformance to CustomStringConvertible protocol. This is similar to toString() method of Java, and to str() method of Python.
What's been override and by what?
The implementation of the property is what's being overridden. The class that has the implementation does the overriding.

Check if optional protocol method is implemented in Swift?

I have a swift protocol:
#objc protocol SomeDelegate {
optional func myFunction()
}
I one of my classes I did:
weak var delegate: SomeDelegate?
Now I want to check if the delegate has myFunction implemented.
In objective-c I can do:
if ([delegate respondsToSelector:#selector(myFunction)]) {
...
}
But this is not available in Swift.
Edit: This is different from: What is the swift equivalent of respondsToSelector? I focus on class protocols not on classes.
How do I check if my delegate has an optional method implemented?
Per The Swift Programming Language:
You check for an implementation of an optional requirement by writing
a question mark after the name of the requirement when it is called,
such as someOptionalMethod?(someArgument). Optional property
requirements, and optional method requirements that return a value,
will always return an optional value of the appropriate type when they
are accessed or called, to reflect the fact that the optional
requirement may not have been implemented.
So the intention is not that you check whether the method is implemented, it's that you attempt to call it regardless and get an optional back.
You can do
if delegate?.myFunction != nil {
}
I've found it successful to add an extension to the protocol that defines basic default implementation and then any class implementing the protocol need only override the functions of interest.
public protocol PresenterDelegate : class {
func presenterDidRefreshCompleteLayout(presenter: Presenter)
func presenterShouldDoSomething(presenter: Presenter) -> Bool
}
then extend
extension PresenterDelegate {
public func presenterDidRefreshCompleteLayout(presenter: Presenter) {}
public func presenterShouldDoSomething(presenter: Presenter) -> Bool {
return true
}
}
Now any class needing to conform to the PresenterDelegate protocol has all functions already implemented, so it's now optional to override it's functionality.
I normally implement it like this:
self.delegate?.myFunction?()
if the delegate methods returns a value:
var result = defaultValue
if let delegateResult = self.delegate?.myFunction?() else {
result = delegateResult
}
//do something with result
Declaration
#objc public protocol nameOfDelegate: class {
#objc optional func delegateMethod(_ varA: int, didSelect item: Item)
}
Implimetation
if let delegate = nameOfDelegate {
delegate.delegateMethod?(1, didDeselect: node)
}
I know this question is 5 years old, but I would like to share what I found. My solution works as of 2021, XCode 11+, Swift 5.
Say I wanted to figure out whether the function sign follows the GIDSignInDelegate protocol and also know what all the optional functions for GIDSignInDelegate are.
I have to look at the source code of the GIDSignIn module, and this is how.
Click on jump to definition on the main module that is imported. It will lead to a file like this:
Copy the entire line, import GoogleSignIn.GIDSignIn and paste it in the ViewController or whatever .swift file (doesn't really matter).
Within the swift file, right click on the GIDSignIn part of the import line GoogleSignIn.GIDSignIn and jump to definition. This will lead you to the actual module with all the available functions (the functions not marked optional may be stubs, which are required functions in the delegate protocol):
From this file, I can see that there is a sign function that is a stub of GIDSignInDelegate and an optional sign function that is implemented as a method overload.
I used this for GIDSignInDelegate, but you can use the same method to figure out whether any function follows any delegate protocol.

Override var conforming to a protocol with a var conforming to a child of the overridden var protocol

This is my inheritance structure
Protocols
protocol BaseProtocol {
}
protocol ChildProtocol: BaseProtocol {
}
Classes
class BaseClass: NSObject {
var myVar: BaseProtocol!
}
class ChildClass: BaseClass {
override var myVar: ChildProtocol!
}
I'm receiving a compiler error:
Property 'myVar' with type 'ChildProtocol!' cannot override a property with type 'BaseProtocol!'
What is the best approach to achieve this?
UPDATE
I updated the question trying to implement the solution with generics but it does not work :( This is my code (now the real one, without examples)
Protocols
protocol TPLPileInteractorOutput {
}
protocol TPLAddInteractorOutput: TPLPileInteractorOutput {
func errorReceived(error: String)
}
Classes
class TPLPileInteractor<T: TPLPileInteractorOutput>: NSObject, TPLPileInteractorInput {
var output: T!
}
And my children
class TPLAddInteractor<T: TPLAddInteractorOutput>: TPLPileInteractor<TPLPileInteractorOutput>, TPLAddInteractorInput {
}
Well, inside my TPLAddInteractor I can't access self.output, it throws a compiler error, for example
'TPLPileInteractorOutput' does not have a member named 'errorReceived'
Besides that, when I create the instance of TPLAddInteractor
let addInteractor: TPLAddInteractor<TPLAddInteractorOutput> = TPLAddInteractor()
I receive this other error
Generic parameter 'T' cannot be bound to non-#objc protocol type 'TPLAddInteractorOutput'
Any thoughts?
#tskulbru is correct: it can't be done, and this has nothing to do with your protocols. Consider the example below, which also fails…this time with Cannot override with a stored property 'myVar':
class Foo {
}
class Goo: Foo {
}
class BaseClass: NSObject {
var myVar: Foo!
}
class ChildClass: BaseClass {
override var myVar: Foo!
}
To understand why, let's reexamine the docs:
Overriding Properties
You can override an inherited instance or class property to provide
your own custom getter and setter for that property, or to add
property observers to enable the overriding property to observe when
the underlying property value changes.
The implication is that if you are going to override a property, you must write your own getter/setter, or else you must add property observers. Simply replacing one variable type with another is not allowed.
Now for some rampant speculation: why is this the case? Well, consider on the one hand that Swift is intended to be optimized for speed. Having to do runtime type checks in order to determine whether your var is in fact a Foo or a Bar slows things down. Then consider that the language designers likely have a preference for composition over inheritance. If both of these are true, it's not surprising that you cannot override a property's type.
All that said, if you needed to get an equivalent behavior, #tskulbru's solution looks quite elegant, assuming you can get it to compile. :)
I don't think you can do that with protocols
The way i would solve the problem you are having is with the use of generics. This means that you essentially have the classes like this (Updated to a working example).
Protocols
protocol BaseProtocol {
func didSomething()
}
protocol ChildProtocol: BaseProtocol {
func didSomethingElse()
}
Classes
class BaseClass<T: BaseProtocol> {
var myProtocol: T?
func doCallBack() {
myProtocol?.didSomething()
}
}
class ChildClass<T: ChildProtocol> : BaseClass<T> {
override func doCallBack() {
super.doCallBack()
myProtocol?.didSomethingElse()
}
}
Implementation/Example use
class DoesSomethingClass : ChildProtocol {
func doSomething() {
var s = ChildClass<DoesSomethingClass>()
s.myProtocol = self
s.doCallBack()
}
func didSomething() {
println("doSomething()")
}
func didSomethingElse() {
println("doSomethingElse()")
}
}
let foo = DoesSomethingClass()
foo.doSomething()
Remember, you need a class which actually implements the protocol, and its THAT class you actually define as the generic type to the BaseClass/ChildClass. Since the code expects the type to be a type which conforms to the protocol.
There are two ways you can go with your code, depending what you want to achieve with your code (you didn't tell us).
The simple case: you just want to be able to assign an object that confirms to ChildProtocol to myVar.
Solution: don't override myVar. Just use it in ChildClass. You can do this by design of the language Swift. It is one of the basics of object oriented languages.
Second case: you not only want to enable assigning instances of ChildProtocol, you also want to disable to be able to assign instances of BaseProtocol.
If you want to do this, use the Generics solution, provided here in the answers section.
If you are unsure, the simple case is correct for you.
Gerd

Resources