Skip to content

Commit 0f98e2d

Browse files
Merge pull request #39 from apivideo/bugfix/ios_request_queue
fix(ios): fix the way RequestTaskQueue operation is synchronous
2 parents ee5b5eb + b6714b5 commit 0f98e2d

File tree

11 files changed

+217
-86
lines changed

11 files changed

+217
-86
lines changed

.github/workflows/build.yml

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
name: Build
2+
3+
on: [push]
4+
5+
jobs:
6+
build-swift:
7+
name: Build with swift
8+
runs-on: macos-12
9+
10+
steps:
11+
- name: Checkout
12+
uses: actions/checkout@v2
13+
- name: xcode version
14+
uses: maxim-lobanov/setup-xcode@v1
15+
with:
16+
xcode-version: '13.4.1'
17+
- name: Build Package with swift
18+
run: swift build
19+
20+
build-xcodebuild:
21+
name: Build with xcodebuild
22+
runs-on: macos-12
23+
24+
steps:
25+
- name: Checkout
26+
uses: actions/checkout@v2
27+
- name: xcode version
28+
uses: maxim-lobanov/setup-xcode@v1
29+
with:
30+
xcode-version: '13.4.1'
31+
- name: Set Default Scheme
32+
run: |
33+
scheme_list=$(xcodebuild -list -json | tr -d "\n")
34+
default=$(echo $scheme_list | ruby -e "require 'json'; puts JSON.parse(STDIN.gets)['workspace']['schemes'][0]")
35+
echo $default | cat >default
36+
echo Using default scheme: $default
37+
- name: Build Package with xcodebuild
38+
env:
39+
scheme: ${{ 'default' }}
40+
run: |
41+
if [ $scheme = default ]; then scheme=$(cat default); fi
42+
xcodebuild -scheme $scheme -destination 'platform=iOS Simulator,name=iPhone 13'
43+
- name: Build Example
44+
env:
45+
scheme: ${{ 'default' }}
46+
run: |
47+
if [ $scheme = default ]; then scheme=$(cat default); fi
48+
xcodebuild clean build -project Example/Example.xcodeproj -scheme $scheme -sdk iphoneos
49+
50+
cocoapods:
51+
name: Verify cocopods podspec
52+
needs: [ build-xcodebuild ]
53+
runs-on: macos-12
54+
55+
steps:
56+
- name: Checkout
57+
uses: actions/checkout@v2
58+
- name: xcode version
59+
uses: maxim-lobanov/setup-xcode@v1
60+
with:
61+
xcode-version: '13.4.1'
62+
- name: Verify cocoapods
63+
run: pod lib lint --allow-warnings

ApiVideoClient.podspec

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ Pod::Spec.new do |s|
55
s.tvos.deployment_target = '10.0'
66
# Add back when CocoaPods/CocoaPods#11558 is released
77
#s.watchos.deployment_target = '3.0'
8-
s.version = '1.1.0'
9-
s.source = { :git => 'https://github.com/apivideo/api.video-ios-client', :tag => 'v1.1.0' }
8+
s.version = '1.1.1'
9+
s.source = { :git => 'https://github.com/apivideo/api.video-ios-client', :tag => 'v1.1.1' }
1010
s.authors = { 'Ecosystem Team' => 'ecosystem@api.video' }
1111
s.license = { :type => 'MIT' }
1212
s.homepage = 'https://docs.api.video'

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
# Changelog
22
All changes to this project will be documented in this file.
33

4+
## [1.1.1] - 2022-12-09
5+
- Fix on upload by chunk and progressive upload.
6+
- Add test on progressive upload.
7+
- Add a `build.yml` CI workflow.
8+
49
## [1.1.0] - 2022-12-06
510
- Refactor upload by chunk and progressive upload. It is now possible to cancel an upload.
611
- Add timeout API

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,14 @@ api.video's iOS streamlines the coding process. Chunking files is handled for y
4242
Specify it in your `Cartfile`:
4343

4444
```
45-
github "apivideo/api.video-ios-client" ~> 1.1.0
45+
github "apivideo/api.video-ios-client" ~> 1.1.1
4646
```
4747

4848
Run `carthage update`
4949

5050
### CocoaPods
5151

52-
Add `pod 'ApiVideoClient', '1.1.0'` in your `Podfile`
52+
Add `pod 'ApiVideoClient', '1.1.1'` in your `Podfile`
5353

5454
Run `pod install`
5555

Sources/APIs.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import Foundation
88
public class ApiVideoClient {
99
public static var apiKey: String? = nil
1010
public static var basePath = "https://ws.api.video"
11-
internal static var customHeaders:[String: String] = ["AV-Origin-Client": "ios:1.1.0"]
11+
internal static var customHeaders:[String: String] = ["AV-Origin-Client": "ios:1.1.1"]
1212
private static var chunkSize: Int = 50 * 1024 * 1024
1313
internal static var requestBuilderFactory: RequestBuilderFactory = AlamofireRequestBuilderFactory()
1414
internal static var credential = ApiVideoCredential()

Sources/APIs/VideosAPI.swift

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ open class VideosAPI {
8686
/**
8787
* Create a progressive upload session
8888
*
89-
* - returns: a progressive upload session
89+
- returns: a progressive upload session
9090
*/
9191
public class func buildProgressiveUploadSession(videoId: String) -> ProgressiveUploadSession {
9292
ProgressiveUploadSession(videoId: videoId)
@@ -101,6 +101,7 @@ open class VideosAPI {
101101
self.videoId = videoId
102102
super.init(queueLabel: videoId)
103103
}
104+
104105

105106
public func uploadPart(file: URL, onProgressReady: ((Progress) -> Void)? = nil, apiResponseQueue: DispatchQueue = ApiVideoClient.apiResponseQueue, completion: @escaping ((_ data: Video?, _ error: Error?) -> Void)) -> RequestTask {
106107
let chunkId = partId
@@ -128,13 +129,14 @@ open class VideosAPI {
128129
numOfChunks = partId
129130
}
130131
let requestBuilder = uploadWithRequestBuilder(videoId: videoId, file: file, chunkId: partId, numOfChunks: numOfChunks, onProgressReady: onProgressReady)
131-
execute(requestBuilder, apiResponseQueue: apiResponseQueue, completion: completion)
132+
execute(requestBuilder, apiResponseQueue: apiResponseQueue) { data, error in
133+
completion(data, error)
134+
}
132135
return requestBuilder.requestTask
133136
}
134137
}
135138

136139

137-
138140
/**
139141
Upload a video
140142
- POST /videos/{videoId}/source
@@ -279,7 +281,7 @@ The latter allows you to split a video source into X chunks and send those chunk
279281
/**
280282
* Create a progressive uploadWithUploadToken session
281283
*
282-
* - returns: a progressive uploadWithUploadToken session
284+
- returns: a progressive uploadWithUploadToken session
283285
*/
284286
public class func buildProgressiveUploadWithUploadTokenSession(token: String) -> ProgressiveUploadWithUploadTokenSession {
285287
ProgressiveUploadWithUploadTokenSession(token: token)
@@ -295,6 +297,12 @@ The latter allows you to split a video source into X chunks and send those chunk
295297
self.token = token
296298
super.init(queueLabel: token)
297299
}
300+
301+
override func willExecuteRequestBuilder(requestBuilder: RequestBuilder<Video>) -> Void {
302+
if let videoId = videoId {
303+
uploadAddVideoIdParameterWithRequestBuilder(requestBuilder: requestBuilder, videoId: videoId)
304+
}
305+
}
298306

299307
public func uploadPart(file: URL, onProgressReady: ((Progress) -> Void)? = nil, apiResponseQueue: DispatchQueue = ApiVideoClient.apiResponseQueue, completion: @escaping ((_ data: Video?, _ error: Error?) -> Void)) -> RequestTask {
300308
let chunkId = partId
@@ -322,11 +330,28 @@ The latter allows you to split a video source into X chunks and send those chunk
322330
numOfChunks = partId
323331
}
324332
let requestBuilder = uploadWithUploadTokenWithRequestBuilder(token: token, file: file, videoId: videoId, chunkId: partId, numOfChunks: numOfChunks, onProgressReady: onProgressReady)
325-
execute(requestBuilder, apiResponseQueue: apiResponseQueue, completion: completion)
333+
execute(requestBuilder, apiResponseQueue: apiResponseQueue) { data, error in
334+
if let data = data {
335+
self.videoId = data.videoId
336+
}
337+
completion(data, error)
338+
}
326339
return requestBuilder.requestTask
327340
}
328341
}
329-
342+
/**
343+
* Add a videoId to the request builder if it does not exist already.
344+
- parameter requestBuilder: the request builder
345+
- parameter videoId: the videoId to add to the request
346+
*/
347+
internal class func uploadAddVideoIdParameterWithRequestBuilder(requestBuilder: RequestBuilder<Video>, videoId: String) {
348+
guard let parameters = requestBuilder.parameters else {
349+
return
350+
}
351+
if (!parameters.keys.contains("videoId")) {
352+
requestBuilder.parameters!["videoId"] = videoId
353+
}
354+
}
330355

331356

332357
/**
Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
// ProgressiveUploadSessioning.swift
1+
// ProgressiveUploadSessionProtocol.swift
22
//
33

44
import Foundation
55

66
public protocol ProgressiveUploadSessionProtocol {
7-
func uploadPart(file: URL, onProgressReady: ((Progress) -> Void)?, apiResponseQueue: DispatchQueue, completion: @escaping ((_ data: Video?, _ error: Error?) -> Void)) throws -> RequestTask
8-
func uploadLastPart(file: URL, onProgressReady: ((Progress) -> Void)?, apiResponseQueue: DispatchQueue, completion: @escaping ((_ data: Video?, _ error: Error?) -> Void)) throws -> RequestTask
9-
func uploadPart(file: URL, partId: Int, onProgressReady: ((Progress) -> Void)?, apiResponseQueue: DispatchQueue, completion: @escaping ((_ data: Video?, _ error: Error?) -> Void)) throws -> RequestTask
10-
func uploadLastPart(file: URL, partId: Int, onProgressReady: ((Progress) -> Void)?, apiResponseQueue: DispatchQueue, completion: @escaping ((_ data: Video?, _ error: Error?) -> Void)) throws -> RequestTask
11-
func uploadPart(file: URL, partId: Int, isLastPart: Bool, onProgressReady: ((Progress) -> Void)?, apiResponseQueue: DispatchQueue, completion: @escaping ((_ data: Video?, _ error: Error?) -> Void)) throws -> RequestTask
7+
func uploadPart(file: URL, onProgressReady: ((Progress) -> Void)?, apiResponseQueue: DispatchQueue, completion: @escaping ((_ data: Video?, _ error: Error?) -> Void)) -> RequestTask
8+
func uploadLastPart(file: URL, onProgressReady: ((Progress) -> Void)?, apiResponseQueue: DispatchQueue, completion: @escaping ((_ data: Video?, _ error: Error?) -> Void)) -> RequestTask
9+
func uploadPart(file: URL, partId: Int, onProgressReady: ((Progress) -> Void)?, apiResponseQueue: DispatchQueue, completion: @escaping ((_ data: Video?, _ error: Error?) -> Void)) -> RequestTask
10+
func uploadLastPart(file: URL, partId: Int, onProgressReady: ((Progress) -> Void)?, apiResponseQueue: DispatchQueue, completion: @escaping ((_ data: Video?, _ error: Error?) -> Void)) -> RequestTask
11+
func uploadPart(file: URL, partId: Int, isLastPart: Bool, onProgressReady: ((Progress) -> Void)?, apiResponseQueue: DispatchQueue, completion: @escaping ((_ data: Video?, _ error: Error?) -> Void)) -> RequestTask
12+
13+
func cancel()
1214
}

Sources/Upload/RequestTaskQueue.swift

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import Alamofire
33

44
public class RequestTaskQueue<T>: RequestTask {
55
private let operationQueue: OperationQueue
6-
private let operationLock = NSLock()
76
private var requestBuilders: [RequestBuilder<T>] = []
87

98
private let _downloadProgress = Progress(totalUnitCount: 0)
@@ -53,7 +52,7 @@ public class RequestTaskQueue<T>: RequestTask {
5352
apiResponseQueue: DispatchQueue = ApiVideoClient.apiResponseQueue,
5453
completion: @escaping (_ data: T?, _ error: Error?) -> Void) -> Void {
5554
requestBuilders.append(requestBuilder)
56-
return operationQueue.addOperation(RequestOperation(lock: operationLock, requestBuilder: requestBuilder, apiResponseQueue: apiResponseQueue, willExecuteRequestBuilder: willExecuteRequestBuilder, completion: completion))
55+
return operationQueue.addOperation(RequestOperation(requestBuilder: requestBuilder, apiResponseQueue: apiResponseQueue, willExecuteRequestBuilder: willExecuteRequestBuilder, completion: completion))
5756
}
5857

5958

@@ -66,14 +65,13 @@ public class RequestTaskQueue<T>: RequestTask {
6665
}
6766

6867
final class RequestOperation<T>: Operation {
69-
private let lock: NSLock
7068
private let requestBuilder: RequestBuilder<T>
7169
private let apiResponseQueue: DispatchQueue
7270
private let completion: (_ data: T?, _ error: Error?) -> Void
7371
private let willExecuteRequestBuilder: (_: RequestBuilder<T>) -> Void
74-
75-
init(lock: NSLock, requestBuilder: RequestBuilder<T>, apiResponseQueue: DispatchQueue, willExecuteRequestBuilder: @escaping (_: RequestBuilder<T>) -> Void, completion: @escaping (_ data: T?, _ error: Error?) -> Void) {
76-
self.lock = lock
72+
private let group = DispatchGroup()
73+
74+
init(requestBuilder: RequestBuilder<T>, apiResponseQueue: DispatchQueue, willExecuteRequestBuilder: @escaping (_: RequestBuilder<T>) -> Void, completion: @escaping (_ data: T?, _ error: Error?) -> Void) {
7775
self.requestBuilder = requestBuilder
7876
self.apiResponseQueue = apiResponseQueue
7977
self.willExecuteRequestBuilder = willExecuteRequestBuilder
@@ -82,11 +80,11 @@ final class RequestOperation<T>: Operation {
8280
}
8381

8482
override func main() {
85-
self.lock.lock()
8683
guard !isCancelled else {
87-
self.lock.unlock()
8884
return
8985
}
86+
group.enter()
87+
9088
self.willExecuteRequestBuilder(requestBuilder)
9189
requestBuilder.execute(apiResponseQueue) { result in
9290
switch result {
@@ -95,8 +93,9 @@ final class RequestOperation<T>: Operation {
9593
case let .failure(error):
9694
self.completion(nil, error)
9795
}
98-
self.lock.unlock()
96+
self.group.leave()
9997
}
100-
98+
// Make task synchronous
99+
group.wait()
101100
}
102101
}

Sources/Upload/UploadChunkRequestTaskQueue.swift

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public class UploadChunkRequestTaskQueue: RequestTaskQueue<Video> {
6969

7070
override func willExecuteRequestBuilder(requestBuilder: RequestBuilder<Video>) -> Void {
7171
if let videoId = videoId {
72-
uploadAddVideoIdParameterWithRequestBuilder(requestBuilder: requestBuilder, videoId: videoId)
72+
VideosAPI.uploadAddVideoIdParameterWithRequestBuilder(requestBuilder: requestBuilder, videoId: videoId)
7373
}
7474
requestBuilder.onProgressReady = progressReadyHook
7575
}
@@ -101,18 +101,4 @@ public class UploadChunkRequestTaskQueue: RequestTaskQueue<Video> {
101101
}
102102
}
103103
}
104-
105-
/**
106-
* Add a videoId to the request builder if it does not exist already.
107-
- parameter requestBuilder: the request builder
108-
- parameter videoId: the videoId to add to the request
109-
*/
110-
private func uploadAddVideoIdParameterWithRequestBuilder(requestBuilder: RequestBuilder<Video>, videoId: String) {
111-
guard let parameters = requestBuilder.parameters else {
112-
return
113-
}
114-
if (!parameters.keys.contains("videoId")) {
115-
requestBuilder.parameters!["videoId"] = videoId
116-
}
117-
}
118104
}

0 commit comments

Comments
 (0)