├── .gitignore ├── Dockerfile ├── LICENSE ├── Package.resolved ├── Package.swift ├── README.md ├── Sources └── PlaygroundForSwiftServer │ ├── Resources │ ├── code.tar.gz │ ├── index.js │ └── nature.jpg │ └── playground.swift └── docker-compose.yml /.gitignore: -------------------------------------------------------------------------------- 1 | .swiftpm 2 | .build 3 | **.DS_Store 4 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM swift:5.7.0 2 | WORKDIR /app 3 | COPY Package.swift Package.swift 4 | COPY Package.resolved Package.resolved 5 | RUN swift package resolve 6 | COPY . . 7 | CMD swift run 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2021, Appwrite 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "async-http-client", 6 | "repositoryURL": "https://github.com/swift-server/async-http-client.git", 7 | "state": { 8 | "branch": null, 9 | "revision": "e8babad8226b9b3f956a252d5b80e36b0c9d62a9", 10 | "version": "1.22.0" 11 | } 12 | }, 13 | { 14 | "package": "Appwrite", 15 | "repositoryURL": "https://github.com/appwrite/sdk-for-swift", 16 | "state": { 17 | "branch": null, 18 | "revision": "4544a01720da808bc7e3462235f3c522eebc5cab", 19 | "version": "5.0.2" 20 | } 21 | }, 22 | { 23 | "package": "swift-algorithms", 24 | "repositoryURL": "https://github.com/apple/swift-algorithms", 25 | "state": { 26 | "branch": null, 27 | "revision": "f6919dfc309e7f1b56224378b11e28bab5bccc42", 28 | "version": "1.2.0" 29 | } 30 | }, 31 | { 32 | "package": "swift-atomics", 33 | "repositoryURL": "https://github.com/apple/swift-atomics.git", 34 | "state": { 35 | "branch": null, 36 | "revision": "cd142fd2f64be2100422d658e7411e39489da985", 37 | "version": "1.2.0" 38 | } 39 | }, 40 | { 41 | "package": "swift-collections", 42 | "repositoryURL": "https://github.com/apple/swift-collections.git", 43 | "state": { 44 | "branch": null, 45 | "revision": "3d2dc41a01f9e49d84f0a3925fb858bed64f702d", 46 | "version": "1.1.2" 47 | } 48 | }, 49 | { 50 | "package": "swift-http-types", 51 | "repositoryURL": "https://github.com/apple/swift-http-types", 52 | "state": { 53 | "branch": null, 54 | "revision": "ae67c8178eb46944fd85e4dc6dd970e1f3ed6ccd", 55 | "version": "1.3.0" 56 | } 57 | }, 58 | { 59 | "package": "swift-log", 60 | "repositoryURL": "https://github.com/apple/swift-log.git", 61 | "state": { 62 | "branch": null, 63 | "revision": "9cb486020ebf03bfa5b5df985387a14a98744537", 64 | "version": "1.6.1" 65 | } 66 | }, 67 | { 68 | "package": "swift-nio", 69 | "repositoryURL": "https://github.com/apple/swift-nio.git", 70 | "state": { 71 | "branch": null, 72 | "revision": "4c4453b489cf76e6b3b0f300aba663eb78182fad", 73 | "version": "2.70.0" 74 | } 75 | }, 76 | { 77 | "package": "swift-nio-extras", 78 | "repositoryURL": "https://github.com/apple/swift-nio-extras.git", 79 | "state": { 80 | "branch": null, 81 | "revision": "d1ead62745cc3269e482f1c51f27608057174379", 82 | "version": "1.24.0" 83 | } 84 | }, 85 | { 86 | "package": "swift-nio-http2", 87 | "repositoryURL": "https://github.com/apple/swift-nio-http2.git", 88 | "state": { 89 | "branch": null, 90 | "revision": "b5f7062b60e4add1e8c343ba4eb8da2e324b3a94", 91 | "version": "1.34.0" 92 | } 93 | }, 94 | { 95 | "package": "swift-nio-ssl", 96 | "repositoryURL": "https://github.com/apple/swift-nio-ssl.git", 97 | "state": { 98 | "branch": null, 99 | "revision": "7b84abbdcef69cc3be6573ac12440220789dcd69", 100 | "version": "2.27.2" 101 | } 102 | }, 103 | { 104 | "package": "swift-nio-transport-services", 105 | "repositoryURL": "https://github.com/apple/swift-nio-transport-services.git", 106 | "state": { 107 | "branch": null, 108 | "revision": "38ac8221dd20674682148d6451367f89c2652980", 109 | "version": "1.21.0" 110 | } 111 | }, 112 | { 113 | "package": "swift-numerics", 114 | "repositoryURL": "https://github.com/apple/swift-numerics.git", 115 | "state": { 116 | "branch": null, 117 | "revision": "0a5bc04095a675662cf24757cc0640aa2204253b", 118 | "version": "1.0.2" 119 | } 120 | }, 121 | { 122 | "package": "swift-system", 123 | "repositoryURL": "https://github.com/apple/swift-system.git", 124 | "state": { 125 | "branch": null, 126 | "revision": "d2ba781702a1d8285419c15ee62fd734a9437ff5", 127 | "version": "1.3.2" 128 | } 129 | } 130 | ] 131 | }, 132 | "version": 1 133 | } 134 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.5 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "PlaygroundForSwiftServer", 8 | platforms: [ 9 | .macOS(.v11) 10 | ], 11 | dependencies: [ 12 | // Dependencies declare other packages that this package depends on. 13 | .package(name: "Appwrite", url: "https://github.com/appwrite/sdk-for-swift", .exact("5.0.2")) 14 | ], 15 | targets: [ 16 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 17 | // Targets can depend on other targets in this package, and on products in packages this package depends on. 18 | .executableTarget( 19 | name: "PlaygroundForSwiftServer", 20 | dependencies: [ 21 | "Appwrite" 22 | ], 23 | resources: [ 24 | .process("Resources/nature.jpg") 25 | ] 26 | ) 27 | ] 28 | ) 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Playground for Swift 2 | 3 | Simple examples that help you get started with Appwrite + Swift (=❤️) 4 | 5 | This is Appwrite server side integration with Swift. For Apple integration please look at our [Apple playground](https://github.com/appwrite/playground-for-apple-swiftui) and [Apple SDK](https://github.com/appwrite/sdk-for-apple) 6 | 7 | ### Work in progress 8 | 9 | Appwrite playground is a simple way to explore the Appwrite API and Appwrite Swift SDK. Use the source code of this page to learn how to use different Appwrite Swift SDK features. 10 | 11 | ## Get Started 12 | This playground doesn't include any Appwrite best practices, but rather intended to show some of the most simple examples and use cases of using the Appwrite API in your Swift application. 13 | 14 | ## System Requirements 15 | * An Appwrite instance. 16 | * Create a project in Appwrite instance using console 17 | * Generate a secret key in the Appwrite instance using console 18 | 19 | ### Installation 20 | 1. Clone this repository 21 | 2. Open `Sources/PlaygroundForSwiftServer/main.swift` file 22 | 3. Copy the project id, endpoint, secret key from the Appwrite Console 23 | 4. Update project id, endpoint, secret key by copying from the console in the `Sources/PlaygroundForSwiftServer/main.swift` file 24 | 5. Run the playground: 25 | - Xcode: 26 | - Click **Run** with the `PlaygroundForSwiftServer` scheme selected 27 | - Command Line: 28 | - Execute `swift run` in the root of the repository 29 | - Docker: 30 | - Execute `docker compose up` 31 | 6. You will see the JSON response in the console 32 | 33 | ### API Covered in Playground. 34 | * Create Collection 35 | * List Collection 36 | * Delete Collection 37 | * Add Document 38 | * List Documents 39 | * Upload File 40 | * Delete File 41 | * Create Function 42 | * List Function 43 | * Delete Function 44 | * Create User 45 | * List User 46 | * Delete User 47 | 48 | ## Contributing 49 | 50 | All code contributions - including those of people having commit access - must go through a pull request and approved by a core developer before being merged. This is to ensure proper review of all the code. 51 | 52 | We truly ❤️ pull requests! If you wish to help, you can learn more about how you can contribute to this project in the [contribution guide](https://github.com/appwrite/appwrite/blob/master/CONTRIBUTING.md). 53 | 54 | ## Security 55 | 56 | For security issues, kindly email us [security@appwrite.io](mailto:security@appwrite.io) instead of posting a public issue in GitHub. 57 | 58 | ## Follow Us 59 | 60 | Join our growing community around the world! Follow us on [Twitter](https://twitter.com/appwrite), [Facebook Page](https://www.facebook.com/appwrite.io), [Facebook Group](https://www.facebook.com/groups/appwrite.developers/) or join our [Discord Server](https://appwrite.io/discord) for more help, ideas and discussions. 61 | -------------------------------------------------------------------------------- /Sources/PlaygroundForSwiftServer/Resources/code.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appwrite/playground-for-swift/2e6232bc62406121c88423f02242f15b564446ee/Sources/PlaygroundForSwiftServer/Resources/code.tar.gz -------------------------------------------------------------------------------- /Sources/PlaygroundForSwiftServer/Resources/index.js: -------------------------------------------------------------------------------- 1 | module.exports = async ({req, res}) => { 2 | return res.json({ 3 | message: "Hello Open Runtimes 👋" 4 | }); 5 | } 6 | -------------------------------------------------------------------------------- /Sources/PlaygroundForSwiftServer/Resources/nature.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appwrite/playground-for-swift/2e6232bc62406121c88423f02242f15b564446ee/Sources/PlaygroundForSwiftServer/Resources/nature.jpg -------------------------------------------------------------------------------- /Sources/PlaygroundForSwiftServer/playground.swift: -------------------------------------------------------------------------------- 1 | import Appwrite 2 | import NIO 3 | import Foundation 4 | 5 | let host = "YOUR_ENDPOINT" 6 | let projectId = "YOUR_PROJECT_ID" 7 | let apiKey = "YOUR_API_KEY" 8 | 9 | var databaseId = "" 10 | var collectionId = "" 11 | var fileId = "" 12 | var userId = "" 13 | var functionId = "" 14 | var deploymentId = "" 15 | var documentId = "" 16 | var bucketId = "" 17 | 18 | let client = Client() 19 | .setEndpoint(host) 20 | .setProject(projectId) 21 | .setKey(apiKey) 22 | 23 | @main 24 | struct Playground { 25 | static func main() async throws { 26 | try await createUser() 27 | try await listUsers() 28 | 29 | try await createDatabase() 30 | try await createCollection() 31 | try await listCollection() 32 | try await createDocument() 33 | try await listDocuments() 34 | try await deleteDocument() 35 | try await deleteCollection() 36 | try await deleteDatabase() 37 | 38 | try await createFunction() 39 | try await listFunctions() 40 | try await uploadDeployment() 41 | try await createSyncExecution() 42 | try await deleteFunction() 43 | 44 | try await createBucket() 45 | try await uploadFile() 46 | try await listFiles() 47 | try await deleteFile() 48 | try await deleteBucket() 49 | 50 | print("Playground ran successfully!") 51 | } 52 | } 53 | 54 | func createUser() async throws { 55 | print("Running create user API") 56 | let users = Users(client) 57 | 58 | do { 59 | let user = try await users.create( 60 | userId: ID.unique(), 61 | email: "email@example.com", 62 | password: "password" 63 | ) 64 | userId = user.id 65 | print(user.toMap()) 66 | } catch { 67 | print(error.localizedDescription) 68 | } 69 | } 70 | 71 | func listUsers() async throws { 72 | print("Running List Users API") 73 | let users = Users(client) 74 | 75 | do { 76 | let userList = try await users.list() 77 | print(userList.toMap()) 78 | } catch { 79 | print(error.localizedDescription) 80 | } 81 | } 82 | 83 | func createDatabase() async throws { 84 | let databases = Databases(client) 85 | print("Running Create Database API") 86 | 87 | do { 88 | let database = try await databases.create( 89 | databaseId: ID.unique(), 90 | name: "Movies" 91 | ) 92 | databaseId = database.id 93 | print(database.toMap()) 94 | } catch { 95 | print(error.localizedDescription) 96 | } 97 | } 98 | 99 | func deleteDatabase() async throws { 100 | let databases = Databases(client) 101 | print("Running Delete Database API") 102 | 103 | do { 104 | _ = try await databases.delete(databaseId: databaseId) 105 | print("Database deleted!") 106 | } catch { 107 | print(error.localizedDescription) 108 | } 109 | } 110 | 111 | func createCollection() async throws { 112 | let databases = Databases(client) 113 | print("Running Create Collection API") 114 | 115 | do { 116 | let collection = try await databases.createCollection( 117 | databaseId: databaseId, 118 | collectionId: ID.unique(), 119 | name: "Movies", 120 | permissions: [ 121 | Permission.read(Role.any()), 122 | Permission.create(Role.users()), 123 | Permission.update(Role.users()), 124 | Permission.delete(Role.users()) 125 | ] 126 | ) 127 | collectionId = collection.id 128 | print(collection.toMap()) 129 | 130 | let stringAttr = try await databases.createStringAttribute( 131 | databaseId: databaseId, 132 | collectionId: collectionId, 133 | key: "name", 134 | size: 60, 135 | required: true 136 | ) 137 | print(stringAttr.toMap()) 138 | 139 | let intAttr = try await databases.createIntegerAttribute( 140 | databaseId: databaseId, 141 | collectionId: collectionId, 142 | key: "releaseYear", 143 | required: true 144 | ) 145 | print(intAttr.toMap()) 146 | 147 | // let floatAttr = try await databases.createFloatAttribute( 148 | // databaseId: databaseId, 149 | // collectionId: collectionId, 150 | // key:"rating", 151 | // required: true, 152 | // min: 0.0, 153 | // max: 99.99 154 | // ) 155 | // print(floatAttr.toMap()) 156 | 157 | let boolAttr = try await databases.createBooleanAttribute( 158 | databaseId: databaseId, 159 | collectionId: collectionId, 160 | key: "kids", 161 | required: true 162 | ) 163 | print(boolAttr.toMap()) 164 | 165 | let emailAttr = try await databases.createEmailAttribute( 166 | databaseId: databaseId, 167 | collectionId: collectionId, 168 | key: "email", 169 | required: true 170 | ) 171 | print(emailAttr.toMap()) 172 | 173 | sleep(3) 174 | 175 | let index = try await databases.createIndex( 176 | databaseId: databaseId, 177 | collectionId: collectionId, 178 | key: "name_email_index", 179 | type: .fulltext, 180 | attributes: ["name", "email"] 181 | ) 182 | print(index.toMap()) 183 | } catch { 184 | print(error.localizedDescription) 185 | } 186 | } 187 | 188 | func listCollection() async throws { 189 | let databases = Databases(client) 190 | print("Running List Collection API") 191 | 192 | do { 193 | let collectionList = try await databases.listCollections( 194 | databaseId: databaseId 195 | ) 196 | print(collectionList.toMap()) 197 | } catch { 198 | print(error.localizedDescription) 199 | } 200 | } 201 | 202 | func deleteCollection() async throws { 203 | let databases = Databases(client) 204 | print("Running Delete Collection API") 205 | 206 | do { 207 | _ = try await databases.deleteCollection( 208 | databaseId: databaseId, 209 | collectionId: collectionId 210 | ) 211 | print("Collection deleted!") 212 | } catch { 213 | print(error.localizedDescription) 214 | } 215 | } 216 | 217 | func createDocument() async throws { 218 | let databases = Databases(client) 219 | print("Running Add Document API") 220 | 221 | do { 222 | let document = try await databases.createDocument( 223 | databaseId: databaseId, 224 | collectionId: collectionId, 225 | documentId: ID.unique(), 226 | data: [ 227 | "name": "The Matrix", 228 | "releaseYear": 1999, 229 | // "rating": 8.7, 230 | "kids": false, 231 | "email": "team@appwrite.io" 232 | ], 233 | permissions: [ 234 | Permission.read(Role.any()) 235 | ] 236 | ) 237 | documentId = document.id 238 | print(document.toMap()) 239 | } catch { 240 | print(error.localizedDescription) 241 | } 242 | } 243 | 244 | func listDocuments() async throws { 245 | let databases = Databases(client) 246 | print("Running List Document API") 247 | 248 | do { 249 | let documentList = try await databases.listDocuments( 250 | databaseId: databaseId, 251 | collectionId: collectionId 252 | ) 253 | print(documentList.toMap()) 254 | } catch { 255 | print(error.localizedDescription) 256 | } 257 | } 258 | 259 | func deleteDocument() async throws { 260 | let databases = Databases(client) 261 | print("Running Delete Document API") 262 | 263 | do { 264 | _ = try await databases.deleteDocument( 265 | databaseId: databaseId, 266 | collectionId: collectionId, 267 | documentId: documentId 268 | ) 269 | print("Document deleted!") 270 | } catch { 271 | print(error.localizedDescription) 272 | } 273 | } 274 | 275 | func createBucket() async throws { 276 | let storage = Storage(client) 277 | print("Running Create Bucket API") 278 | 279 | do { 280 | let bucket = try await storage.createBucket( 281 | bucketId: ID.unique(), 282 | name: "Movies", 283 | permissions: [ 284 | Permission.read(Role.any()), 285 | Permission.create(Role.users()), 286 | Permission.update(Role.users()), 287 | Permission.delete(Role.users()) 288 | ], 289 | fileSecurity: true 290 | ) 291 | bucketId = bucket.id 292 | print(bucket.toMap()) 293 | } catch { 294 | print(error.localizedDescription) 295 | } 296 | } 297 | 298 | func uploadFile() async throws { 299 | let storage = Storage(client) 300 | print("Running Upload File API") 301 | 302 | let file = InputFile.fromPath(Bundle.module.path( 303 | forResource: "nature", 304 | ofType: "jpg" 305 | )!) 306 | 307 | do { 308 | let file = try await storage.createFile( 309 | bucketId: bucketId, 310 | fileId: ID.unique(), 311 | file: file, 312 | permissions: [ 313 | Permission.read(Role.any()) 314 | ], 315 | onProgress: nil 316 | ) 317 | fileId = file.id 318 | print(file.toMap()) 319 | } catch { 320 | print(error.localizedDescription) 321 | } 322 | } 323 | 324 | func listFiles() async throws { 325 | let storage = Storage(client) 326 | print("Running List File API") 327 | 328 | do { 329 | let fileList = try await storage.listFiles( 330 | bucketId: bucketId 331 | ) 332 | print(fileList.toMap()) 333 | } catch { 334 | print(error.localizedDescription) 335 | } 336 | } 337 | 338 | func deleteFile() async throws { 339 | let storage = Storage(client) 340 | print("Running Delete File API") 341 | 342 | do { 343 | _ = try await storage.deleteFile( 344 | bucketId: bucketId, 345 | fileId: fileId 346 | ) 347 | print("File deleted!") 348 | } catch { 349 | print(error.localizedDescription) 350 | } 351 | } 352 | 353 | 354 | func deleteBucket() async throws { 355 | let storage = Storage(client) 356 | print("Running Delete Bucket API") 357 | 358 | do { 359 | _ = try await storage.deleteBucket(bucketId: bucketId) 360 | print("Bucket deleted!") 361 | } catch { 362 | print(error.localizedDescription) 363 | } 364 | } 365 | 366 | func createFunction() async throws { 367 | let functions = Functions(client) 368 | print("Running Create Function API") 369 | 370 | do { 371 | let function = try await functions.create( 372 | functionId: ID.unique(), 373 | name: "Test Function", 374 | runtime: .node160, 375 | execute: [Role.any()] 376 | ) 377 | functionId = function.id 378 | print(function.toMap()) 379 | } catch { 380 | print(error.localizedDescription) 381 | } 382 | } 383 | 384 | func uploadDeployment() async throws { 385 | let functions = Functions(client) 386 | print("Running Create Deployment API") 387 | 388 | do { 389 | let deployment = try await functions.createDeployment(functionId: functionId, code: InputFile.fromPath("PlaygroundForSwiftServer/Resources/code.tar.gz"), activate: true, entrypoint: "index.js") // add proper path here 390 | deploymentId = deployment.id 391 | print(deployment.toMap()) 392 | } catch { 393 | print(error.localizedDescription) 394 | } 395 | } 396 | 397 | func createSyncExecution() async throws { 398 | Thread.sleep(forTimeInterval: 5) 399 | let functions = Functions(client) 400 | print("Running Create Execution API") 401 | 402 | do { 403 | let execution = try await functions.createExecution(functionId: functionId, body: "", async: false, path: "/") 404 | print(execution.toMap()) 405 | } catch { 406 | print(error.localizedDescription) 407 | } 408 | } 409 | 410 | func listFunctions() async throws { 411 | let functions = Functions(client) 412 | print("Running List Functions API") 413 | 414 | do { 415 | let functionList = try await functions.list() 416 | print(functionList.toMap()) 417 | } catch { 418 | print(error.localizedDescription) 419 | } 420 | } 421 | 422 | func deleteFunction() async throws { 423 | let functions = Functions(client) 424 | print("Running Delete Function API") 425 | 426 | do { 427 | _ = try await functions.delete(functionId: functionId) 428 | print("Function deleted!") 429 | } catch { 430 | print(error.localizedDescription) 431 | } 432 | } 433 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.9' 2 | 3 | services: 4 | playground-for-swift: 5 | build: . 6 | volumes: 7 | - "./:/app" 8 | working_dir: /app 9 | command: swift run 10 | --------------------------------------------------------------------------------