From c1704272fb3b127294837557bd4eb890c4a33473 Mon Sep 17 00:00:00 2001 From: Gareth Reese Date: Thu, 19 Sep 2024 13:51:09 +0100 Subject: [PATCH] Run swift format and update the swift format package --- FlagsmithClient/Classes/FlagEvent.swift | 2 +- .../Classes/Flagsmith+Concurrency.swift | 5 +- FlagsmithClient/Classes/Flagsmith.swift | 28 +-- .../Classes/Internal/APIManager.swift | 5 +- .../Classes/Internal/ReconnectionDelay.swift | 2 +- .../Classes/Internal/SSEManager.swift | 38 ++-- FlagsmithClient/Classes/Traits.swift | 2 +- FlagsmithClient/Tests/SSEManagerTests.swift | 45 +++-- Package.resolved | 186 +++++++++--------- 9 files changed, 154 insertions(+), 159 deletions(-) diff --git a/FlagsmithClient/Classes/FlagEvent.swift b/FlagsmithClient/Classes/FlagEvent.swift index 9ba79dc..1f54ddc 100644 --- a/FlagsmithClient/Classes/FlagEvent.swift +++ b/FlagsmithClient/Classes/FlagEvent.swift @@ -11,6 +11,6 @@ public struct FlagEvent: Codable, Sendable { enum CodingKeys: String, CodingKey { case updatedAt = "updated_at" } - + public let updatedAt: Double } diff --git a/FlagsmithClient/Classes/Flagsmith+Concurrency.swift b/FlagsmithClient/Classes/Flagsmith+Concurrency.swift index 5387484..53e71ee 100644 --- a/FlagsmithClient/Classes/Flagsmith+Concurrency.swift +++ b/FlagsmithClient/Classes/Flagsmith+Concurrency.swift @@ -9,7 +9,6 @@ import Foundation @available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) public extension Flagsmith { - var flagStreamContinuation: AsyncStream<[Flag]>.Continuation? { get { return anyFlagStreamContinuation as? AsyncStream<[Flag]>.Continuation @@ -18,13 +17,13 @@ public extension Flagsmith { anyFlagStreamContinuation = newValue } } - + var flagStream: AsyncStream<[Flag]> { AsyncStream { continuation in anyFlagStreamContinuation = continuation } } - + /// Get all feature flags (flags and remote config) optionally for a specific identity /// /// - Parameters: diff --git a/FlagsmithClient/Classes/Flagsmith.swift b/FlagsmithClient/Classes/Flagsmith.swift index 0a1aa70..ef55e97 100644 --- a/FlagsmithClient/Classes/Flagsmith.swift +++ b/FlagsmithClient/Classes/Flagsmith.swift @@ -20,13 +20,13 @@ public final class Flagsmith: @unchecked Sendable { private let apiManager: APIManager private let sseManager: SSEManager private let analytics: FlagsmithAnalytics - + // The last time we got flags via the API private var lastUpdatedAt: Double = 0.0 - + // The last identity used for fetching flags private var lastUsedIdentity: String? - + var anyFlagStreamContinuation: Any? // AsyncStream<[Flag]>.Continuation? for iOS 13+ /// Base URL @@ -36,7 +36,7 @@ public final class Flagsmith: @unchecked Sendable { get { apiManager.baseURL } set { apiManager.baseURL = newValue } } - + /// Base `URL` used for the event source. /// /// The default implementation uses: `https://realtime.flagsmith.com/`. @@ -61,7 +61,7 @@ public final class Flagsmith: @unchecked Sendable { get { analytics.enableAnalytics } set { analytics.enableAnalytics = newValue } } - + /// Are realtime updates enabled? public var enableRealtimeUpdates: Bool { get { sseManager.isStarted } @@ -163,12 +163,12 @@ public final class Flagsmith: @unchecked Sendable { } } } - + private func handleFlagsError(_ error: any Error, completion: @Sendable @escaping (Result<[Flag], any Error>) -> Void) { - if self.defaultFlags.isEmpty { + if defaultFlags.isEmpty { completion(.failure(error)) } else { - completion(.success(self.defaultFlags)) + completion(.success(defaultFlags)) } } @@ -344,7 +344,7 @@ public final class Flagsmith: @unchecked Sendable { private func getFlagUsingDefaults(withID id: String, forIdentity _: String? = nil) -> Flag? { return defaultFlags.first(where: { $0.feature.name == id }) } - + private func handleSSEResult(_ result: Result) { switch result { case let .success(event): @@ -352,31 +352,31 @@ public final class Flagsmith: @unchecked Sendable { if lastUpdatedAt < event.updatedAt { // Evict everything fron the cache cacheConfig.cache.removeAllCachedResponses() - + // Now we can get the new values, which we can emit to the flagUpdateFlow if used getFeatureFlags(forIdentity: lastUsedIdentity) { result in switch result { case let .failure(error): print("Flagsmith - Error getting flags in SSE stream: \(error.localizedDescription)") - + case .success: // On success the flastream is updated automatically in the API call print("Flagsmith - Flags updated from SSE stream.") } } } - + case let .failure(error): print("handleSSEResult Error in SSE connection: \(error.localizedDescription)") } } - + func updateFlagStreamAndLastUpdatedAt(_ flags: [Flag]) { // Update the flag stream if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) { flagStreamContinuation?.yield(flags) } - + // Update the last updated time if the API is giving us newer data if let apiManagerUpdatedAt = apiManager.lastUpdatedAt, apiManagerUpdatedAt > lastUpdatedAt { lastUpdatedAt = apiManagerUpdatedAt diff --git a/FlagsmithClient/Classes/Internal/APIManager.swift b/FlagsmithClient/Classes/Internal/APIManager.swift index 40fd35b..e640f14 100644 --- a/FlagsmithClient/Classes/Internal/APIManager.swift +++ b/FlagsmithClient/Classes/Internal/APIManager.swift @@ -49,7 +49,7 @@ final class APIManager: NSObject, URLSessionDataDelegate, @unchecked Sendable { } } } - + private var _lastUpdatedAt: Double? var lastUpdatedAt: Double? { get { @@ -81,7 +81,6 @@ final class APIManager: NSObject, URLSessionDataDelegate, @unchecked Sendable { if let error = error { DispatchQueue.main.async { completion(.failure(FlagsmithError.unhandled(error))) } } else { - let data = tasksToData[dataTask.taskIdentifier] ?? Data() DispatchQueue.main.async { completion(.success(data)) } } @@ -197,7 +196,7 @@ final class APIManager: NSObject, URLSessionDataDelegate, @unchecked Sendable { } } } - + private func updateLastUpdatedFromRequest(_ request: URLRequest) { // Extract the lastUpdatedAt from the updatedAt header if let lastUpdatedAt = request.allHTTPHeaderFields?["x-flagsmith-document-updated-at"] { diff --git a/FlagsmithClient/Classes/Internal/ReconnectionDelay.swift b/FlagsmithClient/Classes/Internal/ReconnectionDelay.swift index a088fd4..ab25e24 100644 --- a/FlagsmithClient/Classes/Internal/ReconnectionDelay.swift +++ b/FlagsmithClient/Classes/Internal/ReconnectionDelay.swift @@ -14,7 +14,7 @@ class ReconnectionDelay { private let multiplier: Double init(initialDelay: TimeInterval = 1.0, maxDelay: TimeInterval = 60.0, multiplier: Double = 2.0) { - self.attempt = 0 + attempt = 0 self.initialDelay = initialDelay self.maxDelay = maxDelay self.multiplier = multiplier diff --git a/FlagsmithClient/Classes/Internal/SSEManager.swift b/FlagsmithClient/Classes/Internal/SSEManager.swift index e7a9ada..2290f57 100644 --- a/FlagsmithClient/Classes/Internal/SSEManager.swift +++ b/FlagsmithClient/Classes/Internal/SSEManager.swift @@ -25,7 +25,7 @@ final class SSEManager: NSObject, URLSessionDataDelegate, @unchecked Sendable { } } } - + private var _dataTask: URLSessionDataTask? private var dataTask: URLSessionDataTask? { get { @@ -37,7 +37,7 @@ final class SSEManager: NSObject, URLSessionDataDelegate, @unchecked Sendable { } } } - + /// Base `URL` used for requests. private var _baseURL = URL(string: "https://realtime.flagsmith.com/")! var baseURL: URL { @@ -50,7 +50,7 @@ final class SSEManager: NSObject, URLSessionDataDelegate, @unchecked Sendable { } } } - + /// API Key unique to an organization. private var _apiKey: String? var apiKey: String? { @@ -63,22 +63,22 @@ final class SSEManager: NSObject, URLSessionDataDelegate, @unchecked Sendable { } } } - + var isStarted: Bool { return completionHandler != nil } - + private var completionHandler: CompletionHandler? private let serialAccessQueue = DispatchQueue(label: "sseFlagsmithSerialAccessQueue", qos: .default) let propertiesSerialAccessQueue = DispatchQueue(label: "ssePropertiesSerialAccessQueue", qos: .default) private let reconnectionDelay = ReconnectionDelay() - + override init() { super.init() let configuration = URLSessionConfiguration.default session = URLSession(configuration: configuration, delegate: self, delegateQueue: OperationQueue.main) } - + // Helper function to process SSE data internal func processSSEData(_ data: String) { // Split the data into lines and decode the 'data:' lines from JSON into FlagEvent objects @@ -99,10 +99,10 @@ final class SSEManager: NSObject, URLSessionDataDelegate, @unchecked Sendable { } } } - + // MARK: URLSessionDelegate - - func urlSession(_: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) { + + func urlSession(_: URLSession, dataTask _: URLSessionDataTask, didReceive data: Data) { serialAccessQueue.sync { if let message = String(data: data, encoding: .utf8) { processSSEData(message) @@ -110,13 +110,13 @@ final class SSEManager: NSObject, URLSessionDataDelegate, @unchecked Sendable { } } } - + func urlSession(_: URLSession, task: URLSessionTask, didCompleteWithError error: (any Error)?) { serialAccessQueue.sync { if task != dataTask { return } - + // If the connection times out or we have no error passed to us it's pretty common, so just reconnect if let error = error { if let error = error as? URLError, error.code == .timedOut { @@ -128,7 +128,7 @@ final class SSEManager: NSObject, URLSessionDataDelegate, @unchecked Sendable { start(completion: self.completionHandler!) return } - + // Otherwise reconnect with increasing delay using the reconnectionTimer so that we don't load the phone / server serialAccessQueue.asyncAfter(deadline: .now() + reconnectionDelay.nextDelay()) { [weak self] in if let self { @@ -137,30 +137,30 @@ final class SSEManager: NSObject, URLSessionDataDelegate, @unchecked Sendable { } } } - + // MARK: Public Methods - + func start(completion: @escaping CompletionHandler) { guard let apiKey = apiKey, !apiKey.isEmpty else { completion(.failure(FlagsmithError.apiKey)) return } - + guard let completeEventSourceUrl = URL(string: "\(baseURL.absoluteString)sse/environments/\(apiKey)/stream") else { completion(.failure(FlagsmithError.apiURL("Invalid event source URL"))) return } - + var request = URLRequest(url: completeEventSourceUrl) request.setValue("text/event-stream, application/json; charset=utf-8", forHTTPHeaderField: "Accept") request.setValue("no-cache", forHTTPHeaderField: "Cache-Control") request.setValue("keep-alive", forHTTPHeaderField: "Connection") - + completionHandler = completion dataTask = session.dataTask(with: request) dataTask?.resume() } - + func stop() { dataTask?.cancel() completionHandler = nil diff --git a/FlagsmithClient/Classes/Traits.swift b/FlagsmithClient/Classes/Traits.swift index 87ee8ec..46467b6 100644 --- a/FlagsmithClient/Classes/Traits.swift +++ b/FlagsmithClient/Classes/Traits.swift @@ -14,7 +14,7 @@ public struct Traits: Codable, Sendable { public let traits: [Trait] public let identifier: String? public let flags: [Flag] - + init(traits: [Trait], identifier: String?, flags: [Flag] = []) { self.traits = traits self.identifier = identifier diff --git a/FlagsmithClient/Tests/SSEManagerTests.swift b/FlagsmithClient/Tests/SSEManagerTests.swift index 0951f74..d463fa1 100644 --- a/FlagsmithClient/Tests/SSEManagerTests.swift +++ b/FlagsmithClient/Tests/SSEManagerTests.swift @@ -3,30 +3,30 @@ import XCTest class SSEManagerTests: FlagsmithClientTestCase { var sseManager: SSEManager! - + override func setUp() { super.setUp() sseManager = SSEManager() sseManager.apiKey = "seemingly-valid-api-key" } - + override func tearDown() { sseManager = nil super.tearDown() } - + func testBaseURL() { let baseURL = URL(string: "https://my.url.com/")! sseManager.baseURL = baseURL XCTAssertEqual(sseManager.baseURL, baseURL) } - + func testAPIKey() { let apiKey = "testAPIKey" sseManager.apiKey = apiKey XCTAssertEqual(sseManager.apiKey, apiKey) } - + /// Verify that an invalid API key produces the expected error. func testInvalidAPIKey() throws { sseManager.apiKey = nil @@ -43,36 +43,35 @@ class SSEManagerTests: FlagsmithClientTestCase { return } } - + requestFinished.fulfill() } wait(for: [requestFinished], timeout: 1.0) } - + func testValidSSEData() { let requestFinished = expectation(description: "Request Finished") - + sseManager.start { result in if case let .failure(err) = result { XCTFail("Failed during testValidSSEData \(err)") - } - + if case let .success(data) = result { XCTAssertNotNil(data) requestFinished.fulfill() } } - + sseManager.processSSEData("data: {\"updated_at\": 1689172003.899101}") - + wait(for: [requestFinished], timeout: 1.0) } - + func testInalidSSEDataNotANum() { let requestFinished = expectation(description: "Request Finished") - + sseManager.start { result in if case let .failure(err) = result { let error = err as? FlagsmithError @@ -81,39 +80,39 @@ class SSEManagerTests: FlagsmithClientTestCase { XCTFail("Wrong Error") return } - + requestFinished.fulfill() } - + if case .success = result { XCTFail("Should not have succeeded") } } - + sseManager.processSSEData("data: {\"updated_at\": I-am-not-a-number-I-am-a-free-man}") - + wait(for: [requestFinished], timeout: 1.0) } - + func testIgnoresNonDataMessages() { let requestFinished = expectation(description: "Request Finished") - + sseManager.start { result in if case let .failure(err) = result { XCTFail("Failed during testValidSSEData \(err)") } - + if case let .success(data) = result { XCTAssertNotNil(data) requestFinished.fulfill() } } - + sseManager.processSSEData("If you've got to be told by someone then it's got to be me") sseManager.processSSEData("And that's not made from cheese and it doesn't get you free") sseManager.processSSEData("ping: 8374934498.3453445") sseManager.processSSEData("data: {\"updated_at\": 1689172003.899101}") - + wait(for: [requestFinished], timeout: 1.0) } } diff --git a/Package.resolved b/Package.resolved index 92cd757..4713076 100644 --- a/Package.resolved +++ b/Package.resolved @@ -1,97 +1,95 @@ { - "object": { - "pins": [ - { - "package": "CollectionConcurrencyKit", - "repositoryURL": "https://github.com/JohnSundell/CollectionConcurrencyKit.git", - "state": { - "branch": null, - "revision": "b4f23e24b5a1bff301efc5e70871083ca029ff95", - "version": "0.2.0" - } - }, - { - "package": "CryptoSwift", - "repositoryURL": "https://github.com/krzyzanowskim/CryptoSwift.git", - "state": { - "branch": null, - "revision": "c9c3df6ab812de32bae61fc0cd1bf6d45170ebf0", - "version": "1.8.2" - } - }, - { - "package": "SourceKitten", - "repositoryURL": "https://github.com/jpsim/SourceKitten.git", - "state": { - "branch": null, - "revision": "b6dc09ee51dfb0c66e042d2328c017483a1a5d56", - "version": "0.34.1" - } - }, - { - "package": "swift-argument-parser", - "repositoryURL": "https://github.com/apple/swift-argument-parser.git", - "state": { - "branch": null, - "revision": "8f4d2753f0e4778c76d5f05ad16c74f707390531", - "version": "1.2.3" - } - }, - { - "package": "swift-syntax", - "repositoryURL": "https://github.com/apple/swift-syntax.git", - "state": { - "branch": null, - "revision": "6ad4ea24b01559dde0773e3d091f1b9e36175036", - "version": "509.0.2" - } - }, - { - "package": "SwiftFormat", - "repositoryURL": "https://github.com/nicklockwood/SwiftFormat", - "state": { - "branch": null, - "revision": "ab238886b8b50f8b678b251f3c28c0c887305407", - "version": "0.53.8" - } - }, - { - "package": "SwiftLint", - "repositoryURL": "https://github.com/realm/SwiftLint.git", - "state": { - "branch": null, - "revision": "f17a4f9dfb6a6afb0408426354e4180daaf49cee", - "version": "0.54.0" - } - }, - { - "package": "SwiftyTextTable", - "repositoryURL": "https://github.com/scottrhoyt/SwiftyTextTable.git", - "state": { - "branch": null, - "revision": "c6df6cf533d120716bff38f8ff9885e1ce2a4ac3", - "version": "0.9.0" - } - }, - { - "package": "SWXMLHash", - "repositoryURL": "https://github.com/drmohundro/SWXMLHash.git", - "state": { - "branch": null, - "revision": "a853604c9e9a83ad9954c7e3d2a565273982471f", - "version": "7.0.2" - } - }, - { - "package": "Yams", - "repositoryURL": "https://github.com/jpsim/Yams.git", - "state": { - "branch": null, - "revision": "9234124cff5e22e178988c18d8b95a8ae8007f76", - "version": "5.1.2" - } + "pins" : [ + { + "identity" : "collectionconcurrencykit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/JohnSundell/CollectionConcurrencyKit.git", + "state" : { + "revision" : "b4f23e24b5a1bff301efc5e70871083ca029ff95", + "version" : "0.2.0" } - ] - }, - "version": 1 + }, + { + "identity" : "cryptoswift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/krzyzanowskim/CryptoSwift.git", + "state" : { + "revision" : "c9c3df6ab812de32bae61fc0cd1bf6d45170ebf0", + "version" : "1.8.2" + } + }, + { + "identity" : "sourcekitten", + "kind" : "remoteSourceControl", + "location" : "https://github.com/jpsim/SourceKitten.git", + "state" : { + "revision" : "b6dc09ee51dfb0c66e042d2328c017483a1a5d56", + "version" : "0.34.1" + } + }, + { + "identity" : "swift-argument-parser", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-argument-parser.git", + "state" : { + "revision" : "8f4d2753f0e4778c76d5f05ad16c74f707390531", + "version" : "1.2.3" + } + }, + { + "identity" : "swift-syntax", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-syntax.git", + "state" : { + "revision" : "6ad4ea24b01559dde0773e3d091f1b9e36175036", + "version" : "509.0.2" + } + }, + { + "identity" : "swiftformat", + "kind" : "remoteSourceControl", + "location" : "https://github.com/nicklockwood/SwiftFormat", + "state" : { + "revision" : "ab6844edb79a7b88dc6320e6cee0a0db7674dac3", + "version" : "0.54.5" + } + }, + { + "identity" : "swiftlint", + "kind" : "remoteSourceControl", + "location" : "https://github.com/realm/SwiftLint.git", + "state" : { + "revision" : "f17a4f9dfb6a6afb0408426354e4180daaf49cee", + "version" : "0.54.0" + } + }, + { + "identity" : "swiftytexttable", + "kind" : "remoteSourceControl", + "location" : "https://github.com/scottrhoyt/SwiftyTextTable.git", + "state" : { + "revision" : "c6df6cf533d120716bff38f8ff9885e1ce2a4ac3", + "version" : "0.9.0" + } + }, + { + "identity" : "swxmlhash", + "kind" : "remoteSourceControl", + "location" : "https://github.com/drmohundro/SWXMLHash.git", + "state" : { + "revision" : "a853604c9e9a83ad9954c7e3d2a565273982471f", + "version" : "7.0.2" + } + }, + { + "identity" : "yams", + "kind" : "remoteSourceControl", + "location" : "https://github.com/jpsim/Yams.git", + "state" : { + "revision" : "9234124cff5e22e178988c18d8b95a8ae8007f76", + "version" : "5.1.2" + } + } + ], + "version" : 2 }