Using AWS as a Back End: The Data Store API

In this tutorial, you’ll extend the Isolation Nation app from the previous tutorial, adding analytics and real-time chat functionality using AWS Pinpoint and AWS Amplify DataStore. By Tom Elliott.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 5 of 5 of this article. Click here to view the first page.

Replying to Messages

The changes needed for replying to messages are almost identical to those for sending messages. If you want to build a fully-functional chat app, then read on! You'll cover the ground quickly, since it's so similar to the coding above. But if you're more interested in learning, feel free to skip this section.

Open RepliesScreenViewModel.swift and import Amplify at the top of the file:

import Amplify

Next, add the model conversion code as an extension at the bottom:

// MARK: AWS Model to Model conversions

extension Reply {
  func asModel() -> ReplyModel {
    return ReplyModel(
      id: id,
      body: body,
      authorName: author.username,
      messageId: message?.id,
      createdAt: createdAt.foundationDate
    )
  }
}

Replace the stub implementation in fetchReplies() with a DataStore query:

Amplify.DataStore
  .query(Message.self, byId: messageID) { [self] messageResult in
  switch messageResult {
  case .failure(let error):
    logger?.
      logError("Error fetching replies for message \(messageID): \(error)")
    replyListState = .errored(error)
    return

  case .success(let message):
    self.message = message?.asModel()
    replyList = message?.replies?.sorted { $0.createdAt < $1.createdAt }
      .map({ $0.asModel() }) ?? []
    replyListState = .loaded(replyList)
  }
}

In addReply(), add an implementation to create a reply:

guard let author = userSession.loggedInUser else {
  return
}

Amplify.DataStore.query(Message.self, byId: messageID) { [self] messageResult in
  switch messageResult {
  case .failure(let error):
    logger?.logError("Error fetching message \(messageID): \(error)")
    replyListState = .errored(error)
    return

  case .success(let message):
    var newReply = Reply(
      author: author, 
      body: input.body, 
      createdAt: Temporal.DateTime.now())
    newReply.message = message
    Amplify.DataStore.save(newReply) { saveResult in
      switch saveResult {
      case .failure(let error):
        logger?.logError("Error saving reply: \(error)")
        replyListState = .errored(error)
      case .success:
        replyList.append(newReply.asModel())
        replyListState = .loaded(replyList)
        return
      }
    }
  }
}

Add the scaffolding for handling subscriptions:

var fetchReplySubscription: AnyCancellable?

private func subscriptionCompletionHandler(
  completion: Subscribers.Completion<DataStoreError>
) {
  if case .failure(let error) = completion {
    logger?.logError("Error fetching replies for message \(messageID): \(error)")
    replyListState = .errored(error)
  }
}

Finally, implement subscribe():

fetchReplySubscription = Amplify.DataStore.publisher(for: Reply.self)
  .receive(on: DispatchQueue.main)
  .sink(receiveCompletion: subscriptionCompletionHandler) { [self] changes in
    do {
      let reply = try changes.decodeModel(as: Reply.self)

      guard 
        let replyMessageID = reply.message?.id, 
        replyMessageID == messageID 
      else {
        return
      }

      replyListState = .updating(replyList)
      let isNewReply = replyList.filter { $0.id == reply.id }.isEmpty
      if isNewReply {
        replyList.append(reply.asModel())
      }
      replyListState = .loaded(replyList)
    } catch {
      logger?.logError("\(error.localizedDescription)")
      replyListState = .errored(error)
    }
  }

Whoa, that was speedy!

Build and run on both simulators. Tap the thread to see the messages, then tap a message to view the replies. Send some replies back and forth between your users. Isn't it lovely how well they get along? :]

Replies with subscriptions in action

Congratulations! You have a working chat app!

Where to Go From Here?

In this two-part tutorial series, you've created a fully-functioning chat app using AWS Amplify as a back end. Here are some links to documentation that will help you lock down the knowledge you've gained in this tutorial:

If you'd like to review the complete code, download the final project using the Download Materials button at the top or bottom of this article.

You can learn more about Amplify from the Amplify Docs. These include libraries for the web and Android. If you want to add extra functionality to your app, you could look into using S3 to save static data like user images. Or you could use the @auth GraphQL directive to add object-level or field-level authentication to your model data.

AWS contains a bewildering number of services and products to help you build your dream app, and you've only just scratched the surface. Good luck! :]