Contents

Appracatappra/UrlUtilities

Adds functions to construct a URL, HTTP Entity or URL Request in a combine like fashion.

Support

If you find UrlUtilities useful and would like to help support its continued development and maintenance, please consider making a small donation, especially if you are using it in a commercial product:

<a href="https://www.buymeacoffee.com/KevinAtAppra" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/v2/default-yellow.png" alt="Buy Me A Coffee" style="height: 60px !important;width: 217px !important;" ></a>

It's through the support of contributors like yourself, I can continue to build, release and maintain high-quality, well documented Swift Packages like UrlUtilities for free.

Installation

Swift Package Manager (Xcode 11 and above)

  1. In Xcode, select the File > Add Package Dependency… menu item.
  2. Paste https://github.com/Appracatappra/UrlUtilities.git in the dialog box.
  3. Follow the Xcode's instruction to complete the installation.

Why not CocoaPods, or Carthage, or etc?

Supporting multiple dependency managers makes maintaining a library exponentially more complicated and time consuming.

Since, the Swift Package Manager is integrated with Xcode 11 (and greater), it's the easiest choice to support going further.

Overview

`UrlUtilities` includes several functions that are useful when working with **RESTful API Endpoints**. It includes the following utilities:

* `URLBuilder` - Assembles a **URL String** from a base path and any number of path or query parameters.
* `HTTPBodyBuilder` - Assembles the body of a `URLRequest` by setting a **JSON** pattern and filling it with any number of parameters.

Additionally, `UrlUtilities` has the following extensions:

* `String` - Adds the `urlEncoded` property which returns the string value properly encoded to send to a **URL** with any special characters encoded.
* `URLRequest` - Includes the following extensions:
	* `defaultAuthorization` - Holds a default authorization string that will automatically be added to any `URLRequest.build` function calls.
	* `build(url:URL, data:Data?, method:HttpMethodType = .post, timeout:Double = 10)` - Creates a `URLRequest` with the common default values.
	* `build(url:URL, method:HttpMethodType = .post, timeout:Double = 10)` - Creates a `URLRequest` with the common default values.

### URLBuilder

`UrlUtilities` allows you to construct a **URL** string from a base path, then add path or query parameters in method similar to adding properties to a **SwiftUI View**. Take a look at the following example:

```swift
let endpoint = URLBuilder("https://api.restendpoint.com")
	.addPathParameter(3)
	.addPathParameter("login")
	.addParameter(name: "username", value: "jdoe")
	.addParameter(name: "password", value: "pass123")
	
// Get the string value
// "https://api.restendpoint.com/3/login?username=jdoe&password=pass123"
let validation = endpoint.urlString

// Ensure we have a good url
guard let url = endpoint.url else {return nil}
```

In the example above:

* `addPathParameter` - Appends the given value to the base path directly.
* `addParameter` - Adds a query parameter to the URL in the form `name=value`. It will automatically append "?" for the first parameter or "&" for subsequent parameters.

The value can be: `String`, `Bool`, `Int`, `Double`, `Float`, `Color` or `Enum`. For `Enum`, it must conform to either `String, Hashable` or `Int Hashable` (see below for details). 

`Color` will be converted into a hex value in the form `#rrggbbaa` using the standard HTTP and CSS syntax.

> **NOTE:** Any string value will be automatically **URL Encoded** to ensure any special characters don't interfere with the generated **URL**.

### Optional Parameters

`UrlUtilities` makes it east to work with optional parameters. For example:

```swift
let sessionID:Int = 0
let username = "YOUR_USSERNAME"
let password = "YOUR_PASSWORD"

let endpoint = URLBuilder("https://login")
    .addParameter(name: "username", value: "jdoe")
    .addParameter(name: "password", value: "pass123")
    .addParameter(name: "sessionID", value: sessionID, onCondition: (sessionID != 0))
```

In the above example, `onCondition` must evaluate to `true` before the parameter `sessionID` is included. Path parameters work in the same fashion:

```swift
let sessionID:Int = 0
let username = "YOUR_USSERNAME"
let password = "YOUR_PASSWORD"

let endpoint = URLBuilder("https://login")
	.addPathParameter(sessionID, onCondition: (sessionID != 0))
	.addParameter(name: "username", value: "jdoe")
	.addParameter(name: "password", value: "pass123")
```

> **NOTE:** Any `String` parameter that evaluates to the **Empty String** (`""`) will automatically be excluded. The same goes for parameters that evaluate to `nil`. You do not need to specify `onCondition` for these types of values.

### Enum Values

An `Enum` that has a `rawValue` of either `String` or `Int` and also conforms to `Hashable` can be used without explicitly specifying `rawValue` when adding a parameter. See:

```swift
enum UserAccoutType: String, Hashable {
    case guest = "guest"
    case registered = "registered"
}

let sessionID:Int = 0
let username = "YOUR_USERNAME"
let password = "YOUR_PASSWORD"

let endpoint = URLBuilder("https://login")
    .addParameter(name: "username", value: username)
    .addParameter(name: "password", value: password)
    .addParameter(name: "type", value: UserAccountType.guest)
```

### HTTPBodyBuilder

`UrlUtilities` include methods for constructing a `URLRequest` body using similar parameter builders. Take the following example:

```swift
let apiKey: String = "YOUR_API_KEY"
let sessionID:Int = 0
let username = "YOUR_USERNAME"
let password = "YOUR_PASSWORD"

// Assemble post body.
let body = HTTPBodyBuilder("""
    {
      "user": "<user>",
      "password": "<password>",
      "type": "<type>"
    }
    """)
    .addParameter(name: "user", value: username)
    .addParameter(name: "password", value: password)
    .addParameter(name: "type", value: UserAccoutType.guest)
    
// Get the string contents
let endpoint = body.dataString

// Get the body data
let data = body.data
```

### Default Parameters

Both `URLBuilder` and `HTTPBodyBuilder` allow you to define a set of **Default Parameters** that will be included in every `URLBuilder` and `HTTPBodyBuilder` created. This is useful for items like **API Keys** that must be sent to every **RESTful API Endpoint** call.

For example:

```swift
let apiKey: String = "YOUR_API_KEY"
let sessionID:Int = 0

// Set API Key
URLBuilder.addDefaultParameter(name: "api_key", value: apiKey)
    
let endpoint = URLBuilder("https://login")
    .addParameter(name: "username", value: "jdoe")
    .addParameter(name: "password", value: "pass123")
    .addParameter(name: "sessionID", value: sessionID, onCondition: (sessionID != 0))
    .urlString
    
// Get the string value
// "https://login?api_key=YOUR_API_KEY&username=jdoe&password=pass123"
let validation = endpoint.urlString
```

`addDefaultParameter` works exactly like `addParameter` and excepts the same data types.

> **NOTE:** If you define a **Default Parameter**, you can override it later with a `addParameter` call with the same **Parameter Name**. This will only affect the current `URLBuilder` or `HTTPBodyBuilder` instance.
> 
> To affect all subsequent instances, call `addDefaultParameter` call with the same **Parameter Name**.

### Finding and Deleting Parameters

Both `URLBuilder` and `HTTPBodyBuilder` allow you to find and delete both **Parameters** and **Default Parameters**. For example:

```swift
let apiKey: String = "YOUR_API_KEY"
let sessionID:Int = 0

// Set API Key
URLBuilder.addDefaultParameter(name: "api_key", value: apiKey)
    
let endpoint = URLBuilder("https://login")
    .addParameter(name: "username", value: "jdoe")
    .addParameter(name: "password", value: "pass123")
    .addParameter(name: "sessionID", value: sessionID, onCondition: (sessionID != 0))

// Find user name
let name = endpoint.findParameter("username")

// Remove the default API Key
endpoint.deleteParameter("api_key")
```

### Building URLRequests

`UrlUtilities` extends the `URLRequest` class with convenience constructors that handle typical requests. Take the following example:

```swift
let apiKey: String = "YOUR_API_KEY"
let sessionID:Int = 0

// Set api key
URLBuilder.addDefaultParameter(name: "api_key", value: apiKey)

let endpoint = URLBuilder("https://create-user")
    .addParameter(name: "user", value: username)
    .addParameter(name: "password", value: password)
    .addParameter(name: "sessionID", value: sessionID, onCondition: (sessionID != 0))
    
guard let url = endpoint.url else {
    // Failed to create URLRequest"
    return
}
    
// Assemble post body.
let body = HTTPBodyBuilder("""
    {
      "user": <user>,
      "password": "<password>",
      "type": "<type>"
    }
    """)
    .addParameter(name: "username", value: "jdoe")
    .addParameter(name: "password", value: "pass123")
    .addParameter(name: "type", value: UserAccoutType.registered)
    
let request = URLRequest.build(url: url, data: body.data)
```

There is an additional version of `URLRequest.build` that doesn't require a body:

```swift
let apiKey: String = "YOUR_API_KEY"
let sessionID:Int = 0

// Set api key
URLBuilder.addDefaultParameter(name: "api_key", value: apiKey)

let endpoint = URLBuilder("https://delete-user")
    .addParameter(name: "user", value: username)
    .addParameter(name: "sessionID", value: sessionID, onCondition: (sessionID != 0))
    
guard let url = endpoint.url else {
    // Failed to create URLRequest"
    return
}
    
let request = URLRequest.build(url: url, method: .delete)
```

#### Default Authorization

When assembling a `URLRequest` with `build` if you set the `URLRequest.defaultAuthorization` to a string value first, it will automatically be added to the `allHTTPHeaderFields` under the `Authorization` key.

For example, if you are using a **Bearer Token** to authorize your requests, you could do the following:

```swift
let bearerToken: String = "YOUR_BEARER_TOKEN"
let sessionID:Int = 0

// Set the Bearer Token
URLRequest.defaultAuthorization = "Bearer \(bearerToken)"

let endpoint = URLBuilder("https://delete-user")
    .addParameter(name: "user", value: username)
    .addParameter(name: "sessionID", value: sessionID, onCondition: (sessionID != 0))
    
guard let url = endpoint.url else {
    // Failed to create URLRequest"
    return
}
    
let request = URLRequest.build(url: url, method: .delete)
```

# Documentation

The **Package** includes full **DocC Documentation** for all of its features.

Package Metadata

Repository: Appracatappra/UrlUtilities

Stars: 1

Forks: 0

Open issues: 0

Default branch: main

Primary language: swift

License: MIT

README: README.md