ios - swizzling of firebase cloud messing - ios

I am working with an app using Firebase Cloud Messaging for Push Notification. After reading its documentation, I have a little confuse about "Swizzling disabled", I tried to find some tutorials which are talking about it, but unfortunately there is no any tutorials. Could you please help me to describe what exactly "swizzling" mean? and what is case we need to use it?
Thank you so much.
Ryan

Method swizzling means that you change the implementation of a given function at runtime.
It is often used when you don't have access to the code of the function or if you don't want to modify the code of a library and when inheritance doesn't apply.
Basically what Firebase does: you don't have access to the Push Notifications API/functions except for the delegates that Apple exposes. By swizzling such a function, you can add some logic to what it is currently doing. (You can still call the "previous" function like you would do with super or completely replace its original purpose).
This is how Firebase asks you to activate PUSH Notifications. It makes it easier for you to add it in your code and give Firebase a lot of flexibility as with one line in your AppDelegate they can run whatever they want.
NB: A simple example: you don't have access to the print function, you can just use it. Instead of wrapping the print function in a custom function and replacing its usage everywhere; you could swizzle print with one of your custom function to extend or replace its original functionality. As it is applied at runtime, you wouldn't have to change anything in your project and all print calls would be "redirected" to your new custom function.

Related

Can I make a background data fetch in an external library?

I am currently making a SDK that would require fetching data in the background. For an example, let's say that the SDK provides some weather data that needs to be relatively fresh to be useful. At some point, the data is outdated and needs to be refreshed. The problem is, that the app might be often used in places with poor internet connection, or with no internet access at all. This brings me to the idea that maybe I should fetch the data in background, when the internet is accessible.
The SDK is packaged into a XCFramework, and distributed using Swift Package Manager. When I try adding capabilities to my target, Xcodes gives me a screen that states "No matches, Capabilities are not supported for SDK".
Is it even possible to make a background data fetch without the access to app capabilities? Or does this responsibility fall to the client app for my SDK? Sorry if the answer is obvious, I've tried searching online for a direct response to my problem, and wasn't able to find a clear answer.
An SDK/framework can't request such capabilities, but it can contain the code necessary to do all the work. You need to expose a function that the client can call to run this code.
E.g.
Inside your framework
public func setupBackgroundDataFetching() {
// create background task
}
Then inside your README, you need to tell users that they have to enable this capability and call:
let weatherSDK = WeatherSDK()
weatherSDK.setupBackgroundDataFetching()
Inside their AppDelegate. Depending on your use case you may need to have the function take in some parameters, or make a singleton class and have this as a static/class func, etc. But the basic idea is the same, wrap up the code and ask the client to invoke it
Example:
Heres the repo of a crash reporting tool I use: https://github.com/getsentry/sentry-cocoa
You'll notice their README contains installation/usage guide which asks users to run a setup method in their AppDelegate, which takes in a configuration. Based on this configuration, they can setup anything they need once the app starts, such as a background task if needed

Keeping a WKWebView and it's UIViewController in the background running and accessible from multiple ViewControllers

Background: In order to make web requests to an API endpoint, I need to scrape a website and retrieve a token every 25-30 seconds. I'm doing this with a WKWebView and injecting some custom JavaScript using WKUserScript to retrieve AJAX response headers containing the token. Please focus on the question specifically and not on this background information - I'm attempting this entirely for my own educational purposes.
Goal
I will have different 'model' classes, or even just other UIViewControllers, that may need to call the shared UIViewController to retrieve this token to make an authenticated request.
Maybe I might abstract this into one "Sdk" class. Regardless, this 'model' SDK class could be instantiated and used by any other ViewController.
More info
I would like to be able to call the UIViewController of the WKWebView and retrieve some data. Unless I re-create it every 25 seconds, I need to run it in the background or share it. I would like to be able to run a UIViewController 'in the background' and receive some information from it once WKWebView has done it's thing.
I know there are multiple ways of communicating with another ViewController including delegation and segueing. However, I'm not sure that these help me keep the view containing the WKWebView existing in the background so I can call it's ViewController and have it re-perform the scrape. Delegation may work for normal code, but what about one that must have the view existing? Would I have to re-create this WKWebView dynamically each time a different model, or view controller, were to try and get this token?
One post suggests utilising ContainerViewControllers. From this, I gather that in the 'master' ViewController (the one containing the other ones), I could place the hidden WKWebView to do it's thing and communicate to the child view controllers that way via delegation.
Another post suggests using AppDelegate and making it a shared service. I'm completely against using a Singleton as it is widely considered an anti-pattern. There must be another way, even if a little more complex, that helps me do what I want without resorting to this 'cheat'.
This post talks about communicating between multiple ViewControllers, but I can't figure out how this would be useful when something needs to stay running and executing things.
How about any other ways to do this? Run something in a background thread with a strong pointer so it doesn't get discarded? I'm using Xcode 9.2, Swift 4, and iOS 11. As I'm very new to iOS programming, any small code examples on this would be appreciated.
Unfortunately, WKWebView must be in the view hierarchy to use it. You must have added it as a sub view of an on-screen view controller.
This was fine for me. I added this off-screen so it was not visible. Hidden attribute might have worked as well. Either way you must call addSubview with it to make it work.
There are some other questions and answers here which verify this.
Here is a way if you don't wish to use a singleton.
1- In the DidFinishlaunchingWithOptions, Make a timer that runs in the background and call a method inside the app delegate Called FetchNewToken.
2- In FetchNewToken, make the call needed and retrieve the new token (you can use alamofire or any 3rd library to make the call easier for you).
Up on successfully retrieving the token, save it in NSUserDefaults under the name upToDateToken
You can access this token anywhere from the application using NSUserDefaults and it will always be up to date.

Swizzling methods : Programmatically dismiss system dialogs like “<App> would like to access your contacts” in iOS

Is there a way to programmatically dismiss dialogs like the ones where the app wants to access contacts?
I think there's a way by swizzling API methods, but I don't really know which. What is the methodology to find out which methods need to be swizzled? If swizzling is not the way then what could be another alternative?
As a note, this is not for a product, is just for testing so swizzling is a good option if it works.Kindly share sample code if any.
No there's no way to do that. The security notices are presented by the system UI Window Server, not the app itself.
This is done for security, so what you're trying to do totally negates the whole point of them popping up in the first place.

Registering setup code in objective-c

Is there a standard mechanism with Objective C and the iOS runtime to register setup code?
Why?
The advantage of this is that you can decouple your code nicely.
If a subsystem needs particular setup, the set up stays in that subsystem.
If a group of files need to register that they should all be offered as a particular service, that can be encapsulated in individual files that offer the service and there is no need for a separate configuration file to be kept up to date.
Getting the code to run isn't especially important – I can do that myself with various entry points. What I need is for the compiler or linker or run time or magic to be able to collect up anything that I've registered in different parts of a program, and let me have it when I need it.
How to in c++
With c++, I've typically arranged for this with static instances that are constructed before main() is called. I could use objective-c++, but I'd much prefer to use a standard mechanism.
Thanks.
I would look up:
+ (void)initialize
This method is called whenever a message is first sent to the class meta-object, such as, for example, when alloc-ing an object of that class.
Registering Code
Use the class method +(void) load for components that need to self register themselves.
Note that the load method is run on all subclasses and all categories. This is nothing like the the normal method calling behaviour.
Creating a Registry
If components need to register themselves in some kind of container, use the class method +(void) initialise to create a container to hold the components that are going to register themselves. It seems from my limited testing that initialize can be called before load when a load method uses a class with an initialize method, which is pretty cool if reliable.
Running Registered Code
If the components should do something at some specific entry point of your App, then at that entry point, grab the registered components from the registry and do that thing. Eg, you might extend you UIApplicationDelegate's -(BOOL) application:didFinishLaunchingWithOptions: to actually perform the setup stages the components registered.
In my case, I actually want the registered code to get run every time a specific kind of object is constructed, so I call the registered methods there and let them have the object being constructed.
More references on load and initialise
Thank you for the answers and comments that let me put this answer together.
Quite a lot of detail from Mike Ash, although I was initially put off by his statements about load being "tricky because it runs so early".
A very helpful S.O. question on load and initialize.

How to prevent iOS app launching whilest defining protocol handling

I have successfully implemented urlHandling of a self-defined protocol.
I need to prevent the app from launching when other apps try to call this url for security issues. The protocol is only needed for a callback from executed JavaScript-code, so only calls within the app are allowed.
To use the response of UIWebView:stringByEvaluatingJavascriptFromString is not an option because the JS code has to work on multiple platforms.
I thought of intercepting the call itself within my app (which would work by using UIWebViewDelegate:shouldStartLoadWithRequest) but I could not figure out if that is possible with WP8. (In Android this would work with WebViewClient:shouldOverideUrlLoading)
How about implementing the UIApplicationDelegate protocol method:
application:openURL:sourceApplication:annotation:
and checking sourceApplication?
As far as I know, there is no standard API on iOS to have something like a "white-list" for which apps are allowed to call your custom URL scheme and open your app this way. When using this URL schemes, you can only have the whole cake, not just a small piece.
Suggestion A:
You could set a boolean variable in your app delegate to store if openURL was initiated from your app or another, in your view controller check if this variable is set to NO and exchange the rootViewController of your apps' window with a blank screen or an info screen telling the user that the app was not opened from a trusted source or whatever.
Suggestion B:
If you really don't want your app to open, you could make it crash in application:openURL:sourceApplication:annotation:.
Just check if the sourceApplication is a trusted source and if not use something like NSAssert(YES == NO, #"Bad bad boy") to make it crash.
B would only be a hack and I would not consider it best practice. Try suggestion A, it is a much more user-friendly way and you should be able to achieve your goal.
finally I've found a solution that works for me. ;) It's a different approach, though.
First: you might want to check out WebViewJavascriptBridge, thats a Bridge for communication in both directions implementing callbacks and everyhting you might need. But that is working with iframes... I wanted to use ajax.
I'm not implementing a protocol that my app is going to listen to. What I do now is implement a NSURLProtocol, which will allways return NO for canInitWithRequest. I am using a unique path (like /!anythingUnique) to identify my JS-Calls. With that set up I can call ajax and do stuff in the Obj-C code.
implement NSURLProtocol.
register NSURLProtocol via [NSURLProtocol registerClass:[SFJURLProtocol class]];.
you will need to set a baseURL... do that by loading any page or or via webView:loadHTMLSString. Do that when you initialize the controller.
fire Requests from JavaScript
catch them in your canInitWithRequestfunction. You might want to do stuff in the Controller for the sake of clean code, so think of a callback mechanism or something. ;)
???
Profit!
I hope this helps.

Resources