The other day I was trying to get my React Native application on a physical device to talk to my locally hosted api for development purposes. This is a common development practice to develop against a local backend, and I was surprised at how much pain it was to setup, and how little there was on the web on the topic. So here is a post to document some of my findings. Keep in mind, I am no networking expert. This covers iOS only.
From an API/backend hosting perspective, usage of localhost(127.0.0.1
) is local to the LOCAL machine(Right, duh). Localhost is meant to reference ME. So when you are hosting your frontend application also on your local machine and hit the localhost:port
combo for the locally hosted backend, all is well.
In the case of React Native development on a physical device however, things are a bit trickier. Hitting localhost:port
from a mobile device would just reference a port on the mobile device...however for iOS development this all a bit interesting because it replaces references to localhost
with the building computers IP during the build process. So requesting https://localhost:7259
from the IOS device gets converted to [https://10.0.0.97:7259](my local ip of my computer)
via the build process. But because we are hosting the backend api at [https://127.0.0.1:7259]
(on the build computer), requests made to https://10.0.0.97:7259
(from a mobile device) don't magically get redirected to 127.0.0.1:7259
on the build computer. This makes sense. There isn't anything telling our local api on the build computer to listen on 10.0.0.97:7259
. So instead we need to change the backend api on the build computer to host the API at 0.0.0.0:7259
. This is a more server networking friendly address, and processes running on the build computer that reference localhost:7259
(maybe you also have a local web UI hitting your backend as well) still work as well. Here is a definition of 0.0.0.0
:
In the context of servers, 0.0.0.0 can mean "all IPv4 addresses on the local machine". If a host has two IP addresses, 192.168.1.1 and 10.1.2.1, and a server running on the host is configured to listen on 0.0.0.0, it will be reachable at both of those IP addresses. -wiki
I'll just say working with iOS via React Native and SSL has been unpleasant during local development. iOS doesn't like self-signed certificates, but I believe it is becoming more and more common practice to develop locally with SSL enabled (HTTPS). My goal is always to keep my local environment as similar to production as possible. This means also including SSL locally. iOS makes this somewhat challenging. Two things I believe you have to do:
#import <React/RCTBridgeModule.h> #import <React/RCTHTTPRequestHandler.h> #if DEBUG @implementation RCTHTTPRequestHandler (GHPatch) - (void)URLSession:(NSURLSession _)session didReceiveChallenge:(NSURLAuthenticationChallenge _)challenge completionHandler: (void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential \* credential))completionHandler { completionHandler( NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]); } @end #endif
<key>NSAppTransportSecurity</key> <dict> <key>NSAllowsArbitraryLoads</key> <true/> <key>NSExceptionDomains</key> <dict> <key>localhost</key> <dict> <key>NSExceptionAllowsInsecureHTTPLoads</key> <string>YES</string> <key>NSIncludesSubdomains</key> <string>YES</string> </dict> </dict> </dict>
These changes should only apply for local development and are unlikely to affect your production build (this is not a tested statement).
The use of an app called a-Shell
was pivotal when troubleshooting communication between my smartphone and my computer. telnet
, dig
, curl
, and ping
all are useful tools to confirm machines are talking to each other correctly.