Skip to content

Commit 8fe84b7

Browse files
author
simonbility
committed
Add Example Project
1 parent 0227843 commit 8fe84b7

File tree

9 files changed

+435
-0
lines changed

9 files changed

+435
-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: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
public struct ExternalObject: Codable, Hashable, Sendable {
2+
public let foo: String
3+
public let bar: String
4+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
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+
public func encode(to encoder: any Encoder) throws {
17+
var container = encoder.singleValueContainer()
18+
try container.encode(self.rawValue)
19+
}
20+
21+
}
22+
23+
extension Int {
24+
fileprivate var isPrime: Bool {
25+
if self <= 1 { return false }
26+
if self <= 3 { return true }
27+
28+
var i = 2
29+
while i * i <= self {
30+
if self % i == 0 { return false }
31+
i += 1
32+
}
33+
return true
34+
}
35+
}
Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
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+
/// A type that performs HTTP operations defined by the OpenAPI document.
13+
package protocol APIProtocol: Sendable {
14+
/// - Remark: HTTP `GET /user`.
15+
/// - Remark: Generated from `#/paths//user/get(getUser)`.
16+
func getUser(_ input: Operations.GetUser.Input) async throws -> Operations.GetUser.Output
17+
}
18+
19+
/// Convenience overloads for operation inputs.
20+
extension APIProtocol {
21+
/// - Remark: HTTP `GET /user`.
22+
/// - Remark: Generated from `#/paths//user/get(getUser)`.
23+
package func getUser(
24+
query: Operations.GetUser.Input.Query = .init(),
25+
headers: Operations.GetUser.Input.Headers = .init()
26+
) async throws -> Operations.GetUser.Output {
27+
try await getUser(Operations.GetUser.Input(
28+
query: query,
29+
headers: headers
30+
))
31+
}
32+
}
33+
34+
/// Server URLs defined in the OpenAPI document.
35+
package enum Servers {
36+
/// Example service deployment.
37+
package enum Server1 {
38+
/// Example service deployment.
39+
package static func url() throws -> Foundation.URL {
40+
try Foundation.URL(
41+
validatingOpenAPIServerURL: "https://example.com/api",
42+
variables: []
43+
)
44+
}
45+
}
46+
/// Example service deployment.
47+
@available(*, deprecated, renamed: "Servers.Server1.url")
48+
package static func server1() throws -> Foundation.URL {
49+
try Foundation.URL(
50+
validatingOpenAPIServerURL: "https://example.com/api",
51+
variables: []
52+
)
53+
}
54+
}
55+
56+
/// Types generated from the components section of the OpenAPI document.
57+
package enum Components {
58+
/// Types generated from the `#/components/schemas` section of the OpenAPI document.
59+
package enum Schemas {
60+
/// - Remark: Generated from `#/components/schemas/UUID`.
61+
package typealias Uuid = Swift.String
62+
/// A value with the greeting contents.
63+
///
64+
/// - Remark: Generated from `#/components/schemas/User`.
65+
package struct User: Codable, Hashable, Sendable {
66+
/// - Remark: Generated from `#/components/schemas/User/id`.
67+
package var id: Components.Schemas.Uuid?
68+
/// - Remark: Generated from `#/components/schemas/User/favorite_prime_number`.
69+
package var favoritePrimeNumber: Swift.Int?
70+
/// - Remark: Generated from `#/components/schemas/User/foo`.
71+
package struct FooPayload: Codable, Hashable, Sendable {
72+
/// - Remark: Generated from `#/components/schemas/User/foo/foo`.
73+
package var foo: Swift.String?
74+
/// - Remark: Generated from `#/components/schemas/User/foo/bar`.
75+
package var bar: Swift.String?
76+
/// Creates a new `FooPayload`.
77+
///
78+
/// - Parameters:
79+
/// - foo:
80+
/// - bar:
81+
package init(
82+
foo: Swift.String? = nil,
83+
bar: Swift.String? = nil
84+
) {
85+
self.foo = foo
86+
self.bar = bar
87+
}
88+
package enum CodingKeys: String, CodingKey {
89+
case foo
90+
case bar
91+
}
92+
}
93+
/// - Remark: Generated from `#/components/schemas/User/foo`.
94+
package var foo: Components.Schemas.User.FooPayload?
95+
/// Creates a new `User`.
96+
///
97+
/// - Parameters:
98+
/// - id:
99+
/// - favoritePrimeNumber:
100+
/// - foo:
101+
package init(
102+
id: Components.Schemas.Uuid? = nil,
103+
favoritePrimeNumber: Swift.Int? = nil,
104+
foo: Components.Schemas.User.FooPayload? = nil
105+
) {
106+
self.id = id
107+
self.favoritePrimeNumber = favoritePrimeNumber
108+
self.foo = foo
109+
}
110+
package enum CodingKeys: String, CodingKey {
111+
case id
112+
case favoritePrimeNumber = "favorite_prime_number"
113+
case foo
114+
}
115+
}
116+
}
117+
/// Types generated from the `#/components/parameters` section of the OpenAPI document.
118+
package enum Parameters {}
119+
/// Types generated from the `#/components/requestBodies` section of the OpenAPI document.
120+
package enum RequestBodies {}
121+
/// Types generated from the `#/components/responses` section of the OpenAPI document.
122+
package enum Responses {}
123+
/// Types generated from the `#/components/headers` section of the OpenAPI document.
124+
package enum Headers {}
125+
}
126+
127+
/// API operations, with input and output types, generated from `#/paths` in the OpenAPI document.
128+
package enum Operations {
129+
/// - Remark: HTTP `GET /user`.
130+
/// - Remark: Generated from `#/paths//user/get(getUser)`.
131+
package enum GetUser {
132+
package static let id: Swift.String = "getUser"
133+
package struct Input: Sendable, Hashable {
134+
/// - Remark: Generated from `#/paths/user/GET/query`.
135+
package struct Query: Sendable, Hashable {
136+
/// The name of the user
137+
///
138+
/// - Remark: Generated from `#/paths/user/GET/query/name`.
139+
package var name: Swift.String?
140+
/// Creates a new `Query`.
141+
///
142+
/// - Parameters:
143+
/// - name: The name of the user
144+
package init(name: Swift.String? = nil) {
145+
self.name = name
146+
}
147+
}
148+
package var query: Operations.GetUser.Input.Query
149+
/// - Remark: Generated from `#/paths/user/GET/header`.
150+
package struct Headers: Sendable, Hashable {
151+
package var accept: [OpenAPIRuntime.AcceptHeaderContentType<Operations.GetUser.AcceptableContentType>]
152+
/// Creates a new `Headers`.
153+
///
154+
/// - Parameters:
155+
/// - accept:
156+
package init(accept: [OpenAPIRuntime.AcceptHeaderContentType<Operations.GetUser.AcceptableContentType>] = .defaultValues()) {
157+
self.accept = accept
158+
}
159+
}
160+
package var headers: Operations.GetUser.Input.Headers
161+
/// Creates a new `Input`.
162+
///
163+
/// - Parameters:
164+
/// - query:
165+
/// - headers:
166+
package init(
167+
query: Operations.GetUser.Input.Query = .init(),
168+
headers: Operations.GetUser.Input.Headers = .init()
169+
) {
170+
self.query = query
171+
self.headers = headers
172+
}
173+
}
174+
@frozen package enum Output: Sendable, Hashable {
175+
package struct Ok: Sendable, Hashable {
176+
/// - Remark: Generated from `#/paths/user/GET/responses/200/content`.
177+
@frozen package enum Body: Sendable, Hashable {
178+
/// - Remark: Generated from `#/paths/user/GET/responses/200/content/application\/json`.
179+
case json(Components.Schemas.User)
180+
/// The associated value of the enum case if `self` is `.json`.
181+
///
182+
/// - Throws: An error if `self` is not `.json`.
183+
/// - SeeAlso: `.json`.
184+
package var json: Components.Schemas.User {
185+
get throws {
186+
switch self {
187+
case let .json(body):
188+
return body
189+
}
190+
}
191+
}
192+
}
193+
/// Received HTTP response body
194+
package var body: Operations.GetUser.Output.Ok.Body
195+
/// Creates a new `Ok`.
196+
///
197+
/// - Parameters:
198+
/// - body: Received HTTP response body
199+
package init(body: Operations.GetUser.Output.Ok.Body) {
200+
self.body = body
201+
}
202+
}
203+
/// A success response with the user.
204+
///
205+
/// - Remark: Generated from `#/paths//user/get(getUser)/responses/200`.
206+
///
207+
/// HTTP response code: `200 ok`.
208+
case ok(Operations.GetUser.Output.Ok)
209+
/// The associated value of the enum case if `self` is `.ok`.
210+
///
211+
/// - Throws: An error if `self` is not `.ok`.
212+
/// - SeeAlso: `.ok`.
213+
package var ok: Operations.GetUser.Output.Ok {
214+
get throws {
215+
switch self {
216+
case let .ok(response):
217+
return response
218+
default:
219+
try throwUnexpectedResponseStatus(
220+
expectedStatus: "ok",
221+
response: self
222+
)
223+
}
224+
}
225+
}
226+
/// Undocumented response.
227+
///
228+
/// A response with a code that is not documented in the OpenAPI document.
229+
case undocumented(statusCode: Swift.Int, OpenAPIRuntime.UndocumentedPayload)
230+
}
231+
@frozen package enum AcceptableContentType: AcceptableProtocol {
232+
case json
233+
case other(Swift.String)
234+
package init?(rawValue: Swift.String) {
235+
switch rawValue.lowercased() {
236+
case "application/json":
237+
self = .json
238+
default:
239+
self = .other(rawValue)
240+
}
241+
}
242+
package var rawValue: Swift.String {
243+
switch self {
244+
case let .other(string):
245+
return string
246+
case .json:
247+
return "application/json"
248+
}
249+
}
250+
package static var allCases: [Self] {
251+
[
252+
.json
253+
]
254+
}
255+
}
256+
}
257+
}
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)