File tree Expand file tree Collapse file tree 2 files changed +48
-0
lines changed
Expand file tree Collapse file tree 2 files changed +48
-0
lines changed Original file line number Diff line number Diff line change @@ -248,6 +248,8 @@ open class ImageDownloader: @unchecked Sendable {
248248 // The session bound to the downloader.
249249 private var session : URLSession
250250
251+ private let lock = NSLock ( )
252+
251253 // MARK: Initializers
252254
253255 /// Creates a downloader with a given name.
@@ -375,6 +377,9 @@ open class ImageDownloader: @unchecked Sendable {
375377 callback: SessionDataTask . TaskCallback
376378 ) -> DownloadTask
377379 {
380+ lock. lock ( )
381+ defer { lock. unlock ( ) }
382+
378383 // Ready to start download. Add it to session task manager (`sessionHandler`)
379384 let downloadTask : DownloadTask
380385 if let existingTask = sessionDelegate. task ( for: context. url) {
Original file line number Diff line number Diff line change @@ -684,6 +684,49 @@ class ImageDownloaderTests: XCTestCase {
684684 XCTAssertEqual ( result. originalData, testImageData)
685685 XCTAssertEqual ( result. url, url)
686686 }
687+
688+ func testConcurrentDownloadSameURL( ) {
689+ let exp = expectation ( description: #function)
690+
691+ // Given
692+ let url = testURLs [ 0 ]
693+ stub ( url, data: testImageData)
694+
695+ let callbackLock = NSLock ( )
696+ var callbackCount = 0
697+ let expectedCount = 10
698+ exp. expectedFulfillmentCount = expectedCount
699+
700+ // When
701+ DispatchQueue . concurrentPerform ( iterations: expectedCount) { index in
702+ downloader. downloadImage ( with: url) { result in
703+ switch result {
704+ case . success( let imageResult) :
705+ XCTAssertNotNil ( imageResult. image)
706+ XCTAssertEqual ( imageResult. url, url)
707+ XCTAssertEqual ( imageResult. originalData, testImageData)
708+
709+ callbackLock. lock ( )
710+ callbackCount += 1
711+ callbackLock. unlock ( )
712+
713+ exp. fulfill ( )
714+
715+ case . failure( let error) :
716+ XCTFail ( " Download should succeed: \( error) " )
717+ exp. fulfill ( )
718+ }
719+ }
720+ }
721+
722+ // Then
723+ waitForExpectations ( timeout: 3 ) { _ in
724+ callbackLock. lock ( )
725+ let finalCount = callbackCount
726+ callbackLock. unlock ( )
727+ XCTAssertEqual ( finalCount, expectedCount, " All \( expectedCount) concurrent requests should receive callbacks " )
728+ }
729+ }
687730}
688731
689732class URLNilDataModifier : ImageDownloaderDelegate {
You can’t perform that action at this time.
0 commit comments