Skip to content

Make simple API request using functional programming

License

Notifications You must be signed in to change notification settings

railwaymen/restler

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Restler

Package Build Status Example App Build Status Coverage Status

The Restler framework has been built to use features of the newest versions of Swift. Inspiration for it is the Vapor library for building Server-side with Swift. What we love is functional programming, so you can build your desired request just calling some chained functions. The main goal of the framework is to provide a nice interface for making API requests the easiest as possible and the fastest as possible.

List of Content

  1. Documentation
  2. Instalation
  3. Usage
  4. Contribution

Documentation

We think that README isn't a good place for complete documentation so that's why we decided to generate it to a folder inside the framework repository. Full documentation you can find in the Documentation folder. If you're looking for a description of a specific protocol or class, we put here a list of the most important symbols.

Restler

RestlerType - it's the main protocol which should be used when it comes to mocking or using Restler's class' instance.

Request builder

All these protocols are defined in one file: RestlerRequestBuilderType

Request

Restler.Request - generic class for all request types provided by Restler.

Errors

  • Restler.Error - errors returned by Restler.
  • Restler.ErrorType - types which Restler can decode by himself. Every different type would be an unknownError.

Error parser

Instalation

Nothing is easier there - you just add the framework to the Swift Package Manager dependencies if you use one.

Otherwise you can use CocoaPods. If you use one simply add to your Podfile:

...
pod 'Restler/Core'
...

It's important to specify it with /Core! (Changed in v1.0) and call in your console:

pod install

Import the framework to the project:

import RestlerCore

and call it!

Usage Examples

Error parser

If you don't want to add the same error to be parsed on a failure of every request, simply add the error directly to the error parser of the Restler object.

restler.errorParser.decode(ErrorToDecodeOnFailure.self)

If you don't want to decode it anymore, simply stop decoding it:

restler.errorParser.stopDecoding(ErrorToDecodeOnFailure.self)

Header

Setting header values is very easy. Simply set it as a dictionary:

restler.header = [
  .contentType: "application/json",
  .cacheControl: "none",
  "customKey": "value"
]
restler.header[.cacheControl] = nil

If you're using basic authentication in the "Authorization" key, simply provide username and password to the header:

restler.header.setBasicAuthentication(username: "me", password: "password")

Restler calls

GET

Restler(baseURL: myBaseURL)
  .get(Endpoint.myProfile) // 1
  .query(anEncodableQueryObject) // 2
  .failureDecode(ErrorToDecodeOnFailure.self) // 3
  .setInHeader("myNewTemporaryToken", forKey: "token") // 4
  .receive(on: .main) // 5
  .decode(Profile.self) // 6
  // 7

  .subscribe(
    onSuccess: { profile in // 8
      updateProfile(with: profile)
    },
    onCompletion: { _ in // 9
      hideLoadingIndicator()
  })
  1. Makes GET request to the given endpoint.
  2. Encodes the object and puts it in query for the GET request.
  3. If an error will occur, an error parser would try to decode the given type.
  4. Sets the specified value for the given key in the header only for this request.
  5. Sets dispatch queue on which completion handlers will be called to the main queue.
  6. Decodes Profile object on a successful response. If it is not optional, a failure handler can be called.
  7. Since this moment we're operating on a request, not a request builder.
  8. A handler called if Restler would successfully end the request.
  9. A handler called on completion of the request whatever the result would be.

POST

Restler(baseURL: myBaseURL)
  .post(Endpoint.myProfile) // 1
  .body(anEncodableQueryObject) // 2
  .failureDecode(ErrorToDecodeOnFailure.self)
  .decode(Profile.self)

  .subscribe(
    onFailure: { error in // 3
      print("\(error)")
    },
    onCompletion: { _ in
      hideLoadingIndicator()
  })
  1. Makes POST request to the given endpoint.
  2. Encodes the object and puts it into the body of the request. Ignored if the selected request method doesn't support it.
  3. A handler called if the request has failed.

Other

Any other method call is very similar to these two, but if you have questions simply create an issue.

Restler + Combine

  • Restler approach:
Restler(baseURL: myBaseURL)
  .get("/profile") // 1
  .decode(Profile.self) // 2
  .publisher // 3
  .catch { _ in Empty() } // 4
  .assign(to: \.profile, on: self) // 5
  .store(in: &subscriptions) // 6
  1. Makes GET request to the given endpoint.
  2. Decode Profile object.
  3. Convert the request object to publisher for easy using Combine in your code.
  4. Handle the thrown error
  5. Assign each element from a Publisher to a property on an object.
  6. Stores this type-erasing cancellable instance in the specified collection.
  • More Combine approach:
Restler(baseURL: myBaseURL)
  .get(Endpoint.myProfile) // 1
  .query(anEncodableQueryObject) // 2
  .publisher()? // 3
  .receive(on: DispatchQueue.main) // 4
  .map(\.data) // 5
  .decode(type: Profile.self, decoder: JSONDecoder()) // 6
  .catch { _ in Empty() } // 7
  .assign(to: \.profile, on: self) // 8
  .store(in: &subscriptions) // 9
  1. Makes GET request to the given endpoint.
  2. Encodes the object and puts it in query for the GET request.
  3. Builds a request and returns publisher for Combine support.
  4. Specifies the scheduler on which to receive elements from the publisher. In this case the main queue.
  5. Get Data object from DataTaskPublisher.
  6. Decode Profile object.
  7. Handle the error
  8. Assigns each element from a Publisher to a property on an object.
  9. Stores this type-erasing cancellable instance in the specified collection.

Restler + RxSwift

First of all, you need to add RxRestler to your target you can do it simply in SPM. In CocoaPods you should add to your Podfile:

pod `Restler/Rx`

Then import RxRestler to every file it's needed.

Restler(baseURL: myBaseURL)
  .get(Endpoint.myProfile)
  .query(anEncodableQueryObject)
  .receive(on: .main) // 1
  .decode(Profile.self) // 2
  .rx // 3
  .subscribe( // 4
    onSuccess: { print("This is my profile:", $0) },
    onError: { print("This is an error:", $0) })
  .disposed(by: bag) // 5
  1. Subscribe handlers will be called on the provided queue even if it's done with RxSwift (setting a scheduler with this property set may cause some little delay between receiving a response and handling it but the handlers will be called on the provided scheduler).
  2. Decode some type on successful response - Void, Data, or some custom object.
  3. Move request to Rx usage. This returns Single<Profile> in this case.
  4. Here we call already the RxSwift function.
  5. Remember about adding the Disposable to the DisposeBag. The networking task will be canceled automatically if the bag will deinitialize.

Contribution

If you want to contribute in this framework, simply put your pull request here.

If you have found any bug, file it in the issues.

If you would like Restler to do something else, create an issue with a feature request.

Configuration

  1. Clone the project and open the project's folder in the terminal.
  2. Run a configuration script: ./Scripts/configure.sh
  3. Fill configuration file in folder Restler-Example/Restler-Example/Configuration named Debug.xcconfig with needed information.
  4. Open the project in the folder Restler-Example. You can do it from the terminal: open Restler-Example/Restler-Example.xcodeproj
  5. Run tests to be sure everything works properly.

Lint

Run command ./Scripts/pod_lib_lint.rb Restler.podspec to lint the podspec before pushing changes to the repo.

Releasing

  1. Open the project root directory.
  2. cd Scripts/releaseTool
  3. swift run ReleaseTool release ../..

Dependencies

Gems