Malibu
:surfer: Malibu is a networking library built on promises
Install / Use
/learn @vadymmarkov/MalibuREADME

Description
Palm trees, coral reefs and breaking waves. Welcome to the surf club Malibu,
a networking library built on promises. It's more than just a wrapper
around URLSession, but a powerful framework that helps to chain your
requests, validations and request processing.
Using When under the hood, Malibu adds a lot of sugar helpers and moves your code up to the next level:
- No more "callback hell".
- Your requests are described in one place.
- Response processing could be easily broken down into multiple logical tasks.
- Data and errors are handled separately.
- Your networking code is much cleaner, readable and follows
DRYprinciple.
Equip yourself with the necessary gears of Malibu, become a big wave surfer and let the days of shark infested asynchronous networking be a thing of the past. Enjoy the ride!
Features
- [x] Multiple network stacks
- [x] Declarative requests
- [x] Chainable response callbacks built on promises
- [x] All needed content types and parameter encodings
- [x] HTTP response validation
- [x] Response data serialization
- [x] Response mocking
- [x] Request, response and error logging
- [x] Synchronous and asynchronous modes
- [x] Request pre-processing and middleware
- [x] Request offline storage
- [x] Extensive unit test coverage
Table of Contents
- Catching the wave
- RequestConvertible
- Request
- Networking
- Response
- Logging
- Installation
- Author
- Credits
- Contributing
- License
Catching the wave
You can start your ride straight away, not thinking about configurations:
// Create your request => GET http://sharkywaters.com/api/boards?type=1
let request = Request.get("http://sharkywaters.com/api/boards", parameters: ["type": 1])
// Make a call
Malibu.request(request)
.validate()
.toJsonDictionary()
.then({ dictionary -> [Board] in
// Let's say we use https://github.com/zenangst/Tailor for mapping
return try dictionary.relationsOrThrow("boards") as [Board]
})
.done({ boards in
// Handle response data
})
.fail({ error in
// Handle errors
})
.always({ _ in
// Hide progress bar
})
If you still don't see any benefits, keep scrolling down and be ready for even more magic 😉...
RequestConvertible
Most of the time we need separate network stacks to work with multiple API
services. It's super easy to archive with Malibu. Create an
enum that conforms to RequestConvertible protocol and describe your
requests with all the properties:
enum SharkywatersEndpoint: RequestConvertible {
// Describe requests
case fetchBoards
case showBoard(id: Int)
case createBoard(type: Int, title: String)
case updateBoard(id: Int, type: Int, title: String)
case deleteBoard(id: Int)
// Every request will be scoped by the base url
// Base url is recommended, but optional
static var baseUrl: URLStringConvertible? = "http://sharkywaters.com/api/"
// Additional headers for every request
static var headers: [String: String] = [
"Accept" : "application/json"
]
// Build requests
var request: Request {
switch self {
case .fetchBoards:
return Request.get("boards")
case .showBoard(let id):
return Request.get("boards/\(id)")
case .createBoard(let type, let title):
return Request.post("boards", parameters: ["type": type, "title": title])
case .updateBoard(let id, let title):
return Request.patch("boards/\(id)", parameters: ["title": title])
case .deleteBoard(let id):
return Request.delete("boards/\(id)")
}
}
}
Note that Accept-Language, Accept-Encoding and User-Agent headers are
included automatically.
Request
Request is described with a struct in Malibu:
let request = Request(
// HTTP method
method: .get,
// Request url or path
resource: "boards",
// Content type
contentType: .query,
// Request parameters
parameters: ["type": 1, "text": "classic"],
// Headers
headers: ["custom": "header"],
// Offline storage configuration
storePolicy: .unspecified,
// Cache policy
cachePolicy: .useProtocolCachePolicy)
There are also multiple helper methods with default values for every HTTP method:
// GET request
let getRequest = Request.get("boards")
// POST request
let postRequest = Request.post(
"boards",
// Content type is set to `.json` by default for POST
contentType: .formURLEncoded,
parameters: ["type" : kind, "title" : title])
// PUT request
let putRequest = Request.put("boards/1", parameters: ["type" : kind, "title" : title])
// PATCH request
let patchRequest = Request.patch("boards/1", parameters: ["title" : title])
// DELETE request
let deleteRequest = Request.delete("boards/1")
URLSessionDataTask is default for executing requests. For uploading there
are two additional options that use URLSessionUploadTask instead of
URLSessionDataTask.
// Upload data to url
Request.upload(data: data, to: "boards")
// Upload multipart data with parameters
// You are responsible for constructing a proper value,
// which is normally a string created from data.
Request.upload(
multipartParameters: ["key": "value"],
to: "http:/api.loc/posts"
)
Content types
query- creates a query string to be appended to any existing url.formURLEncoded- usesapplication/x-www-form-urlencodedas aContent-Typeand formats your parameters with percent-encoding.json- sets theContent-Typetoapplication/jsonand sends a JSON representation of the parameters as the body of the request.multipartFormData- sends parameters encoded asmultipart/form-data.custom(String)- uses givenContent-Typestring as a header.
Encoding
Malibu comes with 3 parameter encoding implementations:
FormURLEncoder- a percent-escaped encoding following RFC 3986.JsonEncoder-JSONSerializationbased encoding.MultipartFormEncoder- multipart data builder.
You can extend default functionality by adding a custom parameter encoder
that conforms to ParameterEncoding protocol:
// Override default JSON encoder
Malibu.parameterEncoders[.json] = CustomJsonEncoder()
// Register encoder for the custom encoding type
Malibu.parameterEncoders[.custom("application/xml")] = CustomXMLEncoder()
Cache policy
URLSession handles cache based on the URLRequest.CachePolicy property:
let getRequest = Request.get("boards". cachePolicy: .useProtocolCachePolicy)
URLRequest.CachePolicy.useProtocolCachePolicy is the default policy for URL
load requests. URLSession will automatically add the If-None-Match header
in the request before sending it to the backend. When URLSession gets the
304 Not Modified response status it will call the URLSessionDataTask
completion block with the 200 status code and data loaded from the cached
response.
You can set cachePolicy property to .reloadIgnoringLocalCacheData if you
want to prevent this automatic cache management. Then URLSession will not
add the If-None-Match header to the client requests, and the server will
always return a full response.
Networking
Networking class is a core component of Malibu that executes actual HTTP
requests on a specified API service.
Initialization
It's pretty straightforward to create a new Networking instance:
// Simple networking that works with `SharkywatersEndpoint` requests.
let simpleNetworking = Networking<SharkywatersEndpoint>()
// More advanced networking
let networking = Networking<SharkywatersEndpoint>(
// `OperationQueue` Mode
mode: .async,
// Optional mock provider
mockProvider: customMockProvider,
// `default`, `ephemeral`, `background` or `custom`
sessionConfiguration: .default,
// Custom `URLSessionDelegate` could set if needed
sessionDelegate: self
)
Mode
Malibu uses OperationQueue to execute/cancel requests. It makes it
easier to manage request lifetime and concurrency.
When you create a new networking instance there is an optional argument to specify mode which will be used:
syncasynclimited(maxConcurrentOperationCount)
Mocks
Mocking is great when it comes to writing your tests. But it also could speed up your development while the backend developers are working really hardly on API implementation.
In order to start mocking you have to do the followi
Related Skills
node-connect
342.0kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
84.7kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
342.0kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
84.7kCommit, push, and open a PR
