Swift
POST request
API integration
HTTP request
Swift programming

How to send a POST request with BODY in swift

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In Swift, sending a POST request with a body means creating a URLRequest, setting the HTTP method to POST, adding the body data, and then sending it with URLSession. The important details are the body encoding and the matching Content-Type header.

A Simple JSON POST Request

A common case is sending JSON to an API.

swift
1import Foundation
2
3struct LoginRequest: Codable {
4    let username: String
5    let password: String
6}
7
8func sendLogin() async throws {
9    let url = URL(string: "https://example.com/api/login")!
10    var request = URLRequest(url: url)
11    request.httpMethod = "POST"
12    request.setValue("application/json", forHTTPHeaderField: "Content-Type")
13
14    let payload = LoginRequest(username: "alice", password: "secret")
15    request.httpBody = try JSONEncoder().encode(payload)
16
17    let (data, response) = try await URLSession.shared.data(for: request)
18
19    if let httpResponse = response as? HTTPURLResponse {
20        print(httpResponse.statusCode)
21    }
22
23    print(String(data: data, encoding: .utf8) ?? "no body")
24}

This is the modern async and await form. The body is encoded as JSON, assigned to httpBody, and sent as part of the request.

Why Headers Matter

The server needs to know how to interpret the request body. That is why Content-Type matters.

Typical values are:

  • 'application/json for JSON APIs'
  • 'application/x-www-form-urlencoded for form-style payloads'
  • 'multipart/form-data for file uploads'

If the header does not match the body format, the server may reject the request or parse it incorrectly.

Form-Encoded Body Example

Not every API expects JSON. Some older endpoints still want form data.

swift
1import Foundation
2
3func sendForm() async throws {
4    let url = URL(string: "https://example.com/api/token")!
5    var request = URLRequest(url: url)
6    request.httpMethod = "POST"
7    request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
8
9    let form = "grant_type=client_credentials&scope=read"
10    request.httpBody = form.data(using: .utf8)
11
12    let (data, _) = try await URLSession.shared.data(for: request)
13    print(String(data: data, encoding: .utf8) ?? "no body")
14}

The transport mechanism is the same. Only the encoding and header change.

If You Are Not Using Async and Await

Older Swift code often uses a completion handler instead.

swift
1URLSession.shared.dataTask(with: request) { data, response, error in
2    if let error = error {
3        print(error)
4        return
5    }
6    print(String(data: data ?? Data(), encoding: .utf8) ?? "no body")
7}.resume()

That still works. Async and await is just easier to read when your deployment target allows it.

Validate the Response Too

A POST request is not complete just because the network call returned data. You should also inspect:

  • the status code
  • any server error payload
  • timeouts or transport errors

That is especially important for APIs that return JSON error bodies with useful diagnostics.

It is also common to decode the success body into a Swift type instead of printing raw text. Once the response format is known, JSONDecoder should usually be the next step after validating the HTTP status code.

That keeps the networking layer type-safe and easier to evolve.

It also simplifies testing.

Common Pitfalls

The most common mistake is forgetting to set the httpMethod to POST, which leaves the request as a GET.

Another mistake is sending JSON in httpBody but not setting the Content-Type header to application/json.

A third pitfall is building the request body as a plain string when JSON encoding would be safer and less error-prone.

Summary

  • Build a URLRequest, set httpMethod to POST, and assign data to httpBody.
  • Match the body encoding to the correct Content-Type header.
  • Use JSONEncoder for JSON payloads whenever possible.
  • Send the request with URLSession, ideally using async and await in modern Swift.
  • Check the HTTP response, not just whether the network call returned data.

Course illustration
Course illustration

All Rights Reserved.