How do I force an iOS app to use 3G instead of WiFi? - ios

I want to use a messaging protocol that works fine over 3G, but not over some corporate firewalls. How can my app force the use of the cellular network when it fails to connect over WiFi even in the case that the WiFi network is reachable?
EDIT: After reading through the implementation of the Reachability class I remain unsure whether the two are indeed mutually exclusive. It could well be possible to discover both interfaces via gethostbyname(), which I might try just to see what comes out.

Use getifaddrs to enumerate the network interfaces. Look for ifa_name that starts with "pdp." This will be the cellular interface (e.g., pdp_ip0). Test (ifa_flags & IFF_UP) to make sure the interface is up, and get the IP address from ifa_addr. Then use bind() to bind your socket to that address.
Not sure how you activate the cellular interface if it is not already up. Normally, I just make a high level http call to get iOS to wake up the network, but not sure under what conditions the cellular service becomes active when wifi is also available. I suspect it is usually there as a fallback to the wifi.

http://iphonedevsdk.com/discussion/comment/120957
From the looks of that thread, it seems like its impossible. Your best bet may be to try using the protocol, and saying something like "please disconnect from wifi and retry" if it doesn't work.
This thread says just about the same thing:
how to programatically start 3g connection on iphone?
You can use the Reachability class which apple wrote in order to test whether the network connection is currently over 3G or WiFi, and even to check if a specific resource is available on the network.

You can use any of the socket library to force to use cellular. Find the cellular interface from getifaddrs, pass the interface to the socket function, socket bind will happen with cellular. e.g. CocoaAsyncSocket

Related

Make an API call even if internet is not available

So I'm working on an IoT app, where I need to configure the hardware by connecting to its WiFi. If I use AFNetworking or URLSession to make the call, they fail with this error message.
"The Internet connection appears to be offline."
I need a way to make the API call to the local Wifi of the hardware by overriding the Reachability check. I'm sure there must be a way to do so, but I can't seem to come across something like that.
Reachability tells you if a certain host is reachable via Wifi. reachability.connection == .wifi test should be enough for that.
However, to know the reachability via that utility, you must know your host's IP within the wifi network, not internet.
What are all IP addresses that your host exposes? They will each belong to a network - internet, wifi, LAN and such.
What you probably need is the correct network utility to tell you that, for example, arp.

Specify Network Type for NSURLConnection & UIWebView

I realize I can determine what type of connection the app has access to within iOS (I have WiFi access; I have Cellular access). However, if I have access to both can I inform a NSURLConnection or UIWebView to use one or the other?
Ideally, a few specific calls I make with NSURLConnection would use cellular network, and a few calls and webview loads would use WiFi.
I don't believe this is possible, but I would like to know concretely.
This isn't something you can specify. It's entirely down to the OS as to which connection to use (cellular vs WiFi) and it will choose the best available based on a number of things like connection strength, WiFi assist etc.

Knowing status of a bonjour service

I'm using NSNetService and NSNetServiceBrowser to publish and scan for Bonjour services on the network. The implementation is working fine, the services are found on the network and they are capable of communicating. I'm currently trying to understand the framework's lifecycle and this what I've got so far:
// Scanning
netServiceBrowserWillSearch:
netServiceBrowser:didFindService:moreComing: // The device finds itself
// Advertising
netServiceWillPublish:
netServiceDidPublish:
This happens if I start the services with the adapter on. Now I need to know, at all times, whether the service is being actively advertised on the network; that is, if other devices are capable of finding it. So I test it with turning the Wi-Fi adapter off:
netServiceBrowser:didRemoveService:moreComing:
netServiceBrowser:didFindService:moreComing: // The device finds itself again, even after the adapter is turned off
Then I turn the adapter back on:
netServiceBrowser:didRemoveService:moreComing:
netServiceBrowser:didFindService:moreComing: // Yet again
The problem is that there is absolutely no difference in turning the adapter on or off, so I can't look for a pattern. Is there any other way that I can catch these events?
Edit: It gets worst. Even if I start the services with both adapters off (airplane mode) netServiceDidPublish: still gets called. So far it seems that netServiceDidNotPublish: is called only when I try to register the same service twice. This is very counter intuitive to me; maybe the service got published to the adapter, but not the network, and as such these callbacks are very misleading. At this point there is no way I can know whether the service is visible on the network.
For future reference, I needed to use workarounds to solve this. The problem is that Bonjour publishes its services to the protocol stack, so the adapter never gets queried for state. This makes sense, as Bonjour is a multi-transport protocol. In order to solve this I used an adaptation of Apple's reachability framework to listen to adapter state changes for Infrastructural Wi-Fi, at which point I query the adapter for the presence of the adwl0 interface for Wi-Fi direct support. Important note: that article claims to find support for general Wi-Fi connectivity which is not true; the awdl0 interface is the Wi-Fi Direct interface, which is why this will fail in devices such as the iPhone 4/4S. This is OK, because those devices do not support Wi-Fi Direct. As Bonjour also works with Bluetooth, I use CoreBluetooth to listen to Bluetooth adapter state changes. Although this framework is meant for Bluetooth Low Energy, I believe the Bluetooth adapter being on is a strong assurance that the Bonjour services are visible on the network. It's a bit unfortunate that Apple doesn't allow doing this without workarounds, but that's what we get, I guess.

How to make iOS believe there is Internet Connectivity

I am working on a web application for iOS that is going to be accesed from a local webserver in a network that has NO internet connectivity at all.
My problem is that everytime an iOS device is locked, it disconnects from the WiFi network, so when the device is unlocked again, it has to reconnect. Part of that reconnection process is determining if there is Internet connection (which there isn't). Until the process is not finished iOS does not allow any DNS resolution (so if I write http://10.0.0.1 it will go there, but not if I request http://something.local.com).
Since we control that network, we want to know how to does iOS verifies Internet connectivity so that we can fake the responses it expects.
I don't know if it's possible to resolve DNS without an internet connection on iOS, but if that's the case, that would be a way better solution since you don't need to mess with your router settings. Use my solution only if it really isn't possible with only code.
I'll suggest you to follow this guide: http://blog.jerodsanto.net/2009/06/sniff-your-iphones-network-traffic to check which actions your iPhone executes to detect an internet connection.
Using this information you could forward the is-there-internet-requests on your router to a local server which fakes the there-is-internet-responses.
This assumes Apple really uses an external server to detect this, which I'm not sure about. But it wouldn't hurt to give it a try!
Have you looked at the Reachability Class? You don't have to use the reachabilityForInternetConnection method, which checks if a default route is available. You can use the reachabilityWithAddress: method and check if your server is reachable.

How to specify that WiFi or 3G is required?

First of all, I know I can use UIRequiresPersistentWiFi to specify that my app needs WiFi.
But in fact, my app works fine with both WiFi or 3G. How can I express such a network requirement? It there any Info.plist key for this? Or is testing myself with Reachability the only way to go?
What exactly are you trying to do? If all you need is network access, then you don't have to do anything at all to make it work. Reachability can be useful, because it lets you quickly test if the network is available before attempting to connect anywhere, but that's only useful if you need to know if there is a network before even attempting to use it. Under normal circumstances, you can just attempt to use the network, and gracefully handle any errors that may occur.
My suggestion would be use SCNetworkReachabilitySetCallback (which gets called whenever the status of the network changes) and then throw an error if there's no WiFi or 3G connection.
b.t.w., all the keys UIKit supports in Info.plist can be seen at http://developer.apple.com/library/ios/#documentation/general/Reference/InfoPlistKeyReference/Articles/iPhoneOSKeys.html

Resources