iONess is HTTP Request Helper for iOS platform used by HCI iOS App

Overview

iONess

iONess (iOS Network Session) is HTTP Request Helper for the iOS platform used by Home Credit Indonesia iOS App. It uses Ergo as a concurrent helper and promise pipelining.

build test SwiftPM Compatible Version License Platform

Example

To run the example project, clone the repo, and run pod install from the Example directory first.

Requirements

  • Swift 5.0 or higher (or 5.1 when using Swift Package Manager)
  • iOS 10 or higher (latest version)
  • iOS 8 or higher (1.2.5 version)

Only Swift Package Manager

  • macOS 10.10 or higher
  • tvOS 10 or higher

Installation

Cocoapods

iONess is available through CocoaPods. To install it, simply add the following line to your Podfile:

for iOS 10 or higher

pod 'iONess', '~> 2.0'

or for iOS 8 or higher

pod 'iONess', '~> 1.2.5'

Swift Package Manager from XCode

  • Add it using xcode menu File > Swift Package > Add Package Dependency
  • Add https://github.com/oss-homecredit-id/iONess.git as Swift Package URL
  • Set rules at version, with Up to Next Major option and put 2.0.2 as its version for iOS 10 or higher or 1.2.5 for iOS 8 or higher
  • Click next and wait

Swift Package Manager from Package.swift

Add as your target dependency in Package.swift. Use 2.0.2 as its version for iOS 10 or higher or 1.2.5 for iOS 8 or higher

dependencies: [
  .package(url: "https://github.com/oss-homecredit-id/iONess.git", .upToNextMajor(from: "2.0.2"))
]

Use it in your target as iONess

 .target(
  name: "MyModule",
  dependencies: ["iONess"]
)

Contributor

License

iONess is available under the MIT license. See the LICENSE file for more info.

Usage Example

Basic Usage

iONess is designed to simplify the request process for HTTP Requests. All you need to do is just create the request using Ness / NetworkSessionManager class:

Ness.default
  .httpRequest(.get, withUrl: "https://myurl.com")
  .dataRequest()
  .then { result in
    // do something with result this will not executed when request failed
  }

or with no completion at all:

Ness.default
  .httpRequest(.get, withUrl: "https://myurl.com")
  .dataRequest()

When data dataRequest() is called, it will always execute the request right away no matter it has completion or not. dataRequest() actually returning Promise object from Ergo so you could always do everything you can do with Ergo Promise:

Ness.default
  .httpRequest(.get, withUrl: "https://myurl.com")
  .dataRequest()
  .then { result in
    // do something with result this will not executed when request failed
  }.handle { error in
    // do something if error occurs
  }.finally { result, error in
    // do something regarding of error or not after request completed
  }

You could always check Ergo here about what its promise can do.

Create Request

To create a request you can do something like this:

Ness.default.httpRequest(.get, withUrl: "https://myurl.com")
  .set(urlParameters: ["param1": "value1", "param2": "value2"])
  .set(headers: ["Authorization": myToken])
  .set(body: dataBody)
  ..
  ..

or with customize URLSession:

// create session
var session = URLSession()
session.configuration = myCustomConfig
session.delegateQueue = myOperationQueue
..
..

// create Ness instance
let ness = Ness(with: session)

// create request
ness.httpRequest(.get, withUrl: "https://myurl.com")
  .set(urlParameters: ["param1": "value1", "param2": "value2"])
  .set(headers: ["Authorization": myToken])
  .set(body: dataBody)
  ..
  ..

it's better to save the instance of Ness and reused it since it will be just creating the request with the same URLSession unless you want to use any other URLSession for another request.

available enumeration for HTTP Method to use are:

  • post
  • get
  • put
  • patch
  • delete
  • head
  • connect
  • options
  • trace
  • none if you don't want to include HTTP Method header
  • custom(String)

to set a custom type of body, you need to pass those custom type encoder that implements HTTPBodyEncoder object to encode the object into the data:

Ness.default.httpRequest(.get, withUrl: "https://myurl.com")
  .set(body: myObject, with encoder: myEndoder) -> Self
  ..
  ..

The declaration of HTTPBodyEncoder is:

public protocol HTTPBodyEncoder {
  var relatedHeaders: [String: String]? { get }
  func encoder(_ any: Any) throws -> Data
}

the relatedHeaders is the associated header with this encoding which will be auto-assigned to the request headers. this variable is optional since the default implementation are returning nil

there some different default method to set the body with iONess default body encoder which are:

  • func set(body: Data) -> Self
  • func set(stringBody: String, encoding: String.Encoding = .utf8) -> Self
  • func set(jsonBody: [String: Any]) -> Self
  • func set(arrayJsonBody: [Any]) -> Self
  • func set<EObject: Encodable>(jsonEncodable: EObject) -> Self
  • func set<EObject: Encodable>(arrayJsonEncodable: [EObject]) -> Self

After the request is ready then prepare the request which will return Thenable:

Ness.default.httpRequest(.get, withUrl: "https://myurl.com")
  .set(urlParameters: ["param1": "value1", "param2": "value2"])
  .set(headers: ["Authorization": myToken])
  .set(body: dataBody)
  ..
  ..
  .dataRequest()

or for download, you need to give the target location URL where you want to downloaded data to be saved:

Ness.default.httpRequest(.get, withUrl: "https://myurl.com")
  .set(urlParameters: ["param1": "value1", "param2": "value2"])
  .set(headers: ["Authorization": myToken])
  .set(body: dataBody)
  ..
  ..
  .downloadRequest(forSavedLocation: myTargetUrl)

or for upload you need to give file location URL which you want to upload:

Ness.default.httpRequest(.get, withUrl: "https://myurl.com")
  .set(urlParameters: ["param1": "value1", "param2": "value2"])
  .set(headers: ["Authorization": myToken])
  .set(body: dataBody)
  ..
  ..
  .uploadRequest(forFileLocation: myTargetUrl)

Data Request Promise

After creating a data request, you can just execute the request with then method:

Ness.default
  .httpRequest(.get, withUrl: "https://myurl.com")
  ..
  ..
  .dataRequest()
  .then { result in
  // do something with result
}

The result is the URLResult object which contains:

  • urlResponse: URLResponse? which is the original response which you can read the documentation at here
  • error: Error? which is an error if happens. it will be nil on success response
  • responseData: Data? which is raw data of the response body
  • isFailed: Bool which is true if request is failed
  • isSucceed: Bool which is true if the request is succeed
  • httpMessage: HTTPResultMessage? which is the response message of the request. It Will be nil if the result is not an HTTP result

The HTTPResultMessage is the detailed HTTP response from the URLResult:

  • url: HTTPURLCompatible which is the origin URL of the response
  • headers: Header which is headers of the response
  • body: Data? which is the body of the response
  • statusCode: Int which is the status code of the response

You can get the promise object or ignore it. It will return DataPromise which contains the status of the request

let requestPromise = Ness.default
  .httpRequest(.get, withUrl: "https://myurl.com")
  ..
  ..
  .dataRequest()
let status = requestPromise.status

The statuses are:

  • running(Float) which contains the percentage of request progress from 0 - 1
  • dropped
  • idle
  • completed(HTTPURLResponse) which contains the completed response
  • error(Error) which contains an error if there are occurs

you can cancel the request using drop() function:

requestPromise.drop()

since the promise is based on the Ergo Promise, it contains the result of the request if it already finished and an error if the error occurs:

// will be nil if the request is not finished yet or if the error occurs
let result = requestPromise.result

// will be nil if an error did not occur or the request is not finished yet
let error = requestPromise.error

// will be true if request completed
print(requestPromise.isCompleted)

Upload Request Promise

Upload requests are the same as Data requests in terms of Promise.

Download Request Promise

Download requests have a slight difference from data requests or upload requests. The download request can be paused and resumed, and the result is different

The result is the DownloadResult object which contains:

  • urlResponse: URLResponse? which is the original response which you can read the documentation at here
  • error: Error? which is an error if happens. it will be nil on success response
  • dataLocalURL: URL? which is the location of downloaded data
  • isFailed: Bool which is true if request is failed
  • isSucceed: Bool which is true if the request is succeed

You can pause the download and resume:

request.pause()

let resumeStatus = request.resume()

resume will return ResumeStatus which is enumeration:

  • resumed
  • failToResume

Decode Response Body For Data Request

to parse the body, you can do:

let decodedBody = try? result.message.parseBody(using: myDecoder)

the parseBody are accept any object that implement ResponseDecoder. The declaration of ResponseDecoder protocol is like this:

public protocol ResponseDecoder {
  associatedtype Decoded
  func decode(from data: Data) throws -> Decoded
}

so you can do something like this:

class MyResponseDecoder: ResponseDecoder {
  typealias Decoded = MyObject
   
  func decode(from data: Data) throws -> MyObject {
    // do something to decode data into MyObject
  }
}

there are default base decoder you can use if you don't want to parse from Data

class MyJSONResponseDecoder: BaseJSONDecoder<MyObject> {
  typealias Decoded = MyObject
   
  override func decode(from json: [String: Any]) throws -> MyObject {
    // do something to decode json into MyObject
  }
}

class MyStringResponseDecoder: BaseStringDecoder<MyObject> {
  typealias Decoded = MyObject
   
  override func decode(from string: String) throws -> MyObject {
    // do something to decode string into MyObject
  }
}

the HTTPResultMessage have default function to automatically parse the body which:

  • func parseBody(toStringEndcoded encoding: String.Encoding = .utf8) throws -> String
  • func parseJSONBody() throws -> [String: Any]
  • func parseArrayJSONBody() throws -> [Any]
  • func parseJSONBody<DObject: Decodable>() throws -> DObject
  • func parseArrayJSONBody<DObject: Decodable>() throws -> [DObject]
  • func parseJSONBody<DOBject: Decodable>(forType type: DOBject.Type) throws -> DOBject
  • func parseArrayJSONBody<DObject: Decodable>(forType type: DObject.Type) throws -> [DObject]

Validator

You can add validation for the response like this:

Ness.default
  .httpRequest(.get, withUrl: "https://myurl.com")
  ..
  ..
  .validate(statusCodes: 0..<300)
  .validate(shouldHaveHeaders: ["Content-Type": "application/json"])
  .dataRequest()

If the response is not valid, then it will have an error or be dispatched into handle closure with an error.

the provided validate method are:

  • validate(statusCode: Int) -> Self
  • validate(statusCodes: Range<Int>) -> Self
  • validate(shouldHaveHeaders headers: [String:String]) -> Self
  • validate(_ validation: HeaderValidator.Validation, _ headers: [String: String]) -> Self

You can add custom validator to validate the http response. The type of validator is URLValidator:

public protocol ResponseValidator {
  func validate(for response: URLResponse) -> ResponseValidatorResult
}

ResponseValidatorResult is a enumeration which contains:

  • valid
  • invalid
  • invalidWithReason(String) invalid with custom reason which will be a description on NetworkSessionError Error

and put your custom ResponseValidator like this:

Ness.default
  .httpRequest(.get, withUrl: "https://myurl.com")
  ..
  ..
  .validate(using: MyCustomValidator())
  .dataRequest()

You can use HTTPValidator if you want to validate only HTTPURLResponse and automatically invalidate the other:

public protocol HTTPValidator: URLValidator {
  func validate(forHttp response: HTTPURLResponse) -> URLValidatorResult
}

Remember you can put as many validators as you want, which will validate the response using all those validators from the first until the end or until one validator returns invalid If you don't provide any URLValidator, then it will be considered invalid if there's an error or no response from the server, otherwise, all the responses will be considered valid

NetworkSessionManagerDelegate

You can manipulate request or action globally in Session level by using NetworkSessionManagerDelegate:

public protocol NetworkSessionManagerDelegate: class {
  func ness(_ manager: Ness, willRequest request: URLRequest) -> URLRequest
  func ness(_ manager: Ness, didRequest request: URLRequest) -> Void
}

both methods are optional. The methods will run and functional for:

  • ness(_: , willRequest: ) will run before any request executed. You can manipulate URLRequest object here and return it or do anything before request and return the current URLRequest
  • ness(_: , didRequest: ) will run after any request is executed, but not after the request is finished.

RetryControl

You can control when to retry if your request is failed using RetryControl protocol:

public protocol RetryControl {
  func shouldRetry(
    for request: URLRequest, 
    response: URLResponse?, 
    error: Error, 
    didHaveDecision: (RetryControlDecision) -> Void) -> Void
}

The method will run on a request failure. The only thing you need to do is pass the RetryControlDecision into didHaveDecision closure which is an enumeration with members:

  • noRetry which will automatically fail the request
  • retryAfter(TimeInterval) which will retry the same request after TimeInterval
  • retry which will retry the same request immediately

You can assign RetryControl when preparing a request:

Ness.default
  .httpRequest(.get, withUrl: "https://myurl.com")
  ..
  ..
  .dataRequest(with: myRetryControl)

It can be applicable for download or upload requests too.

iONess has some default RetryControl which is CounterRetryControl that the basic algorithm is just counting the failure time and stop retry when the counter reaches the maxCount. to use it, just init the CounterRetryControl when preparing with your maxCount or optionally with TimeInterval before retry. For example, if you want to auto-retry a maximum of 3 times with a delay of 1 second for every retry:

Ness.default
  .httpRequest(.get, withUrl: "https://myurl.com")
  ..
  ..
  .dataRequest(
    with: CounterRetryControl(
      maxRetryCount: 3, 
      timeIntervalBeforeTryToRetry: 1
    )
  )

DuplicatedHandler

You can handle what to do if there are multiple duplicated request happen with DuplicatedHandler:

public protocol DuplicatedHandler {
  func duplicatedDownload(request: URLRequest, withPreviousCompletion previousCompletion: @escaping URLCompletion<URL>, currentCompletion: @escaping URLCompletion<URL>) -> RequestDuplicatedDecision<URL>
  func duplicatedUpload(request: URLRequest, withPreviousCompletion previousCompletion: @escaping URLCompletion<Data>, currentCompletion: @escaping URLCompletion<Data>) -> RequestDuplicatedDecision<Data>
  func duplicatedData(request: URLRequest, withPreviousCompletion previousCompletion: @escaping URLCompletion<Data>, currentCompletion: @escaping URLCompletion<Data>) -> RequestDuplicatedDecision<Data>
}

It will ask for RequestDuplicatedDecision depending on what type of duplicated request. The RequestDuplicatedDecision are enumeration with members:

  • dropAndRequestAgain which will drop the previous request and do a new request with the current completion
  • dropAndRequestAgainWithCompletion((Param?, URLResponse?, Error?) -> Void) which will drop previous request and do new request with custom completion
  • ignoreCurrentCompletion which will ignore the current completion, so when the request is complete, it will just run the first request completion
  • useCurrentCompletion which will ignore the previous completion, so when the request is complete, it will just run the lastest request completion
  • useBothCompletion which will keep the previous completion, so when the request is complete, it will just run all the request completion
  • useCompletion((Param?, URLResponse?, Error?) -> Void) which will ignore all completion and use the custom one

The duplicatedHandler is stick to the Ness \ NetworkSessionManager, so if you have duplicated request with different Ness \ NetworkSessionManager, it should not be called.

To assign RequestDuplicatedDecision, you can just assign it to duplicatedHandler property, or just add it when init:

// just handler
let ness = Ness(duplicatedHandler: myHandler)

// with session
let ness = Ness(session: mySession, duplicatedHandler: myHandler)

// using property
ness.duplicatedHandler = myHandler

Or you can just use some default handler:

// just handler
let ness = Ness(onDuplicated: .keepAllCompletion)

// with session
let ness = Ness(session: mySession, onDuplicated: .keepFirstCompletion)

// using property
ness.duplicatedHandler = DefaultDuplicatedHandler.keepLatestCompletion

There are 4 DefaultDuplicatedHandler:

  • dropPreviousRequest which will drop the previous request and do a new request with the current completion
  • keepAllCompletion will keep the previous completion, so when the request is complete, it will just run all the request completion
  • keepFirstCompletion which will ignore the current completion, so when the request is complete, it will just run the first request completion
  • keepLatestCompletion which will ignore the previous completion, so when the request is complete, it will just run the lastest request completion

Contribute

You know how, just clone and do pull request

Comments
  • Feature/ergo

    Feature/ergo

    • Introduce Ergo as Promise pipelining handler
    • Remove unnecessary old Promise pipelining handler
    • Remove unnecessary integration tests, regarding with new Promise, since its already been tested in its repository
    • Update README.md
    • Bump major version because it will break the old one If updated automatically
    opened by hainayanda 0
  • Add to Swift Package Manager

    Add to Swift Package Manager

    • Code not changing even a little bit
    • Add to Swift Package Manager
    • Move files to match Swift Package Manager folder structure
    • Bump swift version to 5.1 so it matched with latest stable Swift Package Manager
    enhancement 
    opened by hainayanda 0
  • Release 1.2.3

    Release 1.2.3

    • Added observer method call
    request.then(
        observing: someScreen, 
        call: SomeScreen.methodName,
        whenFailedCall: SomeScreen.failedMethodName,
        finallyCall: SomeScreen.finallyMethodName
    )
    
    • Change retry delaying dispatcher to be run on the current dispatcher if possible
    • Integration test for observer method call
    opened by hainayanda 0
  • Added github actions for Unit Test

    Added github actions for Unit Test

    • Update Quick and Nimble pod
    • Added script to run Unit Test
    • Added github actions to run those Unit Test everytime someone do pull request
    • Fix some unit test according to newest Quick and Nimble
    opened by hainayanda 0
  • Release 1.2.1

    Release 1.2.1

    • Added LockRunner protocol to abstract safe multithread access
    • Change RetryControl closure into escaping
    • Added Finally block
    • Added Timeout
    • Fix Aggregate not running first aggregated request
    • Make some weak variable on closure strong, since in the real implementation, there's no one keeping the variable to be strong and the variable will always be released after the closure completed
    • Fix retry control called when task is cancelled
    • Remove some mutating to achieve more functional code, thus less effect to any outside variable
    • Added some unit test for aggregate
    • Added validate and retrycontrol to unit test
    • Added unit test for decoder
    opened by hainayanda 0
  • Release 1.2.0

    Release 1.2.0

    • Change RetryControl from sync into async, so it will be more flexible in implementation
    • Impacting all request typr, since it changing from sync to async when decide how to retry
    opened by hainayanda 0
Releases(2.0.2)
  • 2.0.2(Mar 28, 2022)

  • 2.0.1(Jul 21, 2021)

  • 2.0.0(Jul 2, 2021)

    • Introduce Ergo as Promise pipelining handler
    • Remove unnecessary old Promise pipelining handler
    • Remove unnecessary integration tests, regarding with new Promise, since its already been tested in its repository
    • Update README.md
    • Bump major version because it will break the old one If updated automatically
    Source code(tar.gz)
    Source code(zip)
  • 1.2.5(Apr 12, 2021)

  • 1.2.4(Feb 2, 2021)

    • Code not changing even a little bit
    • Add to Swift Package Manager
    • Move files to match Swift Package Manager folder structure
    • Bump swift version to 5.1 so it matched with latest stable Swift Package Manager
    Source code(tar.gz)
    Source code(zip)
  • 1.2.3(Jan 15, 2021)

    • Added observer method call
    request.then(
        observing: someScreen, 
        call: SomeScreen.methodName,
        whenFailedCall: SomeScreen.failedMethodName,
        finallyCall: SomeScreen.finallyMethodName
    )
    
    • Change retry delaying dispatcher to be run on the current dispatcher if possible
    • Integration test for observer method call
    Source code(tar.gz)
    Source code(zip)
  • 1.2.2(Oct 28, 2020)

  • 1.2.1(Oct 28, 2020)

    Added

    • Added Finally block
    • Added Timeout
    • Added LockRunner protocol to abstract safe multithread access

    Changed

    • Change RetryControl closure into escaping
    • Remove some mutating method to achieve more functional code, thus less effect to any outside variable
    • Make some weak Dropable on closure strong, since in the real implementation, there's no one keeping the Dropable to be strong and the Dropable will always be released after the closure completed

    Fixed

    • Fix Aggregate not run the first aggregated request
    • Fix RetryControl called when task is cancelled by checking the failure reason

    Unit Test

    • Added some unit test for aggregate
    • Added validate and RetryControl to unit test
    • Added unit test for decoder
    Source code(tar.gz)
    Source code(zip)
  • 1.2.0(Oct 27, 2020)

  • 1.1.1(Oct 26, 2020)

  • 1.1.0(Oct 22, 2020)

  • 1.0.0(Oct 21, 2020)

Owner
OSFE Homecredit Indonesia
Open Source Multi-Platform Development of Homecredit Indonesa.
OSFE Homecredit Indonesia
This package is meant to make http request of an easy way inspiren in the architecture of Moya package

NetworkAgent This package is meant to make http request of an easy way inspiren in the architecture of Moya package. This package is 100% free of depe

Angel Rada 19 Sep 8, 2022
Http Request wrapper written in Swift

Net Net is a HttpRequest wrapper written in Swift Features GET, POST, PUT, DELETE method Powerful request params: nested params, number, string, dic,

Le Van Nghia 304 Jun 29, 2022
An Generic HTTP Request Library For Swift

GRequest An HTTP request library written in Swift. ##Basic Usage Be simple, as it should be: Request("https://api.github.com/repos/lingoer/SwiftyJSON/

Ruoyu Fu 114 Oct 18, 2022
Minimalist HTTP request library via async / await

Making HTTP API requests in Swift using Foundation can be verbose and use a mix of types like URL, URLComponents and URLRequest to form a request and then handling all the encoding and decoding steps later

Nicholas Maccharoli 13 Nov 5, 2022
Deal with query items, HTTP headers, request body and more in an easy, declarative way

Reusable system for complex URL requests with Swift. Deal with query items, HTTP headers, request body and more in an easy, declarative way. Check out our engineering blog to learn more!

Parable Health 19 Sep 5, 2022
Http - Demo for Http Layer

http Example To run the example project, clone the repo, and run pod install fro

null 0 Jan 24, 2022
Cross-platform JsonRPC client implementation with HTTP and WebSocket support

JsonRPC.swift Cross-platform JsonRPC client implementation with HTTP and WebSocket support Getting started Installation Package Manager Add the follow

Tesseract 5 Oct 19, 2022
The HTTP library used by the Spotify iOS client

Authentication and back-off logic is a pain, let's do it once and forget about it! This is a library that allows you to centralise this logic and forg

Spotify 625 Nov 20, 2022
An easy to integrate Model Based Google Maps Helper (SVHTTPClient, AFNetworking) That lets you Geo Code , Reverse Geocode, Get Directions , Places Autocomplete.

GoogleMapsHelper Read Me in Russian : http://gargo.of.by/googlemapshelper/ A GOOGLE MAPS Helper that help you do multiple tasks like HOW TO USE // usi

Zeeshan Haider 21 Jul 28, 2022
Request adapter for URL requests from "MovieLister" demo app (Swift for Good book, a chapter by Ben Scheirman)

RequestAdapter Request adapter for URL requests from "MovieLister" demo app (Swift for Good book, a chapter by Ben Scheirman) The code is taken from:

Mihaela Mihaljevic Jakic 0 Nov 22, 2021
An iOS library to route API paths to objects on client side with request, mapping, routing and auth layers

WANetworkRouting Developed and Maintained by ipodishima Founder & CTO at Wasappli Inc. Sponsored by Wisembly A routing library to fetch objects from a

null 10 Nov 20, 2022
Type-safe networking abstraction layer that associates request type with response type.

APIKit APIKit is a type-safe networking abstraction layer that associates request type with response type. // SearchRepositoriesRequest conforms to Re

Yosuke Ishikawa 1.9k Dec 30, 2022
Automatically sets the network activity indicator for any performed request.

BigBrother BIG BROTHER IS WATCHING YOU. BigBrother is a Swift library made for iOS that automatically watches for any performed request and sets the n

Marcelo Fabri 446 Dec 17, 2022
NSURLSession network abstraction layer, using Codable and Decodable for response and Encodable for request. ⚙️🚀

SONetworking NSURLSession network abstraction layer, using Codable and Decodable for response and Encodable for request. Project Folder and File Struc

Ahmad AlSofi 4 Jan 28, 2022
YTKNetwork is a high level request util based on AFNetworking.

YTKNetwork What YTKNetwork is a high level request util based on AFNetworking. It's developed by the iOS Team of YuanTiKu. It provides a High Level AP

猿辅导技术团队 6.5k Jan 6, 2023
DBNetworkStack is a network abstraction for fetching request and mapping them to model objects

DBNetworkStack Main Features ?? Typed network resources ?? Value oriented architecture ?? Exchangeable implementations ?? Extendable API ?? Composable

DB Systel GmbH 33 Jan 10, 2022
Super lightweight async HTTP server library in pure Swift runs in iOS / MacOS / Linux

Embassy Super lightweight async HTTP server in pure Swift. Please read: Embedded web server for iOS UI testing. See also: Our lightweight web framewor

Envoy 540 Dec 15, 2022
A little and powerful iOS framework for intercepting HTTP/HTTPS Traffic.

A little and powerful iOS framework for intercepting HTTP/HTTPS Traffic.

Proxyman 867 Dec 29, 2022
Extensible HTTP Networking for iOS

Bridge Simple Typed JSON HTTP Networking in Swift 4.0 GET GET<Dict>("http://httpbin.org/ip").execute(success: { (response) in let ip: Dict = respo

null 90 Nov 19, 2022