From 54ddd0ab2afba5b7c5e468625f2d602148b42637 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 9 Dec 2025 23:18:35 +0000 Subject: [PATCH 1/2] Fix library issues: remove deprecated APIs and enable skipped test - Remove deprecated UserDefaults.synchronize() calls (deprecated since iOS 4.0) - Remove misleading swiftlint force_cast comments (casts were already safe) - Fix and uncomment dispatchError() test that was using outdated API - Enable dispatchError test in xctestplan by removing skip entry --- EventSource.xctestplan | 3 - Sources/EventSource/EventParser.swift | 1 - Sources/EventSource/EventSource.swift | 2 - Sources/EventSource/EventSourceDelegate.swift | 1 - Tests/EventSourceTests/EventSourceTests.swift | 72 ++++++++----------- 5 files changed, 28 insertions(+), 51 deletions(-) diff --git a/EventSource.xctestplan b/EventSource.xctestplan index 87f24e4..83e3547 100644 --- a/EventSource.xctestplan +++ b/EventSource.xctestplan @@ -13,9 +13,6 @@ }, "testTargets" : [ { - "skippedTests" : [ - "EventSourceTests\/dispatchError()" - ], "target" : { "containerPath" : "container:", "identifier" : "EventSourceTests", diff --git a/Sources/EventSource/EventParser.swift b/Sources/EventSource/EventParser.swift index ce8fcd5..6067d75 100644 --- a/Sources/EventSource/EventParser.swift +++ b/Sources/EventSource/EventParser.swift @@ -83,7 +83,6 @@ actor EventParser { private func dispatchEvent() async { lastEventId = lastEventIdBuffer ?? lastEventId UserDefaults.eventSource.set(lastEventId, forKey: "com.briannadoubt.event-source.last-event-id") - UserDefaults.eventSource.synchronize() lastEventIdBuffer = nil guard !data.isEmpty else { eventType = "" diff --git a/Sources/EventSource/EventSource.swift b/Sources/EventSource/EventSource.swift index f03704d..927d126 100644 --- a/Sources/EventSource/EventSource.swift +++ b/Sources/EventSource/EventSource.swift @@ -222,7 +222,6 @@ public actor EventSource: NSObject { */ public var urlSessionConfiguration: URLSessionConfiguration { get { - // swiftlint:disable:next force_cast let sessionConfig = _urlSessionConfiguration.copy() as? URLSessionConfiguration sessionConfig?.httpAdditionalHeaders = ["Accept": "text/event-stream", "Cache-Control": "no-cache"] sessionConfig?.timeoutIntervalForRequest = idleTimeout @@ -237,7 +236,6 @@ public actor EventSource: NSObject { return sessionConfig ?? .default } set { - // swiftlint:disable:next force_cast _urlSessionConfiguration = newValue.copy() as? URLSessionConfiguration ?? .default } } diff --git a/Sources/EventSource/EventSourceDelegate.swift b/Sources/EventSource/EventSourceDelegate.swift index 499a614..d5d8467 100644 --- a/Sources/EventSource/EventSourceDelegate.swift +++ b/Sources/EventSource/EventSourceDelegate.swift @@ -83,7 +83,6 @@ final class EventSourceDelegate: NSObject, URLSessionDataDelegate { func handleInitialReply(response: URLResponse) async -> URLSession.ResponseDisposition { Self.logger.debug("Initial reply received") - // swiftlint:disable:next force_cast let httpResponse = response as? HTTPURLResponse let statusCode = httpResponse?.statusCode ?? 500 if (200..<300).contains(statusCode) && statusCode != 204 { diff --git a/Tests/EventSourceTests/EventSourceTests.swift b/Tests/EventSourceTests/EventSourceTests.swift index 1eb295c..3964e74 100644 --- a/Tests/EventSourceTests/EventSourceTests.swift +++ b/Tests/EventSourceTests/EventSourceTests.swift @@ -71,7 +71,6 @@ struct EventSourceTests { #expect(config.headerTransform(["abc": "123"]) == ["abc": "123"]) await #expect(config.connectionErrorHandler(TestError()) == .proceed) UserDefaults.eventSource.removeObject(forKey: "com.briannadoubt.event-source.last-event-id") - UserDefaults.eventSource.synchronize() } @Test @@ -107,7 +106,6 @@ struct EventSourceTests { #expect(config.idleTimeout == 180.0) await #expect(config.connectionErrorHandler(TestError()) == .shutdown) UserDefaults.eventSource.removeObject(forKey: "com.briannadoubt.event-source.last-event-id") - UserDefaults.eventSource.synchronize() } @Test @@ -131,7 +129,6 @@ struct EventSourceTests { #expect(config.urlSessionConfiguration.allowsCellularAccess == false) #expect(sessionConfig !== config.urlSessionConfiguration) UserDefaults.eventSource.removeObject(forKey: "com.briannadoubt.event-source.last-event-id") - UserDefaults.eventSource.synchronize() } @Test @@ -144,7 +141,6 @@ struct EventSourceTests { es = EventSource(config: config, sessionType: MockDataTaskSession.self) await #expect(es.getLastEventId() == "def") UserDefaults.eventSource.removeObject(forKey: "com.briannadoubt.event-source.last-event-id") - UserDefaults.eventSource.synchronize() } @Test @@ -156,7 +152,6 @@ struct EventSourceTests { #expect(configuration.httpAdditionalHeaders?["Accept"] as? String == "text/event-stream") #expect(configuration.httpAdditionalHeaders?["Cache-Control"] as? String == "no-cache") UserDefaults.eventSource.removeObject(forKey: "com.briannadoubt.event-source.last-event-id") - UserDefaults.eventSource.synchronize() } @Test @@ -193,35 +188,36 @@ struct EventSourceTests { #expect(request.timeoutInterval == config.idleTimeout) #expect(request.allHTTPHeaderFields == overrideHeaders.merging(staticHeaders) { $1 }) UserDefaults.eventSource.removeObject(forKey: "com.briannadoubt.event-source.last-event-id") - UserDefaults.eventSource.synchronize() } -// @Test -// func dispatchError() async { -// let mockHandler = MockHandler() -// -// var config = EventSource.Config(handler: mockHandler, url: URL(string: "abc")!) -// -//// var connectionErrorHandlerCallCount = 0 -// config.connectionErrorHandler = { error in -//// connectionErrorHandlerCallCount += 1 -// return .proceed -// } -// -// let es = EventSource(config: config, urlSession: MockDataTaskSession.self) -// await #expect(es.dispatchError(error: TestError()) == .proceed) -//// #expect(connectionErrorHandlerCallCount == 1) -// guard -// case .error(let err) = await mockHandler.expectEvent(), -// err is TestError -// else { -// Issue.record("handler should receive error if EventSource is not shutting down") -// return -// } -// await mockHandler.events.expectNoEvent() -// await #expect(es.dispatchError(error: TestError()) == .shutdown) -//// #expect(connectionErrorHandlerCallCount == 2) -// } + @Test + func dispatchError() async { + let mockHandler = MockHandler() + + var config = EventSource.Config(handler: mockHandler, url: URL(string: "abc")!) + + var connectionErrorHandlerCallCount = 0 + config.connectionErrorHandler = { error in + connectionErrorHandlerCallCount += 1 + // Return shutdown on second call to test both paths + return connectionErrorHandlerCallCount == 1 ? .proceed : .shutdown + } + + let es = EventSource(config: config, sessionType: MockDataTaskSession.self) + await #expect(es.dispatchError(error: TestError()) == .proceed) + #expect(connectionErrorHandlerCallCount == 1) + guard + case .error(let err) = await mockHandler.events.first, + err is TestError + else { + Issue.record("handler should receive error if EventSource is not shutting down") + return + } + await #expect(es.dispatchError(error: TestError()) == .shutdown) + #expect(connectionErrorHandlerCallCount == 2) + // Error should not be dispatched to handler when shutdown is returned + await #expect(mockHandler.events.count == 1) + } #if !os(Linux) && !os(Windows) @Test @@ -242,7 +238,6 @@ struct EventSourceTests { #expect(session?.lastRequest?.allHTTPHeaderFields?["Last-Event-Id"] == nil) await es.stop() UserDefaults.eventSource.removeObject(forKey: "com.briannadoubt.event-source.last-event-id") - UserDefaults.eventSource.synchronize() } @Test @@ -268,7 +263,6 @@ struct EventSourceTests { #expect(session?.lastRequest?.allHTTPHeaderFields?["X-LD-Header"] == "def") await es.stop() UserDefaults.eventSource.removeObject(forKey: "com.briannadoubt.event-source.last-event-id") - UserDefaults.eventSource.synchronize() } @Test @@ -287,7 +281,6 @@ struct EventSourceTests { #expect(session?.requests.count == 1) await es.stop() UserDefaults.eventSource.removeObject(forKey: "com.briannadoubt.event-source.last-event-id") - UserDefaults.eventSource.synchronize() } @Test @@ -303,7 +296,6 @@ struct EventSourceTests { await #expect(mockHandler.events.first == .opened) await eventSource.stop() UserDefaults.eventSource.removeObject(forKey: "com.briannadoubt.event-source.last-event-id") - UserDefaults.eventSource.synchronize() } @Test @@ -343,7 +335,6 @@ struct EventSourceTests { await #expect((eventSource.urlSession as? MockDataTaskSession)?.requests.last?.allHTTPHeaderFields?["Last-Event-Id"] == "abc") await eventSource.stop() UserDefaults.eventSource.removeObject(forKey: "com.briannadoubt.event-source.last-event-id") - UserDefaults.eventSource.synchronize() } @Test @@ -374,7 +365,6 @@ struct EventSourceTests { await #expect(mockHandler.events[safe: 2] == .opened) await eventSource.stop() UserDefaults.eventSource.removeObject(forKey: "com.briannadoubt.event-source.last-event-id") - UserDefaults.eventSource.synchronize() } @Test @@ -397,7 +387,6 @@ struct EventSourceTests { await eventSource.stop() await #expect(mockHandler.events[safe: 2] == .closed) UserDefaults.eventSource.removeObject(forKey: "com.briannadoubt.event-source.last-event-id") - UserDefaults.eventSource.synchronize() } @Test @@ -419,7 +408,6 @@ struct EventSourceTests { await eventSource.stop() } UserDefaults.eventSource.removeObject(forKey: "com.briannadoubt.event-source.last-event-id") - UserDefaults.eventSource.synchronize() } @Test @@ -440,7 +428,6 @@ struct EventSourceTests { // Error should not have been given to the handler await #expect(mockHandler.events.isEmpty) UserDefaults.eventSource.removeObject(forKey: "com.briannadoubt.event-source.last-event-id") - UserDefaults.eventSource.synchronize() } @Test @@ -463,7 +450,6 @@ struct EventSourceTests { // Error should not have been given to the handler await #expect(mockHandler.events.count == 2) UserDefaults.eventSource.removeObject(forKey: "com.briannadoubt.event-source.last-event-id") - UserDefaults.eventSource.synchronize() } @Test @@ -479,7 +465,6 @@ struct EventSourceTests { // Error should not have been given to the handler await #expect(mockHandler.events.isEmpty) UserDefaults.eventSource.removeObject(forKey: "com.briannadoubt.event-source.last-event-id") - UserDefaults.eventSource.synchronize() } @Test @@ -500,7 +485,6 @@ struct EventSourceTests { // Error should not have been given to the handler await #expect(mockHandler.events.isEmpty) UserDefaults.eventSource.removeObject(forKey: "com.briannadoubt.event-source.last-event-id") - UserDefaults.eventSource.synchronize() } #endif } From 89c0a16a8b1d86d862959a2eb796561a76fa5b4e Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 10 Dec 2025 03:21:31 +0000 Subject: [PATCH 2/2] Fix Swift 6 concurrency error in dispatchError test Use LockIsolated from swift-concurrency-extras to safely capture and mutate the call counter in a @Sendable closure. --- Tests/EventSourceTests/EventSourceTests.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Tests/EventSourceTests/EventSourceTests.swift b/Tests/EventSourceTests/EventSourceTests.swift index 3964e74..7273fb8 100644 --- a/Tests/EventSourceTests/EventSourceTests.swift +++ b/Tests/EventSourceTests/EventSourceTests.swift @@ -196,16 +196,16 @@ struct EventSourceTests { var config = EventSource.Config(handler: mockHandler, url: URL(string: "abc")!) - var connectionErrorHandlerCallCount = 0 + let connectionErrorHandlerCallCount = LockIsolated(0) config.connectionErrorHandler = { error in - connectionErrorHandlerCallCount += 1 + connectionErrorHandlerCallCount.withValue { $0 += 1 } // Return shutdown on second call to test both paths - return connectionErrorHandlerCallCount == 1 ? .proceed : .shutdown + return connectionErrorHandlerCallCount.value == 1 ? .proceed : .shutdown } let es = EventSource(config: config, sessionType: MockDataTaskSession.self) await #expect(es.dispatchError(error: TestError()) == .proceed) - #expect(connectionErrorHandlerCallCount == 1) + #expect(connectionErrorHandlerCallCount.value == 1) guard case .error(let err) = await mockHandler.events.first, err is TestError @@ -214,7 +214,7 @@ struct EventSourceTests { return } await #expect(es.dispatchError(error: TestError()) == .shutdown) - #expect(connectionErrorHandlerCallCount == 2) + #expect(connectionErrorHandlerCallCount.value == 2) // Error should not be dispatched to handler when shutdown is returned await #expect(mockHandler.events.count == 1) }