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.
This method requires macOS Ventura (13.0) or above!
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.
There is one caveat with adding and removing participants with this code. The participant has to be inside the iMessage chat.db, otherwise they may not get added. We haven't been able to find a workaround or a way of registering the participant inside the database as of yet.
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.
Pinning chats was introduced on Big Sur, and as such this code will crash if run on macOS 10. This code also seems to crash on Monterey and up - since we cannot run classdump on Monterey yet we have been unable to check and see if the APIs changed.
For now, only use this code on macOS Big Sur.
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:
Deserialize the
attributedBodydata 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.If this data object exists, generate the message
NSAttributedStringfrom it. Otherwise, just make a plaintextNSAttributedString.If we have a subject, then make a subject
NSAttributedString(otherwise null)If we have an effect ID, make an effect ID
NSString(otherwise null)Big Sur+ Only - If we have a selected message GUID (which means the user replied to something), generate a thread originator GUID.
We first have to get the
IMMessageobject for the selected messageGet an
IMMessagePartChatItemfrom theIMMessage. We have to be very careful here as sometimes the getter returns an array, and sometimes it returns a single object.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
IMCreateThreadIdentifierForMessagePartChatItemto generate one for us.
Send the message with all the parameters (most can be null). Flags set to
100005is arbitrary, that's what worked for us in our testing.
Be sure to only use the Big Sur specific methods on Big Sur, otherwise the iMessage app will crash on macOS 10.
Parsing a Tapback into its Tapback ID
This function simply converts text based reaction types to their integer counterparts in iMessage.
Sending a Tapback
Get the integer representation of the tapback using the
parseReactionTypefunction.Get the
IMMessageobject for the message the user selected to react toGet an
IMMessagePartChatItemfrom thisIMMessage. As is the case when sending a reply, sometimes the getter is an array and sometimes it is the single object, so watch out.Build a message summary. This is what is shown on the chat list page, e.g. 'John liked "Test"'.
amcstands for associated message content, whileamsstands for associated message summary.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.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.MacOS 11+ requires some extra things:
Get the range for the message being reacted to - this refers directly with the
IMMessagePartChatItemas a message can have multiple items and the range helps identify which oneAdd the partIndex to the associated message guid:
p:<index>/<guid>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
Sometimes, the
IMMessagePartChatItemwill 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.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
IMMessagePartChatItembut we haven't figured it out quite yet. See this ticket for more details.
Last updated
Was this helpful?