From 872cb9795f33b1b15dc486d7ba95c8e029f44337 Mon Sep 17 00:00:00 2001 From: AbhijeetMallick Date: Mon, 8 Apr 2024 12:14:01 +0530 Subject: [PATCH 1/4] Add Starscream as a pod --- .DS_Store | Bin 10244 -> 10244 bytes Clickstream.podspec | 1 + Clickstream.xcodeproj/project.pbxproj | 250 ++---------- .../Infrastructure/RetryMechanismTests.swift | 10 +- .../Mocks/SocketHandlerMock.swift | 18 +- Example/Example.xcodeproj/project.pbxproj | 24 +- Podfile | 3 +- Podfile.lock | 6 +- .../NetworkService/Connectable.swift | 16 +- .../NetworkService/NetworkService.swift | 19 +- .../Sockets/SocketHandler.swift | 292 +++++++------- .../Utilities/URLRequest+Equality.swift | 23 ++ .../Sources/Compression/Compression.swift | 29 -- .../Sources/Compression/WSCompression.swift | 247 ------------ .../Sources/DataBytes/Data+Extensions.swift | 53 --- .../Sources/Engine/Engine.swift | 22 -- .../Sources/Engine/NativeEngine.swift | 96 ----- .../Sources/Engine/WSEngine.swift | 236 ------------ .../Framer/FoundationHTTPHandler.swift | 123 ------ .../Framer/FoundationHTTPServerHandler.swift | 99 ----- .../Sources/Framer/FrameCollector.swift | 107 ------ .../Sources/Framer/Framer.swift | 357 ------------------ .../Sources/Framer/HTTPHandler.swift | 146 ------- .../Sources/Framer/StringHTTPHandler.swift | 143 ------- .../Sources/Security/FoundationSecurity.swift | 101 ----- .../Sources/Security/Security.swift | 45 --- .../Sources/Server/Server.swift | 56 --- .../Sources/Server/WebSocketServer.swift | 198 ---------- .../Sources/Starscream/WebSocket.swift | 179 --------- .../Transport/FoundationTransport.swift | 243 ------------ .../Sources/Transport/TCPTransport.swift | 193 ---------- .../Sources/Transport/Transport.swift | 56 --- 32 files changed, 271 insertions(+), 3120 deletions(-) create mode 100644 Sources/Clickstream/NetworkManager/Infrastructure/Utilities/URLRequest+Equality.swift delete mode 100644 Sources/Clickstream/ThirdPartyLibraries/Sources/Compression/Compression.swift delete mode 100644 Sources/Clickstream/ThirdPartyLibraries/Sources/Compression/WSCompression.swift delete mode 100644 Sources/Clickstream/ThirdPartyLibraries/Sources/DataBytes/Data+Extensions.swift delete mode 100644 Sources/Clickstream/ThirdPartyLibraries/Sources/Engine/Engine.swift delete mode 100644 Sources/Clickstream/ThirdPartyLibraries/Sources/Engine/NativeEngine.swift delete mode 100644 Sources/Clickstream/ThirdPartyLibraries/Sources/Engine/WSEngine.swift delete mode 100644 Sources/Clickstream/ThirdPartyLibraries/Sources/Framer/FoundationHTTPHandler.swift delete mode 100644 Sources/Clickstream/ThirdPartyLibraries/Sources/Framer/FoundationHTTPServerHandler.swift delete mode 100644 Sources/Clickstream/ThirdPartyLibraries/Sources/Framer/FrameCollector.swift delete mode 100644 Sources/Clickstream/ThirdPartyLibraries/Sources/Framer/Framer.swift delete mode 100644 Sources/Clickstream/ThirdPartyLibraries/Sources/Framer/HTTPHandler.swift delete mode 100644 Sources/Clickstream/ThirdPartyLibraries/Sources/Framer/StringHTTPHandler.swift delete mode 100644 Sources/Clickstream/ThirdPartyLibraries/Sources/Security/FoundationSecurity.swift delete mode 100644 Sources/Clickstream/ThirdPartyLibraries/Sources/Security/Security.swift delete mode 100644 Sources/Clickstream/ThirdPartyLibraries/Sources/Server/Server.swift delete mode 100644 Sources/Clickstream/ThirdPartyLibraries/Sources/Server/WebSocketServer.swift delete mode 100644 Sources/Clickstream/ThirdPartyLibraries/Sources/Starscream/WebSocket.swift delete mode 100644 Sources/Clickstream/ThirdPartyLibraries/Sources/Transport/FoundationTransport.swift delete mode 100644 Sources/Clickstream/ThirdPartyLibraries/Sources/Transport/TCPTransport.swift delete mode 100644 Sources/Clickstream/ThirdPartyLibraries/Sources/Transport/Transport.swift diff --git a/.DS_Store b/.DS_Store index a54c1ccf3a719813fe370992374ad14761f208a7..a537561c206c0bb85c5f8d238595854bf346ad40 100644 GIT binary patch delta 69 zcmZn(XbISGMv#fqcd~$x!Q_8}T&()%k`FwY>@Q?E*-nU;MXsRW9*|?t7_hlth?j{e VFKO~Tku!`NHfM|e=Lb_WnE*}?7+3%R delta 69 zcmZn(XbISGMv%!(cd~$x!Q_8}T&xOt8i&tK_7^goY$wFa0_E5 1.10" s.dependency "ReachabilitySwift", '>= 5.0.0' s.dependency "GRDB.swift", "~> 6.7" + s.dependency "Starscream", "~> 4.0" s.default_subspec = 'Core' diff --git a/Clickstream.xcodeproj/project.pbxproj b/Clickstream.xcodeproj/project.pbxproj index 9688f4f..e351748 100644 --- a/Clickstream.xcodeproj/project.pbxproj +++ b/Clickstream.xcodeproj/project.pbxproj @@ -3,13 +3,16 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ 68E7BD202456E6F10072549A /* libClickstream.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 68E7BADF244F08C00072549A /* libClickstream.a */; }; 6E2A2FF5A4C4820DF0F31C3C /* libPods-ClickstreamTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0900D4ED17E642E4DA1234F8 /* libPods-ClickstreamTests.a */; }; 98CDAC393665EEC635D0C690 /* libPods-Clickstream.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E561B35C0082DAE47E3BDE1 /* libPods-Clickstream.a */; }; + 9A0BBA882BC3A59300F7DC01 /* URLRequest+Equality.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A0BBA872BC3A59300F7DC01 /* URLRequest+Equality.swift */; }; + 9A0BBA892BC3A59300F7DC01 /* URLRequest+Equality.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A0BBA872BC3A59300F7DC01 /* URLRequest+Equality.swift */; }; + 9A0BBA8A2BC3C98000F7DC01 /* SortedArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467EA2A5BD72000BFA976 /* SortedArray.swift */; }; BD6BDB6829FBC1360006B04A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BD6BDB3E29FBC1360006B04A /* Assets.xcassets */; }; BD6BDB6929FBC1360006B04A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BD6BDB3E29FBC1360006B04A /* Assets.xcassets */; }; BD6BDB6A29FBC1360006B04A /* RadioLabelTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD6BDB4129FBC1360006B04A /* RadioLabelTableViewCell.swift */; }; @@ -158,46 +161,6 @@ BDE468342A5BD72000BFA976 /* EventResponse.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467BF2A5BD72000BFA976 /* EventResponse.pb.swift */; }; BDE468352A5BD72000BFA976 /* Event.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467C02A5BD72000BFA976 /* Event.pb.swift */; }; BDE468362A5BD72000BFA976 /* Event.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467C02A5BD72000BFA976 /* Event.pb.swift */; }; - BDE468372A5BD72000BFA976 /* Transport.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467C42A5BD72000BFA976 /* Transport.swift */; }; - BDE468382A5BD72000BFA976 /* Transport.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467C42A5BD72000BFA976 /* Transport.swift */; }; - BDE468392A5BD72000BFA976 /* FoundationTransport.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467C52A5BD72000BFA976 /* FoundationTransport.swift */; }; - BDE4683A2A5BD72000BFA976 /* FoundationTransport.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467C52A5BD72000BFA976 /* FoundationTransport.swift */; }; - BDE4683B2A5BD72000BFA976 /* TCPTransport.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467C62A5BD72000BFA976 /* TCPTransport.swift */; }; - BDE4683C2A5BD72000BFA976 /* TCPTransport.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467C62A5BD72000BFA976 /* TCPTransport.swift */; }; - BDE4683D2A5BD72000BFA976 /* Security.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467C82A5BD72000BFA976 /* Security.swift */; }; - BDE4683E2A5BD72000BFA976 /* Security.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467C82A5BD72000BFA976 /* Security.swift */; }; - BDE4683F2A5BD72000BFA976 /* FoundationSecurity.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467C92A5BD72000BFA976 /* FoundationSecurity.swift */; }; - BDE468402A5BD72000BFA976 /* FoundationSecurity.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467C92A5BD72000BFA976 /* FoundationSecurity.swift */; }; - BDE468412A5BD72000BFA976 /* Data+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467CB2A5BD72000BFA976 /* Data+Extensions.swift */; }; - BDE468422A5BD72000BFA976 /* Data+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467CB2A5BD72000BFA976 /* Data+Extensions.swift */; }; - BDE468432A5BD72000BFA976 /* Server.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467CD2A5BD72000BFA976 /* Server.swift */; }; - BDE468442A5BD72000BFA976 /* Server.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467CD2A5BD72000BFA976 /* Server.swift */; }; - BDE468452A5BD72000BFA976 /* WebSocketServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467CE2A5BD72000BFA976 /* WebSocketServer.swift */; }; - BDE468462A5BD72000BFA976 /* WebSocketServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467CE2A5BD72000BFA976 /* WebSocketServer.swift */; }; - BDE468472A5BD72000BFA976 /* FoundationHTTPHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467D02A5BD72000BFA976 /* FoundationHTTPHandler.swift */; }; - BDE468482A5BD72000BFA976 /* FoundationHTTPHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467D02A5BD72000BFA976 /* FoundationHTTPHandler.swift */; }; - BDE468492A5BD72000BFA976 /* HTTPHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467D12A5BD72000BFA976 /* HTTPHandler.swift */; }; - BDE4684A2A5BD72000BFA976 /* HTTPHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467D12A5BD72000BFA976 /* HTTPHandler.swift */; }; - BDE4684B2A5BD72000BFA976 /* FrameCollector.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467D22A5BD72000BFA976 /* FrameCollector.swift */; }; - BDE4684C2A5BD72000BFA976 /* FrameCollector.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467D22A5BD72000BFA976 /* FrameCollector.swift */; }; - BDE4684D2A5BD72000BFA976 /* StringHTTPHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467D32A5BD72000BFA976 /* StringHTTPHandler.swift */; }; - BDE4684E2A5BD72000BFA976 /* StringHTTPHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467D32A5BD72000BFA976 /* StringHTTPHandler.swift */; }; - BDE4684F2A5BD72000BFA976 /* FoundationHTTPServerHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467D42A5BD72000BFA976 /* FoundationHTTPServerHandler.swift */; }; - BDE468502A5BD72000BFA976 /* FoundationHTTPServerHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467D42A5BD72000BFA976 /* FoundationHTTPServerHandler.swift */; }; - BDE468512A5BD72000BFA976 /* Framer.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467D52A5BD72000BFA976 /* Framer.swift */; }; - BDE468522A5BD72000BFA976 /* Framer.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467D52A5BD72000BFA976 /* Framer.swift */; }; - BDE468532A5BD72000BFA976 /* Compression.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467D72A5BD72000BFA976 /* Compression.swift */; }; - BDE468542A5BD72000BFA976 /* Compression.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467D72A5BD72000BFA976 /* Compression.swift */; }; - BDE468552A5BD72000BFA976 /* WSCompression.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467D82A5BD72000BFA976 /* WSCompression.swift */; }; - BDE468562A5BD72000BFA976 /* WSCompression.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467D82A5BD72000BFA976 /* WSCompression.swift */; }; - BDE468572A5BD72000BFA976 /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467DA2A5BD72000BFA976 /* WebSocket.swift */; }; - BDE468582A5BD72000BFA976 /* WebSocket.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467DA2A5BD72000BFA976 /* WebSocket.swift */; }; - BDE468592A5BD72000BFA976 /* Engine.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467DC2A5BD72000BFA976 /* Engine.swift */; }; - BDE4685A2A5BD72000BFA976 /* Engine.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467DC2A5BD72000BFA976 /* Engine.swift */; }; - BDE4685B2A5BD72000BFA976 /* NativeEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467DD2A5BD72000BFA976 /* NativeEngine.swift */; }; - BDE4685C2A5BD72000BFA976 /* NativeEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467DD2A5BD72000BFA976 /* NativeEngine.swift */; }; - BDE4685D2A5BD72000BFA976 /* WSEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467DE2A5BD72000BFA976 /* WSEngine.swift */; }; - BDE4685E2A5BD72000BFA976 /* WSEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467DE2A5BD72000BFA976 /* WSEngine.swift */; }; BDE4685F2A5BD72000BFA976 /* EventBatchCreator.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467E12A5BD72000BFA976 /* EventBatchCreator.swift */; }; BDE468602A5BD72000BFA976 /* EventBatchCreator.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467E12A5BD72000BFA976 /* EventBatchCreator.swift */; }; BDE468612A5BD72000BFA976 /* EventSchedulerDependencies.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467E22A5BD72000BFA976 /* EventSchedulerDependencies.swift */; }; @@ -212,7 +175,6 @@ BDE4686A2A5BD72000BFA976 /* EventBatchSizeRegulator.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467E82A5BD72000BFA976 /* EventBatchSizeRegulator.swift */; }; BDE4686B2A5BD72000BFA976 /* SchedulerService.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467E92A5BD72000BFA976 /* SchedulerService.swift */; }; BDE4686C2A5BD72000BFA976 /* SchedulerService.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467E92A5BD72000BFA976 /* SchedulerService.swift */; }; - BDE4686D2A5BD72000BFA976 /* SortedArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467EA2A5BD72000BFA976 /* SortedArray.swift */; }; BDE4686E2A5BD72000BFA976 /* SortedArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467EA2A5BD72000BFA976 /* SortedArray.swift */; }; BDE4686F2A5BD72000BFA976 /* DatabaseHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467EE2A5BD72000BFA976 /* DatabaseHandler.swift */; }; BDE468702A5BD72000BFA976 /* DatabaseHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467EE2A5BD72000BFA976 /* DatabaseHandler.swift */; }; @@ -254,17 +216,13 @@ /* Begin PBXFileReference section */ 0900D4ED17E642E4DA1234F8 /* libPods-ClickstreamTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ClickstreamTests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 1E89089CF6FF061500E7C7DC /* Pods-ClickstreamLib.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ClickstreamLib.debug.xcconfig"; path = "Target Support Files/Pods-ClickstreamLib/Pods-ClickstreamLib.debug.xcconfig"; sourceTree = ""; }; 3F2AFBAF5784EF40DEFD78DD /* Pods-ClickstreamTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ClickstreamTests.release.xcconfig"; path = "Target Support Files/Pods-ClickstreamTests/Pods-ClickstreamTests.release.xcconfig"; sourceTree = ""; }; 47E59ADC19B15059C977CB9C /* Pods-Clickstream.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Clickstream.release.xcconfig"; path = "Target Support Files/Pods-Clickstream/Pods-Clickstream.release.xcconfig"; sourceTree = ""; }; - 4A21BE293CE76A6536BAD044 /* Pods-ClickstreamLib.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ClickstreamLib.release.xcconfig"; path = "Target Support Files/Pods-ClickstreamLib/Pods-ClickstreamLib.release.xcconfig"; sourceTree = ""; }; 5E561B35C0082DAE47E3BDE1 /* libPods-Clickstream.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Clickstream.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 5F13A1F04B78213DC5E4995A /* Pods-ClickstreamLib.alpha.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ClickstreamLib.alpha.xcconfig"; path = "Target Support Files/Pods-ClickstreamLib/Pods-ClickstreamLib.alpha.xcconfig"; sourceTree = ""; }; - 64EC00981DF4B0D69ADB558C /* Pods-ClickstreamLib.production.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ClickstreamLib.production.xcconfig"; path = "Target Support Files/Pods-ClickstreamLib/Pods-ClickstreamLib.production.xcconfig"; sourceTree = ""; }; 68E7BADF244F08C00072549A /* libClickstream.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libClickstream.a; sourceTree = BUILT_PRODUCTS_DIR; }; 68E7BD1B2456E6F10072549A /* ClickstreamTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ClickstreamTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 88F9204101C5E8D41805FD1A /* Pods-ClickstreamTests.production.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ClickstreamTests.production.xcconfig"; path = "Target Support Files/Pods-ClickstreamTests/Pods-ClickstreamTests.production.xcconfig"; sourceTree = ""; }; - 97173CFFF6833EC6EF76541D /* Pods-ClickstreamLib.integration.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ClickstreamLib.integration.xcconfig"; path = "Target Support Files/Pods-ClickstreamLib/Pods-ClickstreamLib.integration.xcconfig"; sourceTree = ""; }; + 9A0BBA872BC3A59300F7DC01 /* URLRequest+Equality.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLRequest+Equality.swift"; sourceTree = ""; }; 9D31FA91B32979CBBA2C3849 /* Pods-ClickstreamTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ClickstreamTests.debug.xcconfig"; path = "Target Support Files/Pods-ClickstreamTests/Pods-ClickstreamTests.debug.xcconfig"; sourceTree = ""; }; A870FFC059A664D0DF0292E5 /* Pods-ClickstreamTests.alpha.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ClickstreamTests.alpha.xcconfig"; path = "Target Support Files/Pods-ClickstreamTests/Pods-ClickstreamTests.alpha.xcconfig"; sourceTree = ""; }; A8F29FD3E426DD04C26B8560 /* Pods-ClickstreamTests.integration.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ClickstreamTests.integration.xcconfig"; path = "Target Support Files/Pods-ClickstreamTests/Pods-ClickstreamTests.integration.xcconfig"; sourceTree = ""; }; @@ -357,26 +315,6 @@ BDE467BE2A5BD72000BFA976 /* EteExperiment.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EteExperiment.pb.swift; sourceTree = ""; }; BDE467BF2A5BD72000BFA976 /* EventResponse.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventResponse.pb.swift; sourceTree = ""; }; BDE467C02A5BD72000BFA976 /* Event.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Event.pb.swift; sourceTree = ""; }; - BDE467C42A5BD72000BFA976 /* Transport.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Transport.swift; sourceTree = ""; }; - BDE467C52A5BD72000BFA976 /* FoundationTransport.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FoundationTransport.swift; sourceTree = ""; }; - BDE467C62A5BD72000BFA976 /* TCPTransport.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TCPTransport.swift; sourceTree = ""; }; - BDE467C82A5BD72000BFA976 /* Security.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Security.swift; sourceTree = ""; }; - BDE467C92A5BD72000BFA976 /* FoundationSecurity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FoundationSecurity.swift; sourceTree = ""; }; - BDE467CB2A5BD72000BFA976 /* Data+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Data+Extensions.swift"; sourceTree = ""; }; - BDE467CD2A5BD72000BFA976 /* Server.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Server.swift; sourceTree = ""; }; - BDE467CE2A5BD72000BFA976 /* WebSocketServer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebSocketServer.swift; sourceTree = ""; }; - BDE467D02A5BD72000BFA976 /* FoundationHTTPHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FoundationHTTPHandler.swift; sourceTree = ""; }; - BDE467D12A5BD72000BFA976 /* HTTPHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HTTPHandler.swift; sourceTree = ""; }; - BDE467D22A5BD72000BFA976 /* FrameCollector.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FrameCollector.swift; sourceTree = ""; }; - BDE467D32A5BD72000BFA976 /* StringHTTPHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringHTTPHandler.swift; sourceTree = ""; }; - BDE467D42A5BD72000BFA976 /* FoundationHTTPServerHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FoundationHTTPServerHandler.swift; sourceTree = ""; }; - BDE467D52A5BD72000BFA976 /* Framer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Framer.swift; sourceTree = ""; }; - BDE467D72A5BD72000BFA976 /* Compression.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Compression.swift; sourceTree = ""; }; - BDE467D82A5BD72000BFA976 /* WSCompression.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WSCompression.swift; sourceTree = ""; }; - BDE467DA2A5BD72000BFA976 /* WebSocket.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebSocket.swift; sourceTree = ""; }; - BDE467DC2A5BD72000BFA976 /* Engine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Engine.swift; sourceTree = ""; }; - BDE467DD2A5BD72000BFA976 /* NativeEngine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NativeEngine.swift; sourceTree = ""; }; - BDE467DE2A5BD72000BFA976 /* WSEngine.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WSEngine.swift; sourceTree = ""; }; BDE467E12A5BD72000BFA976 /* EventBatchCreator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventBatchCreator.swift; sourceTree = ""; }; BDE467E22A5BD72000BFA976 /* EventSchedulerDependencies.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventSchedulerDependencies.swift; sourceTree = ""; }; BDE467E32A5BD72000BFA976 /* EventWarehouser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventWarehouser.swift; sourceTree = ""; }; @@ -422,11 +360,6 @@ 2E71FBE7FAE66AF83A297BB5 /* Pods */ = { isa = PBXGroup; children = ( - 1E89089CF6FF061500E7C7DC /* Pods-ClickstreamLib.debug.xcconfig */, - 97173CFFF6833EC6EF76541D /* Pods-ClickstreamLib.integration.xcconfig */, - 4A21BE293CE76A6536BAD044 /* Pods-ClickstreamLib.release.xcconfig */, - 5F13A1F04B78213DC5E4995A /* Pods-ClickstreamLib.alpha.xcconfig */, - 64EC00981DF4B0D69ADB558C /* Pods-ClickstreamLib.production.xcconfig */, 9D31FA91B32979CBBA2C3849 /* Pods-ClickstreamTests.debug.xcconfig */, A8F29FD3E426DD04C26B8560 /* Pods-ClickstreamTests.integration.xcconfig */, 3F2AFBAF5784EF40DEFD78DD /* Pods-ClickstreamTests.release.xcconfig */, @@ -788,7 +721,6 @@ BDE4678D2A5BD72000BFA976 /* NetworkManager */, BDE467A72A5BD72000BFA976 /* Core */, BDE467BC2A5BD72000BFA976 /* Contracts */, - BDE467C12A5BD72000BFA976 /* ThirdPartyLibraries */, BDE467DF2A5BD72000BFA976 /* EventScheduler */, BDE467F42A5BD72000BFA976 /* EventProcessor */, ); @@ -861,6 +793,7 @@ BDE4679E2A5BD72000BFA976 /* NetworkReachability.swift */, BDE4679F2A5BD72000BFA976 /* DispatchSourceTimer+RepeatingTimer.swift */, BDE467A02A5BD72000BFA976 /* KeepAliveService.swift */, + 9A0BBA872BC3A59300F7DC01 /* URLRequest+Equality.swift */, ); path = Utilities; sourceTree = ""; @@ -991,105 +924,6 @@ path = Contracts; sourceTree = ""; }; - BDE467C12A5BD72000BFA976 /* ThirdPartyLibraries */ = { - isa = PBXGroup; - children = ( - BDE467C22A5BD72000BFA976 /* Sources */, - ); - path = ThirdPartyLibraries; - sourceTree = ""; - }; - BDE467C22A5BD72000BFA976 /* Sources */ = { - isa = PBXGroup; - children = ( - BDE467C32A5BD72000BFA976 /* Transport */, - BDE467C72A5BD72000BFA976 /* Security */, - BDE467CA2A5BD72000BFA976 /* DataBytes */, - BDE467CC2A5BD72000BFA976 /* Server */, - BDE467CF2A5BD72000BFA976 /* Framer */, - BDE467D62A5BD72000BFA976 /* Compression */, - BDE467D92A5BD72000BFA976 /* Starscream */, - BDE467DB2A5BD72000BFA976 /* Engine */, - ); - path = Sources; - sourceTree = ""; - }; - BDE467C32A5BD72000BFA976 /* Transport */ = { - isa = PBXGroup; - children = ( - BDE467C42A5BD72000BFA976 /* Transport.swift */, - BDE467C52A5BD72000BFA976 /* FoundationTransport.swift */, - BDE467C62A5BD72000BFA976 /* TCPTransport.swift */, - ); - path = Transport; - sourceTree = ""; - }; - BDE467C72A5BD72000BFA976 /* Security */ = { - isa = PBXGroup; - children = ( - BDE467C82A5BD72000BFA976 /* Security.swift */, - BDE467C92A5BD72000BFA976 /* FoundationSecurity.swift */, - ); - path = Security; - sourceTree = ""; - }; - BDE467CA2A5BD72000BFA976 /* DataBytes */ = { - isa = PBXGroup; - children = ( - BDE467CB2A5BD72000BFA976 /* Data+Extensions.swift */, - ); - path = DataBytes; - sourceTree = ""; - }; - BDE467CC2A5BD72000BFA976 /* Server */ = { - isa = PBXGroup; - children = ( - BDE467CD2A5BD72000BFA976 /* Server.swift */, - BDE467CE2A5BD72000BFA976 /* WebSocketServer.swift */, - ); - path = Server; - sourceTree = ""; - }; - BDE467CF2A5BD72000BFA976 /* Framer */ = { - isa = PBXGroup; - children = ( - BDE467D02A5BD72000BFA976 /* FoundationHTTPHandler.swift */, - BDE467D12A5BD72000BFA976 /* HTTPHandler.swift */, - BDE467D22A5BD72000BFA976 /* FrameCollector.swift */, - BDE467D32A5BD72000BFA976 /* StringHTTPHandler.swift */, - BDE467D42A5BD72000BFA976 /* FoundationHTTPServerHandler.swift */, - BDE467D52A5BD72000BFA976 /* Framer.swift */, - ); - path = Framer; - sourceTree = ""; - }; - BDE467D62A5BD72000BFA976 /* Compression */ = { - isa = PBXGroup; - children = ( - BDE467D72A5BD72000BFA976 /* Compression.swift */, - BDE467D82A5BD72000BFA976 /* WSCompression.swift */, - ); - path = Compression; - sourceTree = ""; - }; - BDE467D92A5BD72000BFA976 /* Starscream */ = { - isa = PBXGroup; - children = ( - BDE467DA2A5BD72000BFA976 /* WebSocket.swift */, - ); - path = Starscream; - sourceTree = ""; - }; - BDE467DB2A5BD72000BFA976 /* Engine */ = { - isa = PBXGroup; - children = ( - BDE467DC2A5BD72000BFA976 /* Engine.swift */, - BDE467DD2A5BD72000BFA976 /* NativeEngine.swift */, - BDE467DE2A5BD72000BFA976 /* WSEngine.swift */, - ); - path = Engine; - sourceTree = ""; - }; BDE467DF2A5BD72000BFA976 /* EventScheduler */ = { isa = PBXGroup; children = ( @@ -1231,6 +1065,7 @@ 68E7BD172456E6F10072549A /* Sources */, 68E7BD182456E6F10072549A /* Frameworks */, 68E7BD192456E6F10072549A /* Resources */, + 01A626ECDAE0A8DA078D9294 /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -1299,6 +1134,23 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 01A626ECDAE0A8DA078D9294 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-ClickstreamTests/Pods-ClickstreamTests-resources-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-ClickstreamTests/Pods-ClickstreamTests-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ClickstreamTests/Pods-ClickstreamTests-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; 0737F26BE8B9A6B567D7D80E /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -1369,19 +1221,15 @@ buildActionMask = 2147483647; files = ( BD6BDB7229FBC1360006B04A /* EventVisualizerLandingViewController.swift in Sources */, - BDE468452A5BD72000BFA976 /* WebSocketServer.swift in Sources */, BD6BDB7629FBC1360006B04A /* EventsListViewModel.swift in Sources */, BD6BDB7429FBC1360006B04A /* EventsListViewController.swift in Sources */, BDE468232A5BD72000BFA976 /* JSONStringDecoder.swift in Sources */, BD6BDB9829FBC1360006B04A /* CSLocationDTO.swift in Sources */, BDE468132A5BD72000BFA976 /* ProtoConvertible.swift in Sources */, BD6BDB7029FBC1360006B04A /* EventVisualizerLandingViewModel.swift in Sources */, - BDE468372A5BD72000BFA976 /* Transport.swift in Sources */, BDE468092A5BD72000BFA976 /* Atomic.swift in Sources */, - BDE468492A5BD72000BFA976 /* HTTPHandler.swift in Sources */, BDE467F92A5BD72000BFA976 /* NetworkBuilder.swift in Sources */, BD6BDB6A29FBC1360006B04A /* RadioLabelTableViewCell.swift in Sources */, - BDE468572A5BD72000BFA976 /* WebSocket.swift in Sources */, BD6BDB9E29FBC1360006B04A /* CSCommonPropertiesDTO.swift in Sources */, BD6BDB9A29FBC1360006B04A /* ClickStreamHealthConfigurations.swift in Sources */, BDE468752A5BD72000BFA976 /* DatabasePersistable.swift in Sources */, @@ -1392,21 +1240,16 @@ BDE4682D2A5BD72000BFA976 /* EventBatch.swift in Sources */, BD6BDB7E29FBC1360006B04A /* EventsHelper.swift in Sources */, BDE4685F2A5BD72000BFA976 /* EventBatchCreator.swift in Sources */, - BDE4684B2A5BD72000BFA976 /* FrameCollector.swift in Sources */, BD6BDB8C29FBC1360006B04A /* Tracker.swift in Sources */, BDE468732A5BD72000BFA976 /* TableDefinable.swift in Sources */, - BDE4685D2A5BD72000BFA976 /* WSEngine.swift in Sources */, BDE4682F2A5BD72000BFA976 /* EventRequest.pb.swift in Sources */, - BDE4683B2A5BD72000BFA976 /* TCPTransport.swift in Sources */, BD6BDB8029FBC1360006B04A /* UITableViewExtensions.swift in Sources */, BD6BDB9429FBC1360006B04A /* HealthMeta.pb.swift in Sources */, BD6BDB9629FBC1360006B04A /* Health.pb.swift in Sources */, BDE467FD2A5BD72000BFA976 /* NetworkManagerDependencies.swift in Sources */, BDE4680B2A5BD72000BFA976 /* DeviceStatusNotifier.swift in Sources */, BDE468292A5BD72000BFA976 /* Event.swift in Sources */, - BDE468432A5BD72000BFA976 /* Server.swift in Sources */, BDE4680D2A5BD72000BFA976 /* NetworkReachability.swift in Sources */, - BDE468472A5BD72000BFA976 /* FoundationHTTPHandler.swift in Sources */, BDE4682B2A5BD72000BFA976 /* ClickstreamEvent.swift in Sources */, BDE468312A5BD72000BFA976 /* EteExperiment.pb.swift in Sources */, BDE468152A5BD72000BFA976 /* EventRequest.swift in Sources */, @@ -1414,39 +1257,31 @@ BDE468692A5BD72000BFA976 /* EventBatchSizeRegulator.swift in Sources */, BDE468792A5BD72000BFA976 /* EventProcessor.swift in Sources */, BDE468252A5BD72000BFA976 /* Logger.swift in Sources */, - BDE4683F2A5BD72000BFA976 /* FoundationSecurity.swift in Sources */, - BDE468592A5BD72000BFA976 /* Engine.swift in Sources */, BDE468352A5BD72000BFA976 /* Event.pb.swift in Sources */, - BDE4686D2A5BD72000BFA976 /* SortedArray.swift in Sources */, BDE4687B2A5BD72000BFA976 /* EventProcessorDependencies.swift in Sources */, BD6BDB7A29FBC1360006B04A /* EventDetailsViewController.swift in Sources */, BD6BDB9229FBC1360006B04A /* AppVersionChecker.swift in Sources */, BDE4681D2A5BD72000BFA976 /* ClickStreamEventClassification.swift in Sources */, BDE468612A5BD72000BFA976 /* EventSchedulerDependencies.swift in Sources */, BD6BDB8629FBC1360006B04A /* HealthTracker.swift in Sources */, - BDE468552A5BD72000BFA976 /* WSCompression.swift in Sources */, BDE4680F2A5BD72000BFA976 /* DispatchSourceTimer+RepeatingTimer.swift in Sources */, BD6BDB8429FBC1360006B04A /* CollectionMapper.swift in Sources */, - BDE4683D2A5BD72000BFA976 /* Security.swift in Sources */, BDE4686B2A5BD72000BFA976 /* SchedulerService.swift in Sources */, BDE468032A5BD72000BFA976 /* NetworkService.swift in Sources */, BDE468072A5BD72000BFA976 /* NetworkConfiguration.swift in Sources */, + 9A0BBA8A2BC3C98000F7DC01 /* SortedArray.swift in Sources */, BDE4686F2A5BD72000BFA976 /* DatabaseHandler.swift in Sources */, - BDE4684D2A5BD72000BFA976 /* StringHTTPHandler.swift in Sources */, BDE468632A5BD72000BFA976 /* EventWarehouser.swift in Sources */, BDE468052A5BD72000BFA976 /* Connectable.swift in Sources */, BDE467FB2A5BD72000BFA976 /* RetryMechanism.swift in Sources */, BDE468192A5BD72000BFA976 /* ClickstreamDependencies.swift in Sources */, BDE468772A5BD72000BFA976 /* EventClassifier.swift in Sources */, - BDE468532A5BD72000BFA976 /* Compression.swift in Sources */, BDE468712A5BD72000BFA976 /* DatabaseDAO.swift in Sources */, - BDE468512A5BD72000BFA976 /* Framer.swift in Sources */, BD6BDB8229FBC1360006B04A /* EventStateViewable.swift in Sources */, BD6BDB6E29FBC1360006B04A /* EventsListingTableViewCell.swift in Sources */, - BDE4684F2A5BD72000BFA976 /* FoundationHTTPServerHandler.swift in Sources */, - BDE468392A5BD72000BFA976 /* FoundationTransport.swift in Sources */, BDE468672A5BD72000BFA976 /* AppStateNotifierService.swift in Sources */, BD6BDB8829FBC1360006B04A /* HealthAnalysisEvent.swift in Sources */, + 9A0BBA882BC3A59300F7DC01 /* URLRequest+Equality.swift in Sources */, BD6BDB6C29FBC1360006B04A /* FilterTextFieldTableViewCell.swift in Sources */, BDE467FF2A5BD72000BFA976 /* Heartbeat.swift in Sources */, BDE468652A5BD72000BFA976 /* EventBatchProcessor.swift in Sources */, @@ -1455,8 +1290,6 @@ BDDC872229E6AF580024ED8C /* FileManagerOverride.swift in Sources */, BD6BDB7C29FBC1360006B04A /* FilterViewController.swift in Sources */, BDE468112A5BD72000BFA976 /* KeepAliveService.swift in Sources */, - BDE468412A5BD72000BFA976 /* Data+Extensions.swift in Sources */, - BDE4685B2A5BD72000BFA976 /* NativeEngine.swift in Sources */, BD6BDB8E29FBC1360006B04A /* TrackerConstants.swift in Sources */, BDE468332A5BD72000BFA976 /* EventResponse.pb.swift in Sources */, BDE4681B2A5BD72000BFA976 /* ClickStreamConstraints.swift in Sources */, @@ -1469,7 +1302,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - BDE468542A5BD72000BFA976 /* Compression.swift in Sources */, + 9A0BBA892BC3A59300F7DC01 /* URLRequest+Equality.swift in Sources */, BD6BDB9F29FBC1360006B04A /* CSCommonPropertiesDTO.swift in Sources */, BDE467752A5BD24500BFA976 /* BatchSizeRegulatorMock.swift in Sources */, BDE4680E2A5BD72000BFA976 /* NetworkReachability.swift in Sources */, @@ -1479,60 +1312,44 @@ BDE4686C2A5BD72000BFA976 /* SchedulerService.swift in Sources */, BDE467742A5BD24500BFA976 /* AppStateNotifierMock.swift in Sources */, BD6BDB7729FBC1360006B04A /* EventsListViewModel.swift in Sources */, - BDE468582A5BD72000BFA976 /* WebSocket.swift in Sources */, BDE468342A5BD72000BFA976 /* EventResponse.pb.swift in Sources */, BDE468682A5BD72000BFA976 /* AppStateNotifierService.swift in Sources */, BDE468122A5BD72000BFA976 /* KeepAliveService.swift in Sources */, BD6BDB9929FBC1360006B04A /* CSLocationDTO.swift in Sources */, - BDE468482A5BD72000BFA976 /* FoundationHTTPHandler.swift in Sources */, BDE4682A2A5BD72000BFA976 /* Event.swift in Sources */, BDE4676F2A5BD24500BFA976 /* MockConstants.swift in Sources */, BDE468022A5BD72000BFA976 /* SocketHandler.swift in Sources */, BD6BDB6F29FBC1360006B04A /* EventsListingTableViewCell.swift in Sources */, - BDE468502A5BD72000BFA976 /* FoundationHTTPServerHandler.swift in Sources */, - BDE468382A5BD72000BFA976 /* Transport.swift in Sources */, BDE4677A2A5BD24500BFA976 /* DispatchQueue+Detection.swift in Sources */, BD6BDB9B29FBC1360006B04A /* ClickStreamHealthConfigurations.swift in Sources */, - BDE468422A5BD72000BFA976 /* Data+Extensions.swift in Sources */, BDE467882A5BD24500BFA976 /* DeviceStatusNotifierTests.swift in Sources */, BDE467852A5BD24500BFA976 /* NetworkManagerDependenciesTests.swift in Sources */, BDE467822A5BD24500BFA976 /* EventProcessorDependenciesTests.swift in Sources */, BDE467872A5BD24500BFA976 /* KeepAliveServiceTests.swift in Sources */, BDE4686E2A5BD72000BFA976 /* SortedArray.swift in Sources */, BDE468162A5BD72000BFA976 /* EventRequest.swift in Sources */, - BDE4683C2A5BD72000BFA976 /* TCPTransport.swift in Sources */, BD6BDB8929FBC1360006B04A /* HealthAnalysisEvent.swift in Sources */, BDE467782A5BD24500BFA976 /* EventSchedulerDependenciesTests.swift in Sources */, BDE4681E2A5BD72000BFA976 /* ClickStreamEventClassification.swift in Sources */, BDE467722A5BD24500BFA976 /* EventBatchTests.swift in Sources */, BDE468642A5BD72000BFA976 /* EventWarehouser.swift in Sources */, - BDE468462A5BD72000BFA976 /* WebSocketServer.swift in Sources */, - BDE4685C2A5BD72000BFA976 /* NativeEngine.swift in Sources */, - BDE4685A2A5BD72000BFA976 /* Engine.swift in Sources */, BD6BDB9129FBC1360006B04A /* Notifiable.swift in Sources */, BD6BDB9729FBC1360006B04A /* Health.pb.swift in Sources */, - BDE4684E2A5BD72000BFA976 /* StringHTTPHandler.swift in Sources */, BD6BDB7B29FBC1360006B04A /* EventDetailsViewController.swift in Sources */, BDE467702A5BD24500BFA976 /* ClickStreamTests.swift in Sources */, - BDE4684C2A5BD72000BFA976 /* FrameCollector.swift in Sources */, - BDE468562A5BD72000BFA976 /* WSCompression.swift in Sources */, BDE468622A5BD72000BFA976 /* EventSchedulerDependencies.swift in Sources */, BDE467842A5BD24500BFA976 /* SocketHandlerMock.swift in Sources */, BD6BDB8529FBC1360006B04A /* CollectionMapper.swift in Sources */, - BDE468402A5BD72000BFA976 /* FoundationSecurity.swift in Sources */, BDE4681A2A5BD72000BFA976 /* ClickstreamDependencies.swift in Sources */, BDE467772A5BD24500BFA976 /* EventBatchProcessorTests.swift in Sources */, - BDE468442A5BD72000BFA976 /* Server.swift in Sources */, BDE468182A5BD72000BFA976 /* ClickStream.swift in Sources */, BDE468322A5BD72000BFA976 /* EteExperiment.pb.swift in Sources */, BD6BDB7F29FBC1360006B04A /* EventsHelper.swift in Sources */, BDE468002A5BD72000BFA976 /* Heartbeat.swift in Sources */, BDE468262A5BD72000BFA976 /* Logger.swift in Sources */, - BDE4683E2A5BD72000BFA976 /* Security.swift in Sources */, BDE468242A5BD72000BFA976 /* JSONStringDecoder.swift in Sources */, BDE4687A2A5BD72000BFA976 /* EventProcessor.swift in Sources */, BDE468042A5BD72000BFA976 /* NetworkService.swift in Sources */, - BDE4683A2A5BD72000BFA976 /* FoundationTransport.swift in Sources */, BDE468142A5BD72000BFA976 /* ProtoConvertible.swift in Sources */, BDE468282A5BD72000BFA976 /* DeviceInfo.swift in Sources */, BDE4677B2A5BD24500BFA976 /* SchedulerServiceTests.swift in Sources */, @@ -1541,7 +1358,6 @@ BDE468202A5BD72000BFA976 /* Constants.swift in Sources */, BDE467712A5BD24500BFA976 /* ClickStreamDependenciesTests.swift in Sources */, BDE467892A5BD24500BFA976 /* NetworkBuilderTests.swift in Sources */, - BDE4684A2A5BD72000BFA976 /* HTTPHandler.swift in Sources */, BD6BDB8D29FBC1360006B04A /* Tracker.swift in Sources */, BDE467812A5BD24500BFA976 /* EventProcessorTest.swift in Sources */, BD6BDB9329FBC1360006B04A /* AppVersionChecker.swift in Sources */, @@ -1581,10 +1397,8 @@ BDE468082A5BD72000BFA976 /* NetworkConfiguration.swift in Sources */, BDE468302A5BD72000BFA976 /* EventRequest.pb.swift in Sources */, BD6BDB9529FBC1360006B04A /* HealthMeta.pb.swift in Sources */, - BDE468522A5BD72000BFA976 /* Framer.swift in Sources */, BD6BDB7329FBC1360006B04A /* EventVisualizerLandingViewController.swift in Sources */, BDE467FE2A5BD72000BFA976 /* NetworkManagerDependencies.swift in Sources */, - BDE4685E2A5BD72000BFA976 /* WSEngine.swift in Sources */, BDE467762A5BD24500BFA976 /* DatabaseMock.swift in Sources */, BDDC872329E6AF580024ED8C /* FileManagerOverride.swift in Sources */, BD6BDB8129FBC1360006B04A /* UITableViewExtensions.swift in Sources */, @@ -1725,6 +1539,7 @@ baseConfigurationReference = CA0F06B342B881F06295F6E3 /* Pods-Clickstream.debug.xcconfig */; buildSettings = { CODE_SIGN_STYLE = Automatic; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -1739,6 +1554,7 @@ baseConfigurationReference = 47E59ADC19B15059C977CB9C /* Pods-Clickstream.release.xcconfig */; buildSettings = { CODE_SIGN_STYLE = Automatic; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -1813,6 +1629,7 @@ baseConfigurationReference = C7CD1A2E08F13591955370E3 /* Pods-Clickstream.integration.xcconfig */; buildSettings = { CODE_SIGN_STYLE = Automatic; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; ONLY_ACTIVE_ARCH = NO; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1883,6 +1700,7 @@ baseConfigurationReference = D982B8160D113D472BF2C8E0 /* Pods-Clickstream.production.xcconfig */; buildSettings = { CODE_SIGN_STYLE = Automatic; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -1953,6 +1771,7 @@ baseConfigurationReference = E083FF5B9C4808DB73B36B32 /* Pods-Clickstream.alpha.xcconfig */; buildSettings = { CODE_SIGN_STYLE = Automatic; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; @@ -1970,6 +1789,7 @@ CODE_SIGN_STYLE = Manual; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = ClickstreamTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -1993,6 +1813,7 @@ CODE_SIGN_STYLE = Manual; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = ClickstreamTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2016,6 +1837,7 @@ CODE_SIGN_STYLE = Manual; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = ClickstreamTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2039,6 +1861,7 @@ CODE_SIGN_STYLE = Manual; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = ClickstreamTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -2062,6 +1885,7 @@ CODE_SIGN_STYLE = Manual; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = ClickstreamTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/ClickstreamTests/NetworkManagerTests/Infrastructure/RetryMechanismTests.swift b/ClickstreamTests/NetworkManagerTests/Infrastructure/RetryMechanismTests.swift index 5fee240..7836857 100644 --- a/ClickstreamTests/NetworkManagerTests/Infrastructure/RetryMechanismTests.swift +++ b/ClickstreamTests/NetworkManagerTests/Infrastructure/RetryMechanismTests.swift @@ -36,17 +36,17 @@ class RetryMechanismTests: XCTestCase { let deviceStatus = DefaultDeviceStatus(performOnQueue: mockQueue) let networkService = DefaultNetworkService(with: config, performOnQueue: .main) let persistence = DefaultDatabaseDAO(database: database, performOnQueue: dbQueueMock) - let keepAliveService = DefaultKeepAliveService(with: mockQueue, duration: 2, reachability: NetworkReachabilityMock(isReachable: false)) - + let keepAliveService = DefaultKeepAliveServiceWithSafeTimer(with: mockQueue, duration: 2, reachability: NetworkReachabilityMock(isReachable: false)) + //when let sut = DefaultRetryMechanism(networkService: networkService, reachability: NetworkReachabilityMock(isReachable: true), deviceStatus: deviceStatus, appStateNotifier: AppStateNotifierMock(state: .didBecomeActive), performOnQueue: mockQueue, persistence: persistence, keepAliveService: keepAliveService) persistence.deleteAll() - + let mockEventRequest: EventRequest = EventRequest(guid: UUID().uuidString, data: Data()) sut.trackBatch(with: mockEventRequest) - + DispatchQueue.global(qos: .utility).asyncAfter(deadline: .now() + 3) { if (mockEventRequest.eventType != .instant), let fetchedRequest = persistence.fetchAll()?.first { if fetchedRequest.retriesMade > 0 { @@ -56,7 +56,7 @@ class RetryMechanismTests: XCTestCase { } //then - wait(for: [expectation], timeout: 5.0) + wait(for: [expectation], timeout: 10.0) } func test_whenTheMaxRetriesAreReached_thenTheBatchMustGetRemovedFromTheCache() { diff --git a/ClickstreamTests/NetworkManagerTests/Mocks/SocketHandlerMock.swift b/ClickstreamTests/NetworkManagerTests/Mocks/SocketHandlerMock.swift index d8e5a60..2ed68e2 100644 --- a/ClickstreamTests/NetworkManagerTests/Mocks/SocketHandlerMock.swift +++ b/ClickstreamTests/NetworkManagerTests/Mocks/SocketHandlerMock.swift @@ -17,10 +17,20 @@ enum SocketConnectionState { } final class SocketHandlerMockSuccess: SocketHandler { - static var state: SocketConnectionState = .successWithData - private let connectionCallback: ConnectionStatus? + init(performOnQueue: SerialQueue) { + + } + + func setup(request: URLRequest, keepTrying: Bool, connectionCallback: ConnectionStatus?) { + self.connectionCallback = connectionCallback + SerialQueue.main.asyncAfter(deadline: .now() + 0.5) { + connectionCallback?(.success(.connected))//change this. -AV + } + } + + private var connectionCallback: ConnectionStatus? func sendPing(_ data: Data) { } @@ -50,7 +60,7 @@ final class SocketHandlerMockSuccess: SocketHandler { connectionCallback?(.success(.disconnected)) } - var isConnected: Bool { - true + var isConnected: Atomic { + return Atomic(true) } } diff --git a/Example/Example.xcodeproj/project.pbxproj b/Example/Example.xcodeproj/project.pbxproj index 571eb52..4d4257c 100644 --- a/Example/Example.xcodeproj/project.pbxproj +++ b/Example/Example.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -167,6 +167,7 @@ 9AEA264824924B340018931D /* Sources */, 9AEA264924924B340018931D /* Frameworks */, 9AEA264A24924B340018931D /* Resources */, + 3F6D58C8F488CB53E6EFC4AD /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -225,6 +226,23 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 3F6D58C8F488CB53E6EFC4AD /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Example/Pods-Example-resources-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Example/Pods-Example-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Example/Pods-Example-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; 685A269624A9E10A001B58D0 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -429,7 +447,7 @@ CODE_SIGN_STYLE = Manual; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = Example/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -461,7 +479,7 @@ CODE_SIGN_STYLE = Manual; DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = Example/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/Podfile b/Podfile index a7113de..e98ba83 100644 --- a/Podfile +++ b/Podfile @@ -2,7 +2,7 @@ source 'https://github.com/CocoaPods/Specs.git' -platform :ios, '11.0' +platform :ios, '12.0' use_modular_headers! project 'Clickstream.xcodeproj' @@ -12,6 +12,7 @@ def clickstream_pods pod 'SwiftProtobuf', '~> 1.10' pod 'ReachabilitySwift', '>= 5.0.0' pod 'GRDB.swift', '~> 6.7' + pod 'Starscream', '~> 4.0.6' end target 'Clickstream' do diff --git a/Podfile.lock b/Podfile.lock index a802d02..3545478 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -3,24 +3,28 @@ PODS: - GRDB.swift/standard (= 6.24.1) - GRDB.swift/standard (6.24.1) - ReachabilitySwift (5.0.0) + - Starscream (4.0.8) - SwiftProtobuf (1.25.2) DEPENDENCIES: - GRDB.swift (~> 6.7) - ReachabilitySwift (>= 5.0.0) + - Starscream (~> 4.0.6) - SwiftProtobuf (~> 1.10) SPEC REPOS: https://github.com/CocoaPods/Specs.git: - GRDB.swift - ReachabilitySwift + - Starscream - SwiftProtobuf SPEC CHECKSUMS: GRDB.swift: 136dcb5d8dddca50aae3ba7d77475f79e7232cd8 ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825 + Starscream: 19b5533ddb925208db698f0ac508a100b884a1b9 SwiftProtobuf: 407a385e97fd206c4fbe880cc84123989167e0d1 -PODFILE CHECKSUM: f24f9da678cf7c77fd3275d27f766a88682ecadf +PODFILE CHECKSUM: 12f0fcd6592ee203f8b3339e7ec80ee9771693c0 COCOAPODS: 1.15.2 diff --git a/Sources/Clickstream/NetworkManager/Infrastructure/NetworkService/Connectable.swift b/Sources/Clickstream/NetworkManager/Infrastructure/NetworkService/Connectable.swift index fbf3b37..403a8c3 100644 --- a/Sources/Clickstream/NetworkManager/Infrastructure/NetworkService/Connectable.swift +++ b/Sources/Clickstream/NetworkManager/Infrastructure/NetworkService/Connectable.swift @@ -42,11 +42,8 @@ protocol ConnectableInputs { /// Initializer /// - Parameters: - /// - request: URLRequest which the connectable must connect to. - /// - keepTrying: A control flag which tells the connectable to keep trying till the connection is not established. /// - performOnQueue: A queue instance on which the tasks are performed. - /// - connectionCallback: A callback to update about the connection status. - init(request: URLRequest, keepTrying: Bool, performOnQueue: SerialQueue, connectionCallback: ConnectionStatus?) + init(performOnQueue: SerialQueue) /// Writes data to the stream. @@ -58,13 +55,20 @@ protocol ConnectableInputs { /// Disconnects the connection. func disconnect() - // func connect(with request: URLRequest, _ completion: @escaping ((Result<[String: String]?, ConnectionError>) -> Void)) + /// Sets up a connectable + /// - Parameters: + /// - request: URLRequest which the connectable must connect to. + /// - keepTrying: A control flag which tells the connectable to keep trying till the connection is not established. + /// - connectionCallback: A callback to update about the connection status. + func setup(request: URLRequest, + keepTrying: Bool, + connectionCallback: ConnectionStatus?) } protocol ConnectableOutputs { /// Returns the connection state. - var isConnected: Bool { get } + var isConnected: Atomic { get } } protocol Connectable: ConnectableInputs, ConnectableOutputs { } diff --git a/Sources/Clickstream/NetworkManager/Infrastructure/NetworkService/NetworkService.swift b/Sources/Clickstream/NetworkManager/Infrastructure/NetworkService/NetworkService.swift index 656a1a7..3811c1a 100755 --- a/Sources/Clickstream/NetworkManager/Infrastructure/NetworkService/NetworkService.swift +++ b/Sources/Clickstream/NetworkManager/Infrastructure/NetworkService/NetworkService.swift @@ -15,8 +15,8 @@ protocol NetworkServiceInputs { /// - Parameters: /// - connectionStatusListener: A callback to listen to the change in the status. /// - keepTrying: allow connectable to try reconnection exponentially - @discardableResult func initiateConnection(connectionStatusListener: ConnectionStatus?, - keepTrying: Bool) -> Connectable? + func initiateConnection(connectionStatusListener: ConnectionStatus?, + keepTrying: Bool) /// Writes data to the given connectable and fires a completion event after the write is completed. /// - Parameters: @@ -77,15 +77,14 @@ final class DefaultNetworkService: NetworkService { extension DefaultNetworkService { func initiateConnection(connectionStatusListener: ConnectionStatus?, - keepTrying: Bool = false) -> Connectable? { - guard _connectable == nil else { return self._connectable } + keepTrying: Bool = false) { + guard _connectable == nil else { return } self.connectionCallback = connectionStatusListener let request = networkConfig.request - _connectable = C(request: request, - keepTrying: false, - performOnQueue: performQueue, - connectionCallback: self.connectionCallback) - return _connectable + _connectable = C(performOnQueue: performQueue) + connectable?.setup(request: request, + keepTrying: false, + connectionCallback: self.connectionCallback) } func write(_ data: Data, completion: @escaping (Result) -> Void) where T : Message { @@ -129,6 +128,6 @@ extension DefaultNetworkService { extension DefaultNetworkService { var isConnected: Bool { - _connectable?.isConnected ?? false + _connectable?.isConnected.value ?? false } } diff --git a/Sources/Clickstream/NetworkManager/Infrastructure/Sockets/SocketHandler.swift b/Sources/Clickstream/NetworkManager/Infrastructure/Sockets/SocketHandler.swift index 0e2a4c0..dbed456 100644 --- a/Sources/Clickstream/NetworkManager/Infrastructure/Sockets/SocketHandler.swift +++ b/Sources/Clickstream/NetworkManager/Infrastructure/Sockets/SocketHandler.swift @@ -1,6 +1,6 @@ // // SocketHandler.swift -// Clickstream +// ClickStream // // Created by Anirudh Vyas on 21/04/20. // Copyright © 2020 Gojek. All rights reserved. @@ -8,46 +8,69 @@ import Foundation import Reachability +import Starscream -protocol SocketHandler: Connectable, HeartBeatable { } +protocol SocketHandler: Connectable { } final class DefaultSocketHandler: SocketHandler { - + + + /// Used as a websocket callback queue. + private let performQueue: SerialQueue + + /// States whether the socket request is open. + private var isConnectionRequestOpen: Bool = false + + /// Refers to the `URLRequest` for setting up a socket connection + private var request: URLRequest? + + /// Callback for the socket status + private var connectionCallback: ConnectionStatus? + + /// Holds the number of retries made for negotiating a connection. private static var retries = 0 - private var open: Bool - private var connected: Bool - private let request: URLRequest - private let connectionCallback: ConnectionStatus? + + /// Websocket. private var webSocket: WebSocket? - private let mutex = NSLock() - private var pingTimer: DispatchSourceTimer? + + /// Callback for a socket `write` action private var writeCallback: ((Result) -> Void)? - private let performQueue: SerialQueue - private var isRetryInProgress: Bool = false + + /// Tracking time taken by the socket to establish a connection + /// TODO:Abhijeet to fix +// private var socketConnectionTimeTrace: Trace = Trace(name: ClickStreamDebugConstants.Traces.ClickstreamSocketConnectionTime.rawValue) + + /// Provides the socket state + var isConnected: Atomic = Atomic(false) + /// Records the time stamp for the last connection request made private var lastConnectRequestTimestamp: Date? - - var isConnected: Bool { - get { - mutex.lock() - let isConnected = connected - mutex.unlock() - return isConnected - } + + /// Custom Socket Handler initialiser + /// - Parameter performOnQueue: Queue on which a socket performs actions + init(performOnQueue: SerialQueue) { + self.performQueue = performOnQueue + DefaultSocketHandler.retries = 0 } - init(request: URLRequest, - keepTrying: Bool, - performOnQueue: SerialQueue, - connectionCallback: ConnectionStatus?) { - self.open = false - self.connected = false - self.performQueue = performOnQueue - self.request = request - self.connectionCallback = connectionCallback + /// Attempt at making a connection + /// - Parameters: + /// - request: URLRequest for setting up socket connection + /// - keepTrying: Suggests if the connection attempts should tried multiple times + /// - connectionCallback: Connection callback closure provides with the state of the socket as `ConnectionStatus` + func setup(request: URLRequest, + keepTrying: Bool, + connectionCallback: ConnectionStatus?) { + guard self.isConnectionRequestOpen == false || !request.isEqual(to: self.request) else { return } + self.isConnected.mutate { isConnected in + isConnected = false + } + self.connectionCallback = connectionCallback + self.request = request webSocket = WebSocket(request: request) webSocket?.callbackQueue = performQueue + webSocket?.respondToPingWithPong = true // Add socket event listener addSocketEventListener() // Negotiate connection @@ -59,81 +82,82 @@ final class DefaultSocketHandler: SocketHandler { negotiateConnection(initiate: true) } } - + /// Negotiates a connection, by calling the /// - Parameters: /// - initiate: A control flag to control whether the negotiation should de initiated or not. /// - maxInterval: A given max interval for the retries. /// - maxRetries: A given number of max retry attempts - private func negotiateConnection(initiate: Bool, maxInterval: TimeInterval = 0.0, maxRetries: Int = 0) { + private func negotiateConnection(initiate: Bool, + maxInterval: TimeInterval = 0.0, + maxRetries: Int = 0) { - if connected || DefaultSocketHandler.retries > maxRetries { - isRetryInProgress = false - DefaultSocketHandler.retries = 0 // Reset to zero for further negotiation calls. + if isConnected.value || DefaultSocketHandler.retries > maxRetries { + // Exit Condition. + // Reset to zero for further negotiation calls. + DefaultSocketHandler.retries = 0 return } else if initiate || DefaultSocketHandler.retries > 0 { - if !open { - lastConnectRequestTimestamp = Date() - print("socket-connecting", .verbose) + if !isConnectionRequestOpen || + Date().timeIntervalSince(lastConnectRequestTimestamp ?? Date()) > self.request?.timeoutInterval ?? 60 { Clickstream.connectionState = .connecting + print("socket-connecting") + isConnectionRequestOpen = true connectionCallback?(.success(.connecting)) +// socketConnectionTimeTrace.attributes = [Constants.Strings.networkType: Reachability.getNetworkType().trackingId] +// socketConnectionTimeTrace.start() webSocket?.connect() + lastConnectRequestTimestamp = Date() // recording time } } if DefaultSocketHandler.retries < maxRetries { performQueue.asyncAfter(deadline: .now() + - min(pow(Constants.Defaults.coefficientOfConnectionRetries*connectionRetryCoefficient, - Double(DefaultSocketHandler.retries+1)), maxInterval)) { + min(pow(Constants.Defaults.coefficientOfConnectionRetries*connectionRetryCoefficient, + Double(DefaultSocketHandler.retries)), maxInterval)) { [weak self] in guard let checkedSelf = self else { return } - checkedSelf.negotiateConnection(initiate: false, maxInterval: maxInterval, maxRetries: maxRetries) + checkedSelf.negotiateConnection(initiate: false, + maxInterval: maxInterval, + maxRetries: maxRetries) } DefaultSocketHandler.retries += 1 } } deinit { - disconnect() - stopPing() - webSocket?.delegate = nil +// socketConnectionTimeTrace.attributes = [Constants.Strings.networkType: Reachability.getNetworkType().trackingId, +// Constants.Strings.status: Constants.Strings.failure] +// socketConnectionTimeTrace.stop() + print("socket-deinit") } } extension DefaultSocketHandler { - - func write(_ data: Data, completion: @escaping ((Result) -> Void)) { + + /// Writing data on the socket connection. + /// - Parameters: + /// - data: data to be returned + /// - completion: completion callback + func write(_ data: Data, + completion: @escaping ((Result) -> Void)) { webSocket?.write(data: data) self.writeCallback = completion } + /// Call this to disconnect from the socket. func disconnect() { - stopPing() - Clickstream.connectionState = .closed + print("socket-disconnect") webSocket?.disconnect(closeCode: CloseCode.normal.rawValue) - } -} - -extension DefaultSocketHandler { - func sendPing(_ data: Data) { - guard pingTimer == nil else { - return - } - pingTimer = DispatchSource.makeTimerSource(flags: .strict) - pingTimer?.schedule(deadline: .now() + Clickstream.configurations.maxPingInterval, - repeating: Clickstream.configurations.maxPingInterval) - pingTimer?.setEventHandler(handler: { [weak self] in - self?.keepAlive(data) - }) - pingTimer?.resume() - } - - func stopPing() { - pingTimer?.cancel() - pingTimer = nil + Clickstream.connectionState = .closed + reset() } - private func keepAlive(_ data: Data) { - if open { - webSocket?.write(ping: data) + /// Resets the state variables and socket event listeners. + private func reset() { + print("socket-reset-connection") + isConnectionRequestOpen = false + webSocket?.onEvent = nil + self.isConnected.mutate { isConnected in + isConnected = false } } } @@ -142,88 +166,61 @@ extension DefaultSocketHandler { // swiftlint:disable:next cyclomatic_complexity private func addSocketEventListener() { webSocket?.onEvent = { [weak self] event in guard let checkedSelf = self else { return } + print("socket-onEvent: \(event)") switch event { case .connected: - print("connected",.critical) Clickstream.connectionState = .connected - checkedSelf.connected = true - checkedSelf.isRetryInProgress = false - checkedSelf.sendPing(Data()) - checkedSelf.connectionCallback?(.success(.connected)) - #if TRACKER_ENABLED - if Tracker.debugMode { - let timeInterval = Date().timeIntervalSince(checkedSelf.lastConnectRequestTimestamp ?? Date()) - let event = HealthAnalysisEvent(eventName: .ClickstreamConnectionSuccess, - timeToConnection: ("\(timeInterval)")) - Tracker.sharedInstance?.record(event: event) + checkedSelf.isConnected.mutate { isConnected in + isConnected = true } - #endif - case .disconnected(let error, let code): - // DuplicateID Error - print("disconnected with error: \(error) errorCode: \(code)", .critical) + checkedSelf.isConnectionRequestOpen = false + checkedSelf.connectionCallback?(.success(.connected)) +// checkedSelf.socketConnectionTimeTrace.attributes = [Constants.Strings.networkType: Reachability.getNetworkType().trackingId, +// Constants.Strings.status: Constants.Strings.success] +// checkedSelf.socketConnectionTimeTrace.stop() + case .disconnected(_, let code): Clickstream.connectionState = .closed - checkedSelf.open = false - checkedSelf.stopPing() - #if TRACKER_ENABLED - if Tracker.debugMode { - checkedSelf.trackHealthEvent(eventName: .ClickstreamConnectionDropped, code: code) - } - #endif +// checkedSelf.trackHealthEvent(eventName: .ClickstreamConnectionFailed, code: code) + checkedSelf.isConnectionRequestOpen = false +// checkedSelf.socketConnectionTimeTrace.attributes = [Constants.Strings.networkType: Reachability.getNetworkType().trackingId, +// Constants.Strings.status: Constants.Strings.failure] +// checkedSelf.socketConnectionTimeTrace.stop() case .text(let responseString): checkedSelf.writeCallback?(.success(responseString.data(using: .utf8))) case .error(let error): - if let error = error { - print("error \(error)", .verbose) - } - if error.debugDescription.contains(Constants.Strings.connectionError) { - checkedSelf.open = false - checkedSelf.stopPing() - checkedSelf.retryConnection() - } + checkedSelf.isConnectionRequestOpen = false + // checkedSelf.socketConnectionTimeTrace.attributes = [Constants.Strings.networkType: Reachability.getNetworkType().trackingId, + // Constants.Strings.status: Constants.Strings.failure] + // checkedSelf.socketConnectionTimeTrace.stop() checkedSelf.writeCallback?(.failure(.failed)) - #if TRACKER_ENABLED + + #if TRACKER_ENABLED if Tracker.debugMode { let timeInterval = Date().timeIntervalSince(checkedSelf.lastConnectRequestTimestamp ?? Date()) self?.trackHealthEvent(eventName: .ClickstreamConnectionFailure, error: error, timeToConnection: ("\(timeInterval)")) } - #endif - + #endif case .binary(let response): checkedSelf.writeCallback?(.success(response)) - case .pong: - print("pong", .verbose) case .cancelled: - print("cancelled", .verbose) Clickstream.connectionState = .failed - checkedSelf.open = false - checkedSelf.connected = false - checkedSelf.stopPing() + checkedSelf.retryConnection() checkedSelf.connectionCallback?(.success(.cancelled)) - case .ping: - print("ping", .verbose) case .viabilityChanged(let status): - checkedSelf.open = status - case .reconnectSuggested(let status): - print("reconnectSuggested", .verbose) - if status { - checkedSelf.open = false - checkedSelf.stopPing() - checkedSelf.retryConnection() - } - case .timedOut: - checkedSelf.open = false + checkedSelf.isConnectionRequestOpen = status + case .peerClosed: + checkedSelf.isConnectionRequestOpen = false + default: + break } } } + /// Retries socket connection when `.cancelled` state of the socket is received. private func retryConnection() { - print("retryingConnection") - if isRetryInProgress { - return - } - if connected { - connected = false - connectionCallback?(.success(.disconnected)) + isConnectionRequestOpen = false + isConnected.mutate { isConnected in + isConnected = false } } } @@ -248,35 +245,34 @@ extension SocketHandler { } } -// MARK: - Track Clickstream health. extension DefaultSocketHandler { - #if TRACKER_ENABLED +#if TRACKER_ENABLED func trackHealthEvent(eventName: HealthEvents, - error: Error? = nil, code: UInt16? = nil, timeToConnection: String? = nil) { + error: Error? = nil, + code: UInt16? = nil, + timeToConnection: String? = nil) { + guard Tracker.debugMode else { return } - if let error = error { - if case HTTPUpgradeError.notAnUpgrade(let code) = error { - if code == 401 { - let event = HealthAnalysisEvent(eventName: eventName, - reason: FailureReason.AuthenticationError.rawValue, timeToConnection: timeToConnection) - Tracker.sharedInstance?.record(event: event) - } else if code == 1008 { - let event = HealthAnalysisEvent(eventName: eventName, - reason: FailureReason.DuplicateID.rawValue, timeToConnection: timeToConnection) - Tracker.sharedInstance?.record(event: event) - } else { - let event = HealthAnalysisEvent(eventName: eventName, - reason: error.localizedDescription, timeToConnection: timeToConnection) - Tracker.sharedInstance?.record(event: event) - } + if let error = error, case HTTPUpgradeError.notAnUpgrade(let code) = error { + + if code.0 == 401 { + let event = HealthAnalysisEvent(eventName: eventName, + reason: FailureReason.AuthenticationError.rawValue) + Tracker.sharedInstance?.record(event: event) + } else if code.0 == 1008 { + let event = HealthAnalysisEvent(eventName: eventName, + reason: FailureReason.DuplicateID.rawValue) + Tracker.sharedInstance?.record(event: event) } else { - + let event = HealthAnalysisEvent(eventName: eventName, + reason: error.localizedDescription) + Tracker.sharedInstance?.record(event: event) } } else if code == 1008 { let event = HealthAnalysisEvent(eventName: eventName, - reason: FailureReason.DuplicateID.rawValue, timeToConnection: timeToConnection) + reason: FailureReason.DuplicateID.rawValue) Tracker.sharedInstance?.record(event: event) } } - #endif +#endif } diff --git a/Sources/Clickstream/NetworkManager/Infrastructure/Utilities/URLRequest+Equality.swift b/Sources/Clickstream/NetworkManager/Infrastructure/Utilities/URLRequest+Equality.swift new file mode 100644 index 0000000..967f0c5 --- /dev/null +++ b/Sources/Clickstream/NetworkManager/Infrastructure/Utilities/URLRequest+Equality.swift @@ -0,0 +1,23 @@ +// +// URLRequest+Equality.swift +// Clickstream +// +// Created by Abhijeet Mallick on 08/04/24. +// Copyright © 2024 Gojek. All rights reserved. +// + +import Foundation + +extension URLRequest { + func isEqual(to: URLRequest?) -> Bool { + guard let to = to else { + return false + } + guard let bearerSelf = self.allHTTPHeaderFields?["Authorization"] else { return false } + guard let bearerProvided = to.allHTTPHeaderFields?["Authorization"] else { return false } + if self.url == to.url && bearerSelf == bearerProvided { + return true + } + return false + } +} diff --git a/Sources/Clickstream/ThirdPartyLibraries/Sources/Compression/Compression.swift b/Sources/Clickstream/ThirdPartyLibraries/Sources/Compression/Compression.swift deleted file mode 100644 index 0e7fae5..0000000 --- a/Sources/Clickstream/ThirdPartyLibraries/Sources/Compression/Compression.swift +++ /dev/null @@ -1,29 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////////////////////// -// -// Compression.swift -// Starscream -// -// Created by Dalton Cherry on 2/4/19. -// Copyright © 2019 Vluxe. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -////////////////////////////////////////////////////////////////////////////////////////////////// - -import Foundation - -public protocol CompressionHandler { - func load(headers: [String: String]) - func decompress(data: Data, isFinal: Bool) -> Data? - func compress(data: Data) -> Data? -} diff --git a/Sources/Clickstream/ThirdPartyLibraries/Sources/Compression/WSCompression.swift b/Sources/Clickstream/ThirdPartyLibraries/Sources/Compression/WSCompression.swift deleted file mode 100644 index 2129584..0000000 --- a/Sources/Clickstream/ThirdPartyLibraries/Sources/Compression/WSCompression.swift +++ /dev/null @@ -1,247 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////////////////////// -// -// WSCompression.swift -// -// Created by Joseph Ross on 7/16/14. -// Copyright © 2017 Joseph Ross & Vluxe. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -////////////////////////////////////////////////////////////////////////////////////////////////// - -////////////////////////////////////////////////////////////////////////////////////////////////// -// -// Compression implementation is implemented in conformance with RFC 7692 Compression Extensions -// for WebSocket: https://tools.ietf.org/html/rfc7692 -// -////////////////////////////////////////////////////////////////////////////////////////////////// - -import Foundation -import zlib - -public class WSCompression: CompressionHandler { - let headerWSExtensionName = "Sec-WebSocket-Extensions" - var decompressor: Decompressor? - var compressor: Compressor? - var decompressorTakeOver = false - var compressorTakeOver = false - - public init() { - - } - - public func load(headers: [String: String]) { - guard let extensionHeader = headers[headerWSExtensionName] else { return } - decompressorTakeOver = false - compressorTakeOver = false - - let parts = extensionHeader.components(separatedBy: ";") - for p in parts { - let part = p.trimmingCharacters(in: .whitespaces) - if part.hasPrefix("server_max_window_bits=") { - let valString = part.components(separatedBy: "=")[1] - if let val = Int(valString.trimmingCharacters(in: .whitespaces)) { - decompressor = Decompressor(windowBits: val) - } - } else if part.hasPrefix("client_max_window_bits=") { - let valString = part.components(separatedBy: "=")[1] - if let val = Int(valString.trimmingCharacters(in: .whitespaces)) { - compressor = Compressor(windowBits: val) - } - } else if part == "client_no_context_takeover" { - compressorTakeOver = true - } else if part == "server_no_context_takeover" { - decompressorTakeOver = true - } - } - } - - public func decompress(data: Data, isFinal: Bool) -> Data? { - guard let decompressor = decompressor else { return nil } - do { - let decompressedData = try decompressor.decompress(data, finish: isFinal) - if decompressorTakeOver { - try decompressor.reset() - } - return decompressedData - } catch { - //do nothing with the error for now - } - return nil - } - - public func compress(data: Data) -> Data? { - guard let compressor = compressor else { return nil } - do { - let compressedData = try compressor.compress(data) - if compressorTakeOver { - try compressor.reset() - } - return compressedData - } catch { - //do nothing with the error for now - } - return nil - } - - -} - -class Decompressor { - private var strm = z_stream() - private var buffer = [UInt8](repeating: 0, count: 0x2000) - private var inflateInitialized = false - private let windowBits: Int - - init?(windowBits: Int) { - self.windowBits = windowBits - guard initInflate() else { return nil } - } - - private func initInflate() -> Bool { - if Z_OK == inflateInit2_(&strm, -CInt(windowBits), - ZLIB_VERSION, CInt(MemoryLayout.size)) - { - inflateInitialized = true - return true - } - return false - } - - func reset() throws { - teardownInflate() - guard initInflate() else { throw WSError(type: .compressionError, message: "Error for decompressor on reset", code: 0) } - } - - func decompress(_ data: Data, finish: Bool) throws -> Data { - return try data.withUnsafeBytes { (bytes: UnsafePointer) -> Data in - return try decompress(bytes: bytes, count: data.count, finish: finish) - } - } - - func decompress(bytes: UnsafePointer, count: Int, finish: Bool) throws -> Data { - var decompressed = Data() - try decompress(bytes: bytes, count: count, out: &decompressed) - - if finish { - let tail:[UInt8] = [0x00, 0x00, 0xFF, 0xFF] - try decompress(bytes: tail, count: tail.count, out: &decompressed) - } - - return decompressed - } - - private func decompress(bytes: UnsafePointer, count: Int, out: inout Data) throws { - var res: CInt = 0 - strm.next_in = UnsafeMutablePointer(mutating: bytes) - strm.avail_in = CUnsignedInt(count) - - repeat { - buffer.withUnsafeMutableBytes { (bufferPtr) in - strm.next_out = bufferPtr.bindMemory(to: UInt8.self).baseAddress - strm.avail_out = CUnsignedInt(bufferPtr.count) - - res = inflate(&strm, 0) - } - - let byteCount = buffer.count - Int(strm.avail_out) - out.append(buffer, count: byteCount) - } while res == Z_OK && strm.avail_out == 0 - - guard (res == Z_OK && strm.avail_out > 0) - || (res == Z_BUF_ERROR && Int(strm.avail_out) == buffer.count) - else { - throw WSError(type: .compressionError, message: "Error on decompressing", code: 0) - } - } - - private func teardownInflate() { - if inflateInitialized, Z_OK == inflateEnd(&strm) { - inflateInitialized = false - } - } - - deinit { - teardownInflate() - } -} - -class Compressor { - private var strm = z_stream() - private var buffer = [UInt8](repeating: 0, count: 0x2000) - private var deflateInitialized = false - private let windowBits: Int - - init?(windowBits: Int) { - self.windowBits = windowBits - guard initDeflate() else { return nil } - } - - private func initDeflate() -> Bool { - if Z_OK == deflateInit2_(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, - -CInt(windowBits), 8, Z_DEFAULT_STRATEGY, - ZLIB_VERSION, CInt(MemoryLayout.size)) - { - deflateInitialized = true - return true - } - return false - } - - func reset() throws { - teardownDeflate() - guard initDeflate() else { throw WSError(type: .compressionError, message: "Error for compressor on reset", code: 0) } - } - - func compress(_ data: Data) throws -> Data { - var compressed = Data() - var res: CInt = 0 - data.withUnsafeBytes { (ptr:UnsafePointer) -> Void in - strm.next_in = UnsafeMutablePointer(mutating: ptr) - strm.avail_in = CUnsignedInt(data.count) - - repeat { - buffer.withUnsafeMutableBytes { (bufferPtr) in - strm.next_out = bufferPtr.bindMemory(to: UInt8.self).baseAddress - strm.avail_out = CUnsignedInt(bufferPtr.count) - - res = deflate(&strm, Z_SYNC_FLUSH) - } - - let byteCount = buffer.count - Int(strm.avail_out) - compressed.append(buffer, count: byteCount) - } - while res == Z_OK && strm.avail_out == 0 - - } - - guard res == Z_OK && strm.avail_out > 0 - || (res == Z_BUF_ERROR && Int(strm.avail_out) == buffer.count) - else { - throw WSError(type: .compressionError, message: "Error on compressing", code: 0) - } - - compressed.removeLast(4) - return compressed - } - - private func teardownDeflate() { - if deflateInitialized, Z_OK == deflateEnd(&strm) { - deflateInitialized = false - } - } - - deinit { - teardownDeflate() - } -} diff --git a/Sources/Clickstream/ThirdPartyLibraries/Sources/DataBytes/Data+Extensions.swift b/Sources/Clickstream/ThirdPartyLibraries/Sources/DataBytes/Data+Extensions.swift deleted file mode 100644 index 1d0852d..0000000 --- a/Sources/Clickstream/ThirdPartyLibraries/Sources/DataBytes/Data+Extensions.swift +++ /dev/null @@ -1,53 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////////////////////// -// -// Data+Extensions.swift -// Starscream -// -// Created by Dalton Cherry on 3/27/19. -// Copyright © 2019 Vluxe. All rights reserved. -// -// Fix for deprecation warnings -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -////////////////////////////////////////////////////////////////////////////////////////////////// - -import Foundation - -internal extension Data { - struct ByteError: Swift.Error {} - - #if swift(>=5.0) - func withUnsafeBytes(_ completion: (UnsafePointer) throws -> ResultType) rethrows -> ResultType { - return try withUnsafeBytes { - if let baseAddress = $0.baseAddress, $0.count > 0 { - return try completion(baseAddress.assumingMemoryBound(to: ContentType.self)) - } else { - throw ByteError() - } - } - } - #endif - - #if swift(>=5.0) - mutating func withUnsafeMutableBytes(_ completion: (UnsafeMutablePointer) throws -> ResultType) rethrows -> ResultType { - return try withUnsafeMutableBytes { - if let baseAddress = $0.baseAddress, $0.count > 0 { - return try completion(baseAddress.assumingMemoryBound(to: ContentType.self)) - } else { - throw ByteError() - } - } - } - #endif -} diff --git a/Sources/Clickstream/ThirdPartyLibraries/Sources/Engine/Engine.swift b/Sources/Clickstream/ThirdPartyLibraries/Sources/Engine/Engine.swift deleted file mode 100644 index 71a4d06..0000000 --- a/Sources/Clickstream/ThirdPartyLibraries/Sources/Engine/Engine.swift +++ /dev/null @@ -1,22 +0,0 @@ -// -// Engine.swift -// Starscream -// -// Created by Dalton Cherry on 6/15/19. -// Copyright © 2019 Vluxe. All rights reserved. -// - -import Foundation - -public protocol EngineDelegate: AnyObject { - func didReceive(event: WebSocketEvent) -} - -public protocol Engine { - func register(delegate: EngineDelegate) - func start(request: URLRequest) - func stop(closeCode: UInt16) - func forceStop() - func write(data: Data, opcode: FrameOpCode, completion: (() -> ())?) - func write(string: String, completion: (() -> ())?) -} diff --git a/Sources/Clickstream/ThirdPartyLibraries/Sources/Engine/NativeEngine.swift b/Sources/Clickstream/ThirdPartyLibraries/Sources/Engine/NativeEngine.swift deleted file mode 100644 index 7294e36..0000000 --- a/Sources/Clickstream/ThirdPartyLibraries/Sources/Engine/NativeEngine.swift +++ /dev/null @@ -1,96 +0,0 @@ -// -// NativeEngine.swift -// Starscream -// -// Created by Dalton Cherry on 6/15/19. -// Copyright © 2019 Vluxe. All rights reserved. -// - -import Foundation - -@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *) -public class NativeEngine: NSObject, Engine, URLSessionDataDelegate, URLSessionWebSocketDelegate { - private var task: URLSessionWebSocketTask? - weak var delegate: EngineDelegate? - - public func register(delegate: EngineDelegate) { - self.delegate = delegate - } - - public func start(request: URLRequest) { - let session = URLSession(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: nil) - task = session.webSocketTask(with: request) - doRead() - task?.resume() - } - - public func stop(closeCode: UInt16) { - let closeCode = URLSessionWebSocketTask.CloseCode(rawValue: Int(closeCode)) ?? .normalClosure - task?.cancel(with: closeCode, reason: nil) - } - - public func forceStop() { - stop(closeCode: UInt16(URLSessionWebSocketTask.CloseCode.abnormalClosure.rawValue)) - } - - public func write(string: String, completion: (() -> ())?) { - task?.send(.string(string), completionHandler: { (error) in - completion?() - }) - } - - public func write(data: Data, opcode: FrameOpCode, completion: (() -> ())?) { - switch opcode { - case .binaryFrame: - task?.send(.data(data), completionHandler: { (error) in - completion?() - }) - case .textFrame: - let text = String(data: data, encoding: .utf8)! - write(string: text, completion: completion) - case .ping: - task?.sendPing(pongReceiveHandler: { (error) in - completion?() - }) - default: - break //unsupported - } - } - - private func doRead() { - task?.receive { [weak self] (result) in - switch result { - case .success(let message): - switch message { - case .string(let string): - self?.broadcast(event: .text(string)) - case .data(let data): - self?.broadcast(event: .binary(data)) - @unknown default: - break - } - break - case .failure(let error): - self?.broadcast(event: .error(error)) - } - self?.doRead() - } - } - - private func broadcast(event: WebSocketEvent) { - delegate?.didReceive(event: event) - } - - public func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didOpenWithProtocol protocol: String?) { - let p = `protocol` ?? "" - broadcast(event: .connected([HTTPWSHeader.protocolName: p])) - } - - public func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didCloseWith closeCode: URLSessionWebSocketTask.CloseCode, reason: Data?) { - var r = "" - if let d = reason { - r = String(data: d, encoding: .utf8) ?? "" - } - broadcast(event: .disconnected(r, UInt16(closeCode.rawValue))) - } -} diff --git a/Sources/Clickstream/ThirdPartyLibraries/Sources/Engine/WSEngine.swift b/Sources/Clickstream/ThirdPartyLibraries/Sources/Engine/WSEngine.swift deleted file mode 100644 index 3fc8556..0000000 --- a/Sources/Clickstream/ThirdPartyLibraries/Sources/Engine/WSEngine.swift +++ /dev/null @@ -1,236 +0,0 @@ -// -// WSEngine.swift -// Starscream -// -// Created by Dalton Cherry on 6/15/19. -// Copyright © 2019 Vluxe. All rights reserved. -// - -import Foundation - -public class WSEngine: Engine, TransportEventClient, FramerEventClient, -FrameCollectorDelegate, HTTPHandlerDelegate { - private let transport: Transport - private let framer: Framer - private let httpHandler: HTTPHandler - private let compressionHandler: CompressionHandler? - private let certPinner: CertificatePinning? - private let headerChecker: HeaderValidator - private var request: URLRequest! - - private let frameHandler = FrameCollector() - private var didUpgrade = false - private var secKeyValue = "" - private let writeQueue = DispatchQueue(label: "com.vluxe.starscream.writequeue") - private let mutex = DispatchSemaphore(value: 1) - private var canSend = false - - weak var delegate: EngineDelegate? - public var respondToPingWithPong: Bool = true - - public init(transport: Transport, - certPinner: CertificatePinning? = nil, - headerValidator: HeaderValidator = FoundationSecurity(), - httpHandler: HTTPHandler = FoundationHTTPHandler(), - framer: Framer = WSFramer(), - compressionHandler: CompressionHandler? = nil) { - self.transport = transport - self.framer = framer - self.httpHandler = httpHandler - self.certPinner = certPinner - self.headerChecker = headerValidator - self.compressionHandler = compressionHandler - framer.updateCompression(supports: compressionHandler != nil) - frameHandler.delegate = self - } - - public func register(delegate: EngineDelegate) { - self.delegate = delegate - } - - public func start(request: URLRequest) { - mutex.wait() - let isConnected = canSend - mutex.signal() - if isConnected { - return - } - - self.request = request - transport.register(delegate: self) - framer.register(delegate: self) - httpHandler.register(delegate: self) - frameHandler.delegate = self - guard let url = request.url else { - return - } - transport.connect(url: url, timeout: request.timeoutInterval, certificatePinning: certPinner) - } - - public func stop(closeCode: UInt16 = CloseCode.normal.rawValue) { - let capacity = MemoryLayout.size - var pointer = [UInt8](repeating: 0, count: capacity) - writeUint16(&pointer, offset: 0, value: closeCode) - let payload = Data(bytes: pointer, count: MemoryLayout.size) - write(data: payload, opcode: .connectionClose, completion: { [weak self] in - self?.reset() - self?.forceStop() - }) - } - - public func forceStop() { - transport.disconnect() - } - - public func write(string: String, completion: (() -> ())?) { - let data = string.data(using: .utf8)! - write(data: data, opcode: .textFrame, completion: completion) - } - - public func write(data: Data, opcode: FrameOpCode, completion: (() -> ())?) { - writeQueue.async { [weak self] in - guard let s = self else { return } - s.mutex.wait() - let canWrite = s.canSend - s.mutex.signal() - if !canWrite { - return - } - - var isCompressed = false - var sendData = data - if let compressedData = s.compressionHandler?.compress(data: data) { - sendData = compressedData - isCompressed = true - } - - let frameData = s.framer.createWriteFrame(opcode: opcode, payload: sendData, isCompressed: isCompressed) - s.transport.write(data: frameData, completion: {_ in - completion?() - }) - } - } - - // MARK: - TransportEventClient - - public func connectionChanged(state: ConnectionState) { - switch state { - case .connected: - secKeyValue = HTTPWSHeader.generateWebSocketKey() - let wsReq = HTTPWSHeader.createUpgrade(request: request, supportsCompression: framer.supportsCompression(), secKeyValue: secKeyValue) - let data = httpHandler.convert(request: wsReq) - transport.write(data: data, completion: {_ in }) - case .waiting: - break - case .timedOut: - broadcast(event: .timedOut) - case .failed(let error): - handleError(error) - case .viability(let isViable): - broadcast(event: .viabilityChanged(isViable)) - case .shouldReconnect(let status): - broadcast(event: .reconnectSuggested(status)) - case .receive(let data): - if didUpgrade { - framer.add(data: data) - } else { - let offset = httpHandler.parse(data: data) - if offset > 0 { - let extraData = data.subdata(in: offset.. Data? { - return compressionHandler?.decompress(data: data, isFinal: isFinal) - } - - public func didForm(event: FrameCollector.Event) { - switch event { - case .text(let string): - broadcast(event: .text(string)) - case .binary(let data): - broadcast(event: .binary(data)) - case .pong(let data): - broadcast(event: .pong(data)) - case .ping(let data): - broadcast(event: .ping(data)) - if respondToPingWithPong { - write(data: data ?? Data(), opcode: .pong, completion: nil) - } - case .closed(let reason, let code): - broadcast(event: .disconnected(reason, code)) - stop(closeCode: code) - case .error(let error): - handleError(error) - } - } - - private func broadcast(event: WebSocketEvent) { - delegate?.didReceive(event: event) - } - - //This call can be coming from a lot of different queues/threads. - //be aware of that when modifying shared variables - private func handleError(_ error: Error?) { - if let wsError = error as? WSError { - stop(closeCode: wsError.code) - } else { - stop() - } - - delegate?.didReceive(event: .error(error)) - } - - private func reset() { - mutex.wait() - canSend = false - didUpgrade = false - mutex.signal() - } - - -} diff --git a/Sources/Clickstream/ThirdPartyLibraries/Sources/Framer/FoundationHTTPHandler.swift b/Sources/Clickstream/ThirdPartyLibraries/Sources/Framer/FoundationHTTPHandler.swift deleted file mode 100644 index fb024aa..0000000 --- a/Sources/Clickstream/ThirdPartyLibraries/Sources/Framer/FoundationHTTPHandler.swift +++ /dev/null @@ -1,123 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////////////////////// -// -// FoundationHTTPHandler.swift -// Starscream -// -// Created by Dalton Cherry on 1/25/19. -// Copyright © 2019 Vluxe. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -////////////////////////////////////////////////////////////////////////////////////////////////// - -import Foundation -#if os(watchOS) -public typealias FoundationHTTPHandler = StringHTTPHandler -#else -public class FoundationHTTPHandler: HTTPHandler { - - var buffer = Data() - weak var delegate: HTTPHandlerDelegate? - - public init() { - - } - - public func convert(request: URLRequest) -> Data { - let msg = CFHTTPMessageCreateRequest(kCFAllocatorDefault, request.httpMethod! as CFString, - request.url! as CFURL, kCFHTTPVersion1_1).takeRetainedValue() - if let headers = request.allHTTPHeaderFields { - for (aKey, aValue) in headers { - CFHTTPMessageSetHeaderFieldValue(msg, aKey as CFString, aValue as CFString) - } - } - if let body = request.httpBody { - CFHTTPMessageSetBody(msg, body as CFData) - } - guard let data = CFHTTPMessageCopySerializedMessage(msg) else { - return Data() - } - return data.takeRetainedValue() as Data - } - - public func parse(data: Data) -> Int { - let offset = findEndOfHTTP(data: data) - if offset > 0 { - buffer.append(data.subdata(in: 0.. Bool { - var pointer = [UInt8]() - data.withUnsafeBytes { pointer.append(contentsOf: $0) } - - let response = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, false).takeRetainedValue() - if !CFHTTPMessageAppendBytes(response, pointer, data.count) { - return false //not enough data, wait for more - } - if !CFHTTPMessageIsHeaderComplete(response) { - return false //not enough data, wait for more - } - - let code = CFHTTPMessageGetResponseStatusCode(response) - if code != HTTPWSHeader.switchProtocolCode { - delegate?.didReceiveHTTP(event: .failure(HTTPUpgradeError.notAnUpgrade(code))) - return true - } - - if let cfHeaders = CFHTTPMessageCopyAllHeaderFields(response) { - let nsHeaders = cfHeaders.takeRetainedValue() as NSDictionary - var headers = [String: String]() - for (key, value) in nsHeaders { - if let key = key as? String, let value = value as? String { - headers[key] = value - } - } - delegate?.didReceiveHTTP(event: .success(headers)) - return true - } - - delegate?.didReceiveHTTP(event: .failure(HTTPUpgradeError.invalidData)) - return true - } - - public func register(delegate: HTTPHandlerDelegate) { - self.delegate = delegate - } - - private func findEndOfHTTP(data: Data) -> Int { - let endBytes = [UInt8(ascii: "\r"), UInt8(ascii: "\n"), UInt8(ascii: "\r"), UInt8(ascii: "\n")] - var pointer = [UInt8]() - data.withUnsafeBytes { pointer.append(contentsOf: $0) } - var k = 0 - for i in 0.. Data { - #if os(watchOS) - //TODO: build response header - return Data() - #else - let response = CFHTTPMessageCreateResponse(kCFAllocatorDefault, HTTPWSHeader.switchProtocolCode, - nil, kCFHTTPVersion1_1).takeRetainedValue() - - //TODO: add other values to make a proper response here... - //TODO: also sec key thing (Sec-WebSocket-Key) - for (key, value) in headers { - CFHTTPMessageSetHeaderFieldValue(response, key as CFString, value as CFString) - } - guard let cfData = CFHTTPMessageCopySerializedMessage(response)?.takeRetainedValue() else { - return Data() - } - return cfData as Data - #endif - } - - public func parse(data: Data) { - buffer.append(data) - if parseContent(data: buffer) { - buffer = Data() - } - } - - //returns true when the buffer should be cleared - func parseContent(data: Data) -> Bool { - var pointer = [UInt8]() - data.withUnsafeBytes { pointer.append(contentsOf: $0) } - #if os(watchOS) - //TODO: parse data - return false - #else - let response = CFHTTPMessageCreateEmpty(kCFAllocatorDefault, true).takeRetainedValue() - if !CFHTTPMessageAppendBytes(response, pointer, data.count) { - return false //not enough data, wait for more - } - if !CFHTTPMessageIsHeaderComplete(response) { - return false //not enough data, wait for more - } - if let method = CFHTTPMessageCopyRequestMethod(response)?.takeRetainedValue() { - if (method as NSString) != getVerb { - delegate?.didReceive(event: .failure(HTTPUpgradeError.invalidData)) - return true - } - } - - if let cfHeaders = CFHTTPMessageCopyAllHeaderFields(response) { - let nsHeaders = cfHeaders.takeRetainedValue() as NSDictionary - var headers = [String: String]() - for (key, value) in nsHeaders { - if let key = key as? String, let value = value as? String { - headers[key] = value - } - } - delegate?.didReceive(event: .success(headers)) - return true - } - - delegate?.didReceive(event: .failure(HTTPUpgradeError.invalidData)) - return true - #endif - } -} diff --git a/Sources/Clickstream/ThirdPartyLibraries/Sources/Framer/FrameCollector.swift b/Sources/Clickstream/ThirdPartyLibraries/Sources/Framer/FrameCollector.swift deleted file mode 100644 index 1822b0d..0000000 --- a/Sources/Clickstream/ThirdPartyLibraries/Sources/Framer/FrameCollector.swift +++ /dev/null @@ -1,107 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////////////////////// -// -// FrameCollector.swift -// Starscream -// -// Created by Dalton Cherry on 1/24/19. -// Copyright © 2019 Vluxe. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -////////////////////////////////////////////////////////////////////////////////////////////////// - -import Foundation - -public protocol FrameCollectorDelegate: AnyObject { - func didForm(event: FrameCollector.Event) - func decompress(data: Data, isFinal: Bool) -> Data? -} - -public class FrameCollector { - public enum Event { - case text(String) - case binary(Data) - case pong(Data?) - case ping(Data?) - case error(Error) - case closed(String, UInt16) - } - weak var delegate: FrameCollectorDelegate? - var buffer = Data() - var frameCount = 0 - var isText = false //was the first frame a text frame or a binary frame? - var needsDecompression = false - - public func add(frame: Frame) { - //check single frame action and out of order frames - if frame.opcode == .connectionClose { - var code = frame.closeCode - var reason = "connection closed by server" - if let customCloseReason = String(data: frame.payload, encoding: .utf8) { - reason = customCloseReason - } else { - code = CloseCode.protocolError.rawValue - } - delegate?.didForm(event: .closed(reason, code)) - return - } else if frame.opcode == .pong { - delegate?.didForm(event: .pong(frame.payload)) - return - } else if frame.opcode == .ping { - delegate?.didForm(event: .ping(frame.payload)) - return - } else if frame.opcode == .continueFrame && frameCount == 0 { - let errCode = CloseCode.protocolError.rawValue - delegate?.didForm(event: .error(WSError(type: .protocolError, message: "first frame can't be a continue frame", code: errCode))) - reset() - return - } else if frameCount > 0 && frame.opcode != .continueFrame { - let errCode = CloseCode.protocolError.rawValue - delegate?.didForm(event: .error(WSError(type: .protocolError, message: "second and beyond of fragment message must be a continue frame", code: errCode))) - reset() - return - } - if frameCount == 0 { - isText = frame.opcode == .textFrame - needsDecompression = frame.needsDecompression - } - - let payload: Data - if needsDecompression { - payload = delegate?.decompress(data: frame.payload, isFinal: frame.isFin) ?? frame.payload - } else { - payload = frame.payload - } - buffer.append(payload) - frameCount += 1 - - if frame.isFin { - if isText { - if let string = String(data: buffer, encoding: .utf8) { - delegate?.didForm(event: .text(string)) - } else { - let errCode = CloseCode.protocolError.rawValue - delegate?.didForm(event: .error(WSError(type: .protocolError, message: "not valid UTF-8 data", code: errCode))) - } - } else { - delegate?.didForm(event: .binary(buffer)) - } - reset() - } - } - - func reset() { - buffer = Data() - frameCount = 0 - } -} diff --git a/Sources/Clickstream/ThirdPartyLibraries/Sources/Framer/Framer.swift b/Sources/Clickstream/ThirdPartyLibraries/Sources/Framer/Framer.swift deleted file mode 100644 index 58f6030..0000000 --- a/Sources/Clickstream/ThirdPartyLibraries/Sources/Framer/Framer.swift +++ /dev/null @@ -1,357 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////////////////////// -// -// Framer.swift -// Starscream -// -// Created by Dalton Cherry on 1/23/19. -// Copyright © 2019 Vluxe. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -////////////////////////////////////////////////////////////////////////////////////////////////// - -import Foundation - -let FinMask: UInt8 = 0x80 -let OpCodeMask: UInt8 = 0x0F -let RSVMask: UInt8 = 0x70 -let RSV1Mask: UInt8 = 0x40 -let MaskMask: UInt8 = 0x80 -let PayloadLenMask: UInt8 = 0x7F -let MaxFrameSize: Int = 32 - -// Standard WebSocket close codes -public enum CloseCode: UInt16 { - case normal = 1000 - case goingAway = 1001 - case protocolError = 1002 - case protocolUnhandledType = 1003 - // 1004 reserved. - case noStatusReceived = 1005 - //1006 reserved. - case encoding = 1007 - case policyViolated = 1008 - case messageTooBig = 1009 -} - -public enum FrameOpCode: UInt8 { - case continueFrame = 0x0 - case textFrame = 0x1 - case binaryFrame = 0x2 - // 3-7 are reserved. - case connectionClose = 0x8 - case ping = 0x9 - case pong = 0xA - // B-F reserved. - case unknown = 100 -} - -public struct Frame { - let isFin: Bool - let needsDecompression: Bool - let isMasked: Bool - let opcode: FrameOpCode - let payloadLength: UInt64 - let payload: Data - let closeCode: UInt16 //only used by connectionClose opcode -} - -public enum FrameEvent { - case frame(Frame) - case error(Error) -} - -public protocol FramerEventClient: AnyObject { - func frameProcessed(event: FrameEvent) -} - -public protocol Framer { - func add(data: Data) - func register(delegate: FramerEventClient) - func createWriteFrame(opcode: FrameOpCode, payload: Data, isCompressed: Bool) -> Data - func updateCompression(supports: Bool) - func supportsCompression() -> Bool -} - -public class WSFramer: Framer { - private let queue = DispatchQueue(label: "com.vluxe.starscream.wsframer", attributes: []) - private weak var delegate: FramerEventClient? - private var buffer = Data() - public var compressionEnabled = false - private let isServer: Bool - - public init(isServer: Bool = false) { - self.isServer = isServer - } - - public func updateCompression(supports: Bool) { - compressionEnabled = supports - } - - public func supportsCompression() -> Bool { - return compressionEnabled - } - - enum ProcessEvent { - case needsMoreData - case processedFrame(Frame, Int) - case failed(Error) - } - - public func add(data: Data) { - queue.async { [weak self] in - self?.buffer.append(data) - while(true) { - let event = self?.process() ?? .needsMoreData - switch event { - case .needsMoreData: - return - case .processedFrame(let frame, let split): - guard let s = self else { return } - s.delegate?.frameProcessed(event: .frame(frame)) - if split >= s.buffer.count { - s.buffer = Data() - return - } - s.buffer = s.buffer.advanced(by: split) - case .failed(let error): - self?.delegate?.frameProcessed(event: .error(error)) - self?.buffer = Data() - return - } - } - } - } - - public func register(delegate: FramerEventClient) { - self.delegate = delegate - } - - private func process() -> ProcessEvent { - if buffer.count < 2 { - return .needsMoreData - } - var pointer = [UInt8]() - buffer.withUnsafeBytes { pointer.append(contentsOf: $0) } - - let isFin = (FinMask & pointer[0]) - let opcodeRawValue = (OpCodeMask & pointer[0]) - let opcode = FrameOpCode(rawValue: opcodeRawValue) ?? .unknown - let isMasked = (MaskMask & pointer[1]) - let payloadLen = (PayloadLenMask & pointer[1]) - let RSV1 = (RSVMask & pointer[0]) - var needsDecompression = false - - if compressionEnabled && opcode != .continueFrame { - needsDecompression = (RSV1Mask & pointer[0]) > 0 - } - if !isServer && (isMasked > 0 || RSV1 > 0) && opcode != .pong && !needsDecompression { - let errCode = CloseCode.protocolError.rawValue - return .failed(WSError(type: .protocolError, message: "masked and rsv data is not currently supported", code: errCode)) - } - let isControlFrame = (opcode == .connectionClose || opcode == .ping) - if !isControlFrame && (opcode != .binaryFrame && opcode != .continueFrame && - opcode != .textFrame && opcode != .pong) { - let errCode = CloseCode.protocolError.rawValue - return .failed(WSError(type: .protocolError, message: "unknown opcode: \(opcodeRawValue)", code: errCode)) - } - if isControlFrame && isFin == 0 { - let errCode = CloseCode.protocolError.rawValue - return .failed(WSError(type: .protocolError, message: "control frames can't be fragmented", code: errCode)) - } - - var offset: Int = 2 - - if isControlFrame && payloadLen > 125 { - return .failed(WSError(type: .protocolError, message: "payload length is longer than allowed for a control frame", code: CloseCode.protocolError.rawValue)) - } - - var dataLength = UInt64(payloadLen) - var closeCode = CloseCode.normal.rawValue - if opcode == .connectionClose { - if payloadLen == 1 { - closeCode = CloseCode.protocolError.rawValue - dataLength = 0 - } else if payloadLen > 1 { - if pointer.count < 4 { - return .needsMoreData - } - let size = MemoryLayout.size - closeCode = readUint16(pointer: pointer, offset: offset) - offset += size - dataLength -= UInt64(size) - if closeCode < 1000 || (closeCode > 1003 && closeCode < 1007) || (closeCode > 1013 && closeCode < 3000) { - closeCode = CloseCode.protocolError.rawValue - } - } - } - - if payloadLen == 127 { - let size = MemoryLayout.size - if size + offset > pointer.count { - return .needsMoreData - } - dataLength = readUint64(pointer: pointer, offset: offset) - offset += size - } else if payloadLen == 126 { - let size = MemoryLayout.size - if size + offset > pointer.count { - return .needsMoreData - } - dataLength = UInt64(readUint16(pointer: pointer, offset: offset)) - offset += size - } - - let maskStart = offset - if isServer { - offset += MemoryLayout.size - } - - if dataLength > (pointer.count - offset) { - return .needsMoreData - } - - //I don't like this cast, but Data's count returns an Int. - //Might be a problem with huge payloads. Need to revisit. - let readDataLength = Int(dataLength) - - let payload: Data - if readDataLength == 0 { - payload = Data() - } else { - if isServer { - payload = unmaskDataa(pointer: pointer, maskStart: maskStart, offset: offset, length: readDataLength) - } else { - let end = offset + readDataLength - payload = Data(pointer[offset.. 0, needsDecompression: needsDecompression, isMasked: isMasked > 0, opcode: opcode, payloadLength: dataLength, payload: payload, closeCode: closeCode) - return .processedFrame(frame, offset) - } - - /** - Read a UInt16 from a buffer. - - parameter offset: is the offset index to start the read from (e.g. buffer[0], buffer[1], etc). - - returns: a UInt16 of the value from the buffer - */ - func readUint16(pointer: [UInt8], offset: Int) -> UInt16 { - return (UInt16(pointer[offset + 0]) << 8) | UInt16(pointer[offset + 1]) - } - - /** - Read a UInt64 from a buffer. - - parameter offset: is the offset index to start the read from (e.g. buffer[0], buffer[1], etc). - - returns: a UInt64 of the value from the buffer - */ - func readUint64(pointer: [UInt8], offset: Int) -> UInt64 { - var value = UInt64(0) - for i in 0...7 { - value = (value << 8) | UInt64(pointer[offset + i]) - } - return value - } - - func unmaskDataa(pointer: [UInt8], maskStart: Int, offset: Int, length: Int) -> Data { - var unmaskedBytes = [UInt8](repeating: 0, count: length) - let maskSize = MemoryLayout.size - for i in 0.. Data { - let payloadLength = payload.count - - let capacity = payloadLength + MaxFrameSize - var pointer = [UInt8](repeating: 0, count: capacity) - - //set the framing info - pointer[0] = FinMask | opcode.rawValue - if isCompressed { - pointer[0] |= RSV1Mask - } - - var offset = 2 //skip pass the framing info - if payloadLength < 126 { - pointer[1] = UInt8(payloadLength) - } else if payloadLength <= Int(UInt16.max) { - pointer[1] = 126 - writeUint16(&pointer, offset: offset, value: UInt16(payloadLength)) - offset += MemoryLayout.size - } else { - pointer[1] = 127 - writeUint64(&pointer, offset: offset, value: UInt64(payloadLength)) - offset += MemoryLayout.size - } - - //clients are required to mask the payload data, but server don't according to the RFC - if !isServer { - pointer[1] |= MaskMask - - //write the random mask key in - let maskKey: UInt32 = UInt32.random(in: 0...UInt32.max) - - writeUint32(&pointer, offset: offset, value: maskKey) - let maskStart = offset - offset += MemoryLayout.size - - //now write the payload data in - for i in 0...size)] - offset += 1 - } - } else { - for i in 0..> 8) - buffer[offset + 1] = UInt8(value & 0xff) -} - -/** - Write a UInt32 to the buffer. It fills the 4 array "slots" of the UInt8 array. - - parameter buffer: is the UInt8 array (pointer) to write the value too. - - parameter offset: is the offset index to start the write from (e.g. buffer[0], buffer[1], etc). - */ -public func writeUint32( _ buffer: inout [UInt8], offset: Int, value: UInt32) { - for i in 0...3 { - buffer[offset + i] = UInt8((value >> (8*UInt32(3 - i))) & 0xff) - } -} - -/** - Write a UInt64 to the buffer. It fills the 8 array "slots" of the UInt8 array. - - parameter buffer: is the UInt8 array (pointer) to write the value too. - - parameter offset: is the offset index to start the write from (e.g. buffer[0], buffer[1], etc). - */ -public func writeUint64( _ buffer: inout [UInt8], offset: Int, value: UInt64) { - for i in 0...7 { - buffer[offset + i] = UInt8((value >> (8*UInt64(7 - i))) & 0xff) - } -} diff --git a/Sources/Clickstream/ThirdPartyLibraries/Sources/Framer/HTTPHandler.swift b/Sources/Clickstream/ThirdPartyLibraries/Sources/Framer/HTTPHandler.swift deleted file mode 100644 index 2b51a71..0000000 --- a/Sources/Clickstream/ThirdPartyLibraries/Sources/Framer/HTTPHandler.swift +++ /dev/null @@ -1,146 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////////////////////// -// -// HTTPHandler.swift -// Starscream -// -// Created by Dalton Cherry on 1/24/19. -// Copyright © 2019 Vluxe. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -////////////////////////////////////////////////////////////////////////////////////////////////// - -import Foundation - -public enum HTTPUpgradeError: Error { - case notAnUpgrade(Int) - case invalidData -} - -public struct HTTPWSHeader { - static let upgradeName = "Upgrade" - static let upgradeValue = "websocket" - static let hostName = "Host" - static let connectionName = "Connection" - static let connectionValue = "Upgrade" - static let protocolName = "Sec-WebSocket-Protocol" - static let versionName = "Sec-WebSocket-Version" - static let versionValue = "13" - static let extensionName = "Sec-WebSocket-Extensions" - static let keyName = "Sec-WebSocket-Key" - static let originName = "Origin" - static let acceptName = "Sec-WebSocket-Accept" - static let switchProtocolCode = 101 - static let defaultSSLSchemes = ["wss", "https"] - - /// Creates a new URLRequest based off the source URLRequest. - /// - Parameter request: the request to "upgrade" the WebSocket request by adding headers. - /// - Parameter supportsCompression: set if the client support text compression. - /// - Parameter secKeyName: the security key to use in the WebSocket request. https://tools.ietf.org/html/rfc6455#section-1.3 - /// - returns: A URLRequest request to be converted to data and sent to the server. - public static func createUpgrade(request: URLRequest, supportsCompression: Bool, secKeyValue: String) -> URLRequest { - guard let url = request.url, let parts = getPartss(hostCaller: url.host, portCaller: url.port, schemeCaller: url.scheme) else { - return request - } - - var req = request - if request.value(forHTTPHeaderField: HTTPWSHeader.originName) == nil { - var origin = url.absoluteString - if let hostUrl = URL (string: "/", relativeTo: url) { - origin = hostUrl.absoluteString - origin.remove(at: origin.index(before: origin.endIndex)) - } - req.setValue(origin, forHTTPHeaderField: HTTPWSHeader.originName) - } - req.setValue(HTTPWSHeader.upgradeValue, forHTTPHeaderField: HTTPWSHeader.upgradeName) - req.setValue(HTTPWSHeader.connectionValue, forHTTPHeaderField: HTTPWSHeader.connectionName) - req.setValue(HTTPWSHeader.versionValue, forHTTPHeaderField: HTTPWSHeader.versionName) - req.setValue(secKeyValue, forHTTPHeaderField: HTTPWSHeader.keyName) - - if let cookies = HTTPCookieStorage.shared.cookies(for: url), !cookies.isEmpty { - let headers = HTTPCookie.requestHeaderFields(with: cookies) - for (key, val) in headers { - req.setValue(val, forHTTPHeaderField: key) - } - } - - if supportsCompression { - let val = "permessage-deflate; client_max_window_bits; server_max_window_bits=15" - req.setValue(val, forHTTPHeaderField: HTTPWSHeader.extensionName) - } - let hostValue = req.allHTTPHeaderFields?[HTTPWSHeader.hostName] ?? "\(parts.host):\(parts.port)" - req.setValue(hostValue, forHTTPHeaderField: HTTPWSHeader.hostName) - return req - } - - // generateWebSocketKey 16 random characters between a-z and return them as a base64 string - public static func generateWebSocketKey() -> String { - return Data((0..<16).map{ _ in UInt8.random(in: 97...122) }).base64EncodedString() - } - - /// isTLSScheme returns true if the scheme is https or wss - static func isTLSScheme(schemeCaller: String?) -> Bool { - guard let scheme = schemeCaller else { - return false - } - return HTTPWSHeader.defaultSSLSchemes.contains(scheme) - } - - /// getParts pulls host and port from the url. - static func getPartss(hostCaller: String?, portCaller: Int?, schemeCaller: String?) -> URLParts? { - guard let host = hostCaller else { - return nil // no host, this isn't a valid url - } - let isTLS = isTLSScheme(schemeCaller: schemeCaller) - var port = portCaller ?? 0 - if portCaller == nil { - if isTLS { - port = 443 - } else { - port = 80 - } - } - return URLParts(port: port, host: host, isTLS: isTLS) - } -} - -public enum HTTPEvent { - case success([String: String]) - case failure(Error) -} - -public protocol HTTPHandlerDelegate: AnyObject { - func didReceiveHTTP(event: HTTPEvent) -} - -public protocol HTTPHandler { - func register(delegate: HTTPHandlerDelegate) - func convert(request: URLRequest) -> Data - func parse(data: Data) -> Int -} - -public protocol HTTPServerDelegate: AnyObject { - func didReceive(event: HTTPEvent) -} - -public protocol HTTPServerHandler { - func register(delegate: HTTPServerDelegate) - func parse(data: Data) - func createResponse(headers: [String: String]) -> Data -} - -public struct URLParts { - let port: Int - let host: String - let isTLS: Bool -} diff --git a/Sources/Clickstream/ThirdPartyLibraries/Sources/Framer/StringHTTPHandler.swift b/Sources/Clickstream/ThirdPartyLibraries/Sources/Framer/StringHTTPHandler.swift deleted file mode 100644 index 33f5f9b..0000000 --- a/Sources/Clickstream/ThirdPartyLibraries/Sources/Framer/StringHTTPHandler.swift +++ /dev/null @@ -1,143 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////////////////////// -// -// StringHTTPHandler.swift -// Starscream -// -// Created by Dalton Cherry on 8/25/19. -// Copyright © 2019 Vluxe. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -////////////////////////////////////////////////////////////////////////////////////////////////// - -import Foundation - -public class StringHTTPHandler: HTTPHandler { - - var buffer = Data() - weak var delegate: HTTPHandlerDelegate? - - public init() { - - } - - public func convert(request: URLRequest) -> Data { - guard let url = request.url else { - return Data() - } - - var path = url.absoluteString - let offset = (url.scheme?.count ?? 2) + 3 - path = String(path[path.index(path.startIndex, offsetBy: offset).. Int { - let offset = findEndOfHTTP(data: data) - if offset > 0 { - buffer.append(data.subdata(in: 0.. Bool { - guard let str = String(data: data, encoding: .utf8) else { - delegate?.didReceiveHTTP(event: .failure(HTTPUpgradeError.invalidData)) - return true - } - let splitArr = str.components(separatedBy: "\r\n") - var code = -1 - var i = 0 - var headers = [String: String]() - for str in splitArr { - if i == 0 { - let responseSplit = str.components(separatedBy: .whitespaces) - guard responseSplit.count > 1 else { - delegate?.didReceiveHTTP(event: .failure(HTTPUpgradeError.invalidData)) - return true - } - if let c = Int(responseSplit[1]) { - code = c - } - } else { - guard let separatorIndex = str.firstIndex(of: ":") else { break } - let key = str.prefix(upTo: separatorIndex).trimmingCharacters(in: .whitespaces) - let val = str.suffix(from: str.index(after: separatorIndex)).trimmingCharacters(in: .whitespaces) - headers[key.lowercased()] = val - } - i += 1 - } - - if code != HTTPWSHeader.switchProtocolCode { - delegate?.didReceiveHTTP(event: .failure(HTTPUpgradeError.notAnUpgrade(code))) - return true - } - - delegate?.didReceiveHTTP(event: .success(headers)) - return true - } - - public func register(delegate: HTTPHandlerDelegate) { - self.delegate = delegate - } - - private func findEndOfHTTP(data: Data) -> Int { - let endBytes = [UInt8(ascii: "\r"), UInt8(ascii: "\n"), UInt8(ascii: "\r"), UInt8(ascii: "\n")] - var pointer = [UInt8]() - data.withUnsafeBytes { pointer.append(contentsOf: $0) } - var k = 0 - for i in 0.. ())) { - if allowSelfSigned { - completion(.success) - return - } - - if let validateDomain = domain { - SecTrustSetPolicies(trust, SecPolicyCreateSSL(true, validateDomain as NSString?)) - } - - handleSecurityTrust(trust: trust, completion: completion) - } - - private func handleSecurityTrust(trust: SecTrust, completion: ((PinningState) -> ())) { - if #available(iOS 12.0, OSX 10.14, watchOS 5.0, tvOS 12.0, *) { - var error: CFError? - if SecTrustEvaluateWithError(trust, &error) { - completion(.success) - } else { - completion(.failed(error)) - } - } else { - handleOldSecurityTrust(trust: trust, completion: completion) - } - } - - private func handleOldSecurityTrust(trust: SecTrust, completion: ((PinningState) -> ())) { - var result: SecTrustResultType = .unspecified - SecTrustEvaluate(trust, &result) - if result == .unspecified || result == .proceed { - completion(.success) - } else { - let e = CFErrorCreate(kCFAllocatorDefault, "FoundationSecurityError" as NSString?, Int(result.rawValue), nil) - completion(.failed(e)) - } - } -} - -extension FoundationSecurity: HeaderValidator { - public func validate(headers: [String: String], key: String) -> Error? { - if let acceptKey = headers[HTTPWSHeader.acceptName] { - let sha = "\(key)258EAFA5-E914-47DA-95CA-C5AB0DC85B11".sha1Base64() - if sha != acceptKey { - return WSError(type: .securityError, message: "accept header doesn't match", code: SecurityErrorCode.acceptFailed.rawValue) - } - } - return nil - } -} - -private extension String { - func sha1Base64() -> String { - let data = self.data(using: .utf8)! - let pointer = data.withUnsafeBytes { (bytes: UnsafeRawBufferPointer) -> [UInt8] in - var digest = [UInt8](repeating: 0, count:Int(CC_SHA1_DIGEST_LENGTH)) - CC_SHA1(bytes.baseAddress, CC_LONG(data.count), &digest) - return digest - } - return Data(pointer).base64EncodedString() - } -} diff --git a/Sources/Clickstream/ThirdPartyLibraries/Sources/Security/Security.swift b/Sources/Clickstream/ThirdPartyLibraries/Sources/Security/Security.swift deleted file mode 100644 index d83a6bd..0000000 --- a/Sources/Clickstream/ThirdPartyLibraries/Sources/Security/Security.swift +++ /dev/null @@ -1,45 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////////////////////// -// -// Security.swift -// Starscream -// -// Created by Dalton Cherry on 3/16/19. -// Copyright © 2019 Vluxe. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -////////////////////////////////////////////////////////////////////////////////////////////////// - -import Foundation - -public enum SecurityErrorCode: UInt16 { - case acceptFailed = 1 - case pinningFailed = 2 -} - -public enum PinningState { - case success - case failed(CFError?) -} - -// CertificatePinning protocol provides an interface for Transports to handle Certificate -// or Public Key Pinning. -public protocol CertificatePinning: AnyObject { - func evaluateTrust(trust: SecTrust, domain: String?, completion: ((PinningState) -> ())) -} - -// validates the "Sec-WebSocket-Accept" header as defined 1.3 of the RFC 6455 -// https://tools.ietf.org/html/rfc6455#section-1.3 -public protocol HeaderValidator: AnyObject { - func validate(headers: [String: String], key: String) -> Error? -} diff --git a/Sources/Clickstream/ThirdPartyLibraries/Sources/Server/Server.swift b/Sources/Clickstream/ThirdPartyLibraries/Sources/Server/Server.swift deleted file mode 100644 index 64cf561..0000000 --- a/Sources/Clickstream/ThirdPartyLibraries/Sources/Server/Server.swift +++ /dev/null @@ -1,56 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////////////////////// -// -// Server.swift -// Starscream -// -// Created by Dalton Cherry on 4/2/19. -// Copyright © 2019 Vluxe. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -////////////////////////////////////////////////////////////////////////////////////////////////// - -import Foundation - -public enum ConnectionEvent { - case connected([String: String]) - case disconnected(String, UInt16) - case text(String) - case binary(Data) - case pong(Data?) - case ping(Data?) - case error(Error) -} - -public protocol Connection { - func write(data: Data, opcode: FrameOpCode) -} - -public protocol ConnectionDelegate: AnyObject { - func didReceive(event: ServerEvent) -} - -public enum ServerEvent { - case connected(Connection, [String: String]) - case disconnected(Connection, String, UInt16) - case text(Connection, String) - case binary(Connection, Data) - case pong(Connection, Data?) - case ping(Connection, Data?) -} - -public protocol Server { - func start(address: String, port: UInt16) -> Error? -} - - diff --git a/Sources/Clickstream/ThirdPartyLibraries/Sources/Server/WebSocketServer.swift b/Sources/Clickstream/ThirdPartyLibraries/Sources/Server/WebSocketServer.swift deleted file mode 100644 index 5eb42a6..0000000 --- a/Sources/Clickstream/ThirdPartyLibraries/Sources/Server/WebSocketServer.swift +++ /dev/null @@ -1,198 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////////////////////// -// -// WebSocketServer.swift -// Starscream -// -// Created by Dalton Cherry on 4/5/19. -// Copyright © 2019 Vluxe. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -////////////////////////////////////////////////////////////////////////////////////////////////// - -#if canImport(Network) -import Foundation -import Network - -/// WebSocketServer is a Network.framework implementation of a WebSocket server -@available(watchOS, unavailable) -@available(macOS 10.14, iOS 12.0, watchOS 5.0, tvOS 12.0, *) -public class WebSocketServer: Server, ConnectionDelegate { - public var onEvent: ((ServerEvent) -> Void)? - private var connections = [String: ServerConnection]() - private var listener: NWListener? - private let queue = DispatchQueue(label: "com.vluxe.starscream.server.networkstream", attributes: []) - - public init() { - - } - - public func start(address: String, port: UInt16) -> Error? { - //TODO: support TLS cert adding/binding - let parameters = NWParameters(tls: nil, tcp: NWProtocolTCP.Options()) - let p = NWEndpoint.Port(rawValue: port)! - parameters.requiredLocalEndpoint = NWEndpoint.hostPort(host: NWEndpoint.Host.name(address, nil), port: p) - - guard let listener = try? NWListener(using: parameters, on: p) else { - return WSError(type: .serverError, message: "unable to start the listener at: \(address):\(port)", code: 0) - } - listener.newConnectionHandler = {[weak self] conn in - let transport = TCPTransport(connection: conn) - let c = ServerConnection(transport: transport) - c.delegate = self - self?.connections[c.uuid] = c - } -// listener.stateUpdateHandler = { state in -// switch state { -// case .ready: -// print("ready to get sockets!") -// case .setup: -// print("setup to get sockets!") -// case .cancelled: -// print("server cancelled!") -// case .waiting(let error): -// print("waiting error: \(error)") -// case .failed(let error): -// print("server failed: \(error)") -// @unknown default: -// print("wat?") -// } -// } - self.listener = listener - listener.start(queue: queue) - return nil - } - - public func didReceive(event: ServerEvent) { - onEvent?(event) - switch event { - case .disconnected(let conn, _, _): - guard let conn = conn as? ServerConnection else { - return - } - connections.removeValue(forKey: conn.uuid) - default: - break - } - } -} - -@available(macOS 10.14, iOS 12.0, watchOS 5.0, tvOS 12.0, *) -public class ServerConnection: Connection, HTTPServerDelegate, FramerEventClient, FrameCollectorDelegate, TransportEventClient { - let transport: TCPTransport - private let httpHandler = FoundationHTTPServerHandler() - private let framer = WSFramer(isServer: true) - private let frameHandler = FrameCollector() - private var didUpgrade = false - public var onEvent: ((ConnectionEvent) -> Void)? - public weak var delegate: ConnectionDelegate? - private let id: String - var uuid: String { - return id - } - - init(transport: TCPTransport) { - self.id = UUID().uuidString - self.transport = transport - transport.register(delegate: self) - httpHandler.register(delegate: self) - framer.register(delegate: self) - frameHandler.delegate = self - } - - public func write(data: Data, opcode: FrameOpCode) { - let wsData = framer.createWriteFrame(opcode: opcode, payload: data, isCompressed: false) - transport.write(data: wsData, completion: {_ in }) - } - - // MARK: - TransportEventClient - - public func connectionChanged(state: ConnectionState) { - switch state { - case .connected: - break - case .waiting: - break - case .timedOut: - break - case .failed(let error): - print("server connection error: \(error ?? WSError(type: .protocolError, message: "default error, no extra data", code: 0))") //handleError(error) - case .viability(_): - break - case .shouldReconnect(_): - break - case .receive(let data): - if didUpgrade { - framer.add(data: data) - } else { - httpHandler.parse(data: data) - } - case .cancelled: - print("server connection cancelled!") - //broadcast(event: .cancelled) - } - } - - /// MARK: - HTTPServerDelegate - - public func didReceive(event: HTTPEvent) { - switch event { - case .success(let headers): - didUpgrade = true - let response = httpHandler.createResponse(headers: [:]) - transport.write(data: response, completion: {_ in }) - delegate?.didReceive(event: .connected(self, headers)) - onEvent?(.connected(headers)) - case .failure(let error): - onEvent?(.error(error)) - } - } - - /// MARK: - FrameCollectorDelegate - - public func frameProcessed(event: FrameEvent) { - switch event { - case .frame(let frame): - frameHandler.add(frame: frame) - case .error(let error): - onEvent?(.error(error)) - } - } - - public func didForm(event: FrameCollector.Event) { - switch event { - case .text(let string): - delegate?.didReceive(event: .text(self, string)) - onEvent?(.text(string)) - case .binary(let data): - delegate?.didReceive(event: .binary(self, data)) - onEvent?(.binary(data)) - case .pong(let data): - delegate?.didReceive(event: .pong(self, data)) - onEvent?(.pong(data)) - case .ping(let data): - delegate?.didReceive(event: .ping(self, data)) - onEvent?(.ping(data)) - case .closed(let reason, let code): - delegate?.didReceive(event: .disconnected(self, reason, code)) - onEvent?(.disconnected(reason, code)) - case .error(let error): - onEvent?(.error(error)) - } - } - - public func decompress(data: Data, isFinal: Bool) -> Data? { - return nil - } -} -#endif diff --git a/Sources/Clickstream/ThirdPartyLibraries/Sources/Starscream/WebSocket.swift b/Sources/Clickstream/ThirdPartyLibraries/Sources/Starscream/WebSocket.swift deleted file mode 100644 index c9c7923..0000000 --- a/Sources/Clickstream/ThirdPartyLibraries/Sources/Starscream/WebSocket.swift +++ /dev/null @@ -1,179 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////////////////////// -// -// Websocket.swift -// Starscream -// -// Created by Dalton Cherry on 7/16/14. -// Copyright (c) 2014-2019 Dalton Cherry. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -////////////////////////////////////////////////////////////////////////////////////////////////// - -import Foundation - -public enum ErrorType: Error { - case compressionError - case securityError - case protocolError //There was an error parsing the WebSocket frames - case serverError -} - -public struct WSError: Error { - public let type: ErrorType - public let message: String - public let code: UInt16 - - public init(type: ErrorType, message: String, code: UInt16) { - self.type = type - self.message = message - self.code = code - } -} - -public protocol WebSocketClient: AnyObject { - func connect() - func disconnect(closeCode: UInt16) - func write(string: String, completion: (() -> ())?) - func write(stringData: Data, completion: (() -> ())?) - func write(data: Data, completion: (() -> ())?) - func write(ping: Data, completion: (() -> ())?) - func write(pong: Data, completion: (() -> ())?) -} - -//implements some of the base behaviors -extension WebSocketClient { - public func write(string: String) { - write(string: string, completion: nil) - } - - public func write(data: Data) { - write(data: data, completion: nil) - } - - public func write(ping: Data) { - write(ping: ping, completion: nil) - } - - public func write(pong: Data) { - write(pong: pong, completion: nil) - } - - public func disconnect() { - disconnect(closeCode: CloseCode.normal.rawValue) - } -} - -public enum WebSocketEvent { - case connected([String: String]) - case disconnected(String, UInt16) - case text(String) - case binary(Data) - case pong(Data?) - case ping(Data?) - case error(Error?) - case viabilityChanged(Bool) - case reconnectSuggested(Bool) - case cancelled - case timedOut -} - -public protocol WebSocketDelegate: AnyObject { - func didReceive(event: WebSocketEvent, client: WebSocket) -} - -open class WebSocket: WebSocketClient, EngineDelegate { - private let engine: Engine - public weak var delegate: WebSocketDelegate? - public var onEvent: ((WebSocketEvent) -> Void)? - - public var request: URLRequest - // Where the callback is executed. It defaults to the main UI thread queue. - public var callbackQueue = DispatchQueue.main - public var respondToPingWithPong: Bool { - set { - guard let e = engine as? WSEngine else { return } - e.respondToPingWithPong = newValue - } - get { - guard let e = engine as? WSEngine else { return true } - return e.respondToPingWithPong - } - } - - // serial write queue to ensure writes happen in order - private let writeQueue = DispatchQueue(label: "com.vluxe.starscream.writequeue") - private var canSend = false - private let mutex = DispatchSemaphore(value: 1) - - public init(request: URLRequest, engine: Engine) { - self.request = request - self.engine = engine - } - - public convenience init(request: URLRequest, certPinner: CertificatePinning? = FoundationSecurity(), compressionHandler: CompressionHandler? = nil, useCustomEngine: Bool = true) { - if #available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *), !useCustomEngine { - self.init(request: request, engine: NativeEngine()) - } else if #available(macOS 10.14, iOS 12.0, watchOS 5.0, tvOS 12.0, *) { - self.init(request: request, engine: WSEngine(transport: TCPTransport(), certPinner: certPinner, compressionHandler: compressionHandler)) - } else { - self.init(request: request, engine: WSEngine(transport: FoundationTransport(), certPinner: certPinner, compressionHandler: compressionHandler)) - } - } - - public func connect() { - engine.register(delegate: self) - engine.start(request: request) - } - - public func disconnect(closeCode: UInt16 = CloseCode.normal.rawValue) { - engine.stop(closeCode: closeCode) - } - - public func forceDisconnect() { - engine.forceStop() - } - - public func write(data: Data, completion: (() -> ())?) { - write(data: data, opcode: .binaryFrame, completion: completion) - } - - public func write(string: String, completion: (() -> ())?) { - engine.write(string: string, completion: completion) - } - - public func write(stringData: Data, completion: (() -> ())?) { - write(data: stringData, opcode: .textFrame, completion: completion) - } - - public func write(ping: Data, completion: (() -> ())?) { - write(data: ping, opcode: .ping, completion: completion) - } - - public func write(pong: Data, completion: (() -> ())?) { - write(data: pong, opcode: .pong, completion: completion) - } - - private func write(data: Data, opcode: FrameOpCode, completion: (() -> ())?) { - engine.write(data: data, opcode: opcode, completion: completion) - } - - // MARK: - EngineDelegate - public func didReceive(event: WebSocketEvent) { - callbackQueue.async { [weak self] in - guard let s = self else { return } - s.delegate?.didReceive(event: event, client: s) - s.onEvent?(event) - } - } -} diff --git a/Sources/Clickstream/ThirdPartyLibraries/Sources/Transport/FoundationTransport.swift b/Sources/Clickstream/ThirdPartyLibraries/Sources/Transport/FoundationTransport.swift deleted file mode 100644 index 1de5522..0000000 --- a/Sources/Clickstream/ThirdPartyLibraries/Sources/Transport/FoundationTransport.swift +++ /dev/null @@ -1,243 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////////////////////// -// -// FoundationTransport.swift -// Starscream -// -// Created by Dalton Cherry on 1/23/19. -// Copyright © 2019 Vluxe. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -////////////////////////////////////////////////////////////////////////////////////////////////// - -import Foundation - -public enum FoundationTransportError: Error { - case invalidRequest - case invalidOutputStream - case timeout -} - -public class FoundationTransport: NSObject, Transport, StreamDelegate { - private weak var delegate: TransportEventClient? - private let workQueue = DispatchQueue(label: "com.vluxe.starscream.websocket", attributes: []) - private var inputStream: InputStream? - private var outputStream: OutputStream? - private var isOpen = false - private var onConnect: ((InputStream, OutputStream) -> Void)? - private var isTLS = false - private var certPinner: CertificatePinning? - - public var usingTLS: Bool { - return self.isTLS - } - - public init(streamConfiguration: ((InputStream, OutputStream) -> Void)? = nil) { - super.init() - onConnect = streamConfiguration - } - - deinit { - inputStream?.delegate = nil - outputStream?.delegate = nil - } - - public func connect(url: URL, timeout: Double = 10, certificatePinning: CertificatePinning? = nil) { - guard let parts = getPartss(hostCaller: url.host, portCaller: url.port, schemeCaller: url.scheme) else { - delegate?.connectionChanged(state: .failed(FoundationTransportError.invalidRequest)) - return - } - self.certPinner = certificatePinning - self.isTLS = parts.isTLS - var readStream: Unmanaged? - var writeStream: Unmanaged? - let h = parts.host as NSString - CFStreamCreatePairWithSocketToHost(nil, h, UInt32(parts.port), &readStream, &writeStream) - inputStream = readStream!.takeRetainedValue() - outputStream = writeStream!.takeRetainedValue() - guard let inStream = inputStream, let outStream = outputStream else { - return - } - inStream.delegate = self - outStream.delegate = self - - if isTLS { - let key = CFStreamPropertyKey(rawValue: kCFStreamPropertySocketSecurityLevel) - CFReadStreamSetProperty(inStream, key, kCFStreamSocketSecurityLevelNegotiatedSSL) - CFWriteStreamSetProperty(outStream, key, kCFStreamSocketSecurityLevelNegotiatedSSL) - } - - onConnect?(inStream, outStream) - - isOpen = false - CFReadStreamSetDispatchQueue(inStream, workQueue) - CFWriteStreamSetDispatchQueue(outStream, workQueue) - inStream.open() - outStream.open() - - - workQueue.asyncAfter(deadline: .now() + timeout, execute: { [weak self] in - guard let s = self else { return } - if !s.isOpen { - s.delegate?.connectionChanged(state: .failed(FoundationTransportError.timeout)) - } - }) - } - - /// isTLSScheme returns true if the scheme is https or wss - func isTLSScheme(schemeCaller: String?) -> Bool { - guard let scheme = schemeCaller else { - return false - } - return HTTPWSHeader.defaultSSLSchemes.contains(scheme) - } - - /// getParts pulls host and port from the url. - func getPartss(hostCaller: String?, portCaller: Int?, schemeCaller: String?) -> URLParts? { - guard let host = hostCaller else { - return nil // no host, this isn't a valid url - } - let isTLS = isTLSScheme(schemeCaller: schemeCaller) - var port = portCaller ?? 0 - if portCaller == nil { - if isTLS { - port = 443 - } else { - port = 80 - } - } - return URLParts(port: port, host: host, isTLS: isTLS) - } - - public func disconnect() { - if let stream = inputStream { - stream.delegate = nil - CFReadStreamSetDispatchQueue(stream, nil) - stream.close() - } - if let stream = outputStream { - stream.delegate = nil - CFWriteStreamSetDispatchQueue(stream, nil) - stream.close() - } - isOpen = false - outputStream = nil - inputStream = nil - } - - public func register(delegate: TransportEventClient) { - self.delegate = delegate - } - - public func write(data: Data, completion: @escaping ((Error?) -> ())) { - guard let outStream = outputStream else { - completion(FoundationTransportError.invalidOutputStream) - return - } - var total = 0 - let buffer = UnsafeRawPointer((data as NSData).bytes).assumingMemoryBound(to: UInt8.self) - //NOTE: this might need to be dispatched to the work queue instead of being written inline. TBD. - while total < data.count { - let written = outStream.write(buffer, maxLength: data.count) - if written < 0 { - completion(FoundationTransportError.invalidOutputStream) - return - } - total += written - } - completion(nil) - } - - private func getSecurityData() -> (SecTrust?, String?) { - #if os(watchOS) - return (nil, nil) - #else - guard let outputStream = outputStream else { - return (nil, nil) - } - let trust = outputStream.property(forKey: kCFStreamPropertySSLPeerTrust as Stream.PropertyKey) as! SecTrust? - var domain = outputStream.property(forKey: kCFStreamSSLPeerName as Stream.PropertyKey) as! String? - - if domain == nil, - let sslContextOut = CFWriteStreamCopyProperty(outputStream, CFStreamPropertyKey(rawValue: kCFStreamPropertySSLContext)) as! SSLContext? { - var peerNameLen: Int = 0 - SSLGetPeerDomainNameLength(sslContextOut, &peerNameLen) - var peerName = Data(count: peerNameLen) - let _ = peerName.withUnsafeMutableBytes { (peerNamePtr: UnsafeMutablePointer) in - SSLGetPeerDomainName(sslContextOut, peerNamePtr, &peerNameLen) - } - if let peerDomain = String(bytes: peerName, encoding: .utf8), peerDomain.count > 0 { - domain = peerDomain - } - } - return (trust, domain) - #endif - } - - private func read() { - guard let stream = inputStream else { - return - } - let maxBuffer = 4096 - let buf = NSMutableData(capacity: maxBuffer) - let buffer = UnsafeMutableRawPointer(mutating: buf!.bytes).assumingMemoryBound(to: UInt8.self) - let length = stream.read(buffer, maxLength: maxBuffer) - if length < 1 { - return - } - let data = Data(bytes: buffer, count: length) - delegate?.connectionChanged(state: .receive(data)) - } - - // MARK: - StreamDelegate - - open func stream(_ aStream: Stream, handle eventCode: Stream.Event) { - switch eventCode { - case .hasBytesAvailable: - if aStream == inputStream { - read() - } - case .errorOccurred: - delegate?.connectionChanged(state: .failed(aStream.streamError)) - case .endEncountered: - if aStream == inputStream { - delegate?.connectionChanged(state: .cancelled) - } - case .openCompleted: - if aStream == inputStream { - let (trust, domain) = getSecurityData() - if let pinner = certPinner, let trust = trust { - pinner.evaluateTrust(trust: trust, domain: domain, completion: { [weak self] (state) in - switch state { - case .success: - self?.isOpen = true - self?.delegate?.connectionChanged(state: .connected) - case .failed(let error): - self?.delegate?.connectionChanged(state: .failed(error)) - } - - }) - } else { - isOpen = true - delegate?.connectionChanged(state: .connected) - } - } - case .endEncountered: - if aStream == inputStream { - delegate?.connectionChanged(state: .cancelled) - } - default: - break - } - } -} diff --git a/Sources/Clickstream/ThirdPartyLibraries/Sources/Transport/TCPTransport.swift b/Sources/Clickstream/ThirdPartyLibraries/Sources/Transport/TCPTransport.swift deleted file mode 100644 index 0c9dbb3..0000000 --- a/Sources/Clickstream/ThirdPartyLibraries/Sources/Transport/TCPTransport.swift +++ /dev/null @@ -1,193 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////////////////////// -// -// HTTPTransport.swift -// Starscream -// -// Created by Dalton Cherry on 1/23/19. -// Copyright © 2019 Vluxe. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -////////////////////////////////////////////////////////////////////////////////////////////////// - -#if canImport(Network) -import Foundation -import Network - -public enum TCPTransportError: Error { - case invalidRequest -} - -@available(macOS 10.14, iOS 12.0, watchOS 5.0, tvOS 12.0, *) -public class TCPTransport: Transport { - private var connection: NWConnection? - private let queue = DispatchQueue(label: "com.vluxe.starscream.networkstream", attributes: []) - private weak var delegate: TransportEventClient? - private var isRunning = false - private var isTLS = false - - public var usingTLS: Bool { - return self.isTLS - } - - public init(connection: NWConnection) { - self.connection = connection - start() - } - - public init() { - //normal connection, will use the "connect" method below - } - - public func connect(url: URL, timeout: Double = 10, certificatePinning: CertificatePinning? = nil) { - guard let parts = getPartss(hostCaller: url.host, portCaller: url.port, schemeCaller: url.scheme) else { - delegate?.connectionChanged(state: .failed(TCPTransportError.invalidRequest)) - return - } - self.isTLS = parts.isTLS - let options = NWProtocolTCP.Options() - options.connectionTimeout = Int(timeout.rounded(.up)) - - let tlsOptions = isTLS ? NWProtocolTLS.Options() : nil - if let tlsOpts = tlsOptions { - sec_protocol_options_set_verify_block(tlsOpts.securityProtocolOptions, { (sec_protocol_metadata, sec_trust, sec_protocol_verify_complete) in - let trust = sec_trust_copy_ref(sec_trust).takeRetainedValue() - guard let pinner = certificatePinning else { - sec_protocol_verify_complete(true) - return - } - pinner.evaluateTrust(trust: trust, domain: parts.host, completion: { (state) in - switch state { - case .success: - sec_protocol_verify_complete(true) - case .failed(_): - sec_protocol_verify_complete(false) - } - }) - }, queue) - } - let parameters = NWParameters(tls: tlsOptions, tcp: options) - let conn = NWConnection(host: NWEndpoint.Host.name(parts.host, nil), port: NWEndpoint.Port(rawValue: UInt16(parts.port))!, using: parameters) - connection = conn - start() - } - - /// isTLSScheme returns true if the scheme is https or wss - func isTLSScheme(schemeCaller: String?) -> Bool { - guard let scheme = schemeCaller else { - return false - } - return HTTPWSHeader.defaultSSLSchemes.contains(scheme) - } - - /// getParts pulls host and port from the url. - func getPartss(hostCaller: String?, portCaller: Int?, schemeCaller: String?) -> URLParts? { - guard let host = hostCaller else { - return nil // no host, this isn't a valid url - } - let isTLS = isTLSScheme(schemeCaller: schemeCaller) - var port = portCaller ?? 0 - if portCaller == nil { - if isTLS { - port = 443 - } else { - port = 80 - } - } - return URLParts(port: port, host: host, isTLS: isTLS) - } - - public func disconnect() { - isRunning = false - connection?.cancel() - } - - public func register(delegate: TransportEventClient) { - self.delegate = delegate - } - - public func write(data: Data, completion: @escaping ((Error?) -> ())) { - connection?.send(content: data, completion: .contentProcessed { (error) in - completion(error) - }) - } - - private func start() { - guard let conn = connection else { - return - } - conn.stateUpdateHandler = { [weak self] (newState) in - switch newState { - case .ready: - self?.delegate?.connectionChanged(state: .connected) - case .waiting(let error): - switch error { - case .posix(let errorCode): - if(errorCode == .ETIMEDOUT) { - self?.delegate?.connectionChanged(state: .timedOut) - } else { - self?.delegate?.connectionChanged(state: .waiting) - } - default: - self?.delegate?.connectionChanged(state: .waiting) - } - case .cancelled: - self?.delegate?.connectionChanged(state: .cancelled) - case .failed(let error): - self?.delegate?.connectionChanged(state: .failed(error)) - case .setup, .preparing: - break - @unknown default: - break - } - } - - conn.viabilityUpdateHandler = { [weak self] (isViable) in - self?.delegate?.connectionChanged(state: .viability(isViable)) - } - - conn.betterPathUpdateHandler = { [weak self] (isBetter) in - self?.delegate?.connectionChanged(state: .shouldReconnect(isBetter)) - } - - conn.start(queue: queue) - isRunning = true - readLoop() - } - - //readLoop keeps reading from the connection to get the latest content - private func readLoop() { - if !isRunning { - return - } - connection?.receive(minimumIncompleteLength: 2, maximumLength: 4096, completion: {[weak self] (data, context, isComplete, error) in - guard let s = self else {return} - if let data = data { - s.delegate?.connectionChanged(state: .receive(data)) - } - - // Refer to https://developer.apple.com/documentation/network/implementing_netcat_with_network_framework - if let context = context, context.isFinal, isComplete { - return - } - - if error == nil { - s.readLoop() - } - - }) - } -} -#else -typealias TCPTransport = FoundationTransport -#endif diff --git a/Sources/Clickstream/ThirdPartyLibraries/Sources/Transport/Transport.swift b/Sources/Clickstream/ThirdPartyLibraries/Sources/Transport/Transport.swift deleted file mode 100644 index 24a3206..0000000 --- a/Sources/Clickstream/ThirdPartyLibraries/Sources/Transport/Transport.swift +++ /dev/null @@ -1,56 +0,0 @@ -////////////////////////////////////////////////////////////////////////////////////////////////// -// -////////////////////////////////////////////////////////////////////////////////////////////////// -// -// Transport.swift -// Starscream -// -// Created by Dalton Cherry on 1/23/19. -// Copyright © 2019 Vluxe. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// -////////////////////////////////////////////////////////////////////////////////////////////////// - -import Foundation - -public enum ConnectionState { - case connected - case waiting - case cancelled - case timedOut - case failed(Error?) - - //the viability (connection status) of the connection has updated - //e.g. connection is down, connection came back up, etc - case viability(Bool) - - //the connection has upgrade to wifi from cellular. - //you should consider reconnecting to take advantage of this - case shouldReconnect(Bool) - - //the connection receive data - case receive(Data) -} - -public protocol TransportEventClient: AnyObject { - func connectionChanged(state: ConnectionState) -} - -public protocol Transport: AnyObject { - func register(delegate: TransportEventClient) - func connect(url: URL, timeout: Double, certificatePinning: CertificatePinning?) - func disconnect() - func write(data: Data, completion: @escaping ((Error?) -> ())) - var usingTLS: Bool { get } -} From b3b0b3a783188273d012219684100d7831e784bb Mon Sep 17 00:00:00 2001 From: AbhijeetMallick Date: Mon, 8 Apr 2024 14:09:13 +0530 Subject: [PATCH 2/4] Added Starscream as a POD --- Clickstream.xcodeproj/project.pbxproj | 14 +++++ .../Sockets/SocketHandler.swift | 56 +++++++++++-------- .../Performance/PerformanceTracer.swift | 47 ++++++++++++++++ .../Tracker/Utilities/TrackerConstants.swift | 8 +++ 4 files changed, 103 insertions(+), 22 deletions(-) create mode 100644 Sources/Tracker/Performance/PerformanceTracer.swift diff --git a/Clickstream.xcodeproj/project.pbxproj b/Clickstream.xcodeproj/project.pbxproj index e351748..4829ca0 100644 --- a/Clickstream.xcodeproj/project.pbxproj +++ b/Clickstream.xcodeproj/project.pbxproj @@ -13,6 +13,8 @@ 9A0BBA882BC3A59300F7DC01 /* URLRequest+Equality.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A0BBA872BC3A59300F7DC01 /* URLRequest+Equality.swift */; }; 9A0BBA892BC3A59300F7DC01 /* URLRequest+Equality.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A0BBA872BC3A59300F7DC01 /* URLRequest+Equality.swift */; }; 9A0BBA8A2BC3C98000F7DC01 /* SortedArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467EA2A5BD72000BFA976 /* SortedArray.swift */; }; + 9A0BBA8D2BC3CA5500F7DC01 /* PerformanceTracer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A0BBA8C2BC3CA5500F7DC01 /* PerformanceTracer.swift */; }; + 9A0BBA8E2BC3CA5500F7DC01 /* PerformanceTracer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A0BBA8C2BC3CA5500F7DC01 /* PerformanceTracer.swift */; }; BD6BDB6829FBC1360006B04A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BD6BDB3E29FBC1360006B04A /* Assets.xcassets */; }; BD6BDB6929FBC1360006B04A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BD6BDB3E29FBC1360006B04A /* Assets.xcassets */; }; BD6BDB6A29FBC1360006B04A /* RadioLabelTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BD6BDB4129FBC1360006B04A /* RadioLabelTableViewCell.swift */; }; @@ -223,6 +225,7 @@ 68E7BD1B2456E6F10072549A /* ClickstreamTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ClickstreamTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 88F9204101C5E8D41805FD1A /* Pods-ClickstreamTests.production.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ClickstreamTests.production.xcconfig"; path = "Target Support Files/Pods-ClickstreamTests/Pods-ClickstreamTests.production.xcconfig"; sourceTree = ""; }; 9A0BBA872BC3A59300F7DC01 /* URLRequest+Equality.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLRequest+Equality.swift"; sourceTree = ""; }; + 9A0BBA8C2BC3CA5500F7DC01 /* PerformanceTracer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PerformanceTracer.swift; sourceTree = ""; }; 9D31FA91B32979CBBA2C3849 /* Pods-ClickstreamTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ClickstreamTests.debug.xcconfig"; path = "Target Support Files/Pods-ClickstreamTests/Pods-ClickstreamTests.debug.xcconfig"; sourceTree = ""; }; A870FFC059A664D0DF0292E5 /* Pods-ClickstreamTests.alpha.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ClickstreamTests.alpha.xcconfig"; path = "Target Support Files/Pods-ClickstreamTests/Pods-ClickstreamTests.alpha.xcconfig"; sourceTree = ""; }; A8F29FD3E426DD04C26B8560 /* Pods-ClickstreamTests.integration.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ClickstreamTests.integration.xcconfig"; path = "Target Support Files/Pods-ClickstreamTests/Pods-ClickstreamTests.integration.xcconfig"; sourceTree = ""; }; @@ -395,6 +398,14 @@ name = Products; sourceTree = ""; }; + 9A0BBA8B2BC3CA4000F7DC01 /* Performance */ = { + isa = PBXGroup; + children = ( + 9A0BBA8C2BC3CA5500F7DC01 /* PerformanceTracer.swift */, + ); + path = Performance; + sourceTree = ""; + }; 9AAFBB13247CBA15009BFDFB /* Recovered References */ = { isa = PBXGroup; children = ( @@ -491,6 +502,7 @@ BD6BDB5429FBC1360006B04A /* Tracker */ = { isa = PBXGroup; children = ( + 9A0BBA8B2BC3CA4000F7DC01 /* Performance */, BD6BDB5529FBC1360006B04A /* Health */, BD6BDB5929FBC1360006B04A /* Tracker.swift */, BD6BDB5A29FBC1360006B04A /* Utilities */, @@ -1221,6 +1233,7 @@ buildActionMask = 2147483647; files = ( BD6BDB7229FBC1360006B04A /* EventVisualizerLandingViewController.swift in Sources */, + 9A0BBA8D2BC3CA5500F7DC01 /* PerformanceTracer.swift in Sources */, BD6BDB7629FBC1360006B04A /* EventsListViewModel.swift in Sources */, BD6BDB7429FBC1360006B04A /* EventsListViewController.swift in Sources */, BDE468232A5BD72000BFA976 /* JSONStringDecoder.swift in Sources */, @@ -1323,6 +1336,7 @@ BDE4677A2A5BD24500BFA976 /* DispatchQueue+Detection.swift in Sources */, BD6BDB9B29FBC1360006B04A /* ClickStreamHealthConfigurations.swift in Sources */, BDE467882A5BD24500BFA976 /* DeviceStatusNotifierTests.swift in Sources */, + 9A0BBA8E2BC3CA5500F7DC01 /* PerformanceTracer.swift in Sources */, BDE467852A5BD24500BFA976 /* NetworkManagerDependenciesTests.swift in Sources */, BDE467822A5BD24500BFA976 /* EventProcessorDependenciesTests.swift in Sources */, BDE467872A5BD24500BFA976 /* KeepAliveServiceTests.swift in Sources */, diff --git a/Sources/Clickstream/NetworkManager/Infrastructure/Sockets/SocketHandler.swift b/Sources/Clickstream/NetworkManager/Infrastructure/Sockets/SocketHandler.swift index dbed456..94204a3 100644 --- a/Sources/Clickstream/NetworkManager/Infrastructure/Sockets/SocketHandler.swift +++ b/Sources/Clickstream/NetworkManager/Infrastructure/Sockets/SocketHandler.swift @@ -37,8 +37,9 @@ final class DefaultSocketHandler: SocketHandler { private var writeCallback: ((Result) -> Void)? /// Tracking time taken by the socket to establish a connection - /// TODO:Abhijeet to fix -// private var socketConnectionTimeTrace: Trace = Trace(name: ClickStreamDebugConstants.Traces.ClickstreamSocketConnectionTime.rawValue) + #if TRACKER_ENABLED + private var socketConnectionTimeTrace: Trace = Trace(name: TrackerConstant.Traces.ClickstreamSocketConnectionTime.rawValue) + #endif /// Provides the socket state var isConnected: Atomic = Atomic(false) @@ -104,8 +105,10 @@ final class DefaultSocketHandler: SocketHandler { print("socket-connecting") isConnectionRequestOpen = true connectionCallback?(.success(.connecting)) -// socketConnectionTimeTrace.attributes = [Constants.Strings.networkType: Reachability.getNetworkType().trackingId] -// socketConnectionTimeTrace.start() + #if TRACKER_ENABLED + socketConnectionTimeTrace.attributes = [Constants.Strings.networkType: Reachability.getNetworkType().trackingId] + socketConnectionTimeTrace.start() + #endif webSocket?.connect() lastConnectRequestTimestamp = Date() // recording time } @@ -124,9 +127,11 @@ final class DefaultSocketHandler: SocketHandler { } deinit { -// socketConnectionTimeTrace.attributes = [Constants.Strings.networkType: Reachability.getNetworkType().trackingId, -// Constants.Strings.status: Constants.Strings.failure] -// socketConnectionTimeTrace.stop() + #if TRACKER_ENABLED + socketConnectionTimeTrace.attributes = [Constants.Strings.networkType: Reachability.getNetworkType().trackingId, + Constants.Strings.status: Constants.Strings.failure] + socketConnectionTimeTrace.stop() + #endif print("socket-deinit") } } @@ -175,31 +180,34 @@ extension DefaultSocketHandler { } checkedSelf.isConnectionRequestOpen = false checkedSelf.connectionCallback?(.success(.connected)) -// checkedSelf.socketConnectionTimeTrace.attributes = [Constants.Strings.networkType: Reachability.getNetworkType().trackingId, -// Constants.Strings.status: Constants.Strings.success] -// checkedSelf.socketConnectionTimeTrace.stop() - case .disconnected(_, let code): + #if TRACKER_ENABLED + checkedSelf.socketConnectionTimeTrace.attributes = [Constants.Strings.networkType: Reachability.getNetworkType().trackingId, + Constants.Strings.status: Constants.Strings.success] + checkedSelf.socketConnectionTimeTrace.stop() + #endif + case .disconnected(let error, let code): + print("disconnected with error: \(error) errorCode: \(code)", .critical) Clickstream.connectionState = .closed -// checkedSelf.trackHealthEvent(eventName: .ClickstreamConnectionFailed, code: code) checkedSelf.isConnectionRequestOpen = false -// checkedSelf.socketConnectionTimeTrace.attributes = [Constants.Strings.networkType: Reachability.getNetworkType().trackingId, -// Constants.Strings.status: Constants.Strings.failure] -// checkedSelf.socketConnectionTimeTrace.stop() + #if TRACKER_ENABLED + checkedSelf.socketConnectionTimeTrace.attributes = [Constants.Strings.networkType: Reachability.getNetworkType().trackingId, + Constants.Strings.status: Constants.Strings.failure] + checkedSelf.socketConnectionTimeTrace.stop() + let errorObject = NSError(domain: "", code: Int(code), userInfo: [NSLocalizedDescriptionKey: error]) + checkedSelf.trackHealthEvent(eventName: .ClickstreamConnectionDropped, error: errorObject, code: code) + #endif case .text(let responseString): checkedSelf.writeCallback?(.success(responseString.data(using: .utf8))) case .error(let error): checkedSelf.isConnectionRequestOpen = false - // checkedSelf.socketConnectionTimeTrace.attributes = [Constants.Strings.networkType: Reachability.getNetworkType().trackingId, - // Constants.Strings.status: Constants.Strings.failure] - // checkedSelf.socketConnectionTimeTrace.stop() - checkedSelf.writeCallback?(.failure(.failed)) - #if TRACKER_ENABLED - if Tracker.debugMode { + checkedSelf.socketConnectionTimeTrace.attributes = [Constants.Strings.networkType: Reachability.getNetworkType().trackingId, + Constants.Strings.status: Constants.Strings.failure] + checkedSelf.socketConnectionTimeTrace.stop() let timeInterval = Date().timeIntervalSince(checkedSelf.lastConnectRequestTimestamp ?? Date()) self?.trackHealthEvent(eventName: .ClickstreamConnectionFailure, error: error, timeToConnection: ("\(timeInterval)")) - } #endif + checkedSelf.writeCallback?(.failure(.failed)) case .binary(let response): checkedSelf.writeCallback?(.success(response)) case .cancelled: @@ -272,6 +280,10 @@ extension DefaultSocketHandler { let event = HealthAnalysisEvent(eventName: eventName, reason: FailureReason.DuplicateID.rawValue) Tracker.sharedInstance?.record(event: event) + } else { + let event = HealthAnalysisEvent(eventName: eventName, + reason: error?.localizedDescription) + Tracker.sharedInstance?.record(event: event) } } #endif diff --git a/Sources/Tracker/Performance/PerformanceTracer.swift b/Sources/Tracker/Performance/PerformanceTracer.swift new file mode 100644 index 0000000..5619d2b --- /dev/null +++ b/Sources/Tracker/Performance/PerformanceTracer.swift @@ -0,0 +1,47 @@ +// +// PerformanceTracer.swift +// Clickstream +// +// Created by Abhijeet Mallick on 08/04/24. +// Copyright © 2024 Gojek. All rights reserved. +// + +import Foundation + +protocol Traceable { + mutating func start() + mutating func stop() +} + +struct Trace: Traceable { + + private var isRunning: Bool = false + + var name: String + var attributes: [String:Any] + + init(name: String, + attributes:[String:Any] = [:]) { + self.name = name + self.attributes = attributes + } + + mutating func start() { + if Tracker.debugMode && isRunning == false { + let notificationDict: [String:Any] = [TrackerConstant.traceName: name, + TrackerConstant.traceAttributes: attributes] + NotificationCenter.default.post(name: TrackerConstant.TraceStartNotification, object: notificationDict) + isRunning = true + } + } + + mutating func stop() { + if Tracker.debugMode && isRunning { + isRunning = false + let notificationDict: [String:Any] = [TrackerConstant.traceName: name, + TrackerConstant.traceAttributes: attributes] + NotificationCenter.default.post(name: TrackerConstant.TraceStopNotification, object: notificationDict) + } + } +} + diff --git a/Sources/Tracker/Utilities/TrackerConstants.swift b/Sources/Tracker/Utilities/TrackerConstants.swift index f9338a2..6c3776d 100644 --- a/Sources/Tracker/Utilities/TrackerConstants.swift +++ b/Sources/Tracker/Utilities/TrackerConstants.swift @@ -49,6 +49,14 @@ enum FailureReason: String, Codable, CaseIterable { } public struct TrackerConstant { + + internal enum Traces: String { + case ClickstreamSocketConnectionTime = "ClickstreamSocketConnectionTimeTrace" + } + + public static var traceName = "traceName" + public static var traceAttributes = "traceAttributes" + //TODO: Added Traces after merging Health tracking public static let DebugEventsNotification = NSNotification.Name(rawValue: "ClickstreamDebugNotifications") public static let TraceStartNotification = NSNotification.Name(rawValue: "ClickstreamTraceStartNotification") From f57b24d24343d28bfefe43f63a59a5eabb56e4b5 Mon Sep 17 00:00:00 2001 From: AbhijeetMallick Date: Mon, 8 Apr 2024 14:13:05 +0530 Subject: [PATCH 3/4] Removed unused classes --- Clickstream.xcodeproj/project.pbxproj | 10 -- .../SortedArrayTests.swift | 86 ----------------- .../Utilities/SortedArray.swift | 94 ------------------- 3 files changed, 190 deletions(-) delete mode 100644 ClickstreamTests/EventSchedulerTests/SortedArrayTests.swift delete mode 100644 Sources/Clickstream/EventScheduler/Infrastructure/Utilities/SortedArray.swift diff --git a/Clickstream.xcodeproj/project.pbxproj b/Clickstream.xcodeproj/project.pbxproj index 4829ca0..7bdb69a 100644 --- a/Clickstream.xcodeproj/project.pbxproj +++ b/Clickstream.xcodeproj/project.pbxproj @@ -12,7 +12,6 @@ 98CDAC393665EEC635D0C690 /* libPods-Clickstream.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5E561B35C0082DAE47E3BDE1 /* libPods-Clickstream.a */; }; 9A0BBA882BC3A59300F7DC01 /* URLRequest+Equality.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A0BBA872BC3A59300F7DC01 /* URLRequest+Equality.swift */; }; 9A0BBA892BC3A59300F7DC01 /* URLRequest+Equality.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A0BBA872BC3A59300F7DC01 /* URLRequest+Equality.swift */; }; - 9A0BBA8A2BC3C98000F7DC01 /* SortedArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467EA2A5BD72000BFA976 /* SortedArray.swift */; }; 9A0BBA8D2BC3CA5500F7DC01 /* PerformanceTracer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A0BBA8C2BC3CA5500F7DC01 /* PerformanceTracer.swift */; }; 9A0BBA8E2BC3CA5500F7DC01 /* PerformanceTracer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A0BBA8C2BC3CA5500F7DC01 /* PerformanceTracer.swift */; }; BD6BDB6829FBC1360006B04A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BD6BDB3E29FBC1360006B04A /* Assets.xcassets */; }; @@ -86,7 +85,6 @@ BDE467792A5BD24500BFA976 /* AppStateNotifierServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467522A5BD24500BFA976 /* AppStateNotifierServiceTests.swift */; }; BDE4677A2A5BD24500BFA976 /* DispatchQueue+Detection.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467542A5BD24500BFA976 /* DispatchQueue+Detection.swift */; }; BDE4677B2A5BD24500BFA976 /* SchedulerServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467552A5BD24500BFA976 /* SchedulerServiceTests.swift */; }; - BDE4677C2A5BD24500BFA976 /* SortedArrayTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467562A5BD24500BFA976 /* SortedArrayTests.swift */; }; BDE4677D2A5BD24500BFA976 /* EventWarehouserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467572A5BD24500BFA976 /* EventWarehouserTests.swift */; }; BDE4677E2A5BD24500BFA976 /* DatabaseDAOTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE4675A2A5BD24500BFA976 /* DatabaseDAOTests.swift */; }; BDE4677F2A5BD24500BFA976 /* EventCreatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE4675B2A5BD24500BFA976 /* EventCreatorTests.swift */; }; @@ -177,7 +175,6 @@ BDE4686A2A5BD72000BFA976 /* EventBatchSizeRegulator.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467E82A5BD72000BFA976 /* EventBatchSizeRegulator.swift */; }; BDE4686B2A5BD72000BFA976 /* SchedulerService.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467E92A5BD72000BFA976 /* SchedulerService.swift */; }; BDE4686C2A5BD72000BFA976 /* SchedulerService.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467E92A5BD72000BFA976 /* SchedulerService.swift */; }; - BDE4686E2A5BD72000BFA976 /* SortedArray.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467EA2A5BD72000BFA976 /* SortedArray.swift */; }; BDE4686F2A5BD72000BFA976 /* DatabaseHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467EE2A5BD72000BFA976 /* DatabaseHandler.swift */; }; BDE468702A5BD72000BFA976 /* DatabaseHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467EE2A5BD72000BFA976 /* DatabaseHandler.swift */; }; BDE468712A5BD72000BFA976 /* DatabaseDAO.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE467EF2A5BD72000BFA976 /* DatabaseDAO.swift */; }; @@ -271,7 +268,6 @@ BDE467522A5BD24500BFA976 /* AppStateNotifierServiceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppStateNotifierServiceTests.swift; sourceTree = ""; }; BDE467542A5BD24500BFA976 /* DispatchQueue+Detection.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DispatchQueue+Detection.swift"; sourceTree = ""; }; BDE467552A5BD24500BFA976 /* SchedulerServiceTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchedulerServiceTests.swift; sourceTree = ""; }; - BDE467562A5BD24500BFA976 /* SortedArrayTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SortedArrayTests.swift; sourceTree = ""; }; BDE467572A5BD24500BFA976 /* EventWarehouserTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventWarehouserTests.swift; sourceTree = ""; }; BDE4675A2A5BD24500BFA976 /* DatabaseDAOTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseDAOTests.swift; sourceTree = ""; }; BDE4675B2A5BD24500BFA976 /* EventCreatorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventCreatorTests.swift; sourceTree = ""; }; @@ -325,7 +321,6 @@ BDE467E72A5BD72000BFA976 /* AppStateNotifierService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppStateNotifierService.swift; sourceTree = ""; }; BDE467E82A5BD72000BFA976 /* EventBatchSizeRegulator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventBatchSizeRegulator.swift; sourceTree = ""; }; BDE467E92A5BD72000BFA976 /* SchedulerService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchedulerService.swift; sourceTree = ""; }; - BDE467EA2A5BD72000BFA976 /* SortedArray.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SortedArray.swift; sourceTree = ""; }; BDE467EE2A5BD72000BFA976 /* DatabaseHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseHandler.swift; sourceTree = ""; }; BDE467EF2A5BD72000BFA976 /* DatabaseDAO.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DatabaseDAO.swift; sourceTree = ""; }; BDE467F22A5BD72000BFA976 /* TableDefinable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TableDefinable.swift; sourceTree = ""; }; @@ -620,7 +615,6 @@ BDE467522A5BD24500BFA976 /* AppStateNotifierServiceTests.swift */, BDE467532A5BD24500BFA976 /* Utilities */, BDE467552A5BD24500BFA976 /* SchedulerServiceTests.swift */, - BDE467562A5BD24500BFA976 /* SortedArrayTests.swift */, BDE467572A5BD24500BFA976 /* EventWarehouserTests.swift */, BDE467582A5BD24500BFA976 /* Data */, BDE4675B2A5BD24500BFA976 /* EventCreatorTests.swift */, @@ -972,7 +966,6 @@ BDE467E72A5BD72000BFA976 /* AppStateNotifierService.swift */, BDE467E82A5BD72000BFA976 /* EventBatchSizeRegulator.swift */, BDE467E92A5BD72000BFA976 /* SchedulerService.swift */, - BDE467EA2A5BD72000BFA976 /* SortedArray.swift */, ); path = Utilities; sourceTree = ""; @@ -1282,7 +1275,6 @@ BDE4686B2A5BD72000BFA976 /* SchedulerService.swift in Sources */, BDE468032A5BD72000BFA976 /* NetworkService.swift in Sources */, BDE468072A5BD72000BFA976 /* NetworkConfiguration.swift in Sources */, - 9A0BBA8A2BC3C98000F7DC01 /* SortedArray.swift in Sources */, BDE4686F2A5BD72000BFA976 /* DatabaseHandler.swift in Sources */, BDE468632A5BD72000BFA976 /* EventWarehouser.swift in Sources */, BDE468052A5BD72000BFA976 /* Connectable.swift in Sources */, @@ -1340,7 +1332,6 @@ BDE467852A5BD24500BFA976 /* NetworkManagerDependenciesTests.swift in Sources */, BDE467822A5BD24500BFA976 /* EventProcessorDependenciesTests.swift in Sources */, BDE467872A5BD24500BFA976 /* KeepAliveServiceTests.swift in Sources */, - BDE4686E2A5BD72000BFA976 /* SortedArray.swift in Sources */, BDE468162A5BD72000BFA976 /* EventRequest.swift in Sources */, BD6BDB8929FBC1360006B04A /* HealthAnalysisEvent.swift in Sources */, BDE467782A5BD24500BFA976 /* EventSchedulerDependenciesTests.swift in Sources */, @@ -1367,7 +1358,6 @@ BDE468142A5BD72000BFA976 /* ProtoConvertible.swift in Sources */, BDE468282A5BD72000BFA976 /* DeviceInfo.swift in Sources */, BDE4677B2A5BD24500BFA976 /* SchedulerServiceTests.swift in Sources */, - BDE4677C2A5BD24500BFA976 /* SortedArrayTests.swift in Sources */, BDE468602A5BD72000BFA976 /* EventBatchCreator.swift in Sources */, BDE468202A5BD72000BFA976 /* Constants.swift in Sources */, BDE467712A5BD24500BFA976 /* ClickStreamDependenciesTests.swift in Sources */, diff --git a/ClickstreamTests/EventSchedulerTests/SortedArrayTests.swift b/ClickstreamTests/EventSchedulerTests/SortedArrayTests.swift deleted file mode 100644 index d1a6f36..0000000 --- a/ClickstreamTests/EventSchedulerTests/SortedArrayTests.swift +++ /dev/null @@ -1,86 +0,0 @@ -// -// SortedArrayTests.swift -// ClickstreamTests -// -// Created by Abhijeet Mallick on 20/05/20. -// Copyright © 2020 Gojek. All rights reserved. -// - -@testable import Clickstream -import XCTest - -class SortedArrayTests: XCTestCase { - - func testInitWithArray() { - let sortedArray = SortedArray(sequence: ["a", "c" , "d", "e" ,"b"]) - XCTAssertEqual(Array(sortedArray), ["a", "b", "c", "d", "e"]) - } - - func testInit() { - // given - let unsortedArray = ["a", "c" , "d", "e" ,"b"] - - // when - var sortedArray = SortedArray() - sortedArray.append(with: unsortedArray) - - // then - XCTAssertEqual(Array(sortedArray), ["a", "b", "c", "d", "e"]) - } - - func testAppendSingleElement() { - var sortedArray = SortedArray(sequence: ["b", "c", "d", "e"]) - sortedArray.append(with: "a") - XCTAssertEqual(Array(sortedArray), ["a", "b" ,"c", "d", "e"]) - } - - func testAppendSequenceOfElements() { - var sortedArray = SortedArray(sequence: ["a", "b", "c", "e"]) - sortedArray.append(with: ["d", "f"]) - XCTAssertEqual(Array(sortedArray), ["a", "b", "c", "d", "e", "f"]) - } - - func testRemoveElementAtIndex() { - var sortedArray = SortedArray(sequence: ["a", "c" , "d", "e" ,"b"]) - sortedArray.remove(at: 2) - XCTAssertEqual(Array(sortedArray), ["a", "b", "d", "e"]) - } - - func testRemoveAllElements() { - var sortedArray = SortedArray(sequence: ["a", "c" , "d", "e" ,"b"]) - sortedArray.removeAll() - XCTAssert(sortedArray.isEmpty) - } - - func testRemoveFirst() { - var sortedArray = SortedArray(sequence: ["a", "c" , "d", "e" ,"b"]) - sortedArray.removeFirst(2) - XCTAssertEqual(Array(sortedArray), ["c", "d", "e"]) - } - - func testPrefixAndRemoveElement() { - var sortedArray = SortedArray(sequence: ["a", "c" , "d", "e" ,"b"]) - let firstTwoElements = sortedArray.prefixAndRemove(2) - XCTAssertEqual(Array(sortedArray), ["c", "d", "e"]) - XCTAssertEqual(firstTwoElements, ["a", "b"]) - } - - func testPrefixAndRemoveAll() { - var sortedArray = SortedArray(sequence: ["a", "c" , "d", "e" ,"b"]) - let allElements = sortedArray.prefixAndRemoveAll() - XCTAssert(sortedArray.isEmpty) - XCTAssertEqual(allElements, ["a", "b", "c", "d", "e"]) - } - - func testIndexBefore() { - // given - let indexBefore = 2 - let sortedArray = SortedArray(sequence: ["a", "c" , "d", "e" ,"b"]) - - // when - let index = sortedArray.index(before: indexBefore) - - // then - XCTAssertEqual(index, indexBefore - 1 ) - } -} diff --git a/Sources/Clickstream/EventScheduler/Infrastructure/Utilities/SortedArray.swift b/Sources/Clickstream/EventScheduler/Infrastructure/Utilities/SortedArray.swift deleted file mode 100644 index 308c67c..0000000 --- a/Sources/Clickstream/EventScheduler/Infrastructure/Utilities/SortedArray.swift +++ /dev/null @@ -1,94 +0,0 @@ -// -// SortedArray.swift -// Clickstream -// -// Created by Abhijeet Mallick on 20/05/20. -// Copyright © 2020 Gojek. All rights reserved. -// - -import Foundation - -struct SortedArray where Element: Comparable { - private var elements: [Element] - - /// determines the sort order of array - private let sortingOrder: (Element, Element) -> Bool - - init() { - self.init(sortingOrder: <) - } - - init(sortingOrder: @escaping (Element, Element) -> Bool) { - self.sortingOrder = sortingOrder - self.elements = [] - } - - init(sequence: S) where S.Iterator.Element == Element { - self.init(sequence: sequence, sortingOrder: <) - } - - init(sequence: S, sortingOrder: @escaping (Element, Element) -> Bool) where S.Iterator.Element == Element { - let sorted = sequence.sorted(by: sortingOrder) - self.elements = sorted - self.sortingOrder = sortingOrder - } - - mutating func append(with element: Element) { - self.elements.append(element) - self.elements.sort(by: self.sortingOrder) - } - - mutating func append(with elements: S) where S.Iterator.Element == Element { - self.elements.append(contentsOf: elements) - self.elements.sort(by: self.sortingOrder) - } - - mutating func remove(at index: Int) { - self.elements.remove(at: index) - } - - mutating func removeFirst(_ n: Int) { - self.elements.removeFirst(n) - } - - mutating func removeAll() { - self.elements.removeAll() - } - - mutating func prefixAndRemove(_ n: Int) -> [Element] { - let prefixArray = self.elements.prefix(n) - self.removeFirst(n) - return Array(prefixArray) - } - - mutating func prefixAndRemoveAll() -> [Element] { - defer { - self.removeAll() - } - return self.elements - } -} - -/// Conform to Collection Protocol so that we can accss all the properties/methods of Collection, such as count, contains, filter etc -extension SortedArray: Collection { - - public var startIndex: Int { - return elements.startIndex - } - - public var endIndex: Int { - return elements.endIndex - } - - public func index(after i: Int) -> Int { - return elements.index(after: i) - } - - public func index(before i: Int) -> Int { - return elements.index(before: i) - } - - public subscript(position: Int) -> Element { - return elements[position] - } -} From 0c802e582bfe969907dd23d18fe3f338e393ffd6 Mon Sep 17 00:00:00 2001 From: AbhijeetMallick Date: Mon, 8 Apr 2024 14:28:18 +0530 Subject: [PATCH 4/4] Removed unused DefaultKeepAliveService class --- .../EventProcessorDependenciesTests.swift | 2 +- .../EventProcessorTest.swift | 4 +- .../EventBatchProcessorTests.swift | 4 +- .../EventCreatorTests.swift | 8 ++-- .../EventSchedulerDependenciesTests.swift | 2 +- .../EventWarehouserTests.swift | 2 +- .../Infrastructure/NetworkBuilderTests.swift | 6 +-- .../Infrastructure/RetryMechanismTests.swift | 2 +- .../Utilities/KeepAliveServiceTests.swift | 4 +- .../Utilities/KeepAliveService.swift | 44 ------------------- 10 files changed, 17 insertions(+), 61 deletions(-) diff --git a/ClickstreamTests/EventProcessorTests/EventProcessorDependenciesTests.swift b/ClickstreamTests/EventProcessorTests/EventProcessorDependenciesTests.swift index 8f82070..4eeffbc 100644 --- a/ClickstreamTests/EventProcessorTests/EventProcessorDependenciesTests.swift +++ b/ClickstreamTests/EventProcessorTests/EventProcessorDependenciesTests.swift @@ -25,7 +25,7 @@ class EventProcessorDependenciesTests: XCTestCase { let deviceStatus = DefaultDeviceStatus(performOnQueue: mockQueue) let persistence = DefaultDatabaseDAO(database: database, performOnQueue: dbQueueMock) let eventPersistence = DefaultDatabaseDAO(database: database, performOnQueue: mockQueue) - let keepAliveService = DefaultKeepAliveService(with: mockQueue, duration: 2, reachability: NetworkReachabilityMock(isReachable: true)) + let keepAliveService = DefaultKeepAliveServiceWithSafeTimer(with: mockQueue, duration: 2, reachability: NetworkReachabilityMock(isReachable: true)) let retryMech = DefaultRetryMechanism(networkService: networkService, reachability: NetworkReachabilityMock(isReachable: true), deviceStatus: deviceStatus, appStateNotifier: AppStateNotifierMock(state: .didBecomeActive), performOnQueue: mockQueue, persistence: persistence, keepAliveService: keepAliveService) diff --git a/ClickstreamTests/EventProcessorTests/EventProcessorTest.swift b/ClickstreamTests/EventProcessorTests/EventProcessorTest.swift index ee45a04..ecebbea 100644 --- a/ClickstreamTests/EventProcessorTests/EventProcessorTest.swift +++ b/ClickstreamTests/EventProcessorTests/EventProcessorTest.swift @@ -25,7 +25,7 @@ class EventProcessorTest: XCTestCase { private var eventWarehouser: DefaultEventWarehouser! private var persistence: DefaultDatabaseDAO! private var eventPersistence: DefaultDatabaseDAO! - private var keepAliveService: DefaultKeepAliveService! + private var keepAliveService: DefaultKeepAliveServiceWithSafeTimer! private let dbQueueMock = SerialQueue(label: "com.mock.gojek.clickstream.network", qos: .utility, attributes: .concurrent) private let database = try! DefaultDatabase(qos: .WAL) private let batchSizeRegulator = BatchSizeRegulatorMock() @@ -38,7 +38,7 @@ class EventProcessorTest: XCTestCase { persistence = DefaultDatabaseDAO(database: database, performOnQueue: dbQueueMock) eventPersistence = DefaultDatabaseDAO(database: database, performOnQueue: dbQueueMock) - keepAliveService = DefaultKeepAliveService(with: processorQueueMock, duration: 2, reachability: NetworkReachabilityMock(isReachable: true)) + keepAliveService = DefaultKeepAliveServiceWithSafeTimer(with: processorQueueMock, duration: 2, reachability: NetworkReachabilityMock(isReachable: true)) retryMech = DefaultRetryMechanism(networkService: networkService, reachability: NetworkReachabilityMock(isReachable: true), deviceStatus: DefaultDeviceStatus(performOnQueue: processorQueueMock), appStateNotifier: AppStateNotifierMock(state: .didBecomeActive), performOnQueue: processorQueueMock, persistence: persistence, keepAliveService: keepAliveService) networkBuilder = DefaultNetworkBuilder(networkConfigs: config, retryMech: retryMech, performOnQueue: processorQueueMock) diff --git a/ClickstreamTests/EventSchedulerTests/EventBatchProcessorTests.swift b/ClickstreamTests/EventSchedulerTests/EventBatchProcessorTests.swift index 4b3fcea..4c59111 100644 --- a/ClickstreamTests/EventSchedulerTests/EventBatchProcessorTests.swift +++ b/ClickstreamTests/EventSchedulerTests/EventBatchProcessorTests.swift @@ -36,7 +36,7 @@ class EventBatchProcessorTests: XCTestCase { let persistence = DefaultDatabaseDAO(database: database, performOnQueue: dbQueueMock) let eventPersistence = DefaultDatabaseDAO(database: database, performOnQueue: dbQueueMock) - let keepAliveService = DefaultKeepAliveService(with: schedulerQueueMock, duration: 2, reachability: NetworkReachabilityMock(isReachable: true)) + let keepAliveService = DefaultKeepAliveServiceWithSafeTimer(with: schedulerQueueMock, duration: 2, reachability: NetworkReachabilityMock(isReachable: true)) let retryMech = DefaultRetryMechanism(networkService: networkService, reachability: NetworkReachabilityMock(isReachable: true), deviceStatus: DefaultDeviceStatus(performOnQueue: schedulerQueueMock), appStateNotifier: AppStateNotifierMock(state: .didBecomeActive), performOnQueue: schedulerQueueMock, persistence: persistence, keepAliveService: keepAliveService) let networkBuilder = DefaultNetworkBuilder(networkConfigs: config, retryMech: retryMech, performOnQueue: schedulerQueueMock) @@ -81,7 +81,7 @@ class EventBatchProcessorTests: XCTestCase { let persistence = DefaultDatabaseDAO(database: database, performOnQueue: dbQueueMock) let eventPersistence = DefaultDatabaseDAO(database: database, performOnQueue: dbQueueMock) - let keepAliveService = DefaultKeepAliveService(with: schedulerQueueMock, duration: 2, reachability: NetworkReachabilityMock(isReachable: true)) + let keepAliveService = DefaultKeepAliveServiceWithSafeTimer(with: schedulerQueueMock, duration: 2, reachability: NetworkReachabilityMock(isReachable: true)) let retryMech = DefaultRetryMechanism(networkService: networkService, reachability: NetworkReachabilityMock(isReachable: true), deviceStatus: DefaultDeviceStatus(performOnQueue: schedulerQueueMock), appStateNotifier: AppStateNotifierMock(state: .didBecomeActive), performOnQueue: schedulerQueueMock, persistence: persistence, keepAliveService: keepAliveService) let networkBuilder = DefaultNetworkBuilder(networkConfigs: config, retryMech: retryMech, performOnQueue: schedulerQueueMock) diff --git a/ClickstreamTests/EventSchedulerTests/EventCreatorTests.swift b/ClickstreamTests/EventSchedulerTests/EventCreatorTests.swift index 22821d1..ec3ff99 100644 --- a/ClickstreamTests/EventSchedulerTests/EventCreatorTests.swift +++ b/ClickstreamTests/EventSchedulerTests/EventCreatorTests.swift @@ -24,7 +24,7 @@ class EventCreatorTests: XCTestCase { let networkService = DefaultNetworkService(with: config, performOnQueue: networkQueue) let deviceStatus = DefaultDeviceStatus(performOnQueue: networkQueue) let persistence = DefaultDatabaseDAO(database: database, performOnQueue: dbQueueMock) - let keepAliveService = DefaultKeepAliveService(with: schedulerQueueMock, duration: 2, reachability: NetworkReachabilityMock(isReachable: true)) + let keepAliveService = DefaultKeepAliveServiceWithSafeTimer(with: schedulerQueueMock, duration: 2, reachability: NetworkReachabilityMock(isReachable: true)) let retryMech = DefaultRetryMechanism(networkService: networkService, reachability: NetworkReachabilityMock(isReachable: true), deviceStatus: deviceStatus, appStateNotifier: AppStateNotifierMock(state: .didBecomeActive), performOnQueue: networkQueue, persistence: persistence,keepAliveService: keepAliveService) let networkBuilder = DefaultNetworkBuilder(networkConfigs: config, retryMech: retryMech, performOnQueue: networkQueue) @@ -43,7 +43,7 @@ class EventCreatorTests: XCTestCase { let deviceStatus = DefaultDeviceStatus(performOnQueue: networkQueue) let networkService = DefaultNetworkService(with: config, performOnQueue: networkQueue) let persistence = DefaultDatabaseDAO(database: database, performOnQueue: dbQueueMock) - let keepAliveService = DefaultKeepAliveService(with: schedulerQueueMock, duration: 2, reachability: NetworkReachabilityMock(isReachable: true)) + let keepAliveService = DefaultKeepAliveServiceWithSafeTimer(with: schedulerQueueMock, duration: 2, reachability: NetworkReachabilityMock(isReachable: true)) let retryMech = DefaultRetryMechanism(networkService: networkService, reachability: NetworkReachabilityMock(isReachable: false), deviceStatus: deviceStatus, appStateNotifier: AppStateNotifierMock(state: .didBecomeActive), performOnQueue: networkQueue, persistence: persistence, keepAliveService: keepAliveService) let networkBuilder = DefaultNetworkBuilder(networkConfigs: config, retryMech: retryMech, performOnQueue: networkQueue) @@ -61,7 +61,7 @@ class EventCreatorTests: XCTestCase { let event = Event(guid: "", timestamp: Date(), type: "realTime", eventProtoData: Data()) let networkService = DefaultNetworkService(with: config, performOnQueue: networkQueue) let persistence = DefaultDatabaseDAO(database: database, performOnQueue: dbQueueMock) - let keepAliveService = DefaultKeepAliveService(with: schedulerQueueMock, duration: 2, reachability: NetworkReachabilityMock(isReachable: true)) + let keepAliveService = DefaultKeepAliveServiceWithSafeTimer(with: schedulerQueueMock, duration: 2, reachability: NetworkReachabilityMock(isReachable: true)) let retryMech = DefaultRetryMechanism(networkService: networkService, reachability: NetworkReachabilityMock(isReachable: true), deviceStatus: deviceStatus, appStateNotifier: AppStateNotifierMock(state: .didBecomeActive), performOnQueue: networkQueue, persistence: persistence, keepAliveService: keepAliveService) let networkBuilder = DefaultNetworkBuilder(networkConfigs: config, retryMech: retryMech, performOnQueue: networkQueue) @@ -82,7 +82,7 @@ class EventCreatorTests: XCTestCase { let deviceStatus = DefaultDeviceStatus(performOnQueue: networkQueue) let networkService = DefaultNetworkService(with: config, performOnQueue: networkQueue) let persistence = DefaultDatabaseDAO(database: database, performOnQueue: dbQueueMock) - let keepAliveService = DefaultKeepAliveService(with: schedulerQueueMock, duration: 2, reachability: NetworkReachabilityMock(isReachable: true)) + let keepAliveService = DefaultKeepAliveServiceWithSafeTimer(with: schedulerQueueMock, duration: 2, reachability: NetworkReachabilityMock(isReachable: true)) let retryMech = DefaultRetryMechanism(networkService: networkService, reachability: NetworkReachabilityMock(isReachable: false), deviceStatus: deviceStatus, appStateNotifier: AppStateNotifierMock(state: .didBecomeActive), performOnQueue: networkQueue, persistence: persistence, keepAliveService: keepAliveService) let networkBuilder = DefaultNetworkBuilder(networkConfigs: config, retryMech: retryMech, performOnQueue: networkQueue) diff --git a/ClickstreamTests/EventSchedulerTests/EventSchedulerDependenciesTests.swift b/ClickstreamTests/EventSchedulerTests/EventSchedulerDependenciesTests.swift index 061eb2d..93c01c9 100644 --- a/ClickstreamTests/EventSchedulerTests/EventSchedulerDependenciesTests.swift +++ b/ClickstreamTests/EventSchedulerTests/EventSchedulerDependenciesTests.swift @@ -23,7 +23,7 @@ class EventSchedulerDependenciesTests: XCTestCase { let networkService = DefaultNetworkService(with: config, performOnQueue: mockQueue) let deviceStatus = DefaultDeviceStatus(performOnQueue: mockQueue) let persistence = DefaultDatabaseDAO(database: database, performOnQueue: dbQueueMock) - let keepAliveService = DefaultKeepAliveService(with: mockQueue, duration: 2, reachability: NetworkReachabilityMock(isReachable: true)) + let keepAliveService = DefaultKeepAliveServiceWithSafeTimer(with: mockQueue, duration: 2, reachability: NetworkReachabilityMock(isReachable: true)) let retryMech = DefaultRetryMechanism(networkService: networkService, reachability: NetworkReachabilityMock(isReachable: true), deviceStatus: deviceStatus, appStateNotifier: AppStateNotifierMock(state: .didBecomeActive), performOnQueue: mockQueue, persistence: persistence, keepAliveService: keepAliveService) let networkBuildable: NetworkBuildable = DefaultNetworkBuilder.init(networkConfigs: config, retryMech: retryMech, performOnQueue: mockQueue) diff --git a/ClickstreamTests/EventSchedulerTests/EventWarehouserTests.swift b/ClickstreamTests/EventSchedulerTests/EventWarehouserTests.swift index 44c9a9e..df939af 100644 --- a/ClickstreamTests/EventSchedulerTests/EventWarehouserTests.swift +++ b/ClickstreamTests/EventSchedulerTests/EventWarehouserTests.swift @@ -36,7 +36,7 @@ class EventWarehouserTests: XCTestCase { let persistence = DefaultDatabaseDAO(database: database, performOnQueue: dbQueueMock) let networkService = DefaultNetworkService(with: config, performOnQueue: .main) - let keepAliveService = DefaultKeepAliveService(with: schedulerQueueMock, duration: 2, reachability: NetworkReachabilityMock(isReachable: true)) + let keepAliveService = DefaultKeepAliveServiceWithSafeTimer(with: schedulerQueueMock, duration: 2, reachability: NetworkReachabilityMock(isReachable: true)) let retryMech = DefaultRetryMechanism(networkService: networkService, reachability: NetworkReachabilityMock(isReachable: true), deviceStatus: DefaultDeviceStatus(performOnQueue: schedulerQueueMock), appStateNotifier: AppStateNotifierMock(state: .didBecomeActive), performOnQueue: schedulerQueueMock, persistence: DefaultDatabaseDAO(database: database, performOnQueue: dbQueueMock), keepAliveService: keepAliveService) diff --git a/ClickstreamTests/NetworkManagerTests/Infrastructure/NetworkBuilderTests.swift b/ClickstreamTests/NetworkManagerTests/Infrastructure/NetworkBuilderTests.swift index 1480f2f..33c276d 100644 --- a/ClickstreamTests/NetworkManagerTests/Infrastructure/NetworkBuilderTests.swift +++ b/ClickstreamTests/NetworkManagerTests/Infrastructure/NetworkBuilderTests.swift @@ -30,7 +30,7 @@ class NetworkBuilderTests: XCTestCase { let deviceStatus = DefaultDeviceStatus(performOnQueue: mockQueue) let networkService = DefaultNetworkService(with: config, performOnQueue: mockQueue) - let keepAliveService = DefaultKeepAliveService(with: mockQueue, duration: 2, reachability: NetworkReachabilityMock(isReachable: true)) + let keepAliveService = DefaultKeepAliveServiceWithSafeTimer(with: mockQueue, duration: 2, reachability: NetworkReachabilityMock(isReachable: true)) let persistence = DefaultDatabaseDAO(database: database, performOnQueue: dbQueueMock) let retryMech = DefaultRetryMechanism(networkService: networkService, reachability: NetworkReachabilityMock(isReachable: true), deviceStatus: deviceStatus, appStateNotifier: AppStateNotifierMock(state: .didBecomeActive), performOnQueue: mockQueue, persistence: persistence, keepAliveService: keepAliveService) @@ -56,7 +56,7 @@ class NetworkBuilderTests: XCTestCase { let deviceStatus = DefaultDeviceStatus(performOnQueue: mockQueue) let networkService = DefaultNetworkService(with: config, performOnQueue: .main) let persistence = DefaultDatabaseDAO(database: database, performOnQueue: dbQueueMock) - let keepAliveService = DefaultKeepAliveService(with: mockQueue, duration: 2, reachability: NetworkReachabilityMock(isReachable: true)) + let keepAliveService = DefaultKeepAliveServiceWithSafeTimer(with: mockQueue, duration: 2, reachability: NetworkReachabilityMock(isReachable: true)) let retryMech = DefaultRetryMechanism(networkService: networkService, reachability: NetworkReachabilityMock(isReachable: true), deviceStatus: deviceStatus, appStateNotifier: AppStateNotifierMock(state: .didBecomeActive), performOnQueue: mockQueue, persistence: persistence, keepAliveService: keepAliveService) @@ -83,7 +83,7 @@ class NetworkBuilderTests: XCTestCase { let deviceStatus = DefaultDeviceStatus(performOnQueue: mockQueue) let networkService = DefaultNetworkService(with: config, performOnQueue: .main) let persistence = DefaultDatabaseDAO(database: database, performOnQueue: dbQueueMock) - let keepAliveService = DefaultKeepAliveService(with: mockQueue, duration: 10, reachability: NetworkReachabilityMock(isReachable: true)) + let keepAliveService = DefaultKeepAliveServiceWithSafeTimer(with: mockQueue, duration: 10, reachability: NetworkReachabilityMock(isReachable: true)) let retryMech = DefaultRetryMechanism(networkService: networkService, reachability: NetworkReachabilityMock(isReachable: true), deviceStatus: deviceStatus, appStateNotifier: AppStateNotifierMock(state: .willResignActive), performOnQueue: mockQueue, persistence: persistence, keepAliveService: keepAliveService) diff --git a/ClickstreamTests/NetworkManagerTests/Infrastructure/RetryMechanismTests.swift b/ClickstreamTests/NetworkManagerTests/Infrastructure/RetryMechanismTests.swift index 7836857..972a0a3 100644 --- a/ClickstreamTests/NetworkManagerTests/Infrastructure/RetryMechanismTests.swift +++ b/ClickstreamTests/NetworkManagerTests/Infrastructure/RetryMechanismTests.swift @@ -71,7 +71,7 @@ class RetryMechanismTests: XCTestCase { let deviceStatus = DefaultDeviceStatus(performOnQueue: mockQueue) let networkService = DefaultNetworkService(with: config, performOnQueue: .main) let persistence = DefaultDatabaseDAO(database: database, performOnQueue: dbQueueMock) - let keepAliveService = DefaultKeepAliveService(with: mockQueue, duration: 2, reachability: NetworkReachabilityMock(isReachable: false)) + let keepAliveService = DefaultKeepAliveServiceWithSafeTimer(with: mockQueue, duration: 2, reachability: NetworkReachabilityMock(isReachable: false)) //when let sut = DefaultRetryMechanism(networkService: networkService, reachability: NetworkReachabilityMock(isReachable: true), deviceStatus: deviceStatus, appStateNotifier: AppStateNotifierMock(state: .didBecomeActive), performOnQueue: mockQueue, persistence: persistence, keepAliveService: keepAliveService) diff --git a/ClickstreamTests/NetworkManagerTests/Utilities/KeepAliveServiceTests.swift b/ClickstreamTests/NetworkManagerTests/Utilities/KeepAliveServiceTests.swift index 97d7c18..9505dc4 100644 --- a/ClickstreamTests/NetworkManagerTests/Utilities/KeepAliveServiceTests.swift +++ b/ClickstreamTests/NetworkManagerTests/Utilities/KeepAliveServiceTests.swift @@ -18,7 +18,7 @@ class KeepAliveServiceTests: XCTestCase { let mockQueue = SerialQueue(label: "com.mock.gojek.clickstream.keepAlive", qos: .utility) SerialQueue.registerDetection(of: mockQueue) //Registers a queue to be detected. - let sut = DefaultKeepAliveService(with: mockQueue, duration: 2.0, reachability: NetworkReachabilityMock(isReachable: true)) + let sut = DefaultKeepAliveServiceWithSafeTimer(with: mockQueue, duration: 2.0, reachability: NetworkReachabilityMock(isReachable: true)) //when sut.start { let queueName = SerialQueue.currentQueueLabel ?? "" @@ -34,7 +34,7 @@ class KeepAliveServiceTests: XCTestCase { var callbackCount = 0 let mockQueue = SerialQueue(label: "com.mock.gojek.clickstream.keepAlive", qos: .utility) - let sut = DefaultKeepAliveService(with: mockQueue, duration: 1.0, reachability: NetworkReachabilityMock(isReachable: true)) + let sut = DefaultKeepAliveServiceWithSafeTimer(with: mockQueue, duration: 1.0, reachability: NetworkReachabilityMock(isReachable: true)) //when sut.start { diff --git a/Sources/Clickstream/NetworkManager/Infrastructure/Utilities/KeepAliveService.swift b/Sources/Clickstream/NetworkManager/Infrastructure/Utilities/KeepAliveService.swift index 5152322..be47642 100644 --- a/Sources/Clickstream/NetworkManager/Infrastructure/Utilities/KeepAliveService.swift +++ b/Sources/Clickstream/NetworkManager/Infrastructure/Utilities/KeepAliveService.swift @@ -19,50 +19,6 @@ protocol KeepAliveServiceOutputs { } protocol KeepAliveService: KeepAliveServiceInputs, KeepAliveServiceOutputs { } -final class DefaultKeepAliveService: KeepAliveService { - - private let performQueue: SerialQueue - private let duration: TimeInterval - private let reachability: NetworkReachability - - private var timer: DispatchSourceTimer? - private var subscriber: KeepAliveCallback? - - init(with performOnQueue: SerialQueue, - duration: TimeInterval, - reachability: NetworkReachability) { - self.performQueue = performOnQueue - self.duration = duration - self.reachability = reachability - } - - func start(with subscriber: @escaping KeepAliveCallback) { - stop() - makeTimer()?.resume() - self.subscriber = subscriber - } - - @discardableResult - private func makeTimer() -> DispatchSourceTimer? { - guard timer == nil else { return timer } - timer = DispatchSource.makeTimerSource(flags: .strict, queue: performQueue) - let timerDuration = duration*reachability.connectionRetryCoefficient - timer?.schedule(deadline: .now() + timerDuration, repeating: timerDuration) - timer?.setEventHandler(handler: { [weak self] in - guard let checkedSelf = self else { return } - checkedSelf.performQueue.async { - checkedSelf.subscriber?() - } - }) - return timer - } - - func stop() { - timer?.cancel() - timer = nil - } -} - final class DefaultKeepAliveServiceWithSafeTimer: KeepAliveService { private let performQueue: SerialQueue