├── .swiftpm
└── xcode
│ ├── package.xcworkspace
│ └── contents.xcworkspacedata
│ └── xcuserdata
│ └── davidcrooks.xcuserdatad
│ └── xcschemes
│ └── xcschememanagement.plist
├── Example
├── CodableWebSocketExample.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── swiftpm
│ │ │ └── Package.resolved
│ └── xcuserdata
│ │ └── davidcrooks.xcuserdatad
│ │ └── xcschemes
│ │ └── xcschememanagement.plist
├── CodableWebSocketExample
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ └── Contents.json
│ │ └── Contents.json
│ ├── Base.lproj
│ │ └── LaunchScreen.storyboard
│ ├── ContentView.swift
│ ├── Info.plist
│ ├── Preview Content
│ │ └── Preview Assets.xcassets
│ │ │ └── Contents.json
│ ├── Publisher+FilterOutErrors.swift
│ ├── SceneDelegate.swift
│ └── Thing.swift
└── CodableWebSocketExampleTests
│ ├── Info.plist
│ └── WebSocketDemoTests.swift
├── Package.swift
├── README.md
├── Sources
└── CodableWebSocket
│ ├── CodableWebSocket.swift
│ └── CodableWebsocketSubscription.swift
└── Tests
├── CodableWebSocketTests
├── CodableWebSocketTests.swift
└── XCTestManifests.swift
└── LinuxMain.swift
/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.swiftpm/xcode/xcuserdata/davidcrooks.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | CodableWebSocket.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | CodableWebSocket
16 |
17 | primary
18 |
19 |
20 | CodableWebSocketTests
21 |
22 | primary
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/Example/CodableWebSocketExample.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 52;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 5410D2B8250661A90097DE94 /* Thing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5410D2B7250661A90097DE94 /* Thing.swift */; };
11 | 5410D2BA250670A70097DE94 /* Publisher+FilterOutErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5410D2B9250670A70097DE94 /* Publisher+FilterOutErrors.swift */; };
12 | 54115A132514E14B002DF01D /* CodableWebSocket in Frameworks */ = {isa = PBXBuildFile; productRef = 54115A122514E14B002DF01D /* CodableWebSocket */; };
13 | 54CBB0E625061F7400FD9ED7 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54CBB0E525061F7400FD9ED7 /* AppDelegate.swift */; };
14 | 54CBB0E825061F7400FD9ED7 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54CBB0E725061F7400FD9ED7 /* SceneDelegate.swift */; };
15 | 54CBB0EA25061F7400FD9ED7 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54CBB0E925061F7400FD9ED7 /* ContentView.swift */; };
16 | 54CBB0EC25061F7600FD9ED7 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 54CBB0EB25061F7600FD9ED7 /* Assets.xcassets */; };
17 | 54CBB0EF25061F7600FD9ED7 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 54CBB0EE25061F7600FD9ED7 /* Preview Assets.xcassets */; };
18 | 54CBB0F225061F7600FD9ED7 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 54CBB0F025061F7600FD9ED7 /* LaunchScreen.storyboard */; };
19 | 54CBB0FD25061F7600FD9ED7 /* WebSocketDemoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54CBB0FC25061F7600FD9ED7 /* WebSocketDemoTests.swift */; };
20 | /* End PBXBuildFile section */
21 |
22 | /* Begin PBXContainerItemProxy section */
23 | 54CBB0F925061F7600FD9ED7 /* PBXContainerItemProxy */ = {
24 | isa = PBXContainerItemProxy;
25 | containerPortal = 54CBB0DA25061F7400FD9ED7 /* Project object */;
26 | proxyType = 1;
27 | remoteGlobalIDString = 54CBB0E125061F7400FD9ED7;
28 | remoteInfo = WebSocketDemo;
29 | };
30 | /* End PBXContainerItemProxy section */
31 |
32 | /* Begin PBXFileReference section */
33 | 5410D2B7250661A90097DE94 /* Thing.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Thing.swift; sourceTree = ""; };
34 | 5410D2B9250670A70097DE94 /* Publisher+FilterOutErrors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Publisher+FilterOutErrors.swift"; sourceTree = ""; };
35 | 54CBB0E225061F7400FD9ED7 /* CodableWebSocketExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CodableWebSocketExample.app; sourceTree = BUILT_PRODUCTS_DIR; };
36 | 54CBB0E525061F7400FD9ED7 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
37 | 54CBB0E725061F7400FD9ED7 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; };
38 | 54CBB0E925061F7400FD9ED7 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; };
39 | 54CBB0EB25061F7600FD9ED7 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
40 | 54CBB0EE25061F7600FD9ED7 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
41 | 54CBB0F125061F7600FD9ED7 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
42 | 54CBB0F325061F7600FD9ED7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
43 | 54CBB0F825061F7600FD9ED7 /* CodableWebSocketExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CodableWebSocketExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
44 | 54CBB0FC25061F7600FD9ED7 /* WebSocketDemoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebSocketDemoTests.swift; sourceTree = ""; };
45 | 54CBB0FE25061F7600FD9ED7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
46 | /* End PBXFileReference section */
47 |
48 | /* Begin PBXFrameworksBuildPhase section */
49 | 54CBB0DF25061F7400FD9ED7 /* Frameworks */ = {
50 | isa = PBXFrameworksBuildPhase;
51 | buildActionMask = 2147483647;
52 | files = (
53 | 54115A132514E14B002DF01D /* CodableWebSocket in Frameworks */,
54 | );
55 | runOnlyForDeploymentPostprocessing = 0;
56 | };
57 | 54CBB0F525061F7600FD9ED7 /* Frameworks */ = {
58 | isa = PBXFrameworksBuildPhase;
59 | buildActionMask = 2147483647;
60 | files = (
61 | );
62 | runOnlyForDeploymentPostprocessing = 0;
63 | };
64 | /* End PBXFrameworksBuildPhase section */
65 |
66 | /* Begin PBXGroup section */
67 | 54CBB0D925061F7400FD9ED7 = {
68 | isa = PBXGroup;
69 | children = (
70 | 54CBB0E425061F7400FD9ED7 /* CodableWebSocketExample */,
71 | 54CBB0FB25061F7600FD9ED7 /* CodableWebSocketExampleTests */,
72 | 54CBB0E325061F7400FD9ED7 /* Products */,
73 | );
74 | sourceTree = "";
75 | };
76 | 54CBB0E325061F7400FD9ED7 /* Products */ = {
77 | isa = PBXGroup;
78 | children = (
79 | 54CBB0E225061F7400FD9ED7 /* CodableWebSocketExample.app */,
80 | 54CBB0F825061F7600FD9ED7 /* CodableWebSocketExampleTests.xctest */,
81 | );
82 | name = Products;
83 | sourceTree = "";
84 | };
85 | 54CBB0E425061F7400FD9ED7 /* CodableWebSocketExample */ = {
86 | isa = PBXGroup;
87 | children = (
88 | 54CBB0E525061F7400FD9ED7 /* AppDelegate.swift */,
89 | 54CBB0E725061F7400FD9ED7 /* SceneDelegate.swift */,
90 | 5410D2B7250661A90097DE94 /* Thing.swift */,
91 | 5410D2B9250670A70097DE94 /* Publisher+FilterOutErrors.swift */,
92 | 54CBB0E925061F7400FD9ED7 /* ContentView.swift */,
93 | 54CBB0EB25061F7600FD9ED7 /* Assets.xcassets */,
94 | 54CBB0F025061F7600FD9ED7 /* LaunchScreen.storyboard */,
95 | 54CBB0F325061F7600FD9ED7 /* Info.plist */,
96 | 54CBB0ED25061F7600FD9ED7 /* Preview Content */,
97 | );
98 | path = CodableWebSocketExample;
99 | sourceTree = "";
100 | };
101 | 54CBB0ED25061F7600FD9ED7 /* Preview Content */ = {
102 | isa = PBXGroup;
103 | children = (
104 | 54CBB0EE25061F7600FD9ED7 /* Preview Assets.xcassets */,
105 | );
106 | path = "Preview Content";
107 | sourceTree = "";
108 | };
109 | 54CBB0FB25061F7600FD9ED7 /* CodableWebSocketExampleTests */ = {
110 | isa = PBXGroup;
111 | children = (
112 | 54CBB0FC25061F7600FD9ED7 /* WebSocketDemoTests.swift */,
113 | 54CBB0FE25061F7600FD9ED7 /* Info.plist */,
114 | );
115 | path = CodableWebSocketExampleTests;
116 | sourceTree = "";
117 | };
118 | /* End PBXGroup section */
119 |
120 | /* Begin PBXNativeTarget section */
121 | 54CBB0E125061F7400FD9ED7 /* CodableWebSocketExample */ = {
122 | isa = PBXNativeTarget;
123 | buildConfigurationList = 54CBB10125061F7600FD9ED7 /* Build configuration list for PBXNativeTarget "CodableWebSocketExample" */;
124 | buildPhases = (
125 | 54CBB0DE25061F7400FD9ED7 /* Sources */,
126 | 54CBB0DF25061F7400FD9ED7 /* Frameworks */,
127 | 54CBB0E025061F7400FD9ED7 /* Resources */,
128 | );
129 | buildRules = (
130 | );
131 | dependencies = (
132 | );
133 | name = CodableWebSocketExample;
134 | packageProductDependencies = (
135 | 54115A122514E14B002DF01D /* CodableWebSocket */,
136 | );
137 | productName = WebSocketDemo;
138 | productReference = 54CBB0E225061F7400FD9ED7 /* CodableWebSocketExample.app */;
139 | productType = "com.apple.product-type.application";
140 | };
141 | 54CBB0F725061F7600FD9ED7 /* CodableWebSocketExampleTests */ = {
142 | isa = PBXNativeTarget;
143 | buildConfigurationList = 54CBB10425061F7600FD9ED7 /* Build configuration list for PBXNativeTarget "CodableWebSocketExampleTests" */;
144 | buildPhases = (
145 | 54CBB0F425061F7600FD9ED7 /* Sources */,
146 | 54CBB0F525061F7600FD9ED7 /* Frameworks */,
147 | 54CBB0F625061F7600FD9ED7 /* Resources */,
148 | );
149 | buildRules = (
150 | );
151 | dependencies = (
152 | 54CBB0FA25061F7600FD9ED7 /* PBXTargetDependency */,
153 | );
154 | name = CodableWebSocketExampleTests;
155 | productName = WebSocketDemoTests;
156 | productReference = 54CBB0F825061F7600FD9ED7 /* CodableWebSocketExampleTests.xctest */;
157 | productType = "com.apple.product-type.bundle.unit-test";
158 | };
159 | /* End PBXNativeTarget section */
160 |
161 | /* Begin PBXProject section */
162 | 54CBB0DA25061F7400FD9ED7 /* Project object */ = {
163 | isa = PBXProject;
164 | attributes = {
165 | LastSwiftUpdateCheck = 1170;
166 | LastUpgradeCheck = 1170;
167 | ORGANIZATIONNAME = "David Crooks";
168 | TargetAttributes = {
169 | 54CBB0E125061F7400FD9ED7 = {
170 | CreatedOnToolsVersion = 11.7;
171 | };
172 | 54CBB0F725061F7600FD9ED7 = {
173 | CreatedOnToolsVersion = 11.7;
174 | TestTargetID = 54CBB0E125061F7400FD9ED7;
175 | };
176 | };
177 | };
178 | buildConfigurationList = 54CBB0DD25061F7400FD9ED7 /* Build configuration list for PBXProject "CodableWebSocketExample" */;
179 | compatibilityVersion = "Xcode 9.3";
180 | developmentRegion = en;
181 | hasScannedForEncodings = 0;
182 | knownRegions = (
183 | en,
184 | Base,
185 | );
186 | mainGroup = 54CBB0D925061F7400FD9ED7;
187 | packageReferences = (
188 | 54115A112514E14B002DF01D /* XCRemoteSwiftPackageReference "CodableWebsocket" */,
189 | );
190 | productRefGroup = 54CBB0E325061F7400FD9ED7 /* Products */;
191 | projectDirPath = "";
192 | projectRoot = "";
193 | targets = (
194 | 54CBB0E125061F7400FD9ED7 /* CodableWebSocketExample */,
195 | 54CBB0F725061F7600FD9ED7 /* CodableWebSocketExampleTests */,
196 | );
197 | };
198 | /* End PBXProject section */
199 |
200 | /* Begin PBXResourcesBuildPhase section */
201 | 54CBB0E025061F7400FD9ED7 /* Resources */ = {
202 | isa = PBXResourcesBuildPhase;
203 | buildActionMask = 2147483647;
204 | files = (
205 | 54CBB0F225061F7600FD9ED7 /* LaunchScreen.storyboard in Resources */,
206 | 54CBB0EF25061F7600FD9ED7 /* Preview Assets.xcassets in Resources */,
207 | 54CBB0EC25061F7600FD9ED7 /* Assets.xcassets in Resources */,
208 | );
209 | runOnlyForDeploymentPostprocessing = 0;
210 | };
211 | 54CBB0F625061F7600FD9ED7 /* Resources */ = {
212 | isa = PBXResourcesBuildPhase;
213 | buildActionMask = 2147483647;
214 | files = (
215 | );
216 | runOnlyForDeploymentPostprocessing = 0;
217 | };
218 | /* End PBXResourcesBuildPhase section */
219 |
220 | /* Begin PBXSourcesBuildPhase section */
221 | 54CBB0DE25061F7400FD9ED7 /* Sources */ = {
222 | isa = PBXSourcesBuildPhase;
223 | buildActionMask = 2147483647;
224 | files = (
225 | 54CBB0E625061F7400FD9ED7 /* AppDelegate.swift in Sources */,
226 | 5410D2BA250670A70097DE94 /* Publisher+FilterOutErrors.swift in Sources */,
227 | 54CBB0E825061F7400FD9ED7 /* SceneDelegate.swift in Sources */,
228 | 5410D2B8250661A90097DE94 /* Thing.swift in Sources */,
229 | 54CBB0EA25061F7400FD9ED7 /* ContentView.swift in Sources */,
230 | );
231 | runOnlyForDeploymentPostprocessing = 0;
232 | };
233 | 54CBB0F425061F7600FD9ED7 /* Sources */ = {
234 | isa = PBXSourcesBuildPhase;
235 | buildActionMask = 2147483647;
236 | files = (
237 | 54CBB0FD25061F7600FD9ED7 /* WebSocketDemoTests.swift in Sources */,
238 | );
239 | runOnlyForDeploymentPostprocessing = 0;
240 | };
241 | /* End PBXSourcesBuildPhase section */
242 |
243 | /* Begin PBXTargetDependency section */
244 | 54CBB0FA25061F7600FD9ED7 /* PBXTargetDependency */ = {
245 | isa = PBXTargetDependency;
246 | target = 54CBB0E125061F7400FD9ED7 /* CodableWebSocketExample */;
247 | targetProxy = 54CBB0F925061F7600FD9ED7 /* PBXContainerItemProxy */;
248 | };
249 | /* End PBXTargetDependency section */
250 |
251 | /* Begin PBXVariantGroup section */
252 | 54CBB0F025061F7600FD9ED7 /* LaunchScreen.storyboard */ = {
253 | isa = PBXVariantGroup;
254 | children = (
255 | 54CBB0F125061F7600FD9ED7 /* Base */,
256 | );
257 | name = LaunchScreen.storyboard;
258 | sourceTree = "";
259 | };
260 | /* End PBXVariantGroup section */
261 |
262 | /* Begin XCBuildConfiguration section */
263 | 54CBB0FF25061F7600FD9ED7 /* Debug */ = {
264 | isa = XCBuildConfiguration;
265 | buildSettings = {
266 | ALWAYS_SEARCH_USER_PATHS = NO;
267 | CLANG_ANALYZER_NONNULL = YES;
268 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
269 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
270 | CLANG_CXX_LIBRARY = "libc++";
271 | CLANG_ENABLE_MODULES = YES;
272 | CLANG_ENABLE_OBJC_ARC = YES;
273 | CLANG_ENABLE_OBJC_WEAK = YES;
274 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
275 | CLANG_WARN_BOOL_CONVERSION = YES;
276 | CLANG_WARN_COMMA = YES;
277 | CLANG_WARN_CONSTANT_CONVERSION = YES;
278 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
279 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
280 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
281 | CLANG_WARN_EMPTY_BODY = YES;
282 | CLANG_WARN_ENUM_CONVERSION = YES;
283 | CLANG_WARN_INFINITE_RECURSION = YES;
284 | CLANG_WARN_INT_CONVERSION = YES;
285 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
286 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
287 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
288 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
289 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
290 | CLANG_WARN_STRICT_PROTOTYPES = YES;
291 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
292 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
293 | CLANG_WARN_UNREACHABLE_CODE = YES;
294 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
295 | COPY_PHASE_STRIP = NO;
296 | DEBUG_INFORMATION_FORMAT = dwarf;
297 | ENABLE_STRICT_OBJC_MSGSEND = YES;
298 | ENABLE_TESTABILITY = YES;
299 | GCC_C_LANGUAGE_STANDARD = gnu11;
300 | GCC_DYNAMIC_NO_PIC = NO;
301 | GCC_NO_COMMON_BLOCKS = YES;
302 | GCC_OPTIMIZATION_LEVEL = 0;
303 | GCC_PREPROCESSOR_DEFINITIONS = (
304 | "DEBUG=1",
305 | "$(inherited)",
306 | );
307 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
308 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
309 | GCC_WARN_UNDECLARED_SELECTOR = YES;
310 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
311 | GCC_WARN_UNUSED_FUNCTION = YES;
312 | GCC_WARN_UNUSED_VARIABLE = YES;
313 | IPHONEOS_DEPLOYMENT_TARGET = 13.7;
314 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
315 | MTL_FAST_MATH = YES;
316 | ONLY_ACTIVE_ARCH = YES;
317 | SDKROOT = iphoneos;
318 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
319 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
320 | };
321 | name = Debug;
322 | };
323 | 54CBB10025061F7600FD9ED7 /* Release */ = {
324 | isa = XCBuildConfiguration;
325 | buildSettings = {
326 | ALWAYS_SEARCH_USER_PATHS = NO;
327 | CLANG_ANALYZER_NONNULL = YES;
328 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
329 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
330 | CLANG_CXX_LIBRARY = "libc++";
331 | CLANG_ENABLE_MODULES = YES;
332 | CLANG_ENABLE_OBJC_ARC = YES;
333 | CLANG_ENABLE_OBJC_WEAK = YES;
334 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
335 | CLANG_WARN_BOOL_CONVERSION = YES;
336 | CLANG_WARN_COMMA = YES;
337 | CLANG_WARN_CONSTANT_CONVERSION = YES;
338 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
339 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
340 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
341 | CLANG_WARN_EMPTY_BODY = YES;
342 | CLANG_WARN_ENUM_CONVERSION = YES;
343 | CLANG_WARN_INFINITE_RECURSION = YES;
344 | CLANG_WARN_INT_CONVERSION = YES;
345 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
346 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
347 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
348 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
349 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
350 | CLANG_WARN_STRICT_PROTOTYPES = YES;
351 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
352 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
353 | CLANG_WARN_UNREACHABLE_CODE = YES;
354 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
355 | COPY_PHASE_STRIP = NO;
356 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
357 | ENABLE_NS_ASSERTIONS = NO;
358 | ENABLE_STRICT_OBJC_MSGSEND = YES;
359 | GCC_C_LANGUAGE_STANDARD = gnu11;
360 | GCC_NO_COMMON_BLOCKS = YES;
361 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
362 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
363 | GCC_WARN_UNDECLARED_SELECTOR = YES;
364 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
365 | GCC_WARN_UNUSED_FUNCTION = YES;
366 | GCC_WARN_UNUSED_VARIABLE = YES;
367 | IPHONEOS_DEPLOYMENT_TARGET = 13.7;
368 | MTL_ENABLE_DEBUG_INFO = NO;
369 | MTL_FAST_MATH = YES;
370 | SDKROOT = iphoneos;
371 | SWIFT_COMPILATION_MODE = wholemodule;
372 | SWIFT_OPTIMIZATION_LEVEL = "-O";
373 | VALIDATE_PRODUCT = YES;
374 | };
375 | name = Release;
376 | };
377 | 54CBB10225061F7600FD9ED7 /* Debug */ = {
378 | isa = XCBuildConfiguration;
379 | buildSettings = {
380 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
381 | CODE_SIGN_STYLE = Automatic;
382 | DEVELOPMENT_ASSET_PATHS = "\"CodableWebSocketExample/Preview Content\"";
383 | DEVELOPMENT_TEAM = 84Y3R5UC5Q;
384 | ENABLE_PREVIEWS = YES;
385 | INFOPLIST_FILE = CodableWebSocketExample/Info.plist;
386 | LD_RUNPATH_SEARCH_PATHS = (
387 | "$(inherited)",
388 | "@executable_path/Frameworks",
389 | );
390 | PRODUCT_BUNDLE_IDENTIFIER = com.crooks.WebSocketDemo;
391 | PRODUCT_NAME = "$(TARGET_NAME)";
392 | SWIFT_VERSION = 5.0;
393 | TARGETED_DEVICE_FAMILY = "1,2";
394 | };
395 | name = Debug;
396 | };
397 | 54CBB10325061F7600FD9ED7 /* Release */ = {
398 | isa = XCBuildConfiguration;
399 | buildSettings = {
400 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
401 | CODE_SIGN_STYLE = Automatic;
402 | DEVELOPMENT_ASSET_PATHS = "\"CodableWebSocketExample/Preview Content\"";
403 | DEVELOPMENT_TEAM = 84Y3R5UC5Q;
404 | ENABLE_PREVIEWS = YES;
405 | INFOPLIST_FILE = CodableWebSocketExample/Info.plist;
406 | LD_RUNPATH_SEARCH_PATHS = (
407 | "$(inherited)",
408 | "@executable_path/Frameworks",
409 | );
410 | PRODUCT_BUNDLE_IDENTIFIER = com.crooks.WebSocketDemo;
411 | PRODUCT_NAME = "$(TARGET_NAME)";
412 | SWIFT_VERSION = 5.0;
413 | TARGETED_DEVICE_FAMILY = "1,2";
414 | };
415 | name = Release;
416 | };
417 | 54CBB10525061F7600FD9ED7 /* Debug */ = {
418 | isa = XCBuildConfiguration;
419 | buildSettings = {
420 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
421 | BUNDLE_LOADER = "$(TEST_HOST)";
422 | CODE_SIGN_STYLE = Automatic;
423 | DEVELOPMENT_TEAM = 84Y3R5UC5Q;
424 | INFOPLIST_FILE = WebSocketDemoTests/Info.plist;
425 | IPHONEOS_DEPLOYMENT_TARGET = 13.7;
426 | LD_RUNPATH_SEARCH_PATHS = (
427 | "$(inherited)",
428 | "@executable_path/Frameworks",
429 | "@loader_path/Frameworks",
430 | );
431 | PRODUCT_BUNDLE_IDENTIFIER = com.crooks.WebSocketDemoTests;
432 | PRODUCT_NAME = "$(TARGET_NAME)";
433 | SWIFT_VERSION = 5.0;
434 | TARGETED_DEVICE_FAMILY = "1,2";
435 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/WebSocketDemo.app/WebSocketDemo";
436 | };
437 | name = Debug;
438 | };
439 | 54CBB10625061F7600FD9ED7 /* Release */ = {
440 | isa = XCBuildConfiguration;
441 | buildSettings = {
442 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
443 | BUNDLE_LOADER = "$(TEST_HOST)";
444 | CODE_SIGN_STYLE = Automatic;
445 | DEVELOPMENT_TEAM = 84Y3R5UC5Q;
446 | INFOPLIST_FILE = WebSocketDemoTests/Info.plist;
447 | IPHONEOS_DEPLOYMENT_TARGET = 13.7;
448 | LD_RUNPATH_SEARCH_PATHS = (
449 | "$(inherited)",
450 | "@executable_path/Frameworks",
451 | "@loader_path/Frameworks",
452 | );
453 | PRODUCT_BUNDLE_IDENTIFIER = com.crooks.WebSocketDemoTests;
454 | PRODUCT_NAME = "$(TARGET_NAME)";
455 | SWIFT_VERSION = 5.0;
456 | TARGETED_DEVICE_FAMILY = "1,2";
457 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/WebSocketDemo.app/WebSocketDemo";
458 | };
459 | name = Release;
460 | };
461 | /* End XCBuildConfiguration section */
462 |
463 | /* Begin XCConfigurationList section */
464 | 54CBB0DD25061F7400FD9ED7 /* Build configuration list for PBXProject "CodableWebSocketExample" */ = {
465 | isa = XCConfigurationList;
466 | buildConfigurations = (
467 | 54CBB0FF25061F7600FD9ED7 /* Debug */,
468 | 54CBB10025061F7600FD9ED7 /* Release */,
469 | );
470 | defaultConfigurationIsVisible = 0;
471 | defaultConfigurationName = Release;
472 | };
473 | 54CBB10125061F7600FD9ED7 /* Build configuration list for PBXNativeTarget "CodableWebSocketExample" */ = {
474 | isa = XCConfigurationList;
475 | buildConfigurations = (
476 | 54CBB10225061F7600FD9ED7 /* Debug */,
477 | 54CBB10325061F7600FD9ED7 /* Release */,
478 | );
479 | defaultConfigurationIsVisible = 0;
480 | defaultConfigurationName = Release;
481 | };
482 | 54CBB10425061F7600FD9ED7 /* Build configuration list for PBXNativeTarget "CodableWebSocketExampleTests" */ = {
483 | isa = XCConfigurationList;
484 | buildConfigurations = (
485 | 54CBB10525061F7600FD9ED7 /* Debug */,
486 | 54CBB10625061F7600FD9ED7 /* Release */,
487 | );
488 | defaultConfigurationIsVisible = 0;
489 | defaultConfigurationName = Release;
490 | };
491 | /* End XCConfigurationList section */
492 |
493 | /* Begin XCRemoteSwiftPackageReference section */
494 | 54115A112514E14B002DF01D /* XCRemoteSwiftPackageReference "CodableWebsocket" */ = {
495 | isa = XCRemoteSwiftPackageReference;
496 | repositoryURL = "https://github.com/quantumOrange/CodableWebsocket";
497 | requirement = {
498 | kind = upToNextMajorVersion;
499 | minimumVersion = 0.1.1;
500 | };
501 | };
502 | /* End XCRemoteSwiftPackageReference section */
503 |
504 | /* Begin XCSwiftPackageProductDependency section */
505 | 54115A122514E14B002DF01D /* CodableWebSocket */ = {
506 | isa = XCSwiftPackageProductDependency;
507 | package = 54115A112514E14B002DF01D /* XCRemoteSwiftPackageReference "CodableWebsocket" */;
508 | productName = CodableWebSocket;
509 | };
510 | /* End XCSwiftPackageProductDependency section */
511 | };
512 | rootObject = 54CBB0DA25061F7400FD9ED7 /* Project object */;
513 | }
514 |
--------------------------------------------------------------------------------
/Example/CodableWebSocketExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Example/CodableWebSocketExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Example/CodableWebSocketExample.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "object": {
3 | "pins": [
4 | {
5 | "package": "CodableWebSocket",
6 | "repositoryURL": "https://github.com/quantumOrange/CodableWebsocket",
7 | "state": {
8 | "branch": null,
9 | "revision": "8c5fe7b0294c952d65cc53e22114d3c8997a200d",
10 | "version": "0.1.2"
11 | }
12 | }
13 | ]
14 | },
15 | "version": 1
16 | }
17 |
--------------------------------------------------------------------------------
/Example/CodableWebSocketExample.xcodeproj/xcuserdata/davidcrooks.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | CodableWebSocketExample.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 | WebSocketDemo.xcscheme_^#shared#^_
13 |
14 | orderHint
15 | 0
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Example/CodableWebSocketExample/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // WebSocketDemo
4 | //
5 | // Created by David Crooks on 07/09/2020.
6 | // Copyright © 2020 David Crooks. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 |
15 |
16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
17 | // Override point for customization after application launch.
18 | return true
19 | }
20 |
21 | // MARK: UISceneSession Lifecycle
22 |
23 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
24 | // Called when a new scene session is being created.
25 | // Use this method to select a configuration to create the new scene with.
26 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
27 | }
28 |
29 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {
30 | // Called when the user discards a scene session.
31 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
32 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
33 | }
34 |
35 |
36 | }
37 |
38 |
--------------------------------------------------------------------------------
/Example/CodableWebSocketExample/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "20x20"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "scale" : "3x",
11 | "size" : "20x20"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "scale" : "2x",
16 | "size" : "29x29"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "3x",
21 | "size" : "29x29"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "scale" : "2x",
26 | "size" : "40x40"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "scale" : "3x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "scale" : "2x",
36 | "size" : "60x60"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "scale" : "3x",
41 | "size" : "60x60"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "scale" : "1x",
46 | "size" : "20x20"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "scale" : "2x",
51 | "size" : "20x20"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "scale" : "1x",
56 | "size" : "29x29"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "scale" : "2x",
61 | "size" : "29x29"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "scale" : "1x",
66 | "size" : "40x40"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "scale" : "2x",
71 | "size" : "40x40"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "scale" : "1x",
76 | "size" : "76x76"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "scale" : "2x",
81 | "size" : "76x76"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "scale" : "2x",
86 | "size" : "83.5x83.5"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "scale" : "1x",
91 | "size" : "1024x1024"
92 | }
93 | ],
94 | "info" : {
95 | "author" : "xcode",
96 | "version" : 1
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/Example/CodableWebSocketExample/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/CodableWebSocketExample/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Example/CodableWebSocketExample/ContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | // WebSocketDemo
4 | //
5 | // Created by David Crooks on 07/09/2020.
6 | // Copyright © 2020 David Crooks. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 | import Combine
11 | import CodableWebSocket
12 |
13 | class ViewModel:ObservableObject {
14 | var socket:CodableWebSocket
15 | var cancelable:AnyCancellable? = nil
16 |
17 | @Published var thing:Thing = Thing(name: "nothing", number: 0)
18 |
19 | init() {
20 | socket = CodableWebSocket(url:URL(string:"ws://echo.websocket.org")!)
21 |
22 | cancelable = socket
23 | .codable()
24 | .receive(on:DispatchQueue.main)
25 | .filterOutErrors()
26 | .assign(to: \ViewModel.thing, on: self)
27 |
28 | }
29 | }
30 |
31 | struct ContentView: View
32 | {
33 | @ObservedObject var viewModel:ViewModel = ViewModel()
34 |
35 | var body: some View
36 | {
37 | VStack
38 | {
39 | Text("Received: \(viewModel.thing.name). Thing number \(viewModel.thing.number).")
40 | Button("Send A Thing")
41 | {
42 | _ = self
43 | .viewModel
44 | .socket
45 | .receive(.codable(self.viewModel.thing.next()))
46 | }
47 |
48 | }
49 | }
50 | }
51 |
52 | struct ContentView_Previews: PreviewProvider {
53 | static var previews: some View
54 | {
55 | ContentView()
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Example/CodableWebSocketExample/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UIApplicationSceneManifest
24 |
25 | UIApplicationSupportsMultipleScenes
26 |
27 | UISceneConfigurations
28 |
29 | UIWindowSceneSessionRoleApplication
30 |
31 |
32 | UISceneConfigurationName
33 | Default Configuration
34 | UISceneDelegateClassName
35 | $(PRODUCT_MODULE_NAME).SceneDelegate
36 |
37 |
38 |
39 |
40 | UILaunchStoryboardName
41 | LaunchScreen
42 | UIRequiredDeviceCapabilities
43 |
44 | armv7
45 |
46 | UISupportedInterfaceOrientations
47 |
48 | UIInterfaceOrientationPortrait
49 | UIInterfaceOrientationLandscapeLeft
50 | UIInterfaceOrientationLandscapeRight
51 |
52 | UISupportedInterfaceOrientations~ipad
53 |
54 | UIInterfaceOrientationPortrait
55 | UIInterfaceOrientationPortraitUpsideDown
56 | UIInterfaceOrientationLandscapeLeft
57 | UIInterfaceOrientationLandscapeRight
58 |
59 | NSAppTransportSecurity
60 |
61 | NSAllowsArbitraryLoads
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/Example/CodableWebSocketExample/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Example/CodableWebSocketExample/Publisher+FilterOutErrors.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Publisher+FilterOutErrors.swift
3 | // WebSocketDemo
4 | //
5 | // Created by David Crooks on 07/09/2020.
6 | // Copyright © 2020 David Crooks. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Combine
11 |
12 | extension Publisher {
13 | func filterOutErrors() -> Publishers.CompactMap>, Self.Output>
14 | {
15 | map{ Optional($0)}
16 | .replaceError(with:nil)
17 | .compactMap{$0}
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Example/CodableWebSocketExample/SceneDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SceneDelegate.swift
3 | // WebSocketDemo
4 | //
5 | // Created by David Crooks on 07/09/2020.
6 | // Copyright © 2020 David Crooks. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import SwiftUI
11 |
12 | class SceneDelegate: UIResponder, UIWindowSceneDelegate {
13 |
14 | var window: UIWindow?
15 |
16 |
17 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
18 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
19 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
20 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
21 |
22 | // Create the SwiftUI view that provides the window contents.
23 | let contentView = ContentView()
24 |
25 | // Use a UIHostingController as window root view controller.
26 | if let windowScene = scene as? UIWindowScene {
27 | let window = UIWindow(windowScene: windowScene)
28 | window.rootViewController = UIHostingController(rootView: contentView)
29 | self.window = window
30 | window.makeKeyAndVisible()
31 | }
32 | }
33 |
34 | func sceneDidDisconnect(_ scene: UIScene) {
35 | // Called as the scene is being released by the system.
36 | // This occurs shortly after the scene enters the background, or when its session is discarded.
37 | // Release any resources associated with this scene that can be re-created the next time the scene connects.
38 | // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
39 | }
40 |
41 | func sceneDidBecomeActive(_ scene: UIScene) {
42 | // Called when the scene has moved from an inactive state to an active state.
43 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
44 | }
45 |
46 | func sceneWillResignActive(_ scene: UIScene) {
47 | // Called when the scene will move from an active state to an inactive state.
48 | // This may occur due to temporary interruptions (ex. an incoming phone call).
49 | }
50 |
51 | func sceneWillEnterForeground(_ scene: UIScene) {
52 | // Called as the scene transitions from the background to the foreground.
53 | // Use this method to undo the changes made on entering the background.
54 | }
55 |
56 | func sceneDidEnterBackground(_ scene: UIScene) {
57 | // Called as the scene transitions from the foreground to the background.
58 | // Use this method to save data, release shared resources, and store enough scene-specific state information
59 | // to restore the scene back to its current state.
60 | }
61 |
62 |
63 | }
64 |
65 |
--------------------------------------------------------------------------------
/Example/CodableWebSocketExample/Thing.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Thing.swift
3 | // WebSocketDemo
4 | //
5 | // Created by David Crooks on 07/09/2020.
6 | // Copyright © 2020 David Crooks. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct Thing:Codable {
12 | let name:String
13 | let number:Int
14 |
15 |
16 | func next() -> Thing {
17 | let name = Thing.names[number % Thing.names.count]
18 | return Thing(name: name, number: number+1)
19 | }
20 |
21 | static let names = ["Banana","Grapes","Bicycle","Surfboard","Octopus","Drill","Book"]
22 | }
23 |
--------------------------------------------------------------------------------
/Example/CodableWebSocketExampleTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Example/CodableWebSocketExampleTests/WebSocketDemoTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WebSocketDemoTests.swift
3 | // WebSocketDemoTests
4 | //
5 | // Created by David Crooks on 07/09/2020.
6 | // Copyright © 2020 David Crooks. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import WebSocketDemo
11 |
12 | class WebSocketDemoTests: XCTestCase {
13 |
14 | override func setUpWithError() throws {
15 | // Put setup code here. This method is called before the invocation of each test method in the class.
16 | }
17 |
18 | override func tearDownWithError() throws {
19 | // Put teardown code here. This method is called after the invocation of each test method in the class.
20 | }
21 |
22 | func testExample() throws {
23 | // This is an example of a functional test case.
24 | // Use XCTAssert and related functions to verify your tests produce the correct results.
25 | }
26 |
27 | func testPerformanceExample() throws {
28 | // This is an example of a performance test case.
29 | self.measure {
30 | // Put the code you want to measure the time of here.
31 | }
32 | }
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.2
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "CodableWebSocket",
8 | platforms: [.iOS(.v13)],
9 | products: [
10 | // Products define the executables and libraries produced by a package, and make them visible to other packages.
11 | .library(
12 | name: "CodableWebSocket",
13 | targets: ["CodableWebSocket"]),
14 | ],
15 | dependencies: [
16 | // Dependencies declare other packages that this package depends on.
17 | // .package(url: /* package url */, from: "1.0.0"),
18 | ],
19 | targets: [
20 | // Targets are the basic building blocks of a package. A target can define a module or a test suite.
21 | // Targets can depend on other targets in this package, and on products in packages which this package depends on.
22 | .target(
23 | name: "CodableWebSocket",
24 | dependencies: []),
25 | .testTarget(
26 | name: "CodableWebSocketTests",
27 | dependencies: ["CodableWebSocket"]),
28 | ]
29 | )
30 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Codable Websocket
2 |
3 | A Combine Publisher and Subscriber that wraps a URLSessionWebSocketTask and makes it easy to send and receive any codable type over a websocket.
4 |
5 | **Combine, SwiftUI, Websocket**
6 |
7 | ## Usage
8 | You can create a socket with a URL with any type that conforms to the Codable protocol:
9 | ```swift
10 | let socket = CodableWebSocket(url:URL(string:"ws://echo.websocket.org")!)
11 | ```
12 | You can send values to the websocket like this:
13 |
14 | ``` swift
15 | let value = MyCodableType()
16 |
17 | socket
18 | .receive(.codable(value))
19 | ```
20 |
21 | And receive values fromt the server like this:
22 |
23 | ``` swift
24 | let cancelable = socket
25 | .codable
26 | .sink(receiveCompletion:
27 | { completion in
28 | switch completion
29 | {
30 | case .finished:
31 | break
32 | case .failure(let error):
33 | print(error.localizedDescription)
34 | }
35 | },
36 | receiveValue:
37 | { value in
38 | // do something with the value here
39 | print("Receved:\(value)")
40 | }
41 | )
42 | ```
43 |
44 | ## Example
45 |
46 | The example app is a simple demo that conects to ws://echo.websocket.org. This websocket just echos back whatever is sent. Whenever the user hits the send button we send a **Thing** to the server. Whatever is sent back (which will be just the **Thing** we sent) we display on the screen.
47 |
48 |
--------------------------------------------------------------------------------
/Sources/CodableWebSocket/CodableWebSocket.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WebSocket.swift
3 | // WebSocketDemo
4 | //
5 | // Created by David Crooks on 07/09/2020.
6 | // Copyright © 2020 David Crooks. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Combine
11 |
12 | public enum SocketData {
13 | case message(String)
14 | case codable(T)
15 | case uncodable(Data)
16 | }
17 |
18 | public final class CodableWebSocket:Publisher,Subscriber {
19 |
20 | public typealias Output = Result,Error>
21 | public typealias Input = SocketData
22 | public typealias Failure = Error
23 | let webSocketTask:URLSessionWebSocketTask
24 | public var combineIdentifier: CombineIdentifier = CombineIdentifier()
25 |
26 | public init(url:URL)
27 | {
28 | let urlSession = URLSession(configuration: .default)
29 | webSocketTask = urlSession.webSocketTask(with:url)
30 | webSocketTask.resume()
31 | }
32 |
33 | // MARK: Publisher
34 |
35 | public func receive(subscriber: S) where S : Subscriber, CodableWebSocket.Failure == S.Failure, CodableWebSocket.Output == S.Input {
36 | let subscription = CodableWebsocketSubscription(subscriber: subscriber, socket:webSocketTask)
37 | subscriber.receive(subscription: subscription)
38 | }
39 |
40 | // MARK: Subscriber
41 |
42 | public func receive(subscription: Subscription) {
43 | subscription.request(.unlimited)
44 | }
45 |
46 | public func receive(_ input: SocketData) -> Subscribers.Demand {
47 | let message:URLSessionWebSocketTask.Message
48 |
49 | switch input {
50 |
51 | case .message(let string):
52 | message = URLSessionWebSocketTask.Message.string(string)
53 | case .codable(let codable):
54 | if let data = try? JSONEncoder().encode(codable) {
55 | message = URLSessionWebSocketTask.Message.data(data)
56 | }
57 | else {
58 | fatalError()
59 | }
60 | case .uncodable(let data):
61 | message = URLSessionWebSocketTask.Message.data(data)
62 | }
63 |
64 | webSocketTask.send(message, completionHandler: {
65 | error in
66 | if let error = error {
67 | if self.webSocketTask.closeCode != .invalid {
68 | //closed!
69 | }
70 | Swift.print("ERROR on send \(error)")
71 | }
72 | })
73 | return .unlimited
74 | }
75 |
76 | public func receive(completion: Subscribers.Completion) {
77 | Swift.print("Completion")
78 | }
79 |
80 | }
81 |
82 | extension CodableWebSocket {
83 | public func codable()-> AnyPublisher.Failure> {
84 | return compactMap{ result -> T? in
85 | guard case Result,Error>.success(let socketdata) = result,
86 | case SocketData.codable(let codable) = socketdata
87 | else { return nil }
88 | return codable
89 | }.eraseToAnyPublisher()
90 | }
91 | }
92 |
93 |
--------------------------------------------------------------------------------
/Sources/CodableWebSocket/CodableWebsocketSubscription.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CodableWebsocketSubscription.swift
3 | // CodableWebSocketExample
4 | //
5 | // Created by David Crooks on 18/09/2020.
6 | // Copyright © 2020 David Crooks. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Combine
11 |
12 | final class CodableWebsocketSubscription: Subscription where SubscriberType.Input == Result,Error>,SubscriberType.Failure == Error {
13 | private var subscriber: SubscriberType?
14 |
15 | let webSocketTask:URLSessionWebSocketTask
16 |
17 | init(subscriber: SubscriberType, socket:URLSessionWebSocketTask) {
18 | self.subscriber = subscriber
19 | webSocketTask = socket
20 | receive()
21 | }
22 |
23 | func request(_ demand: Subscribers.Demand) {
24 | // Nothing to do here
25 | }
26 |
27 | func cancel() {
28 | subscriber = nil
29 | }
30 |
31 | func receive()
32 | {
33 | webSocketTask
34 | .receive
35 | {[weak self] result in
36 | let newResult:Result,Error> = result.map { message in
37 |
38 | switch message
39 | {
40 | case .string(let str):
41 | return SocketData.message(str)
42 | case .data(let data):
43 | if let thing = try? JSONDecoder().decode(T.self, from: data)
44 | {
45 | return .codable(thing)
46 | }
47 | else
48 | {
49 | return .uncodable(data)
50 | }
51 |
52 | @unknown default:
53 | fatalError()
54 | }
55 |
56 | }
57 |
58 |
59 | if case Result.failure(let error) = newResult, self?.webSocketTask.closeCode != .invalid {
60 | self?.subscriber?.receive(completion:Subscribers.Completion.failure(error))
61 | }
62 | else {
63 | _ = self?.subscriber?.receive(newResult)
64 | }
65 |
66 | self?.receive()
67 |
68 | }
69 |
70 | }
71 | }
72 |
73 |
--------------------------------------------------------------------------------
/Tests/CodableWebSocketTests/CodableWebSocketTests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 | @testable import CodableWebSocket
3 |
4 | final class CodableWebSocketTests: XCTestCase {
5 | func testExample() {
6 | // This is an example of a functional test case.
7 | // Use XCTAssert and related functions to verify your tests produce the correct
8 | // results.
9 |
10 | }
11 |
12 | static var allTests = [
13 | ("testExample", testExample),
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/Tests/CodableWebSocketTests/XCTestManifests.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 |
3 | #if !canImport(ObjectiveC)
4 | public func allTests() -> [XCTestCaseEntry] {
5 | return [
6 | testCase(CodableWebSocketTests.allTests),
7 | ]
8 | }
9 | #endif
10 |
--------------------------------------------------------------------------------
/Tests/LinuxMain.swift:
--------------------------------------------------------------------------------
1 | import XCTest
2 |
3 | import CodableWebSocketTests
4 |
5 | var tests = [XCTestCaseEntry]()
6 | tests += CodableWebSocketTests.allTests()
7 | XCTMain(tests)
8 |
--------------------------------------------------------------------------------