What network protocol does Reachability use on iOS? - ios

I am sure there is little % of iOS developers who haven't used Reachability, directly or via some framework like Alamofire.
What I am interested what it actually does? The best guess I can make is that given host it opens sockets and then listens for said host. But what network protocol does it use, is it simple UDP (it is not http as far as I can observe), where it periodically sends packages to said host and awaits answer?

Reachability sends no packets at all. It doesn't even tell you that a host is actually reachable. It just tells you that if you made a network request, then the system has an active network interface that it would try to use. That's all. There's no promise those packets would arrive (let alone that you'd get a response), just that iOS would try to send them.
Reachability really only has a couple of uses (and most of the time shouldn't be used). It is useful if "no network is available" would cause you to modify your user interface, or to tell you that it might be a good time to re-try a previously failed connection. (Since iOS 12, you should really use NWPathMonitor for this. I don't know any good uses for Reachability since iOS 12.)
The only way that you can know that a request will actually succeed is to try to send it and see if you get a response. That's why it is not recommended that you test Reachability before sending requests. Just send the request and deal with the errors if they come, since it is always possible to have an error, even if Reachability said you could connect.

Related

What triggers the "would like to find and connect to devices on your local network" permission notification on iOS 14?

What actual method calls, excluding Bonjour, triggers the "would like to find and connect to devices on your local network" permission notification on iOS 14?
This is the screen shot from the WWDC session on this new permission. Which is only somewhat helpful as an overview. I'm more interested in figuring out what all method calls trigger this.
If you're using react native with a debug configuration, then you are including all the code responsible for communicating with your dev machine so you can probably ignore this message.
However it's best to check you have no other libs that require access too. To do this just build a Release version and see if the message persists.
In a nutshell, Bonjour. Its use is no longer "transparent". See https://developer.apple.com/videos/play/wwdc2020/10110/ for more information:
If your app interacts with devices using Bonjour or other local networking protocols, you must add support for local network privacy permissions in iOS 14.
Even an existing app is subject to this rule; the first attempt to use Bonjour triggers the authorization alert.
Apple (Eskimo on the Dev Forums) released a FAQ providing more details around this alert:
https://developer.apple.com/forums/thread/663858
Particularly, providing more info as to what triggers this alert is FAQ-2:
https://developer.apple.com/forums/thread/663874
What operations require local network access?
The general rule is that outgoing traffic to a local network address
requires that the user grant your app local network access. Common
scenarios include:
Making an outgoing TCP connection — yes
Listening for and accepting incoming TCP connections — no
Sending a UDP unicast — yes
Sending a UDP multicast — yes
Sending a UDP broadcast — yes
Receiving an incoming UDP unicast — no
Receiving an incoming UDP multicast — yes
Receiving an incoming UDP broadcast — yes
These TCP and UDP checks are done at the lowest levels of the system
and thus apply to all networking APIs. This includes Network
framework, BSD Sockets, NSStream, NSURLSession and WKWebView, and any
other protocols that you layer on top of those.
IMPORTANT Receiving an incoming UDP multicast or broadcast does not
currently require local network access but, because we hope to change
that in a future update, our advice right now is that you write your
code as if did (r. 69792887, 70017649).
Resolving link-local DNS names (those ending with local, per RFC 6762)
requires local network access. Again, this check applies to a wide
variety of APIs including <dns_sd.h>, <net_db.h>, Network framework,
NSStream, and NSURLSession.
Finally, all Bonjour operations require local network access:
Registering a service with Bonjour — yes
Browsing for Bonjour services — yes
Resolving a Bonjour service — yes
Again, these checks apply to all APIs that use Bonjour, including
<dns_sd.h>, Network framework, NSNetService, and Multipeer
Connectivity.
Note You must declare the Bonjour service types you use in your
Info.plist. See How do I map my Multipeer Connectivity service type to
an entry in the Bonjour services property? for details.
Bonjour-based services where you don’t see any details of the network
do not require local network access. These include:
AirPlay — no
Printing via UIKit — no
One of my apps was triggering this prompt unexpectedly in our internet multiplayer mode. We use RakNet for our networking (which is a C++ lib that uses BSD sockets to send/receive UDP) and I was able to track the problem to the RNS2_Berkley::BindShared function here.
After creating a UDP socket, RakNet tests health/validity of the socket by having it send a little test packet to itself. iOS 14 was flagging this send-to-self behaviour as communication on the local network. I'm not sure if this send-to-self behaviour is a common pattern in socket programming, or a particular quirk of RakNet. Frustratingly, the OS prompt didn't actually appear until later when the socket was used for real which made the issue very hard to track.
I think that this is a false-positive from the OS and raised it with Apple (FB8802121). I won't be holding my breath though so I've just disabled that RakNet behaviour for iOS and am hoping that it wasn't too important.
Edit: To more directly answer the original question: sendto is a method call that can trigger this prompt.
I get rejected from apple app review for this alert. I'm using GCDWebServer which creates an embedded http server in my app.
I think I should provide a message in info.plist to tell user what my app want to do. Before I didn't set the text string in it.
And I would like to update if this will pass the app review.
<key>NSLocalNetworkUsageDescription</key>
<string>xxx uses the local network to connect with devices around you.</string>
Regarding iOS 14: permission request: would like to find and connect to devices on your local network and after numerous deploys to my physical device, I have figured out what causes this in my case.
I have a Xamarin.Forms app which
calls localhost:xxxx to do some local logging while I develop
I use a product thats called LiveSharp that does local hot reloading for XAML AND C# code changes.
So Livesharp actually communicates with a server on my localhost as well.
After disabling both of these and a fresh clean install on my physical device, the permission request has GONE .. Yay
Note: I had to completely remove LiveSharp nuget packages from my project.
Also I tried to re-enable my localhost logging, and for some reason the permission request does not appear.. :headscratch
So: remove any localhost communication that happens in your app. Or at least put an if statement around it allowing it if set to true in appsettings
I also have a Xamarin app and I used the package LiveXaml. After removing it the message was gone.

Reachability class and hostnames that require https or vpn (ios)

I have implemented the Reachability class successfully. However, in my app I'd like to notify the user when a connection to our server only can't be made (but the regular internet is working).
Reachability is reporting that the iPhone successfully can reach a hostname, when I thought it might return an error b/c I was not on an authenticated network:
The hostname I use is 'foreverfreebank.com'. Again, Reachability's hostname check reports success.
However,
--to access this site via the web, one needs to use https in the browser, i.e. 'https://foreverfreebank.com'
Does Reachability account for this case? Or does the protocol part ('https') not count for anything?
I am confused b/c if you type in in the Firefox browser 'http://foreverfreebank.com', (leaving out the 's'), you get an error. That is, http:// should NOT be reachable.
Similarly, I have another site that requires VPN access to get to the site: http://checksforever.com. When I try the hostname: 'checksforever.com', Reachability reports success even though I am not on VPN.
Is this expected behavior? Thanks
You need to check reachability on a specific port (443 in this case). This is something that is not supported by Reachability API. Refer to this question, or this.
Relevant part from documentation:
A remote host is considered reachable when a data packet, sent by an
application into the network stack, can leave the local device.
Reachability does not guarantee that the data packet will actually be
received by the host.
So, basically Reachability doesn't test if the remote host accepts incoming connections. For this purpose, I suspect you'll need to open an URL connection and wait for time out/other errors.

Should I listen for reachability updates in each UIViewController?

I see a lot of Reachability examples where people only display a message when reachability status changes.
But recently, I saw in Foursquare app that they display a message every time the user try to make an action requiring an Internet connection.
I think this is more robust and a better UX to remind the user he can't do anything without Internet. Mainly because users can switch between apps, do something else and forget he has no connection when he comes back.
Also as soon as they get the connection back I can see that they fetch data from the Internet and refresh the UI.
What I am really looking for is the best way to do this. How this is done?
Do they have a general UIViewController that checks for reachability each time it needs a connection?
Or do they have a kind of proxy class before each Internet request that cancels the request and display a message?
How you guys are dealing with that?
Thanks.
EDIT:
The solution I came up with is using AFNetworking which also provide reachability status in the box.
Basically I created an AFHTTPClient and set a reachability callback block on it to listen to status changes. The AFHTTPClient object is application wide (kind of a singleton). (in fact I have one AFHTTPClient per host I need to reach a.com, b.com ...).
Then when I need to perform a request I create a new AFHTTPRequestOperation (AFJSONRequestOperation in my case) and I enqueue it on my AFHTTPClient object.
In the failure block of the operation I check to see if the host is reachable with the networkReachabilityStatus property of the AFHTTPClient. If it's unreachable I display a message that there is no internet connection to the user.
I wrapped that up so I don't have to do this each time I create an operation. So now in the app, each time the user try to do something when there is no connection he got a message remembering him that he has no internet access.
I also use the reachability callback to reload data on a screen once I get the connection back (or rather once I am supposed to have a connection).
I don't know if it's best practice but I think it's nice to know that the app takes care of reloading important data as soon as a new connection is available.
If someone is interested by a sample code I can provide it.
On a WWDC talk this year the Apple engineer on stage recommended users to never base the application internet access on the Reachability example app status. Often reachability doesn't provide a complete information (it is based on a complex mechanism) and the suggestion provided by the engineer was this:
try to do your internet connection, whatever it is the Reachability status; then set your UI hint based on success/fail result
if it fails due to networking issue, then register to Reachability and retry again when Reachability gives the green light; this is needed when you want to recover automatically from the fail condition
in any case give the user the possibility to "force a retry", whatever is the Reachability status. If it succeeds, reset your UI hint immediately.
What the Apple engineer said is perfectly true: quite often you can see in the console log Reachability failure messages while the internet connection is perfectly alive.
Other thing: there is no better "network hint" than the one displayed in the status bar: if you have there the wi-fi icon, the 3G/4G icon, the cellular field strength.
Returning to your original question: there is no absolute better way to manage this stuff, this depends heavily on the application architecture. In case you prefer to concentrate your networking stuff in a dedicated class (not a UIViewController but a NSObject subclass) then it could make sense to define a read-only property for that class that is updated with "success/fail" after latest internet connection with the servers app (it makes no sense to ping other servers like Google or Apple: first of all it's not elegant, second the issue may come from your the servers that serve the app and not the device internet connection status!). #property (readonly) BOOL lastConnectionToMyServerSuccess
Then your view controllers can register (by KVO or by central notification) to this property changes and update their UI accordingly by showing an icon or else (I repeat: leave the user the possibility to try manually to connect to the internet). View controllers should unregister from KVO when out of sight ("viewWillDisappear:") or unloaded ("viewDidLoad:") or dealloc'd.
Of course this adds some extra complexities. E.g.: you use the app, the internet light was green. Then you suspend it, do something else and after a few minutes your return to the app. In such case the app should ping your servers to restore the internet light status again, as after a few minutes network conditions could have changed (e.g. you are on a train). In any case all loaded view controllers will get the KVO notification from the network dedicated class and update themselves.

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