Search code examples
iosnode.jsswiftrequest

Pinging localhost in Swift with Xcode


Eventually my Swift frontend will make API calls to an external server, but in the development process, I want to test things locally on my computer. How do I reach my localhost server from Xcode? At this point I'm just trying to ensure contact--I don't need to actually retrieve anything yet.

So far I have the following code:

let urlString = "MY_IP_ADDRESS"
let url = URL(string: urlString)!

do {
    try url.checkResourceIsReachable()
    print("Success!")
} catch {
    print("Failure!")
}

In the urlString variable, I tried my actual IP Address. I also tried localhost:4001/hello where 4001 was the port my node.js server was listening on and I defined /hello. Am I using the wrong address, the wrong code, or is there no way to contact my local machine from Xcode?


Solution

  • If you print(error) in your catch block (which is always a good practice -- print("Failure!") doesn't tell you why the catch block was invoked) you'll notice that there's an error telling you:

    The file couldn’t be opened because the specified URL type isn’t supported.

    Although it isn't explicitly said, I think the implication here is that checkResourceIsReachable is for filesystem URLs -- not remote http requests.

    Instead, use a system of verifying the URL is reachable meant for remote resources.

    import SwiftUI
    import Combine
    
    class CheckURL : ObservableObject {
        enum URLResult : String {
            case unknown, unreachable, reachable
        }
        
        @Published var urlReachable : URLResult = .unknown
        private var cancellable : AnyCancellable?
        
        func verifyURL(urlPath: String) {
            guard let url = URL(string: urlPath) else {
                assertionFailure("Invalid URL")
                self.urlReachable = .unknown
                return
            }
            
            var request = URLRequest(url: url)
            request.httpMethod = "HEAD"
            
            cancellable = URLSession.shared.dataTaskPublisher(for: url).tryMap({ (_ , response: URLResponse) -> URLResult in
                if let response = response as? HTTPURLResponse, response.statusCode == 200 {
                    return .reachable
                } else {
                    return .unreachable
                }
            })
            .replaceError(with: .unreachable)
            .receive(on: RunLoop.main).sink { result in
                self.urlReachable = result
            }
        }
    }
    
    struct ContentView : View {
        @StateObject private var urlChecker = CheckURL()
        
        var body: some View {
            Text("Reachable? \(urlChecker.urlReachable.rawValue)")
                .onAppear {
                        urlChecker.verifyURL(urlPath: "http://localhost:4001/")
                }
        }
    }
    

    As mentioned in the comments, you'll want to make sure you have NSAllowsArbitraryLoads set to YES in your Info.plist