DEV Community

loading...
Cover image for Simple iOS networking layer with URLSession

Simple iOS networking layer with URLSession

Tamas Dancsi
iOS and front-end dev. I try to think before coding, sometimes it works :)
・2 min read

iOS developers love using URLSession to implement their networking layer for many reasons. It's highly customizable and it's capable of implementing almost everything.

With that said, in many cases the client-side API-s grow quite big very fast. To make life a bit easier, I created a small swift package you can use to make your networking layer more readable. The code is available on my personal Github.

EasyNetworking is a simple wrapper around URLSession to make its usage more lightweight. All awesome features (for example thread-safety) are therefore included.

Features

  • Data and upload task handling, optional response parsing to Decodable-s.
  • Returns Combine publishers, therefore your view model-s can subscribe to the results by using .sink.
  • It uses the default URLSession.shared, but you can pass your own session to it as well.

What is not included

Custom features are not added to the library, but since you can pass your own URLSession, delegate logic can be added on the client-side. Some examples: session delegate handling, caching, handling authentication challenges, certificate pinning.

Integration

Select your main project. On the Swift packages tab, tap on the + icon to add a new dependency. Enter git@github.com:tamasdancsi/ios-easy-networking.git, hit next twice and you're ready to go.

Usage

An example project can be found in the Example folder.

  1. Create an APIClient instance. By default it uses the URLSession.shared singleton but you can pass your own instance as well in case you want to go custom. Similarly with the jsonDecoder param which defaults to a regular JSONDecoder instance.
import SwiftUI
import Combine
import EasyNetworking

class ContentViewModel: ObservableObject {

    @Published var todos: [Todo] = []

    private lazy var apiClient: APIClient = {
        APIClient(baseURL: URL(string: "https://jsonplaceholder.typicode.com")!)
    }()

    private var cancellables: Set<AnyCancellable> = []
}
Enter fullscreen mode Exit fullscreen mode
  1. Prepare your API requests with flexible RequestConfiguration-s.
private func updateTodo(todo: Todo) -> AnyPublisher<TodoUpdateResult, Error> {
    do {
        let configuration = RequestConfiguration(endpointPath: "/todos/\(todo.id)",
                                                  method: .put,
                                                  body: .params(try todo.asDictionary()))
        return apiClient.request(configuration: configuration)
    } catch {
        return Fail(error: error).eraseToAnyPublisher()
    }
}

private func getTodos() -> AnyPublisher<[Todo], Error> {
    let configuration = RequestConfiguration(endpointPath: "/todos")
    return apiClient.request(configuration: configuration)
}
Enter fullscreen mode Exit fullscreen mode
  1. Then you can send your requests by subscribing to the returned AnyPublisher-s with .sink.
getTodos()
    .sink { completion in
        // Handle completion
    } receiveValue: { todos in
        // Handle received values
    }
    .store(in: &cancellables)
Enter fullscreen mode Exit fullscreen mode
updateTodo(todo: updatedTodo)
    .sink { completion in
        // Handle completion
    } receiveValue: { result in
        // Handle received values
    }
    .store(in: &cancellables)
Enter fullscreen mode Exit fullscreen mode

Discussion (0)