I have a login view controller. Once the login is done, it dismisses itself and calls a function in another view controller. And in that function there are four different functions that do different actions. The problem is the protocol calls 4 functions at the same time. So is there a way to call a specific function inside the other function? like;
func mainfunction(){
funcOne()
funcThree()
funcTwo()
funcOne()
}
So instead of calling all, I want to call one. And mainFunction is a part of protocol.
Thanks
Just extend your protocol. Instead of having one mainFunction, the protocol would have all four.
#protocol YourProtocol {
func funcOne()
func funcTwo()
func funcThree()
func funcFour()
}
If you add the separate functions in the protocol declaration, you will be able to call them individually if they exist.
The protocol serves as a contract of what should be implemented. It gives the compiler and developers a way of knowing what is supposed to be available. It's very similar to the concept of declaring functions ahead of time in a C header file. The functions are not available until you or someone else implements them.
Related
I am working on implementing an iOS version of an existing Kotlin Android app. The specific feature that I am reproducing is a stepped wizard workflow.
How I Did it in Android
In Android, the library that I used had the following process:
Add a "stepper" view to a root activity, and provide a StepAdapter to this view
The StepAdapter controls the number of steps, and returns each step on request by index.
Each step is a Fragment implementing a Step interface that provides hooks for validation and transitioning between steps.
I ended up creating an abstract Fragment type that provides default implementations for the Step methods:
abstract class StepFragment(private val index: Int) : Fragment(), Step {
...overriding some methods from Fragment
...implementing methods from Step
}
Each step's Fragment inherits this class, passing a hard coded index into StepFragment's constructor. The steps may override some of the methods from StepFragment, and some of them call the superclass's implementation as well (I use the #CallSuper annotation to facilitate this). The StepAdapter instantiates and returns each one of these fragments.
How I'm Trying to Do it in iOS
Back in the iOS/Swift world, I found a similar library, but all it provides is the view for the progress bar. It does not handle instantiating and displaying the content of each step like the Android one does. I need to handle that myself.
You can, however, provide a delegate to the stepper that allows you to hook into the transitions (willSelectIndex, didSelectIndex, canSelectIndex, etc.). I made the root ViewController the delegate for the stepper. This ViewController has the progress bar view at the top, and a container for each step beneath the progress bar. Each step's ViewController is embedded into a corresponding container. To control transitions between steps, I simply show and hide these container views. I have that part figured out.
What I do not have figured out is how to replicate the StepFragment class from Android Fragments into Swift ViewControllers. I have the following problems:
Swift does not have abstract classes (for some reason).
I do not instantiate the step ViewControllers; that is handled internally by the storyboard. This means that I can't provide the step index in the constructor like I can in Android. An abstract property would be the typical way to get around this, but (see above) Swift doesn't have abstract classes.
I could get around these problems in a few ways:
Instead of an abstract class, I can use a protocol. I can specify the requirement that all classes that conform to the protocol must be ViewControllers, but I cannot override methods from ViewController in the protocol (as far as I've been able to see). I can add an extension to the protocol to provide functions with default implementations, which implementors can then override, and they can even call into the original functions (using (self as Protocol).func()). However, this feels very clunky.
Instead of an abstract class, I can use a regular class. I can achieve everything that I want to here, except that I can't enforce that the subclass implements abstract members at compile time. I would need to use something like fatalError() in the base class, which only throws at runtime. This also feels clunky.
I keep hearing about the "delegate" pattern, and I think it's a nice elegant way to solve a lot of problems in a "Swifty" way, but I have no idea how I would use that here. I think I would prefer being able to do it this way over the above two.
TL;DR
To sum up my problem, I am looking for a way for multiple "step" child ViewControllers to hook into a wizard workflow controlled by a parent ViewController. They should be able to perform some (potentially async) logic on step selection, before transitions, and even block transitions. I must be able to optionally ignore some of these hooks, which should then use a default implementation.
I discovered that you actually can provide default implementations for protocol functions, so this is the solution I came to:
protocol StepViewController: UIViewController {
var stepIndex: Int { get }
func onBackClicked(_ goToPreviousStep: () -> Void)
...
}
extension StepViewController {
// this is a default implementation, so implementors don't have to provide it
func onBackClicked(_ goToPreviousStep: () -> Void) {
goToPreviousStep()
}
...
}
class StepOneViewController: UIViewController, StepViewController {
let stepIndex: Int = 0
func onBackClicked(_ goToPreviousStep: () -> Void) {
// if I want to call the default implementation I can do this:
(self as StepViewController).onBackClicked(goToPreviousStep)
}
}
I'm trying to use the MVC in an app loading a url into a webView. I'm struggling with how to/whether to define the back, forward, reload, etc... functions in the model or the viewController. I'm pretty sure they belong in the model, but then how do I call them in the IBAction for the corresponding button?
In trying to call the class function in the IBAction, first I have to create an instance of WebViewLoadRequest in each IBAction which seems extraneous. I can't (and probably shouldn't) create a global instance of WebViewLoadRequest because self isn't available in the property initializer to reference the UIWebView Outlet
class WebViewLoadRequest {
var outlet: UIWebView
var url : String
private var address: URL
init(outlet: UIWebView, url: String) {
self.outlet = outlet
self.url = url
self.address = URL(string: url)!
}
func load() {
self.outlet.loadRequest(URLRequest(url:address))
}
func back() {
if outlet.canGoBack {
outlet.goBack()
}
}
func forward() {
if outlet.canGoForward {
outlet.goForward()
}
}
func refresh() {
outlet.reload()
}
}
To simplify:
How can I write the following function in the model layer and implement it in the ViewController?
//outlet refers to a UIWebView
func back() {
if outlet.canGoBack {
outlet.goBack()
}
}
Or should this strictly be a function of the view controller? I realize essentially just the view is being changed, but there's a lot going on behind the scenes in the goBack operation that I'm not sure belongs in the ViewController
You are confusing two things here IMO, there is object oriented programming which you described in your question. There is also another concept which is commonly used and also very common in Swift, this concept is MVC. Model, View, Controller. This is basically a seperation of powers. In practice this comes down to:
Model - custom class with all the logic, in your case the WebViewLoadRequest
View - defined in a 'storyboard'
Controller - Will be a subclass of UIViewController, this subclass also has a reference to your model, all the IBActions and IBOutlets to connect to and update your UI.
You can learn more about MVC here:
https://www.raywenderlich.com/132662/mvc-in-ios-a-modern-approach
Also I would strongly recommend watching the C193p course from Standford by Paul Hegarty, this teaches you all there is to know. For example the 4th week is about MVC
https://itunes.apple.com/us/course/developing-ios-10-apps-with-swift/id1198467120
EDIT:
Basically your construction is a construction of delegation. The normal way to solve this issue is by creating a delegate property on your model-class, the delegate implements a certain protocol with for example a 'canIGoBack'-function. The model can request extra data from the delegate by calling that delegate function. Apple has something about delegation in their swift manual (check the 'Delegation' part:
https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html
I believe Paul Hegarty also discussed it.
However, in this case it is also fine to do the canGoBack in the viewcontroller and conditionally call your model. MVC is only a guideline, there are many reasons to sometimes do logic in a viewcontroller, the biggest reason is probably if it makes shit easier. Implementing delegation only for a canGoBack is most of the times just silly.
The question (reflection or something like it?)
Is it possible (in Swift) to extract all the method signatures of an iOS protocol programmatically, such as for UITextViewDelegate, whose methods are all optional, without having to instantiate a separate class that explicitly implements all the protocol's methods?
In this case, want to intervene as the delegate to intercept one of the methods and do some related operation to that activity, then daisy chain the delegate call forward. But unfortunately, becoming the delegate entails responsibility to forward all of the protocol the downstream consumer
If you're saying what I think you're saying, there actually is a very simple way to do this: implement forwardingTarget(for:), as I do here:
https://github.com/mattneub/Programming-iOS-Book-Examples/blob/master/bk2ch12p611tabBarMore/ch25p882tabBarMore/MyDataSource.swift
The idea is that I don't know what the table view's dataSource actually does, because the table view and its data source belong to Cocoa, but I want to act as a "man in the middle" between them and just tweak the behavior of one data source method if it is called. All other method calls just get passed along, without my even knowing what they are.
I was just understanding the method swizzling done in obj c Method Swizzling and dangers of using method swizzling and couldn't help but draw a comparison between doing method swizzling and overwriting method implementation using categories.
They both help override the functionality of the predefined framework methods.
So is there any difference between the two or they can be used interchangeably?
The main difference is that Objective C prevents you from invoking the original implementation from a category override. This is because Objective-C's super invocations start from the super-class, while categories override methods on the same class level.
Method swizzling, on the other hand, lets you keep a reference to the original implementation as well, so that you could call it from inside your implementation. For example, in the article at your first link the author writes this:
- (void) logged_viewDidAppear:(BOOL)animated {
[self logged_viewDidAppear:animated];
NSLog(#"logged view did appear for %#", [self class]);
}
The second line makes a call to logged_viewDidAppear: method, which looks like an unconditional call to itself that should cause infinite recursion. However, this is not what happens: after swizzling, this call gets transformed into a call to the original viewDidAppear: because of the way method swizzling works.
In contrast, overriding a method from a category does not give you access to the logic of the method that you are overriding. It lets you replace the logic, but it does not let you extend it.
extension UIViewController{
public func myViewDidLoad(){
self.viewDidLoad()
//but you need to call this method everywhere replacing
}
//you cant do this
public func viewDidLoad(){
self.viewDidLoad()
//my code
}
}
Categories or extension let you do these:
Add computed properties and computed type properties
Define instance methods and type methods
Provide new initializers
Define subscripts
Define and use new nested types
Make an existing type conform to a protocol
(from Apple)
They don't let you extend original method of the same class that you are extending and if you try like the above code method signature conflict pops up.
You might want to check this website to get the concept diagrammatically. I really loved it.
http://matteogobbi.github.io/blog/2014/12/15/extending-methods-in-a-category-by-method-swizzling/
Make sure to check this awesome article for good implementation detail:
http://nshipster.com/method-swizzling/
I am making an app for iOS in Swift and I am trying to keep my API call logic outside of my ViewControllers. I followed this tutorial for 1 API call that I'll describe below.
I am creating a protocol 'APIControllerProtocol' that the ViewController will implement, and then I am creating an APIController class that will contain a property called 'delegate' to hold an instance of type APIControllerProtocol. The protocol is simply defined as
protocol APIControllerProtocol {
func didReceiveAPIResults(result: SwiftyJSON.JSON)
}
And the ViewController will implement the didReceiveAPIResults function to be called after the API call returns JSON data. The ViewController will contain an instance of the APIController as well to call a function which makes the call to the API.
At this point I want to have more ViewControllers perform API calls, and if I only need to make 1 API call per ViewController, I can just add another function to APIController, and I can have the new ViewController use the same protocol. However, this logic is flawed if there is a ViewController that would need to make 2 or more API calls, because
I cannot call didReceiveAPIResults in the ViewController in 2 different ways,
I don't think adding multiple 'delegate' properties to the APIController class to hold different types of protocols is the way to go.
Defining multiple functions in a protocol would mean I would have to implement all of these functions in ViewControllers that only need to make 1 API call.
My ideas to solve this problem are
Make an APIController class and APIControllerProtocol for each ViewController
Use optional functions in the APIControllerProtocol so not every ViewController would have to implement all of the functions (I don't know how this would really work)
Any other insights would be appreciated.
Your use of the term "API" is confusing. It sounds like what you mean is a server request, so that's what I'm going to call it.
I would suggest using a completion block/closure design rather than a protocol-based design.
Make your calls to your network request class take a completion closure, and call that completion closure once the server request is done.
You can write your network request class to maintain a list of requests and the completion closure for each pending request, and invoke the closure for each request once it completes or fails.
Defining multiple functions in a protocol would mean I would have to
implement all of these functions in ViewControllers that only need to
make 1 API call.
This is incorrect. Create optional functions and you probably have your answer.
#objc protocol APIControllerProtocol {
func didReceiveAPIResults(result: SwiftyJSON.JSON)
optional func someAwesomeMethod()
}
https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html
Optional Protocol Requirements
You can define optional requirements for protocols, These requirements
do not have to be implemented by types that conform to the protocol.
Optional requirements are prefixed by the optional modifier as part of
the protocol’s definition.
An optional protocol requirement can be called with optional chaining,
to account for the possibility that the requirement was not
implemented by a type that conforms to the protocol. For information
on optional chaining, see Optional Chaining.
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.
The tutorial you're basing your logic on does not look ok to me. What it does, it implements a standard delegation pattern tweaked to the max. DetailsViewController has an APIViewController instance to which it assigns itself as the delegate - pretty standard. But then DetailsViewController calls methods on that APIViewController instance in order to get the delegate methods fired, which are implemented in DetailsViewController. That's too much going in circles for my taste.
A much more sensible pattern would be to create a singleton (shared instance) object which would handle your API calls and any view controller could access it. There you can implement your methods that take your parameters, one of them being a closure which could be called when your request completes passing the result to the view controller.
So in your view controller you'd call something like this:
APIController.sharedInstance.doSomethingWith(someParameter, completion: { (result) -> Void in
// Do something with the result
})
Your APIController would implement this something like this
func doSomethingWith(param: String, completion: (AnyObject) -> Void) {
// do your data fetching here...
// pass the result to the closure when the data is retrieved
completion(result)
}