UIWebView slow loading using NSUrlProtocol Xamarin.Forms - ios

I'm working on an iOS application using Xamarin.Forms. This application is using UIWebView controller that shows a web application that is hosting on my server. Each time that I make a request I have to send a custom header in order to identify that this request comes to the mobile application and not from a browser, to do this I'm using an NSUrlProtocol object that overrides the method Request that inserts the custom header on each request.This is my code:
public override NSUrlRequest Request {
get {
NSMutableDictionary headers = null;
if (null == base.Request.Headers) {
headers = new NSMutableDictionary ();
} else {
headers = new NSMutableDictionary (base.Request.Headers);
}
headers.Add(NSObject.FromObject(AppVariables.headerVariable), NSObject.FromObject (AppVariables.appVersion));
NSMutableUrlRequest newRequest = (NSMutableUrlRequest)base.Request.MutableCopy ();
newRequest.Headers = headers;
return newRequest;
}
}
The problem that I have right now is that I noticed since I started using the NSUrlProtocol the loading time of the pages is increasing a lot. Right now the loading is taking 10 seconds, before this implementation the page took 3 seconds approximately.
Can anyone please point out some helpful direction to overcome this??

I don't see any reasons for the delay in response time when you're using custom headers. Like Andreas mentioned in the comments, I believe it has to do with your server code. I would recommend profiling your server code.
Do you see similar results when you send the requests (with custom headers) from Fiddler or cURL?

Just like #AndreasPaulsson and #prashant had mentioned, server might be the culprit. I would recommend testing the API with tools like Postman and check the response speed. I would also recommend you to check ModernHttpClient by Paul C Betts. In iOS the library uses NSUrlSession.

Related

iOS How to check how many request was sent for server, parse swift 4

I have something strange with my parse server, I start to check Analytics toll at the parse, and I saw during some period I have more than600 request per minutes, to the server, in my opinion, is not possible because only I am, testing app.
At image you can see a trend, I have 10-14 requests, and immediately I get 600?
How I can check how many requests are sent my app?
You can use a proxy to catch your request
-> Charles is a really useful tool for this
If you want count in your app how many are sent, it depends on how you organised your code.
-> you could create a counter for this (in your custom "ReqeustMaker" create a static variable or even in in a shared instance class if you need more fancy stuff)
Using the following code you can trap all the calls, and find them all on Xcode debug console:
class MyURLProtocol: NSURLProtocol {
class func canInit(with request: URLRequest) -> Bool {
print("Requests : \(request.url?.absoluteString ?? "")")
return false
}
}
and call this when app is loading:
URLProtocol.registerClass(MyURLProtocol.self)

Bypass Service-Worker caching

I have a progressive web-app, which speaks to an API. The calls to this api get cached by a service worker, which works great.
But now, I want to add a reload-button, which ideally forces the service worker to try to bypass the cache and update it if successful, also it should not return the cached result if a connection could not be made.
I am a bit unsure how to solve this. I am using the sw-toolbox.
All requests go through the fetch callback which receives a request object. Thus, before returning a cached response you can look for an additional header parameter (you need to include it into your request to API) to skip the logic returning cached response.
Based on your description, you are using the application cache. It can be accessed from the app fronted independent of the sw-tool box.
function onReloadButtonClicked(event) {
//Check for browser cache support
if ('caches' in window) {
//Update cache if network query is successful
caches.open('your_cache_name')
.then(function(cache) {
cache.add('your_url');
}).catch(function(err) {
// Do something with the error
});
}
}

Intercept responses from WebSocket connection in UIWebView

I have an app which has a UIWebView inside of it with a loaded website. This website has a chart in it which is periodicly updated with data from remote server via websockets (socket.io).
Im new to websockets technology but Im trying to somehow intercept the chart data that the website is receiving from server via it.
Till now I have managed to catch http requests sent by the website of such address format: “http://website-address/socket.io/?auth_token=...”
I have the socket.io library for iOS but don’t know how to use it to somehow spoof the website connection and acquire the data downloaded by the website. Can anyone help? Is it even possible?
Switch to WKWebView if you can. Using javascript bridge is much easier there. That said, with UIWebView, you'd need to inject a script that adds a handler for events received by the socket that you are trying to listen to. You can either create an io variable by yourself but apparently the server needs auth token. If you cannot create an auth token, you can only do this if you have access to the io variable created by the website.
Then for adding a handler, you'll need to know what the event name is, that delivers the chart data. You can snoop around the website and see if you can find that. If you cannot all bets are off. Once we register a handler and get the data, we need to pass this back to your native code. This is where WKWebView would keep it clean by letting you add message handlers that can deliver messages from js to native code. For UIWebView you'll have to create a custom url scheme and spoof a navigation request to pass the data. Lets assume your custom url scheme is 'myApp'. Then the script you'd need to inject would be:
<script>
/* if you can access/create the auth token
var socket = io('http://website-address/socket.io/?auth_token=');
*/
var socket = getioReferenceCreatedByWebsite();
socket.on('<eventName>',function(){
window.location = 'myApp://<data>';
};
</script>
In your native code:
...
webView.delegate = self;
[webView stringByEvaluatingJavaScriptFromString:#"<theAboveJSAsAString>"];
....
}
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{
if(request.URL.scheme == #"myApp"){
NSString *data = request.URL.path;
//handle the data
return NO;
}
return YES;
}
In regards to Santhosh R answer. I had the problem he mentioned where I could not get a reference to the websocket object as it was caught up in a closure.
I solved this by adding in a preload script which wraps the native Websocket object to store any instantiated websocket objects in an array and then return the newly created websocket object.
Here is the code.
in your WebView element add in a preload attribute.
<webview id="myWebview" src="http://exmple.com" preload="./interceptor.js"></webview>
and then in inteceptor.js
window.NativeWebsocket = WebSocket;
window.WebSocket = function(url, protocols){
window.interceptedWebsockets = [];
var ws = new NativeWebsocket(url, protocols);
interceptedWebsockets.push(ws);
return ws;
}
Then, inside your WebView context you can an access array of instantiated websocket objects using window.interceptedWebsockets

Switch between different API hosts

I'm working on an app which primarily works with an API that will be installed in an internal system. The API is also accessible via the public internet. The client wants to allow users to enter both an internal and external (public internet) URL that the app will then connect to depending on availability of the internal and external URLs.
The app is basically done with the exception that it currently connects to the internal URL only for all it's API calls. I'm using AFNetworking with block-based completion/failure invocations for each API call.
Based on the logic that we have designed, the app will always check for the API's availability by querying for the server's current time. This is done by calling http://internal_url/api/time. If this API fails to return an appropriate respond, we'll switch to the external URL http://external_url/api/time and call the same API on that URL. If both fails, the app will inform the user accordingly and not perform any other queries to the API.
Without revealing too much, here's some code on how I the API calls are currently setup:
- (void)someAPIMethodCall:(NSDictionary *)parameters completionBlock:block failure:block {
// query /api/time and return the URL (internal/external) that is currently up
AFHTTPClient *client = [AFHTTPClient clientWithBaseURL:<url returned from above query>];
[client operationWithSuccess:block failure:block];
}
So my question would be: what is the best way to get the query /api/time method above to work? Obviously, this method needs to complete and return either the internal/external URL so that the subsequent actual API query could use. AFAIK, AFNetworking calls are block-based so it will return before the above /api/time returns. I've also thought of a separate class that uses NSURLConnection synchronously which will block the main-thread while it waits for the /api/time to return.
I'd like to tell you to simply use the same URL internally and externally (via DNS) but that's not what you want.
I think you're asking how to conditionally call the other url.
You want someAPIMethodCall to be asynchronous... so you don't want to block on the call to checking for the correct api to call.
Aside from caching the results so you don't have to do this every time, you simply want to call another block based method of your own that has a completion block which passes IN a parameter of the URL to call for your real query.
- (void)someAPIMethodCall:(NSDictionary *)parameters completionBlock:(void (^)(void))succesBlock failure((^)(void)):failureBlock {
[self callBlockWithMyApiUrl:^(NSString *apiUrl){
AFHTTPClient *client = [AFHTTPClient clientWithBaseURL:apiUrl];
[client operationWithSuccess:successBlock failure:failureBlock];
} onFailure:^{
failureBlock
}
}
- (NSString *)callBlockWithMyApiUrl:(NSString * (^)(void))success (void (^)(void))failure
{
// Your code to test for the working URI
// If you're doing it this way, I'd suggest caching the result.
// Subscribe to networking interface changes to dump the cache.
}

How to check status of web server in iOS?

I need to check status of web server, specifically, the information if the web server returns proper status code 200. Is there any way to get the HTTP response code from web server in iOS?
This is well documented in the official docs, with no need to implement a 3rd party library (although if you're new to iOS coding, a Networking API can simplify the underlying function calls). Your class must be an NSURLConnection delegate, and after making the NSURLConnection you implement the delegate method something like this:
-(void)connection:(NSURLConnection *)connection didReceiveResponse:(NSHTTPURLResponse*)response
{
if ([response statusCode] == 200) {
// Handle valid response (You can probably just let the connection continue)
}
else {
// Other status code logic here (perhaps cancel the NSURLConnection and show error)
}
}
You should find the URL Loading Guide very useful reading for this and other networking functions on iOS.

Resources