NSNetServiceBrowser issues on iOS - not detecting all changes - ios

I think this may be an iOS bug.
I run the following simple code, to broadcast a net service:
self.netService = [[NSNetService alloc] initWithDomain:#"local."
type:#"_testnetwork._tcp."
name:[[UIDevice currentDevice] name]
port:port];
self.netService.delegate = self;
[self.netService publish];
On another device, I run this code, to detect net services:
self.netServiceBrowser = [[NSNetServiceBrowser alloc] init];
self.netServiceBrowser.delegate = (id)self;
[self.netServiceBrowser searchForServicesOfType:#"_testnetwork._tcp." inDomain:#"local."];
What proceeds to happen is that the browser will detect when the net service appears for the first time, and then when the service goes away, most of the time it won't detect it, and still thinks the NSNetService is still broadcasting. Interestingly, the apps can be stopped on both devices and the browser can be started and will still detect the old service as being currently broadcast - it hangs around.
Using this code in terminal on mac shows correctly when the services appear and disappear:
dns-sd -B _test._tcp
Any solution to this problem? Is this an iOS bug?

Related

iOS 10 - WLAN Access Setting Doesn't Appear In Some iOS Devices

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.

NSNetService Not Resolving

I have an existing application (actually a pair of iOS applications) which have a very basic Bonjour implementation so the 'client' can find the 'server' on the network. This worked fine until a recent test (I'm not completely for sure that this is related to iOS8, but this is the first time I've compiled these apps with iOS 8). In this case, all works as expected until the client goes to resolve the address of the server. At this point, it always times out. Also, I've tried a few Bonjour discovery apps from the app store and they timeout also (so I'm assuming this is a problem with the server code).
In the client application my NSNetServiceDelegate methods are called in this order:
netServiceWillResolve:
(then it waits the full timeout period that I specified in resolveWithTimeout:)
netService:didNotResolve:
This code has worked flawlessly in the past. I'm not entirely sure what would cause this behavior.
Here is the code for publishing the service:
- (void)initializeServerServiceStatus
{
NSString *deviceName = [[UIDevice currentDevice] name];
NSString *serviceName = [NSString stringWithFormat:#"iPad Server - %#", deviceName];
self.serverPresentationService = [[NSNetService alloc] initWithDomain:#"local."
type:SERVICE_NAME_IPAD_SERVER
name:serviceName
port:self.port];
self.serverPresentationService.delegate = self;
[self.serverPresentationService publish];
self.isPublishing = YES;
}
I have verified that the netServiceDidPublish: method is called and that the netServiceDidStop: is not called.
UPDATE: I have verified that if the service is published on an iOS 7 device, all works as expected (even if the resolving device is on iOS 8).
UPDATE 2: I have verified that Apple knows of some problems with Bonjour in the iOS 8 release: https://devforums.apple.com/message/1045870#1045870

GCDAsyncUdpSocket on iOS missing multicasted datagrams

I have a device on the network that is multicasting a very small file via UDP. The iOS app I am developing is responsible for reading these packets and I have chosen to use GCDAsyncUdpSocket to do so. The file is sent every half second, however I am not receiving it nearly that often (only receiving about every 3-10 seconds).
Thinking that it may be an issue with the device, I began monitoring the traffic with Wireshark. This appeared to reflect what I was seeing in my app until I enabled "Monitor Mode" in Wireshark, at which point every UDP packet was being captured. In addition, the iOS simulator began receiving all of the missing packets since it shares the NIC with the Mac I am developing on.
Is there a way to enable "Monitor Mode" on an iOS device or something I am missing that would allow the missing packets to come in? I also see that there is a readStream method in GCDAsyncUdpSocket. Perhaps I need to use this instead of beginReceiving? Though I do not know how to set up streams in Objective-C if that is the case.
Here is my test code as it is now:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSLog(#"View Loaded");
[self setupSocket];
}
- (void)setupSocket
{
udpSocket = [[GCDAsyncUdpSocket alloc] initWithDelegate:self delegateQueue:dispatch_get_main_queue()];
NSError *error = nil;
if (![udpSocket bindToPort:5555 error:&error])
{
NSLog(#"Error binding to port: %#", error);
return;
}
if(![udpSocket joinMulticastGroup:#"226.1.1.1" error:&error]){
NSLog(#"Error connecting to multicast group: %#", error);
return;
}
if (![udpSocket beginReceiving:&error])
{
NSLog(#"Error receiving: %#", error);
return;
}
NSLog(#"Socket Ready");
}
- (void)udpSocket:(GCDAsyncUdpSocket *)sock didReceiveData:(NSData *)data
fromAddress:(NSData *)address
withFilterContext:(id)filterContext
{
NSString *msg = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
if (msg)
{
NSLog(#"RCV: %#", msg);
}
else
{
NSString *host = nil;
uint16_t port = 0;
[GCDAsyncUdpSocket getHost:&host port:&port fromAddress:address];
NSLog(#"Unknown message from : %#:%hu", host, port);
}
}
Solution for anybody who comes looking here in the future:
Based on ilmiacs's answer, I was able to significantly reduce the number of missing packets by pinging the target iOS device. Using a Mac, I ran this in the terminal -
sudo ping -i 0.2 -s 4 <Target IP>
Now that I have it running with a Mac pinging the iOS device, I am going to look into Apple's iOS ping examples and see if I can have the device ping itself to stimulate its own wireless adapter (127.0.0.1).
Through my work on networking apps for iOS devices I have revealed that their network adapters have two different modes, let's call them active and passive. I did not manage to find any documentation on this. Here are my findings:
As long as in active mode, the adapter is quite responsive. I have response times of 3-5ms.
After some time of inactivity, the network adapter of the iOS falls from active to passive mode. The time for this to happen, depends on the actual device model. 3rd gen iPad it is around 200ms. For the iPhone 4 its more like 50ms.
A ping request or a TCP packet will move the adapter from passive to active mode. This may take anything from 50ms to 800ms, with an average of around 200ms.
This behavior is completely reproducible by issuing ping commands. E.g.
ping -i 0.2 <ios-device-ip>
sets the ping interval to 200ms and keeps my iPads network adapter in the active state.
This behavior is completely consistent with your observations, if the adapter (more often than not) ignores UDP packets when in passive mode. The wireshark activity probably keeps it in active mode so then it would get the UDPs.
Check out whether the ping trick helps.
One probably could keep the network adapter of the iDevice in the active state by opening and connecting two sockets on the device itself and regularly sending itself packets. This would introduce some minimal overhead.
As to why apple decided to implement such a feature, I can only speculate about. But probably keeping the adapter active costs sufficient battery power to legitimate such a design choice.
Hope this helps.
I had the same issue.
Starting the network activity indicator solved that problem for me:
UIApplication* app = [UIApplication sharedApplication];
app.networkActivityIndicatorVisible = YES;
If you want to view the packet on the iOS device, you can tether your iOS device to a Mac and mount the wifi adapter using shell command rvictl. You can then use wireshark, tcpdump, etc. to monitor the traffic on your 802.11 interface on your iOS device.
As far as not receiving data until 3 to 7 seconds - Most likely your device is entering into power save mode (IEEE PSM) which is an 802.11 feature that essentially puts the wireless NIC to sleep.
PSM mode can yield poor performance on devices - especially in your case where you have periodic bursts of data every 1/2 second. Your periodic ping is waking the NIC up.

Location Services will not activate on iPad

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..!!

Getting the HostName of a Remote iOS Device with NSNetService

After more than a few hours of searching, I got in what looks like a dead end. In this case, all that I am trying to do, is to get all the iOS Devices of the network with Bonjour. I did so like this
self.serviceBrowser = [[NSNetServiceBrowser alloc] init];
[self.serviceBrowser setDelegate:self];
[self.serviceBrowser searchForServicesOfType:#"_apple-mobdev2._tcp." inDomain:#"local."];
This works fine, though what I get is the following:
local. _apple-mobdev2._tcp. [MAC ADDRESS HERE]
I tried to resolve the connection by using the sync port (62078), since service.port returns -1.
for (NSNetService *service in self.services) {
NSLog(#"%#", service);
NSNetService *newService = [[NSNetService alloc] initWithDomain:service.domain type:service.type name:service.name port:62078];
[newService setDelegate:self];
[newService resolveWithTimeout:30];
}
This in its own turn calls netServiceWillResolve: with no problem at all, but, it doesn't make it to netServiceDidResolveAddress:
But neither does this fail. netService:didNotResolve: isn't called either, I believe it is just waiting for a response to be resolved.
To support this claim, once it did make it to the method and actually [service hostName]; did return Yanniss-iPhone, but that happened at a completely random time that I had left the Mac App running for around half an hour. What could have invoked this to run? Or does anyone know of a different way to get the hostName of the remote device? The other answers do not answer my question, since I am looking for the hostName of the remote device, not of the Mac device.
Relative to that, I've found that when you kill and restart iTunes, along with iTunes Helper, the very log I mentioned below is sent again. Which is why I believe the correct log was an iTunes related event. Any help is very much appreciated!
iTunes search bonjour for wifi sync capability. As for the didNotResolve or resolve delay, bonjour services randomly cast itself anywhere between a few seconds to 30 minutes.
I am actually trying to connect to iOS devices too, but I could not get any response or any devices returned. :\

Resources