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.
Related
DefaultPath property of NEProvider class in iOS is used to represent current default network path used for connections created by the provider.
I am using this property to notify if interface changes from Wi-Fi to 3G or vice-versa using the KVO method. I am getting notification whenever there is change in interface but sometimes even though there is no change in interface I am getting the notification.
Do anyone face the same problem?
I tried to use Reachability class to detect network interface change but it is not working when VPN is connected.
It is already mentioned in Apple DOC-
NWPath contains the viability status and the properties of the path
that a network connection will take on a device.
For example, if the path status is NWPathStatusSatisfied, then a
connection attempt will be made.
When attached to a specific connection, a path takes all of the
connection parameters into account. For example, if the route for a
connection changes or is removed, the path will reflect that change.
Note that every path is evaluated within the context of the process it
is running in, and may be different across processes.
As you have mentioned you are tracking Wi-Fi to 3G or vice-versa , but there is no type of specific documentation check is the mobile is connected through 3G or WiFI or whatsoever.
This the following NWPathStatus you can track down,so it is quite sure that you are catching a wrong notification.
enum {
NWPathStatusInvalid = 0,
NWPathStatusSatisfied = 1,
NWPathStatusUnsatisfied = 2,
NWPathStatusSatisfiable = 3,
}
;
Here is a question that I know has an answer since I see apps that do this functionality. I have tried (writing directly, using background fetch) but nothing works. I found an app currently on the app store with the functionality that I am looking for. With Background Fetch set to OFF and main app NOT running in background. I go to the Widget and add an item. I open HealthKit and I see the data there as expected.
I would like to do the same for my app. I would like my today extension (widget) and/or WatchKit extension to write to the HealthKit store even when app is not running in background.
Like I said I have tested an app that does this functionality even though in Apple documentation it says this:
The HealthKit store can only be accessed by an authorized app. You
cannot access HealthKit from extensions (like the Today view) or from
a WatchKit app.
Because the HealthKit store is encrypted, your app cannot read data
from the store when the phone is locked. This means your app may not
be able to access the store when it is launched in the background.
However, apps can still write data to the store, even when the phone
is locked. The store temporarily caches the data and saves it to the
encrypted store as soon as the phone is unlocked.
Any answers or insights are appreciated. Thanks everybody.
The Health Data Store is indeed encrypted while the device is locked. Locked is defined as requiring a passcode on the device and the screen was turned off (so a passcode or touch id is required before you can get back to the main screen). While the store is encrypted it is not possible to read any data from it, no matter if the app is running in the background or not. Even setting up observer queries while the app is running will not allow it to continue to be read from. I imagine this level of protection is done simply using the Data Protection capability with the NSFileProtectionComplete option.
What HealthKit functionality have you observed in this other app? If it was displaying step and distance data, then they are likely getting this data directly from the pedometer (CMPedometer), which is not restricted when the device is locked.
Lehn0058's comment about authorization was correct. I had to request authorization explicitly from the WatchKit and Today Extension even though authorization was already given in the app. Afterwards both are able to write to the Health Store. The comment from Apple above only has to do with Reading from the Health Store and NOT writing to the Health Store. Here is some sample code for anybody else who gets in to the same problem. Thanks again.
In WatchKit InterfaceController.m
- (void)awakeWithContext:(id)context {
[super awakeWithContext:context];
// Configure interface objects here.
[[HealthKitManager sharedManager] requestHealthKitAccess];
}
In Today Extension TodayViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
[[HealthKitManager sharedManager] requestHealthKitAccess];
}
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
Although my app is usable without any internet connection, it may exchange data with a web server (in order to show some user statistics). So I advertise the app as "needs no internet connection". Some users subsequently have turned off cellular data for my app, which should be completely fine. But when my app tries to exchange data, these users are bugged with the "Cellular data is turned off for [App Name]." dialog.
This is an annoyance to them and I want to prevent these dialogs and simply skip the whole data exchange thing.
There is Apple's Reachability Sample Code.
But although I turned off WiFi for the whole device and cellular data for the app, Reachability confirms a positive internet connection. To be more specific, it reports
Reachability Flag Status: WR t------ networkStatusForFlags
no matter whether I activated cellular data or not. Of course, when cellular data is turned off, no internet connection is actually available, so the data exchange fails. But the user is presented with the cellular data dialog anyway.
Is there any way to detect whether a internet connection is available on iOS 7 and iOS 8, taking into account the cellular data setting for a specific app – all without bugging the user every time again with the cellular data dialog?
My app currently comes without any settings panel, so I want to avoid setting up a (second, in-app) switch "don't use cellular data". Also, I don't want to restrict data exchange to a WiFi connection since it's just a 2 KB of data per session which isn't a big thing for most users.
I think the only supported way in iOS8 is to send a Ping to a known server and bug the user with the alert panel a few times. On iOS8, Apple displays the panel only twice, then skips it even if the app is restarted, maybe it will show up a day later again. (This is really bad news for ad-supported apps.)
Apple says (https://devforums.apple.com/message/1059332#1059332):
Another developer wrote in to DTS and thus I had a chance to
investigate this in depth. Alas, the news is much as I expected:
there is no supported way to detect that your app is in this state.
Nor is there a way to make a "no user interaction" network connection,
that is, request that the connection fail rather than present UI like
this.
The following articles suggest ways to use ping:
http://www.splinter.com.au/how-to-ping-a-server-in-objective-c-iphone/
http://elbsolutions.com/projects/reachability-with-simpleping-wrapper/
Try using this git project.
How to install you can see inside the Readme on git.
I also used dispatch_once to be sure the Reachability will only be initialized once. This dispatch type is sometimes very useful!
Define variable in class
BOOL _online = NO;
Initialize the variable
static dispatch_once_t predicate;
dispatch_once(&predicate, ^{
Reachability *reach =
[Reachability reachabilityWithHostname:gameApiHost];
reach.reachableBlock = ^(Reachability*reach) {
NSLog(#"REACHABLE!"); _online = YES;
};
reach.unreachableBlock = ^(Reachability*reach) {
NSLog(#"UNREACHABLE!"); _online = NO;
};
[reach startNotifier];
});
iOS Multipeer Connectivity question...
My app uses MCNearbyServiceBrowser and MCNearbyServiceAdvertiser (but not simultaneously on a given device).
My MCNearbyServiceAdvertiser always uses the same PeerId ... I store it in NSUserDefaults, per the 2014 WWDC session's advice on this.
When another device is browsing for services, the browsing device gets a foundPeer browser delegate callback, as expected.
However, if on the browsing device I switch away from my app (e.g., via a Home button tap) and then switch back to my app, I get another call to foundPeer for the advertising device, but this time the PeerId is different!
This seems odd, because my advertiser always uses the same PeerId.
Any ideas why this might be happening? Unexpected?
(I was planning to see if a newly-found advertising device with a given PeerId is already in my table view of advertisers, but the above issue kind of messes up that plan.)
Thank you.
-Allan
From the apple docs: “ The Multipeer Connectivity framework is responsible for creating peer objects that represent other devices.”
After pressing the home button and switching back to the app the framework has created a new PeerID object to represent the advertising device. This is another object than the previous one, even though it represents the same advertising device. So you can not rely on PeerID object equality.
To identify peer correctly I suggest you should create an NSUUID string and archive it on disk and reuse it. When you initialise MCPeerID object the display name you should be passing would be displayName+UUID. Use display name for UI elements and UUID for identifying peer.
I hope it helps.
You will not get the same MCPeerID when you create two from the same display name. This ensures uniqueness when you have a name collision. It's common to use the device name as the display name. Not everyone personalizes theirs.
If you want to recognize and be recognized by previously connected peers, then you must save and retrieve the actual MCPeerID.
To see what I mean, paste the following code into a playground and run it.
import MultipeerConnectivity
let hostName = "TestPlaygroundHostName"
let firstPeerID = MCPeerID(displayName: hostName)
let secondPeerID = MCPeerID(displayName: hostName)
firstPeerID.hashValue == secondPeerID.hashValue