Coder Social home page Coder Social logo

artemkalinovsky / legatus Goto Github PK

View Code? Open in Web Editor NEW
38.0 4.0 3.0 143 KB

Combine 🚜 - driven REST API client, based on Alamofire πŸ”₯

License: MIT License

Swift 100.00%
swift swift-framework combine alamofire networklayer networklibrary jsonparser xmlparser swiftui api api-rest apimanager

legatus's Introduction

swift workflow

Legatus πŸ‡

A legatus (anglicised as legate) was a high-ranking Roman military officer in the Roman Army, equivalent to a modern high-ranking general officer. Initially used to delegate power, the term became formalised under Augustus as the officer in command of a legion. Legatus was also a term for an ambassador of the Roman Republic who was appointed by the senate for a mission (legatio) to a foreign nation, as well as for ambassadors who came to Rome from other countries.

Intro 🎬

The basic idea of Legatus is that we want some network abstraction layer that sufficiently encapsulates actually calling Alamofire directly.

Also, it would be cool to have network layer, that will compatible with SwiftUI out-of-the-box πŸ“¦, isn't it?🧐

Luckily, Legatus was implemented with Combine framework and have couple of fancy methods, that will allow you to assign(to:on:) your response models right to @Published properties. Neat!🀩

Some awesome features of Legatus🌟:

  • SOLID design (e.g.: APIClient don't stores and configures requests, each request is encapsulated in separate entity).
  • Easy retrying of requests.
  • Elegant and flexible canceling of requests.
  • Reachability tracking.
  • Support JSON and XML response formats.
  • Combine extension.
  • Swift Concurrency support.

Legatus is inspired by Moya.

Project Status πŸ€–

I consider it's ready for production use.
Any contributions (pull requests, questions, propositions) are always welcome!πŸ˜ƒ

Requirements πŸ“

  • Swift 5.6+
  • macOS 12+
  • iOS 15+
  • tvOS 15+
  • watchOS 8+

Installation πŸ“¦

  • Swift Package Manager

You can use Xcode SPM GUI: File -> Swift Packages -> Add Package Dependency -> Pick "Up to Next Major Version 2.0.0".

Or add the following to your Package.swift file:

.package(url: "https://github.com/artemkalinovsky/Legatus.git", .upToNextMajor(from: Version("2.0.0")))

and then specify "Legatus" as a dependency of the Target in which you wish to use Legatus. Here's an example PackageDescription :

// swift-tools-version:5.6
import PackageDescription

let package = Package(
    name: "MyPackage",
    products: [
        .library(
            name: "MyPackage",
            targets: ["MyPackage"]),
    ],
    dependencies: [
        .package(url: "https://github.com/artemkalinovsky/Legatus.git", .upToNextMajor(from: Version("2.0.0")))
    ],
    targets: [
        .target(
            name: "MyPackage",
            dependencies: ["Legatus"])
    ]
)

Basic Usage πŸ§‘β€πŸ’»

Let's suppose we want to fetch list of users from JSON and response is look like this:

{ 
   "results":[ 
      { 
         "name":{ 
            "first":"brad",
            "last":"gibson"
         },
         "email":"[email protected]"
      }
   ]
}
  • Setup

  1. Create APIClient :
    let apiClient = APIClient(baseURL: URL(string: "https://webservice.com/api/")!)
  1. Create response model:
import Foundation
import Legatus

final class User: Decodable {
    let firstName: String?
    let lastName: String?
    let email: String?

    enum CodingKeys: String, CodingKey {
        case name
        case email
    }

    enum NameKeys: String, CodingKey {
        case firstName = "first"
        case lastName = "last"
    }

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        email = try values.decodeIfPresent(String.self, forKey: .email)

        let name = try values.nestedContainer(keyedBy: NameKeys.self, forKey: .name)
        firstName = try name.decodeIfPresent(String.self, forKey: .firstName)
        lastName = try name.decodeIfPresent(String.self, forKey: .lastName)
    }
}
  1. Create request with endpoint path and desired reponse deserializer:
import Foundation
import Legatus

final class UsersApiRequest: DeserializeableRequest {

    var path: String {
        "users"
    }
    
    var deserializer: ResponseDeserializer<[User]> {
        JSONDeserializer<User>.collectionDeserializer(keyPath: "results")
    }

}
  • Perfrom created request

    apiClient.executeRequest(request: UsersApiRequest()) { result in }

VoilΓ !πŸ§‘β€πŸŽ¨

Advanced Usage πŸ€“πŸ’»

  • Working with CoreData models.

To deserialize your response right to CoreData NSManagedObject , just call designated initializer firstly:

import Foundation
import CoreData
import Legatus

@objc(CoreDataObject)
public class CoreDataObject: NSManagedObject, Decodable {

    required convenience public init(from decoder: Decoder) throws {
        self.init(context: NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType))

        //TODO: implement decoding
    }

}
  • Working with Realm models.

To deserialize your response right to Realm Object subclass:

import Foundation
import RealmSwift
import Legatus

final class RealmObject: Object, Decodable {

    @objc dynamic var name = ""

    required init() {
        super.init()
    }

    convenience init(from decoder: Decoder) throws {
        self.init()

        //TODO: implement decoding
    }
}

Same functionality available for XMLDeserializer too.

  • Retrying requests

If you want to retry previously failed request, just provide count of desiried retry times:

    apiClient.executeRequest(request: UsersApiRequest(), retries: 3) { result in }
  • Request cancelation

To cancel certain request, you have to store it's cancelation token and call cancel() method.

    let cancelationToken = apiClient.executeRequest(request: UsersApiRequest()) { result in }
    
    DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(300)) {
            cancelationToken.cancel()
    }

Also, you can cancel all active requests:

    apiClient.cancelAllRequests()

Combine Extension 🚜

While working with SwiftUI, where most of UI updates based on Combine mechanisms under the hood, it's very convenient to get Publisher as request result for future transformations and assigns:

    @Published var users = [User]()
    var subscriptions = Set<AnyCancellable>()

    apiClient
         .responsePublisher(request: UsersApiRequest())
         .catch { _ in return Just([User]())}
         .assign(to: \.users, on: self)
         .store(in: &subscriptions)

Swift Concurrency Extension 🚦

    do {
        let usersResponse = try await apiClient.executeRequest(request: UsersApiRequest())
    } catch {
        // handle error
    }

Apps using Legatus πŸ“±

Credits πŸ‘

License πŸ“„

Legatus is released under an MIT license. See LICENCE for more information.

legatus's People

Contributors

artemkalinovsky avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  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.