Manipulating data
Create and update
To write data to the DataStore, pass an instance of a model to Amplify.DataStore.save()
:
1do {2 try await Amplify.DataStore.save(3 Post(title: "My first post",4 status: .active,5 content: "Amplify.DataStore is awesome!")6 )7 print("Created a new post successfully")8} catch let error as DataStoreError {9 print("Error creating post - \(error)")10} catch {11 print("Unexpected error \(error)")12}
The save
method creates a new record, or in the event that one already exists in the local store, it updates the record.
1var existingPost: Post = /* get an existing post */2existingPost.title = "[updated] My first post"3
4do {5 try await Amplify.DataStore.save(existingPost)6 print("Updated the existing post")7} catch let error as DataStoreError {8 print("Error updating post - \(error)")9} catch {10 print("Unexpected error \(error)")11}
1import SwiftUI2import Amplify3import Combine4
5/*6 Example showing how to observe the model and keep the state updated before performing a save. This uses the7 `@Published` property for views to observe and re-render changes to the model.8 */9class PostViewModel: ObservableObject {10 @Published var post: Post?11 var subscription: AnyCancellable?12
13 init() {14 }15
16 func observe(postId: String) {17 self.subscription = Amplify.Publisher.create(18 Amplify.DataStore.observeQuery(19 for: Post.self,20 where: Post.keys.id == postId21 )22 )23 .sink { completion in24 print("Completion event: \(completion)")25 } receiveValue: { snapshot in26 guard let post = snapshot.items.first else {27 return28 }29 DispatchQueue.main.async {30 self.post = post31 }32 }33 }34
35 func updateTitle(_ title: String) async {36 guard var post = post else {37 return38 }39 post.title = title40 do {41 let updatedPost = try await Amplify.DataStore.save(post)42 print("Updated post successfully: \(updatedPost)")43 } catch let error as DataStoreError {44 print("Failed to update post: \(error)")45 } catch {46 print("Unexpected error \(error)")47 }48 }49}50struct PostView: View {51 @StateObject var vm = PostViewModel()52 @State private var title = ""53 let postId: String54
55 init(postId: String) {56 self.postId = postId57 }58
59 var body: some View {60 VStack {61 Text("Post's current title: \(vm.post?.title ?? "")")62 TextField("Enter new title", text: $title)63 Button("Click to update the title to '\(title)'") {64 Task { await vm.updateTitle(title) }65 }66 }.onAppear(perform: {67 Task { await vm.observe(postId: postId) }68 })69 }70}
Delete
To delete an item, simply pass in an instance.
1do {2 try await Amplify.DataStore.delete(post)3 print("Post deleted!")4} catch let error as DataStoreError {5 print("Error deleting post - \(error)")6} catch {7 print("Unexpected error \(error)")8}
A delete can also be achieved by a type
and its id
.
1do {2 try await Amplify.DataStore.delete(Post.self, withId: "123")3 print("Post deleted!")4} catch let error as DataStoreError {5 print("Error deleting post - \(error)")6} catch {7 print("Unexpected error \(error)")8}
Query Data
Queries are performed against the local store. When cloud synchronization is enabled, the local store is updated in the background by the DataStore Sync Engine.
For more advanced filtering, such as matching arbitrary field values on an object, you can supply a query predicate.
1do {2 let result = try await Amplify.DataStore.query(Post.self)3 // result will be of type [Post]4 print("Posts: \(result)")5} catch let error as DataStoreError {6 print("Error on query() for type Post - \(error)")7} catch {8 print("Unexpected error \(error)")9}
Query by identifier
Query has an alternative signature that allows to fetch a single item by its id
:
1do {2 let result = try await Amplify.DataStore.query(Post.self, byId: "123")3 // result will be a single object of type Post?4 print("Post: \(result)")5} catch {6 print("Error on query() for type Post - \(error)")7}
Predicates
Predicates are filters that can be used to match items in the DataStore. When applied to a query(), they constrain the returned results. When applied to a save(), they act as a pre-requisite for updating the data. You can match against fields in your schema by using the following predicates:
Strings: eq | ne | le | lt | ge | gt | contains | notContains | beginsWith | between
Numbers: eq | ne | le | lt | ge | gt | between
Lists: contains | notContains
For example if you wanted a list of all Post
Models that have a rating
greater than 4:
1let p = Post.keys2do {3 let result = try await Amplify.DataStore.query(Post.self, where: p.rating > 4)4 print("Posts: \(result)")5} catch let error as DataStoreError {6 print("Error listing posts - \(error)")7} catch {8 print("Unexpected error \(error)")9}
Multiple conditions can also be used, like the ones defined in GraphQL Transform condition statements. For example, fetch all posts that have a rating greater than 4
and are ACTIVE
:
1let p = Post.keys2do {3 let result = try await Amplify.DataStore.query(4 Post.self,5 where: p.rating > 4 && p.status == PostStatus.active6 )7 // result of type [Post]8 print("Published posts with rating greater than 4: \(result)")9} catch let error as DataStoreError {10 print("Error listing posts - \(error)")11} catch {12 print("Unexpected error \(error)")13}
You can also write this in a compositional function manner by replacing the operators with their equivalent predicate statements such as .gt
, .and
, etc:
1let p = Post.keys2try await Amplify.DataStore.query(3 Post.self,4 where: p.rating.gt(4).and(p.status.eq(PostStatus.active))5)
Alternatively, the or
logical operator can also be used:
1let p = Post.keys2do {3 let result = try await Amplify.DataStore.query(4 Post.self,5 where: p.rating == nil || p.status == PostStatus.active6 )7 // result of type [Post]8 print("Posts in draft or without rating: \(result)")9} catch let error as DataStoreError {10 print("Error listing posts - \(error)")11} catch {12 print("Unexpected error \(error)")13}
Sort
Query results can also be sorted by one or more fields.
For example, to sort all Post
objects by rating
in ascending order:
1do {2 let result = try await Amplify.DataStore.query(3 Post.self,4 sort: .ascending(Post.keys.rating))5 print("Posts: \(result)")6} catch let error as DataStoreError {7 print("Error listing posts - \(error)")8} catch {9 print("Unexpected error \(error)")10}
To get all Post
objects sorted first by rating
in ascending order, and then by title
in descending order:
1do {2 let result = try await Amplify.DataStore.query(3 Post.self,4 sort: .by(5 .ascending(Post.keys.rating),6 .descending(Post.keys.title)7 )8 )9 print("Posts: \(result)")10} catch let error as DataStoreError {11 print("Failed with error \(error)")12} catch {13 print("Error listing posts - \(error)")14}
Pagination
Query results can also be paginated by passing in a page
number (starting at 0) and an optional limit
(defaults to 100). This will return a list of the first 100 items:
1let posts = try await Amplify.DataStore.query(2 Post.self,3 paginate: .page(0, limit: 100))
The paginate
arguments takes an object of type QueryPaginationInput
. That object can be created with the following factory functions:
.page(_ page: UInt, limit: UInt)
: the page number (starting at0
) and the page size, defined bylimit
(defaults to100
).firstPage
: an idiomatic shortcut to.page(0, limit: 100)
.firstResult
: an idiomatic shortcut to.page(0, limit: 1)