What's New

Implement a simple chat bubble with a button

Let's say you want to implement a simple chat bubble like this, with 'UIKit framework' and Swift:

  1. When sending a message, fill in the key-value data in 'metaData' property which will be used for creating an UIView instance.

import UIKit
import TalkPlus

func sendCardViewMessage()  {
 
    guard let channel = <YOUR_TALK_PLUS_CHANNEL> else { return }    
    
    //  fill in the key-value data in 'metaData' property 
    let params = TPMessageSendParams(contentType: .text, 
        messageType: .hidden, 
        channel: channel)
        
    params?.metaData = [
        "type": "msg_with_button",
        "title": "Hi!",
        "body": "This is a simple chat bubble with a button.",
        "button_text": "Say Hello",
        "button_action": "SayHello"
    ]
    
     // Send the message to the channel
    TalkPlus.sharedInstance()?.sendMessage(params, success: { message in 
        //  succeeded in sending the message
    }, failure: { errorCode, error in
        // failed in sending message.
    })
}

You can enter up to 5 key-value pairs in data field. The maximum size of key is 128 characters and the maximum size of value is 1024 characters. Both key and value must be strings.

  1. Define a 'CardViewMessageModel' class that contains data to be displayed on the screen.

import UIKit

class CardViewMessageModel: Codable {
    var type:String
    var title:String
    var body:String
    var button_text:String
    var button_action:String
    
    init(type: String, 
         title: String,
         body: String,
         button_text: String,
         button_action: String)
    {
        self.type = type
        self.title = title
        self.body = body
        self.button_text = button_text
        self.button_action = button_action
    }
    
    static func addCardView(cvm:CardViewMessage, parentView:UIView) {
        // 1. Container View
        let containerView = UIView()
        containerView.backgroundColor = .white
        containerView.layer.cornerRadius = 10
        containerView.layer.shadowColor = UIColor.black.cgColor
        containerView.layer.shadowOpacity = 0.2
        containerView.layer.shadowOffset = CGSize(width: 0, height: 2)
        containerView.layer.shadowRadius = 4
        containerView.translatesAutoresizingMaskIntoConstraints = false
        parentView.addSubview(containerView)
        // 2. Title Label
        let titleLabel = UILabel()
        titleLabel.text = cvm.title
        titleLabel.font = UIFont.boldSystemFont(ofSize: 18)
        titleLabel.translatesAutoresizingMaskIntoConstraints = false
        containerView.addSubview(titleLabel)
        // 3. Description Label
        let descriptionLabel = UILabel()
        descriptionLabel.text = cvm.body
        descriptionLabel.numberOfLines = 0
        descriptionLabel.font = UIFont.systemFont(ofSize: 14)
        descriptionLabel.translatesAutoresizingMaskIntoConstraints = false
        containerView.addSubview(descriptionLabel)
        // 4. Button
        let button = UIButton(type: .system)
        button.setTitle(cvm.button_text, for: .normal)
        button.backgroundColor = .systemBlue
        button.setTitleColor(.white, for: .normal)
        button.layer.cornerRadius = 5
        button.translatesAutoresizingMaskIntoConstraints = false
        containerView.addSubview(button)
        
        // 5. Auto Layout Constraints
        NSLayoutConstraint.activate([
            // Container View Constraints
            containerView.centerXAnchor.constraint(equalTo: parentView.centerXAnchor),
            containerView.centerYAnchor.constraint(equalTo: parentView.centerYAnchor),
            containerView.widthAnchor.constraint(equalToConstant: 250),
            containerView.heightAnchor.constraint(equalToConstant: 150),
            // Title Label Constraints
            titleLabel.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 16),
            titleLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 16),
            titleLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -16),
            // Description Label Constraints
            descriptionLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 8),
            descriptionLabel.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 16),
            descriptionLabel.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -16),
            // Button Constraints
            button.topAnchor.constraint(equalTo: descriptionLabel.bottomAnchor, constant: 16),
            button.centerXAnchor.constraint(equalTo: containerView.centerXAnchor),
            button.widthAnchor.constraint(equalToConstant: 100),
            button.heightAnchor.constraint(equalToConstant: 40),
            button.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: -10)
        ])
    }
}
  1. Adopt 'TPChannelDelegate' protocol to your ViewController, and add required methods for this protocol.

import UIKit
import TalkPlus

class ViewController: UIViewController {
    static func dictionaryToClass<T: Codable>(_ dictionary: [String: Any], 
        type: T.Type) -> T? 
    {
        let decoder = JSONDecoder()
        guard let data = try? JSONSerialization.data(withJSONObject: dictionary, 
            options: .fragmentsAllowed) else { return nil }
        guard let object = try? decoder.decode(T.self, from: data) else {  return nil }
        return object
    }
}

extension ViewController: TPChannelDelegate {

}
  1. 'messageReceived' method will be called whenever receiving a new message. At this method, call 'getData' method in 'TPMessage to get key-value data and then create an instance of 'CardViewMessageModel'.

extension ViewController: TPChannelDelegate {
    ... 
    // Called when receiving a new message from the chat channel
    func messageReceived(_ tpChannel: TPChannel!, message tpMessage: TPMessage!) {
        // Create an instance of 'CardViewMessageModel' 
        guard let data = tpMessage.getData() as? [String : Any], 
                    data.count > 0 else { return }
        // Decode key-value data, and then create a new 
        guard let cvm = dictionaryToClass(data, 
            type: CardViewMessageModel.self) else { return }
        // Creates a view and add a view to where you want. 
        CardViewMessageModel.addCardView(cvm: cvm, parentView: self.view)
    }
    ... 
}
  1. As a final step, you need to create a view and add it where you want. In the above example, 'CardViewMessageModel.addCardView' method creates a view and adds the created view to the parent view.

Last updated