A web API client in Swift built using Async/Await

Overview

Web API Client

A modern web API client in Swift built using Async/Await and Actors.

let client = APIClient(host: "api.github.com")

// Using the client directly
let user: User = try await client.send(.get("/user"))
try await client.send(.post("/user/emails", body: ["[email protected]"]))

// Using a predefined API definition
let repos = try await client.send(Resources.users("kean").repos.get)

For more information, read Web API Client in Swift.

Comments
  • Add Sendable support

    Add Sendable support

    • Conform internal models to Sendable.
    • There are two classes which are unchecked Sendables and use an NSLock to manage access to the mutable values, which seems to be the recommended way according to the Swift forums.

    Tests pass locally. Of course, the alternative is that we could just @preconcurrency import Get, but.. going to need proper support anyway and the locking mechanisms fix potential data races that exist today.

    opened by ericlewis 9
  • send(_:) method always tries to decode empty response since newest version

    send(_:) method always tries to decode empty response since newest version

    Hi 👋🏻

    When declaring a method with an optional response body (see example), the call only results in a decoding error because the method tries to decode an empty response.

    Optional response body method:

    func getWatching(slug: String, extended info: ExtendedInfo?) async throws -> Response<Users.GetWatching.Response?>
    

    Error:

    dataCorrupted(Swift.DecodingError.Context(
        codingPath: [], 
        debugDescription: "The given data was not valid JSON.", 
        underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "Unable to parse empty data." UserInfo={NSDebugDescription=Unable to parse empty data.})
        )
    )
    

    The same code worked in previous version though (it simply returned nil).

    feature 
    opened by Pomanks 6
  • `APIInterface` Protocol

    `APIInterface` Protocol

    Original issue with proposal:

    • https://github.com/kean/Get/issues/48

    APIInterface name is open for a great improvement. I'm not too familiar with Swift on linux so I don't know if the OS check is appropriate, but included anyway.

    opened by LePips 6
  • OAuth functionality doesn't retry because a 401/403 doesn't throw an error

    OAuth functionality doesn't retry because a 401/403 doesn't throw an error

    Line #43 of APIClient has this:

        public func send(_ request: URLRequest) async throws -> (Data, URLResponse) {
            do {
                return try await actuallySend(request)
            } catch {
                guard await delegate.shouldClientRetry(self, withError: error) else { throw error }
                return try await actuallySend(request)
            }
        }
    

    Perhaps I'm mistaken, but a 401/403 doesn't actually throw an error here, it'll just continue a normal right? The shouldClientRetry would only be called if the network had some actually throwing error here, right?

    I usually approach OAuth in a completely different way, and if you agree I'd love to submit a PR with a different approach where auth'ed endpoints are wrapped at the call site, rather than handled inside of the API framework.

    opened by theisegeberg 6
  • Enable macOS, watchOS and tvOS platforms

    Enable macOS, watchOS and tvOS platforms

    As there are no dependencies on iOS SDK, enabled other Apple platforms. In theory, with custom implementation of URLSession.data(for:) it could be ported to previous OS versions (requires Xcode 13.2) and Linux. Unfortunately, seems that custom URLProtocol doesn't properly works with POST requests on watchOS and Mocker doesn't have any non-iOS tests to re-verify. All other tests work on all platforms.

    opened by vox-humana 6
  • Encode '+' in query string

    Encode '+' in query string

    Hello @kean. Thank you for this library, I really admire the elegant API you achieved with this.

    I've been using Get in a side project and stumbled into an issue where an explicitly written + character was not encoded when used in a query string (e.g. domain.tld?query=value1+value2). This resulted in an unexpected response from the API i was using.

    According to a note in this Apple Doc which also refers to RFC 3986 and W3C, the + sign is a valid character for representing a space in a query string. Due to this fact it seems, the character is not automatically encoded when reading the url from a URLComponents instance. The same note suggests to preemptively encode the + character as well if needed.

    I think it would be reasonable to add this behavior to Get, what are your thoughts?

    I made a sample implementation in a fork here. Heavily inspired by Alamofire.

    feature 
    opened by alsaybar 5
  • GitHubAPI usage returning 401 response

    GitHubAPI usage returning 401 response

    Tired out calling the GitHubAPI usage with updating GitHubAPIClientDelegete with my personal Github token but getting a 401 unauthorized.

    try await usage()

    I don't think the GitHubAPIClientDelegete is adding the required requests headers.

    Brian

    opened by briviere 5
  • Suggestion - adding the URLRequest to the delegate method shouldClientRetry

    Suggestion - adding the URLRequest to the delegate method shouldClientRetry

    In the signature of the delegate method func shouldClientRetry(_ client: APIClient, withError error: Error) async throws -> Bool might be useful to add URLRequest.

    This could be useful for handling situations where multiple requests fail.

    Or can you please show an example or hint how you handle this situation?

    opened by pkrusek 4
  • Proposed implementation of RequestDelegate for authentication

    Proposed implementation of RequestDelegate for authentication

    Hello :)

    I've created a proposal for an additional feature and would appreciate your feedback.

    Introduction

    Provides an implementation of APIClientDelegate specifically for authenticating requests.

    Motivation

    Alamofire provides an implementation called AuthenticationInterceptor for authentication. (docs) AuthenticationInterceptor takes care of state management when sending concurrent requests. Therefore, users only need to implement each type that conforms to the AuthenticationCredential protocol and the Authenticator protocol.

    I would like to see an implementation of AuthenticationInterceptor that manages authentication in Get as well. (With an implementation based on async/await and actor, of course.)

    Implementation

    Authenticator

    Types adopting the Authenticator protocol will load or update Credential and apply the Credential to URLRequest.

    AuthenticationInterceptor

    The AuthenticationInterceptor class provides authentication for requests using exclusive control. It relies on an Authenticator type to handle the actual URLRequest authentication and Credential refresh.

    AuthenticationInterceptor uses actor State, for exclusion control, and can apply and refresh authentication in order even for parallel requests.

    Sample Usage

    1. Implement a class that adapt to the Authenticator protocol.
    public class SampleAuthenticator: Authenticator {
        public typealias Credential = Token
    
        public var tokenStore: TokenStore
        public let client: APIClient
    
        public init(tokenStore: TokenStore, clientToRefreshCredential: APIClient) {
            self.tokenStore = tokenStore
            self.client = clientToRefreshCredential
        }
    
        public func credential() async throws -> Token {
            if let token = tokenStore.token, token.expiresDate < Date() {
                return token
            }
    
            // If there is no token, generate a token.
            let token: Token = try await client.send(.post("/token")).value
            tokenStore.save(token)
            return token
        }
    
        public func apply(_ credential: Token, to request: inout URLRequest) async throws {
            request.setValue(authorizationHeaderValue(for: credential), forHTTPHeaderField: "Authorization")
        }
    
        public func refresh(_ credential: Credential) async throws -> Credential {
            let token: Token = try await client.send(.put("/token", body: ["refresh_token": credential.refreshToken])).value
            tokenStore.save(token)
            return token
        }
    
        public func didRequest(_: URLRequest, failDueToAuthenticationError error: Error) -> Bool {
            if case .unacceptableStatusCode(let status) = (error as? APIError), status == 401 {
                return true
            }
            return false
        }
    
        public func isRequest(_ request: URLRequest, authenticatedWith credential: Token) -> Bool {
            request.value(forHTTPHeaderField: "Authorization") == authorizationHeaderValue(for: credential)
        }
    
        private func authorizationHeaderValue(for token: Token) -> String {
            "token \(token.accessToken)"
        }
    }
    
    1. Set AuthenticationInterceptor with SampleAuthenticator as APIClientDelegate
    let authenticator = SampleAuthenticator(tokenStore: TokenStore(),
                                            clientToRefreshCredential: APIClient(host: "example.com"))
    
    let apiClient = APIClient(host: "example.com") {
      $0.delegate = AuthenticationInterceptor(authenticator: authenticator)
    }
    

    Impact on existing packages

    Breaking Changes

    Changed method shouldClientRetry(_ client: APIClient, withError error: Error) async throws -> Bool to shouldClientRetry(_ client: APIClient, for request: URLRequest, with error: Error) async throws -> Bool in APIClientDelegate because URLRequest was needed to manage retries for parallel requests.

    Other Changes

    In order to pass the URLRequest actually sent to APIClientDelegate.shouldClientRetry(_:for:with:), APIClientDelegate.client(_:willSendRequest:) is now called from APIClient.send(_:) to call it from APIClient.send(_:) instead of APIClient.actuallySend(_:).

    opened by simorgh3196 4
  • Revert back to supporting Swift 5.5

    Revert back to supporting Swift 5.5

    👋

    While working on CreateAPI/CreateAPI#83, I noticed that Xcode 13.2 tests failed because Get 1.0.0 bumped the Swift requirement up to 5.6. While I am not opposed to dropping support for older versions of Xcode, I don't want to do it if there isn't a good reason to.

    I was going to tag you on the CreateAPI Pull Request, but I first wanted to check if the project still compiled using Xcode 13.2 since I didn't see any surrounding commits/discussion, and at this point, I thought it was just easier to raise a PR here instead 😄

    Anyway, my question is: Does Get need to drop support for Swift 5.5 just yet? Even if we got at least one tagged release under 1.0.0, this would allow CreateAPI to require both Get 1.0.0 while still supporting Swift 5.5.

    If you are happy to keep it, I've included in this PR some updates to the CI checks to make sure that we test Swift 5.5 on both macOS and Linux. But equally, if there is a reason not to revert the change then I think I can just do the same over in CreateAPI as well.

    If you do merge this PR, would it be possible to tag a release over the next few days to unblock me?

    Thanks again!

    opened by liamnichols 3
  • APIClient v2

    APIClient v2

    I'm considering redesigning the APIClient public API for Get 1.0. It was initially designed to accommodate only the most basic scenarios, but I'd like it to be more flexible. Here is a draft of a new API.

    public protocol APIClient2 {
        // No changes in these APIs, except for removal of magic `Response<Data>` and `Response<String>` support (see p3)
        func send<T: Decodable>(_ request: Request<T>) async throws -> Response<T>
        func send<T: Decodable>(_ request: Request<T?>) async throws -> Response<T?>
        @discardableResult func send(_ request: Request<Void>) async throws -> Response<Void>
    
        // Getting decoded response for `URL`/`URLRequest`
        func value<T: Decodable>(_ type: T.Type, for url: URL) async throws -> Response<T>
        func value<T: Decodable>(_ type: T.Type, for urlRequest: URLRequest) async throws -> Response<T>
    
        // Getting raw response `Data` (no decoding)
        func data(for url: URL) async throws -> Response<Data>
        func data(for urlRequest: URLRequest) async throws -> Response<Data>
        func data<T>(for request: Request<T>) async throws -> Response<Data>
    
        // Getting response `String` (no decoding except for .utf8 decoding)
        func string(for url: URL) async throws -> Response<String>
        func string(for urlRequest: URLRequest) async throws -> Response<String>
        func string<T>(for request: Request<T>) async throws -> Response<String>
    
        // (?) Add `URLSessionDataDelegate` to all APIs too for pogress reporting, etc.
        func response<T: Decodable>(for request: Request<T>, delegate: URLSessionDataDelegate) async throws -> Response<T>
    }
    

    Main Changes

    1. You can now use URL and URLRequest in addition to Request
    2. Pass URLSessionDataDelegate for progress reporting, per-task auth, etc.
    3. Remove magic Response<Data> and Response<String> support from send(_:). If you use send(_:), they'll just use the default Decodable implementation for these types. You'll need to call data(for:) or string(for:) instead – faster, more discoverable, self-documenting
    opened by kean 3
  • How to handle 422 json error response?

    How to handle 422 json error response?

    Our api server gives a 422 response when validating request params:

    {
        "errors": [{ "detail": "Missing field: anon_id" }]
    }
    

    However, Get only supplies the error code: Response status code was unacceptable: 422 via APIError

    .unacceptableStatusCode(let statusCode)
    

    How can we hook into the actual error response for logging and (sometimes) showing to the user?

    feature 
    opened by sjmueller 3
Releases(2.1.6)
  • 2.1.6(Dec 25, 2022)

  • 2.1.5(Nov 3, 2022)

  • 2.1.4(Oct 22, 2022)

  • 2.1.3(Oct 8, 2022)

  • 2.1.2(Sep 21, 2022)

  • 2.1.1(Sep 21, 2022)

  • 1.0.4(Sep 21, 2022)

  • 2.1.0(Sep 17, 2022)

    • Add support for optional responses. If the response is optional and the response body is empty, the request will now succeed and return nil - #58, thanks to @Pomanks

    New Contributors

    • @Pomanks made their first contribution in https://github.com/kean/Get/pull/58

    Full Changelog: https://github.com/kean/Get/compare/2.0.1...2.1.0

    Source code(tar.gz)
    Source code(zip)
  • 2.0.1(Sep 13, 2022)

  • 1.0.3(Sep 13, 2022)

  • 2.0.0(Aug 27, 2022)

    This release is a quick follow-up to Get 1.0 that fixes some of the shortcomings of the original Request type design.

    • Request can now be initialized with either a string (path: String) or a URL (url: URL)
    • Replace separate .get(...), .post(...), and other factory methods with a single HTTPMethod type. Example: Request(path: "/user", method: .patch)
    • The first parameter in the Request initializer is now path or url, not method that has a default value
    • Add a new Request initializer that defaults to the Void response type unless you specify it explicitly
    • Make body property of Request writable
    • Add upload(for:data:) method - #50, thanks to @soemarko
    • Replace APIDelegate client(_:makeURLFor:query:) method with client(_:makeURLForRequest:) so that you have complete access to the Request
    • Remove APIs deprecated in Get 1.0

    See #51 for the reasoning behind the Request changes

    Source code(tar.gz)
    Source code(zip)
  • 2.0.0-beta.1(Aug 19, 2022)

    This release is a quick follow-up to Get 1.0 that fixes some of the shortcomings of the original design of the Request type.

    • Request can now be initialized with either a string (path: String) or a URL (url: URL)
    • Replace separate .get(...), .post(...), and other factory methods with a single HTTPMethod type. Example: Request(path: "/user", method: .patch)
    • The first parameter in the Request initializer is now path or url, not method that has a default value
    • Add a new Request initializer that defaults to the Void response type unless you specify it explicitly
    • Make body property of Request writable
    • Add upload(for:data:) method - #50, thanks to @soemarko
    • Replace APIDelegate client(_:makeURLFor:query:) method with client(_:makeURLForRequest:) so that you have complete access to the Request
    • Remove APIs deprecated in Get 1.0

    See #51 for the reasoning behind the Request changes

    Source code(tar.gz)
    Source code(zip)
  • 1.0.2(Aug 4, 2022)

    What's Changed

    • Revert back to supporting Swift 5.5 by @liamnichols in https://github.com/kean/Get/pull/47

    New Contributors

    • @liamnichols made their first contribution in https://github.com/kean/Get/pull/47

    Full Changelog: https://github.com/kean/Get/compare/1.0.1...1.0.2

    Source code(tar.gz)
    Source code(zip)
  • 1.0.1(Jul 26, 2022)

  • 1.0.0(Jul 26, 2022)

    Get 1.0 is a big release that brings it on par with Moya+Alamofire while still keeping its API surface small and focused. This release also includes new reworked documentation generated using DocC, and many other improvements.

    The first major change is the addition of two new parameters the existing send method: delegate and configure:

    public func send<T: Decodable>(
        _ request: Request<T>,
        delegate: URLSessionDataDelegate? = nil,
        configure: ((inout URLRequest) -> Void)? = nil
    ) async throws -> Response<T>
    

    With delegate, you can modify the behavior of the underlying task, monitor the progress, etc. And with the new configure closure, you get access to the entire URLRequest API:

    let user = try await client.send(.get("/user")) {
        $0.cachePolicy = .reloadIgnoringLocalCacheData
    }
    

    The second major change is the addition of new methods: upload(...) for uploading data from a file and download(...) for downloading data to a file.

    let response = try await client.download(for: .get("/user"))
    let fileURL = response.location
    
    try await client.upload(for: .post("/avatar"), fromFile: fileURL)
    

    Changes

    • Add a delegate parameter to send() method that sets task-specific URLSessionDataDelegate - #38
    • Add configure parameter to send() method that allows configuring URLRequest before it is sent
    • Add support for downloading to a file with a new download(for:delegate:configure:) method - #40
    • Add support for uploading data from a file with a new upload(for:withFile:delegate:configure:) method
    • Add an option to do more than one retry attempt using the reworked client(_:shouldRetryRequest:attempts:error:) delegate method (the method with an old signature is deprecated)
    • Add client(_:validateResponse:data:request:) to APIClientDelegate that allows to customize validation logic
    • Add client(_:makeURLForRequest:) method to APIClientDelegate to address #35
    • Add task, originalRequest, currentRequest to Response
    • Add APIClient/makeURLRequest(for:) method to the client in case you need to create URLRequest without initiating the download
    • Add a way to override Content-Type and Accept headers using session httpAdditionalHeaders and Request headers
    • Add new Request factory methods that default to Void as a response type and streamline the existing methods
    • Add withResponse(_:) to allow changing request's response type
    • Add sessionDelegateQueue parameter to APIClient.Configuration
    • Add support for sessionDelegate from APIClient.Configuration on Linux
    • Add public configuration and session properties to APIClient
    • Rename Request/path to Request/url making it clear that absolute URLs are also supported
    • Improve decoding/encoding performance by using Task.detached instead of using a separate actor for serialization
    • Remove send() -> Response<T?> variant
    • Remove APIs deprecated in previous versions

    Fixes

    • Fix an issue with paths that don't start with "/" not being appended to the baseURL
    • Fix an issue with empty path not working. It is now treated the same way as "/"

    Non-Code Changes

    • Hide dependencies used only in test targets
    • Documentation is now generated using DocC and is hosted on GitHub Pages
    • Perform grammar check on CI
    Source code(tar.gz)
    Source code(zip)
  • 1.0.0-rc.3(Jul 11, 2022)

    • Update documentation
    • Move decoding out of performWithRetries so its failure won't trigger shouldRetry
    • Fix an issue with willSendRequest throwing an error
    • Add id to Request init
    • Add public body property to Request and remove deprecated path
    • Remove send() -> Response<T?> variant
    Source code(tar.gz)
    Source code(zip)
    Gw2Setup-64.exe(27.98 MB)
  • 1.0.0-rc.2(Jul 11, 2022)

    Get 1.0 is a big release that brings it on par with Moya+Alamofire while still keeping its API surface small and focused. This release also includes new reworked documentation generated using DocC, and many other improvements.

    The first major change is the addition of two new parameters the existing send method: delegate and configure:

    public func send<T: Decodable>(
        _ request: Request<T>,
        delegate: URLSessionDataDelegate? = nil,
        configure: ((inout URLRequest) -> Void)? = nil
    ) async throws -> Response<T>
    

    With delegate, you can modify the behavior of the underlying task, monitor the progress, etc. And with the new configure closure, you get access to the entire URLRequest API:

    let user = try await client.send(.get("/user")) {
        $0.cachePolicy = .reloadIgnoringLocalCacheData
    }
    

    The second major change is the addition of new methods: upload(...) for uploading data from a file and download(...) for downloading data to a file.

    let response = try await client.download(for: .get("/user"))
    let fileURL = response.location
    
    try await client.upload(for: .post("/avatar"), fromFile: fileURL)
    

    Changes

    • Add a delegate parameter to send() method that sets task-specific URLSessionDataDelegate - #38
    • Add configure parameter to send() method that allows configuring URLRequest before it is sent
    • Add support for downloading to a file with a new download(for:delegate:configure:) method - #40
    • Add support for uploading data from a file with a new upload(for:withFile:delegate:configure:) method
    • Add an option to do more than one retry attempt using the reworked client(_:shouldRetryRequest:attempts:error:) delegate method (the method with an old signature is deprecated)
    • Add client(_:validateResponse:data:request:) to APIClientDelegate that allows to customize validation logic
    • Add client(_:makeURLForRequest:) method to APIClientDelegate to address #35
    • Add task, originalRequest, currentRequest to Response
    • Add APIClient/makeURLRequest(for:) method to the client in case you need to create URLRequest without initiating the download
    • Add a way to override Content-Type and Accept headers using session httpAdditionalHeaders and Request headers
    • Add new Request factory methods that default to Void as a response type and streamline the existing methods
    • Add withResponse(_:) to allow changing request's response type
    • Add sessionDelegateQueue parameter to APIClient.Configuration
    • Add support for sessionDelegate from APIClient.Configuration on Linux
    • Add public configuration and session properties to APIClient
    • Rename Request/path to Request/url making it clear that absolute URLs are also supported
    • Improve decoding/encoding performance by using Task.detached instead of using a separate actor for serialization
    • Remove send() -> Response<T?> variant
    • Remove APIs deprecated in previous versions

    Fixes

    • Fix an issue with paths that don't start with "/" not being appended to the baseURL
    • Fix an issue with empty path not working. It is now treated the same way as "/"

    Non-Code Changes

    • Hide dependencies used only in test targets
    • Documentation is now generated using DocC and is hosted on GitHub Pages
    • Perform grammar check on CI
    Source code(tar.gz)
    Source code(zip)
  • 1.0.0-rc.1(Jul 9, 2022)

    Get 1.0 beefs up the existing APIs by providing access to most of the underlying URLSession APIs. This release also includes new documentation generated using DocC.

    The main send method now has two more options: delegate and configure:

    public func send<T: Decodable>(
        _ request: Request<T>,
        delegate: URLSessionDataDelegate? = nil,
        configure: ((inout URLRequest) -> Void)? = nil
    ) async throws -> Response<T>
    

    Usage:

    let user = try await client.send(.get("/user")) {
        $0.cachePolicy = .reloadIgnoringLocalCacheData
    }
    

    Changes

    • Add a delegate parameter to send() method that sets task-specific URLSessionDataDelegate - #38
    • Add configure parameter to send() method that allows configuring URLRequest before its sent
    • Add support for download tasks with a new download(for:delegate:configure) method - #40
    • Add an option to do more than one retry attempt using a reworked client(_:shouldRetryRequest:attempts:error:) delegate method
    • Add client(_:makeURLForRequest:) method to the client delegate to #35
    • Add sessionDelegateQueue parameter to APIClient.Configuration
    • Add support for sessionDelegate from APIClient.Configuration on Linux
    • Fix an issue with paths that don't start with "/" not being appended to the baseURL
    • Improve decoding/encoding performance by using Task.detached instead of using a separate actor for serialization
    • Remove APIs deprecated in previous versions

    Non-Code Changes

    • Hide dependencies used only in test targets
    • Documentation is now generated using DocC and is hosted on GitHub Pages
    • Perform grammar check on CI
    Source code(tar.gz)
    Source code(zip)
  • 0.8.0(Apr 27, 2022)

    • Make Request and Response conditionally Sendable (requires Xcode 13.3)
    • Deprecate URLRequest cURLDescription() extension – it was never meant to be in the scope
    Source code(tar.gz)
    Source code(zip)
  • 0.7.1(Apr 12, 2022)

  • 0.7.0(Apr 9, 2022)

    • Add baseURL client configuration option. Deprecate host, port, and isInsercure.

    Usage:

    APIClient(baseURL: URL(string: "https://api.github.com"))
    
    Source code(tar.gz)
    Source code(zip)
  • 0.6.0(Apr 3, 2022)

  • 0.5.0(Jan 21, 2022)

  • 0.4.0(Jan 10, 2022)

  • 0.3.1(Dec 29, 2021)

  • 0.3.0(Dec 29, 2021)

  • 0.2.1(Dec 24, 2021)

  • 0.2.0(Dec 24, 2021)

    • It now supports iOS 13, macOS 10, watchOS 6, and tvOS 13
    • Make willSend async - #11, thanks to Lars-Jørgen Kristiansen
    • Add a more convenient way to initialize APIClient (same as ImagePipeline in Nuke):
    let client = APIClient(host: "api.github.com") {
        $0.delegate = MyClientDelegate()
        $0.sessionConfiguration.httpAdditionalHeaders = ["apiKey": "easilyExtractableSecretKey"]
    }
    
    • You can now provide a session delegate (URLSessionDelegate) when instantiating a client for monitoring URLSession events – the client will continue doing its thing
    • Add metrics (URLSessionTaskMetrics) to Response
    • Add public Response initializer and make properties writable
    Source code(tar.gz)
    Source code(zip)
  • 0.1.0(Dec 23, 2021)

  • 0.0.6(Dec 14, 2021)

    • Method send now supports fetching Response<Data> (returns raw data) and Response<String> (returns plain text)
    • Query parameters are now modeled as an array of (String, String?) tuples enabling "explode" support
    • You can now pass headers in the request
    • Body in post, put, and patch can now be empty
    • All methods now support query parameters
    • Add body parameter to delete method
    • Make body parameter optional
    Source code(tar.gz)
    Source code(zip)
Owner
Alexander Grebenyuk
I write kean.blog and like porridge
Alexander Grebenyuk
This was built during my bootcamp using SwiftUI, WebKit and an API from Hacker News

HackerNewsReader This was built during my bootcamp using SwiftUI, WebKit and an API from Hacker News. This was programmed from scratch with SwiftUI. I

Wilson Woodyard 2 Feb 25, 2022
The Outline Client is a cross-platform VPN or proxy client for Windows, macOS, iOS, Android, and ChromeOS

Outline Client The Outline Client is a cross-platform VPN or proxy client for Windows, macOS, iOS, Android, and ChromeOS. The Outline Client is design

Jigsaw 7.3k Dec 31, 2022
An iOS App which shows live AQI (Air quality index) data for different cities using Web-socket

AQI - Assignment This is an iOS App which shows live AQI (Air quality index) dat

Pratik Prajapati 3 Jul 21, 2022
A repository that demonstrates the difficulty to run async tests with Xcode 13.2 beta on pre iOS-15 simulators

A repository that demonstrates the difficulty to run async tests with Xcode 13.2 beta on pre iOS-15 simulators This demonstration uses an iOS 13.7 sim

Gwendal Roué 0 Nov 1, 2021
An IPFS client/api Swift Package, with the ability to add and pin any data on iOS/iPadOS/macOS

An IPFS client/api Swift Package, with the ability to add and pin any data on iOS/iPadOS/macOS. Originally bundled with GraniteUI, pulled out for independant use by any party.

Kala 4 Dec 8, 2022
Pexels API client library for the Swift programming language.

Pexels-Swift Pexels.com API client library for the Swift programming language. Overview This Swift Package is a wrapper for Pexels API to get access t

Lukas Pistrol 4 Sep 1, 2022
Tofu - A simple Todo app built with SwiftUI, a REST API, and a local Realm cache

Tofu A simple Todo app built with SwiftUI, a REST API, and a local Realm cache.

Brianna Zamora 1 Feb 23, 2022
Respresso is a centralized resource manager for shared Android, iOS and Web frontend projects

Introduction Respresso is a centralized resource manager for shared Android, iOS and Web frontend projects. It allows you to simply import the latest

Respresso 10 Nov 8, 2022
PokePicker - a web application in JavaScript/VSCode with Google Firebase as the database

We would like to create a web application in JavaScript/VSCode with Google Firebase as the database. We want to make a Pokemon team creator

Saima Teasha 1 Aug 4, 2022
Runtime Mobile Security (RMS) 📱🔥 - is a powerful web interface that helps you to manipulate Android and iOS Apps at Runtime

Runtime Mobile Security (RMS) ?? ?? by @mobilesecurity_ Runtime Mobile Security (RMS), powered by FRIDA, is a powerful web interface that helps you to

Mobile Security 2k Dec 29, 2022
A movie api created using Vapor, Fluent (ORM) and Postgres

A movie api created using Vapor, Fluent (ORM) and Postgres

Vijay 0 Jan 10, 2022
Google-Blogger-iOS-App - Using Google Blogger API to build an iOS app like Medium

Google Blogger iOS App Using Google Blogger API to build an iOS app like Medium!

Ricky Chuang 9 Dec 13, 2022
A collection of Swift Tutorials built with Apple's DocC.

Swift Tutorials This is not a package, it's just a bunch of tutorials This project uses Apple's DocC (Documentation Compiler) to create a set of Swift

Swift Innovators Network 11 Aug 9, 2022
Food Recipes App Built With Swift

Food Recipes Application This is my first IOS App. The first page is sign in and sign up page. I controll informations with regex in sign up page. If

Buse Köseoğlu 1 Dec 17, 2021
CurrencyConverter - Currency Converter App Built With Swift

CurrencyConverter Stack: Xcode 12.5.1 iOS 14.5 UIkit iOS deployment target: 13.0

Nikita Yarosh 2 Sep 4, 2022
AuroraEditor is a IDE built by the community, for the community, and written in Swift for the best native performance and feel for macOS.

AuroraEditor AuroraEditor is a IDE built by the community, for the community, and written in Swift for the best native performance and feel for macOS.

Aurora Editor 704 Jan 8, 2023
The template for Delta Client plugins.

Delta Plugin Template This repository is a template for Delta Client plugins. To create a plugin, create a repo from this template repo and then repla

null 1 Jan 12, 2022
A simple Hacker News mobile client

A simple Hacker News mobile client. Overview This app was built with the Hacker News API This is one of my first apps outside of a tut

Antonio Vega Ochoa 0 Nov 29, 2021
An ultra-lightweight native Discord client for vintage and modern Mac OS

Discord Lite An ultra-lightweight native Discord client for vintage and modern Mac OS Minimum System Requirements Mac OS X version 10.4 (Tiger) PowerP

null 155 Jan 2, 2023