Skip to content

Commit 4348001

Browse files
committed
add docc root page
1 parent 7f7e2b7 commit 4348001

File tree

2 files changed

+158
-0
lines changed

2 files changed

+158
-0
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,10 @@ Add `.package(url: "https://github.com/SwiftScream/swift-environment-decoder.git
1717

1818
## Usage
1919

20+
[Detailed documentation](https://swiftpackageindex.com/SwiftScream/swift-environment-decoder/master/documentation/environmentdecoder) is available on the swift package index.
21+
22+
This should be enough to get you started...
23+
2024
### Decoding the Environment
2125

2226
```swift
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
# ``EnvironmentDecoder``
2+
3+
4+
## Overview
5+
6+
Suppose you're builing a server app and want to enable specifying the port to bind to in the environment. You might write something like this:
7+
8+
```
9+
let port = Int(ProcessInfo.processInfo.environment["PORT"]) ?? 3000
10+
```
11+
12+
As your use of the environment grows, this can become cumbersome and error prone.
13+
14+
``EnvironmentDecoder`` enables type-safe access to the environment leveraging Swift's `Decodable`.
15+
16+
To use it you specify the shape of the environment that your application expects as a `Decodable` struct. ``EnvironmentDecoder`` can instantiate an instance of this struct from the current environment, throwing an Error if the environment is not valid.
17+
18+
## Example
19+
20+
Let's say you have a server app and want the environment to specify the port to bind to, a list of allowed regions, and a couple of boolean feature flags.
21+
22+
Your environment might look like:
23+
24+
```
25+
PORT=1234
26+
ALLOWED_REGIONS=regionA,regionB,regionC
27+
FEATURE_FLAGS_ENABLE_A=true
28+
FEATURE_FLAGS_ENABLE_B=false
29+
```
30+
31+
``EnvironmentDecoder`` enables access to this like:
32+
33+
```swift
34+
import EnvironmentDecoder
35+
36+
struct MyEnvironment: Decodable {
37+
let port: UInt16
38+
let allowedRegions: [String]
39+
let featureFlags: MyEnvironmentFeatureFlags
40+
}
41+
42+
struct MyEnvironmentFeatureFlags: Decodable {
43+
let enableA: Bool
44+
let enableB: Bool
45+
}
46+
47+
let environment = try EnvironmentDecoder().decode(MyEnvironment.self)
48+
```
49+
50+
## Mapping Properties to Environment Variables
51+
52+
Environment variables are a flat namespace, but Decodable types can be nested to create meaningful hierarchy.
53+
54+
``EnvironmentDecoder`` will, by default, leverage the type hierarcy to create meaningful environment variable names. This can be disabled in the initializer by specifying `prefixKeysWithCodingPath: false`.
55+
56+
#### An Example:
57+
58+
```swift
59+
struct DatabaseConnection: Decodable {
60+
let connectionString: String
61+
}
62+
63+
struct WebServiceConnection: Decodable {
64+
let host: String
65+
let port: Int16
66+
}
67+
68+
struct Environment: Decodable {
69+
let region: String
70+
let database: DatabaseConnection
71+
let webService: WebServiceConnection
72+
}
73+
```
74+
75+
When `prefixKeysWithCodingPath` is enabled the environment is expected to be:
76+
```
77+
REGION
78+
DATABASE_CONNECTION_STRING
79+
WEB_SERVICE_HOST
80+
WEB_SERVICE_PORT
81+
```
82+
83+
When `prefixKeysWithCodingPath` is disabled the environment is expected to be:
84+
```
85+
REGION
86+
CONNECTION_STRING
87+
HOST
88+
PORT
89+
```
90+
91+
The usual methods that the Codable framework provides can be used to customise coding keys.
92+
93+
## Supported Data Types
94+
95+
Almost all `Decodable` data structures are supported.
96+
97+
The one limitation is that elements of an Unkeyed Container must not be represented by an Unkeyed Container or Keyed Container.
98+
For example, elements of an array cannot themselves be an array, or an object represented by key/value pairs.
99+
100+
It is possible to use a custom Decodable type in an array, so long as that type decodes from a single value.
101+
102+
Outside of this limitation, all Decodable types are supported. Some notes on specific data types follows.
103+
104+
#### Data
105+
106+
Several strategies are supported for decoding `Data`.
107+
108+
Refer to ``EnvironmentDecoder/DataDecodingStrategy``
109+
110+
#### Date
111+
112+
Several strategies are supported for decoding `Date`.
113+
114+
Refer to ``EnvironmentDecoder/DateDecodingStrategy``
115+
116+
#### Unkeyed Containers
117+
118+
UnkeyedContainers, for example Arrays, are supported so long as the element value is represented by a single value.
119+
120+
UnkeyedContainer values are decoded from a character-separated list of values.
121+
By default the separator character is a comma. This can be customised by specifying `unkeyedContainerSeparator` in the initializer.
122+
123+
By default whitespace is trimmed from unkeyed container values. This can be disabled by specifying `trimWhitespaceFromUnkeyedContainerValues` in the initializer.
124+
125+
126+
## Error Handling
127+
128+
If the environment cannot be decoded into the specified type, a `Swift.DecodingError` is thrown.
129+
130+
This is a great way to catch issues with the environment early. In some cases it may be appropriate to output a message, and terminate the process.
131+
132+
Some examples of why this may occur:
133+
134+
- a required environment variable is not specified
135+
- the value of an environment variable cannot be decoded as the specified type
136+
137+
```swift
138+
let environment: MyEnvironment
139+
do {
140+
environment = try EnvironmentDecoder().decode(MyEnvironment.self)
141+
} catch {
142+
fatalError(error.localizedDescription)
143+
}
144+
```
145+
146+
## Alternative Environment
147+
148+
By default EnvironmentDecoder will decode `ProcessInfo.processInfo.environment`.
149+
If that's not what you want, you can pass in a `[String: String]` instead.
150+
151+
```swift
152+
let someEnvDictionary: [String: String] = //...
153+
let environment = try EnvironmentDecoder().decode(MyEnvironment.self, from: someEnvDictionary)
154+
```

0 commit comments

Comments
 (0)