Skip to content

Commit 86d4d19

Browse files
feat: support for 0.13.0
1 parent af4a92d commit 86d4d19

39 files changed

+600
-185
lines changed

Package.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ import PackageDescription
44

55
let package = Package(
66
name: "Appwrite",
7+
platforms: [
8+
.iOS("15.0"),
9+
.macOS("11.0"),
10+
.watchOS("6.0"),
11+
.tvOS("13.0"),
12+
],
713
products: [
814
.library(
915
name: "Appwrite",

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22

33
![Swift Package Manager](https://img.shields.io/github/v/release/appwrite/sdk-for-apple.svg?color=green&style=flat-square)
44
![License](https://img.shields.io/github/license/appwrite/sdk-for-apple.svg?style=flat-square)
5-
![Version](https://img.shields.io/badge/api%20version-0.12.0-blue.svg?style=flat-square)
5+
![Version](https://img.shields.io/badge/api%20version-0.13.0-blue.svg?style=flat-square)
66
[![Build Status](https://img.shields.io/travis/com/appwrite/sdk-generator?style=flat-square)](https://travis-ci.com/appwrite/sdk-generator)
77
[![Twitter Account](https://img.shields.io/twitter/follow/appwrite?color=00acee&label=twitter&style=flat-square)](https://twitter.com/appwrite)
88
[![Discord](https://img.shields.io/discord/564160730845151244?label=discord&style=flat-square)](https://appwrite.io/discord)
99

10-
**This SDK is compatible with Appwrite server version 0.12.x. For older versions, please check [previous releases](https://github.com/appwrite/sdk-for-apple/releases).**
10+
**This SDK is compatible with Appwrite server version 0.13.x. For older versions, please check [previous releases](https://github.com/appwrite/sdk-for-apple/releases).**
1111

1212
Appwrite is an open-source backend as a service server that abstract and simplify complex and repetitive development tasks behind a very simple to use REST API. Appwrite aims to help you develop your apps faster and in a more secure way. Use the Apple SDK to integrate your app with the Appwrite server to easily start interacting with all of Appwrite backend APIs and tools. For full API documentation and tutorials go to [https://appwrite.io/docs](https://appwrite.io/docs)
1313

@@ -17,13 +17,13 @@ Appwrite is an open-source backend as a service server that abstract and simplif
1717

1818
### Xcode with Swift Package Manager
1919

20-
The Appwrite Swift SDK is available via multiple package managers, including Swift Package Manager. In order to use the Appwrite Swift SDK from Xcode, select File > Swift Packages > **Add Package Dependency**
20+
The Appwrite Swift SDK is available via Swift Package Manager. In order to use the Appwrite Swift SDK from Xcode, select File > **Add Packages**
2121

22-
In the dialog that appears, enter the Appwrite Swift SDK [package URL]([email protected]:appwrite/sdk-for-apple.git) and click **Next**.
22+
In the dialog that appears, enter the Appwrite Swift SDK [package URL]([email protected]:appwrite/sdk-for-apple.git) in the search field. Once found, select `sdk-for-apple`.
2323

24-
Once the repository information is loaded, add your version rules and click **Next** again.
24+
On the right, select your version rules and ensure your desired target is selected in the **Add to Project** field.
2525

26-
On the final screen, make sure you see `Appwrite` as a product selected for your target:
26+
Now click add package and you're done!
2727

2828
### Swift Package Manager
2929

Sources/Appwrite/Client.swift

Lines changed: 115 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,23 +12,28 @@ let CRLF = "\r\n"
1212
open class Client {
1313

1414
// MARK: Properties
15+
public static var chunkSize = 5 * 1024 * 1024 // 5MB
1516

1617
open var endPoint = "https://HOSTNAME/v1"
1718

1819
open var endPointRealtime: String? = nil
1920

2021
open var headers: [String: String] = [
2122
"content-type": "",
22-
"x-sdk-version": "appwrite:swiftclient:0.2.1", "X-Appwrite-Response-Format": "0.12.0"
23+
"x-sdk-version": "appwrite:swiftclient:0.2.1", "X-Appwrite-Response-Format": "0.13.0"
2324
]
2425

2526
open var config: [String: String] = [:]
2627

28+
open var selfSigned: Bool = false
29+
2730
open var http: HTTPClient
2831

2932
private static let boundaryChars =
3033
"abcdefghijklmnopqrstuvwxyz1234567890"
3134

35+
private static let boundary = randomBoundary()
36+
3237
private static var eventLoopGroupProvider =
3338
HTTPClient.EventLoopGroupProvider.createNew
3439

@@ -136,6 +141,7 @@ open class Client {
136141
/// @return Client
137142
///
138143
open func setSelfSigned(_ status: Bool = true) -> Client {
144+
self.selfSigned = status
139145
try! http.syncShutdown()
140146
http = Client.createHTTP(selfSigned: status)
141147
return self
@@ -266,7 +272,9 @@ open class Client {
266272
method: .RAW(value: method)
267273
)
268274
} catch {
269-
completion?(Result.failure(AppwriteError(message: error.localizedDescription)))
275+
completion?(Result.failure(AppwriteError(
276+
message: error.localizedDescription
277+
)))
270278
return
271279
}
272280

@@ -281,7 +289,9 @@ open class Client {
281289
do {
282290
try buildBody(for: &request, with: validParams)
283291
} catch let error {
284-
completion?(Result.failure(AppwriteError(message: error.localizedDescription)))
292+
completion?(Result.failure(AppwriteError(
293+
message: error.localizedDescription
294+
)))
285295
return
286296
}
287297

@@ -299,7 +309,7 @@ open class Client {
299309
with params: [String: Any?]
300310
) throws {
301311
if request.headers["content-type"][0] == "multipart/form-data" {
302-
buildMultipart(&request, with: params)
312+
buildMultipart(&request, with: params, chunked: !request.headers["content-range"].isEmpty)
303313
} else {
304314
try buildJSON(&request, with: params)
305315
}
@@ -334,7 +344,10 @@ open class Client {
334344
}
335345

336346
switch result {
337-
case .failure(let error): print(error)
347+
case .failure(let error):
348+
completion(.failure(AppwriteError(
349+
message: error.localizedDescription
350+
)))
338351
case .success(var response):
339352
switch response.status.code {
340353
case 0..<400:
@@ -361,20 +374,32 @@ open class Client {
361374
}
362375
default:
363376
var message = ""
377+
var type = ""
378+
379+
if response.body == nil {
380+
completion(.failure(AppwriteError(
381+
message: "Unknown error with status code \(response.status.code)",
382+
code: Int(response.status.code)
383+
)))
384+
return
385+
}
364386

365387
do {
366388
let dict = try JSONSerialization
367389
.jsonObject(with: response.body!) as? [String: Any]
368390

369391
message = dict?["message"] as? String
370392
?? response.status.reasonPhrase
393+
394+
type = dict?["type"] as? String ?? ""
371395
} catch {
372396
message = response.body!.readString(length: response.body!.readableBytes)!
373397
}
374398

375399
let error = AppwriteError(
376400
message: message,
377-
code: Int(response.status.code)
401+
code: Int(response.status.code),
402+
type: type
378403
)
379404

380405
completion(.failure(error))
@@ -383,7 +408,83 @@ open class Client {
383408
}
384409
}
385410

386-
private func randomBoundary() -> String {
411+
func chunkedUpload<T>(
412+
path: String,
413+
headers: inout [String: String],
414+
params: inout [String: Any?],
415+
paramName: String,
416+
convert: (([String: Any]) -> T)? = nil,
417+
onProgress: ((UploadProgress) -> Void)? = nil,
418+
completion: ((Result<T, AppwriteError>) -> Void)? = nil
419+
) {
420+
let file = params[paramName] as! File
421+
let size = file.buffer.readableBytes
422+
423+
if size < Client.chunkSize {
424+
call(
425+
method: "POST",
426+
path: path,
427+
headers: headers,
428+
params: params,
429+
convert: convert,
430+
completion: completion
431+
)
432+
return
433+
}
434+
435+
var input = file.buffer
436+
var offset = 0
437+
var result = [String:Any]()
438+
let group = DispatchGroup()
439+
440+
while offset < size {
441+
let slice = input.readSlice(length: Client.chunkSize)
442+
?? input.readSlice(length: Int(size - offset))
443+
444+
params[paramName] = File(
445+
name: file.name,
446+
buffer: slice!
447+
)
448+
449+
headers["content-range"] = "bytes \(offset)-\(min((offset + Client.chunkSize) - 1, size))/\(size)"
450+
451+
group.enter()
452+
453+
call(
454+
method: "POST",
455+
path: path,
456+
headers: headers,
457+
params: params,
458+
convert: { return $0 }
459+
) { response in
460+
switch response {
461+
case let .success(map):
462+
result = map
463+
group.leave()
464+
case let .failure(error):
465+
completion?(.failure(error))
466+
return
467+
}
468+
}
469+
470+
group.wait()
471+
472+
offset += Client.chunkSize
473+
headers["x-appwrite-id"] = result["$id"] as? String
474+
onProgress?(UploadProgress(
475+
id: result["$id"] as? String ?? "",
476+
progress: Double(min(offset, size))/Double(size) * 100.0,
477+
sizeUploaded: min(offset, size),
478+
chunksTotal: result["chunksTotal"] as? Int ?? -1,
479+
chunksUploaded: result["chunksUploaded"] as? Int ?? -1
480+
))
481+
}
482+
483+
completion?(.success(convert!(result)))
484+
}
485+
486+
487+
private static func randomBoundary() -> String {
387488
var string = ""
388489
for _ in 0..<16 {
389490
string.append(Client.boundaryChars.randomElement()!)
@@ -402,11 +503,12 @@ open class Client {
402503

403504
private func buildMultipart(
404505
_ request: inout HTTPClient.Request,
405-
with params: [String: Any?] = [:]
506+
with params: [String: Any?] = [:],
507+
chunked: Bool = false
406508
) {
407509
func addPart(name: String, value: Any) {
408510
bodyBuffer.writeString(DASHDASH)
409-
bodyBuffer.writeString(boundary)
511+
bodyBuffer.writeString(Client.boundary)
410512
bodyBuffer.writeString(CRLF)
411513
bodyBuffer.writeString("Content-Disposition: form-data; name=\"\(name)\"")
412514

@@ -428,7 +530,6 @@ open class Client {
428530
bodyBuffer.writeString(CRLF)
429531
}
430532

431-
let boundary = randomBoundary()
432533
var bodyBuffer = ByteBuffer()
433534

434535
for (key, value) in params {
@@ -447,13 +548,15 @@ open class Client {
447548
}
448549

449550
bodyBuffer.writeString(DASHDASH)
450-
bodyBuffer.writeString(boundary)
551+
bodyBuffer.writeString(Client.boundary)
451552
bodyBuffer.writeString(DASHDASH)
452553
bodyBuffer.writeString(CRLF)
453554

454555
request.headers.remove(name: "content-type")
556+
if !chunked {
455557
request.headers.add(name: "Content-Length", value: bodyBuffer.readableBytes.description)
456-
request.headers.add(name: "Content-Type", value: "multipart/form-data;boundary=\"\(boundary)\"")
558+
}
559+
request.headers.add(name: "Content-Type", value: "multipart/form-data;boundary=\"\(Client.boundary)\"")
457560
request.body = .byteBuffer(bodyBuffer)
458561
}
459562

Sources/Appwrite/Models/AppwriteError.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ open class AppwriteError : Swift.Error, Decodable {
44

55
public let message: String
66
public let code: Int?
7+
public let type: String?
78

8-
init(message: String, code: Int? = nil) {
9+
init(message: String, code: Int? = nil, type: String? = nil) {
910
self.message = message
1011
self.code = code
12+
self.type = type
1113
}
1214
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
public class UploadProgress {
2+
public let id: String
3+
public let progress: Double
4+
public let sizeUploaded: Int
5+
public let chunksTotal: Int
6+
public let chunksUploaded: Int
7+
8+
public init(id: String, progress: Double, sizeUploaded: Int, chunksTotal: Int, chunksUploaded: Int) {
9+
self.id = id
10+
self.progress = progress
11+
self.sizeUploaded = sizeUploaded
12+
self.chunksTotal = chunksTotal
13+
self.chunksUploaded = chunksUploaded
14+
}
15+
}

0 commit comments

Comments
 (0)