I'm a bit lost on AFNetorking's Reachability and haven't found a lot of good information out there.
I have an app that logs into a web API. Each VC connects to the API in some way so each VC needs to have network access. I use a subclass of AFHTTPSessionManager to make network requests.
I am confused as to where to put the code - is it just the appDelegate or is it in each VC?
What is the use of this code?
[[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
NSLog(#"Reachability: %#", AFStringFromNetworkReachabilityStatus(status));
}];
And where do you use these methods and how do they relate to the code above?
-startMonitoring
-stopMonitoring
The -startMonitoring method of AFNetworking is used to make your AFNetworkReachabilityManager start monitoring the network connection. If you do not call -startMonitoring, your setReachabilityStatusChangeBlock will never be called (regardless of where you put it) since AFNetworking isn't monitoring your connection.
As you probably assumed, -stopMonitoring does the exact opposite of -startMonitoring - it stops the manager from checking network connectivity. I normally don't use this method in my apps, but if your app doesn't need a network connection for a certain part of it, feel free to call it (just make sure you start monitoring the network again when you need to).
The setReachabilityStatusChangeBlock is called whenever the network status changes. You can put this wherever you want to make changes if/when the network changes. An example of this would be putting it in your app delegate and sending out a NSNotification when the network status changes so that any view controller observing the notification can react appropriately.
You can also manually check to see if the network is online (as long as -startMonitoring was called and AFNetworking is monitoring your network) using something like this:
if ([[AFNetworkReachabilityManager sharedManager] isReachable]) {
NSLog(#"Online");
}
You can read more on AFNetworking's official documentation on GitHub.
Related
I have implemented CoreBluetooth for an app based on Apples CoreBluetooth TemperatureSensor example (https://developer.apple.com/library/ios/samplecode/TemperatureSensor/Introduction/Intro.html). It works great searching for devices, populating the results in a tableview, selecting one and connecting. My issue is that I want to keep the connection alive between views across the app.
In my current setup I have a view with a button that takes you to the Bluetooth setup view. The Bluetooth view is presented modally and here I search for BT devices and connects to one of them. As soon as I dismiss the view, the connection is lost, probably due to it not being retained?
Therefore I use a singleton implementation to keep the object (also as in Apples example) hoping to keep the connection alive, but with no luck. I can however retrieve the object from the singleton and call connectPeripheral and re-connect, but from a user perspective, it is not that great that the user has to input the password again after just connecting earlier.
So, how can I keep the bluetooth connection alive between views, e.g. when having a settings view where the BT device is connected, and then use the same connection in the remaining app?
Update: included code
Also worth mentioning is that my class (incl. shared instance) is not only based on the Apple example but also the SerialGATT implementation from HMSoft (which I guess is based on Apples implementation). Here is a link to one place I found on git https://github.com/ezefranca/kit-iot-wearable-ios/blob/master/kit-iot-wearable-ios/SerialGATT.h
So for my singleton I have added the following to SerialGATT.h (also tried with id, not that it made a difference)
+ (SerialGATT *)sharedManager;
and in SerialGATT.m I have
+ (SerialGATT*)sharedManager {
static SerialGATT *_sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_sharedInstance = [[self alloc] init];
});
return _sharedInstance;
}
When using SerialGATT and the singleton in my Bluetooth viewcontroller, I implement the delegate methods of SerialGATT, and then I have the following in viewDidLoad
[[SerialGATT sharedManager] setup];
[[SerialGATT sharedManager] setDiscoveryDelegate:self];
NB! I have renamed discoveryDelegate from the original SerialGATT example, before it was simply named "delegate". discoveryDelegate is the name used in Apples example, not that it matters.
From here on everything works fine in my modal Bluetooth settings view. As stated above, I can search and connect devices. The delegates are being called, all is fine and dandy. However, when I close the modal, the device is disconnected, but I can still find the object in other views when accessing the singleton, and e.g. reconnect. But then again, I would rather keep the connection alive instead of reconnecting and having to input the password again.
I had created an iOS app which crashes when network is not reachable since data received is nil. Since I don't want to update all the parts of code accessing network , I just want to show a banner when internet is not reachable and temporarily disable all the parts of app so that it docent crashes .
I saw this feature in UBER app and so I was curious .
Can anyone help me in this matter ?
Thank you
Yes, most of the third party library outside used for networking use an Apple class called Reachability. Reachability helps you to check if there is an internet connection before calling a web service or a network functionality in general. You can use in two ways:
test if connectivity is available before doing network operation
register as an observer to connectivity status changes
To do what you want to achieve just register the interesting classes as observers and once you get the notification check if the internet connection is available or not, doing the operation that you want: like alert the user, put an overlay over the interface to block any user operation until a the connection is available again.
It doesn't work that way. The network can be there when a download starts and disappear while your call is executing. Even if you only start calls when Reachability tells you that the network is there, you can still get nil results. The only solution is: Fix your code so you don't crash.
Obviously you may feel free to tell the user that the network isn't there. You might also suggest to the user to check whether they have Airplane mode turned on or whether WiFi / 3G is turned off. But you can't rely on this to avoid getting connection errors.
I created a simple class and a demo project in order to track reachability changes in a block, so you can easily apply changes on your view controller's objects according to these changes https://github.com/PabloAlejandro/PA-reachability-blocks
In order to use it add 'ReachabilityBlocks.h' and 'ReachabilityBlocks.m' to your project and declare a strong instance of 'ReachabilityBlocks'. Then you can call 'listenReachabilityUpdates' instance method in order to start tracking changes.
In your view controller:
#interface ViewController ()
#property (nonatomic, strong) ReachabilityBlocks * reachabilityBlocks;
#end
and then on 'viewDidLoad' (or whenever you prefer to start getting updates)
self.reachabilityBlocks = [ReachabilityBlocks new];
[self.reachabilityBlocks listenReachabilityUpdates:^(GCNetworkReachabilityStatus networkStatus) {
switch (networkStatus) {
case GCNetworkReachabilityStatusWiFi:
self.label.text = #"WiFi";
break;
case GCNetworkReachabilityStatusWWAN:
self.label.text = #"WWAN";
break;
case GCNetworkReachabilityStatusNotReachable:
self.label.text = #"Not Reachable";
break;
}
}];
I hope this helps.
Note: The class imports 'GCNetworkReachability' library, so don't forget to add it to your class, either manually or using Cocoapods
I would like to know how it is possible to continue a async NSURLConnection, which has been started in the foreground, when the app will be terminated.
Currently I am starting a NSURLConnection when the app goes in the background. This works fine as long as the user is slower than the connection, when he wants to terminate the app. But when the user is quicker than it, the connection can't be established.
Here is my code:
// AppDelegate.m
- (void)applicationDidEnterBackground:(UIApplication *)application
{
AnObject *newObject = [[AnObject alloc] init];
[newObject InactiveApp];
}
// AnObject.m
- (void)InactiveApp
{
self.backgroundTaskID = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:NULL];
// setting up the NSURLRequest
// [...]
dispatch_async(dispatch_get_main_queue(), ^
{
[NSURLConnection connectionWithRequest:request delegate:self];
});
}
// delegate functions, endBackgroundTask-closing, etc. is following
Unfortunately this is not working and I would like to know whether someone knows another way to fix it. There has to be a way which is similar like Snapchat or WhatsApp is doing it, because when you write an message and terminate the app right after pressing send, the message will be delivered.
The only way I could imagine is to do it with a background fetch but I think that is not the best solution, due to the fact that I just want to make one single connection when the App is send to the background.
I agree with Andy, that you should pursue NSURLSession and a background NSURLSessionConfiguration. See downloading content in the background section of the App Programming Guide for iOS: Background Execution.
By the way, the idea in your question will work fine (especially if you need support for iOS versions prior to 7.0, where NSURLSession and its background sessions are not available). Two observations regarding your code snippet:
The way you've written it, would appear that your AnObject would be prematurely deallocated when it falls out of scope and your app would therefore fail when it tried to call the delegate methods. Make sure to maintain a strong reference to AnObject.
Don't forget to call endBackgroundTask when the download is done. Likewise (and more subtly), the timeout handler should end the background task, too. See the Executing Finite Length Task section of the aforementioned App Programming Guide.
By the way, you mention requests continuing after the app is terminated. If a user manually terminates an app, that kills both background tasks contemplated in your question as well as background NSURLSession tasks. These are intended to gracefully handle continuing tasks if the app leaves foreground, not if the user manually terminates the app. The NSURLSession approach gracefully handles terminations due to memory pressure, but not manual termination.
I am developing an iPhone app which is using CocoaHTTPServer for making remote server communication.
The app will send the request details to the CocoaHTTPServer which will store the request locally. Once the internet connectivity is available, CocoaHTTPServer will send the request to remote server & will get the server response now CocoaHTTPServer has to send this response back to the app,
But I am confused how to implement it. Is there any inter app communication api for the same?
Any suggestions are greatly appreciated.
Well , I haven't workaround CocoaHTTP server classes so can't explain you verywell but I found there are couple of tutorials will surly guide you.
Thanks to Matt Gallagher for such a detailed article.
You can listen for a connection using NSFileHandle class
listeningHandle = [[NSFileHandle alloc]
initWithFileDescriptor:fileDescriptor
closeOnDealloc:YES];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(receiveIncomingConnectionNotification:)
name:NSFileHandleConnectionAcceptedNotification
object:nil];
[listeningHandle acceptConnectionInBackgroundAndNotify];
When receiveIncomingConnectionNotification: is invoked, each new incoming connection will get its own NSFileHandle. If you're keeping track, you can handle received message
if(CFHTTPMessageIsHeaderComplete(incomingRequest))
{
HTTPResponseHandler *handler =
[HTTPResponseHandler
handlerForRequest:incomingRequest
fileHandle:incomingFileHandle
server:self];
[responseHandlers addObject:handler];
[self stopReceivingForFileHandle:incomingFileHandle close:NO];
[handler startResponse];
return;
}
Note : please go through the full article, it has nice explanation.
Apart from this you may have look on this as well.
Hope this will give you some idea.
You question is focussing on background proces.
When an App goes into background, it get very limited time to finish things up. After that the App freezes in background. That is not a good situation to start communication.
Apple states clearly that the priority is always on the running foreground tasks.
The Notification mechanism (as listed by RDC above) is created to handle external events. During such a wake up you can send/receive a little bit of data, however you'll get minimal priority. Since timing is important in communication, I would not go for that either.
I suggest checking communication during the wakeup call and start activities then. And use the Notification mechanism to wakeup the user, that network is up again.
URL scheme can be used to send the response back to the app. The response from the remote server can be set as a parameter in the URL. The CocoaHTTPServer can invoke the other app which will be the handler of this unique URL. The below link provides more information on the same.
Inter-AppCommunication using URL scheme
Is it possible to handle a request timeout with a UIAlert? I would like to inform the user that there has been a time out. I set 0.0 just for testing to see if it would occur. The log does not print out so i do not believe i am handling correcting
request.timeoutInterval=0.0;
and to handle it:
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
if(error.code == NSURLErrorTimedOut){
NSLog(#"Time out");
}
}
I am using NSURLConnectionDelegate and NSURLConnectionDownloadDelegate
Thanks.
You can find a list of all the relevant error codes here. Note that -1009 is kCFURLErrorNotConnectedToInternet. To force a timeout, you need to have the ability to suppress the actual sending of the URL, or find one that just goes into a black hole.
Apple bundles Network Link Conditioner with Xcode (its in the networking tools I believe). There is a great article on this tool on NSHipster.
Another way (I believe) to get a timeout is to immediately after sending a request is to switch to another app, putting yours in the background. Wait 5 minutes, switch back, and look at the log. What you can do is in a demo app continually send out NSURLConnections - that is, once the first returns, send another. so there is always one outstanding. Now switch your app out - switch to another app in the simulator - wait, then return. You should be able to catch the condition this way, you can see the affect of changing the timeoutinterval value.
There was a long thread about timeouts in the private Apple forums for iOS networking - you may be able to find it. In the end, the system enforces a reasonably long minimum (as I recall), and you may be able to make it longer but not shorter. This hidden behavior has for sure baffled many people. The responder to the question was Quinn "The Eskimo", one of Apple's most experienced networking engineers.