Add User Chat to an iOS Application With Magnet

More and more mobile applications offer the ability to send messages to other users using the application. In many games, for example, you can chat with fellow gamers while chat applications allow you to talk to whomever you like. The possibilities are endless. Adding user chat to an application can be an important feature of your application.

Building a solution to enable users to chat with one another is a daunting task many developers prefer to sidestep. Fortunately, there's no need to reinvent the wheel. Magnet Message is an open source mobile messaging framework that is ideal for this use case. They offer iOS and Android support as well as a REST API. Getting started with Magnet Message is quick and easy thanks to their sandbox environment.

In this tutorial, you will learn how to integrate Magnet Message in an iOS application using Swift 2. You will learn how easy it is to set up Magnet Message in a project and send messages to other users of your application. Let's start by creating an account and setting up the sandbox.

Prerequisites

Even though the Magnet library for iOS is written in Objective-C, in this tutorial I'll be working with Xcode 7.1 and Swift 2.1. You can download Xcode 7.1 from Apple's developer website or from the App Store.

1. Creating a Magnet Account

Before we can start integrating Magnet into a mobile application, we need a Magnet account. A Magnet account gives you access to a sandbox environment for development as well as a number of sample applications to learn from. To follow along, navigate to Magnet's Developer website, click Try It at the top, and sign up with an email address.

Creating a Free Magnet Account

2. Creating a Sandbox Application

Your free Magnet account gives you access to a range of resources, including a sandbox you can use for development. Go to developer.magnet.com and go to the sandbox. You immediately notice that the sandbox already contains three applications. Each of these applications corresponds with one of the sample applications.

Sandbox Applications

For this tutorial, we'll create a new application. Click the green plus button in the top right to create a new application and name it MessageMe.

Creating a New Application

When you click Save, the sandbox automatically opens the application and shows you a Getting Started guide. Click the Apple logo at the top to show the instructions for iOS.

Getting Started Guide

You can skip the instructions, because I'll be guiding you through the setup process. There is one resource we need to get started, a property containing the configuration for the Magnet application. In the second step of the Build the App section, you can see a link to download MagnetMax.plist. Click the link to download the property list to your machine. We'll be needing it in a few moments.

If you open MagnetMax.plist, you'll see that it contains three key-value pairs, BaseURLClientIDClientSecret. This information will be used by Magnet's library to talk to the Magnet Message backend.

3. Setting Up the Project

Launch Xcode 7.1 and choose the Single View Application template from the list of iOS > Application templates.

Choosing the Single View Application Template

Name the application MessageMe, set Language to Swift, Devices to iPhone, and leave the checkboxes at the bottom unchecked. Tell Xcode where you'd like to save the project and click Create.

Configuring the Project

4. Setting Up CocoaPods

As I mentioned earlier, Magnet has developed an open source library for interacting with the Magnet Message backend. The easiest way to integrate the library in an Xcode project is through CocoaPods. Close the project you just created, open a Terminal window, and navigate to the root of the project. Create a Podfile for your project by executing the pod init command from the command line.

The Podfile lives at the root of your project. Open it in a text editor and replace its contents with the following.

From the command line, execute the pod install command to install the dependencies specified in the Podfile. This will install the MagnetMax library that Magnet provides for interacting with the Magnet Message API. It will also create a workspace that we'll use from now on. After completing the installation process, open MessageMe.xcworkspace at the root of your project.

5. Setting Up Magnet

Step 1: Adding MagnetMax.plist

Setting up and configuring Magnet is very easy to do. Earlier in this tutorial, you downloaded MagnetMax.plist from Magnet's sandbox. Add this property list to the MessageMe project in the MessageMe workspace. Because  MagnetMax.plist contains sensitive data, I have excluded it from source control so you won't find it in the source files of this tutorial.

Adding MagnetMaxplist

Step 2: Import MagnetMax Framework

Open AppDelegate.swift and add an import statement for the MagnetMax framework we installed through CocoaPods.

Step 3: Setting Up Magnet

In application(_:didFinishLaunchingWithOptions:), call setupWithConfiguration(_:) on the MagnetMax class, passing in the name of the configuration in MagnetMax.plist that we want to work with.

Step 4: Build and Run

Build and run the target on a physical device or in the simulator to make sure everything is set up correctly. Make sure you've correctly followed the above steps if you run into problems.

6. Registering a User

Before a user can send messages to another user, she needs to create an account in our application. Magnet's account system is simple and transparent, and registering a user is straightforward to implement. In this section, we'll create a simple form to enable users to create an account or, if they already have an account, sign in to their account.

Step 1: Creating Classes

Before we create the user interface, we need to create two UIViewController subclasses, MessageViewController and SignInViewController. The first view controller will be in charge of sending messages. The second view controller will manage user registration as well as the sign in process.

Creating a View Controller Subclass

Step 2: Creating User Interface

Open Main.storyboard, select the view controller that's already present, and choose Embed In > Navigation Controller from Xcode's Editor menu. Open the Object Library on the right and add a bar button item to the view controller's navigation bar. With the bar button item selected, set System Item to Compose in the Attributes Inspector.

Add Compose Bar Button Item

Drag a view controller from the Object Library and set its class to MessageViewController in the Identity Inspector. Create a segue from the compose bar button item to the message view controller, setting Kind to Present Modally. Add a navigation bar at the top of the message view controller and add a bar button item on the left. In the Attributes Inspector, set System Item to Done.

Add two regular buttons to the message view controller and place them in a view. This makes it easier to hide both buttons when necessary. Set the titles of the button to Sign In and Create Account respectively.

Add Sign In and Create Account Buttons

Finally, add another view controller and set its class to SignInViewController in the Identity Inspector. Add a navigation bar at the top and add a bar button item on the left. Set System Item to Cancel in the Attributes Inspector. Add two text fields to the view controller, one for the user's username and one for the user's password. Don't forget to check Secure Text Entry in the Attributes Inspector for the password text field. Last but not least, add a button below the text fields and set its title to Sign In.

Create a segue from the Sign In button of the message view controller to the sign in view controller and set the segue's identifier to SegueSignIn. Repeat this step for the Create Account button and set the segue's identifier to SegueCreateAccount. Both segues should be of kind Show.

Adding the Sign In View Controller

Step 3: Creating Outlets

Open MessageViewController.swift and declare an outlet for the view in which the Sign In and Create Account buttons are located. Note that I've also created an action for the Done button in the navigation bar. Don't forget to connect the outlet and the action in the storyboard. For ease of use, I've also declared two constants for the segue identifiers.

Open SignInViewController.swift and declare three outlets and a helper variable as shown below. The helper variable will help us determine whether the user tapped the Sign In or the Create Account button. I've also created actions for the Sign In and Cancel buttons. Wire everything up in the storyboard before continuing.

Step 4: Delegate Protocol

When the user is successfully signed in, the message view controller needs to be notified. We can do this by creating a delegate protocol. At the top of SignInViewController.swift, I've added an import statement for the MagnetMax library and declared the SignInViewControllerDelegate protocol. The protocol contains one method, controller(_:didSignInWithUser:), which we'll invoke when the user is successfully signed in. We also need to declare a delegate property of type SignInViewControllerDelegate? to keep a reference to the delegate of the sign in view controller.

Step 5: Signing In

The first thing we need to do is implement prepareForSegue(_:sender:) in the MessageViewController class. In this method, we check which button the user tapped and configure the SignInViewController instance accordingly by setting its hasAccount property.

Note that we also set the delegate property of the sign in view controller. This means that the MessageViewController class needs to conform to the SignInViewControllerDelegate protocol as shown below. The implementation of the controller(_:didSignInWithUser:) method is empty for now. Don't forget to add an import statement for the MagnetMax library to avoid compiler warnings.

Let's focus now on the interesting part, the actions of the SignInViewController class. Before we implement the signIn(_:) and cancel(_:) actions, I'd like to implement the viewDidLoad() method to update the title of the Sign In button based on the value of the hasAccount property.

The implementation of the cancel(_:) action is easy enough as you can see below.

The signIn(_:) action is more interesting. In this action, we make sure the user has entered valid data, a username and a password, and then invoke a helper method based on the value of the hasAccount property.

The showAlertWithTitle(_:message:) method is a private convenience method for displaying an alert to the user.

The createAccountWithCredential(_:) method is another private helper method for registering the user. We start by creating an instance of the MMUser class. This class represents a user for the Magnet Message backend. We register the user by invoking registerWithCredential(_:success:failure:), passing in an NSURLCredential object and two closures.

If registration is successful, we immediately sign the user in by invoking signInWithCredential(_:). If something went wrong, we inspect the error that was passed to us. If the error's code is equal to 409, we know the user accidentally created an account with existing credentials. It's then acceptable to sign the user in instead of showing them an alert. If registrations fails, we show the user an alert.

The signInWithCredential(_:) method isn't too complicated. If logging in is successful, we initialize the message module by invoking initModule(_:success:failure:) on MagnetMax. We store the username in the user defaults database and notify the delegate, passing it the MMUser instance. We also dismiss the sign in view controller. If signing the user in is unsuccessful, we show the user an alert.

Run the application on a physical device or in the simulator to test what we've got so far. Note that the username cannot contain spaces. You should now be able to register as a user.

7. Creating the Form

Step 1: Creating User Interface

When the user is signed in, she should be able to send messages to other people using the application. Let's start by creating a form for composing the message. Before we revisit Main.storyboard, we're going to declare a few outlets and an action.

We start by declaring an outlet named messageView. We show this view when the user is signed in. At the same time, we hide buttonsView, which contains the Sign In and Create Account buttons.

The message view will contain a text field to enter recipients, a text field for the message itself, a switch to include the location of the user in the message, and a button to send the message. Also note that the MessageViewController class conforms to the UITextFieldDelegate protocol. I'll explain why that is later in this tutorial.

I've also added two actions. The sendMessage(_:) action will be triggered by tapping the Send Message button. The locationSwitchDidChange(_:) action will be invoked when the user flips the switch on.

The user interface of the message view controller changes quite a bit as you can see below. There are a few things to point out.

  • The message view controller should be set as the delegate of the recipients text field, the top text field.
  • The location switch should be set to off in the Attributes Inspector and the locationSwitchDidChange(_:) action needs to be wired up to the Value Changed event.
  • Wire the Touch Up Inside event to the sendMessage(_:) action we declared a moment ago in the MessageViewController class.
Creating the Form to Send Messages

Step 2: Showing the Form

We only show the form when the user is signed in. We can easily do this through property observers. Start by creating a property, user, of type MMXUser? in the message view controller and add a didSet property observer to it. In the didSet property observer we invoke a helper method, updateView().

The implementation of the updateView() method is simple. We also implement a setupView() method, which we invoke in the viewDidLoad() method of the MessageViewController class.

The only thing left to make this work is update the user property of the message view controller in the controller(_:didSignInWithUser:) delegate method.

Run the application, sign in to your account, and make sure the form is shown in the message view controller.

8. Fetching the User's Location

Fetching the user's location is pretty simple, especially in iOS 9. Since iOS 9, the CLLocationManager class declares a method, requestLocation(), to ask the operating system for the location of the device. Let's see how this works.

Step 1: Updating Info.plist

To make this work, we first need to add an entry to the target's Info.plist. What we need to add is a message informing the user why we need access to their location. Open the target's Info.plist in Xcode and add a new row with key NSLocationWhenInUseUsageDescription and value MessageMe would like to use your current location.

Step 2: Declaring Properties

We also need to declare two properties in the MessageViewController class, a property for the location manager and a property for the current location. Both are optionals.

Note that we also add an import statement for the CoreLocation framework and we conform the MessageViewController class to the CLLocationManagerDelegate protocol.

Step 3: Requesting Permission

We then update the locationSwitchDidChange(_:) method, which is triggered when the value of the location switch changes. We invoke requestLocation() when the switch is on.

The requestLocation() method is a private helper method in which we set up the location manager. Before we ask the operating system for the location of the device, we verify that we have permission to do so. If we don't, we first ask for the user's permission by invoking requestWhenInUseAuthorization().

By requesting the user's permission, the user will be prompted for permission. After the user makes its decision, allowing or denying permission, a delegate method is invoked, locationManager(_:didChangeAuthorizationStatus:). In this method, we request the location if we are allowed to do so.

When requesting the location of the device, one of two delegate methods of the CLLocationManagerDelegate protocol is invoked:

  • locationManager(_:didUpdateLocations:) is invoked if the operating system has located the device.
  • locationManager(_:didFailWithError:) is invoked if locating the device failed for some reason.

In locationManager(_:didUpdateLocations:), we store the location in the currentLocation property.

9. Selecting Recipients

Step 1: Creating RecipientsViewController Class

The final piece of the puzzle is selecting recipients to send the message to. Create a new class, RecipientsViewController, and add an import statement for the MagnetMax library. At the top, declare a protocol, RecipientsViewControllerDelegate, to inform the delegate which recipients the user selected. The delegate will be the message view controller. We also need a property to keep a reference to the delegate.

Before we create the user interface of the view controller, declare an outlet for a table view and implement two actions as shown below.

Step 2: Creating User Interface

The user interface is pretty straightforward. Add a navigation bar at the top and add two bar button items to it, Cancel on the left and Done on the right. Add a table view to the view controller, set its dataSource and delegate outlets, and add a search bar at the top of the table view. It's important that the view controller is the delegate of the search bar.

Finally, create a manual segue from the message view controller to the recipients view controller. Set the segue's identifier to SegueRecipients and Kind to Show in the Attributes Inspector.

Creating the User Interface of the Recipients View Controller

Step 3: Implementing Delegate Protocols

Open RecipientsViewController.swift and conform the RecipientsViewController controller to the UITableViewDataSource, UITableViewDelegate, and UISearchBarDelegate protocols.

Declare a constant for the reuse identifier of the table view cells and a property, users, that will act as the data source of the table view. The users property is of type [MMUser].

In the viewDidLoad() method, register the UITableViewCell class for cell reuse.

The implementation of the UITableViewDataSource protocol is also straightforward. We display the username of each user that we're going to fetch from the Magnet Message backend.

More interesting is the implementation of the searchBar(_:textDidChange:) method. In this method, we invoke searchUsers(_:limit:offset:sort:success:failure:) on the MMUser class. We pass in the query, how many results we'd like to receive, an offset, a key to sort the results by, and two closures, one for success and one for failure.

Step 4: Notifying the Delegate

When the user has made its selection and taps the Done button, we forward the selection of recipients to the delegate, the message view controller.

The only thing left to do is configuring the recipients view controller when the SegueRecipients segue is performed. We do this in the MessageViewController class. Update the prepareForSegue(_:sender:) as shown below.

This also means that we need to declare the SegueRecipients constant and conform the MessageViewController class to the RecipientsViewControllerDelegate protocol.

Step 5: Storing the Recipients

In the MessageViewController class, we temporarily store the recipients in a property, recipients, of type [MMUser].

In the controller(_:didSelectRecipients:) method of the RecipientsViewControllerDelegate protocol, we update the recipients property and the recipients text field. We could use a property observer to accomplish the same thing.

Finally, we show the recipients view controller when the user taps the recipients text field. That's why the message view controller needs to be the delegate of the recipients text field.

10. Sending a Message

It's finally time to send the message. You'll be surprised by how simple this is with the MagnetMax library. We send a message in the sendMessage(_:) action.

We first make sure that the required fields are filled out by the user. Location is optional. We send a message by creating an instance of the MMXMessage class, passing in the recipients and what we'd like to send. Note that Magnet Message supports rich messaging so you can add text as well as media.

To send the message, we invoke sendWithSuccess(_:failure:) on the MMXMessage object, passing in a success and failure closure. This message returns immediately and hands us the identifier of the message. We don't need that for this example. The implementation of showAlertWithTitle(_:message:) is similar to what we've seen before.

Note that you can only successfully send a message to another user if they are using the application. To test what we've built so far, you need to sign in on two devices or use the simulator.

11. Receiving Messages

Step 1: Start Observing

To receive messages from other users, we need to take care of two things in the MessageViewController. In the viewDidLoad(), we tell the MagnetMax library that we want to receive messages by invoking the start() class method.

In the viewDidLoad() method, we also add the view controller as an observer of notifications with name MMXDidReceiveMessageNotification. The MagnetMax library posts a notification with this name whenever it receives a message from the Magnet Message backend.

Step 2: Handling Messages

In didReceiveMessage(_:), we perform a number of checks using guard and then send a delivery confirmation to the Magnet Message backend to tell it that we received the message.

Conclusion

Even though this tutorial was fairly lengthy, I'm sure you agree that integrating Magnet Message didn't take much effort from our part. The heavy lifting is done by the MagnetMax library. Sending and receiving messages is only part of what the Magnet platform can do. Magnet provides support for media, public and private channels, and push notifications. You can read more about these features on Magnet's website.

Tags:

Comments

Related Articles