Our app is using WLAN to communicate with a wireless device. When our app is installed in iOS 10. Sometimes, udp socket doesn't work. The reason for that is, in iOS 10 they added a new setting or permission under your app that allows the user to switch on or off the user of WLAN or cellular data.
The following would appear in the settings of the app:
When I tap on the Wireless... It will bring me to this UI:
After allowing WLAN use. The app would work fine.
Now, the problem is, sometimes, or in some devices running iOS 10, the settings that I just showed you doesn't appear(I am referring to the setting shown on the first image). So, is there anything I can do to make that settings always appear? It seems that sometimes iOS system doesn't recognize that my app is using wireless data. And it would result in my app would never get to use WLAN forever.
There is no user permission to use WIFI in iOS10.
The application in your screenshot (BSW SMART KIT) is using Wireless Accessories (WAC), i.e. is able to connect to wireless speakers. To accomplish this the Wireless Accessory Configuration capability is required. This is what you can dis/enable in the systems settings (your screenshot).
The switch in the settings shows up after connecting to a device via WIFI through WAC. You can see this behaviour in your sample app (BSW SMART KIT) too.
This sample code might let you get the idea. Detailed information in Apples documentation.
After a time of researching. I ended up seeking help with Apple Code Level Support. Apple states that this problem would most probably occur when you reskin your app. They say that probably it's because of the Image UUID of the main app and the reskinned app are the same. When you install both of them in your phone, the system will treat the reskinned app as the same app compared to the main app. So, if the one app fails to access WLAN, then it will also affect the other one. According to them, this appears to be a bug in iOS. And currently, they don't have any solution for the developers. This is the radar bug number:
What I did to somehow lessen the occurrence of the problem is to add tracking to the Network Restriction by using the following code.
- (void)startCheckingNetworkRestriction
{
__weak AppDelegate *weakSelf = self;
_monitor = [[CTCellularData alloc] init];
_monitor.cellularDataRestrictionDidUpdateNotifier = ^(CTCellularDataRestrictedState state)
{
[[NSOperationQueue mainQueue] addOperationWithBlock:^
{
NSString * statusStr;
switch(state)
{
case kCTCellularDataRestrictedStateUnknown:
{
statusStr = #"restriction status:Unknown";
}
break;
case kCTCellularDataRestricted:
{
statusStr = #"restriction status:restricted";
[weakSelf performUrlSession];
}
break;
case kCTCellularDataNotRestricted:
{
statusStr = #"restriction status:not restricted";
}
break;
default:
{
abort();
}
break;
}
NSLog(#"Restriction state: %#", statusStr);
}];
};
}
Take note that you have to import CoreTelephony to do this.
#import CoreTelephony;
when I detect that the network is restricted. I will open a URL session to force internet access attempt and would hope that the restriction alert dialog would pop out. Once the alert is pop out, then the WLAN Access Settings that I was talking about would definitely appear under the settings. There are times that this doesn't work. If it happens, then you'll just have to rename the bundle ID, and make a couple of changes to your code and then rebuild it a couple of times (Well, that's what I did when I was experimenting this). Reinstalling the app won't do a thing. Restarting and resetting the phone won't do either.
Hope this helps.
Related
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:
So I've done a lot of research and I haven't quite found the answer I have been looking for...
I want to make it so that, if cellular data is turned off for my app in particular, the app doesn't work or at least make it so a few buttons are hidden.
I have reachability put in place so that if there is no internet connection, the app does not run. However, if the user is connected to the internet but has cellular data turned off for my app, and they are not connected to wifi, then the app runs (which I do not want it to do).
Any help would be highly appreciated!
I had a similar problem and found this solution. Hopefully it helps.
func isCellularRestricted() {
let cellState = CTCellularData.init()
cellState.cellularDataRestrictionDidUpdateNotifier = { (dataRestrictedState) in
if dataRestrictedState == CTCellularDataRestrictedState.restricted { // State has changed
// your app doesn't have cellular data
} else if dataRestrictedState == CTCellularDataRestrictedState.notRestricted {
// your app does have cellular data
}
}
}
This is the function I implemented in my app delegate (called from didFinishLaunchingWithOptions). It gets called everytime user changes cellular data for your app and only for your app. If user allows cellular data for your app and has turned off cellular data in iOS, this will still return notRestricted.
In my case I made a constant which I set to true/false, depending if cellular data is allowed.
I found the answer somewhere on SO but I cannot find the link.
EDIT (found link): How do I know if cellular access for my iOS app is disabled?
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];
});
I am developing an iOS app which uses Location Services. The app works fine on iPhones (3GS and 4), but does not request locations services when run on the iPad. That is, the user is never asked to enable location services, nor does the app appear in Location Services section of the Settings app.
I have seen a few forum posts describing similar issues, but all of these were resolved by installing the app on the iPad through iTunes (via ad-hoc distribution) rather than running it through Xcode, or by resetting location warnings. I tried both of these solutions on an iPad, an iPad 2, and the iPad simulator, both to no avail.
We use the following code to enable Location Services updates. We have verified both by on-device debugging and placement of NSLogs that our code is getting run.
- (id)init {
[super init];
if (clManager == nil) {
clManager = [[CLLocationManager alloc] init];
}
clManager.delegate = self;
[self startLocationMonitoring];
return self;
}
- (BOOL)isLocationAvailable {
return [CLLocationManager significantLocationChangeMonitoringAvailable];
}
- (void)startLocationMonitoring {
if ([self isLocationAvailable]) {
[clManager startMonitoringSignificantLocationChanges];
} else {
// handle lack of Location Services
}
}
This probably does not answer your question, but might help somebody. The WiFi iPad models don’t support significant location changes (the significantLocationChangeMonitoringAvailable method returns NO) and the startMonitoringSignificantLocationChanges seems to do nothing when running on these – it does not trigger the error delegate method nor it brings forward the blue bubble dialog asking for location services permission.
iPad comes in 2 models (wifi and wifi+3G). http://www.apple.com/ipad/specs/
The wifi model does not have A-GPS support.
If yo go through the specification you will see that A-GPS is only available on wifi+3G model and perhaps you are using the wifi(only) model.
However, through WIFI, iPad can detect locations if router is providing the same which does not seems to be in your case.
You can try your app on iPad2 wifi+3G version and you will see that it runs just fine..!!