Skip to content

Commit b67b6cd

Browse files
author
simonbility
committed
Add Example Project
1 parent cff5760 commit b67b6cd

File tree

8 files changed

+398
-0
lines changed

8 files changed

+398
-0
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
.DS_Store
2+
.build
3+
/Packages
4+
/*.xcodeproj
5+
xcuserdata/
6+
DerivedData/
7+
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
8+
.vscode
9+
/Package.resolved
10+
.ci/
11+
.docc-build/
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// swift-tools-version:5.9
2+
//===----------------------------------------------------------------------===//
3+
//
4+
// This source file is part of the SwiftOpenAPIGenerator open source project
5+
//
6+
// Copyright (c) 2024 Apple Inc. and the SwiftOpenAPIGenerator project authors
7+
// Licensed under Apache License v2.0
8+
//
9+
// See LICENSE.txt for license information
10+
// See CONTRIBUTORS.txt for the list of SwiftOpenAPIGenerator project authors
11+
//
12+
// SPDX-License-Identifier: Apache-2.0
13+
//
14+
//===----------------------------------------------------------------------===//
15+
import PackageDescription
16+
17+
let package = Package(
18+
name: "replace-types-example",
19+
platforms: [.macOS(.v14)],
20+
products: [
21+
.library(name: "Types", targets: ["Types"]),
22+
],
23+
dependencies: [
24+
.package(url: "https://github.com/apple/swift-openapi-runtime", from: "1.7.0"),
25+
],
26+
targets: [
27+
.target(
28+
name: "Types",
29+
dependencies: [
30+
"ExternalLibrary",
31+
.product(name: "OpenAPIRuntime", package: "swift-openapi-runtime")]
32+
),
33+
.target(
34+
name: "ExternalLibrary"
35+
),
36+
]
37+
)
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Replacing types
2+
3+
An example project using [Swift OpenAPI Generator](https://github.com/apple/swift-openapi-generator).
4+
5+
> **Disclaimer:** This example is deliberately simplified and is intended for illustrative purposes only.
6+
7+
## Overview
8+
9+
This example shows how you can structure a Swift package to share the types
10+
from an OpenAPI document between a client and server module by having a common
11+
target that runs the generator in `types` mode only.
12+
13+
This allows you to write extensions or other helper functions that use these
14+
types and use them in both the client and server code.
15+
16+
## Usage
17+
18+
Build and run the server using:
19+
20+
```console
21+
% swift run hello-world-server
22+
Build complete!
23+
...
24+
info HummingBird : [HummingbirdCore] Server started and listening on 127.0.0.1:8080
25+
```
26+
27+
Then, in another terminal window, run the client:
28+
29+
```console
30+
% swift run hello-world-client
31+
Build complete!
32+
+––––––––––––––––––+
33+
|+––––––––––––––––+|
34+
||Hello, Stranger!||
35+
|+––––––––––––––––+|
36+
+––––––––––––––––––+
37+
```
38+
39+
Note how the message is boxed twice: once by the server and once by the client,
40+
both using an extension on a shared type, defined in the `Types` module.
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
public struct PrimeNumber: Codable, Hashable, RawRepresentable, Sendable {
2+
public let rawValue: Int
3+
public init?(rawValue: Int) {
4+
if !rawValue.isPrime { return nil }
5+
self.rawValue = rawValue
6+
}
7+
8+
public init(from decoder: any Decoder) throws {
9+
let container = try decoder.singleValueContainer()
10+
let number = try container.decode(Int.self)
11+
guard let value = PrimeNumber(rawValue: number) else {
12+
throw DecodingError.dataCorruptedError(in: container, debugDescription: "The number is not prime.")
13+
}
14+
self = value
15+
}
16+
17+
public func encode(to encoder: any Encoder) throws {
18+
var container = encoder.singleValueContainer()
19+
try container.encode(self.rawValue)
20+
}
21+
22+
}
23+
24+
extension Int {
25+
fileprivate var isPrime: Bool {
26+
if self <= 1 { return false }
27+
if self <= 3 { return true }
28+
29+
var i = 2
30+
while i * i <= self {
31+
if self % i == 0 { return false }
32+
i += 1
33+
}
34+
return true
35+
}
36+
}
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
// Generated by swift-openapi-generator, do not modify.
2+
@_spi(Generated) import OpenAPIRuntime
3+
#if os(Linux)
4+
@preconcurrency import struct Foundation.URL
5+
@preconcurrency import struct Foundation.Data
6+
@preconcurrency import struct Foundation.Date
7+
#else
8+
import struct Foundation.URL
9+
import struct Foundation.Data
10+
import struct Foundation.Date
11+
#endif
12+
import Foundation
13+
import ExternalLibrary
14+
/// A type that performs HTTP operations defined by the OpenAPI document.
15+
package protocol APIProtocol: Sendable {
16+
/// - Remark: HTTP `GET /user`.
17+
/// - Remark: Generated from `#/paths//user/get(getUser)`.
18+
func getUser(_ input: Operations.GetUser.Input) async throws -> Operations.GetUser.Output
19+
}
20+
21+
/// Convenience overloads for operation inputs.
22+
extension APIProtocol {
23+
/// - Remark: HTTP `GET /user`.
24+
/// - Remark: Generated from `#/paths//user/get(getUser)`.
25+
package func getUser(
26+
query: Operations.GetUser.Input.Query = .init(),
27+
headers: Operations.GetUser.Input.Headers = .init()
28+
) async throws -> Operations.GetUser.Output {
29+
try await getUser(Operations.GetUser.Input(
30+
query: query,
31+
headers: headers
32+
))
33+
}
34+
}
35+
36+
/// Server URLs defined in the OpenAPI document.
37+
package enum Servers {
38+
/// Example service deployment.
39+
package enum Server1 {
40+
/// Example service deployment.
41+
package static func url() throws -> Foundation.URL {
42+
try Foundation.URL(
43+
validatingOpenAPIServerURL: "https://example.com/api",
44+
variables: []
45+
)
46+
}
47+
}
48+
/// Example service deployment.
49+
@available(*, deprecated, renamed: "Servers.Server1.url")
50+
package static func server1() throws -> Foundation.URL {
51+
try Foundation.URL(
52+
validatingOpenAPIServerURL: "https://example.com/api",
53+
variables: []
54+
)
55+
}
56+
}
57+
58+
/// Types generated from the components section of the OpenAPI document.
59+
package enum Components {
60+
/// Types generated from the `#/components/schemas` section of the OpenAPI document.
61+
package enum Schemas {
62+
/// - Remark: Generated from `#/components/schemas/UUID`.
63+
package typealias Uuid = Swift.String
64+
/// A value with the greeting contents.
65+
///
66+
/// - Remark: Generated from `#/components/schemas/User`.
67+
package struct User: Codable, Hashable, Sendable {
68+
/// - Remark: Generated from `#/components/schemas/User/id`.
69+
package var id: Components.Schemas.Uuid?
70+
/// - Remark: Generated from `#/components/schemas/User/favorite_prime_number`.
71+
package var favoritePrimeNumber: Swift.Int?
72+
/// Creates a new `User`.
73+
///
74+
/// - Parameters:
75+
/// - id:
76+
/// - favoritePrimeNumber:
77+
package init(
78+
id: Components.Schemas.Uuid? = nil,
79+
favoritePrimeNumber: Swift.Int? = nil
80+
) {
81+
self.id = id
82+
self.favoritePrimeNumber = favoritePrimeNumber
83+
}
84+
package enum CodingKeys: String, CodingKey {
85+
case id
86+
case favoritePrimeNumber = "favorite_prime_number"
87+
}
88+
}
89+
}
90+
/// Types generated from the `#/components/parameters` section of the OpenAPI document.
91+
package enum Parameters {}
92+
/// Types generated from the `#/components/requestBodies` section of the OpenAPI document.
93+
package enum RequestBodies {}
94+
/// Types generated from the `#/components/responses` section of the OpenAPI document.
95+
package enum Responses {}
96+
/// Types generated from the `#/components/headers` section of the OpenAPI document.
97+
package enum Headers {}
98+
}
99+
100+
/// API operations, with input and output types, generated from `#/paths` in the OpenAPI document.
101+
package enum Operations {
102+
/// - Remark: HTTP `GET /user`.
103+
/// - Remark: Generated from `#/paths//user/get(getUser)`.
104+
package enum GetUser {
105+
package static let id: Swift.String = "getUser"
106+
package struct Input: Sendable, Hashable {
107+
/// - Remark: Generated from `#/paths/user/GET/query`.
108+
package struct Query: Sendable, Hashable {
109+
/// The name of the user
110+
///
111+
/// - Remark: Generated from `#/paths/user/GET/query/name`.
112+
package var name: Swift.String?
113+
/// Creates a new `Query`.
114+
///
115+
/// - Parameters:
116+
/// - name: The name of the user
117+
package init(name: Swift.String? = nil) {
118+
self.name = name
119+
}
120+
}
121+
package var query: Operations.GetUser.Input.Query
122+
/// - Remark: Generated from `#/paths/user/GET/header`.
123+
package struct Headers: Sendable, Hashable {
124+
package var accept: [OpenAPIRuntime.AcceptHeaderContentType<Operations.GetUser.AcceptableContentType>]
125+
/// Creates a new `Headers`.
126+
///
127+
/// - Parameters:
128+
/// - accept:
129+
package init(accept: [OpenAPIRuntime.AcceptHeaderContentType<Operations.GetUser.AcceptableContentType>] = .defaultValues()) {
130+
self.accept = accept
131+
}
132+
}
133+
package var headers: Operations.GetUser.Input.Headers
134+
/// Creates a new `Input`.
135+
///
136+
/// - Parameters:
137+
/// - query:
138+
/// - headers:
139+
package init(
140+
query: Operations.GetUser.Input.Query = .init(),
141+
headers: Operations.GetUser.Input.Headers = .init()
142+
) {
143+
self.query = query
144+
self.headers = headers
145+
}
146+
}
147+
@frozen package enum Output: Sendable, Hashable {
148+
package struct Ok: Sendable, Hashable {
149+
/// - Remark: Generated from `#/paths/user/GET/responses/200/content`.
150+
@frozen package enum Body: Sendable, Hashable {
151+
/// - Remark: Generated from `#/paths/user/GET/responses/200/content/application\/json`.
152+
case json(Components.Schemas.User)
153+
/// The associated value of the enum case if `self` is `.json`.
154+
///
155+
/// - Throws: An error if `self` is not `.json`.
156+
/// - SeeAlso: `.json`.
157+
package var json: Components.Schemas.User {
158+
get throws {
159+
switch self {
160+
case let .json(body):
161+
return body
162+
}
163+
}
164+
}
165+
}
166+
/// Received HTTP response body
167+
package var body: Operations.GetUser.Output.Ok.Body
168+
/// Creates a new `Ok`.
169+
///
170+
/// - Parameters:
171+
/// - body: Received HTTP response body
172+
package init(body: Operations.GetUser.Output.Ok.Body) {
173+
self.body = body
174+
}
175+
}
176+
/// A success response with the user.
177+
///
178+
/// - Remark: Generated from `#/paths//user/get(getUser)/responses/200`.
179+
///
180+
/// HTTP response code: `200 ok`.
181+
case ok(Operations.GetUser.Output.Ok)
182+
/// The associated value of the enum case if `self` is `.ok`.
183+
///
184+
/// - Throws: An error if `self` is not `.ok`.
185+
/// - SeeAlso: `.ok`.
186+
package var ok: Operations.GetUser.Output.Ok {
187+
get throws {
188+
switch self {
189+
case let .ok(response):
190+
return response
191+
default:
192+
try throwUnexpectedResponseStatus(
193+
expectedStatus: "ok",
194+
response: self
195+
)
196+
}
197+
}
198+
}
199+
/// Undocumented response.
200+
///
201+
/// A response with a code that is not documented in the OpenAPI document.
202+
case undocumented(statusCode: Swift.Int, OpenAPIRuntime.UndocumentedPayload)
203+
}
204+
@frozen package enum AcceptableContentType: AcceptableProtocol {
205+
case json
206+
case other(Swift.String)
207+
package init?(rawValue: Swift.String) {
208+
switch rawValue.lowercased() {
209+
case "application/json":
210+
self = .json
211+
default:
212+
self = .other(rawValue)
213+
}
214+
}
215+
package var rawValue: Swift.String {
216+
switch self {
217+
case let .other(string):
218+
return string
219+
case .json:
220+
return "application/json"
221+
}
222+
}
223+
package static var allCases: [Self] {
224+
[
225+
.json
226+
]
227+
}
228+
}
229+
}
230+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
generate:
2+
- types
3+
accessModifier: package
4+
namingStrategy: idiomatic
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../openapi.yaml

0 commit comments

Comments
 (0)