Neorequest 2.7.0

Neorequest 2.7.0

TestsTested
LangLanguage SwiftSwift
License Apache 2
ReleasedLast Release Mar 2019
SPMSupports SPM

Maintained by Edison Santiago.



 
Depends on:
SwiftHTTP>= 0
Locksmith>= 0
PKHUD>= 0
 

  • By
  • Neomode

Neorequest

Neorequest is a simple wrapper around SwiftHTTP with some extras features and providing a cleaner syntax by decoupling error and sucess handling.

Features

  • Cleaner syntax, with separate closures for managing error and success of the request
  • Support for Swift Decodable protocol
  • Request Authorization management
  • Management of loading indicators on the view

Installation

CocoaPods

Neorequest is available through CocoaPods. Just add pod 'Neorequest' to your Podfile and run pod install

Getting Started

Creating a simple request

All responses are automatically parsed to an object using the 'Decodable' protocol. Making a request with Neorequest is as simple as:

Neorequest<Object>.GET(
	url: "https://yoururl.com",
	onError: {
		error in
		//Handle request error here
	},
	onSuccess: {
		obj in
		//Handle success response here
	}
)

Sending parameters

You can simply pass a Dictionary<String: Any> to the parameters property with the data you want to send.

Neorequest<Object>.POST(
    url: "https://yoururl.com",
    parameters: ["name": "John Doe", "age": 7],
    onError: {
        error in
        //Handle request error here
    },
    onSuccess: {
        obj in
        //Handle success response here
    }
)

For uploading files you can refer to the Swift HTTP Upload and simply pass the Upload object as a parameter. To learn how to send a x-www-form-urlencoded request please refer to the NeorequestConfiguration section of this file.

Reading the response

onSuccess((T) -> Void) receives the parsed object specified on the call. All objects are created using the Decodable protocol, so make sure you conform to the protocol.
If the response is an empty object ({}) or an empty response you can provide an empty object as the generic parameter. For convenience, Neorequest provides a NeorequestEmptyResponse out of the box for those cases.

For information about configuring the JSONDecoder to set the dateFormat and other options please refer to the NeorequestConfiguration section of this file.

You can retrieve the values for the response values the same way you do with any Dictionary:

Neorequest<Object>.GET(
    url: "https://yoururl.com",
    onError: {
        error in
        //Handle request error here
    },
    onSuccess: {
        obj in
        //The object is already created an ready for use. No need to parsing.
        print(obj.name)
    }
)

Please note that the response is always parsed to an object using the Swift 4 Decodable Protocol. If an error is thrown during the parsing the onError((Error) -> Void) closure will be called with an error NeorequestError.ParseError(content: String?), where content is a conversion of the original Data received to an UTF8 String.

Arrays on first level are supported by Neorequest. Since the Swift 4 Decodable makes an array of Decodable conformant to Decodable by default all you have to do is pass an array as the call parameter. Please note that a single object is not convertible to an array and vice versa, so passing the wrong type will result in a parse error.

Neorequest<[Object]>.GET(
    url: "https://yoururl.com",
    onError: {
        error in
        //Handle request error here
    },
    onSuccess: {
        objects in
        for object in objects {
            //Do something with the objects of the array
            print(object.name)
        }
    }
)

Global Headers

If you must include a custom HTTP header in all the requests made on your app you can simply add it to Neorequest.globalHeaders: [String:String].

import Neorequest

//The globalHeaders are saved on NSUserDefaults, so you must retrieve the array to edit it
var globalHeaders = Neorequest<Object>.globalHeaders
globalHeaders["Header"] = "Value"
Neorequest<Object>.globalHeaders = globalHeaders

Do not set the Authorization HTTP header here if you are using NeorequestAuthorization.

NeorequestConfiguration

The NeorequestConfiguration struct let you set some specific configurations of the request. We put this parameters on a separate struct so the main request call doesn't get bloated with parameters that won't be used all the time.

Please note that all requests has an associated NeorequestConfiguration with it. If no configuration object is passed one with the default values specified below is created.

NeorequestConfiguration Properties

  • loadingType: NeorequestLoadingType: A enum telling if we should show a loading on the desired view along the request. You can show a fullScreen loading or an overScreen loading, as specified below. The default value is none and no loading is shown unless specified.
    • fullScreen(loadingViewController: UIViewController, loadingIconColor: UIColor?): A overlay view is created on top of all UIViewController views with the same backgroundColor as the viewController.view and with an UIActivityIndicator centered and with the desired color.
    • overScreen: We rely on PKHUD to show a loading over the entire app when this mode is set. Since PKHUD uses UIWindow there's no need to a reference of the UIViewController to display the loading.
  • numberOfRetries: Int: How many times the request should fail before calling the error handler. Default: 1
  • isJsonRequest: Bool: Specify if the request must not be sent as json. If set to false we sent the request as x-www-form-urlencoded (but the response is always treated as json). Default: true
  • progress: ((Float) -> Void)?: The progress closure to be set on the original Request. For more information on this please refer to the SwiftHTTP Documentation. Default: nil
  • onOperationCreated: ((HTTP) -> Void)?: A closure called with the original Operation object after it's creation if you want to use NSOperationQueue. Default: nil
  • jsonDecoderConfigurationHandler: ((JSONDecoder) -> JSONDecoder): A closure for configuring the decoder used by Swift Decodable Protocol to create the object.

NeorequestConfiguration Examples

  • Show a gray loading over the entire view while a request is made:
import Neorequest

var config = NeorequestConfiguration()
config.loadingType = .fullScreen(
    loadingViewController: myViewController,
    loadingIconColor: UIColor.lightGray
)

Neorequest<Object>.GET(
    url: "https://yoururl.com",
    configuration: config,
    onError: {
        error in
        //Handle request error here
    },
    onSuccess: {
        obj in
        //Handle success response here
    }
)
  • Send a form-data request, retry 10 times and show a overScreen loading while a request is made:
import Neorequest

var config = NeorequestConfiguration()
config.isJsonRequest = false
config.numberOfRetries = 10
config.loadingType = .overScreen

Neorequest<Object>.GET(
    url: "https://yoururl.com",
    configuration: config,
    onError: {
        error in
        //Handle request error here
    },
    onSuccess: {
        obj in
        //Handle success response here
    }
)
  • Parse a date using a custom date format
import Neorequest

var config = NeorequestConfiguration()
config.jsonDecoderConfigurationHandler = {
    decoder in
    let dateFmt = DateFormatter()
    dateFmt.locale = Locale.current
    dateFmt.timeZone = TimeZone(identifier: "UTC")
    dateFmt.dateFormat = "yyyy-MM-dd'T'HH:mm"
    decoder.dateDecodingStrategy = .formatted(dateFmt)
    return decoder
}

Neorequest<Object>.GET(
    url: "https://yoururl.com",
    configuration: config,
    onError: {
        error in
        //Handle request error here
    },
    onSuccess: {
        obj in
        //Handle success response here
    }
)

NeorequestAuthorization

NeorequestAuthorization is a singleton class that provides a simple way to manage the authorization of all the requests across the entire app. It handles the token expiration and regeneration for you, so you don't need to worry if the token is still valid or not before making a request. If needed, they will be regenerated before the request proceeds.
The NeorequestAuthorization data is used by default on all HTTP requests made after the values are saved. If you need to make a call where the authorization must not be used (i.e., the authentication request) you can specify it with authorization: nil on the request call.

Neorequest<Object>.POST(
    url: "https://yoururl.com",
    authorization: nil,
    parameters: ["name": "John Doe", "age": 7],
    onError: {
        error in
        //Handle request error here
    },
    onSuccess: {
        obj in
        //Handle success response here
    }
)

Properties

NeorequestAuthorization has some properties to hold the values used on the authorization. All the data is saved only on the iOS Keychain, using Locksmith, so the user sensitive information can be kept secure.

  • authType: We support bearer authorization, basic auth and also a custom authorization method where you must provide the tokenString generation. Default: NeorequestAuthorizationType.bearer
  • account: String?, password: String?: Save the authentication info only once so you can retrieve it anytime during the token generation process. Default: nil
  • tokenString: String? | tokenExpirationDate: Date? | tokenUsageTime: TimeInterval: Manage the token regeneration. The token is only regenerated when needed, and in .bearer mode the class manage this by itself and knows when to regenerate the token. The default tokenUsageTime is 1800.

Closures

While NeorequestAuthorization manages a lot of stuff automatically, you need to set some closures with some specific logic for you app if you are using .bearer or .custom auth.

Bearer authentication

  • NeorequestAuthorization.regenerateBearerTokenHandler((NeorequestAuthorization, onError: ((NeorequestError) -> Void), onSuccess: (() -> Void)) -> Void)
    • This closure is responsible for (re)generating the token when needed and checking if it is successful or not. If the user data was already set the NeorequestAuthorization object passed to it contains all the info you previously saved, so you can retrieve then now. Please remember that this method can be called during other requests, so always calls the received closures onError and onSucess to make sure that the other request can continue.
    • Since the token is always regenerated when the remote API returns an 401 Not Authorized, you can get in a loop if you try to make a request with bad authentication data. Because of this, the user data will not be saved automatically, and you should call .save() when you are sure that the credentials are valid. After the user data are saved and a token is generated you no longer need to worry about it and all will be handled for you.
    • This closure must be set before you make any request in bearer mode, so we recommend setting it on the app initialization.

Custom authentication

  • NeorequestAuthorization.regenerateCustomTokenHandler((NeorequestAuthorization, @escaping ((NeorequestError) -> Void), @escaping (() -> Void)) -> Void)

    • Has the same functionality as regenerateBearerTokenHandler. Please refer to this closure explanation above for more info.
  • NeorequestAuthorization.getCustomValidAuthHeaderStringHandler((NeorequestAuthorization, @escaping ((NeorequestError) -> Void), @escaping ((String) -> Void)) -> Void)

    • Use this method to provide a valid string to be used on the Authorization header of the string. Since you are using a custom auth we can't do this for you.
    • In this handler you should verify if we must renew the token using the parameters of NeorequestAuthorization or if the saved token is still valid. If needed, you can call .regenerateCustomTokenHandler to get another token.
    • You should pass the value of the Authorization HTTP header to the onSucess closure. If you need to append any fixed value to the header please do it here.

Saving and removing user data

Saving new data

Create a new instance of the NeorequestAuthorization, set the desired properties and regenerate the token. The regenerateBearerTokenHandler you provided must save the new values. Please note that saving new data will override any existing data since we still do not support handling multiple accounts.

let auth = NeorequestAuthorization()
auth.account = "account"
auth.password = "password"
auth.regenerateBearerToken(
    onError: {
        error in
        //Manage the error
    },
    onSuccess: {
        //User authenticated and token regenerated
    }
)

Retrieving saved data

NeorequestAuthorization.load() will return an object with all the info initialized from the keychain if it exists or return nil if no data is saved on the keychain.

Force token regeneration

If for some reason you must force a token regeneration on the next request you can call expirateToken on a populated object, which will set the token as expired and it will be renewed at the next request. To regenerate the token right now you can use regenerateBearerToken(onError: ((NeorequestError) -> Void), onSuccess: @escaping (() -> Void)).

Remove existing data

NeorequestAuthorization.load()?.delete() will remove any existing data.

Examples

Set the token regeneration handler on app initialization

import Neorequest

NeorequestAuthorization.regenerateBearerTokenHandler = {
    auth, onError, onSuccess in

    //The remote api authentication endpoint uses x-www-form-url-encoded
    var config = NeorequestConfiguration()
    config.isJsonRequest = false

    Neorequest<TokenObject>.POST(
        url: "http://yoururl.com/auth",
        authorization: nil,
        parameters: ["account": auth.account, "password": auth.password],
        configuration: config,
        onError: onError, //Use the provided error handler to return to the original request
        onSuccess: {
            tokenObj in
            //Retrieve the token and save it
            auth.tokenString = "..."

            //Since the request is valid we can save the auth data now
            auth.save()

            //Proceed with the request
            onSuccess()
        }
    )
}

Log an user in for the first time and save it's data

import Neorequest

let auth = NeorequestAuthorization()
auth.account = account
auth.password = password
auth.regenerateBearerToken(
    onError: {
        error in
        //Problem regenerating the token. Maybe the user provided bad credentials?
    },
    onSuccess: {
        //Token regenerated. Proceed with the next requests
    }
)

License

Neorequest is licensed under the Apache v2 License. See the LICENSE file for more info.