RResultBuilders

RResultBuilder is DSL library based on Result Builder

Features

  • Supports
  • DSL way defining attributed string
  • Building attributed strings is type safe
  • DSL way of constructing ActionSheet, AlertController
  • Swift 5.4 compatible
  • Supports control statements(including optional checking) within DSL body builder
  • Reduces boilerplate code
  • Fully tested code
  • Distribution with Swift Package Manager and CocoaPods

Requirements

  • iOS 11.0+
  • macOS 10.11+
  • watchOS 4.0+
  • Xcode 12.5+

Installation

CocoaPods

You can use CocoaPods to install RResultBuilders by adding it to your Podfile:

platform :ios, '11.0'
use_frameworks!
pod 'RResultBuilders', '1.0.0'
Ruby

Swift Package Manager

Just add this dependency in Package.swift
Open your project in Xcode 11, navigate to Menu -> Swift Packages -> Add Package Dependency and enter

.package('https://github.com/rakutentech/ios-rresultbuilders', from: 1.0.0)
Swift

NSAttributedString

Following components that helps in building attributed string in easy way

  • RText: This component used to construct attributed text from given string
  • RLink: This component used to construct link in attributed text
  • RImageAttachment: This component used to construct image attachment in attributed text
  • Miscelleneaous
    • REmpty: Just empty component
    • RSpace: This component used to insert space and default is one space
    • RLineBreak: This component used to insert new line and default is one line break
Regular
import RResultBuilders
NSAttributedString {
    RText("I love swift and SwiftUI ???")
        .stroke(width: 2, color: .red)
}
.font(.systemFont(ofSize: 50))
Swift
Control statements

If..else

import RResultBuilders

let optionalText: String? = "iPhone12 Pro Max"
NSAttributedString {    
    if let text = optionalText {
        RText(text)
            .font(.systemFont(ofSize: 20))
            .foregroundColor(.red)
    } else {
        RText("Optional else Text")
    }
}
.font(.boldSystemFont(ofSize: 40))
Swift

switch..case

enum Apple { case iPhone, mac, airpod }
let appleDevice = Apple.iPhone
NSAttributedString {
    switch appleDevice {
    case .iPhone:
        RText("This is iPhone")
            .foregroundColor(.blue)
    default:
        RText("Apple future device")
    }
}
.font(.boldSystemFont(ofSize: 40))
Swift

for..in loop

let appleDevices = [<.....>]
NSAttributedString {
    for device in appleDevices {
        RText(device.rawValue)
            .foregroundColor(device.color)
            .font(device.font)
        RSpace()
    }
}
.font(.boldSystemFont(ofSize: 40))
Swift

UIAlertController

There are dedicated actions those can be used to construct alert or action sheet

  • DefaultAction: This action is default type with UIAlertAction.Style as default
  • CancelAction: This action is cancel type with UIAlertAction.Style as cancel
  • DestructiveAction: This action is destructive type with UIAlertAction.Style as destructive
Regular
import RResultBuilders
UIAlertController(
    title: "Delete all data?",
    message: "All your data will be deleted!") {
    DestructiveAction("Yes, Delete it All") {
        print("Deleting all data")
    }
    DefaultAction("Show More Options") {
        print("showing more options")
    }
    CancelAction("No, Don't Delete Anything")
}
Swift
With control statements

If..else

import RResultBuilders

let optionalText: String? = "Show More optionals"
UIAlertController(
    title: "Delete all data?",
    message: "All your data will be deleted!") {
    
    // Optional unwrapping
    if let text = optionalText {
        DefaultAction(text) {
            print("showing more optionals")
        }
    }
    CancelAction("No, Don't Delete Anything")
}
Swift

switch..case

let appleDevice = Apple.iPhone
UIAlertController(
    title: "Delete all data?",
    message: "All your data will be deleted!") {    
    // Switch case
    switch appleDevice {
    case .iPhone:
        DefaultAction("Show More iPhone") {
            print("showing more iPhone")
        }
    default:
        DefaultAction("Show More appleDevice") {
            print("showing more appleDevice")
        }
    }
    CancelAction("No, Don't Delete Anything")
}
Swift

for..in loop

let actions = [<.....>]
UIAlertController(
    title: "Delete all data?",
    message: "All your data will be deleted!") {
    for action in actions {
        action
    }
}
Swift

Request

Making API call in declarative is fairly simple

DSL

Request<Type> {
    URL("https://jsonplaceholder.typicode.com/todo")
}
.onObject { object in
    ...
}
.resume()
Swift

Data Request

To get raw data as response

Request<Type> {
    URL("https://jsonplaceholder.typicode.com/todo")
}
.onData { data in
    ...
}
.onError { err in
    ...
}
.resume()
Swift

Callback handler

You can attach all possible handlers to Request but they are completely optional execept resume

Request<[Todo]> {
    URL(string: "https://jsonplaceholder.typicode.com/todos")!
}
.onRawResponse { (data, response, error) in
    ...
}
.onData { data in
    ...
}
.onObject { todos in
    ...
}
.onError { err in
    ...
}
.resume()
Swift

Request Components

URL

URL can also be build in DSL way

URL {
    Scheme(.https)
    Host("jsonplaceholder.typicode.com")
    Path("todo")            
}
Swift

Supports standard HTTP headers

Header.Accept(.json)
Header.Authorization(.basic(username: "username", password: "password"))
Header.CacheControl(.noCache)
Header.ContentLength(16)
Header.ContentType(.json)
Header.Host(jsonplaceholder.typicode.com", port: "8000")
Header.UserAgent("user-agent")
Header.Custom("custom", value: "customvVal")
Swift

It also supports building Headers in DSL

Headers {
    Header.Accept(.json)
    Header.Authorization(.basic(username: "test", password: "rest"))
    Header.CacheControl(.noCache)
}
Swift
HTTP Body

Custom Encodable Object

Request<Type> {
    RequestBody(sampleTodo)
}
.resume()
Swift

Raw Data

Request<Type> {
    RequestBody(data)
}
.resume()
Swift
HTTP Method
Method.GET
Method.POST
Method.HEAD
Method.PUT
Method.DELETE
Swift
Timeout
Timeout(30) // seconds
Swift
Decoding

You can even specify custom decoder

Request<[Todo]> {
    URL(string: "https://jsonplaceholder.typicode.com/todos")!
}
.onObject(using: JSONDecoder()) { todos in
    ...
}
.resume()
Swift

Also raw data can be decoded

DataRequest {
    URL(string: "https://jsonplaceholder.typicode.com/todos")!
}
..onData { data in
    data?.decoded()
    // Custom decoder
    // data?.decoded(using: JSONDecoder())
}
.resume()
Swift
URLRequest

It also generates raw URLRequest

DataRequest {
    URL(string: "https://jsonplaceholder.typicode.com/todos")!
    Method.GET
    CachePolicy(.reloadIgnoringLocalCacheData)
    Headers {
        Header.Accept(.json)
        Header.Authorization(.basic(username: "test", password: "rest"))
        Header.CacheControl(.noCache)
    }
}.asURLRequest()
Swift

Screenshot

Example Image1
Example Image2

Example

  • Open and Run the project inside Example folder and find various use cases

Contribute

We welcome you for the contribution to RResultBuilders, check the CONTRIBUTING.
If you find any issues or want to suggest your brilliant ideas please feel free to create pull request.

Meta

Distributed under the MIT license. See LICENSE for more information.

GitHub

https://github.com/rakutentech/ios-rresultbuilders