Skip to content
Advertisement

Swift HTTP session not sending actual Request

So I have some Swift code that send a request to my local host


//
//  ContentView.swift
//  Shared
//
//  Created by Ulto4 on 10/23/21.
//

import SwiftUI

struct ContentView: View {
    var body: some View {
        VStack{
        Text("Hello, world!")
            .padding()
        Button(action : {
            self.fu()
        }, label: {
            Image(systemName: "pencil").resizable().aspectRatio(contentMode:.fit)
        })
    }
    }
    func fu(){
        let url = URL(string: "http://127.0.0.1:5000/232")

           

           guard let requestUrl = url else { fatalError() }



           
           var request = URLRequest(url: requestUrl)



         

           request.httpMethod = "GET"



      

           let task = URLSession.shared.dataTask(with: request) { (data, response, error) in

               

              

               if let error = error {

                   print("Error took place (error)")

                   return

               }

               

              

               if let response = response as? HTTPURLResponse {

                   print("Response HTTP Status code: (response.statusCode)")

               }
            
        }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
   
        
    }

}


However, on my Flask app there are no get requests coming in and the function isn’t running. There also isn’t anything printing to the console. I am fairly new to swift so I don’t really know how to fix this. Is there any other way to send requests in swift, if not, How would I fix this?

Advertisement

Answer

You are creating the URLSessionDataTask, but you never start it. Call task.resume(), e.g.

func performRequest() {
    guard let url = URL(string: "http://127.0.0.1:5000/232") else {
        fatalError()
    }

    let task = URLSession.shared.dataTask(with: url) { data, response, error in
        if let error = error {
            print("Error took place (error)")
            return
        }

        if let response = response as? HTTPURLResponse {
            print("Response HTTP Status code: (response.statusCode)")
        }
    }
    task.resume()           // you must call this to start the task
}

That having been said, a few caveats:

  1. You are doing http rather than https. Make sure to temporarily enable insecure network requests with app transport settings, e.g.

    enter image description here

  2. You didn’t say if this was for macOS or iOS.

    • If running on physical iOS device, it will not find your macOS web server at 127.0.0.1 (i.e., it will not find a web server running on your iPhone). You will want to specify the IP number for your web server on your LAN.

    • If macOS, make sure to enable outbound network requests in the target’s “capabilities”:

      enter image description here


You asked:

Is there any other way to send requests in swift?

It is probably beyond the scope of your question, but longer term, when using SwiftUI, you might consider using Combine, e.g., dataTaskPublisher. When running a simple “what was the status code” routine, the difference is immaterial, but when you get into more complicated scenarios where you have to parse and process the responses, Combine is more consistent with SwiftUI’s declarative patterns.

Let us consider a more complicated example where you need to parse JSON responses. For illustrative purposes, below I am testing with httpbin.org, which echos whatever parameters you send. And I illustrate the use of dataTaskPublisher and how it can be used with functional chaining patterns to get out of the mess of hairy imperative code:

struct SampleObject: Decodable {
    let value: String
}

struct HttpBinResponse<T: Decodable>: Decodable {
    let args: T
}

class RequestService: ObservableObject {
    var request: AnyCancellable?
    let decoder = JSONDecoder()
    @Published var status: String = "Not started yet"

    func startRequest() {
        request = createRequest().sink { completion in
            print("completed")
        } receiveValue: { [weak self] object in
            self?.status = "Received " + object.value
        }
    }

    func createRequest() -> AnyPublisher<SampleObject, Error>{
        var components = URLComponents(string: "https://httpbin.org/get")
        components?.queryItems = [URLQueryItem(name: "value", value: "foo")]

        guard let url = components?.url else {
            fatalError("Unable to build URL")
        }

        return URLSession.shared.dataTaskPublisher(for: url)
            .map(.data)
            .decode(type: HttpBinResponse<SampleObject>.self, decoder: decoder)
            .map(.args)
            .receive(on: DispatchQueue.main)
            .eraseToAnyPublisher()
    }
}

struct ContentView: View {
    @ObservedObject var requestService = RequestService()

    var body: some View {
        VStack{
            Text("Hello, world!")
                .padding()
            Button {
                requestService.startRequest()
            } label: {
                Image(systemName: "pencil").resizable().aspectRatio(contentMode:.fit)
            }
            Text(requestService.status)
        }
    }
}

But, like I said, it is beyond the scope of this question. You might want to make sure you get comfortable with SwiftUI and basic URLSession programming patterns (e.g., making sure you resume any tasks you create). Once you have that mastered, you can come back to Combine to write elegant networking code.

FWIW, like workingdog said, you could also use the new async-await rendition of data(for:delegate:). But when in the declarative world of SwiftUI, I would suggest Combine.

User contributions licensed under: CC BY-SA
5 People found this is helpful
Advertisement