IMCore Documentation

Here's what we've found and learned while messing with IMCore

Chats

Getting a Chat Object

Chat objects are defined as IMChat, and we access these via a lookup by guid.

To do this, you'll want to add the IMChatRegistry.h header into your project.

// Retrieve a IMChat instance from a given guid
//
// Uses the chat registry to get an existing instance of a chat based on the chat guid
+(IMChat *) getChat: (NSString *) guid {
    if(guid == nil) return nil;

    IMChat* imChat = [[IMChatRegistry sharedInstance] existingChatWithGUID: guid];
    return imChat;
}

Start/Stop Typing in a Chat

This one is relatively simple:

[chat setLocalUserIsTyping:YES];
[chat setLocalUserIsTyping:NO];

Are we Receiving a Typing Indicator?

This one is also pretty simple:

This will provide a bool value. However, this won't provide you with live updates on typing status. To do this, you'll need to make use of ZKSwizzle (read more about this in Contribution Resources).

Listen to Typing Indicator Status

In the above code, we use swizzling techniques to "intercept" when iMessage calls the isCancelTypingMessage and isIncomingTypingMessage functions. Due to some timing issues described in the code comments, we double verify to make sure the user has actually started or stopped typing, and then pass that status back to clients.

Mark Chat Read

Like the rest, this is fairly straightforward:

This will remove the unread dot from macOS iMessage.

Mark Chat Unread

Like the rest, this is fairly straightforward:

This will add an unread dot to the chat in macOS iMessage.

Change Group Chat Name

Once again, another easy method:

Add or Remove Participants from a Group Chat

First, you'll want to pass addresses to IMHandleRegistrar so you can get IMHandle objects for them.

Then make sure those handles are added to the IMAccount (iMessage account), and after that make sure those handles can be added to the chat itself.

Finally, add the participant (or remove the participant). For whatever reason, the reason argument must be 0 or it doesn't work.

Update Chat Pinned Status

We first grab all the pinned conversations. Then we add or remove the conversation from this array, and send it back to IMPinnedConversationsController. The update reason should be contextMenu otherwise the code may not work.

Deleting a Chat

Pretty simple method. This is not recoverable, the chat and its messages will be permanently deleted!

Messages

Getting an IMMessage

This object is very important when sending a reply or a tapback. We need it to create the association between the existing message that is replied or "tapbacked" to, and the reply or tapback.

This is an asynchronous process in IMCore, so it requires the use of a completion block.

Sending a Message (Including Subject, Effect, Mention, and/or Reply)

There's a lot to break down here. This serves as our all-in-one function of sending a message with an effect, subject line, mention, or reply, or any combination of these things (a message could have all of these things at once).

An example JSON object sent to the bundle might look like this:

And this is how we parse it:

  1. Deserialize the attributedBody data object. This is what contains mentions data sent from the client, specifically the range of text that is the mention, and what address it is mentioning.

  2. If this data object exists, generate the message NSAttributedString from it. Otherwise, just make a plaintext NSAttributedString.

  3. If we have a subject, then make a subject NSAttributedString (otherwise null)

  4. If we have an effect ID, make an effect ID NSString (otherwise null)

  5. Big Sur+ Only - If we have a selected message GUID (which means the user replied to something), generate a thread originator GUID.

    1. We first have to get the IMMessage object for the selected message

    2. Get an IMMessagePartChatItem from the IMMessage. We have to be very careful here as sometimes the getter returns an array, and sometimes it returns a single object.

    3. Finally, we make the thread identifier. The original message may already have the thread identifier, in which case we use that (i.e. it's already part of a thread). If it doesn't, we can use IMCreateThreadIdentifierForMessagePartChatItem to generate one for us.

  6. Send the message with all the parameters (most can be null). Flags set to 100005 is arbitrary, that's what worked for us in our testing.

Parsing a Tapback into its Tapback ID

This function simply converts text based reaction types to their integer counterparts in iMessage.

Sending a Tapback

  1. Get the integer representation of the tapback using the parseReactionType function.

  2. Get the IMMessage object for the message the user selected to react to

  3. Get an IMMessagePartChatItem from this IMMessage. As is the case when sending a reply, sometimes the getter is an array and sometimes it is the single object, so watch out.

  4. Build a message summary. This is what is shown on the chat list page, e.g. 'John liked "Test"'. amc stands for associated message content, while ams stands for associated message summary.

  5. Set the second value to the body of the message being reacted to. If this message is a non-text message, the body string will be the unicode ufffc.

  6. Due to the above, we want to make sure we don't mistakenly send a summary with a non-displayable unicode character inside it. Thus, we set the summary to null if we detect the body string to be ufffc.

  7. MacOS 11+ requires some extra things:

    1. Get the range for the message being reacted to - this refers directly with the IMMessagePartChatItem as a message can have multiple items and the range helps identify which one

    2. Add the partIndex to the associated message guid: p:<index>/<guid>

    3. Provide a fake message NSAttributedString - this is not actually used anywhere but avoids crashes

Editing a Message

This one is fairly easy. The only tricky part is the part index, which corresponds to which part of a message is being edited. Sometimes, a message can be stacked as so:

Message

Attachment

Message

The part index tells IMCore which part of the message is actually being edited, as there are two separate message strings that could be changed. The indexes start at 0.

Unsending a Message

As with editing a message, the part index is crucial to know which part has been unsent and then use the IMMessagePartChatItem in the method correctly.

Currently Known Issues

  1. Sometimes, the IMMessagePartChatItem will not exist. This is almost always for messages that are old. If this happens, tapbacks won't work, and replies will have a weird bug where they attach to an "empty" message. See this ticket for more details.

  2. The reaction function currently doesn't support reacting to a message with multiple parts, for example a message with multiple attachments. This is likely also to do with IMMessagePartChatItem but we haven't figured it out quite yet. See this ticket for more details.

Last updated

Was this helpful?