Awesome Open Source
Awesome Open Source



Language: Swift 5 Platform: iOS 13+ SPM compatible License: MIT Build Status codebeat badge Release version

Networking brings together URLSession, Combine, Decodable and Generics to make connecting to a JSON api a breeze.

struct Api: NetworkingService {

    let network = NetworkingClient(baseURL: "")

    func fetchPost() -> AnyPublisher<Post, Error> {


let api = Api()
api.fetchPost().sink(receiveCompletion: { _ in }) { post in
    // Get back some post \o/
}.store(in: &cancellables)


By providing a lightweight client that automates boilerplate code everyone has to write.
By exposing a delightfully simple api to get the job done simply, clearly, quickly.
Getting swift models from a JSON api is now a problem of the past

URLSession + Combine + Generics + Protocols = Networking.


  • [x] Build a concise Api
  • [x] Automatically map your models
  • [x] Uses latest Apple's Combine
  • [x] Compatible with native Codable and any JSON Parser
  • [x] Embarks a built-in network logger
  • [x] Pure Swift, simple, lightweight & 0 dependencies

Welcome the future. Bye ws , Hello Networking.

Networking is the next generation of the ws project. The improvements are: Using Combine native Apple's framework over Then Promise Library, removing Arrow dependency to favour Codable (Arrow can still be adapted easily though) and removing the Alamofire dependency in favour of a simpler purely native URLSession implementation.
In essence, less dependencies and more native stuff.

Try it!

Networking is part of freshOS iOS toolset. Try it in an example App ! Download Starter Project

Getting Started

Install it

Networking is installed via the official Swift Package Manager.

Select Xcode>File> Swift Packages>Add Package Dependency...
and add

Create a Client

let client = NetworkingClient(baseURL: "")

Make your first call

Use get, post, put & delete methods on the client to make calls.

client.get("/posts/1").sink(receiveCompletion: { _ in }) { (data:Data) in
    // data
}.store(in: &cancellables)

Get the type you want back

Networking recognizes the type you want back via type inference. Types supported are Void, Data, Any(JSON), NetworkingJSONDecodable(Your Model) & [NetworkingJSONDecodable]

This enables keeping a simple api while supporting many types :

let voidPublisher: AnyPublisher<Void, Error> = client.get("")
let dataPublisher: AnyPublisher<Data, Error> = client.get("")
let jsonPublisher: AnyPublisher<Any, Error> = client.get("")
let postPublisher: AnyPublisher<Post, Error> = client.get("")
let postsPublisher: AnyPublisher<[Post], Error> = client.get("")

Pass params

Simply pass a [String: CustomStringConvertible] dictionary to the params parameter.

client.postsPublisher("/posts/1", params: ["optin" : true ])
    .sink(receiveCompletion: { _ in }) { (data:Data) in
      //  response
    }.store(in: &cancellables)

Upload multipart data

For multipart calls (post/put), just pass a MultipartData struct to the multipartData parameter.

let params: [String: CustomStringConvertible] = [ "type_resource_id": 1, "title": photo.title]
let multipartData = MultipartData(name: "file",
                                  fileName: "photo.jpg",
                                   mimeType: "image/jpeg")"/photos/upload",
            params: params,
            multipartData: multipartData).sink(receiveCompletion: { _ in }) { (data:Data?, progress: Progress) in
                if let data = data {
                    print("upload is complete : \(data)")
                } else {
                    print("progress: \(progress)")
}.store(in: &cancellables)

Add Headers

Headers are added via the headers property on the client.

client.headers["Authorization"] = "[mytoken]"

Add Timeout

Timeout (TimeInterval in seconds) is added via the optional timeout property on the client.

let client = NetworkingClient(baseURL: "", timeout: 15)


client.timeout = 15 

Cancel a request

Since Networking uses the Combine framework. You just have to cancel the AnyCancellable returned by the sink call.

var cancellable = client.get("/posts/1").sink(receiveCompletion: { _ in }) { (json:Any) in

Later ...


Log Network calls

3 log levels are supported: off, info, debug

client.logLevels = .debug

Handling errors

Errors can be handled on a Publisher, such as:

client.get("/posts/1").sink(receiveCompletion: { completion in
switch completion {
case .finished:
case .failure(let error):
    switch error {
    case let decodingError DecodingError:
        // handle JSON decoding errors
    case let networkingError NetworkingError:
        // handle NetworkingError
        // print(networkingError.status)
        // print(networkingError.code)
        // handle other error types
}) { (response: Post) in
    // handle the response
}.store(in: &cancellables)

Support JSON-to-Model parsing.

For a model to be parsable by Networking, it needs to conform to the NetworkingJSONDecodable protocol.

For example if you are using Arrow for JSON Parsing. Supporting a Post model will look like this:

extension Post: NetworkingJSONDecodable {
    static func decode(_ json: Any) throws -> Post {
        var t = Post()
        if let arrowJSON = JSON(json) {
        return t

Instead of doing it every models, you can actually do it once for all with a clever extension 🤓.

extension ArrowParsable where Self: NetworkingJSONDecodable {

    public static func decode(_ json: Any) throws -> Self {
        var t: Self = Self()
        if let arrowJSON = JSON(json) {
        return t

extension User: NetworkingJSONDecodable { }
extension Photo: NetworkingJSONDecodable { }
extension Video: NetworkingJSONDecodable { }
// etc.

This default extension is already provided for the native Decodable type. So if your models are Decodable then you just have to add:

extension Mymodel: NetworkingJSONDecodable { }

You can support any JSON parsing by replacing the code above with whatever JSON parsing library you are using \o/ !

// TODO Document network.defaultCollectionParsingKeyPath = "collection" Clean Api

Design a clean api

In order to write a concise api, Networking provides the NetworkingService protocol. This will forward your calls to the underlying client so that your only have to write get("/route") instead of network.get("/route"), while this is overkill for tiny apis, it definitely keep things concise when working with massive apis.

Given an Article model

struct Article: Codable {
    let id: String
    let title: String
    let content: String

Make your Article NetworkingJSONDecodable, this is a one liner since Codable is supported by default.

extension Article: NetworkingJSONDecodable {}

Here is what a typical CRUD api would look like :

struct CRUDApi: NetworkingService {

    var network = NetworkingClient(baseURL: "")

    // Create
    func create(article a: Article) -> AnyPublisher<Article, Error> {
        post("/articles", params: ["title" : a.title, "content" : a.content])

    // Read
    func fetch(article a: Article) -> AnyPublisher<Article, Error> {

    // Update
    func update(article a: Article) -> AnyPublisher<Article, Error> {
        put("/articles/\(", params: ["title" : a.title, "content" : a.content])

    // Delete
    func delete(article a: Article) -> AnyPublisher<Void, Error> {

    // List
    func articles() -> AnyPublisher<[Article], Error> {

Get A Weekly Email With Trending Projects For These Topics
No Spam. Unsubscribe easily at any time.
swift (7,742
ios (3,611
api (1,722
json (1,189
http (928
rest-api (557
rest (494
networking (413
combine (68
httpclient (44