swift: how to create an interactive iMessage application

File->New->Project and choose “iMessage Application”.
You already have view controller, actually MSMessagesAppViewController. Although, to better support both minimized and expanded views, it’s easier to create another “view controller” just for “collapsed” view, so in the main MSMessagesAppViewController you can control this situation with help of the following:

1. First of all in you mini view controller it’s a good idea to have delegate to the main one and storyboard id:

class MiniViewController : UICollectionViewController {
 
    // TODO: add loading view / indicator? Might be useless - needed for debug only.
 
    static let storyboardIdentifier = "MiniViewController"
    var delegate : MessagesViewController?
 
    override func viewDidLoad() {
        super.viewDidLoad()    
    }
 
//...
}

2. So in main controller you can check the “minified” situation

// handle it here
override func willBecomeActive(with conversation: MSConversation) {
    showMinimizedIfNeeded(with: self.presentationStyle)
    //...
}
 
// and here
override func willTransition(to presentationStyle: MSMessagesAppPresentationStyle) {
    showMinimizedIfNeeded(with: presentationStyle)
    //...
}

And here’s how to transition to mini controller

// Go to minified view
func showMinimizedIfNeeded(with presentationStyle: MSMessagesAppPresentationStyle) {
    let viewcontroller : UIViewController
 
    for child in childViewControllers {
        child.willMove(toParentViewController: nil)
        child.view.removeFromSuperview()
        child.removeFromParentViewController()
    }
 
    if presentationStyle == .compact {
        print("show compact version")
        guard let vc = self.storyboard?.instantiateViewController(
            withIdentifier: MiniViewController.storyboardIdentifier) as? MiniViewController else { fatalError("expected view controller") }
        vc.delegate = self
        viewcontroller = vc
    } else {
        // Do nothing for extended version.
        print("Show expanded version")
        return
    }
 
    addChildViewController(viewcontroller)
 
    viewcontroller.view.frame = view.bounds
    viewcontroller.view.translatesAutoresizingMaskIntoConstraints = false
    view.addSubview(viewcontroller.view)
 
    viewcontroller.view.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
    viewcontroller.view.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
    viewcontroller.view.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
    viewcontroller.view.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
 
    viewcontroller.didMove(toParentViewController: self)
}

This is it, so now there are two different view controllers for two different situations.

How to actually send custom message in iMessage?

Here’s the example of on send handler

    @IBAction func onSend(_ sender: Any) {
        print("sending...")
        guard let conversation = activeConversation else { fatalError("Expected a conversation") }
        guard let message = composeMessage(session: conversation.selectedMessage?.session)
            else { return }
 
        // Add the message to the conversation.
        conversation.insert(message) { error in
            if let error = error {
                print(error)
            }
        }
        dismiss()
    }
 
    // MARK: Messaging
    func composeMessage(session: MSSession? = nil) -> MSMessage? {
        let layout = MSMessageTemplateLayout()
        var components = URLComponents()
        let caption = URLQueryItem(name: "caption", value: self.melody)
        let decodedMelody = URLQueryItem(name: "melody", value: self.melody)
 
        components.queryItems = [caption, decodedMelody]
 
        let message = MSMessage(session: session ?? MSSession())
        layout.image = self.screenImage.image
 
        layout.caption = "Melody built with haptic and vibro."
        layout.subcaption = "sent via iVibrio"
        message.summaryText = "something summary"
 
        if let conversation = activeConversation,
            let msg = conversation.selectedMessage{
 
            if msg.senderParticipantIdentifier == conversation.localParticipantIdentifier {
                layout.caption =  "$\(msg.senderParticipantIdentifier.uuidString) My msg"
            }
            else{
                layout.caption =  "$\(msg.senderParticipantIdentifier.uuidString) Edited msg"
            }
        }
 
        message.url = components.url!
        message.layout = layout
 
        return message
    }

And here’s the example how to handle “opening” of already sent message

override func willBecomeActive(with conversation: MSConversation) {        
        if let message = conversation.selectedMessage {
            print(message.url ?? "url empty")
 
            if let url = message.url,
                let urlComponents = NSURLComponents(url: url, resolvingAgainstBaseURL: false), let queryItems = urlComponents.queryItems {
                for queryItem in queryItems {
                    if (queryItem.name == "melody") {
                        // Opened someone's message
                        self.melody = queryItem.value ?? ""
                        melodyUpdated()
                        // let's play it "on open" - or do something with metadata from message
                        if !player.isPlaying() {
                            print("playing...")
                            play(melody: self.melody)
                        }
 
                    }
                    print(queryItem.name + " | " + (queryItem.value ?? "-"))
                }
 
            }
 
        }
    }

This is it, it’s all about how to compose and open custom messages via iMessage. While looking into iMessage Application API have been created this small app which sends vibro patterns via iMessage, so you could kind of poke other person.

App Details: iVibrio – Compose haptic & vibro melody and
App store link.

Social Share Toolbar