Mockingbird is a Network Abstraction Layer (NAL) written in swift leveraging the power and versatility of Foundation's URLSession. It's compatible with iOS version 10 or greater and macOS version 10.13 or greater.
It draws inspiration from Moya and Alamofire with a focus in easier integration and unit testing of the network layer.
At the moment only Cocoapods and Swift Package Manager SPM are supported, with support for Carthage comming soon. Mockingbird provides two main versions, one with support for Swift 4.2 which supports iOS 9 and greater and the other one with support for Swift 5 which itself supports iOS 10 and greater. Both versions support src="https://raw.github.com/jandro-es/Mockingbird/master/ from 10.12 onwards Install the one your application needs.
Mockingbird supports Xcode 11 package manager
To install Mockingird
using Cocoapods just add it to your project's Podfile
:
pod 'Mockingbird-Swift', '~> 1.0'
and if you want to use the RxSwift extensions add them:
pod 'Mockingbird-Swift/RxSwift', '~> 1.0'
pod 'Mockingbird-Swift', '~> 2.0'
and if you want to use the RxSwift extensions add them:
pod 'Mockingbird-Swift/RxSwift', '~> 2.0'
To use the library in your application please import the Swift module as follows:
import Mockingbird_Swift
Just add Mockingbird
as a dependency in your Package.swift
file:
dependencies: [
.package(url: "https://github.com/jandro-es/Mockingbird", .upToNextMajor(from: "1.0.0"))
]
dependencies: [
.package(url: "https://github.com/jandro-es/Mockingbird", .upToNextMajor(from: "2.0.0"))
]
Mockingbird simplifies the creation of a network layer in your application. You can define your entire layer in a declarative way by implementing the RemoteServiceType
protocol in your different types. Generating a simple map of your network endpoints. Mockingbird uses URLSession
as it's undelying network system, exposing it's configuration, allowing you to customise it, among many other customisations, as needed.
A simplified architecture diagram (please have a look at the code for more details):
The first step when building your network layer would be to define, in a declarative way, the service or API you want to interact with. For defining these remote APIs you can use Enum, Struct or Class, but we've found out that the clearest one is using an Enum but it might be different in your use case. To declare an API we need to implement the protocol RemoteService. A simple example will be as:
enum GitHub {
case zen
case userProfile(String)
}
extension GitHub: RemoteServiceType {
var baseURL: URL {
return URL(string: "https://api.github.com")!
}
var path: String {
switch self {
case .zen:
return "/zen"
case .userProfile(let name):
return "/users/\(name)"
}
}
var method: HTTPMethod {
return .get
}
var requestType: RequestType {
switch self {
case .zen, .userProfile:
return .plain
}
}
var testData: Data {
switch self {
case .zen:
return "The hardest thing in this world is to live in it".data(using: String.Encoding.utf8)!
case .userProfile(let name):
return "{\"login\": \"\(name)\", \"id\": 123456}".data(using: String.Encoding.utf8)!
}
}
var validation: RequestValidation {
return .successAndRedirectCodes
}
var headers: [String: String]? {
return nil
}
}
This example declares an interface for the GitHub API, with two endpoints, .zen
and userProfile
. Let's see step by step each one of the requirements.
As it's name indicates, this is the base URL of the API. We could have different bases per endpoint (using enums) but that's considered a bad practice, is better to declare different services per base URL. The base URL should contain the protocol information.
This is the fully qualified path of the endpoint, containing all the URL parameters needed (not query string ones). Mockingbird does not escape those parameters automatically.
This is the HTTP method / verb to use for the request. Mockingbird provides as an enum the following values:
This is the type of request Mockingbird should do, meaning how to prepare the request and how to proccess the responnse. The available request types are:
Request
Request
has a body with Data
Request
has a body with an Encodable
objectRequest
has a body with an Encodable
object using a custom JSONEncoder
ParameterEncodingType
as encoderData
typeData
) and URL parametersThis is the Data
object to return, for the requested endpoint when using stubbing. It can be useful for testing, or even for development under contract agreement.
Mockingbird automatically validates the response based on the required validation technique. This allows to simplify your error management code. The available validation techniques are:
This is just an optional dictionary of HTTP headers to add to the request.
It can be quite simple, if you're happy using the defaults you just need to let the swift compiler know the type:
let githubAPI = Mockingbird<GithubAPI>()
Mockingbird instances allow for a lot of customisation and / or configuration, if you want to know all the options I recommend having a look at the tests, as all of the possible cases are covered there.
The most common scenarios when initialising Mockingbird are:
let githubAPI = Mockingbird<GithubAPI>(middleware: [Middleware.networkActivityIndicator])
Creates an instance for GithubAPI
with the given Middleware.
let githubAPI = Mockingbird<GithubAPI>(sessionConfiguration: configuration)
Creates an instance for GithubAPI
using the given URLSessionConfiguration.
let githubAPI = Mockingbird<GithubAPI>(stubLambda: Mockingbird.immediatelyStub)
Creates an instance for GithubAPI
that retuns, without delay, a stub. This stub is the testData
declared in the GithubAPI
enum.
When using closures as the return technique, it returns a Result
type that has success(value)
and an failure(error)
cases.
githubAPI.request(.zen) { result in
switch result {
case let .success(response):
print("data: \(response.data)")
case let .failure(mockingbirdError):
print("error: \(mockingbirdError)")
}
}
RxSwift extensions are provided out of the box. To use them when requesting data:
githubAPI.rx.request(.zen)
.subscribe { event in
switch event {
case .success(let response):
print("data: \(response.data)")
case .error:
print("error: \(mockingbirdError)")
}
Using RxSwift allows for a clean validation and manipulation using the available Rx operators.
Middleware is a powerful way of adding logging, authentication tokens and / or modifying your requests and responses in a clean and decoupled way. It's defined by the protocol MiddlewareType. Two middleware types are included:
This middleware allows you to automatically add an authentication token to all the requests that need it. It's defined in AccessTokenMiddleware. The supported types of auth token are:
Basic
AccessTokenBearer
AccessTokencustom
AccessTokenTo have a look on how to use it have a look at the tests in MockingbirdIntegrationTests.
This middleware executes a block when the actual network request starts, and finishes. Allowing the user to for example update the UI. One common example in iOS is to show and hide the NetworkActivityIndicator
.
static var networkActivityIndicator: NetworkActivityMiddleware {
return NetworkActivityMiddleware { status, _ in
switch status {
case .began:
DispatchQueue.main.async {
UIApplication.shared.isNetworkActivityIndicatorVisible = true
}
case .ended:
DispatchQueue.main.async {
UIApplication.shared.isNetworkActivityIndicatorVisible = false
}
}
}
}
Mockingbird uses Apple's Unified Logging.
Subsystem | Category |
---|---|
com.filtercode.mockingbird | request_operation |
com.filtercode.mockingbird | request |
com.filtercode.mockingbird | response |
com.filtercode.mockingbird | middleware |
We use Swift Package Manager to develop the framework, after cloning the project run:
swift package generate-xcodeproj
to generate a Xcode project. For building run:
swift build
and for running the tests (assumming you have xcpretty installed, remove the pipe if not).
xcodebuild test -scheme Mockingbird-Package -project Mockingbird.xcodeproj -enableCodeCoverage YES | xcpretty
This project is released under MIT license.
link |
Stars: 1 |
Last commit: 4 years ago |
Swiftpack is being maintained by Petr Pavlik | @ptrpavlik | @swiftpackco | API | Analytics