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.
Related
I'm trying to get a list of available connected peripherals on iOS. What I do first is grab everything that's connected from the sharedAccessoryManager.
But now I want to filter by accessories that are available for my specific protocol and are not currently in session with another app.
The goal is to have multiple apps that can connect to the same kind of accessory, but I want to avoid attempting to start a session with accessories already in session with one of the apps in the background.
Would the best way to do this just open an EASession for each relevant device and immediately closing it, noting whether or not the initWithAccessory returns nil? e.g.
for (EAAccessory *accessory in [[EAAccessoryManager sharedAccessoryManager] connectedAccessories])
{
EASession *session = [[EASession alloc] initWithAccessory:accessory forProtocol:#"myprotocol"];
if (session) {
// close the EASession
session = nil;
// do stuff to save the accessory and report
// to the user that it is available to have a session started
}
}
Are there any problems that might arise from testing session opens for every device? Do I need to clean up the input/output streams too?
The problem seems to be that I start communicating with the accessories, which I don't want to do, instead just check if they're available.
This is exactly what you should get by checking the protocolStrings list. Your Info.plist key will have already filtered out any accessories you don't support at all. But protocolStrings indicates the list of protocols the device is ready to support now:
When deciding whether to connect to an accessory, you should always first check the accessory’s declared protocols in the protocolStrings array. This list indicates the types of data the accessory is capable of processing at that moment, which may not be the full list of protocols for which the accessory is designed. For example, an accessory that is connected but not yet authenticated will report no supported protocols until authentication is successful. Don't connect to the accessory unless and until the list includes the protocol you intend to use.
It's up to the device to accurately report the list of protocols it currently supports. In my experience, constructing an EASession does not guarantee that the session is actually available. For example, if you try to create two sessions to the same device and the same protocol in the same process (which is invalid), the session will be created, but you'll see errors when you try to create the streams.
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'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.
I am developing an iPhone application that has to synchronize with phone call statuses. I am currently playing audio and/or communicating with an external accessory so my application can run in background. So when there is a phone call i show a view telling the user that a phone call is taking place. I am using the CTCallCenter (using an instance variable in my appDelegate) to respond to phone call statuses and i am allocating that instance every time i test if there is a phone call or not. Bur when allocating that instance each time , sometimes i get a strange crash concerning "CUTWeakReference" class. i want to know if there is a best practice for using CTCallCenter when application is in background or is suspended to prevent that crash from happening ? Thanks
Points to consider to fix the issue.
There is an iOS bug that causes instances of the CTCallCenter class to sometimes get notifications after they have been deallocated. Instead of instantiating, using, and releasing instances you must instead retain and never release them to work around the bug.
The [[CTCallCenter alloc] init] must be run in the main queue. Is it thread safe ??? Better call it on main thread only.
static CTCallCenter *netInfo; static dispatch_once_t dispatchToken;
if (!netInfo) {
dispatch_once(&dispatchToken, ^{
netInfo = [[CTCallCenter alloc] init];
});
}
I'm very simply trying to be notified when a Pass is added to a passbook.
One thing to note is that I'm also attempting to use a Pass that I generated using a different apple dev account than what my app is using. I'm trying to figure out if that is part of the problem or not.
This is one VC in a 3-tab application.
ViewController.m:
#interface ViewController ()
{
PKPassLibrary *_passLibrary;
NSArray *_passes;
}
#end
"viewDidLoad":
- (void)viewDidLoad
{
//init passbook
_passLibrary = [[PKPassLibrary alloc] init];
_passes = [_passLibrary passes];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(passLibraryDidChange:) name:PKPassLibraryDidChangeNotification object:_passLibrary];
}
and my notification handler:
- (void)passLibraryDidChange:(NSNotification *)notification
{
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"%#", #"passes added");
});
}
When I run the app in the iOS Simulator, all works as expected and I can see the log output to the console in Xcode.
When I run on the device, the notification is not called when the Pass is added. On the device, I can't even list any passes.
What's even more strange, is that when I go to delete the Pass from passbook, then re-enter the app, the notification will be called.
ps: I really hope it's something simple I'm missing here.
EDIT: updated with more information and a more complete code sample
You don't seem to be retaining a handle to your PKPassLibrary instance. Create a strong #property on your UIViewController subclass. Alloc+Init the property and configure the notification listening in the viewDidLoad (so it'll only get done once).
It might be more appropriate to set this on the UIApplication somewhere, but that depends on the app logic and how your ViewController fits into your UI (for example does it get replaced or released while you still need access to PassKit notifications?)…
Nick.
P.S. Any reason you're NSLogging on the GCD main queue so explicitly? Is that just left over from some UI feedback?
P.P.S. Seeing the notification when you re-enter the app makes perfect sense since that's when viewWillAppear will get called and a new PKPassLibrary instance will immediately fire the notification that's waiting. Not sure why it worked in the simulator - must be accidental.
It came down to having the pass type identifiers created from the same source that the provisioning profile came from.
I then had to take the new Pass Certificates and add them to my keychain, then re-create the actual passes using the new pass type identifiers.
Meanwhile, In Xcode..in the summary section of my target in the Entitlements section, I can refresh the Passes and see the new pass type id's come in.
Once the app runs, I now get properly notified of any change (addition/subtraction/etc).
I also needed to make sure my _passLibrary was being properly retained.