Coder Social home page Coder Social logo

networking's Introduction

Networking

Networking

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: "https://jsonplaceholder.typicode.com")

    func fetchPost() -> AnyPublisher<Post, Error> {
        get("/posts/1")
    }
}

Later...

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

How

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.

What

  • Build a concise Api
  • Automatically map your models
  • Uses latest Apple's Combine
  • Compatible with native Codable and any JSON Parser
  • Embarks a built-in network logger
  • 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 https://github.com/freshOS/Networking.

Create a Client

let client = NetworkingClient(baseURL: "https://jsonplaceholder.typicode.com")

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",
                                  fileData: photo.data,
                                  fileName: "photo.jpg",
                                   mimeType: "image/jpeg")
client.post("/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: "https://jsonplaceholder.typicode.com", timeout: 15)

Alternatively,

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
  print(json)
}

Later ...

cancellable.cancel()

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:
    break
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)
    default:
        // handle other error types
        print("\(error.localizedDescription)")
    }
}   
}) { (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) {
            t.deserialize(arrowJSON)
        }
        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) {
            t.deserialize(arrowJSON)
        }
        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: "https://my-api.com")

    // 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> {
        get("/articles/\(a.id)")
    }

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

    // Delete
    func delete(article a: Article) -> AnyPublisher<Void, Error> {
        delete("/articles/\(a.id)")
    }

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

networking's People

Contributors

s4cha avatar workingdog avatar jeffbarg avatar linsyorozuya avatar arunrdd avatar michalsrutek avatar

Watchers

James Cloos avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.