I've seen plenty of examples here on how to check an internet connection is available but nobody seems to explain the best practice way of doing something once/ if it becomes available.
I'm using Tony Million's Reachability class and have a 'connection available' block that sets a boolean (Online) to true once the connection is available. The Reachability class is initialized in my app delegate didFinishLaunchingWithOptions but by the time my code checks the status of Online Reachability still hasn't finished figuring out if there's a connection, and so my app always sees itself as being offline when it first starts.
Now, I could put my connection-requiring code into the 'connection available' block but there's more than one place my app needs the internet so that's obviously not flexible enough for my needs.
The 'best' idea I had so far is to populate an array with methods that need the internet to do their thing and then have Reachability execute whatever is in that array once it knows there's a connection... but am I over-complicating matters here? Is there a better way of going about this?
This is roughly based on your "best idea". Tony Million's Reachability also posts notifications via NSNotificationCenter when the internet connection changes. All your classes that need to do something when an internet connection becomes available, should register for this notification.
On the GitHub page there is an example for that: https://github.com/tonymillion/Reachability#another-simple-example
You would initialize the Reachability class in your app delegate, like you do right now. And then your other classes register for the kReachabilityChangedNotification notification with the NSNotificationCenter in their initializer. They also have to deregister from the NSNotificationCenter in their dealloc method.
Here is some code you can use as starting point:
- (void)registerForReachabilityNotification
{
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(reachabilityChanged:)
name:kReachabilityChangedNotification
object:nil];
}
- (void)deregisterFromNotifications
{
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
- (void)reachabilityChanged:(NSNotification *)notification
{
Reachability *reachability = notification.object;
switch ([reachability currentReachabilityStatus]) {
case NotReachable:
// No connection
break;
case ReachableViaWiFi:
case ReachableViaWWAN:
// Connection available
break;
}
}
Related
When game controller be paired from system setting, all is fine.
But I want to discover & pair game controller in my app.
Actually I found that it seems feasible by Apple's docs.
doc link : Discovering and Connecting to Controllers.
I have a game controller which is in pairing...
But I found the log of function "startWirelessControllerDiscoveryWithCompletionHandler" never be shown.
Seems the behavior does not conform.
I call "startWirelessControllerDiscoveryWithCompletionHandler" when app load...
I also call "stopWirelessControllerDiscovery", but still same.
- (void)viewDidLoad {
...
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(gameControllerDidConnect:) name:GCControllerDidConnectNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(gameControllerDidDisconnect:) name:GCControllerDidDisconnectNotification object:nil];
[GCController startWirelessControllerDiscoveryWithCompletionHandler:^{
NSLog(#"Finished finding controllers");
[self completionWirelessControllerDiscovery];
}];
...
}
- (void)completionWirelessControllerDiscovery {
if (isDebug) {
NSLog(#"%s-%d", __FUNCTION__, __LINE__);
}
}
Someone has experiences on this?
My experience has been that it does not work for pairing a controller - I have always had to pair my game controllers directly to the device. I've been developing a wrapper around GCController, so I've tested about 5 different MFi controllers with no luck getting GCController to manage the pairing process.
The issue has been discussed here and here, but I wonder if there is a more solid way to solve this whether you have delegates or not - when a function is called after a delay.
At a certain point in a program, at a button push, an object - a CCLayer - is created. That layer creates several objects, some of them at callbacks. That created object layer has a "back" button which destroys it. I am running into a problem when the callbacks, etc are triggered AFTER that object is destructed and try to access objects that don't exist anymore - where the "message sent to deallocated instance 0x258ba480" gives me this good news. How do I avoid that?
1) Is there a way to kill the callbacks (because I obviously don't need them anymore)
2) should/can I test for the existence of these possibly non-existent objects at the callbacks themselves
3) something else?
(My callback is code for checking for an internet connection that I copied from this illustrious website - may it live long and prosper-, using Reachability, and I could solve the problem by simply moving it to the main view and setting a flag on the child view, but I don't want to.)
- (void)testInternetConnection
{
internetReachableFoo = [Reachability reachabilityWithHostname:#"www.google.com"];
// Internet is reachable
internetReachableFoo.reachableBlock = ^(Reachability*reach)
{
// Update the UI on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"Yayyy, we have the interwebs!");
//I do the net stuff here
});
};
// Internet is not reachable
internetReachableFoo.unreachableBlock = ^(Reachability*reach)
{
// Update the UI on the main thread
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"Someone broke the internet :(");
noNetMessageLabel.visible=true; //<-------this goes kaboom
noNetFlag=true;
});
};
[internetReachableFoo startNotifier];
}
There are basically two ways to avoid deallocated delegates from being messaged:
Hold onto the objects you want to message later. That way they won’t get deallocated. This is the case with block callbacks – if a block references some object, the object gets retained until the block ceases to exist. If you message some objects from a block and hit a deallocated object, you must have screwed up the memory management somewhere.
Clear the delegation link before you release the delegate. Nowadays this is usually done using weak, zeroing properties that are automatically set to nil when the referenced object is deallocated. Very convenient. Not your case.
You might consider several options:
First, you may just check for existence of an object before passing message to it:
if (noNetMessageLabel)
noNetMessageLabel.visible = true;
But personally I consider that as a bad architecture.
More wise decision, from my point of view, would be move the code of displaying any alert regarding internet connectivity to the model.
Create method like this in AppDelegate or in the model:
- (NSError*)presentConnectivityAlert
{
if () //any error condition checking appropriate
[[NSNotificationCenter defaultCenter]
postNotificationName:#"connectivityAlert"
object:self
userInfo:userInfo];
}
Also you may consider moving internet checking code to the model too.
In the ViewControllers of your app implement listening to this notification.
- (void)viewDidLoad {
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(didReceiveRemoteNotification:)
name:#"connectivityAlert"
object:nil];
}
- (void)viewDidUnload {
[[NSNotificationCenter defaultCenter]
removeObserver:self
name:#"connectivityAlert"
object:nil];
}
-(void)didReceiveRemoteNotification:(NSDictionary *)userInfo {
if (self.isViewLoaded && self.view.window) {
//Present the user with alert
}
}
Thus you have more general and quite versatile approach to handle connectivity issues throughout all your application.
Is there a way to kill the callbacks
It's not possible to cancel block (in your case), but it's possible to cancel NSOperation in NSOperationQueue. But that will require to rewrite your implementation of Reachability.
Could somebody give me guidance/starting point on how I would update a UI Text Label in an IOS app from a library.
I have temperature data being received from a BT module in a library connected to my app. I want to send that Integer data to my app and update the UI Text label.
NOTE: I have full access to the library.
Any help is appreciated
I think what you are asking for is a way to provide feedback to an application from your Cocoa static library.
I would suggest that you have a look at the NSNotificationCenter class. For example, say that you have a class BTThermometer that, upon the reception of a new measurement calls:
[[NSNotificationCenter defaultCenter] postNotificationName:#"com.my.BTThermometer.NewValue" object:self];
Then, in you application, you could do something like:
[[NSNotificationCenter defaultCenter] addObserverForName:#"com.my.BTThermometer.NewValue" object:self queue:nil usingBlock:^(NSNotification* n) {
dispatch_async(dispatch_get_main_queue(), ^{
someLabel.text = ((BTThermometer*)n.object).temperatureValue;
}
}];
This is a standard mechanism in iOS to decouple your application from the internal workings of your library. The only coupling is the name itself, and of course it is usually a good idea to use a constant (e.g. #define kMyTemperatureEvent #"com.my.BTThermometer.NewValue) so that the compiler catches any typos.
I have an 'internet aware' base class for objects which require networking in my app. All the objects which need to be internet aware inherit from it. As you can imagine I allocate and deallocate a lot of these objects.
The internet aware base class has the following code to interact with the Reachability classes used to check for internet status.
#import "Reachability.h"
- (id) init {
...
self.internetReachable = [Reachability reachabilityForInternetConnection];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(checkNetworkStatus) name:kReachabilityChangedNotification object:nil];
[self.internetReachable startNotifier];
...
}
- (void) dealloc
{
[self.internetReachable stopNotifier];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
As soon as there is a change in internet status in my app, the app crashes with the following error:
*** -[Reachability isKindOfClass:]: message sent to deallocated
instance 0x1e249a30
Ive turned on zombies and tracked the problem down to the following line of code within Reachability.m
NSCAssert([(NSObject*) info isKindOfClass: [Reachability class]], #"info was wrong class in ReachabilityCallback");
Unfortunately, beyond stopping listening for NSNotifcations and stopping the notifier, im not sure what else my objects can do to avoid this error.
Any help or suggestions would be great.
Thanks
Vb
Edit:
OK so following the advice of the answer below I ran it in instruments with allocations
and this was the retain count history.
It is as I suspected, its an object that I have deallocated being called by Foundation (ie. NSNotifcationCenter) and not myself.
My internet objects have a strong pointer to a Reachability object. When they are deallocated, so is the Reachability object. The zombie is the Reachability object.
In the dealloc of my internet object I have called removeObserver, but foundation is still calling the deallocated object. I can't understand why...
The reason that Foundation was still sending the deallocated Reachability the NSNotifcations was because the Reachability object was being deallocated on a different thread to the one it was being created on ie. Reachability isn't thread safe. Using dispatch_async back to the same queue that the Reachability object was being created on has solved the problem.
This happens when the object you made that instantiates Reachability, and hence holds a reference to a Reachability instance, is deallocate without (or before) you call stopNotifier on it!
Solving this is very simple. You must call stopNotifier before your object gets removed from the stack tearing down your Reachability instance with it. You can do this in the dealloc method, or if it's a viewController, you could call it in one of the lifecycle methods such as viewDidDisappear, etc.
There should be no need of messing with threads here. Consider, when you call startNotifier on Reachability this thing starts on a background thread by design of Reachability. So, when you call stopNotifier it takes care of threading for you.
The reason you're having to mess with threads has to do with the fact that your object holding the reference to Reachability got deallocated but was still a registered listener for network changes, which happened with startNotifier. When the network does change, guess what, your object, although still registered to receive notifications, is nowhere to be found! Crashy crash. stopNotifier unregisters it before it dies and everything is good.
- (void)dealloc
{ // self.hostReachability is my property holding my Reachability instance
if (self.hostReachability) {
[self.hostReachability stopNotifier];
}
}
The NSCAssert line is where you first access the deallocated object, but if you want to know more about the object's life cycle you should use Instruments. Use Xcode's Profile tool to run your program in the simulator with the Allocations (but not leaks!) tool. In the allocations tool's launch configuration turn on Enable NSZombie Detection and Record Reference Counts. When you hit the NSCAssert line Instruments should detect the attempt to message the zombie info object and make a note of it. If you look at the detailed information for the zombie info object Instruments will show you its history of reference counts and you should be able to see when it was deallocated.
I am trying to make my iPhone app more robust, by ensuring that it doesn't crash when there is no network connection. Right now the app attempts to make a connection on startup immediately, through the app delegate. It has no issues if wifi or cellular is available, but it will crash if there is no network connection visible. I have looked around on this site and haven't found anything that quite fits my problem. I have the feeling it should be just a simple line of code, like an objective-c equivalent of a pseudo- 'isConnection', or something similar:
if (isConnection) {
- sendSynchronousRequest for json data I'm using
- manipulate the data, etc., and continue with normal operations
} else {
- send an output message to a view controller,
letting the user know what's wrong.
}
I can't seem to isolate the (admittedly abstract) "isConnection" condition that I'm looking for, specifically. Does anyone have experience or advice with this topic?
The reachability class is very easy to use. Download the class files here https://developer.apple.com/library/ios/#samplecode/Reachability/Introduction/Intro.html
You also need to add the SystemConfiguration.framework
Here's the code you need:
-(BOOL)isConnection {
Reachability *reach = [Reachability reachabilityWithHostName:#"www.google.com"];
//replace www.google.com with your own host you're checking for
NetworkStatus hostStatus = [reach currentReachabilityStatus];
if (hostStatus != NotReachable) {
//There are also other status enums like
//ReachableViaWiFi
//ReachableViaWWAN (3G/LTE)
//if you need to detect if user is on cellular, act accordingly
return YES;
}
return NO;
}
Then you can call your method:
if ([self isConnection]) {
//do something
} else {
//no connection, inform user
}
You can use the Reachability class that Apple provides in the Reachability sample application. Not only does it tell you if you're connected, but how you're connected (WiFi, cellular or no connection at all). You can even register for a notification when the connection status changes.
Even though you can use that to check the connection status before initiating the code you describe above, I still think you should investigate why the app crashes when you have no connection, and attempt to resolve it. You could always lose connectivity in the middle of a request attempt, and you don't want that to crash the app either.