Objective-C: Inject code into delegate method - ios

What I want
I want to add some logging code into my app without having to update all of my view controllers.
What I've tried
I attempted to add a category to UIViewController and override one of the delegate methods. However, this produced several warnings/errors.
Is there a way for me to inject code into all of my app's ViewControllers?

Look into method swizzling. It is a powerful tool and can save you a lot of time. Swizzle one of the methods you wish to have logs in, and then call the original method to have it do what it is originally supposed to.

You could subclass UIViewController and then add the logging methods in your super class. You would still need to touch all of your VC's though and change the class to your super class though.

You can't possibly have so many view controller classes in an iOS app that touching each one will be a huge problem. So create your own UIViewController subclass, maybe OurSuperViewController that'll act as a superclass for your view controllers, and add the logging there. Then modify each of your view controller classes so that they inherit from OurSuperViewController, and make sure that each one calls super in the relevant view controller methods.
Once you've done that, you can turn logging on or off at will by modifying just OurSuperViewController.

Related

iOS Swift how to monitor UIViewController lifecyle

I am new to iOS development. So pardon me if this is a very basic thing.
From what I have learnt till now:
UIViewController class resembles somewhat equivalent to an Activity class in Android.
and viewDidLoad/viewWillAppear method to onCreate/onStart method
and viewDidAppear method to onResume method
Please correct me if I am wrong here.
Now, in Android we can monitor which of these methods(including other lifecycle methods) are triggered/called by implementing an Interface (ActivityLifecycleCallbacks) (somewhat resembling a protocol in iOS) which exists in the Application class in any Activity (particularly in a class which extends Application class).
It means that now these methods will be triggered/called whenever there is any navigation from one screen to another in the android app.
How do I do this in iOS using Swift? How do I know which screen(UIViewcontroller) the user is currently in and where he is navigating?
In short I want to write a standalone class which logs which screen(UIViewController) the user is currently in and which one of the lifecycle methods(viewDidLoad, viewWillAppear etc) is being executed?
Someone please help me out.
Edit:- I don't want them to subclass my standalone class instead of UIViewController class.
There are no global events that are fired when the UIViewController lifecycle methods are called. To create those you would need to subclass UIViewController as has been suggested.
You can look at UIApplication.sharedApplication().keyWindow?.rootViewController to get the current root view controller (which is often, but not always the currently active view controller).
You wouldn't typically design an app that depended on some central object tracking the state of view controllers.
The flow of UIViewController methods is pretty well described in the class reference and you can also work it out from the function names -
viewDidLoad called after the view controller instance is loaded (once per instantiation)
viewWillAppear called before this view appears
viewDidAppear called after this view appears
viewWillDisappear called before this view disappears
viewDidDisappear called after this view disappears
Create a view controller subclass and add that implementation in there. Then make sure all the view controllers you create subclass that new class rather than UIViewController itself

How can viewControllerWithRestorationIdentifierPath:coder: find an existing instance?

The docs on viewControllerWithRestorationIdentifierPath:coder: say:
Your implementation of this method should create (or find) the
corresponding view controller object and return it... It is not always
necessary to create a new view controller object in your
implementation of this method. You can also return an existing view
controller object that was created by another means. For example, if
the view controller had already been loaded from a storyboard file,
you would return that object rather than create a new one. [My italics.]
This has always seemed like complete nonsense to me. This is a class method! We don't have any access to any instances at this moment — unless we create one. I'd be grateful if someone can explain to me how on earth a class method can find or know about "the view controller that has already been loaded from a storyboard file".
EDIT: To earn the bounty you must show me an actual case, from your own app, of the class method viewControllerWithRestorationIdentifierPath:coder: being used to "return an existing view controller object that was created by another means."
The most common example of this I can think of is any of the view controllers that are owned by the App Delegate. This is traditionally a tab bar controller or a navigation controller in traditional apps, but sometimes it can be something completely custom, which is when this functionality might be useful.
Since the UIApplication is pretty much a singleton and has one delegate, it means your App Delegate has global state, which makes it accessible from anywhere, including in class methods with: [[UIApplication sharedApplication] delegate].
Of course, any singleton is accessible from anywhere and a common pattern (but one I personally dislike) is to have a NavigationManager singleton which manages any global view controller transitions, so in this case you'd be able to access the existing instances as well.

Omit [super] call when overriding view events in UIViewController. Impact? [duplicate]

This question already has answers here:
What is meaning of calling superview's viewwillappear?
(3 answers)
Closed 9 years ago.
What happens if you don't call super in your implementation of the view events (viewWillAppear, viewDidAppear etc.) of UIViewController?
It seems like I've forgotten to do this before, and it's unclear to me that there was any adverse impacts.
If you subclass one of your own view controller classes, you will certainly want to call super for any of these types of methods or your own base class's methods will not be invoked.
There's also the question of whether your top-level view controller classes need to call super to run the code in the base UIViewController class itself. In the UIViewController reference, it appears that the requirement to call super is documented for certain methods, among them viewWillAppear:, viewDidAppear, viewWillDisappear:, and viewDidDisappear:
If you override this method, you must call super at some point in your implementation.
However, there is no indication of what will happen if you fail to do so.
So apparently, there is something implemented in these methods in the base iOS framework view controller classes. Or at least, Apple reserves the option to implement something in these methods. You could say that they are virtual rather than abstract methods.
If you've subclassed your UIViewController delegate from anywhere (and you always subclass UIViewController at least once, in making your customized view controller), then any delegate methods in subclasses you've derived from won't get called.

viewcontroller inheritance

I have 30+ viewcontroller in my project, and there is a behavior to be added in 28 of them.
When a button is clicked if 10 second not passed in that viewcontroller, I need to present a alertview sliding from top. (I have already implemented this behaviour for one of them)
But my question is, how can I inherit this behavior in a proper way for all of these viewcontrollers?
Googling this, but cannot find relavent solutions.
If all of your view controllers that need this functionality are subclasses of UIViewController, i.e. not UITableViewController or another subclass of UIViewController, you can create a subclass of UIViewController, say ButtonClickAlertViewController (or whatever makes sense) that implements the functionality you need to replicate. Then have all of the classes that need this functionality subclass your ButtonClickAlertViewController class instead of UIViewController.
You may need to take into account how this specific functionality integrates into each of your individual view controller classes. For example, you may need a method in your ButtonClickAlertViewController class that signals a button has been clicked in order to check your timer and possibly display an alert. In each of your classes that subclass ButtonClickAlertViewController, you might need to call this method in each of the IBAction methods that your button click actions call.
Subclass UIViewController and add your desired behavior for all view controllers and then subclass your existing view controllers from the viewController with the desired behavior.
Inheritance chain:
UIViewController -> ViewControllerWithBehaviorForAll ->
YouExistingViewlContollersWhichNeedTheBehavior

Accessing PageViewController's methods from child view controller

The issue is this. I have a PageViewController as my root view controller. Initialising the first page using SetViewControllers in the ViewDidLoad block of the PageViewController class works, as does the flipping back and forth of pages using the page's gesture recognizers. This root view controller is available application-wide via the AppDelegate.
I wanted to add the ability to return to the first page from anywhere in the book, so created a public method in my PageViewController class that simply called the SetViewControllers method. While this appears to work, I've noticed certain quirks. Music that is looping in the background (again, an AVAudio object declared in the AppDelegate) is restarted and the ViewDidLoad block doesn't fire until the page is turned again using the gesture recognizers.
I tried the same thing using GetNextViewController but that simply throws a null reference exception for the PageViewController reference. I'm assuming there is some relationship between the controls I'm not grasping, perhaps something to do with the PageViewController's DataSource. I have tried delegating this to a class that overrides the default methods, as well as using Mono's this.pageViewController.GetNextViewController = delegate(method signature) syntax to no avail.
Does anyone have any insights as to what my problem here is?
Edit: Code for AppDelegate.cs can be viewed here: https://gist.github.com/1747537 and for PageViewController.cs here: https://gist.github.com/1747559.
I can see several issues in your code:
your PageViewController is already the root controller in your app, there is no need to call AddSubview() anywhere.
You are inherinting from UIViewController and then encapsulate a UIPageViewController.You should either inherit from UIPageViewController or have no inheritance at all (if it is true as Dimitri says the UIPageViewController is not meant for subclassing).
The ReturnToFrontPage() could be a method of your AppDelegate instead of your subclassed UIPageViewController.
With your combination of inheritance and encapsulation you are building a hierachy of view controllers. This will make problems with events being triggered if not implemented properly.
In short: don't inherit anything in your case. Use a plain UIPageViewController and make the ReturnTpFrontPage() a method of your AppDelegate, then it should all be a walk in the park.

Resources