DefaultPath property of NEProvider Class in iOS - ios

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,
}
;

Related

Checking if external accessories in iOS can start an EASession

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.

When using NEHotspotConfiguration() to joing a network. Always recieve "unable to join"

I am implementing an APP through Xamarin that will force the iPhone to connect to a specific SSID.
Here is my code
var config = new NEHotspotConfiguration(SSID, Password, isWep: false)
config.JoinOnce = true;
var tcs = new TaskCompletionSource<NSError>();
NEHotspotConfigurationManager.SharedManager.ApplyConfiguration(config, err => tcs.SetResult(err));
There are two test result
Assume the target SSID I want to connect called "SSID-A"
I delete the record of "SSID-A" in the iOS system page. Then deploy this APP to the phone.
I give the correct SSID/Password into the code above.
The system popup a message "Unable to join". Failed to connect to this SSID.
I go to iOS system page. Manually connect to "SSID-A". Check the connection is done.
Then I connect to mobile phone to other SSID. And go back to the APP.
This time. It works.
Why there is a different at here?
What can I do to look more into this problem to solve this?
Thanks!
From applyConfiguration:completionHandler: of apple document, we could see the dicsussion that:
This method attempts to join the network only if it's found nearby. Also, because of the noticeable delay that the Hotspot 2.0 discovery mechanism may incur, the method doesn't attempt to join Hotspot 2.0 networks.
Therefore, Join once seems can not mark sure it works.We could have a try with remove this line, or set false as follows:
config.joinOnce = false;
In addition, it seems a bug in iOS 13 from Apple. You could have a look at this discussion.
In case it helps someone one day I had an issue suddenly start where my device was unable to join a network which has previously been working fine.
To test an error condition I tried connecting to a secure network without adding the passphase to the NEHotspotConfiguration. This attempt to join failed but then subsequently after putting the passphase back in the device wouldn't even display the join request dialog and would just fail with an 'Unable to join' error.
I eventually tried another test phone and this worked just fine so it seemed to be an issue on the device itself. I tried resetting my device's network settings but this made no difference.
The fix was to go into the device's wifi setting, join the network manually, return to my app, attempt to join, disconnect from the network in the device settings, and then forget the network. After that it worked fine on every subsequent attempt.

NEHotspotConfigurationManager unable to Join

I am using NEHotspotConfigurationManager to connect wifi programmatically.
Its an open Network without any password
I am using below code:
if (#available(iOS 11.0, *)) {
NEHotspotConfiguration *config = [[NEHotspotConfiguration
alloc]initWithSSID:SSIDName];
[NEHotspotConfigurationManager.sharedManager applyConfiguration:config completionHandler:^(NSError* error) {
if (error) {
printf([error description]);
}
else
{
printf(#"success");
}
}];
I am connecting to the hostpot/access point of one device, but every time I am getting an error of unable to join but in response it goes to success as the error is nil. Is there any thing I need to add in configuration or add in setting or am I missing anything ?
PS: The wifi is of IOT device
-Thanks in advance
This is an Apple bug in iOS.
Once the request has made it past the Network Extension framework, down to the Wi-Fi subsystem, errors are displayed to the user rather than delivered to your completion handler.
see https://forums.developer.apple.com/thread/96834
What you can do as a workaround:
try to connect
if the error is nil, you may be connected (due to the above mentioned bug this is unclear)
then check which network you are connected to
For more information, including sample code, see my answer here: https://stackoverflow.com/a/56589229/2331445
Additional Hints
Entitlement
Like #zero3nna already mentioned in the comments, the Hotspot Configuration entitlement must be added.
Check SSID name
Make sure you spelled the SSID correctly. I made a test with a NanoESP IoT device and for my device the definition would look like this:
NSString *SSIDName = #"NanoESP";
If you are using a non-existent SSID name (e.g. NanoESP2), there is a dialog that says that it is not possible to join the network.
Due to the above mentioned error you will get a success message in the console of Xcode, which is of course wrong:
Delayed Wi-Fi indicator
I have noticed that using the correct SSID works, but it takes quite some time for this connection to my specific IoT device to appear with the typical icon in the iOS status bar. For some time it's just not shown, see screenshot. To check the status anyway, go to iOS Settings / Wi-Fi:

I want to Show a banner in iOS app when network not reachable and disable apps functionality until it is reachable again . Is it possible?

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

MCNearbyServiceBrowserDelegate foundPeer reports different PeerId for a given advertiser device when switch out, then switch back on browsing device

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

Resources