SkillAgentSearch skills...

Malibu

:surfer: Malibu is a networking library built on promises

Install / Use

/learn @vadymmarkov/Malibu
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Malibu logo

CI Status Version Carthage Compatible License Platform Swift

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 DRY principle.

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

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 - uses application/x-www-form-urlencoded as a Content-Type and formats your parameters with percent-encoding.
  • json - sets the Content-Type to application/json and sends a JSON representation of the parameters as the body of the request.
  • multipartFormData - sends parameters encoded as multipart/form-data.
  • custom(String) - uses given Content-Type string as a header.

Encoding

Malibu comes with 3 parameter encoding implementations:

  • FormURLEncoder - a percent-escaped encoding following RFC 3986.
  • JsonEncoder - JSONSerialization based 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:

  • sync
  • async
  • limited(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

View on GitHub
GitHub Stars412
CategoryDevelopment
Updated2mo ago
Forks39

Languages

Swift

Security Score

85/100

Audited on Jan 7, 2026

No findings