Introducing xcrs-controlkit
Seto Elkahfi committed
Jul 30, 2026 at 15:45 UTC
17091d8d975dc59e8474a2998ebe3c7cacf6a7ed
51 files changed
+2553
.gitignore
new
+7
@@ -0,0 +1,7 @@
1
+.build/
2
+.swiftpm/
3
+Runners/**/build/
4
+DerivedData/
5
+xcuserdata/
6
+*.xcuserstate
7
+.DS_Store
LICENSE
new
+190
@@ -0,0 +1,190 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship made available under
36
+ the License, as indicated by a copyright notice that is included in
37
+ or attached to the work (an example is provided in the Appendix below).
38
+
39
+ "Derivative Works" shall mean any work, whether in Source or Object
40
+ form, that is based on (or derived from) the Work and for which the
41
+ editorial revisions, annotations, elaborations, or other modifications
42
+ represent, as a whole, an original work of authorship. For the purposes
43
+ of this License, Derivative Works shall not include works that remain
44
+ separable from, or merely link (or bind by name) to the interfaces of,
45
+ the Work and Derivative Works thereof.
46
+
47
+ "Contribution" shall mean, as defined by the copyright owner or by the
48
+ Legal Entity authorized to submit on behalf of the copyright owner, any
49
+ work of authorship, including the original version of the Work and any
50
+ modifications or additions to that Work or Derivative Works of the Work.
51
+
52
+ "Contributor" shall mean Licensor and any Legal Entity on behalf of
53
+ whom a Contribution has been received by the Licensor and subsequently
54
+ incorporated within the Work.
55
+
56
+ 2. Grant of Copyright License. Subject to the terms and conditions of
57
+ this License, each Contributor hereby grants to You a perpetual,
58
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
59
+ copyright license to reproduce, prepare Derivative Works of,
60
+ publicly display, publicly perform, sublicense, and distribute the
61
+ Work and such Derivative Works in Source or Object form.
62
+
63
+ 3. Grant of Patent License. Subject to the terms and conditions of
64
+ this License, each Contributor hereby grants to You a perpetual,
65
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
66
+ (except as stated in this section) patent license to make, have made,
67
+ use, offer to sell, sell, import, and otherwise transfer the Work,
68
+ where such license applies only to those patent claims licensable
69
+ by such Contributor that are necessarily infringed by their
70
+ Contribution(s) alone or by combination of their Contribution(s)
71
+ with the Work to which such Contribution(s) was submitted. If You
72
+ institute patent litigation against any entity (including a
73
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
74
+ or a Contribution incorporated within the Work constitutes direct
75
+ or contributory patent infringement, then any and all patent licenses
76
+ granted to You under this License for that Work shall terminate
77
+ as of the date such litigation is filed.
78
+
79
+ 4. Redistribution. You may reproduce and distribute copies of the
80
+ Work or Derivative Works thereof in any medium, with or without
81
+ modifications, and in Source or Object form, provided that You
82
+ meet the following conditions:
83
+
84
+ (a) You must give any other recipients of the Work or
85
+ Derivative Works a copy of this License; and
86
+
87
+ (b) You must cause any modified files to carry prominent notices
88
+ stating that You changed the files; and
89
+
90
+ (c) You must retain, in the Source form of any Derivative Works
91
+ that You distribute, all copyright, patent, trademark, and
92
+ attribution notices from the Source form of the Work,
93
+ excluding those notices that do not pertain to any part of
94
+ the Derivative Works; and
95
+
96
+ (d) If the Work includes a "NOTICE" text file as part of its
97
+ distribution, You must include a readable copy of the
98
+ attribution notices contained within such NOTICE file, in
99
+ at least one of the following places: within a NOTICE text
100
+ file distributed as part of the Derivative Works; within
101
+ the Source form or documentation, if provided along with the
102
+ Derivative Works; or, within a display generated by the
103
+ Derivative Works, if and wherever such third-party notices
104
+ normally appear. The contents of the NOTICE file are for
105
+ informational purposes only and do not modify the License.
106
+ You may add Your own attribution notices within Derivative
107
+ Works that You distribute, alongside or as an addendum to
108
+ the NOTICE text from the Work, provided that such additional
109
+ attribution notices cannot be construed as modifying the License.
110
+
111
+ You may add Your own license statement for Your modifications and
112
+ may provide additional grant of rights to use, copy, modify, merge,
113
+ publish, distribute, sublicense, and/or sell copies of the Work,
114
+ and to permit persons to whom the Work is furnished to do so,
115
+ subject to the following conditions:
116
+
117
+ The above copyright notice and this permission notice shall be
118
+ included in all copies or substantial portions of the Work.
119
+
120
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
121
+ any Contribution intentionally submitted for inclusion in the Work
122
+ by You to the Licensor shall be under the terms and conditions of
123
+ this License, without any additional terms or conditions.
124
+ Notwithstanding the above, nothing herein shall supersede or modify
125
+ the terms of any separate license agreement you may have executed
126
+ with Licensor regarding such Contributions.
127
+
128
+ 6. Trademarks. This License does not grant permission to use the trade
129
+ names, trademarks, service marks, or product names of the Licensor,
130
+ except as required for reasonable and customary use in describing the
131
+ origin of the Work and reproducing the content of the NOTICE file.
132
+
133
+ 7. Disclaimer of Warranty. Unless required by applicable law or
134
+ agreed to in writing, Licensor provides the Work (and each
135
+ Contributor provides its Contributions) on an "AS IS" BASIS,
136
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
137
+ implied, including, without limitation, any warranties or conditions
138
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
139
+ PARTICULAR PURPOSE. You are solely responsible for determining the
140
+ appropriateness of using or redistributing the Work and assume any
141
+ risks associated with Your exercise of permissions under this License.
142
+
143
+ 8. Limitation of Liability. In no event and under no legal theory,
144
+ whether in tort (including negligence), contract, or otherwise,
145
+ unless required by applicable law (such as deliberate and grossly
146
+ negligent acts) or agreed to in writing, shall any Contributor be
147
+ liable to You for damages, including any direct, indirect, special,
148
+ incidental, or exemplary damages of any character arising as a
149
+ result of this License or out of the use or inability to use the
150
+ Work (including but not limited to damages for loss of goodwill,
151
+ work stoppage, computer failure or malfunction, or all other
152
+ commercial damages or losses), even if such Contributor has been
153
+ advised of the possibility of such damages.
154
+
155
+ 9. Accepting Warranty or Additional Liability. While redistributing
156
+ the Work or Derivative Works thereof, You may choose to offer,
157
+ and charge a fee for, acceptance of support, warranty, indemnity,
158
+ or other liability obligations and/or rights consistent with this
159
+ License. However, in accepting such obligations, You may act only
160
+ on Your own behalf and on Your sole responsibility, not on behalf
161
+ of any other Contributor, and only if You agree to indemnify,
162
+ defend, and hold each Contributor harmless for any liability
163
+ incurred by, or claims asserted against, such Contributor by reason
164
+ of your accepting any such warranty or additional liability.
165
+
166
+ END OF TERMS AND CONDITIONS
167
+
168
+ APPENDIX: How to apply the Apache License to your work.
169
+
170
+ To apply the Apache License to your work, attach the following
171
+ boilerplate notice, with the fields enclosed by brackets "[]"
172
+ replaced with your own identifying information. (Don't include
173
+ the brackets!) The text should be enclosed in the appropriate
174
+ comment syntax for the file format in use. Also, it is recommended
175
+ that a file or directory name be given on the same line as the
176
+ copyright notice for easier identification within third-party archives.
177
+
178
+ Copyright 2026 Splitfire AB (smbCloud)
179
+
180
+ Licensed under the Apache License, Version 2.0 (the "License");
181
+ you may not use this file except in compliance with the License.
182
+ You may obtain a copy of the License at
183
+
184
+ http://www.apache.org/licenses/LICENSE-2.0
185
+
186
+ Unless required by applicable law or agreed to in writing, software
187
+ distributed under the License is distributed on an "AS IS" BASIS,
188
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
189
+ See the License for the specific language governing permissions and
190
+ limitations under the License.
Package.swift
new
+27
@@ -0,0 +1,27 @@
1
+// swift-tools-version: 5.9
2
+
3
+import PackageDescription
4
+
5
+let package = Package(
6
+ name: "XCRSControlKit",
7
+ platforms: [
8
+ .iOS(.v16),
9
+ .macOS(.v13),
10
+ .tvOS(.v16),
11
+ .watchOS(.v9),
12
+ .visionOS(.v1)
13
+ ],
14
+ products: [
15
+ .library(
16
+ name: "XCRSControlKit",
17
+ targets: ["XCRSControlKit"]
18
+ )
19
+ ],
20
+ targets: [
21
+ .target(name: "XCRSControlKit"),
22
+ .testTarget(
23
+ name: "XCRSControlKitTests",
24
+ dependencies: ["XCRSControlKit"]
25
+ )
26
+ ]
27
+)
README.md
new
+64
@@ -0,0 +1,64 @@
1
+# XCRSControlKit
2
+
3
+XCRSControlKit is the shared Swift package contract for black-box automation
4
+of Apple platforms. It provides stable target, capability, and request models
5
+that can be used by native test runners and external automation clients.
6
+
7
+The package is intentionally transport-neutral. Platform runners remain
8
+separate Xcode projects because XCTest and UI testing require platform-specific
9
+schemes, host applications, simulator destinations, signing, and entitlements.
10
+
11
+## Supported platforms
12
+
13
+- iOS and iPadOS
14
+- tvOS
15
+- watchOS
16
+- visionOS
17
+- macOS
18
+
19
+## Installation
20
+
21
+Add the package URL to an Xcode project or Swift package:
22
+
23
+```swift
24
+dependencies: [
25
+ .package(url: "https://github.com/smbcloudXYZ/xcrs-controlkit.git", from: "0.1.0")
26
+]
27
+```
28
+
29
+Then import the library:
30
+
31
+```swift
32
+import XCRSControlKit
33
+
34
+let capabilities = XCRSControlKitCapabilities.standard(for: .tvOS)
35
+if capabilities.supports(.remoteButtons) {
36
+ // Connect a tvOS runner and send remote-button requests.
37
+}
38
+```
39
+
40
+## Runner projects
41
+
42
+Runner implementations belong outside the Swift package target:
43
+
44
+```text
45
+Runners/
46
+├── iOS/
47
+├── tvOS/
48
+├── watchOS/
49
+├── visionOS/
50
+└── macOS/
51
+```
52
+
53
+Each runner can import `XCRSControlKit` while using the appropriate Apple
54
+frameworks and Xcode test target for its platform.
55
+
56
+## Status
57
+
58
+The package currently provides the public cross-platform contract. Native
59
+runner projects and transport implementations are being added incrementally,
60
+starting with iOS and tvOS.
61
+
62
+## License
63
+
64
+XCRSControlKit is available under the Apache License, Version 2.0.
Runners/Branding/XCRSControlKitLogo.svg
new
+29
@@ -0,0 +1,29 @@
1
+<svg
2
+ width="512"
3
+ height="512"
4
+ viewBox="0 0 80 80"
5
+ fill="none"
6
+ xmlns="http://www.w3.org/2000/svg"
7
+>
8
+ <rect x="0" y="0" width="80" height="80" rx="12" fill="#1B4332" />
9
+ <text
10
+ x="40"
11
+ y="38.8"
12
+ text-anchor="middle"
13
+ font-family="'Helvetica Neue', Helvetica, Arial, sans-serif"
14
+ font-weight="800"
15
+ font-size="34.4"
16
+ letter-spacing="-2"
17
+ fill="#F5F3EC"
18
+ >XC</text>
19
+ <text
20
+ x="40"
21
+ y="72"
22
+ text-anchor="middle"
23
+ font-family="'Helvetica Neue', Helvetica, Arial, sans-serif"
24
+ font-weight="800"
25
+ font-size="34.4"
26
+ letter-spacing="-2"
27
+ fill="#F5F3EC"
28
+ >RS</text>
29
+</svg>
Runners/README.md
new
+8
@@ -0,0 +1,8 @@
1
+# Platform runners
2
+
3
+Platform runners are separate Xcode projects rather than SwiftPM targets.
4
+They import the local `XCRSControlKit` package and expose XCTest or
5
+XCUITest-specific automation over the runner transport.
6
+
7
+Use one project per Apple platform so each runner can define its own schemes,
8
+host app, deployment target, signing configuration, and platform capabilities.
Runners/iOS/Assets.xcassets/AppIcon.appiconset/AppIcon-1024.png
Binary files /dev/null and b/Runners/iOS/Assets.xcassets/AppIcon.appiconset/AppIcon-1024.png differ
Runners/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json
new
+14
@@ -0,0 +1,14 @@
1
+{
2
+ "images" : [
3
+ {
4
+ "filename" : "AppIcon-1024.png",
5
+ "idiom" : "universal",
6
+ "platform" : "ios",
7
+ "size" : "1024x1024"
8
+ }
9
+ ],
10
+ "info" : {
11
+ "author" : "xcode",
12
+ "version" : 1
13
+ }
14
+}
Runners/iOS/ControlKitRPCServer.swift
new
+268
@@ -0,0 +1,268 @@
1
+import Foundation
2
+import NIOCore
3
+import NIOHTTP1
4
+import NIOPosix
5
+import XCTest
6
+import XCRSControlKit
7
+
8
+private struct JSONRPCRequest {
9
+ let method: String
10
+ let params: [String: Any]
11
+ let id: Any?
12
+}
13
+
14
+@MainActor
15
+final class ControlKitRPCServer {
16
+ private let port: UInt16
17
+ private let host: String
18
+
19
+ init() {
20
+ port = UInt16(ProcessInfo.processInfo.environment["DEVICEKIT_LISTEN_PORT"] ?? "12004") ?? 12004
21
+ host = ProcessInfo.processInfo.environment["DEVICEKIT_LISTEN_HOST"] ?? "127.0.0.1"
22
+ }
23
+
24
+ func start() async throws {
25
+ let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
26
+ defer {
27
+ try? group.syncShutdownGracefully()
28
+ }
29
+ let requestHandler: (HTTPMethod, String, Data) async -> Data = { [self] method, path, body in
30
+ if method == .GET && path == "/health" {
31
+ return Data("OK".utf8)
32
+ }
33
+ return await handle(body)
34
+ }
35
+ let channel = try await ServerBootstrap(group: group)
36
+ .serverChannelOption(ChannelOptions.backlog, value: 256)
37
+ .serverChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
38
+ .childChannelInitializer { channel in
39
+ channel.pipeline.configureHTTPServerPipeline().flatMap {
40
+ channel.pipeline.addHandler(RPCHandler(requestHandler: requestHandler))
41
+ }
42
+ }
43
+ .childChannelOption(ChannelOptions.socketOption(.so_reuseaddr), value: 1)
44
+ .bind(host: host, port: Int(port))
45
+ .get()
46
+ try await channel.closeFuture.get()
47
+ }
48
+
49
+ private func handle(_ body: Data) async -> Data {
50
+ do {
51
+ if body == Data("health".utf8) {
52
+ return try JSONSerialization.data(withJSONObject: ["status": "ok"])
53
+ }
54
+
55
+ guard
56
+ let object = try JSONSerialization.jsonObject(with: body) as? [String: Any],
57
+ let method = object["method"] as? String
58
+ else {
59
+ return try response(error: -32600, message: "Invalid Request", id: nil)
60
+ }
61
+
62
+ let request = JSONRPCRequest(
63
+ method: method,
64
+ params: object["params"] as? [String: Any] ?? [:],
65
+ id: object["id"]
66
+ )
67
+ return try await dispatch(request)
68
+ } catch {
69
+ return (try? response(error: -32603, message: error.localizedDescription, id: nil)) ?? Data()
70
+ }
71
+ }
72
+
73
+ private func dispatch(_ request: JSONRPCRequest) async throws -> Data {
74
+ switch request.method {
75
+ case "device.capabilities":
76
+ let platform: XCRSControlKitPlatform = {
77
+ #if os(tvOS)
78
+ return .tvOS
79
+ #else
80
+ return .iOS
81
+ #endif
82
+ }()
83
+ return try response(
84
+ result: [
85
+ "platform": platform.rawValue,
86
+ "capabilities": XCRSControlKitCapabilities.standard(for: platform).values.map(\.rawValue).sorted()
87
+ ],
88
+ id: request.id
89
+ )
90
+ case "device.info":
91
+ return try response(
92
+ result: ["port": port, "runner": "XCRSControlKit"],
93
+ id: request.id
94
+ )
95
+ case "device.apps.launch":
96
+ let bundleIdentifier = try stringParameter("bundleId", request.params)
97
+ let application = XCUIApplication(bundleIdentifier: bundleIdentifier)
98
+ application.launch()
99
+ return try response(result: ["success": true], id: request.id)
100
+ case "device.apps.terminate":
101
+ let bundleIdentifier = try stringParameter("bundleId", request.params)
102
+ XCUIApplication(bundleIdentifier: bundleIdentifier).terminate()
103
+ return try response(result: ["success": true], id: request.id)
104
+ case "device.io.tap":
105
+ #if os(tvOS)
106
+ throw RunnerError.unsupportedButton("touch")
107
+ #else
108
+ let x = try doubleParameter("x", request.params)
109
+ let y = try doubleParameter("y", request.params)
110
+ let application = foregroundApplication(request.params)
111
+ let frame = application.frame
112
+ let coordinate = application.coordinate(
113
+ withNormalizedOffset: CGVector(
114
+ dx: max(0, min(1, x / Double(frame.width))),
115
+ dy: max(0, min(1, y / Double(frame.height)))
116
+ )
117
+ )
118
+ coordinate.tap()
119
+ return try response(result: ["success": true], id: request.id)
120
+ #endif
121
+ case "device.io.text":
122
+ let text = try stringParameter("text", request.params)
123
+ foregroundApplication(request.params).typeText(text)
124
+ return try response(result: ["success": true], id: request.id)
125
+ case "device.io.button":
126
+ let button = try stringParameter("button", request.params)
127
+ #if os(tvOS)
128
+ let press: XCUIRemote.Button
129
+ switch button {
130
+ case "menu":
131
+ press = .menu
132
+ case "select":
133
+ press = .select
134
+ case "playPause":
135
+ press = .playPause
136
+ case "up":
137
+ press = .up
138
+ case "down":
139
+ press = .down
140
+ case "left":
141
+ press = .left
142
+ case "right":
143
+ press = .right
144
+ default:
145
+ throw RunnerError.unsupportedButton(button)
146
+ }
147
+ XCUIRemote.shared.press(press)
148
+ #else
149
+ guard button == "home" else {
150
+ throw RunnerError.unsupportedButton(button)
151
+ }
152
+ XCUIDevice.shared.press(.home)
153
+ #endif
154
+ return try response(result: ["success": true], id: request.id)
155
+ default:
156
+ return try response(error: -32601, message: "Method not found", id: request.id)
157
+ }
158
+ }
159
+
160
+ private func foregroundApplication(_ params: [String: Any]) -> XCUIApplication {
161
+ if let bundleIdentifier = params["bundleId"] as? String {
162
+ return XCUIApplication(bundleIdentifier: bundleIdentifier)
163
+ }
164
+ return XCUIApplication()
165
+ }
166
+
167
+ private func stringParameter(_ name: String, _ params: [String: Any]) throws -> String {
168
+ guard let value = params[name] as? String, !value.isEmpty else {
169
+ throw RunnerError.invalidParameter(name)
170
+ }
171
+ return value
172
+ }
173
+
174
+ private func doubleParameter(_ name: String, _ params: [String: Any]) throws -> Double {
175
+ if let value = params[name] as? Double {
176
+ return value
177
+ }
178
+ if let value = params[name] as? NSNumber {
179
+ return value.doubleValue
180
+ }
181
+ throw RunnerError.invalidParameter(name)
182
+ }
183
+
184
+ private func response(result: [String: Any], id: Any?) throws -> Data {
185
+ try JSONSerialization.data(withJSONObject: [
186
+ "jsonrpc": "2.0",
187
+ "result": result,
188
+ "id": id ?? NSNull()
189
+ ])
190
+ }
191
+
192
+ private func response(error code: Int, message: String, id: Any?) throws -> Data {
193
+ try JSONSerialization.data(withJSONObject: [
194
+ "jsonrpc": "2.0",
195
+ "error": ["code": code, "message": message],
196
+ "id": id ?? NSNull()
197
+ ])
198
+ }
199
+}
200
+
201
+private final class RPCHandler: ChannelInboundHandler {
202
+ typealias InboundIn = HTTPServerRequestPart
203
+ typealias OutboundOut = HTTPServerResponsePart
204
+
205
+ private let requestHandler: (HTTPMethod, String, Data) async -> Data
206
+ private var requestHead: HTTPRequestHead?
207
+ private var requestBody = Data()
208
+
209
+ init(requestHandler: @escaping (HTTPMethod, String, Data) async -> Data) {
210
+ self.requestHandler = requestHandler
211
+ }
212
+
213
+ func channelRead(context: ChannelHandlerContext, data: NIOAny) {
214
+ switch unwrapInboundIn(data) {
215
+ case .head(let head):
216
+ requestHead = head
217
+ requestBody.removeAll(keepingCapacity: true)
218
+ case .body(var buffer):
219
+ if let bytes = buffer.readBytes(length: buffer.readableBytes) {
220
+ requestBody.append(contentsOf: bytes)
221
+ }
222
+ case .end:
223
+ guard let requestHead else {
224
+ context.close(promise: nil)
225
+ return
226
+ }
227
+
228
+ let method = requestHead.method
229
+ let path = requestHead.uri
230
+ let body = requestBody
231
+ Task {
232
+ let responseBody = await requestHandler(method, path, body)
233
+ context.eventLoop.execute {
234
+ var headers = HTTPHeaders()
235
+ headers.add(name: "Content-Type", value: "application/json")
236
+ headers.add(name: "Content-Length", value: "\(responseBody.count)")
237
+ headers.add(name: "Connection", value: "close")
238
+ let responseHead = HTTPResponseHead(
239
+ version: requestHead.version,
240
+ status: .ok,
241
+ headers: headers
242
+ )
243
+ var buffer = context.channel.allocator.buffer(capacity: responseBody.count)
244
+ buffer.writeBytes(responseBody)
245
+ context.write(self.wrapOutboundOut(.head(responseHead)), promise: nil)
246
+ context.write(self.wrapOutboundOut(.body(.byteBuffer(buffer))), promise: nil)
247
+ context.writeAndFlush(self.wrapOutboundOut(.end(nil))).whenComplete { _ in
248
+ context.close(promise: nil)
249
+ }
250
+ }
251
+ }
252
+ }
253
+ }
254
+}
255
+
256
+private enum RunnerError: LocalizedError {
257
+ case invalidParameter(String)
258
+ case unsupportedButton(String)
259
+
260
+ var errorDescription: String? {
261
+ switch self {
262
+ case .invalidParameter(let name):
263
+ return "Missing or invalid parameter: \(name)"
264
+ case .unsupportedButton(let button):
265
+ return "Unsupported iOS button: \(button)"
266
+ }
267
+ }
268
+}
Runners/iOS/ControlKitRunnerApp.swift
new
+114
@@ -0,0 +1,114 @@
1
+import SwiftUI
2
+
3
+@main
4
+struct ControlKitRunnerApp: App {
5
+ var body: some Scene {
6
+ WindowGroup {
7
+ ControlKitShowcaseView()
8
+ }
9
+ }
10
+}
11
+
12
+private struct ControlKitShowcaseView: View {
13
+ private let repositoryURL = URL(string: "https://github.com/ondeinference/xcrs-controlkit")!
14
+
15
+ var body: some View {
16
+ ZStack {
17
+ LinearGradient(
18
+ colors: [Color.controlKitGreen, Color(red: 0.04, green: 0.12, blue: 0.09)],
19
+ startPoint: .topLeading,
20
+ endPoint: .bottomTrailing
21
+ )
22
+ .ignoresSafeArea()
23
+
24
+ ScrollView {
25
+ VStack(alignment: .leading, spacing: 28) {
26
+ HStack(spacing: 18) {
27
+ logo
28
+ VStack(alignment: .leading, spacing: 4) {
29
+ Text("ControlKit")
30
+ .font(.system(size: 40, weight: .bold, design: .rounded))
31
+ Text("Apple platform automation")
32
+ .font(.title3)
33
+ .foregroundStyle(Color.controlKitCream.opacity(0.78))
34
+ }
35
+ }
36
+
37
+ Text("A clean-room runner for reliable black-box testing across Apple platforms.")
38
+ .font(.title2.weight(.medium))
39
+ .fixedSize(horizontal: false, vertical: true)
40
+
41
+ statusCard
42
+ informationCard
43
+
44
+ Link(destination: repositoryURL) {
45
+ Label("View project on GitHub", systemImage: "arrow.up.right")
46
+ .font(.headline)
47
+ .frame(maxWidth: .infinity)
48
+ }
49
+ .buttonStyle(.borderedProminent)
50
+ .tint(Color.controlKitCream)
51
+ .foregroundStyle(Color.controlKitGreen)
52
+ }
53
+ .foregroundStyle(Color.controlKitCream)
54
+ .padding(32)
55
+ .frame(maxWidth: 820)
56
+ }
57
+ .frame(maxWidth: .infinity, maxHeight: .infinity)
58
+ }
59
+ .frame(maxWidth: .infinity, maxHeight: .infinity)
60
+ .frame(minWidth: UIScreen.main.bounds.width, minHeight: UIScreen.main.bounds.height)
61
+ .ignoresSafeArea(.all)
62
+ .statusBarHidden(true)
63
+ }
64
+
65
+ private var logo: some View {
66
+ Text("XC\nRS")
67
+ .font(.system(size: 30, weight: .black, design: .rounded))
68
+ .multilineTextAlignment(.center)
69
+ .lineSpacing(-8)
70
+ .foregroundStyle(Color.controlKitGreen)
71
+ .frame(width: 96, height: 96)
72
+ .background(Color.controlKitCream, in: RoundedRectangle(cornerRadius: 24))
73
+ }
74
+
75
+ private var statusCard: some View {
76
+ VStack(alignment: .leading, spacing: 14) {
77
+ Label("Runner online", systemImage: "checkmark.circle.fill")
78
+ .font(.headline)
79
+ .foregroundStyle(.green)
80
+ Text("The XCTest host is ready to receive ControlKit automation requests.")
81
+ .foregroundStyle(Color.controlKitCream.opacity(0.78))
82
+ }
83
+ .padding(22)
84
+ .frame(maxWidth: .infinity, alignment: .leading)
85
+ .background(.white.opacity(0.1), in: RoundedRectangle(cornerRadius: 20))
86
+ }
87
+
88
+ private var informationCard: some View {
89
+ VStack(alignment: .leading, spacing: 16) {
90
+ infoRow(title: "Project", value: "XCRSControlKit")
91
+ infoRow(title: "Creator", value: "smbCloudXYZ")
92
+ infoRow(title: "Platform", value: "iOS and iPadOS")
93
+ infoRow(title: "Purpose", value: "Native UI automation runner")
94
+ }
95
+ .padding(22)
96
+ .frame(maxWidth: .infinity, alignment: .leading)
97
+ .background(.white.opacity(0.08), in: RoundedRectangle(cornerRadius: 20))
98
+ }
99
+
100
+ private func infoRow(title: String, value: String) -> some View {
101
+ HStack {
102
+ Text(title)
103
+ .foregroundStyle(Color.controlKitCream.opacity(0.62))
104
+ Spacer()
105
+ Text(value)
106
+ .fontWeight(.semibold)
107
+ }
108
+ }
109
+}
110
+
111
+private extension Color {
112
+ static let controlKitGreen = Color(red: 0.106, green: 0.263, blue: 0.196)
113
+ static let controlKitCream = Color(red: 0.961, green: 0.953, blue: 0.925)
114
+}
Runners/iOS/ControlKitRunnerUITests.swift
new
+10
@@ -0,0 +1,10 @@
1
+import XCTest
2
+
3
+final class ControlKitRunnerUITests: XCTestCase {
4
+ @MainActor
5
+ func testRunAutomation() async throws {
6
+ continueAfterFailure = true
7
+ let server = ControlKitRPCServer()
8
+ try await server.start()
9
+ }
10
+}
Runners/iOS/README.md
new
+9
@@ -0,0 +1,9 @@
1
+# XCRS ControlKit iOS runner
2
+
3
+This is the first native runner owned by XCRS ControlKit. It is an Xcode UI
4
+testing project, not a SwiftPM target, and imports the public
5
+`XCRSControlKit` package for capability and request contracts.
6
+
7
+The runner exposes a small localhost JSON-RPC server to the Rust `xcrs`
8
+client using SwiftNIO's HTTP/1 server pipeline. It intentionally contains no
9
+DeviceKit source or private XCTest headers.
Runners/iOS/XCRSControlKitRunner.xcodeproj/project.pbxproj
new
+500
@@ -0,0 +1,500 @@
1
+// !$*UTF8*$!
2
+{
3
+ archiveVersion = 1;
4
+ classes = {
5
+ };
6
+ objectVersion = 46;
7
+ objects = {
8
+
9
+/* Begin PBXBuildFile section */
10
+ 12FB8732E528D5BC3CFD5353 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 677B2BA6304D7A19CA4199C6 /* Foundation.framework */; };
11
+ 240C04374372369CF9288310 /* ControlKitRPCServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 45F4A2788516250C6390D6A7 /* ControlKitRPCServer.swift */; };
12
+ 5267368AE4FA4A1AF152F2BB /* ControlKitRunnerApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3825CF3F09E2C73AC5BCF561 /* ControlKitRunnerApp.swift */; };
13
+ 623F26CA1E5FC3982AAB778C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = C9FE4276F055DA74C55074CB /* Assets.xcassets */; };
14
+ 6E7B19FB8377403D45E5B319 /* ControlKitRunnerUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 97053F46DD106789E5704C28 /* ControlKitRunnerUITests.swift */; };
15
+ A66D6E921CCD246E49A34BD2 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 677B2BA6304D7A19CA4199C6 /* Foundation.framework */; };
16
+/* End PBXBuildFile section */
17
+
18
+/* Begin PBXContainerItemProxy section */
19
+ A427DD4B39849849959312A5 /* PBXContainerItemProxy */ = {
20
+ isa = PBXContainerItemProxy;
21
+ containerPortal = 5A4615FAD9C725BDA6BB9D0D /* Project object */;
22
+ proxyType = 1;
23
+ remoteGlobalIDString = 65C69F9323FE4A33A9BD5CFD;
24
+ remoteInfo = XCRSControlKitRunner;
25
+ };
26
+/* End PBXContainerItemProxy section */
27
+
28
+/* Begin PBXFileReference section */
29
+ 158D6AE634D750F58BFA4F3C /* XCRSControlKitRunner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = XCRSControlKitRunner.app; sourceTree = BUILT_PRODUCTS_DIR; };
30
+ 3825CF3F09E2C73AC5BCF561 /* ControlKitRunnerApp.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ControlKitRunnerApp.swift; sourceTree = "<group>"; };
31
+ 45F4A2788516250C6390D6A7 /* ControlKitRPCServer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ControlKitRPCServer.swift; sourceTree = "<group>"; };
32
+ 677B2BA6304D7A19CA4199C6 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS18.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; };
33
+ 97053F46DD106789E5704C28 /* ControlKitRunnerUITests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ControlKitRunnerUITests.swift; sourceTree = "<group>"; };
34
+ C9FE4276F055DA74C55074CB /* Assets.xcassets */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
35
+ F2384A3BC0E7D978EED69C35 /* XCRSControlKitRunnerUITests */ = {isa = PBXFileReference; includeInIndex = 0; path = XCRSControlKitRunnerUITests; sourceTree = BUILT_PRODUCTS_DIR; };
36
+/* End PBXFileReference section */
37
+
38
+/* Begin PBXFrameworksBuildPhase section */
39
+ 80BD8489289F29F80E617598 /* Frameworks */ = {
40
+ isa = PBXFrameworksBuildPhase;
41
+ buildActionMask = 2147483647;
42
+ files = (
43
+ A66D6E921CCD246E49A34BD2 /* Foundation.framework in Frameworks */,
44
+ );
45
+ runOnlyForDeploymentPostprocessing = 0;
46
+ };
47
+ CE6C87241EBD1B86DA8BA3AF /* Frameworks */ = {
48
+ isa = PBXFrameworksBuildPhase;
49
+ buildActionMask = 2147483647;
50
+ files = (
51
+ 12FB8732E528D5BC3CFD5353 /* Foundation.framework in Frameworks */,
52
+ );
53
+ runOnlyForDeploymentPostprocessing = 0;
54
+ };
55
+/* End PBXFrameworksBuildPhase section */
56
+
57
+/* Begin PBXGroup section */
58
+ 26FCC7EB76F7FBFA9F2B1238 /* XCRSControlKitRunnerUITests */ = {
59
+ isa = PBXGroup;
60
+ children = (
61
+ 97053F46DD106789E5704C28 /* ControlKitRunnerUITests.swift */,
62
+ 45F4A2788516250C6390D6A7 /* ControlKitRPCServer.swift */,
63
+ );
64
+ name = XCRSControlKitRunnerUITests;
65
+ path = .;
66
+ sourceTree = "<group>";
67
+ };
68
+ 27707640A8563F24E4EB7902 /* Frameworks */ = {
69
+ isa = PBXGroup;
70
+ children = (
71
+ F62FFE7A6809A05F068F78F4 /* iOS */,
72
+ );
73
+ name = Frameworks;
74
+ sourceTree = "<group>";
75
+ };
76
+ 48CB2BAD4FA1AA8E4DA76E10 /* XCRSControlKitRunner */ = {
77
+ isa = PBXGroup;
78
+ children = (
79
+ 3825CF3F09E2C73AC5BCF561 /* ControlKitRunnerApp.swift */,
80
+ C9FE4276F055DA74C55074CB /* Assets.xcassets */,
81
+ );
82
+ name = XCRSControlKitRunner;
83
+ path = .;
84
+ sourceTree = "<group>";
85
+ };
86
+ 6B3D128F7499546CFF24AF0B /* Products */ = {
87
+ isa = PBXGroup;
88
+ children = (
89
+ 158D6AE634D750F58BFA4F3C /* XCRSControlKitRunner.app */,
90
+ F2384A3BC0E7D978EED69C35 /* XCRSControlKitRunnerUITests */,
91
+ );
92
+ name = Products;
93
+ sourceTree = "<group>";
94
+ };
95
+ A58B00BE92DC60DE632CF4D2 = {
96
+ isa = PBXGroup;
97
+ children = (
98
+ 6B3D128F7499546CFF24AF0B /* Products */,
99
+ 27707640A8563F24E4EB7902 /* Frameworks */,
100
+ 48CB2BAD4FA1AA8E4DA76E10 /* XCRSControlKitRunner */,
101
+ 26FCC7EB76F7FBFA9F2B1238 /* XCRSControlKitRunnerUITests */,
102
+ );
103
+ sourceTree = "<group>";
104
+ };
105
+ F62FFE7A6809A05F068F78F4 /* iOS */ = {
106
+ isa = PBXGroup;
107
+ children = (
108
+ 677B2BA6304D7A19CA4199C6 /* Foundation.framework */,
109
+ );
110
+ name = iOS;
111
+ sourceTree = "<group>";
112
+ };
113
+/* End PBXGroup section */
114
+
115
+/* Begin PBXNativeTarget section */
116
+ 3D3C633B3F1CE5F513D7F973 /* XCRSControlKitRunnerUITests */ = {
117
+ isa = PBXNativeTarget;
118
+ buildConfigurationList = 09DCF821A316EB9B782B3281 /* Build configuration list for PBXNativeTarget "XCRSControlKitRunnerUITests" */;
119
+ buildPhases = (
120
+ BE45964975925FCDB7E07A02 /* Sources */,
121
+ CE6C87241EBD1B86DA8BA3AF /* Frameworks */,
122
+ 8C5E82B59DC87CA04ED56147 /* Resources */,
123
+ );
124
+ buildRules = (
125
+ );
126
+ dependencies = (
127
+ AD9C27DDC63B00A39E2F2F6B /* PBXTargetDependency */,
128
+ );
129
+ name = XCRSControlKitRunnerUITests;
130
+ packageProductDependencies = (
131
+ 519A8A0C72E51DA6D0174E87 /* XCRSControlKit */,
132
+ 3C011EAFF81B128EFF169657 /* NIOCore */,
133
+ 3C599300D59533D3F1EAAB06 /* NIOHTTP1 */,
134
+ 752918E24D37ADB5D048781D /* NIOPosix */,
135
+ );
136
+ productName = XCRSControlKitRunnerUITests;
137
+ productReference = F2384A3BC0E7D978EED69C35 /* XCRSControlKitRunnerUITests */;
138
+ productType = "com.apple.product-type.bundle.ui-testing";
139
+ };
140
+ 65C69F9323FE4A33A9BD5CFD /* XCRSControlKitRunner */ = {
141
+ isa = PBXNativeTarget;
142
+ buildConfigurationList = 1E9093BA177C6E09C7249452 /* Build configuration list for PBXNativeTarget "XCRSControlKitRunner" */;
143
+ buildPhases = (
144
+ 2E147C33CA6023EC566F9503 /* Sources */,
145
+ 80BD8489289F29F80E617598 /* Frameworks */,
146
+ B06C9835F24B5A268D7376F3 /* Resources */,
147
+ );
148
+ buildRules = (
149
+ );
150
+ dependencies = (
151
+ );
152
+ name = XCRSControlKitRunner;
153
+ productName = XCRSControlKitRunner;
154
+ productReference = 158D6AE634D750F58BFA4F3C /* XCRSControlKitRunner.app */;
155
+ productType = "com.apple.product-type.application";
156
+ };
157
+/* End PBXNativeTarget section */
158
+
159
+/* Begin PBXProject section */
160
+ 5A4615FAD9C725BDA6BB9D0D /* Project object */ = {
161
+ isa = PBXProject;
162
+ attributes = {
163
+ LastSwiftUpdateCheck = 2630;
164
+ LastUpgradeCheck = 2630;
165
+ };
166
+ buildConfigurationList = 8E50C3CB1D836D41CF571655 /* Build configuration list for PBXProject "XCRSControlKitRunner" */;
167
+ compatibilityVersion = "Xcode 3.2";
168
+ developmentRegion = en;
169
+ hasScannedForEncodings = 0;
170
+ knownRegions = (
171
+ en,
172
+ Base,
173
+ );
174
+ mainGroup = A58B00BE92DC60DE632CF4D2;
175
+ minimizedProjectReferenceProxies = 0;
176
+ packageReferences = (
177
+ 3DBF8CF0F31AA03CAC623B80 /* XCLocalSwiftPackageReference ".." */,
178
+ 5B254FC74CBF06A66B4DD162 /* XCRemoteSwiftPackageReference "swift-nio" */,
179
+ );
180
+ preferredProjectObjectVersion = 77;
181
+ productRefGroup = 6B3D128F7499546CFF24AF0B /* Products */;
182
+ projectDirPath = "";
183
+ projectRoot = "";
184
+ targets = (
185
+ 65C69F9323FE4A33A9BD5CFD /* XCRSControlKitRunner */,
186
+ 3D3C633B3F1CE5F513D7F973 /* XCRSControlKitRunnerUITests */,
187
+ );
188
+ };
189
+/* End PBXProject section */
190
+
191
+/* Begin PBXResourcesBuildPhase section */
192
+ 8C5E82B59DC87CA04ED56147 /* Resources */ = {
193
+ isa = PBXResourcesBuildPhase;
194
+ buildActionMask = 2147483647;
195
+ files = (
196
+ );
197
+ runOnlyForDeploymentPostprocessing = 0;
198
+ };
199
+ B06C9835F24B5A268D7376F3 /* Resources */ = {
200
+ isa = PBXResourcesBuildPhase;
201
+ buildActionMask = 2147483647;
202
+ files = (
203
+ 623F26CA1E5FC3982AAB778C /* Assets.xcassets in Resources */,
204
+ );
205
+ runOnlyForDeploymentPostprocessing = 0;
206
+ };
207
+/* End PBXResourcesBuildPhase section */
208
+
209
+/* Begin PBXSourcesBuildPhase section */
210
+ 2E147C33CA6023EC566F9503 /* Sources */ = {
211
+ isa = PBXSourcesBuildPhase;
212
+ buildActionMask = 2147483647;
213
+ files = (
214
+ 5267368AE4FA4A1AF152F2BB /* ControlKitRunnerApp.swift in Sources */,
215
+ );
216
+ runOnlyForDeploymentPostprocessing = 0;
217
+ };
218
+ BE45964975925FCDB7E07A02 /* Sources */ = {
219
+ isa = PBXSourcesBuildPhase;
220
+ buildActionMask = 2147483647;
221
+ files = (
222
+ 6E7B19FB8377403D45E5B319 /* ControlKitRunnerUITests.swift in Sources */,
223
+ 240C04374372369CF9288310 /* ControlKitRPCServer.swift in Sources */,
224
+ );
225
+ runOnlyForDeploymentPostprocessing = 0;
226
+ };
227
+/* End PBXSourcesBuildPhase section */
228
+
229
+/* Begin PBXTargetDependency section */
230
+ AD9C27DDC63B00A39E2F2F6B /* PBXTargetDependency */ = {
231
+ isa = PBXTargetDependency;
232
+ name = XCRSControlKitRunner;
233
+ target = 65C69F9323FE4A33A9BD5CFD /* XCRSControlKitRunner */;
234
+ targetProxy = A427DD4B39849849959312A5 /* PBXContainerItemProxy */;
235
+ };
236
+/* End PBXTargetDependency section */
237
+
238
+/* Begin XCBuildConfiguration section */
239
+ 00ABD475CB5E7B4186EF1F6F /* Debug */ = {
240
+ isa = XCBuildConfiguration;
241
+ buildSettings = {
242
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
243
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
244
+ CLANG_ENABLE_OBJC_WEAK = NO;
245
+ GENERATE_INFOPLIST_FILE = YES;
246
+ INFOPLIST_KEY_CFBundleDisplayName = ControlKit;
247
+ INFOPLIST_KEY_UIRequiresFullScreen = YES;
248
+ INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait;
249
+ IPHONEOS_DEPLOYMENT_TARGET = 16.0;
250
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
251
+ PRODUCT_BUNDLE_IDENTIFIER = "com.smbcloud.xcrs-controlkit.runner";
252
+ PRODUCT_NAME = ControlKit;
253
+ SDKROOT = iphoneos;
254
+ SWIFT_VERSION = 5.9;
255
+ TARGETED_DEVICE_FAMILY = "1,2";
256
+ };
257
+ name = Debug;
258
+ };
259
+ 2980F2C3DA0326EBDFED9234 /* Release */ = {
260
+ isa = XCBuildConfiguration;
261
+ buildSettings = {
262
+ ALWAYS_SEARCH_USER_PATHS = NO;
263
+ CLANG_ANALYZER_NONNULL = YES;
264
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
265
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
266
+ CLANG_CXX_LIBRARY = "libc++";
267
+ CLANG_ENABLE_MODULES = YES;
268
+ CLANG_ENABLE_OBJC_ARC = YES;
269
+ CLANG_ENABLE_OBJC_WEAK = YES;
270
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
271
+ CLANG_WARN_BOOL_CONVERSION = YES;
272
+ CLANG_WARN_COMMA = YES;
273
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
274
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
275
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
276
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
277
+ CLANG_WARN_EMPTY_BODY = YES;
278
+ CLANG_WARN_ENUM_CONVERSION = YES;
279
+ CLANG_WARN_INFINITE_RECURSION = YES;
280
+ CLANG_WARN_INT_CONVERSION = YES;
281
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
282
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
283
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
284
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
285
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
286
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
287
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
288
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
289
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
290
+ CLANG_WARN_UNREACHABLE_CODE = YES;
291
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
292
+ COPY_PHASE_STRIP = NO;
293
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
294
+ ENABLE_NS_ASSERTIONS = NO;
295
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
296
+ GCC_C_LANGUAGE_STANDARD = gnu11;
297
+ GCC_NO_COMMON_BLOCKS = YES;
298
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
299
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
300
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
301
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
302
+ GCC_WARN_UNUSED_FUNCTION = YES;
303
+ GCC_WARN_UNUSED_VARIABLE = YES;
304
+ MTL_ENABLE_DEBUG_INFO = NO;
305
+ MTL_FAST_MATH = YES;
306
+ PRODUCT_NAME = "$(TARGET_NAME)";
307
+ SWIFT_COMPILATION_MODE = wholemodule;
308
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
309
+ SWIFT_VERSION = 5.0;
310
+ };
311
+ name = Release;
312
+ };
313
+ 720942CCA660A8F8898D0376 /* Debug */ = {
314
+ isa = XCBuildConfiguration;
315
+ buildSettings = {
316
+ CLANG_ENABLE_OBJC_WEAK = NO;
317
+ GENERATE_INFOPLIST_FILE = YES;
318
+ IPHONEOS_DEPLOYMENT_TARGET = 16.0;
319
+ PRODUCT_BUNDLE_IDENTIFIER = "com.smbcloud.xcrs-controlkit.runnerUITests";
320
+ PRODUCT_NAME = XCRSControlKitRunnerUITests;
321
+ SDKROOT = iphoneos;
322
+ SWIFT_VERSION = 5.9;
323
+ TARGETED_DEVICE_FAMILY = "1,2";
324
+ TEST_TARGET_NAME = XCRSControlKitRunner;
325
+ };
326
+ name = Debug;
327
+ };
328
+ A3145EA68D6C9D1145657B84 /* Release */ = {
329
+ isa = XCBuildConfiguration;
330
+ buildSettings = {
331
+ CLANG_ENABLE_OBJC_WEAK = NO;
332
+ GENERATE_INFOPLIST_FILE = YES;
333
+ IPHONEOS_DEPLOYMENT_TARGET = 16.0;
334
+ PRODUCT_BUNDLE_IDENTIFIER = "com.smbcloud.xcrs-controlkit.runnerUITests";
335
+ PRODUCT_NAME = XCRSControlKitRunnerUITests;
336
+ SDKROOT = iphoneos;
337
+ SWIFT_VERSION = 5.9;
338
+ TARGETED_DEVICE_FAMILY = "1,2";
339
+ TEST_TARGET_NAME = XCRSControlKitRunner;
340
+ VALIDATE_PRODUCT = YES;
341
+ };
342
+ name = Release;
343
+ };
344
+ CCA6074EF7667C1F23D52908 /* Debug */ = {
345
+ isa = XCBuildConfiguration;
346
+ buildSettings = {
347
+ ALWAYS_SEARCH_USER_PATHS = NO;
348
+ CLANG_ANALYZER_NONNULL = YES;
349
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
350
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
351
+ CLANG_CXX_LIBRARY = "libc++";
352
+ CLANG_ENABLE_MODULES = YES;
353
+ CLANG_ENABLE_OBJC_ARC = YES;
354
+ CLANG_ENABLE_OBJC_WEAK = YES;
355
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
356
+ CLANG_WARN_BOOL_CONVERSION = YES;
357
+ CLANG_WARN_COMMA = YES;
358
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
359
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
360
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
361
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
362
+ CLANG_WARN_EMPTY_BODY = YES;
363
+ CLANG_WARN_ENUM_CONVERSION = YES;
364
+ CLANG_WARN_INFINITE_RECURSION = YES;
365
+ CLANG_WARN_INT_CONVERSION = YES;
366
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
367
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
368
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
369
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
370
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
371
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
372
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
373
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
374
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
375
+ CLANG_WARN_UNREACHABLE_CODE = YES;
376
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
377
+ COPY_PHASE_STRIP = NO;
378
+ DEBUG_INFORMATION_FORMAT = dwarf;
379
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
380
+ ENABLE_TESTABILITY = YES;
381
+ GCC_C_LANGUAGE_STANDARD = gnu11;
382
+ GCC_DYNAMIC_NO_PIC = NO;
383
+ GCC_NO_COMMON_BLOCKS = YES;
384
+ GCC_OPTIMIZATION_LEVEL = 0;
385
+ GCC_PREPROCESSOR_DEFINITIONS = (
386
+ "DEBUG=1",
387
+ "$(inherited)",
388
+ );
389
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
390
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
391
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
392
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
393
+ GCC_WARN_UNUSED_FUNCTION = YES;
394
+ GCC_WARN_UNUSED_VARIABLE = YES;
395
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
396
+ MTL_FAST_MATH = YES;
397
+ ONLY_ACTIVE_ARCH = YES;
398
+ PRODUCT_NAME = "$(TARGET_NAME)";
399
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
400
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
401
+ SWIFT_VERSION = 5.0;
402
+ };
403
+ name = Debug;
404
+ };
405
+ FD6AFD55D263981B035C1B67 /* Release */ = {
406
+ isa = XCBuildConfiguration;
407
+ buildSettings = {
408
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
409
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
410
+ CLANG_ENABLE_OBJC_WEAK = NO;
411
+ GENERATE_INFOPLIST_FILE = YES;
412
+ INFOPLIST_KEY_CFBundleDisplayName = ControlKit;
413
+ INFOPLIST_KEY_UIRequiresFullScreen = YES;
414
+ INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait;
415
+ IPHONEOS_DEPLOYMENT_TARGET = 16.0;
416
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
417
+ PRODUCT_BUNDLE_IDENTIFIER = "com.smbcloud.xcrs-controlkit.runner";
418
+ PRODUCT_NAME = ControlKit;
419
+ SDKROOT = iphoneos;
420
+ SWIFT_VERSION = 5.9;
421
+ TARGETED_DEVICE_FAMILY = "1,2";
422
+ VALIDATE_PRODUCT = YES;
423
+ };
424
+ name = Release;
425
+ };
426
+/* End XCBuildConfiguration section */
427
+
428
+/* Begin XCConfigurationList section */
429
+ 09DCF821A316EB9B782B3281 /* Build configuration list for PBXNativeTarget "XCRSControlKitRunnerUITests" */ = {
430
+ isa = XCConfigurationList;
431
+ buildConfigurations = (
432
+ A3145EA68D6C9D1145657B84 /* Release */,
433
+ 720942CCA660A8F8898D0376 /* Debug */,
434
+ );
435
+ defaultConfigurationIsVisible = 0;
436
+ defaultConfigurationName = Release;
437
+ };
438
+ 1E9093BA177C6E09C7249452 /* Build configuration list for PBXNativeTarget "XCRSControlKitRunner" */ = {
439
+ isa = XCConfigurationList;
440
+ buildConfigurations = (
441
+ FD6AFD55D263981B035C1B67 /* Release */,
442
+ 00ABD475CB5E7B4186EF1F6F /* Debug */,
443
+ );
444
+ defaultConfigurationIsVisible = 0;
445
+ defaultConfigurationName = Release;
446
+ };
447
+ 8E50C3CB1D836D41CF571655 /* Build configuration list for PBXProject "XCRSControlKitRunner" */ = {
448
+ isa = XCConfigurationList;
449
+ buildConfigurations = (
450
+ CCA6074EF7667C1F23D52908 /* Debug */,
451
+ 2980F2C3DA0326EBDFED9234 /* Release */,
452
+ );
453
+ defaultConfigurationIsVisible = 0;
454
+ defaultConfigurationName = Release;
455
+ };
456
+/* End XCConfigurationList section */
457
+
458
+/* Begin XCLocalSwiftPackageReference section */
459
+ 3DBF8CF0F31AA03CAC623B80 /* XCLocalSwiftPackageReference ".." */ = {
460
+ isa = XCLocalSwiftPackageReference;
461
+ relativePath = ../..;
462
+ };
463
+/* End XCLocalSwiftPackageReference section */
464
+
465
+/* Begin XCRemoteSwiftPackageReference section */
466
+ 5B254FC74CBF06A66B4DD162 /* XCRemoteSwiftPackageReference "swift-nio" */ = {
467
+ isa = XCRemoteSwiftPackageReference;
468
+ repositoryURL = "https://github.com/apple/swift-nio.git";
469
+ requirement = {
470
+ kind = upToNextMajorVersion;
471
+ minimumVersion = 2.0.0;
472
+ };
473
+ };
474
+/* End XCRemoteSwiftPackageReference section */
475
+
476
+/* Begin XCSwiftPackageProductDependency section */
477
+ 3C011EAFF81B128EFF169657 /* NIOCore */ = {
478
+ isa = XCSwiftPackageProductDependency;
479
+ package = 5B254FC74CBF06A66B4DD162 /* XCRemoteSwiftPackageReference "swift-nio" */;
480
+ productName = NIOCore;
481
+ };
482
+ 3C599300D59533D3F1EAAB06 /* NIOHTTP1 */ = {
483
+ isa = XCSwiftPackageProductDependency;
484
+ package = 5B254FC74CBF06A66B4DD162 /* XCRemoteSwiftPackageReference "swift-nio" */;
485
+ productName = NIOHTTP1;
486
+ };
487
+ 519A8A0C72E51DA6D0174E87 /* XCRSControlKit */ = {
488
+ isa = XCSwiftPackageProductDependency;
489
+ package = 5B254FC74CBF06A66B4DD162 /* XCRemoteSwiftPackageReference "swift-nio" */;
490
+ productName = XCRSControlKit;
491
+ };
492
+ 752918E24D37ADB5D048781D /* NIOPosix */ = {
493
+ isa = XCSwiftPackageProductDependency;
494
+ package = 5B254FC74CBF06A66B4DD162 /* XCRemoteSwiftPackageReference "swift-nio" */;
495
+ productName = NIOPosix;
496
+ };
497
+/* End XCSwiftPackageProductDependency section */
498
+ };
499
+ rootObject = 5A4615FAD9C725BDA6BB9D0D /* Project object */;
500
+}
Runners/iOS/XCRSControlKitRunner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
new
+42
@@ -0,0 +1,42 @@
1
+{
2
+ "originHash" : "dc64d5a9a584c1afdd2823a6335adce6e83ebd68e5e5f250156866e5bdc7e973",
3
+ "pins" : [
4
+ {
5
+ "identity" : "swift-atomics",
6
+ "kind" : "remoteSourceControl",
7
+ "location" : "https://github.com/apple/swift-atomics.git",
8
+ "state" : {
9
+ "revision" : "0442cb5a3f98ab802acb777929fdb446bda11a34",
10
+ "version" : "1.3.1"
11
+ }
12
+ },
13
+ {
14
+ "identity" : "swift-collections",
15
+ "kind" : "remoteSourceControl",
16
+ "location" : "https://github.com/apple/swift-collections.git",
17
+ "state" : {
18
+ "revision" : "a0cb0954ecb21e4e31b0070e6ed5674e8556685a",
19
+ "version" : "1.6.0"
20
+ }
21
+ },
22
+ {
23
+ "identity" : "swift-nio",
24
+ "kind" : "remoteSourceControl",
25
+ "location" : "https://github.com/apple/swift-nio.git",
26
+ "state" : {
27
+ "revision" : "0b18836bd8b0162e7e17a995a3fbee20ed8f3b2b",
28
+ "version" : "2.101.3"
29
+ }
30
+ },
31
+ {
32
+ "identity" : "swift-system",
33
+ "kind" : "remoteSourceControl",
34
+ "location" : "https://github.com/apple/swift-system.git",
35
+ "state" : {
36
+ "revision" : "50688cacbd41d547e9eb9f7a213542340b7c442b",
37
+ "version" : "1.7.5"
38
+ }
39
+ }
40
+ ],
41
+ "version" : 3
42
+}
Runners/iOS/XCRSControlKitRunner.xcodeproj/xcshareddata/xcschemes/ControlKit.xcscheme
new
+91
@@ -0,0 +1,91 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<Scheme
3
+ LastUpgradeVersion = "2660"
4
+ version = "1.7">
5
+ <BuildAction
6
+ parallelizeBuildables = "YES"
7
+ buildImplicitDependencies = "YES"
8
+ buildArchitectures = "Automatic">
9
+ <BuildActionEntries>
10
+ <BuildActionEntry
11
+ buildForTesting = "YES"
12
+ buildForRunning = "YES"
13
+ buildForProfiling = "YES"
14
+ buildForArchiving = "YES"
15
+ buildForAnalyzing = "YES">
16
+ <BuildableReference
17
+ BuildableIdentifier = "primary"
18
+ BlueprintIdentifier = "65C69F9323FE4A33A9BD5CFD"
19
+ BuildableName = "ControlKit.app"
20
+ BlueprintName = "XCRSControlKitRunner"
21
+ ReferencedContainer = "container:XCRSControlKitRunner.xcodeproj">
22
+ </BuildableReference>
23
+ </BuildActionEntry>
24
+ </BuildActionEntries>
25
+ </BuildAction>
26
+ <TestAction
27
+ buildConfiguration = "Debug"
28
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
29
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
30
+ shouldUseLaunchSchemeArgsEnv = "YES"
31
+ shouldAutocreateTestPlan = "YES">
32
+ <Testables>
33
+ <TestableReference
34
+ skipped = "NO">
35
+ <BuildableReference
36
+ BuildableIdentifier = "primary"
37
+ BlueprintIdentifier = "3D3C633B3F1CE5F513D7F973"
38
+ BuildableName = "XCRSControlKitRunnerUITests.xctest"
39
+ BlueprintName = "XCRSControlKitRunnerUITests"
40
+ ReferencedContainer = "container:XCRSControlKitRunner.xcodeproj">
41
+ </BuildableReference>
42
+ </TestableReference>
43
+ </Testables>
44
+ </TestAction>
45
+ <LaunchAction
46
+ buildConfiguration = "Debug"
47
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
48
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
49
+ launchStyle = "0"
50
+ useCustomWorkingDirectory = "NO"
51
+ ignoresPersistentStateOnLaunch = "NO"
52
+ debugDocumentVersioning = "YES"
53
+ debugServiceExtension = "internal"
54
+ allowLocationSimulation = "YES"
55
+ queueDebuggingEnableBacktraceRecording = "Yes">
56
+ <BuildableProductRunnable
57
+ runnableDebuggingMode = "0">
58
+ <BuildableReference
59
+ BuildableIdentifier = "primary"
60
+ BlueprintIdentifier = "65C69F9323FE4A33A9BD5CFD"
61
+ BuildableName = "ControlKit.app"
62
+ BlueprintName = "XCRSControlKitRunner"
63
+ ReferencedContainer = "container:XCRSControlKitRunner.xcodeproj">
64
+ </BuildableReference>
65
+ </BuildableProductRunnable>
66
+ </LaunchAction>
67
+ <ProfileAction
68
+ buildConfiguration = "Release"
69
+ shouldUseLaunchSchemeArgsEnv = "YES"
70
+ savedToolIdentifier = ""
71
+ useCustomWorkingDirectory = "NO"
72
+ debugDocumentVersioning = "YES">
73
+ <BuildableProductRunnable
74
+ runnableDebuggingMode = "0">
75
+ <BuildableReference
76
+ BuildableIdentifier = "primary"
77
+ BlueprintIdentifier = "65C69F9323FE4A33A9BD5CFD"
78
+ BuildableName = "ControlKit.app"
79
+ BlueprintName = "XCRSControlKitRunner"
80
+ ReferencedContainer = "container:XCRSControlKitRunner.xcodeproj">
81
+ </BuildableReference>
82
+ </BuildableProductRunnable>
83
+ </ProfileAction>
84
+ <AnalyzeAction
85
+ buildConfiguration = "Debug">
86
+ </AnalyzeAction>
87
+ <ArchiveAction
88
+ buildConfiguration = "Release"
89
+ revealArchiveInOrganizer = "YES">
90
+ </ArchiveAction>
91
+</Scheme>
Runners/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/Contents.json
new
+17
@@ -0,0 +1,17 @@
1
+{
2
+ "images" : [
3
+ {
4
+ "filename" : "ControlKit-tvOS.png",
5
+ "idiom" : "tv",
6
+ "scale" : "1x"
7
+ },
8
+ {
9
+ "idiom" : "tv",
10
+ "scale" : "2x"
11
+ }
12
+ ],
13
+ "info" : {
14
+ "author" : "xcode",
15
+ "version" : 1
16
+ }
17
+}
Runners/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/ControlKit-tvOS.png
Binary files /dev/null and b/Runners/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Content.imageset/ControlKit-tvOS.png differ
Runners/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Back.imagestacklayer/Contents.json
new
+6
@@ -0,0 +1,6 @@
1
+{
2
+ "info" : {
3
+ "author" : "xcode",
4
+ "version" : 1
5
+ }
6
+}
Runners/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Contents.json
new
+11
@@ -0,0 +1,11 @@
1
+{
2
+ "info" : {
3
+ "author" : "xcode",
4
+ "version" : 1
5
+ },
6
+ "layers" : [
7
+ { "filename" : "Front.imagestacklayer" },
8
+ { "filename" : "Middle.imagestacklayer" },
9
+ { "filename" : "Back.imagestacklayer" }
10
+ ]
11
+}
Runners/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/Contents.json
new
+17
@@ -0,0 +1,17 @@
1
+{
2
+ "images" : [
3
+ {
4
+ "filename" : "ControlKit-tvOS.png",
5
+ "idiom" : "tv",
6
+ "scale" : "1x"
7
+ },
8
+ {
9
+ "idiom" : "tv",
10
+ "scale" : "2x"
11
+ }
12
+ ],
13
+ "info" : {
14
+ "author" : "xcode",
15
+ "version" : 1
16
+ }
17
+}
Runners/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/ControlKit-tvOS.png
Binary files /dev/null and b/Runners/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Content.imageset/ControlKit-tvOS.png differ
Runners/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Front.imagestacklayer/Contents.json
new
+6
@@ -0,0 +1,6 @@
1
+{
2
+ "info" : {
3
+ "author" : "xcode",
4
+ "version" : 1
5
+ }
6
+}
Runners/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json
new
+17
@@ -0,0 +1,17 @@
1
+{
2
+ "images" : [
3
+ {
4
+ "filename" : "ControlKit-tvOS.png",
5
+ "idiom" : "tv",
6
+ "scale" : "1x"
7
+ },
8
+ {
9
+ "idiom" : "tv",
10
+ "scale" : "2x"
11
+ }
12
+ ],
13
+ "info" : {
14
+ "author" : "xcode",
15
+ "version" : 1
16
+ }
17
+}
Runners/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/ControlKit-tvOS.png
Binary files /dev/null and b/Runners/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Content.imageset/ControlKit-tvOS.png differ
Runners/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Middle.imagestacklayer/Contents.json
new
+6
@@ -0,0 +1,6 @@
1
+{
2
+ "info" : {
3
+ "author" : "xcode",
4
+ "version" : 1
5
+ }
6
+}
Runners/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/Contents.json
new
+17
@@ -0,0 +1,17 @@
1
+{
2
+ "images" : [
3
+ {
4
+ "filename" : "ControlKit-tvOS.png",
5
+ "idiom" : "tv",
6
+ "scale" : "1x"
7
+ },
8
+ {
9
+ "idiom" : "tv",
10
+ "scale" : "2x"
11
+ }
12
+ ],
13
+ "info" : {
14
+ "author" : "xcode",
15
+ "version" : 1
16
+ }
17
+}
Runners/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/ControlKit-tvOS.png
Binary files /dev/null and b/Runners/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Content.imageset/ControlKit-tvOS.png differ
Runners/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Back.imagestacklayer/Contents.json
new
+6
@@ -0,0 +1,6 @@
1
+{
2
+ "info" : {
3
+ "author" : "xcode",
4
+ "version" : 1
5
+ }
6
+}
Runners/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Contents.json
new
+11
@@ -0,0 +1,11 @@
1
+{
2
+ "info" : {
3
+ "author" : "xcode",
4
+ "version" : 1
5
+ },
6
+ "layers" : [
7
+ { "filename" : "Front.imagestacklayer" },
8
+ { "filename" : "Middle.imagestacklayer" },
9
+ { "filename" : "Back.imagestacklayer" }
10
+ ]
11
+}
Runners/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/Contents.json
new
+17
@@ -0,0 +1,17 @@
1
+{
2
+ "images" : [
3
+ {
4
+ "filename" : "ControlKit-tvOS.png",
5
+ "idiom" : "tv",
6
+ "scale" : "1x"
7
+ },
8
+ {
9
+ "idiom" : "tv",
10
+ "scale" : "2x"
11
+ }
12
+ ],
13
+ "info" : {
14
+ "author" : "xcode",
15
+ "version" : 1
16
+ }
17
+}
Runners/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/ControlKit-tvOS.png
Binary files /dev/null and b/Runners/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Content.imageset/ControlKit-tvOS.png differ
Runners/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Front.imagestacklayer/Contents.json
new
+6
@@ -0,0 +1,6 @@
1
+{
2
+ "info" : {
3
+ "author" : "xcode",
4
+ "version" : 1
5
+ }
6
+}
Runners/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/Contents.json
new
+17
@@ -0,0 +1,17 @@
1
+{
2
+ "images" : [
3
+ {
4
+ "filename" : "ControlKit-tvOS.png",
5
+ "idiom" : "tv",
6
+ "scale" : "1x"
7
+ },
8
+ {
9
+ "idiom" : "tv",
10
+ "scale" : "2x"
11
+ }
12
+ ],
13
+ "info" : {
14
+ "author" : "xcode",
15
+ "version" : 1
16
+ }
17
+}
Runners/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/ControlKit-tvOS.png
Binary files /dev/null and b/Runners/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Content.imageset/ControlKit-tvOS.png differ
Runners/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Middle.imagestacklayer/Contents.json
new
+6
@@ -0,0 +1,6 @@
1
+{
2
+ "info" : {
3
+ "author" : "xcode",
4
+ "version" : 1
5
+ }
6
+}
Runners/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Contents.json
new
+32
@@ -0,0 +1,32 @@
1
+{
2
+ "assets" : [
3
+ {
4
+ "filename" : "App Icon - App Store.imagestack",
5
+ "idiom" : "tv",
6
+ "role" : "primary-app-icon",
7
+ "size" : "1280x768"
8
+ },
9
+ {
10
+ "filename" : "App Icon.imagestack",
11
+ "idiom" : "tv",
12
+ "role" : "primary-app-icon",
13
+ "size" : "400x240"
14
+ },
15
+ {
16
+ "filename" : "Top Shelf Image Wide.imageset",
17
+ "idiom" : "tv",
18
+ "role" : "top-shelf-image-wide",
19
+ "size" : "2320x720"
20
+ },
21
+ {
22
+ "filename" : "Top Shelf Image.imageset",
23
+ "idiom" : "tv",
24
+ "role" : "top-shelf-image",
25
+ "size" : "1920x720"
26
+ }
27
+ ],
28
+ "info" : {
29
+ "author" : "xcode",
30
+ "version" : 1
31
+ }
32
+}
Runners/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/Contents.json
new
+17
@@ -0,0 +1,17 @@
1
+{
2
+ "images" : [
3
+ {
4
+ "filename" : "ControlKit-TopShelf-Wide.png",
5
+ "idiom" : "tv",
6
+ "scale" : "1x"
7
+ },
8
+ {
9
+ "idiom" : "tv",
10
+ "scale" : "2x"
11
+ }
12
+ ],
13
+ "info" : {
14
+ "author" : "xcode",
15
+ "version" : 1
16
+ }
17
+}
Runners/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/ControlKit-TopShelf-Wide.png
Binary files /dev/null and b/Runners/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/ControlKit-TopShelf-Wide.png differ
Runners/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/Contents.json
new
+17
@@ -0,0 +1,17 @@
1
+{
2
+ "images" : [
3
+ {
4
+ "filename" : "ControlKit-TopShelf.png",
5
+ "idiom" : "tv",
6
+ "scale" : "1x"
7
+ },
8
+ {
9
+ "idiom" : "tv",
10
+ "scale" : "2x"
11
+ }
12
+ ],
13
+ "info" : {
14
+ "author" : "xcode",
15
+ "version" : 1
16
+ }
17
+}
Runners/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/ControlKit-TopShelf.png
Binary files /dev/null and b/Runners/tvOS/Assets.xcassets/App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/ControlKit-TopShelf.png differ
Runners/tvOS/Assets.xcassets/Contents.json
new
+6
@@ -0,0 +1,6 @@
1
+{
2
+ "info" : {
3
+ "author" : "xcode",
4
+ "version" : 1
5
+ }
6
+}
Runners/tvOS/ControlKitRunnerApp.swift
new
+98
@@ -0,0 +1,98 @@
1
+import SwiftUI
2
+
3
+@main
4
+struct ControlKitRunnerApp: App {
5
+ var body: some Scene {
6
+ WindowGroup {
7
+ ControlKitShowcaseView()
8
+ }
9
+ }
10
+}
11
+
12
+private struct ControlKitShowcaseView: View {
13
+ var body: some View {
14
+ ZStack {
15
+ LinearGradient(
16
+ colors: [Color.controlKitGreen, Color(red: 0.04, green: 0.12, blue: 0.09)],
17
+ startPoint: .topLeading,
18
+ endPoint: .bottomTrailing
19
+ )
20
+ .ignoresSafeArea()
21
+
22
+ VStack(alignment: .leading, spacing: 30) {
23
+ HStack(spacing: 22) {
24
+ logo
25
+ VStack(alignment: .leading, spacing: 6) {
26
+ Text("ControlKit")
27
+ .font(.system(size: 48, weight: .bold, design: .rounded))
28
+ Text("Apple platform automation")
29
+ .font(.title2)
30
+ .foregroundStyle(Color.controlKitCream.opacity(0.78))
31
+ }
32
+ }
33
+
34
+ Text("A clean-room runner for reliable black-box testing across Apple platforms.")
35
+ .font(.title2.weight(.medium))
36
+ .frame(maxWidth: 800, alignment: .leading)
37
+
38
+ HStack(alignment: .top, spacing: 22) {
39
+ statusCard
40
+ informationCard
41
+ }
42
+ }
43
+ .foregroundStyle(Color.controlKitCream)
44
+ .padding(64)
45
+ .frame(maxWidth: 1200, alignment: .leading)
46
+ }
47
+ }
48
+
49
+ private var logo: some View {
50
+ Text("XC\nRS")
51
+ .font(.system(size: 38, weight: .black, design: .rounded))
52
+ .multilineTextAlignment(.center)
53
+ .lineSpacing(-10)
54
+ .foregroundStyle(Color.controlKitGreen)
55
+ .frame(width: 124, height: 124)
56
+ .background(Color.controlKitCream, in: RoundedRectangle(cornerRadius: 30))
57
+ }
58
+
59
+ private var statusCard: some View {
60
+ VStack(alignment: .leading, spacing: 14) {
61
+ Label("Runner online", systemImage: "checkmark.circle.fill")
62
+ .font(.headline)
63
+ .foregroundStyle(.green)
64
+ Text("The XCTest host is ready to receive ControlKit automation requests.")
65
+ .foregroundStyle(Color.controlKitCream.opacity(0.78))
66
+ }
67
+ .padding(26)
68
+ .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
69
+ .background(.white.opacity(0.1), in: RoundedRectangle(cornerRadius: 24))
70
+ }
71
+
72
+ private var informationCard: some View {
73
+ VStack(alignment: .leading, spacing: 16) {
74
+ infoRow(title: "Project", value: "XCRSControlKit")
75
+ infoRow(title: "Creator", value: "smbCloudXYZ")
76
+ infoRow(title: "Platform", value: "tvOS")
77
+ infoRow(title: "Purpose", value: "Native UI automation runner")
78
+ }
79
+ .padding(26)
80
+ .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
81
+ .background(.white.opacity(0.08), in: RoundedRectangle(cornerRadius: 24))
82
+ }
83
+
84
+ private func infoRow(title: String, value: String) -> some View {
85
+ HStack {
86
+ Text(title)
87
+ .foregroundStyle(Color.controlKitCream.opacity(0.62))
88
+ Spacer()
89
+ Text(value)
90
+ .fontWeight(.semibold)
91
+ }
92
+ }
93
+}
94
+
95
+private extension Color {
96
+ static let controlKitGreen = Color(red: 0.106, green: 0.263, blue: 0.196)
97
+ static let controlKitCream = Color(red: 0.961, green: 0.953, blue: 0.925)
98
+}
Runners/tvOS/ControlKitRunnerUITests.swift
new
+10
@@ -0,0 +1,10 @@
1
+import XCTest
2
+
3
+final class ControlKitRunnerUITests: XCTestCase {
4
+ @MainActor
5
+ func testRunAutomation() async throws {
6
+ continueAfterFailure = true
7
+ let server = ControlKitRPCServer()
8
+ try await server.start()
9
+ }
10
+}
Runners/tvOS/README.md
new
+9
@@ -0,0 +1,9 @@
1
+# XCRS ControlKit tvOS runner
2
+
3
+This is the native tvOS runner owned by XCRS ControlKit. It is an Xcode UI
4
+testing project, not a SwiftPM target, and imports the public
5
+`XCRSControlKit` package for capability and request contracts.
6
+
7
+The runner exposes a localhost JSON-RPC server to the Rust `xcrs` client using
8
+SwiftNIO's HTTP/1 server pipeline. tvOS remote buttons are handled through
9
+`XCUIRemote`, with no DeviceKit source or private XCTest headers.
Runners/tvOS/XCRSControlKitRunner.xcodeproj/project.pbxproj
new
+497
@@ -0,0 +1,497 @@
1
+// !$*UTF8*$!
2
+{
3
+ archiveVersion = 1;
4
+ classes = {
5
+ };
6
+ objectVersion = 60;
7
+ objects = {
8
+
9
+/* Begin PBXBuildFile section */
10
+ 035513224895A0CE4FB0D064 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 30183ACB20CEB829E0490ED5 /* Assets.xcassets */; };
11
+ 0D569CAD6B368B9B5C36E8BE /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2EC08D851E6330976D22EA82 /* Foundation.framework */; };
12
+ 3580744EA3A51BD112ABA6D9 /* ControlKitRunnerApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEC8BA1384D1001CFCB138C1 /* ControlKitRunnerApp.swift */; };
13
+ 42240429301B7F4D00D1B8E9 /* XCRSControlKit in Frameworks */ = {isa = PBXBuildFile; productRef = 8E9294EFB8D77093FD3218B5 /* XCRSControlKit */; };
14
+ 4224042A301B7F4D00D1B8E9 /* NIOCore in Frameworks */ = {isa = PBXBuildFile; productRef = B0B9B1FF3D723F8C6B727F7D /* NIOCore */; };
15
+ 4224042B301B7F4D00D1B8E9 /* NIOHTTP1 in Frameworks */ = {isa = PBXBuildFile; productRef = B7FD539749497B63541C3CA6 /* NIOHTTP1 */; };
16
+ 4224042C301B7F4D00D1B8E9 /* NIOPosix in Frameworks */ = {isa = PBXBuildFile; productRef = B81BFC255C5D3B81571A1A59 /* NIOPosix */; };
17
+ 5AD6201B303072328B2B1CF3 /* ControlKitRunnerUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 675B8D131573D551CBDD5C49 /* ControlKitRunnerUITests.swift */; };
18
+ 5F061CEA82FE3FDD1B4FB7CD /* ControlKitRPCServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 87228C0BF79EA823E25797A5 /* ControlKitRPCServer.swift */; };
19
+ E550F44417BCF134B5F636A8 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2EC08D851E6330976D22EA82 /* Foundation.framework */; };
20
+/* End PBXBuildFile section */
21
+
22
+/* Begin PBXContainerItemProxy section */
23
+ 4072D01C68F474F1230D8E2E /* PBXContainerItemProxy */ = {
24
+ isa = PBXContainerItemProxy;
25
+ containerPortal = 0B09FAC4377C92AD60C7E93C /* Project object */;
26
+ proxyType = 1;
27
+ remoteGlobalIDString = 297E6EBCF6A5F1F8FE403639;
28
+ remoteInfo = XCRSControlKitRunner;
29
+ };
30
+/* End PBXContainerItemProxy section */
31
+
32
+/* Begin PBXFileReference section */
33
+ 2EC08D851E6330976D22EA82 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS18.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; };
34
+ 30183ACB20CEB829E0490ED5 /* Assets.xcassets */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
35
+ 675B8D131573D551CBDD5C49 /* ControlKitRunnerUITests.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ControlKitRunnerUITests.swift; sourceTree = "<group>"; };
36
+ 87228C0BF79EA823E25797A5 /* ControlKitRPCServer.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ControlKitRPCServer.swift; path = ../iOS/ControlKitRPCServer.swift; sourceTree = "<group>"; };
37
+ A310FFE002FC01D2DF5242A0 /* XCRSControlKitRunnerUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = XCRSControlKitRunnerUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
38
+ C3B8D9F0D2E11E792710F2B9 /* XCRSControlKitRunner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; name = XCRSControlKitRunner.app; path = ControlKit.app; sourceTree = BUILT_PRODUCTS_DIR; };
39
+ CEC8BA1384D1001CFCB138C1 /* ControlKitRunnerApp.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; path = ControlKitRunnerApp.swift; sourceTree = "<group>"; };
40
+/* End PBXFileReference section */
41
+
42
+/* Begin PBXFrameworksBuildPhase section */
43
+ 124443B6555ADFF30C5C0A78 /* Frameworks */ = {
44
+ isa = PBXFrameworksBuildPhase;
45
+ buildActionMask = 2147483647;
46
+ files = (
47
+ 4224042C301B7F4D00D1B8E9 /* NIOPosix in Frameworks */,
48
+ 4224042B301B7F4D00D1B8E9 /* NIOHTTP1 in Frameworks */,
49
+ 4224042A301B7F4D00D1B8E9 /* NIOCore in Frameworks */,
50
+ 42240429301B7F4D00D1B8E9 /* XCRSControlKit in Frameworks */,
51
+ 0D569CAD6B368B9B5C36E8BE /* Foundation.framework in Frameworks */,
52
+ );
53
+ runOnlyForDeploymentPostprocessing = 0;
54
+ };
55
+ 4D5B6A5287328D2C1161CAD0 /* Frameworks */ = {
56
+ isa = PBXFrameworksBuildPhase;
57
+ buildActionMask = 2147483647;
58
+ files = (
59
+ E550F44417BCF134B5F636A8 /* Foundation.framework in Frameworks */,
60
+ );
61
+ runOnlyForDeploymentPostprocessing = 0;
62
+ };
63
+/* End PBXFrameworksBuildPhase section */
64
+
65
+/* Begin PBXGroup section */
66
+ 69B76CD159FC79D016CC57A1 /* XCRSControlKitRunnerUITests */ = {
67
+ isa = PBXGroup;
68
+ children = (
69
+ 675B8D131573D551CBDD5C49 /* ControlKitRunnerUITests.swift */,
70
+ 87228C0BF79EA823E25797A5 /* ControlKitRPCServer.swift */,
71
+ );
72
+ name = XCRSControlKitRunnerUITests;
73
+ sourceTree = "<group>";
74
+ };
75
+ 6ACA23F5AEF94EEAA4DD0680 /* Products */ = {
76
+ isa = PBXGroup;
77
+ children = (
78
+ C3B8D9F0D2E11E792710F2B9 /* XCRSControlKitRunner.app */,
79
+ A310FFE002FC01D2DF5242A0 /* XCRSControlKitRunnerUITests.xctest */,
80
+ );
81
+ name = Products;
82
+ sourceTree = "<group>";
83
+ };
84
+ 762B4572EF9B0155C2D74F08 /* Frameworks */ = {
85
+ isa = PBXGroup;
86
+ children = (
87
+ DD999D2C8FCBE1D3F9F52834 /* tvOS */,
88
+ );
89
+ name = Frameworks;
90
+ sourceTree = "<group>";
91
+ };
92
+ 79823D8EFB85505638E6F389 /* XCRSControlKitRunner */ = {
93
+ isa = PBXGroup;
94
+ children = (
95
+ CEC8BA1384D1001CFCB138C1 /* ControlKitRunnerApp.swift */,
96
+ 30183ACB20CEB829E0490ED5 /* Assets.xcassets */,
97
+ );
98
+ name = XCRSControlKitRunner;
99
+ sourceTree = "<group>";
100
+ };
101
+ CBAFCCCB076AD09654393A3B = {
102
+ isa = PBXGroup;
103
+ children = (
104
+ 762B4572EF9B0155C2D74F08 /* Frameworks */,
105
+ 79823D8EFB85505638E6F389 /* XCRSControlKitRunner */,
106
+ 69B76CD159FC79D016CC57A1 /* XCRSControlKitRunnerUITests */,
107
+ );
108
+ sourceTree = "<group>";
109
+ };
110
+ DD999D2C8FCBE1D3F9F52834 /* tvOS */ = {
111
+ isa = PBXGroup;
112
+ children = (
113
+ 2EC08D851E6330976D22EA82 /* Foundation.framework */,
114
+ );
115
+ name = tvOS;
116
+ sourceTree = "<group>";
117
+ };
118
+/* End PBXGroup section */
119
+
120
+/* Begin PBXNativeTarget section */
121
+ 297E6EBCF6A5F1F8FE403639 /* XCRSControlKitRunner */ = {
122
+ isa = PBXNativeTarget;
123
+ buildConfigurationList = 89DAAF59F302E76CB2702737 /* Build configuration list for PBXNativeTarget "XCRSControlKitRunner" */;
124
+ buildPhases = (
125
+ C5901C5F5EE82CE5F1CDFAAF /* Sources */,
126
+ 4D5B6A5287328D2C1161CAD0 /* Frameworks */,
127
+ 33EE014E63B28349A2846990 /* Resources */,
128
+ );
129
+ buildRules = (
130
+ );
131
+ dependencies = (
132
+ );
133
+ name = XCRSControlKitRunner;
134
+ productName = XCRSControlKitRunner;
135
+ productReference = C3B8D9F0D2E11E792710F2B9 /* XCRSControlKitRunner.app */;
136
+ productType = "com.apple.product-type.application";
137
+ };
138
+ 40F02CDFEAF28D1FA9935842 /* XCRSControlKitRunnerUITests */ = {
139
+ isa = PBXNativeTarget;
140
+ buildConfigurationList = B71370B2451E9AC83FC63C82 /* Build configuration list for PBXNativeTarget "XCRSControlKitRunnerUITests" */;
141
+ buildPhases = (
142
+ D43ABD22F352B6001DBD40E8 /* Sources */,
143
+ 124443B6555ADFF30C5C0A78 /* Frameworks */,
144
+ BB611355B972490541A99EA9 /* Resources */,
145
+ );
146
+ buildRules = (
147
+ );
148
+ dependencies = (
149
+ ED17078DE028C0301457A09B /* PBXTargetDependency */,
150
+ );
151
+ name = XCRSControlKitRunnerUITests;
152
+ packageProductDependencies = (
153
+ 8E9294EFB8D77093FD3218B5 /* XCRSControlKit */,
154
+ B0B9B1FF3D723F8C6B727F7D /* NIOCore */,
155
+ B7FD539749497B63541C3CA6 /* NIOHTTP1 */,
156
+ B81BFC255C5D3B81571A1A59 /* NIOPosix */,
157
+ );
158
+ productName = XCRSControlKitRunnerUITests;
159
+ productReference = A310FFE002FC01D2DF5242A0 /* XCRSControlKitRunnerUITests.xctest */;
160
+ productType = "com.apple.product-type.bundle.ui-testing";
161
+ };
162
+/* End PBXNativeTarget section */
163
+
164
+/* Begin PBXProject section */
165
+ 0B09FAC4377C92AD60C7E93C /* Project object */ = {
166
+ isa = PBXProject;
167
+ attributes = {
168
+ LastSwiftUpdateCheck = 2630;
169
+ LastUpgradeCheck = 2630;
170
+ };
171
+ buildConfigurationList = D057C3520D2F6DF298BDCE07 /* Build configuration list for PBXProject "XCRSControlKitRunner" */;
172
+ compatibilityVersion = "Xcode 3.2";
173
+ developmentRegion = en;
174
+ hasScannedForEncodings = 0;
175
+ knownRegions = (
176
+ en,
177
+ Base,
178
+ );
179
+ mainGroup = CBAFCCCB076AD09654393A3B;
180
+ packageReferences = (
181
+ 62D4A3BB7E58C0EEDFBD3798 /* XCLocalSwiftPackageReference "../.." */,
182
+ 33CCE93A21C53FF1112828ED /* XCRemoteSwiftPackageReference "swift-nio" */,
183
+ );
184
+ productRefGroup = 6ACA23F5AEF94EEAA4DD0680 /* Products */;
185
+ projectDirPath = "";
186
+ projectRoot = "";
187
+ targets = (
188
+ 297E6EBCF6A5F1F8FE403639 /* XCRSControlKitRunner */,
189
+ 40F02CDFEAF28D1FA9935842 /* XCRSControlKitRunnerUITests */,
190
+ );
191
+ };
192
+/* End PBXProject section */
193
+
194
+/* Begin PBXResourcesBuildPhase section */
195
+ 33EE014E63B28349A2846990 /* Resources */ = {
196
+ isa = PBXResourcesBuildPhase;
197
+ buildActionMask = 2147483647;
198
+ files = (
199
+ 035513224895A0CE4FB0D064 /* Assets.xcassets in Resources */,
200
+ );
201
+ runOnlyForDeploymentPostprocessing = 0;
202
+ };
203
+ BB611355B972490541A99EA9 /* Resources */ = {
204
+ isa = PBXResourcesBuildPhase;
205
+ buildActionMask = 2147483647;
206
+ files = (
207
+ );
208
+ runOnlyForDeploymentPostprocessing = 0;
209
+ };
210
+/* End PBXResourcesBuildPhase section */
211
+
212
+/* Begin PBXSourcesBuildPhase section */
213
+ C5901C5F5EE82CE5F1CDFAAF /* Sources */ = {
214
+ isa = PBXSourcesBuildPhase;
215
+ buildActionMask = 2147483647;
216
+ files = (
217
+ 3580744EA3A51BD112ABA6D9 /* ControlKitRunnerApp.swift in Sources */,
218
+ );
219
+ runOnlyForDeploymentPostprocessing = 0;
220
+ };
221
+ D43ABD22F352B6001DBD40E8 /* Sources */ = {
222
+ isa = PBXSourcesBuildPhase;
223
+ buildActionMask = 2147483647;
224
+ files = (
225
+ 5AD6201B303072328B2B1CF3 /* ControlKitRunnerUITests.swift in Sources */,
226
+ 5F061CEA82FE3FDD1B4FB7CD /* ControlKitRPCServer.swift in Sources */,
227
+ );
228
+ runOnlyForDeploymentPostprocessing = 0;
229
+ };
230
+/* End PBXSourcesBuildPhase section */
231
+
232
+/* Begin PBXTargetDependency section */
233
+ ED17078DE028C0301457A09B /* PBXTargetDependency */ = {
234
+ isa = PBXTargetDependency;
235
+ name = XCRSControlKitRunner;
236
+ target = 297E6EBCF6A5F1F8FE403639 /* XCRSControlKitRunner */;
237
+ targetProxy = 4072D01C68F474F1230D8E2E /* PBXContainerItemProxy */;
238
+ };
239
+/* End PBXTargetDependency section */
240
+
241
+/* Begin XCBuildConfiguration section */
242
+ 1E279C47A0AE151A4005B2B0 /* Debug */ = {
243
+ isa = XCBuildConfiguration;
244
+ buildSettings = {
245
+ GENERATE_INFOPLIST_FILE = YES;
246
+ PRODUCT_BUNDLE_IDENTIFIER = "com.smbcloud.xcrs-controlkit.tvos.runnerUITests";
247
+ PRODUCT_NAME = XCRSControlKitRunnerUITests;
248
+ SDKROOT = appletvos;
249
+ SWIFT_VERSION = 5.9;
250
+ TVOS_DEPLOYMENT_TARGET = 16.0;
251
+ };
252
+ name = Debug;
253
+ };
254
+ 20B98D730FB57BAC0F7745B2 /* Release */ = {
255
+ isa = XCBuildConfiguration;
256
+ buildSettings = {
257
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
258
+ ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image";
259
+ GENERATE_INFOPLIST_FILE = YES;
260
+ INFOPLIST_KEY_CFBundleDisplayName = ControlKit;
261
+ LD_RUNPATH_SEARCH_PATHS = (
262
+ "$(inherited)",
263
+ "@executable_path/Frameworks",
264
+ );
265
+ PRODUCT_BUNDLE_IDENTIFIER = "com.smbcloud.xcrs-controlkit.tvos.runner";
266
+ PRODUCT_NAME = ControlKit;
267
+ SDKROOT = appletvos;
268
+ SWIFT_VERSION = 5.9;
269
+ TARGETED_DEVICE_FAMILY = 3;
270
+ TVOS_DEPLOYMENT_TARGET = 16.0;
271
+ VALIDATE_PRODUCT = YES;
272
+ };
273
+ name = Release;
274
+ };
275
+ 2D3CFBA5256921E446A203DF /* Release */ = {
276
+ isa = XCBuildConfiguration;
277
+ buildSettings = {
278
+ ALWAYS_SEARCH_USER_PATHS = NO;
279
+ CLANG_ANALYZER_NONNULL = YES;
280
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
281
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
282
+ CLANG_CXX_LIBRARY = "libc++";
283
+ CLANG_ENABLE_MODULES = YES;
284
+ CLANG_ENABLE_OBJC_ARC = YES;
285
+ CLANG_ENABLE_OBJC_WEAK = YES;
286
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
287
+ CLANG_WARN_BOOL_CONVERSION = YES;
288
+ CLANG_WARN_COMMA = YES;
289
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
290
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
291
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
292
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
293
+ CLANG_WARN_EMPTY_BODY = YES;
294
+ CLANG_WARN_ENUM_CONVERSION = YES;
295
+ CLANG_WARN_INFINITE_RECURSION = YES;
296
+ CLANG_WARN_INT_CONVERSION = YES;
297
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
298
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
299
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
300
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
301
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
302
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
303
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
304
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
305
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
306
+ CLANG_WARN_UNREACHABLE_CODE = YES;
307
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
308
+ COPY_PHASE_STRIP = NO;
309
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
310
+ ENABLE_NS_ASSERTIONS = NO;
311
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
312
+ GCC_C_LANGUAGE_STANDARD = gnu11;
313
+ GCC_NO_COMMON_BLOCKS = YES;
314
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
315
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
316
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
317
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
318
+ GCC_WARN_UNUSED_FUNCTION = YES;
319
+ GCC_WARN_UNUSED_VARIABLE = YES;
320
+ MTL_ENABLE_DEBUG_INFO = NO;
321
+ MTL_FAST_MATH = YES;
322
+ PRODUCT_NAME = "$(TARGET_NAME)";
323
+ SWIFT_COMPILATION_MODE = wholemodule;
324
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
325
+ SWIFT_VERSION = 5.0;
326
+ };
327
+ name = Release;
328
+ };
329
+ B86BCF1CBC84DDBB0482F50E /* Release */ = {
330
+ isa = XCBuildConfiguration;
331
+ buildSettings = {
332
+ GENERATE_INFOPLIST_FILE = YES;
333
+ PRODUCT_BUNDLE_IDENTIFIER = "com.smbcloud.xcrs-controlkit.tvos.runnerUITests";
334
+ PRODUCT_NAME = XCRSControlKitRunnerUITests;
335
+ SDKROOT = appletvos;
336
+ SWIFT_VERSION = 5.9;
337
+ TVOS_DEPLOYMENT_TARGET = 16.0;
338
+ VALIDATE_PRODUCT = YES;
339
+ };
340
+ name = Release;
341
+ };
342
+ EDCF57E98B03BA7A29CF004B /* Debug */ = {
343
+ isa = XCBuildConfiguration;
344
+ buildSettings = {
345
+ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
346
+ ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image";
347
+ GENERATE_INFOPLIST_FILE = YES;
348
+ INFOPLIST_KEY_CFBundleDisplayName = ControlKit;
349
+ LD_RUNPATH_SEARCH_PATHS = (
350
+ "$(inherited)",
351
+ "@executable_path/Frameworks",
352
+ );
353
+ PRODUCT_BUNDLE_IDENTIFIER = "com.smbcloud.xcrs-controlkit.tvos.runner";
354
+ PRODUCT_NAME = ControlKit;
355
+ SDKROOT = appletvos;
356
+ SWIFT_VERSION = 5.9;
357
+ TARGETED_DEVICE_FAMILY = 3;
358
+ TVOS_DEPLOYMENT_TARGET = 16.0;
359
+ };
360
+ name = Debug;
361
+ };
362
+ EE217233EFC84E56C7BF8952 /* Debug */ = {
363
+ isa = XCBuildConfiguration;
364
+ buildSettings = {
365
+ ALWAYS_SEARCH_USER_PATHS = NO;
366
+ CLANG_ANALYZER_NONNULL = YES;
367
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
368
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
369
+ CLANG_CXX_LIBRARY = "libc++";
370
+ CLANG_ENABLE_MODULES = YES;
371
+ CLANG_ENABLE_OBJC_ARC = YES;
372
+ CLANG_ENABLE_OBJC_WEAK = YES;
373
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
374
+ CLANG_WARN_BOOL_CONVERSION = YES;
375
+ CLANG_WARN_COMMA = YES;
376
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
377
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
378
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
379
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
380
+ CLANG_WARN_EMPTY_BODY = YES;
381
+ CLANG_WARN_ENUM_CONVERSION = YES;
382
+ CLANG_WARN_INFINITE_RECURSION = YES;
383
+ CLANG_WARN_INT_CONVERSION = YES;
384
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
385
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
386
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
387
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
388
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
389
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
390
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
391
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
392
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
393
+ CLANG_WARN_UNREACHABLE_CODE = YES;
394
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
395
+ COPY_PHASE_STRIP = NO;
396
+ DEBUG_INFORMATION_FORMAT = dwarf;
397
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
398
+ ENABLE_TESTABILITY = YES;
399
+ GCC_C_LANGUAGE_STANDARD = gnu11;
400
+ GCC_DYNAMIC_NO_PIC = NO;
401
+ GCC_NO_COMMON_BLOCKS = YES;
402
+ GCC_OPTIMIZATION_LEVEL = 0;
403
+ GCC_PREPROCESSOR_DEFINITIONS = (
404
+ "DEBUG=1",
405
+ "$(inherited)",
406
+ );
407
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
408
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
409
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
410
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
411
+ GCC_WARN_UNUSED_FUNCTION = YES;
412
+ GCC_WARN_UNUSED_VARIABLE = YES;
413
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
414
+ MTL_FAST_MATH = YES;
415
+ ONLY_ACTIVE_ARCH = YES;
416
+ PRODUCT_NAME = "$(TARGET_NAME)";
417
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
418
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
419
+ SWIFT_VERSION = 5.0;
420
+ };
421
+ name = Debug;
422
+ };
423
+/* End XCBuildConfiguration section */
424
+
425
+/* Begin XCConfigurationList section */
426
+ 89DAAF59F302E76CB2702737 /* Build configuration list for PBXNativeTarget "XCRSControlKitRunner" */ = {
427
+ isa = XCConfigurationList;
428
+ buildConfigurations = (
429
+ 20B98D730FB57BAC0F7745B2 /* Release */,
430
+ EDCF57E98B03BA7A29CF004B /* Debug */,
431
+ );
432
+ defaultConfigurationIsVisible = 0;
433
+ defaultConfigurationName = Release;
434
+ };
435
+ B71370B2451E9AC83FC63C82 /* Build configuration list for PBXNativeTarget "XCRSControlKitRunnerUITests" */ = {
436
+ isa = XCConfigurationList;
437
+ buildConfigurations = (
438
+ B86BCF1CBC84DDBB0482F50E /* Release */,
439
+ 1E279C47A0AE151A4005B2B0 /* Debug */,
440
+ );
441
+ defaultConfigurationIsVisible = 0;
442
+ defaultConfigurationName = Release;
443
+ };
444
+ D057C3520D2F6DF298BDCE07 /* Build configuration list for PBXProject "XCRSControlKitRunner" */ = {
445
+ isa = XCConfigurationList;
446
+ buildConfigurations = (
447
+ EE217233EFC84E56C7BF8952 /* Debug */,
448
+ 2D3CFBA5256921E446A203DF /* Release */,
449
+ );
450
+ defaultConfigurationIsVisible = 0;
451
+ defaultConfigurationName = Release;
452
+ };
453
+/* End XCConfigurationList section */
454
+
455
+/* Begin XCLocalSwiftPackageReference section */
456
+ 62D4A3BB7E58C0EEDFBD3798 /* XCLocalSwiftPackageReference "../.." */ = {
457
+ isa = XCLocalSwiftPackageReference;
458
+ relativePath = ../..;
459
+ };
460
+/* End XCLocalSwiftPackageReference section */
461
+
462
+/* Begin XCRemoteSwiftPackageReference section */
463
+ 33CCE93A21C53FF1112828ED /* XCRemoteSwiftPackageReference "swift-nio" */ = {
464
+ isa = XCRemoteSwiftPackageReference;
465
+ repositoryURL = "https://github.com/apple/swift-nio.git";
466
+ requirement = {
467
+ kind = upToNextMajorVersion;
468
+ minimumVersion = 2.0.0;
469
+ };
470
+ };
471
+/* End XCRemoteSwiftPackageReference section */
472
+
473
+/* Begin XCSwiftPackageProductDependency section */
474
+ 8E9294EFB8D77093FD3218B5 /* XCRSControlKit */ = {
475
+ isa = XCSwiftPackageProductDependency;
476
+ package = 33CCE93A21C53FF1112828ED /* XCRemoteSwiftPackageReference "swift-nio" */;
477
+ productName = XCRSControlKit;
478
+ };
479
+ B0B9B1FF3D723F8C6B727F7D /* NIOCore */ = {
480
+ isa = XCSwiftPackageProductDependency;
481
+ package = 33CCE93A21C53FF1112828ED /* XCRemoteSwiftPackageReference "swift-nio" */;
482
+ productName = NIOCore;
483
+ };
484
+ B7FD539749497B63541C3CA6 /* NIOHTTP1 */ = {
485
+ isa = XCSwiftPackageProductDependency;
486
+ package = 33CCE93A21C53FF1112828ED /* XCRemoteSwiftPackageReference "swift-nio" */;
487
+ productName = NIOHTTP1;
488
+ };
489
+ B81BFC255C5D3B81571A1A59 /* NIOPosix */ = {
490
+ isa = XCSwiftPackageProductDependency;
491
+ package = 33CCE93A21C53FF1112828ED /* XCRemoteSwiftPackageReference "swift-nio" */;
492
+ productName = NIOPosix;
493
+ };
494
+/* End XCSwiftPackageProductDependency section */
495
+ };
496
+ rootObject = 0B09FAC4377C92AD60C7E93C /* Project object */;
497
+}
Runners/tvOS/XCRSControlKitRunner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new
+8
@@ -0,0 +1,8 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+<plist version="1.0">
4
+<dict>
5
+ <key>IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded</key>
6
+ <true/>
7
+</dict>
8
+</plist>
Runners/tvOS/XCRSControlKitRunner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
new
+42
@@ -0,0 +1,42 @@
1
+{
2
+ "originHash" : "dc64d5a9a584c1afdd2823a6335adce6e83ebd68e5e5f250156866e5bdc7e973",
3
+ "pins" : [
4
+ {
5
+ "identity" : "swift-atomics",
6
+ "kind" : "remoteSourceControl",
7
+ "location" : "https://github.com/apple/swift-atomics.git",
8
+ "state" : {
9
+ "revision" : "0442cb5a3f98ab802acb777929fdb446bda11a34",
10
+ "version" : "1.3.1"
11
+ }
12
+ },
13
+ {
14
+ "identity" : "swift-collections",
15
+ "kind" : "remoteSourceControl",
16
+ "location" : "https://github.com/apple/swift-collections.git",
17
+ "state" : {
18
+ "revision" : "a0cb0954ecb21e4e31b0070e6ed5674e8556685a",
19
+ "version" : "1.6.0"
20
+ }
21
+ },
22
+ {
23
+ "identity" : "swift-nio",
24
+ "kind" : "remoteSourceControl",
25
+ "location" : "https://github.com/apple/swift-nio.git",
26
+ "state" : {
27
+ "revision" : "0b18836bd8b0162e7e17a995a3fbee20ed8f3b2b",
28
+ "version" : "2.101.3"
29
+ }
30
+ },
31
+ {
32
+ "identity" : "swift-system",
33
+ "kind" : "remoteSourceControl",
34
+ "location" : "https://github.com/apple/swift-system.git",
35
+ "state" : {
36
+ "revision" : "50688cacbd41d547e9eb9f7a213542340b7c442b",
37
+ "version" : "1.7.5"
38
+ }
39
+ }
40
+ ],
41
+ "version" : 3
42
+}
Runners/tvOS/XCRSControlKitRunner.xcodeproj/xcshareddata/xcschemes/ControlKit.xcscheme
new
+91
@@ -0,0 +1,91 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<Scheme
3
+ LastUpgradeVersion = "2660"
4
+ version = "1.7">
5
+ <BuildAction
6
+ parallelizeBuildables = "YES"
7
+ buildImplicitDependencies = "YES"
8
+ buildArchitectures = "Automatic">
9
+ <BuildActionEntries>
10
+ <BuildActionEntry
11
+ buildForTesting = "YES"
12
+ buildForRunning = "YES"
13
+ buildForProfiling = "YES"
14
+ buildForArchiving = "YES"
15
+ buildForAnalyzing = "YES">
16
+ <BuildableReference
17
+ BuildableIdentifier = "primary"
18
+ BlueprintIdentifier = "297E6EBCF6A5F1F8FE403639"
19
+ BuildableName = "ControlKit.app"
20
+ BlueprintName = "XCRSControlKitRunner"
21
+ ReferencedContainer = "container:XCRSControlKitRunner.xcodeproj">
22
+ </BuildableReference>
23
+ </BuildActionEntry>
24
+ </BuildActionEntries>
25
+ </BuildAction>
26
+ <TestAction
27
+ buildConfiguration = "Debug"
28
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
29
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
30
+ shouldUseLaunchSchemeArgsEnv = "YES"
31
+ shouldAutocreateTestPlan = "YES">
32
+ <Testables>
33
+ <TestableReference
34
+ skipped = "NO">
35
+ <BuildableReference
36
+ BuildableIdentifier = "primary"
37
+ BlueprintIdentifier = "40F02CDFEAF28D1FA9935842"
38
+ BuildableName = "XCRSControlKitRunnerUITests.xctest"
39
+ BlueprintName = "XCRSControlKitRunnerUITests"
40
+ ReferencedContainer = "container:XCRSControlKitRunner.xcodeproj">
41
+ </BuildableReference>
42
+ </TestableReference>
43
+ </Testables>
44
+ </TestAction>
45
+ <LaunchAction
46
+ buildConfiguration = "Debug"
47
+ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
48
+ selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
49
+ launchStyle = "0"
50
+ useCustomWorkingDirectory = "NO"
51
+ ignoresPersistentStateOnLaunch = "NO"
52
+ debugDocumentVersioning = "YES"
53
+ debugServiceExtension = "internal"
54
+ allowLocationSimulation = "YES"
55
+ queueDebuggingEnableBacktraceRecording = "Yes">
56
+ <BuildableProductRunnable
57
+ runnableDebuggingMode = "0">
58
+ <BuildableReference
59
+ BuildableIdentifier = "primary"
60
+ BlueprintIdentifier = "297E6EBCF6A5F1F8FE403639"
61
+ BuildableName = "ControlKit.app"
62
+ BlueprintName = "XCRSControlKitRunner"
63
+ ReferencedContainer = "container:XCRSControlKitRunner.xcodeproj">
64
+ </BuildableReference>
65
+ </BuildableProductRunnable>
66
+ </LaunchAction>
67
+ <ProfileAction
68
+ buildConfiguration = "Release"
69
+ shouldUseLaunchSchemeArgsEnv = "YES"
70
+ savedToolIdentifier = ""
71
+ useCustomWorkingDirectory = "NO"
72
+ debugDocumentVersioning = "YES">
73
+ <BuildableProductRunnable
74
+ runnableDebuggingMode = "0">
75
+ <BuildableReference
76
+ BuildableIdentifier = "primary"
77
+ BlueprintIdentifier = "297E6EBCF6A5F1F8FE403639"
78
+ BuildableName = "ControlKit.app"
79
+ BlueprintName = "XCRSControlKitRunner"
80
+ ReferencedContainer = "container:XCRSControlKitRunner.xcodeproj">
81
+ </BuildableReference>
82
+ </BuildableProductRunnable>
83
+ </ProfileAction>
84
+ <AnalyzeAction
85
+ buildConfiguration = "Debug">
86
+ </AnalyzeAction>
87
+ <ArchiveAction
88
+ buildConfiguration = "Release"
89
+ revealArchiveInOrganizer = "YES">
90
+ </ArchiveAction>
91
+</Scheme>
Sources/XCRSControlKit/XCRSControlKit.docc/XCRSControlKit.md
new
+12
@@ -0,0 +1,12 @@
1
+# ``XCRSControlKit``
2
+
3
+Shared contracts for black-box automation of Apple platforms.
4
+
5
+## Overview
6
+
7
+XCRSControlKit keeps platform runner contracts independent from the Xcode
8
+projects that execute XCTest and XCUITest automation. Consumers can discover a
9
+runner's platform and capabilities, identify a target, and send transport-neutral
10
+requests without depending on a particular runner implementation.
11
+
12
+The package does not import XCTest or private Apple frameworks.
Sources/XCRSControlKit/XCRSControlKit.swift
new
+137
@@ -0,0 +1,137 @@
1
+import Foundation
2
+
3
+/// The Apple platform represented by a ControlKit runner.
4
+public enum XCRSControlKitPlatform: String, Codable, CaseIterable, Sendable {
5
+ case iOS
6
+ case iPadOS
7
+ case tvOS
8
+ case watchOS
9
+ case visionOS
10
+ case macOS
11
+}
12
+
13
+/// An operation supported by a ControlKit runner.
14
+public enum XCRSControlKitCapability: String, Codable, CaseIterable, Sendable {
15
+ case accessibility
16
+ case screenshot
17
+ case appLifecycle
18
+ case touch
19
+ case textInput
20
+ case swipe
21
+ case orientation
22
+ case remoteButtons
23
+ case keyboard
24
+ case pointer
25
+ case spatialInteraction
26
+ case pairedDevice
27
+}
28
+
29
+/// A simulator or device controlled by a runner.
30
+public struct XCRSControlKitTarget: Codable, Equatable, Sendable {
31
+ public let platform: XCRSControlKitPlatform
32
+ public let identifier: String
33
+ public let name: String?
34
+
35
+ public init(
36
+ platform: XCRSControlKitPlatform,
37
+ identifier: String,
38
+ name: String? = nil
39
+ ) {
40
+ self.platform = platform
41
+ self.identifier = identifier
42
+ self.name = name
43
+ }
44
+}
45
+
46
+/// The capabilities advertised by a runner.
47
+public struct XCRSControlKitCapabilities: Codable, Equatable, Sendable {
48
+ public let platform: XCRSControlKitPlatform
49
+ public let values: Set<XCRSControlKitCapability>
50
+
51
+ public init(
52
+ platform: XCRSControlKitPlatform,
53
+ values: Set<XCRSControlKitCapability>
54
+ ) {
55
+ self.platform = platform
56
+ self.values = values
57
+ }
58
+
59
+ public func supports(_ capability: XCRSControlKitCapability) -> Bool {
60
+ values.contains(capability)
61
+ }
62
+
63
+ public static func standard(
64
+ for platform: XCRSControlKitPlatform
65
+ ) -> Self {
66
+ let values: Set<XCRSControlKitCapability>
67
+
68
+ switch platform {
69
+ case .iOS, .iPadOS:
70
+ values = [
71
+ .accessibility,
72
+ .screenshot,
73
+ .appLifecycle,
74
+ .touch,
75
+ .textInput,
76
+ .swipe,
77
+ .orientation
78
+ ]
79
+ case .tvOS:
80
+ values = [
81
+ .accessibility,
82
+ .screenshot,
83
+ .appLifecycle,
84
+ .remoteButtons
85
+ ]
86
+ case .watchOS:
87
+ values = [
88
+ .accessibility,
89
+ .screenshot,
90
+ .appLifecycle,
91
+ .pairedDevice
92
+ ]
93
+ case .visionOS:
94
+ values = [
95
+ .accessibility,
96
+ .screenshot,
97
+ .appLifecycle,
98
+ .spatialInteraction
99
+ ]
100
+ case .macOS:
101
+ values = [
102
+ .accessibility,
103
+ .screenshot,
104
+ .appLifecycle,
105
+ .keyboard,
106
+ .pointer
107
+ ]
108
+ }
109
+
110
+ return Self(platform: platform, values: values)
111
+ }
112
+}
113
+
114
+/// A transport-neutral command sent to a runner.
115
+public struct XCRSControlKitRequest: Codable, Equatable, Sendable {
116
+ public let id: String
117
+ public let method: String
118
+ public let parameters: [String: String]
119
+
120
+ public init(
121
+ id: String = UUID().uuidString,
122
+ method: String,
123
+ parameters: [String: String] = [:]
124
+ ) {
125
+ self.id = id
126
+ self.method = method
127
+ self.parameters = parameters
128
+ }
129
+}
130
+
131
+/// Errors shared by package consumers and platform runners.
132
+public enum XCRSControlKitError: Error, Equatable, Sendable {
133
+ case unsupportedPlatform(XCRSControlKitPlatform)
134
+ case unsupportedCapability(XCRSControlKitCapability)
135
+ case invalidRequest(String)
136
+ case transport(String)
137
+}
Tests/XCRSControlKitTests/XCRSControlKitTests.swift
new
+44
@@ -0,0 +1,44 @@
1
+import XCTest
2
+@testable import XCRSControlKit
3
+
4
+final class XCRSControlKitTests: XCTestCase {
5
+ func testStandardCapabilitiesArePlatformSpecific() {
6
+ let iOSCapabilities = XCRSControlKitCapabilities.standard(for: .iOS)
7
+ let tvOSCapabilities = XCRSControlKitCapabilities.standard(for: .tvOS)
8
+
9
+ XCTAssertTrue(iOSCapabilities.supports(.touch))
10
+ XCTAssertFalse(iOSCapabilities.supports(.remoteButtons))
11
+ XCTAssertTrue(tvOSCapabilities.supports(.remoteButtons))
12
+ XCTAssertFalse(tvOSCapabilities.supports(.touch))
13
+ }
14
+
15
+ func testCapabilitiesAreCodable() throws {
16
+ let capabilities = XCRSControlKitCapabilities.standard(for: .macOS)
17
+ let encoded = try JSONEncoder().encode(capabilities)
18
+ let decoded = try JSONDecoder().decode(
19
+ XCRSControlKitCapabilities.self,
20
+ from: encoded
21
+ )
22
+
23
+ XCTAssertEqual(decoded, capabilities)
24
+ }
25
+
26
+ func testRequestHasStableTransportShape() throws {
27
+ let request = XCRSControlKitRequest(
28
+ id: "request-1",
29
+ method: "device.io.button",
30
+ parameters: ["button": "select"]
31
+ )
32
+
33
+ let object = try JSONSerialization.jsonObject(
34
+ with: JSONEncoder().encode(request)
35
+ ) as? [String: Any]
36
+
37
+ XCTAssertEqual(object?["id"] as? String, "request-1")
38
+ XCTAssertEqual(object?["method"] as? String, "device.io.button")
39
+ XCTAssertEqual(
40
+ (object?["parameters"] as? [String: String])?["button"],
41
+ "select"
42
+ )
43
+ }
44
+}