Popular Searches
Popular Course Categories
Popular Courses

Sending GET and POST Requests Using URLSession

Sending GET and POST Requests Using URLSession

Networking and REST API Integration

Sending GET and POST Requests Using URLSession

Course: iOS App Development

Topic: Sending GET and POST Requests Using URLSession

Course Link: iOS App Development Training

Demo Link: Register for a Course Demo

Website: JustAcademy


1. Introduction

Modern iOS applications communicate with remote servers to retrieve information, submit forms, authenticate users, and synchronize data. For example, a shopping app retrieves products, a news app downloads articles, and a registration screen sends user information to a backend server.

In Swift, Apple's URLSession API provides functionality for sending HTTP requests and receiving server responses. Two of the most commonly used HTTP methods are GET and POST.

  • GET: Retrieves information from a server.
  • POST: Sends information to a server for processing or resource creation.

URLSession works with URLRequest, HTTPURLResponse, JSONEncoder, and JSONDecoder to support networking operations in iOS applications.

2. Learning Objectives

  • Understand HTTP requests and responses.
  • Learn the difference between GET and POST methods.
  • Configure requests using URLRequest.
  • Send asynchronous requests using URLSession.
  • Retrieve JSON data and decode it into Swift models.
  • Encode Swift objects into JSON for POST requests.
  • Handle HTTP status codes and networking errors.
  • Use async/await and completion handlers.
  • Apply networking security and debugging best practices.
  • Build a practical API communication feature for an iOS app.

3. Understanding HTTP and REST APIs

HTTP stands for Hypertext Transfer Protocol. It defines how clients and servers communicate. An iOS app acts as the client, while a remote server receives requests and returns responses.

A REST API commonly exposes resources through URLs and uses HTTP methods to identify the intended operation.

HTTP Method Purpose Example
GET Retrieve data Fetch a list of users.
POST Submit data or create a resource Create a new user.
PUT Replace a resource Replace a profile record.
PATCH Update selected fields Change a user's display name.
DELETE Delete a resource Remove a saved record.

4. What Is URLSession?

URLSession is a Foundation networking API that coordinates data transfers between an application and a remote server. It supports HTTP and HTTPS requests, response handling, file transfers, and asynchronous operations.

Apple provides a shared session for common networking tasks. Custom sessions can be configured when an application needs specific connection, caching, delegate, or background-transfer behavior.

Component Description
URL Represents an endpoint address.
URLRequest Defines the URL, HTTP method, headers, and optional body.
URLSession Executes networking tasks.
URLSession.shared Provides a shared session for straightforward requests.
URLResponse Contains general response information.
HTTPURLResponse Provides HTTP status codes and response headers.
JSONEncoder Converts Swift values into JSON data.
JSONDecoder Converts JSON data into Swift models.

Reference: Apple Developer Documentation — URLSession.

5. Understanding GET Requests

A GET request retrieves data from a server without intending to change its state. It is commonly used to fetch user profiles, product lists, articles, weather information, and other resources.

Step 1: Import Foundation

import Foundation

The Foundation framework provides URLSession, URLRequest, URL, JSONDecoder, and other networking-related types.

Step 2: Create a Model

Suppose an API returns an article with an identifier, title, and body. Define a Swift model that conforms to Decodable.

struct Article: Decodable {
    let id: Int
    let title: String
    let body: String
}

Step 3: Send a GET Request

The following example retrieves articles from a public sample API.

import Foundation

func fetchArticles() async throws -> [Article] {
    guard let url = URL(
        string: "https://jsonplaceholder.typicode.com/posts"
    ) else {
        throw URLError(.badURL)
    }

    var request = URLRequest(url: url)
    request.httpMethod = "GET"
    request.setValue(
        "application/json",
        forHTTPHeaderField: "Accept"
    )

    let (data, response) = try await URLSession.shared.data(
        for: request
    )

    guard let httpResponse = response as? HTTPURLResponse else {
        throw URLError(.badServerResponse)
    }

    guard (200...299).contains(httpResponse.statusCode) else {
        throw URLError(.badServerResponse)
    }

    return try JSONDecoder().decode([Article].self, from: data)
}

How the GET Request Works

  1. The URL identifies the API endpoint.
  2. URLRequest defines the request configuration.
  3. The HTTP method is set to GET.
  4. The Accept header indicates that JSON is the preferred response format.
  5. URLSession sends the request asynchronously.
  6. The HTTP response status is checked.
  7. JSONDecoder converts the received data into an array of Article objects.

6. Using Query Parameters in GET Requests

Query parameters provide additional information to an API, such as search terms, filtering options, sorting rules, and pagination values.

import Foundation

func createSearchURL(query: String, page: Int) throws -> URL {
    guard var components = URLComponents(
        string: "https://api.example.com/articles"
    ) else {
        throw URLError(.badURL)
    }

    components.queryItems = [
        URLQueryItem(name: "q", value: query),
        URLQueryItem(name: "page", value: String(page)),
        URLQueryItem(name: "limit", value: "20")
    ]

    guard let url = components.url else {
        throw URLError(.badURL)
    }

    return url
}

Using URLComponents and URLQueryItem is preferable to manually concatenating query strings because the APIs handle URL encoding.

Common Uses of Query Parameters

  • Search for products or articles.
  • Filter results by category or status.
  • Request a particular page of results.
  • Sort information by name, date, or price.

Do not put passwords or sensitive access tokens in URLs. Query strings may be recorded in logs or other diagnostic systems.

7. Understanding POST Requests

A POST request sends information to a server. It is often used for registration forms, creating records, submitting feedback, sending comments, or performing server-side operations.

Depending on the API, a POST request may contain JSON, form-encoded values, or another supported payload format. Always check the backend API documentation before choosing the body format.

Step 1: Create a Request Model

struct NewArticle: Encodable {
    let title: String
    let body: String
    let userId: Int
}

The Encodable protocol allows JSONEncoder to convert the model into a JSON payload.

Step 2: Configure the POST Request

import Foundation

func createArticle() async throws -> Article {
    guard let url = URL(
        string: "https://jsonplaceholder.typicode.com/posts"
    ) else {
        throw URLError(.badURL)
    }

    let newArticle = NewArticle(
        title: "Learning URLSession",
        body: "This article was submitted from an iOS application.",
        userId: 1
    )

    var request = URLRequest(url: url)
    request.httpMethod = "POST"

    request.setValue(
        "application/json",
        forHTTPHeaderField: "Content-Type"
    )

    request.setValue(
        "application/json",
        forHTTPHeaderField: "Accept"
    )

    request.httpBody = try JSONEncoder().encode(newArticle)

    let (data, response) = try await URLSession.shared.data(
        for: request
    )

    guard let httpResponse = response as? HTTPURLResponse else {
        throw URLError(.badServerResponse)
    }

    guard (200...299).contains(httpResponse.statusCode) else {
        throw URLError(.badServerResponse)
    }

    return try JSONDecoder().decode(Article.self, from: data)
}

This example uses a public testing API. Such APIs may simulate creation rather than permanently saving submitted data. A production endpoint may return a different response model or no response body.

Important POST Request Properties

Property Purpose
httpMethod Specifies POST as the HTTP method.
Content-Type Identifies the request body format.
Accept Indicates the preferred response format.
httpBody Contains the encoded request data.
JSONEncoder Serializes the Swift model into JSON.

8. Difference Between GET and POST

Feature GET POST
Primary purpose Retrieve information Submit or process information
Typical input URL path and query parameters Often a request body
Request body Usually not needed Commonly used
Typical result Requested resource or list Created resource or operation result
Caching May be cached according to HTTP rules Not generally cached by default
Idempotency Defined as idempotent by HTTP semantics Not necessarily idempotent
Example Load a user profile Submit a registration form

POST is not automatically secure simply because data is sent in the request body. Use HTTPS and follow appropriate authentication and data-protection practices.

9. Understanding HTTP Status Codes

An HTTP status code describes the outcome of an HTTP request. A network operation can complete successfully while the server still returns an HTTP error, so the status code must be checked separately.

Status Code Meaning Handling
200 OK Request succeeded Process the returned data.
201 Created A resource was created Read the resource or its identifier.
204 No Content Request succeeded without a response body Do not decode an empty body as JSON.
400 Bad Request Invalid request Validate the submitted fields.
401 Unauthorized Authentication failed or is missing Check credentials or token handling.
403 Forbidden Operation is not permitted Check permissions.
404 Not Found Resource or endpoint was not found Verify the URL.
429 Too Many Requests Rate limit reached Follow server retry guidance.
500 Internal Server Error Server-side failure Display a suitable message.

10. Creating a Reusable Network Error Type

A custom error type makes it easier to distinguish HTTP failures from invalid responses and other networking problems.

enum NetworkError: Error {
    case invalidURL
    case invalidResponse
    case httpStatus(Int)
}

func validateResponse(_ response: URLResponse) throws {
    guard let httpResponse = response as? HTTPURLResponse else {
        throw NetworkError.invalidResponse
    }

    guard (200...299).contains(httpResponse.statusCode) else {
        throw NetworkError.httpStatus(httpResponse.statusCode)
    }
}

The validation function can be reused in multiple networking methods to keep error handling consistent.

11. Handling Networking Errors

Requests may fail because of poor connectivity, invalid URLs, server timeouts, authentication problems, or unexpected response formats. Use do-catch blocks to handle thrown errors.

func loadArticlesSafely() async {
    do {
        let articles = try await fetchArticles()
        print("Articles received: \(articles.count)")
    } catch {
        print("Unable to load articles: \(error.localizedDescription)")
    }
}

In a production application, display a user-friendly message such as “Unable to load articles. Please try again.” Avoid exposing raw technical errors to users.

12. Using URLSession with Completion Handlers

Completion handlers are another way to perform asynchronous requests. They are useful when maintaining older code or integrating APIs that use callback-based patterns.

func fetchSingleArticle() {
    guard let url = URL(
        string: "https://jsonplaceholder.typicode.com/posts/1"
    ) else {
        print("Invalid URL")
        return
    }

    URLSession.shared.dataTask(with: url) {
        data, response, error in

        if let error = error {
            print("Network error: \(error.localizedDescription)")
            return
        }

        guard let httpResponse = response as? HTTPURLResponse,
              (200...299).contains(httpResponse.statusCode),
              let data = data else {
            print("Invalid or unsuccessful response")
            return
        }

        do {
            let article = try JSONDecoder().decode(
                Article.self,
                from: data
            )
            print(article.title)
        } catch {
            print("Decoding failed: \(error)")
        }
    }.resume()
}

The resume() method starts the data task. A task created with a completion handler does not begin executing until it is resumed.

13. Updating the User Interface

Network requests should not block the main thread. When displaying results in UIKit, update UI components on the main thread or main actor. In SwiftUI, a view model can manage the fetched data and loading state.

Task {
    do {
        let articles = try await fetchArticles()

        await MainActor.run {
            print("Ready to display \(articles.count) articles")
            // Update your UI or data source here.
        }
    } catch {
        await MainActor.run {
            print("Could not load articles")
            // Show a user-friendly error state.
        }
    }
}

A well-designed interface should include loading, success, empty, and error states. Separate networking logic from UI layout to make the application easier to test and maintain.

14. Adding Authorization Headers

Some APIs require an access token or another authentication mechanism. A bearer-token API commonly expects an Authorization header.

var request = URLRequest(url: endpoint)
request.httpMethod = "GET"
request.setValue(
    "Bearer \(accessToken)",
    forHTTPHeaderField: "Authorization"
)

Use tokens obtained from a valid authentication flow. Avoid hard-coding production credentials, committing tokens to source control, or logging sensitive information.

15. Configuring Request Timeouts

A custom URLSessionConfiguration lets developers set timeout behavior and other networking options.

let configuration = URLSessionConfiguration.default
configuration.timeoutIntervalForRequest = 30
configuration.timeoutIntervalForResource = 60

let session = URLSession(configuration: configuration)

These values are examples. Choose appropriate timeouts based on the endpoint, expected response size, and user experience.

16. Common Problems and Troubleshooting

Problem Possible Cause Solution
Invalid URL Malformed endpoint Validate the URL and inspect URL construction.
Request does not start Task was not resumed Call resume().
401 response Missing or invalid credentials Check authentication requirements.
404 response Incorrect endpoint Verify the API route and resource identifier.
JSON decoding fails Model does not match the response Inspect the JSON structure and adjust the model.
Empty response body Endpoint returns no content Handle 204 responses without decoding JSON.
POST body rejected Incorrect content type or missing fields Check the API contract and payload.
App Transport Security error Insecure connection or TLS configuration issue Use a properly configured HTTPS endpoint.
UI does not refresh UI state is not updated correctly Update UI state on the main actor.

17. Best Practices

  • Prefer HTTPS for network communication.
  • Validate URLs before sending requests.
  • Use URLComponents for query parameters.
  • Use Codable models for JSON serialization and deserialization.
  • Check HTTP status codes instead of assuming every completed request succeeded.
  • Separate networking code into a service or repository layer.
  • Use async/await for readable asynchronous code where suitable.
  • Provide clear loading, empty, and error states.
  • Protect tokens and sensitive user information.
  • Retry requests only when appropriate, particularly for POST operations.
  • Test offline conditions, timeouts, invalid payloads, and server failures.
  • Follow the backend documentation for headers, authentication, and response formats.

18. Practical Project: Fetch and Submit Articles

Build a small iOS application that retrieves articles from an API and submits a new article through a form.

Project Requirements

  1. Create an Article model that conforms to Decodable.
  2. Create a NewArticle model that conforms to Encodable.
  3. Implement a GET method to retrieve articles.
  4. Display articles in a UITableView or SwiftUI List.
  5. Add text fields for a new article's title and body.
  6. Validate required fields before submission.
  7. Implement a POST request that sends JSON.
  8. Show a loading indicator while the request is in progress.
  9. Display success or failure feedback.
  10. Refresh the displayed list after creation if the backend supports it.

Suggested Project Structure

IOSNetworkingApp/
├── Models/
│   ├── Article.swift
│   └── NewArticle.swift
├── Services/
│   └── ArticleService.swift
├── ViewModels/
│   └── ArticleViewModel.swift
└── Views/
    ├── ArticleListView.swift
    └── CreateArticleView.swift

19. Practice Exercises

  1. Fetch a single article using its identifier.
  2. Retrieve a list of users and decode the JSON response.
  3. Add search parameters to a GET request.
  4. Submit a JSON payload using POST.
  5. Display a suitable message for a 404 response.
  6. Handle an endpoint that returns 204 No Content.
  7. Add an authorization header using a test token.
  8. Move networking code into a reusable service.
  9. Test the app without an internet connection.
  10. Write unit tests for response validation and JSON decoding.

20. Interview Questions and Answers

Q1. What is URLSession?

URLSession is a Foundation API for performing network data transfers, including sending HTTP requests and receiving responses.

Q2. What is the difference between GET and POST?

GET is primarily used to retrieve information, while POST is commonly used to submit data or request that the server create or process a resource.

Q3. What is URLRequest?

URLRequest defines request information such as the URL, method, headers, and optional body.

Q4. What is JSONDecoder?

JSONDecoder converts JSON data into Swift types that conform to Decodable.

Q5. What is JSONEncoder?

JSONEncoder converts Swift values that conform to Encodable into JSON data.

Q6. What is the Content-Type header?

It identifies the media type of the request body. For JSON payloads, a common value is application/json.

Q7. What is the Accept header?

It indicates the response format the client prefers, such as JSON.

Q8. Why should HTTP status codes be checked?

A network transfer may complete even when the server returns an HTTP error. Status validation helps the app distinguish successful responses from failures.

Q9. What is async/await?

Swift concurrency syntax that makes asynchronous code easier to read. An awaited network operation suspends the current task while waiting for a result rather than blocking the thread.

Q10. Is POST always secure?

No. Use HTTPS, appropriate authentication, and safe handling of sensitive data.

Q11. Why use URLComponents?

URLComponents provides a structured way to construct URLs and add query parameters with appropriate encoding.

Q12. Should POST requests always be retried after failure?

No. The server may have processed the original request even if the response was lost. Follow the API's retry and idempotency rules to avoid duplicate operations.

21. Quick Revision Checklist

  • Understand GET and POST request purposes.
  • Construct URLs and URLRequest objects correctly.
  • Configure HTTP methods and headers.
  • Use URLSession to send asynchronous requests.
  • Encode request bodies with JSONEncoder when JSON is required.
  • Decode responses using JSONDecoder.
  • Validate HTTP status codes.
  • Handle network, server, and decoding errors.
  • Update UI state on the main actor when required.
  • Follow HTTPS and credential-security practices.

22. Conclusion

Sending GET and POST requests with URLSession is a fundamental skill in iOS development. GET requests allow applications to retrieve remote information, while POST requests submit data for server-side processing. URLRequest configures each request, JSONEncoder prepares JSON payloads, and JSONDecoder converts responses into Swift models.

Reliable networking requires more than sending a request. Developers must validate HTTP responses, handle errors, protect sensitive information, and present useful feedback to users. Practising these concepts with a test API builds a foundation for developing real-world iOS applications.

23. Additional Learning Resources

Previous Next
whatsapp