# Mock URLProtocol with strict swift 6 concurrency

**URL:** <https://forums.swift.org/t/mock-urlprotocol-with-strict-swift-6-concurrency/77135>\
**Category:** Using Swift\
**Tags:** concurrency\
**Created:** [January 11, 2025, 10:03am UTC](https://forums.swift.org/t/mock-urlprotocol-with-strict-swift-6-concurrency/77135 "2025-01-11T10:03:13Z")\
**Posts on this page:** 18\
**Page:** 1

<div class="post-metadata">

**Author:** ![Cavelle\_Benjamin](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/cavelle_benjamin/32/2888_2.png) [@Cavelle\_Benjamin](https://forums.swift.org/u/Cavelle_Benjamin)\
**Post date:** [January 11, 2025, 10:03am UTC](https://forums.swift.org/t/mock-urlprotocol-with-strict-swift-6-concurrency/77135/1 "2025-01-11T10:03:13Z")

</div>

Hi all. I am trying to mock some network calls by using URLProtocol, I'm also trying to do this in a strict swift 6 compliant way... I've tried wrapping a request handler in an actor and even tried just using a hash table of URLs and responses... I either get 'passing closure as a 'sending' parameter risks causing data races between code in the current task and concurrent execution of the closure' if I wrap the request handler in an actor then use it in a subclass of URLProtocol or I get 'static property 'mockResponses' is not concurrency-safe because it is nonisolated global shared mutable state' when using a hash table inside the subclass. Has anyone successsfully set this up with strict swift 6? I tried annotating with nonisolated(unsafe) as well and that didn’t work.

```swift

import Foundation

actor RequestHandlerStorage {
    private var requestHandler: (@unchecked @Sendable (URLRequest) async throws -> (HTTPURLResponse, Data))?

    func setHandler(_ handler: @unchecked @Sendable @escaping (URLRequest) async throws -> (HTTPURLResponse, Data)) async {
        requestHandler = handler
    }

    func executeHandler(for request: URLRequest) async throws -> (HTTPURLResponse, Data) {
        guard let handler = requestHandler else {
            throw MockURLProtocolError.noRequestHandler
        }
        return try await handler(request)
    }
}

final class MockURLProtocol: URLProtocol {

    private static let requestHandlerStorage = RequestHandlerStorage()

    static func setHandler(_ handler: @unchecked @Sendable @escaping (URLRequest) async throws -> (HTTPURLResponse, Data)) async {
        await requestHandlerStorage.setHandler { request in
            try await handler(request)
        }
    }

    func executeHandler(for request: URLRequest) async throws -> (HTTPURLResponse, Data) {
        return try await Self.requestHandlerStorage.executeHandler(for: request)
    }

    override class func canInit(with request: URLRequest) -> Bool {
        return true
    }

    override class func canonicalRequest(for request: URLRequest) -> URLRequest {
        return request
    }

    override func startLoading() {

        Task {
            do {
                let (response, data) = try await self.executeHandler(for: request)
                client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed)
                client?.urlProtocol(self, didLoad: data)
                client?.urlProtocolDidFinishLoading(self)
            } catch {
                client?.urlProtocol(self, didFailWithError: error)
            }
        }

    }

    override func stopLoading() {}
}

enum MockURLProtocolError: Error {
    case noRequestHandler
    case invalidURL
}

```

---

<div class="post-metadata">

**Author:** ![mattie](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/mattie/32/13267_2.png) [@mattie](https://forums.swift.org/u/mattie)\
**Post date:** [January 11, 2025, 11:51am UTC](https://forums.swift.org/t/mock-urlprotocol-with-strict-swift-6-concurrency/77135/2 "2025-01-11T11:51:59Z")

</div>

You've run into one of the rarer but more challenging problems - inheritance with an ObjC type that doesn't support concurrency well.

(I'm sure you've noticed that `@unchecked` is not used in this way, so skipping that.)

The immediate problem is that `startLoading` a) has no isolation but b) participates in concurrency via `Task`. When a type has no isolation, getting it to work with concurrency is much harder. But, in this case, all of the safe tools I can think of to fix this involve changing the definition of `startLoading`, which we cannot do, because that's not under our control.

Right now, you can make this work by marking `MockURLProtocol` as `@unchecked Sendable`. However, it does not look like `URLProtocol` is actually thread-safe? So, you'll have to be particularly careful with such an opt-out, as the inheritance opens the doors for even more traps that a regular unchecked type might.

(you may also want to keep your eye on the "isolated overloads" concept from [[Prospective Vision] Improving the approachability of data-race safety](https://forums.swift.org/t/prospective-vision-improving-the-approachability-of-data-race-safety/76183), because I think it could help in this case)

---

<div class="post-metadata">

**Author:** ![Cavelle\_Benjamin](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/cavelle_benjamin/32/2888_2.png) [@Cavelle\_Benjamin](https://forums.swift.org/u/Cavelle_Benjamin)\
**Post date:** [January 11, 2025, 1:04pm UTC](https://forums.swift.org/t/mock-urlprotocol-with-strict-swift-6-concurrency/77135/3 "2025-01-11T13:04:22Z")

</div>

Thanks!! The goal is to use for testing scenarios. I am thinking that if I don't try to run the tests in parallel, it should be fine. Hopefully with swift 6, more objc code can be laid to rest. I'll try the @unchecked Sendable and see how it goes.

---

<div class="post-metadata">

**Author:** ![Cavelle\_Benjamin](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/cavelle_benjamin/32/2888_2.png) [@Cavelle\_Benjamin](https://forums.swift.org/u/Cavelle_Benjamin)\
**Post date:** [January 11, 2025, 1:12pm UTC](https://forums.swift.org/t/mock-urlprotocol-with-strict-swift-6-concurrency/77135/4 "2025-01-11T13:12:56Z")

</div>

The following compiles and the tests pass

```swift
import Foundation

actor RequestHandlerStorage {
    private var requestHandler: ( @Sendable (URLRequest) async throws -> (HTTPURLResponse, Data))?

    func setHandler(_ handler: @Sendable @escaping (URLRequest) async throws -> (HTTPURLResponse, Data)) async {
        requestHandler = handler
    }

    func executeHandler(for request: URLRequest) async throws -> (HTTPURLResponse, Data) {
        guard let handler = requestHandler else {
            throw MockURLProtocolError.noRequestHandler
        }
        return try await handler(request)
    }
}

final class MockURLProtocol: URLProtocol, @unchecked Sendable {

    private static let requestHandlerStorage = RequestHandlerStorage()

    static func setHandler(_ handler: @Sendable @escaping (URLRequest) async throws -> (HTTPURLResponse, Data)) async {
        await requestHandlerStorage.setHandler { request in
            try await handler(request)
        }
    }

    func executeHandler(for request: URLRequest) async throws -> (HTTPURLResponse, Data) {
        return try await Self.requestHandlerStorage.executeHandler(for: request)
    }

    override class func canInit(with request: URLRequest) -> Bool {
        return true
    }

    override class func canonicalRequest(for request: URLRequest) -> URLRequest {
        return request
    }

    override func startLoading() {

        Task {
            do {
                let (response, data) = try await self.executeHandler(for: request)
                client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed)
                client?.urlProtocol(self, didLoad: data)
                client?.urlProtocolDidFinishLoading(self)
            } catch {
                client?.urlProtocol(self, didFailWithError: error)
            }
        }

    }

    override func stopLoading() {}
}

enum MockURLProtocolError: Error {
    case noRequestHandler
    case invalidURL
}

```

---

<div class="post-metadata">

**Author:** ![eskimo](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/eskimo/32/13558_2.png) [@eskimo](https://forums.swift.org/u/eskimo)\
**Post date:** [January 13, 2025, 9:38am UTC](https://forums.swift.org/t/mock-urlprotocol-with-strict-swift-6-concurrency/77135/5 "2025-01-13T09:38:30Z")

</div>

> However, it does not look like URLProtocol is actually thread-safe?

That threading rules for `URLProtocol` are weird, poorly documented, and intimately tied to the run loop )-:

* * *

Folks, if you’re doing anything serious with `URLProtocol`, I recommend that you carefully work through the notes in [Read Me About CustomHTTPProtocol](https://developer.apple.com/library/archive/samplecode/CustomHTTPProtocol/Listings/Read_Me_About_CustomHTTPProtocol_txt.html#//apple_ref/doc/uid/DTS40013653-Read_Me_About_CustomHTTPProtocol_txt-DontLinkElementID_23).

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

---

<div class="post-metadata">

**Author:** ![Gteoharov](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/gteoharov/32/39101_2.png) [@Gteoharov](https://forums.swift.org/u/Gteoharov)\
**Post date:** [January 20, 2025, 3:00pm UTC](https://forums.swift.org/t/mock-urlprotocol-with-strict-swift-6-concurrency/77135/6 "2025-01-20T15:00:01Z")

</div>

Thanks to your suggestions regarding MockURLProtocol and Actor, I’ve managed to build a test environment in Xcode 16 using the new Swift 6 concurrency features. I’ve adapted the setup slightly to fit my needs, introducing a HandlerType enum in our actor RequestHandlerStorage to handle success and failure cases separately.

Here’s the tricky part: when I run the tests individually, they all pass. However, running them together results in a failure. Specifically, a test expected an error to be thrown (e.g., when adding a URLResponse instead of an HTTPURLResponse), but instead, the test passes with success unexpectedly. The output is:

_Expectation failed: an error was expected but none was thrown. Instead, the result was_ _(12 bytes, \<NSHTTPURLResponse: 0x6000032fde60\> { URL: [http://any-url.com/](http://any-url.com/) } { Status Code: 200, Headers ..._

This issue doesn’t occur when running the tests separately.

Tests:

```swift
import Testing
import CodingFeed

@Suite("HTTP Client Session")
struct URLSessionHTTPClientTests {
    
    @Test("Get from URL performs a GET request with URL", arguments:
        [TestHelpers.Network.anyURL()],
        [TestHelpers.Network.makeItemJSON([])]
    )
    func test_perforsGETRequestWithURL(url: URL, data: Data) async throws {
        let sut = makeSUT()
        
        await MockURLProtocol.setSuccessHandler { request in
            #expect(request.url == url)
            #expect(request.httpMethod == "GET")
            return (TestHelpers.Network.makeResponse(for: url), data)
        }
        
        _ = try? await sut.get(from: url)
    }
    
    @Test("Get from URL fails when response is not HTTP response", arguments:
        [TestHelpers.Network.anyURL()],
        [TestHelpers.Network.makeItemJSON([])]
    )
    func test_getFromURL_failsOnNonHTTPResponse(url: URL, data: Data) async throws {
        let sut = makeSUT()
        
        await MockURLProtocol.setFailureHandler { _ in
            let nonHTTPResponse = URLResponse(
                url: url,
                mimeType: nil,
                expectedContentLength: 0,
                textEncodingName: nil
            )
            return (nonHTTPResponse, data)
        }
        
        await #expect(throws: (any Error).self) {
            try await sut.get(from: url)
        }
    }
    
    @Test("Get from URL returns data and HTTP response", arguments:
        [TestHelpers.Network.anyURL()],
        [TestHelpers.Network.makeItemJSON([])]
    )
    func test_getFromURL_returnsDataAndHTTPResponse(url: URL, data: Data) async throws {
        let sut = makeSUT()
        
        await MockURLProtocol.setSuccessHandler { _ in
            return (TestHelpers.Network.makeResponse(for: url), data)
        }
        
        let (receivedData, receivedResponse) = try await sut.get(from: url)
        
        #expect(receivedData == data)
        #expect(receivedResponse.url == TestHelpers.Network.makeResponse(for: url).url)
        #expect(receivedResponse.statusCode == TestHelpers.Network.makeResponse(for: url).statusCode)
    }
    
    // MARK: Helpers
    
    private func makeSUT() -> HTTPClient {
        let configuration = URLSessionConfiguration.ephemeral
        configuration.protocolClasses = [MockURLProtocol.self]
        let session = URLSession(configuration: configuration)
        return URLSessionHTTPClient(session: session)
    }
    
    private actor RequestHandlerStorage {
        enum HandlerType {
            case success((@Sendable (URLRequest) async throws -> (HTTPURLResponse, Data)))
            case failure((@Sendable (URLRequest) async throws -> (URLResponse, Data)))
            case none
        }
        
        private var currentHandler: HandlerType = .none
        
        func setSuccessHandler(_ handler: @Sendable @escaping (URLRequest) async throws -> (HTTPURLResponse, Data)) async {
            currentHandler = .success(handler)
        }
        
        func setFailureHandler(_ handler: @Sendable @escaping (URLRequest) async throws -> (URLResponse, Data)) async {
            currentHandler = .failure(handler)
        }
        
        func executeHandler(for request: URLRequest) async throws -> (URLResponse, Data) {
            switch currentHandler {
            case .success(let handler):
                return try await handler(request)
            case .failure(let handler):
                return try await handler(request)
            case .none:
                throw TestHelpers.Network.anyError()
            }
        }
    }

    private final class MockURLProtocol: URLProtocol, @unchecked Sendable {
        private static let storage = RequestHandlerStorage()
        
        static func setSuccessHandler(_ handler: @Sendable @escaping (URLRequest) async throws -> (HTTPURLResponse, Data)) async {
            await storage.setSuccessHandler(handler)
        }
        
        static func setFailureHandler(_ handler: @Sendable @escaping (URLRequest) async throws -> (URLResponse, Data)) async {
            await storage.setFailureHandler(handler)
        }
        
        func executeHandler(for request: URLRequest) async throws -> (URLResponse, Data) {
            return try await Self.storage.executeHandler(for: request)
        }
        
        override class func canInit(with request: URLRequest) -> Bool {
            return true
        }
        
        override class func canonicalRequest(for request: URLRequest) -> URLRequest {
            return request
        }
        
        override func startLoading() {
            Task {
                do {
                    let (response, data) = try await self.executeHandler(for: request)
                    client?.urlProtocol(self, didReceive: response, cacheStoragePolicy: .notAllowed)
                    client?.urlProtocol(self, didLoad: data)
                    client?.urlProtocolDidFinishLoading(self)
                } catch {
                    client?.urlProtocol(self, didFailWithError: error)
                }
            }
        }
        
        override func stopLoading() {}
    }
}

```

and here is what we are actually testing:

```swift
import Foundation

public protocol HTTPClient {
    func get(from url: URL) async throws -> (Data, HTTPURLResponse)
}

public class URLSessionHTTPClient: HTTPClient {
    private let session: URLSession
    
    public init(session: URLSession = .shared) {
        self.session = session
    }
    
    private struct UnexpectedValueRepresentation: Error {}
    
    public func get(from url: URL) async throws -> (Data, HTTPURLResponse) {
        let (data, response) = try await session.data(from: url)
        
        guard let httpResponse = response as? HTTPURLResponse else {
            throw UnexpectedValueRepresentation()
        }
        
        return (data, httpResponse)
    }
}

```

I’m working on implementing a modular approach while keeping SOLID principles in mind. However, I’m still getting familiar with Modern Concurrency in Swift.

Here are the steps I’ve tried so far:  
• Clearing RequestHandlerStorage before and after each test – no success.  
• Using a defer block to ensure cleanup at the end of a function’s scope, but I dropped this approach as defer blocks cannot contain async calls in Swift – no success.  
• Registering and unregistering MockURLProtocol before and after each test – no success.

Despite these efforts, the issue persists. Does anyone have suggestions or insights on how to resolve this?

Thank you in advance for your help!

---

<div class="post-metadata">

**Author:** ![ph1ps](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ph1ps/32/35459_2.png) [@ph1ps](https://forums.swift.org/u/ph1ps)\
**Post date:** [January 20, 2025, 3:15pm UTC](https://forums.swift.org/t/mock-urlprotocol-with-strict-swift-6-concurrency/77135/7 "2025-01-20T15:15:45Z")

</div>

> [@Gteoharov](#):
>
> ```swift
> public protocol HTTPClient {
> func get(from url: URL) async throws -> (Data, HTTPURLResponse)
> }
> 
> ```

May I ask, why you are even using a custom `URLProtocol` here? You already have abstracted your `HTTPClient`. Wouldn't it be easier to create a `MockHTTPClient` and return it in your `makeSUT()`?

---

<div class="post-metadata">

**Author:** ![calicoding](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/calicoding/32/36652_2.png) [@calicoding](https://forums.swift.org/u/calicoding)\
**Post date:** [January 20, 2025, 3:21pm UTC](https://forums.swift.org/t/mock-urlprotocol-with-strict-swift-6-concurrency/77135/8 "2025-01-20T15:21:20Z")

</div>

I haven’t caught up completely on this thread, but I wanted to point out that swift testing runs tests in parallel by default. If you’re using URLProtocol with only a single callback handler then you will suffer from races.

You can resolve this by marking these tests with the serialized trait:

> **[Running tests serially or in parallel | Apple Developer Documentation](https://developer.apple.com/documentation/Testing/Parallelization)**
>
> Control whether tests run serially or in parallel.

But my advice is to rearchitect your use of URLSession such that your tests do not rely on URLProtocol. In the past I’ve handled this by mocking URLSession itself and injecting that, but there are other approaches that would also work.

---

<div class="post-metadata">

**Author:** ![Gteoharov](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/gteoharov/32/39101_2.png) [@Gteoharov](https://forums.swift.org/u/Gteoharov)\
**Post date:** [January 20, 2025, 6:57pm UTC](https://forums.swift.org/t/mock-urlprotocol-with-strict-swift-6-concurrency/77135/9 "2025-01-20T18:57:14Z")

</div>

The reason for using `URLProtocol` instead of a simple `MockHTTPClient` is that we're specifically testing the `URLSessionHTTPClient` implementation, not just the `HTTPClient` protocol contract.

While we do have the `HTTPClient` protocol abstraction (which is great for high-level testing), when testing `URLSessionHTTPClient` we need to verify:

1. The actual URLSession behavior and integration (which only way I know is subclassing old legacy `URLProtocol` and if there is any better approach with "modern" swift concurrency will be really glad to hear it)
2. Correct handling of URLSession-specific responses and errors
3. Proper configuration of URLSession requests

Even if we switch to Alamofire or Moya in the future, they still use URLSession under the hood, so these tests remain valuable for network layer.

---

<div class="post-metadata">

**Author:** ![Gteoharov](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/gteoharov/32/39101_2.png) [@Gteoharov](https://forums.swift.org/u/Gteoharov)\
**Post date:** [January 20, 2025, 7:20pm UTC](https://forums.swift.org/t/mock-urlprotocol-with-strict-swift-6-concurrency/77135/10 "2025-01-20T19:20:20Z")

</div>

Thanks for pointing that out! I’ve actually read the Apple documentation on Swift testing a few times, specifically trying to figure out how to run tests non-parallel since I discovered the race condition. I did read about test suites, but I completely missed the traits section.. after some frustration, I finally Googled the problem and found this post—which was the key to solving it. Huge thanks for this! Let’s just say 100% of my code coverage success now rests on your shoulders. 🕶

 ![Screenshot 2025-01-20 at 21.09.46](https://global.discourse-cdn.com/swift/original/3X/f/f/ff60d407332a0c88b59453a803df2083dc90fd10.png)

Regarding the advice to rearchitect away from `URLProtocol`: while I see the value in mocking `URLSession` itself, I prefer sticking to this legacy class for now `URLProtocol` It’s the only way for me to observe API calls without actually making them, and I like keeping things closely tied to the URLSession framework-specific behavior. Open to new suggestions and advices if any one know any better approach how to test not only abstraction of interface/protocol, but for testing the actual URLSession implementation details!

Thanks again for the insight—it saved me a ton of time and frustration!

---

<div class="post-metadata">

**Author:** ![Cavelle\_Benjamin](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/cavelle_benjamin/32/2888_2.png) [@Cavelle\_Benjamin](https://forums.swift.org/u/Cavelle_Benjamin)\
**Post date:** [January 20, 2025, 7:49pm UTC](https://forums.swift.org/t/mock-urlprotocol-with-strict-swift-6-concurrency/77135/11 "2025-01-20T19:49:16Z")

</div>

You are right. I specifically removed parallel from my testing so that I don't hit any race conditions.

---

<div class="post-metadata">

**Author:** ![eskimo](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/eskimo/32/13558_2.png) [@eskimo](https://forums.swift.org/u/eskimo)\
**Post date:** [January 21, 2025, 8:36am UTC](https://forums.swift.org/t/mock-urlprotocol-with-strict-swift-6-concurrency/77135/12 "2025-01-21T08:36:39Z")

</div>

> when testing URLSessionHTTPClient we need to verify … actual `URLSession` behavior

That’s where things fall apart with the `URLProtocol` approach. Folks do it because they want to test how their code interacts with `URLSession`. However, `URLSession` behaves very differently in the face of a `URLProtocol`. So, you’re not testing how your code interacts with the session’s behaviour, you’re testing how your code interacts with _your understanding of the session’s behaviour_, which isn’t all that helpful IMO.

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

---

<div class="post-metadata">

**Author:** ![Gteoharov](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/gteoharov/32/39101_2.png) [@Gteoharov](https://forums.swift.org/u/Gteoharov)\
**Post date:** [January 21, 2025, 9:40am UTC](https://forums.swift.org/t/mock-urlprotocol-with-strict-swift-6-concurrency/77135/13 "2025-01-21T09:40:31Z")

</div>

Hi Quinn,

Thanks for sharing your perspective - as you and other folks keep mentioning the same thing, it definitely made me reflect on the limitations of using `URLProtocol`. I discovered years ago that it doesn't fully replicate the real-world behavior of `URLSession`, and this gap can lead to potential blind spots in testing.

I'm currently using `URLProtocol` as part of a layered testing strategy. While it doesn't capture all `URLSession` nuances, it allows me to efficiently test core networking logic like request composition, response handling, and error scenarios in isolation. This gives me fast feedback during development over my remote module as separated macOS frameworks where the target is to include only Foundation, as the codebase will be shared across different Apple devices that will consume from that framework.

That said, I see `URLProtocol` as a useful tool for specific scenarios, particularly when I want to isolate and test how my client interacts with mocked requests and responses without depending on real network interactions. While it may not cover all of URLSession's nuances, it's been valuable for ensuring my code constructs and handles requests/responses as expected across the Apple ecosystem.

I agree that pairing these tests with integration tests against a real server or `URLSession` in its natural state would provide a more comprehensive picture of behavior (which is part of my plan for CI/CD). I'll definitely keep your advice in mind for refining my testing strategy to better account for potential gaps in emulating real-world URLSession behavior, especially given this will be a foundation for part of my multi-device application. At this level of Foundation framework testing, I'm not aware of other approaches that would provide similar capabilities, open to hear more constructive feedbacks from community.

Thanks again for pointing this out! I really appreciate the insight!

Best regards,  
Georgi

---

<div class="post-metadata">

**Author:** ![Cavelle\_Benjamin](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/cavelle_benjamin/32/2888_2.png) [@Cavelle\_Benjamin](https://forums.swift.org/u/Cavelle_Benjamin)\
**Post date:** [January 21, 2025, 11:39am UTC](https://forums.swift.org/t/mock-urlprotocol-with-strict-swift-6-concurrency/77135/14 "2025-01-21T11:39:47Z")

</div>

Thanks for the perspective. What alternatives do you suggest?

---

<div class="post-metadata">

**Author:** ![eskimo](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/eskimo/32/13558_2.png) [@eskimo](https://forums.swift.org/u/eskimo)\
**Post date:** [January 22, 2025, 9:05am UTC](https://forums.swift.org/t/mock-urlprotocol-with-strict-swift-6-concurrency/77135/15 "2025-01-22T09:05:34Z")

</div>

I generally recommend that you introduce an abstraction layer between your request-response code and your network transport. You can then:

- Test the request-response code with an in-memory transport

- Test the network transport against a server

That raises the question of what that abstraction layer looks like. Rather than try to mock `URLSession` itself, with its large and complex API, I recommend that you create a much smaller interface that’s focused on your product’s specific needs. For many apps that interface can be as simple as a throwing async function that takes a `URLRequest` and returns an `HTTPURLResponse`.

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

---

<div class="post-metadata">

**Author:** ![GuyB](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/guyb/32/12116_2.png) [@GuyB](https://forums.swift.org/u/GuyB)\
**Post date:** [July 6, 2025, 6:36am UTC](https://forums.swift.org/t/mock-urlprotocol-with-strict-swift-6-concurrency/77135/16 "2025-07-06T06:36:52Z")

</div>

Hi Quinn,  
could you clarify if a URLProtocol implementing HTPP(S) is responsible for management of the httpCookieStorage in the session configuration or not ?

I have a very simple mock URLProtocol set up for testing purposes, and even though I return a response with a Set-Cookie header, nothing gets added to the session store, and any other relevant cookies in the store are not added to any requests sent to the URLProtocol. I didn't see any mention of cookie management in the documents or the ReadMe and sample code you linked to here, and other related posts imply that URLSession takes care of cookies itself, so perhaps I have got something else wrong in my implementation.  
Cheers  
Guy

---

<div class="post-metadata">

**Author:** ![tera](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/tera/32/38201_2.png) [@tera](https://forums.swift.org/u/tera)\
**Post date:** [July 6, 2025, 11:43pm UTC](https://forums.swift.org/t/mock-urlprotocol-with-strict-swift-6-concurrency/77135/17 "2025-07-06T23:43:39Z")

</div>

> [@eskimo](#):
>
> For many apps that interface can be as simple as a throwing async function that takes a `URLRequest` and returns an `HTTPURLResponse`.

Or just store the cached response in URLCache, afterwards requests will return that cached data.

---

<div class="post-metadata">

**Author:** ![eskimo](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/eskimo/32/13558_2.png) [@eskimo](https://forums.swift.org/u/eskimo)\
**Post date:** [July 7, 2025, 7:11am UTC](https://forums.swift.org/t/mock-urlprotocol-with-strict-swift-6-concurrency/77135/18 "2025-07-07T07:11:13Z")

</div>

> could you clarify if a `URLProtocol` implementing HTPP(S) is  
> responsible for management of the `httpCookieStorage` in the  
> session configuration or not?

I can’t recall ever researching that in depth.

Conceptually, cookies are an HTTP thing, and thus I’d expect them to be handled by the HTTP protocol. Your testing seems to suggest that that’s accurate.

As to how a protocol implementation might handle that, keep in mind that `URLProtocol` was designed for `NSURLConnection` and has never been properly updated for `URLSession`. `NSURLConnection` always uses the default cookie store; it’s not configurable like it is in `URLSession`.

Finally, I want to be crystal clear that I recommend against using `URLProtocol` except… actually… there are no exceptions these days. Way back in the day, `URLProtocol` made a lot of sense. The cracks started to show after the CFNetwork rewrite and it basically fell apart with `URLSession` [1]. Since then it’s been something that you only deploy when you absolutely have no other choice. And those situations have disappeared over time, as we’ve enhance other APIs that previously forced you to use `URLProtocol` [2].

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

[1] That was iOS 7, so circa 2013.

[2] The big two were:

- AVFoundation, where we now have `AVAssetResourceLoaderDelegate`
- Web views, where `UIWebView` is no more and `WKWebView` has URL scheme handlers and, if that’s not sufficient, proxy configurations
