Skip to content

Commit 206fbb4

Browse files
Merge pull request #45 from apivideo/bugfix/ios_request_queue
fix(ios): fix the way RequestTaskQueue operation is synchronous
2 parents d237ec3 + 71fbd32 commit 206fbb4

File tree

10 files changed

+123
-43
lines changed

10 files changed

+123
-43
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

ApiVideoUploader.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-uploader', :tag => 'v1.1.0' }
8+
s.version = '1.1.1'
9+
s.source = { :git => 'https://github.com/apivideo/api.video-ios-uploader', :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
@@ -39,14 +39,14 @@ It allows you to upload videos in two ways:
3939
Specify it in your `Cartfile`:
4040

4141
```
42-
github "apivideo/api.video-ios-uploader" ~> 1.1.0
42+
github "apivideo/api.video-ios-uploader" ~> 1.1.1
4343
```
4444

4545
Run `carthage update`
4646

4747
### CocoaPods
4848

49-
Add `pod 'ApiVideoUploader', '1.1.0'` in your `Podfile`
49+
Add `pod 'ApiVideoUploader', '1.1.1'` in your `Podfile`
5050

5151
Run `pod install`
5252

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 ApiVideoUploader {
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-uploader:1.1.0"]
11+
internal static var customHeaders:[String: String] = ["AV-Origin-Client": "ios-uploader: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
@@ -40,7 +40,7 @@ open class VideosAPI {
4040
/**
4141
* Create a progressive upload session
4242
*
43-
* - returns: a progressive upload session
43+
- returns: a progressive upload session
4444
*/
4545
public class func buildProgressiveUploadSession(videoId: String) -> ProgressiveUploadSession {
4646
ProgressiveUploadSession(videoId: videoId)
@@ -55,6 +55,7 @@ open class VideosAPI {
5555
self.videoId = videoId
5656
super.init(queueLabel: videoId)
5757
}
58+
5859

5960
public func uploadPart(file: URL, onProgressReady: ((Progress) -> Void)? = nil, apiResponseQueue: DispatchQueue = ApiVideoUploader.apiResponseQueue, completion: @escaping ((_ data: Video?, _ error: Error?) -> Void)) -> RequestTask {
6061
let chunkId = partId
@@ -82,13 +83,14 @@ open class VideosAPI {
8283
numOfChunks = partId
8384
}
8485
let requestBuilder = uploadWithRequestBuilder(videoId: videoId, file: file, chunkId: partId, numOfChunks: numOfChunks, onProgressReady: onProgressReady)
85-
execute(requestBuilder, apiResponseQueue: apiResponseQueue, completion: completion)
86+
execute(requestBuilder, apiResponseQueue: apiResponseQueue) { data, error in
87+
completion(data, error)
88+
}
8689
return requestBuilder.requestTask
8790
}
8891
}
8992

9093

91-
9294
/**
9395
Upload a video
9496
- POST /videos/{videoId}/source
@@ -233,7 +235,7 @@ The latter allows you to split a video source into X chunks and send those chunk
233235
/**
234236
* Create a progressive uploadWithUploadToken session
235237
*
236-
* - returns: a progressive uploadWithUploadToken session
238+
- returns: a progressive uploadWithUploadToken session
237239
*/
238240
public class func buildProgressiveUploadWithUploadTokenSession(token: String) -> ProgressiveUploadWithUploadTokenSession {
239241
ProgressiveUploadWithUploadTokenSession(token: token)
@@ -249,6 +251,12 @@ The latter allows you to split a video source into X chunks and send those chunk
249251
self.token = token
250252
super.init(queueLabel: token)
251253
}
254+
255+
override func willExecuteRequestBuilder(requestBuilder: RequestBuilder<Video>) -> Void {
256+
if let videoId = videoId {
257+
uploadAddVideoIdParameterWithRequestBuilder(requestBuilder: requestBuilder, videoId: videoId)
258+
}
259+
}
252260

253261
public func uploadPart(file: URL, onProgressReady: ((Progress) -> Void)? = nil, apiResponseQueue: DispatchQueue = ApiVideoUploader.apiResponseQueue, completion: @escaping ((_ data: Video?, _ error: Error?) -> Void)) -> RequestTask {
254262
let chunkId = partId
@@ -276,11 +284,28 @@ The latter allows you to split a video source into X chunks and send those chunk
276284
numOfChunks = partId
277285
}
278286
let requestBuilder = uploadWithUploadTokenWithRequestBuilder(token: token, file: file, videoId: videoId, chunkId: partId, numOfChunks: numOfChunks, onProgressReady: onProgressReady)
279-
execute(requestBuilder, apiResponseQueue: apiResponseQueue, completion: completion)
287+
execute(requestBuilder, apiResponseQueue: apiResponseQueue) { data, error in
288+
if let data = data {
289+
self.videoId = data.videoId
290+
}
291+
completion(data, error)
292+
}
280293
return requestBuilder.requestTask
281294
}
282295
}
283-
296+
/**
297+
* Add a videoId to the request builder if it does not exist already.
298+
- parameter requestBuilder: the request builder
299+
- parameter videoId: the videoId to add to the request
300+
*/
301+
internal class func uploadAddVideoIdParameterWithRequestBuilder(requestBuilder: RequestBuilder<Video>, videoId: String) {
302+
guard let parameters = requestBuilder.parameters else {
303+
return
304+
}
305+
if (!parameters.keys.contains("videoId")) {
306+
requestBuilder.parameters!["videoId"] = videoId
307+
}
308+
}
284309

285310

286311
/**
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 = ApiVideoUploader.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
}

project.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ targets:
77
sources: [Sources]
88
info:
99
path: ./Info.plist
10-
version: 1.1.0
10+
version: 1.1.1
1111
settings:
1212
APPLICATION_EXTENSION_API_ONLY: true
1313
scheme: {}

0 commit comments

Comments
 (0)