📜 ⬆️ ⬇️

We facilitate the support of iOS applications. Part 2 - location and network

Good afternoon, hrobrozhiteli,

Articles are devoted to how I cope with the support of applications that have not been through one version, were written at different times and by different people. I hope they will help other iOS developers.

  1. We facilitate the support of iOS applications. Part 1 - without looking up from Xcode
  2. We facilitate the support of iOS applications. Part 2 - location and network
  3. We facilitate the support of iOS applications. Part 3 - Fall and Logs

In the first article, I shared my experience with hard-to-reproduce bugs. In this article I will tell you how to deal with bugs that are associated with a network or location. Those who are interested in this topic, please under the cat.

The attentive reader of the first article could have guessed that for a long time I worked in an outsourcing business (no need to put a cross on me). And therefore my articles describe situations where there is a customer who is far from you.
')

The second type - incredible location conditions

"Here I have here, in Paris, the data is not so displayed"
It happens that a lot of things in the application are tied to the location, and everything is fine with you, but the client doesn’t display the data and is in Paris ... well, well, what else can I say. And I'm in Siberia, and I have everything OK with the data, to each his own. What to do? Simulate location. And so, consider our options:
1) This is a simple substitution of the location in the simulator. How to do nothing - he will put you in the right point. “IOS Simulator” → Debug → Location.
2) If the points are small, you can start to simulate the path, there are pgx files, are added to xcode through the right icon

And the locations will move you along the way, but ... If my memory serves me, you can do this only by attaching the application to Xcode, and you cannot adjust the time between jumps. What do testers do? Sit at poppies? Good company, if everyone bought a MAC. And always connecting an Xcode app? And if you need more delay, more than Xcode will do?
3) This is where magic runtime methods unexpectedly come to the rescue, a lot has been written about them, for example here , but really why it can be useful ... it is not entirely clear. However, here is an option that will help us solve the problem of changing the location or even navigating. FakeGPSUtility is strictly not to judge, several times it was useful and played an important role, but this is still a bicycle, and here it is not to be unfounded and show you the direction. This project fulfills its purpose - you can replace the location and jumps between them are not fixed. Anyone can do this, even the client, Xcode is not needed and the main thing for me (for someone secondary) we do not change anything in the project code, everything is replaced when the application is launched. That is, you attach it to the project, the project code remains the same, fix a bug / test and delete, or expose the necessary #define . Voila, the useful tool is at hand, and this code will never fall into Release, only for tests.
With iOS 8, the maps began to receive a location (the real location of the device), even when simulating, so if I get my hands, I finish the tool.
In the meantime, she can do this with the card. The red pin is what arrives in the delegate method NSLocationManager , the rest is the native behavior of the card. The simulation was going on - the map was being conducted.

Subspecies third - problems with http request

- When the edge of the connection application does not work
- That is why I test it on WiFi, so why torture myself something!
Consider another case - the problem manifested itself due to a bad network. In general, it is useful to simply simulate a bad internet connection and see how the application works, because while you are on WiFi, and maybe the service is on localhost, you will not understand how it behaves on 3g (and, God forbid, also on edge ). So, for the sake of interest, go to the device in Settings → DeveloperNetwork Link Conditioner → Enable and choose how bad the connection we want. After that, you can dramatically change the point of view about the speed of the application.
And so, you can make the Internet slower, but what if you need to break down any one specific response in order to get the same problem as the client / tester? Then you have to do another bike (tink-tink).
As with FakeGPSUtility, I’m against dopilivat something inside our working classes for testing and bug-playing, the code is added exclusively around the project. NSURProtocol to help us. As for me, the 3 most likely places where a request may need to break is NSURLSessionTask , UIWebView and WKWebView . There are other connections on the network, but here I will not help you - I will not advise anything good, just sit and spoil the project code for the sake of tests.
NSURLSessionTask
To break its response, we need our protocol to fall into all NSURLSessionConfiguration. To do this, override
- (NSArray *)protocolClasses 
And just return the array with our MyURLProtocol. If your application uses other protocols, add them too (you can use NSClassFromString to avoid dragging the headers). If you have even more complex logic and protocols are not everywhere and are not always used, then ... you are to blame - finish the bike in your own image and likeness.
joke, here's the code
 static char kAssociatedObjectKey; - (NSArray *)protocolClasses { NSMutableArray *result = objc_getAssociatedObject(self, &kAssociatedObjectKey); if (result == nil) { result = [self customeProtocolsArray]; objc_setAssociatedObject(self, &kAssociatedObjectKey, result, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } return result; } - (void)setProtocolClasses:(NSArray *)protocolClasses { NSMutableArray *result = [self customeProtocolsArray]; if (protocolClasses.count > 0) { [result addObjectsFromArray:protocolClasses]; } objc_setAssociatedObject(self, &kAssociatedObjectKey, result, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } - (NSMutableArray *)customeProtocolsArray { return [NSMutableArray arrayWithObject:[MyURLProtocol class]]; } 
But, for the bacgkound session, this won't work. proof

UIWebView
It’s still easier - just register the class. For example:
 - (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [NSURLProtocol registerClass:[MyURLProtocol class]]; return YES; } 


WKWebView
But here everything is bad, it will not be possible to replace it from the outside, you still have to stick crutches for this into the project code.

But, actually, the protocol itself
 @implementation MyURLProtocol + (BOOL)canInitWithRequest:(NSURLRequest *)request { #if !defined(STAGE_SERVER) || !STAGE_SERVER return NO; #endif NSString *urlString = [[request URL] absoluteString]; NSRange randge = [urlString rangeOfString:@"http://yandex.ru"]; if (randge.location == 0) { return YES; } return NO; } + (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request { return request; } + (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b { return NO; } - (void)startLoading { dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSError *error = [NSError errorWithDomain:@"MyErrorDomain" code:42 userInfo:@{ NSLocalizedDescriptionKey: @"We fail it!"}]; [self.client URLProtocol:self didFailWithError:error]; }); } - (void)stopLoading { // stop request if you can } @end 


Subspecies fourth - for tests need a specific / obsolete response

We are informed that yesterday the incorrect behavior was reproduced, but today it is no longer working, the service does not receive the same answer. To say that now everything is working is wrong, since according to the law of the genre this situation will repeat on the very first day of release. And you cannot influence the server-side team for a number of reasons: they are in another city, you do not know them, they are on vacation.
In this situation, the NSURLProtocol is in a hurry to help you again. We just need to change + (BOOL) canInitWithRequest: (NSURLRequest *) request , so that with the necessary request we return the necessary data.
This is how our MyURLProtocol might look like in order to simulate the desired response.
MyURLProtocol
 @implementation MyURLProtocol + (BOOL)canInitWithRequest:(NSURLRequest *)request { NSString *urlString = [[request URL] absoluteString]; NSRange randge = [urlString rangeOfString:@"http://localhost:1984/oldresponse"]; if (randge.location == 0) { return YES; } return NO; } + (NSURLRequest *)canonicalRequestForRequest:(NSURLRequest *)request { return request; } + (BOOL)requestIsCacheEquivalent:(NSURLRequest *)a toRequest:(NSURLRequest *)b { return NO; } - (void)startLoading { NSString *path = [[NSBundle mainBundle] pathForResource:@"unexistd_response" ofType:@"json"]; NSData *data = [NSData dataWithContentsOfFile:path]; [self.client URLProtocol:self didLoadData:data]; [self.client URLProtocol:self didFailWithError:nil]; } - (void)stopLoading { // stop request if you can } @end 


Here we read unexistd_response.json from the file system, but no one forbids to redirect to a working machine, to pick up a small service on it and get an answer from it. This is a great option if you have time and do not want to add strange files to a project, even if they are only for a while and for tests.
Personally, I often create NSURLProtocol to work at first on a project, because often the service is only in plans and TK, but you really don't have it. But I already want to write, as if the application works as it should, receives data, and, depending on them, shows the contents to the user. I make myself #define STAGE_SERVER 1 and, until they give me a working service, I work with local files. They then save me when the developer, going home, turns off the computer, which was also a developer server (painfully funny situation, which was repeated several times).

Conclusion


Today we have examined cases where problems are very closely related to http requests or locations. NSURLProtocol and FakeGPSUtility are the heroes of today's article, they will greatly simplify testing and reproducing the incorrect behavior of the application.

Source: https://habr.com/ru/post/254891/


All Articles