iOS Chat Components Release: Create a Chat App for iOS

Chandler_iOS_Comonents_Blog_Header_Image_1200x630.jpg

We're excited to announce the release of our iOS Chat Components. We've created ready-to use-components for iOS developers that make it simple to add essential chat features to your app while reducing your time to market. With our iOS Chat Components you're free to focus on building what you actually care about. Sign up to learn more about our iOS Chat Components release using the form below. Then, read on to learn how you use our new iOS components in an existing open-source iOS app.

PubNub’s iOS Components for Chat enable mobile developers to create rich and flexible chat experiences without the opportunity cost of the time spent building chat features.

Don’t reinvent chat UI components and build the infrastructure yourself when PubNub has already done it for you - you could be focused on other parts of your application! No need to go through the complexity of designing the architecture of a realtime network. Instead, use our components, predefined repositories, and view models to create custom apps for a wide range of use cases.

Our set of iOS Chat Components are packed with the features you’d expect from any chat experience:

Our iOS Chat Components are modifiable to your requirements so you’re free to create chat for various use cases all with different functionalities and customizable looks.

Build a telemedicine chat app, multiplayer chat lobby, live support chat center, live event chat lobby, or another style of chat with minimal effort. Build a powerful and modular mobile chat app using these components as a starting point. Best of all: There’s no need to deal with server code at all. PubNub powers all of the messaging features: message delivery in real-time, metadata, presence, and other chat-related data to facilitate the actions within the chat UI components.

This tutorial series demonstrates how to take PubNub iOS Chat Components and integrate them into any iOS app. All the steps you need to use the components are included in this post. 

How can you create a chat app for iOS with PubNub?

Before using PubNub iOS Chat Components, you should know why PubNub is the best choice for powering them. PubNub is used for real-time message delivery, metadata, presence, and other chat related data to facilitate the actions within the chat UI components.

PubNub is a fantastic backend API for all types of real-time chat applications because it offers a lot of functionality that never have to build yourself. Some essential advantages of PubNub are:

PubNub iOS Chat Components have PubNub services fully incorporated and are ready to use in an existing iOS App. It’s recommended that you’re familiar with Swift and iOS development if you wish to modify the iOS components for use in your application. However, you can absolutely follow along, explore, and use these iOS Chat Components without prior experience.

Available mobile chat components to create a chat app for iOS

The following chat components are included with PubNub iOS Chat Components:

Now that you have an understanding of the available components for iOS, let’s explore how you set up your chat API to try them out.

Setting up a chat API for Mobile Chat Components 

You’ll need to set up a PubNub account to use PubNub iOS Chat Components. The end result will be two API keys that you’ll use in your mobile chat application. A PubNub account and API keys are always free (perfect for startups).

  1. You’ll first need to sign up for a free PubNub account.

  2. Sign in to your PubNub Dashboard.

  3. Go to Apps.

  4. Click Create New App.

  5. Give your app a name.

  6. Click Create.

  7. Click your new app to open its settings.

  8. When you create a new app the first set of keys is generated automatically. However, a single app can have as many keysets as you like. PubNub recommends that you create separate keysets for production and test environments.

  9. Select a keyset. 

  10. Enable the Channel Presence feature for your keyset. This will be used to show how many users are active.

  11. Enable the Storage and Playback feature for your keyset. If you don’t plan to need message persistence then you can leave this off.

  12. Enable any other functionality you plan to use (if desired).

  13. Save the changes.

  14. Copy the Publish and Subscribe keys for the next steps.

How to use iOS Chat Components in your application

Now that you have your PubNub API keys you can begin building with iOS Chat Components. Get started by downloading an existing weather app that we will add chat components to.

Getting Started with iOS Chat Components

Download this weather app at this commit. This was the last commit of the sample app before we added iOS Chat Components. We picked this app because *it wasn't created by PubNub* and we want to show you how you can use our iOS components in real apps like this one. We’ll start from there so you can add them yourself. If you clone the repo at the latest commit you’ll get the completed app.

Download the .zip of the commit and extract the folder. Or clone the repo and switch to this commit e9232c7. You can also use the 'Open with Xcode' button in GitHub.

git clone https://github.com/PubNubDevelopers/Swift-Weather-Chat/
cd Weather
git checkout -b weather_tutorial e9232c7

Open the file ‘Sun.xcodeproj’ in Xcode.

Open ‘Supporting Files > Constants.swift’. We need to configure the Weather API and add our PubNub configuration here.

Go to https://home.openweathermap.org/users/sign_up to get an API key for fetching the weather. The account is free and you can build this app without a paid plan. 

Paste your API key in the quotations beside let “apiKey =”

let apiKey = "WEATHER-KEY-HERE"

In the same file add the following at the bottom:

let PUBNUB_PUBLISH_KEY = "pub-c-key" 
let PUBNUB_SUBSCRIBE_KEY = "sub-c-key"

Replace “pub-c-key” and “sub-c-key” with your PubNub API keys.

Install iOS Chat Components

Inside the Xcode project, select File > Add Packages..., and enter the iOS Components repository URL:

https://github.com/pubnub/chat-components-ios

After the package details load, click the Add Package button in the lower right corner.

For any additional questions about adding packages in Xcode, refer to Apple documentation.

Building Chat Features with iOS Chat Components in Xcode

Open ‘View Controllers > ViewController.swift’. For the rest of this tutorial we’ll be (mostly) focusing on this file. All of our chat functionality will live here. 

Under “import CoreLocation” add the following to import PubNub, PubNubChat, and PubNubChatComponents:

import PubNub
import PubNubChat
import PubNubChatComponents

Directly under “class ViewController: UIViewController” add the following to create a ChatProvider instance, declare the variable we’ll use for the current channel, and a UIView for our chat. After that we’ll create our PubNub configuration and a function to generate a short random string to use as the UUID and username of our chat users. You must create a UUID for each unique user. All of our users in this app will be anonymous ids. 

var chatProvider: PubNubChatProvider?
    var channelId = "San Francisco" // Default channel
    var chatView: UIView!
    
    // Create PubNub Configuration
    lazy var pubnubConfiguration = {
      return PubNubConfiguration(
        publishKey: PUBNUB_PUBLISH_KEY, // see Constants.swift to set PubNub API keys
        subscribeKey: PUBNUB_SUBSCRIBE_KEY,
        uuid: randomString(length: 6)
      )
    }()
    
    func randomString(length: Int) -> String { // Used to create a random username/uuid for chat
      let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
      return String((0..<length).map{ _ in letters.randomElement()! })
    }

Now we can create a function for handling a button press that’ll show our chat UIView. We’ll create a button to link this function to in a later step. Directly after that we’ll also create a function to load the data into the chatProvider for our chat app and handle subscribing to the current channel.

@IBAction func startChat(_ sender: UIButton) { // Show chat for current city / location
        chatView=UIView(frame: self.view.bounds)

        // PubNub.log.levels = [.all]
        // PubNub.log.writers = [ConsoleLogWriter()]
        
        guard let messageListViewModel = try! chatProvider?.messageListComponentViewModel(pubnubChannelId: channelId) else {
           preconditionFailure("Could not create initial view models")
        }
        
        let navigation = UINavigationController()
        navigation.viewControllers = [messageListViewModel.configuredComponentView()]
    
        self.show(navigation, sender: nil)
    }
    
    func preloadData(_ chatProvider: PubNubChatProvider) {
        // Create a user object with UUID
        let user = PubNubChatUser(
          id: chatProvider.pubnubConfig.uuid,
          name: chatProvider.pubnubConfig.uuid,
          avatarURL: URL(string: "https://picsum.photos/seed/\(chatProvider.pubnubConfig.uuid)/200")
        )
        
        // Create a channel object
        let channel = PubNubChatChannel(
          id: channelId,
          name: channelId,
          type: "direct",
          avatarURL: URL(string: "https://picsum.photos/seed/\(channelId)/200")
        )
        
        // Create a membership between the User and the Channel for subscription purposes
        let membership = PubNubChatMember(channel: channel, member: user)
        
        // Subscribe to the channel
        chatProvider.pubnubProvider.subscribe(.init(channels: [channelId], withPresence: true))
        
        // Fill database with the user, channel, and memberships data
        chatProvider.dataProvider.load(members: [membership])
      }

At the bottom of  “override func viewDidLoad()” add the following code to create a new chat provider and call the function to load data:

        // Create a new ChatProvider
        let provider = PubNubChatProvider(
        pubnubConfiguration: pubnubConfiguration
        )
        // Preload Data
        preloadData(provider)
        // Assign for future use
        chatProvider = provider

We also need to update the provider with new channels when the user changes location. In the function “updateInterfaceWith()” add the following inside “DispatchQueue.main.async”:

            self.channelId = weather.cityName
            if let provider = self.chatProvider {
                self.preloadData(provider) // Update chat provider with new channel data
              }

Your entire “ViewController.swift” file should now look like this:


import UIKit
import CoreLocation
import PubNub
import PubNubChat
import PubNubChatComponents

class ViewController: UIViewController {
    var chatProvider: PubNubChatProvider?
    var channelId = "San Francisco" // Default channel
    var chatView: UIView!
    
    // Create PubNub Configuration
    lazy var pubnubConfiguration = {
      return PubNubConfiguration(
        publishKey: PUBNUB_PUBLISH_KEY, // see Constants.swift to set PubNub API keys
        subscribeKey: PUBNUB_SUBSCRIBE_KEY,
        uuid: randomString(length: 6)
      )
    }()
    
    func randomString(length: Int) -> String { // Used to create a random username/uuid for chat
      let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
      return String((0..<length).map{ _ in letters.randomElement()! })
    }
    
    @IBOutlet weak var weatherIconImageView: UIImageView!
    @IBOutlet weak var cityLabel: UILabel!
    @IBOutlet weak var temperatureLabel: UILabel!
    @IBOutlet weak var feelsLikeTemperatureLabel: UILabel!
    
    var networkWeatherManager = NetworkWeatherManager()
    lazy var locationManager: CLLocationManager = {
        let lm = CLLocationManager()
        lm.delegate = self
        lm.desiredAccuracy = kCLLocationAccuracyKilometer
        lm.requestWhenInUseAuthorization()
        return lm
    }()
   
    @IBAction func searchPressed(_ sender: UIButton) {
        self.presentSearchAlertController(withTitle: "Enter city name", message: nil, style: .alert) { [unowned self] city in
            self.networkWeatherManager.fetchCurrentWeather(forRequestType: .cityName(city: city))
        }
    }
    
    @IBAction func startChat(_ sender: UIButton) { // Show chat for current city / location
        chatView=UIView(frame: self.view.bounds)

        // PubNub.log.levels = [.all]
        // PubNub.log.writers = [ConsoleLogWriter()]
        
        guard let messageListViewModel = try! chatProvider?.messageListComponentViewModel(pubnubChannelId: channelId) else {
           preconditionFailure("Could not create intial view models")
        }
        
        let navigation = UINavigationController()
        navigation.viewControllers = [messageListViewModel.configuredComponentView()]
    
        self.show(navigation, sender: nil)
    }
    
    func preloadData(_ chatProvider: PubNubChatProvider) {
        // Create a user object with UUID
        let user = PubNubChatUser(
          id: chatProvider.pubnubConfig.uuid,
          name: chatProvider.pubnubConfig.uuid,
          avatarURL: URL(string: "https://picsum.photos/seed/\(chatProvider.pubnubConfig.uuid)/200")
        )
        
        // Create a channel object
        let channel = PubNubChatChannel(
          id: channelId,
          name: channelId,
          type: "direct",
          avatarURL: URL(string: "https://picsum.photos/seed/\(channelId)/200")
        )
        
        // Create a membership between the User and the Channel for subscription purposes
        let membership = PubNubChatMember(channel: channel, member: user)
        
        // Subscribe to the channel
        chatProvider.pubnubProvider.subscribe(.init(channels: [channelId], withPresence: true))
        
        // Fill database with the user, channel, and memberships data
        chatProvider.dataProvider.load(members: [membership])
      }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        networkWeatherManager.onCompletion = { [weak self] currentWeather in
            guard let self = self else { return }
            self.updateInterfaceWith(weather: currentWeather)
        }
        
        if CLLocationManager.locationServicesEnabled() {
            locationManager.requestLocation()
        }
        
        // Create a new ChatProvider
        let provider = PubNubChatProvider(
        pubnubConfiguration: pubnubConfiguration
        )
        // Preload Data
        preloadData(provider)
        // Assign for future use
        chatProvider = provider

    }
    
    func updateInterfaceWith(weather: CurrentWeather) {
        DispatchQueue.main.async {
            self.channelId = weather.cityName
            if let provider = self.chatProvider {
                self.preloadData(provider) // Update chat provider with new channel data
              }
            self.cityLabel.text = weather.cityName
            self.temperatureLabel.text = weather.temperatureString
            self.feelsLikeTemperatureLabel.text = weather.feelsLikeTemperatureString
            self.weatherIconImageView.image = UIImage(systemName: weather.systemIconNameString)
        }
    }
}

// MARK: - CLLocationManagerDelegate

extension ViewController: CLLocationManagerDelegate {
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let location = locations.last else { return }
        let latitude = location.coordinate.latitude
        let longitude = location.coordinate.longitude
        
        networkWeatherManager.fetchCurrentWeather(forRequestType: .coordinate(latitude: latitude, longitude: longitude))
    }
    
    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        print(error.localizedDescription)
    }
}

The next step is to create a button to show the chat UIView and link it to the “startChat” function. 

Open “Main.storyboard”. Duplicate the search button in the view (or make a new button), change the icon to “paperplane.circle”, and position the button where you want to. You should now have something that looks like this:

Switch to the Connections inspection (last tab on the right) and remove the link on “Touch Up Inside” if you duplicated the search button. Add a second editor and open ‘View Controllers > ViewController.swift’. Create a new link between “Touch Up Inside” and the “startChat” function by dragging from the empty circle beside “Touch Up Inside” to the “startChat” function.

[Optional step] Uncomment the following lines in “ViewController.swift” to see PubNub publish events, subscribes, and messages moving through the network:

// PubNub.log.levels = [.all]
// PubNub.log.writers = [ConsoleLogWriter()]

Start the app in the simulator. Allow location or search for a location. Click the chat icon to open the chat window and start chatting!

What’s Next with iOS Chat Components

In this tutorial we took a look at how PubNub iOS Chat Components can be implemented in an existing app and we configured and started customizing the iOS Chat Components.

The complete Weather-Chat app with PubNub iOS Chat Components can be found in this repo. In the complete app we’ve done everything in this tutorial and refactored the app to follow best Swift practices so that you can see how you would expand further.

With PubNub iOS chat components you can build chat in a way that’s customized perfectly for your needs. PubNub provides a real-time publish/subscribe messaging API built on a global network. PubNub has multiple points of presence on every inhabited continent, supports dozens of SDKs, and has lots of features you’ll need in a chat application use case.

PubNub iOS Chat Components make a great starting point to build chat that you can customize to your exact needs. There isn’t any one size fits all Chat Components solution, but PubNub iOS Chat Components offer the most popular chat functionality that we have seen developers implement.

Use PubNub to build features into your application like:

Need some assistance modifying or building with iOS Chat Components? Get in touch with our sales team and we’ll help you get started.