### Create Message with Tapback - Objective-C
Source: https://docs.bluebubbles.app/private-api/imcore-documentation
Creates a new message, potentially with a tapback (reaction). It handles different message types, including attachments, and constructs a message summary for display. Requires BlueBubblesHelper for reaction conversion and message GUID generation.
```objectivec
createMessage(newAttributedString, subjectAttributedString, effectId, nil, [NSString stringWithFormat:@"bp:%@", [message guid]], &reactionLong, [item messagePartRange], messageSummary, nil, false);
} else {
createMessage(newAttributedString, subjectAttributedString, effectId, nil, [NSString stringWithFormat:@"p:%@/%@", data[@"partIndex"], [message guid]], &reactionLong, [item messagePartRange], messageSummary, nil, false);
}
}
} else {
messageSummary = @{@"amc":@1,@"ams":message.text.string};
NSData *dataenc = [[message text].string dataUsingEncoding:NSNonLossyASCIIStringEncoding];
NSString *encodevalue = [[NSString alloc]initWithData:dataenc encoding:NSUTF8StringEncoding];
NSRange range = NSMakeRange(0, [message text].string.length);
if ([encodevalue isEqualToString:@"\ufffc"] || [encodevalue length] == 0) {
NSMutableAttributedString *newAttributedString = [[NSMutableAttributedString alloc] initWithString: [[BlueBubblesHelper reactionToVerb:(reaction)] stringByAppendingString:(@"an attachment")]];
createMessage(newAttributedString, subjectAttributedString, effectId, nil, [message guid], &reactionLong, range, @{}, nil, false);
} else {
NSMutableAttributedString *newAttributedString = [[NSMutableAttributedString alloc] initWithString: [[BlueBubblesHelper reactionToVerb:(reaction)] stringByAppendingString:([NSString stringWithFormat:(@"“%@”"), [message text].string])]];
createMessage(newAttributedString, subjectAttributedString, effectId, nil, [message guid], &reactionLong, range, messageSummary, nil, false);
}
}
}];
```
--------------------------------
### Get IMMessage for Reply/Tapback (Objective-C)
Source: https://docs.bluebubbles.app/private-api/imcore-documentation
Asynchronously retrieves an `IMMessage` object using its GUID, which is necessary for creating replies or tapbacks. It utilizes `IMChatHistoryController` and requires a completion block to handle the result.
```objectivec
+(void) getMessageItem:(IMChat *)chat :(NSString *)actionMessageGuid completionBlock:(void (^)(IMMessage *message))block {
[[IMChatHistoryController sharedInstance] loadMessageWithGUID:(actionMessageGuid) completionBlock:^(IMMessage *message) {
block(message);
}];
}
```
--------------------------------
### Get IMChat Object by GUID
Source: https://docs.bluebubbles.app/private-api/imcore-documentation
Retrieves an existing IMChat instance using its unique GUID. Requires the IMChatRegistry.h header. Returns an IMChat object or nil if the GUID is invalid.
```objectivec
// 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;
}
```
--------------------------------
### Reboot to Recovery Mode (Intel Macs)
Source: https://docs.bluebubbles.app/private-api/installation
These commands reboot an Intel-based Mac into recovery mode, which is necessary for disabling System Integrity Protection (SIP). This action will instantly reboot your machine, so save all work beforehand. This is intended for official macOS installations.
```bash
sudo nvram internet-recovery-mode=RecoveryModeDisk
sudo reboot recovery
```
--------------------------------
### Delete Chat (Objective-C)
Source: https://docs.bluebubbles.app/private-api/imcore-documentation
Deletes a chat and all its associated messages permanently. It retrieves the chat using its GUID and then uses the `IMChatRegistry` to remove it. This action is irreversible.
```objectivec
// Get the chat
IMChat *chat = [BlueBubblesHelper getChat: data[@"chatGuid"] :transaction];
// Use the chat registry to remove the chat
if (chat != nil) {
[[IMChatRegistry sharedInstance] _chat_remove:(chat)];
}
```
--------------------------------
### Send Tapback (macOS 11+) - Objective-C
Source: https://docs.bluebubbles.app/private-api/imcore-documentation
This Objective-C code snippet demonstrates sending a Tapback on macOS 11 and later. It includes logic for creating an attributed string for the message content and defines a block for sending the message. It handles attachments and constructs the message summary accordingly.
```objectivec
if (attributedString == nil) {
NSString *message = data[@"message"];
// Tapbacks will not have message text, but messages sent must have some sort of text
if (message == nil) {
message = @"TEMP";
}
attributedString = [[NSMutableAttributedString alloc] initWithString: message];
}
void (^createMessage)(NSAttributedString*, NSAttributedString*, NSString*, NSString*, NSString*, long long*, NSRange, NSDictionary*, NSArray*, BOOL) = ^(NSAttributedString *message, NSAttributedString *subject, NSString *effectId, NSString *threadIdentifier, NSString *associatedMessageGuid, long long *reaction, NSRange range, NSDictionary *summaryInfo, NSArray *transferGUIDs, BOOL isAudioMessage) {
messageToSend = [messageToSend initWithSender:(nil) time:(nil) text:(message) messageSubject:(subject) fileTransferGUIDs:(nil) flags:(5) error:(nil) guid:(nil) subject:(nil) associatedMessageGUID:(associatedMessageGuid) associatedMessageType:*(reaction) associatedMessageRange:(range) messageSummaryInfo:(summaryInfo)];
[chat sendMessage:(messageToSend)];
};
[BlueBubblesHelper getMessageItem:(chat) :(data[@"selectedMessageGuid"]) completionBlock:^(IMMessage *message) {
IMMessageItem *messageItem = (IMMessageItem *)message._imMessageItem;
NSObject *items = messageItem._newChatItems;
IMMessagePartChatItem *item;
// sometimes items is an array so we need to account for that
if ([items isKindOfClass:[NSArray class]]) {
for (IMMessagePartChatItem *i in (NSArray *) items) {
if ([i index] == [data[@"partIndex"] integerValue]) {
item = i;
break;
}
}
} else {
item = (IMMessagePartChatItem *)items;
}
NSString *reaction = data[@"reactionType"];
long long reactionLong = [BlueBubblesHelper parseReactionType:(reaction)];
NSDictionary *messageSummary;
// if we actually got an item, proceed, otherwise use a fallback
if (item != nil) {
NSAttributedString *text = [item text];
if (text == nil) {
text = [message text];
}
messageSummary = @{@"amc":@1,@"ams":text.string};
// Send the tapback
// check if the body happens to be an object (ie an attachment) and send the tapback accordingly to show the proper summary
NSData *dataenc = [text.string dataUsingEncoding:NSNonLossyASCIIStringEncoding];
NSString *encodevalue = [[NSString alloc]initWithData:dataenc encoding:NSUTF8StringEncoding];
if ([encodevalue isEqualToString:@"\\ufffc"]) {
NSMutableAttributedString *newAttributedString = [[NSMutableAttributedString alloc] initWithString: [[BlueBubblesHelper reactionToVerb:(reaction)] stringByAppendingString:( @"an attachment")]];
createMessage(newAttributedString, subjectAttributedString, effectId, nil, [NSString stringWithFormat:@"p:%@/%@", data[@"partIndex"], [message guid]], &reactionLong, [item messagePartRange], @{}, nil, false);
} else {
NSMutableAttributedString *newAttributedString = [[NSMutableAttributedString alloc] initWithString: [[BlueBubblesHelper reactionToVerb:(reaction)] stringByAppendingString:([NSString stringWithFormat:( @"%@"), text.string])]];
if ([item text] == nil) {
```
--------------------------------
### Check SIP Status (macOS)
Source: https://docs.bluebubbles.app/private-api/installation
This command checks the current status of System Integrity Protection (SIP) on your macOS system. It is useful for troubleshooting and verifying if SIP has been successfully disabled or is still enabled. The output should be checked and provided when seeking further assistance.
```bash
csrutil status
```
--------------------------------
### Reboot to Recovery Mode (Apple Silicon Macs)
Source: https://docs.bluebubbles.app/private-api/installation
This procedure forces an Apple Silicon Mac to reboot into recovery mode by holding the power button during startup. This is a necessary step for disabling System Integrity Protection (SIP) on these devices. Follow the on-screen prompts to access startup options and then recovery.
```bash
# Shut down the Mac normally
# Press and hold the power button on your Mac until you see "Loading startup options."
# Click Options, then click Continue, and enter the admin password if requested.
```
--------------------------------
### Send Message with Effects and Replies (Objective-C)
Source: https://docs.bluebubbles.app/private-api/imcore-documentation
This Objective-C code snippet demonstrates how to send a message using the BlueBubbles API. It handles constructing messages with attributes like mentions, subjects, effects, and replies to existing threads. It accounts for variations in how message items are returned and generates thread identifiers as needed. This function is intended for use on Big Sur and later.
```objectivec
BlueBubblesHelper getMessageItem:(chat) :(data[@"selectedMessageGuid"]) completionBlock:^(IMMessage *message) {
IMMessageItem *messageItem = (IMMessageItem *)message._imMessageItem;
NSObject *items = messageItem._newChatItems;
IMMessagePartChatItem *item;
// sometimes items is an array so we need to account for that
if ([items isKindOfClass:[NSArray class]]) {
for(IMMessagePartChatItem* imci in (NSArray *)items) {
if([imci._item.guid isEqualToString:(data[@"selectedMessageGuid"])]) {
item = imci;
}
}
} else {
item = (IMMessagePartChatItem *)items;
}
NSString *identifier = @"";
// either reply to an existing thread or create a new thread
if (message.threadIdentifier != nil) {
identifier = message.threadIdentifier;
} else {
identifier = IMCreateThreadIdentifierForMessagePartChatItem(item);
}
createMessage(attributedString, subjectAttributedString, effectId, identifier);
}];
// otherwise just send a regular message
} else {
createMessage(attributedString, subjectAttributedString, effectId, nil);
}
```
--------------------------------
### Send Tapback (macOS 10) - Objective-C
Source: https://docs.bluebubbles.app/private-api/imcore-documentation
This Objective-C code snippet demonstrates how to send a Tapback (message reaction) on macOS 10. It maps the reaction type, retrieves the target message item, and sends the acknowledgment with message summary information. It handles cases where the message body might be an attachment.
```objectivec
//Map the reaction type
long long reactionLong = [BlueBubblesHelper parseReactionType:(data[@"reactionType"])];
// Get the messageItem
[BlueBubblesHelper getMessageItem:(chat) :(data[@"selectedMessageGuid"]) completionBlock:^(IMMessage *message) {
IMMessageItem *imMessage = (IMMessageItem *)message._imMessageItem;
NSObject *items = imMessage._newChatItems;
IMChatItem *item;
// sometimes items is an array so we need to account for that
if ([items isKindOfClass:[NSArray class]]) {
for(IMChatItem* imci in (NSArray *)items) {
if([imci._item.guid isEqualToString:(data[@"selectedMessageGuid"])]) {
item = imci;
}
}
} else {
item = (IMChatItem *)items;
}
//Build the message summary
NSDictionary *messageSummary = @{@"amc":@1,@"ams":[imMessage body].string};
// Send the tapback
// check if the body happens to be an object (ie an attachment) and send the tapback accordingly to show the proper summary
NSData *dataenc = [[imMessage body].string dataUsingEncoding:NSNonLossyASCIIStringEncoding];
NSString *encodevalue = [[NSString alloc]initWithData:dataenc encoding:NSUTF8StringEncoding];
if ([encodevalue isEqualToString:@"\\ufffc"]) {
[chat sendMessageAcknowledgment:(reactionLong) forChatItem:(item) withMessageSummaryInfo:(@{})];
} else {
[chat sendMessageAcknowledgment:(reactionLong) forChatItem:(item) withMessageSummaryInfo:(messageSummary)];
}
}];
```
--------------------------------
### Send Message (macOS 11+, With Replies)
Source: https://docs.bluebubbles.app/private-api/imcore-documentation
This Objective-C code snippet demonstrates sending a message with reply functionality on macOS 11 and later. It includes handling attributed bodies, subjects, and effect IDs, similar to the macOS 10 version. A key addition is the `messageToSend.threadIdentifier` property, enabling replies.
```objectivec
// now we will deserialize the attributedBody if it exists
NSDictionary *attributedDict = data[@"attributedBody"];
// we'll create the NSMutableAttributedString with the associatedBody string if we can,
// else we'll fall back to using the message text
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString: data[@"message"]];
// if associateBody exists, we iterate through it
if (attributedDict != NULL && attributedDict != (NSDictionary*)[NSNull null]) {
attributedString = [[NSMutableAttributedString alloc] initWithString: attributedDict[@"string"]];
NSArray *attrs = attributedDict[@"runs"];
for(NSDictionary *dict in attrs)
{
// construct range and attributes from dict and add to NSMutableAttributedString
NSArray *rangeArray = dict[@"range"];
NSRange range = NSMakeRange([(NSNumber*)[rangeArray objectAtIndex:0] intValue], [(NSNumber*)[rangeArray objectAtIndex:1] intValue]);
NSDictionary *attrsDict = dict[@"attributes"];
[attributedString addAttributes:attrsDict range:range];
}
}
// if we've got a subject, make the string
NSMutableAttributedString *subjectAttributedString = nil;
if (data[@"subject"] != [NSNull null] && [data[@"subject"] length] != 0) {
subjectAttributedString = [[NSMutableAttributedString alloc] initWithString: data[@"subject"]];
}
// if we've got an effect ID, make the string
NSString *effectId = nil;
if (data[@"effectId"] != [NSNull null] && [data[@"effectId"] length] != 0) {
effectId = data[@"effectId"];
}
// function we can call to create the message easily
void (^createMessage)(NSAttributedString*, NSAttributedString*, NSString*, NSString*) = ^(NSAttributedString *message, NSAttributedString *subject, NSString *effectId, NSString *threadIdentifier) {
IMMessage *messageToSend = [[IMMessage alloc] init];
messageToSend = [messageToSend initWithSender:(nil) time:(nil) text:(message) messageSubject:(subject) fileTransferGUIDs:(nil) flags:(100005) error:(nil) guid:(nil) subject:(nil) balloonBundleID:(nil) payloadData:(nil) expressiveSendStyleID:(effectId)];
// remove this line of code if not on Big Sur and up!!
messageToSend.threadIdentifier = threadIdentifier;
[chat sendMessage:(messageToSend)];
};
// create a thread identifier if we are replying (ONLY WORKS ON BIG SUR)
if (data[@"selectedMessageGuid"] != [NSNull null] && [data[@"selectedMessageGuid"] length] != 0) {
```
--------------------------------
### Start/Stop Typing Indicator in Chat
Source: https://docs.bluebubbles.app/private-api/imcore-documentation
Sets the local user's typing status for a given chat. This is a simple boolean toggle to indicate if the user is currently typing or has stopped typing.
```objectivec
[chat setLocalUserIsTyping:YES];
[chat setLocalUserIsTyping:NO];
```
--------------------------------
### Listen to Typing Indicator Status with ZKSwizzle
Source: https://docs.bluebubbles.app/private-api/imcore-documentation
Intercepts typing indicator events using ZKSwizzle to provide more reliable typing status updates. It handles both incoming typing messages and cancellation of typing indicators, with added checks to prevent timing issues and infinite typing indicators.
```objectivec
// Credit to w0lf
// Handles all of the incoming typing events
ZKSwizzleInterface(BBH_IMMessageItem, IMMessageItem, NSObject)
@implementation BBH_IMMessageItem
- (BOOL)isCancelTypingMessage {
// isCancelTypingMessage seems to also have some timing issues and adding a delay would fix this
// But I would rather not rely on delays to have this program work properly
//
// We would rather that the typing message be cancelled prematurely rather
// than having the typing indicator stuck permanently
NSString *guid = [self getGuid];
if(guid != nil) {
if([self isLatestMessage]) {
// handle stopped typing status here
}
}
return ZKOrig(BOOL);
}
- (BOOL)isIncomingTypingMessage {
// We do this because the isIncomingTypingMessage seems to have some timing
// issues and will sometimes notify after the isCancelTypingMessage so we need to confirm
// that the sender actually is typing
[self updateTypingState];
// This is here to ensure that no infinite typing occurs
// If for whatever reason the isCancelTypingMessage does not occur,
// this should catch the error in 2 seconds
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
if(self != nil) {
NSString *guid = [self getGuid];
if(guid != nil) {
if([BlueBubblesHelper isTyping:guid] == NO) {
// handle stopped typing status here
}
}
}
});
return ZKOrig(BOOL);
}
// Check to see if this IMMessageItem matches the last IMChat's message
// This helps to avoid spamming of the tcp socket
- (BOOL) isLatestMessage {
NSString *guid = [self getGuid];
// Fetch the current IMChat to get the IMMessage
IMChat *chat = [BlueBubblesHelper getChat:guid];
IMMessageItem *item = (IMMessageItem*) self;
IMMessage *message = item.message;
if(message.isFromMe) return NO;
// If the IMChat's last message matches our own IMMessage, then we can proceed
// this should avoid spamming of the tcp socket
return chat.lastIncomingMessage.guid == message.guid;
}
// Update the typing state by checking the message state
- (void) updateTypingState {
if(![self isLatestMessage]) return;
NSString *guid = [self getGuid];
// If we failed to get the guid for whatever reason, then we can't do anything
if(guid != nil) {
IMChat *chat = [BlueBubblesHelper getChat:guid];
// Send out the correct response over the tcp socket
if(chat.lastIncomingMessage.isTypingMessage == YES) {
// handle started typing status here
} else {
// handle stopped typing status here
}
}
}
@end
```
--------------------------------
### Send Message (macOS 10, No Replies)
Source: https://docs.bluebubbles.app/private-api/imcore-documentation
This Objective-C code snippet demonstrates how to send a message without reply functionality on macOS 10. It handles deserializing attributed bodies, creating attributed strings for messages and subjects, and setting effect IDs. The `createMessage` block constructs and sends the `IMMessage` object.
```objectivec
// now we will deserialize the attributedBody if it exists
NSDictionary *attributedDict = data[@"attributedBody"];
// we'll create the NSMutableAttributedString with the associatedBody string if we can,
// else we'll fall back to using the message text
NSMutableAttributedString *attributedString = [[NSMutableAttributedString alloc] initWithString: data[@"message"]];
// if associateBody exists, we iterate through it
if (attributedDict != NULL && attributedDict != (NSDictionary*)[NSNull null]) {
attributedString = [[NSMutableAttributedString alloc] initWithString: attributedDict[@"string"]];
NSArray *attrs = attributedDict[@"runs"];
for(NSDictionary *dict in attrs)
{
// construct range and attributes from dict and add to NSMutableAttributedString
NSArray *rangeArray = dict[@"range"];
NSRange range = NSMakeRange([(NSNumber*)[rangeArray objectAtIndex:0] intValue], [(NSNumber*)[rangeArray objectAtIndex:1] intValue]);
NSDictionary *attrsDict = dict[@"attributes"];
[attributedString addAttributes:attrsDict range:range];
}
}
NSMutableAttributedString *subjectAttributedString = nil;
if (data[@"subject"] != [NSNull null] && [data[@"subject"] length] != 0) {
subjectAttributedString = [[NSMutableAttributedString alloc] initWithString: data[@"subject"]];
}
NSString *effectId = nil;
if (data[@"effectId"] != [NSNull null] && [data[@"effectId"] length] != 0) {
effectId = data[@"effectId"];
}
void (^createMessage)(NSAttributedString*, NSAttributedString*, NSString*, NSString*) = ^(NSAttributedString *message, NSAttributedString *subject, NSString *effectId, NSString *threadIdentifier) {
IMMessage *messageToSend = [[IMMessage alloc] init];
messageToSend = [messageToSend initWithSender:(nil) time:(nil) text:(message) messageSubject:(subject) fileTransferGUIDs:(nil) flags:(100005) error:(nil) guid:(nil) subject:(nil) balloonBundleID:(nil) payloadData:(nil) expressiveSendStyleID:(effectId)];
[chat sendMessage:(messageToSend)];
if (transaction != nil) {
[[NetworkController sharedInstance] sendMessage: @{@"transactionId": transaction, @"identifier": [[chat lastMessage] guid]}];
}
};
createMessage(attributedString, subjectAttributedString, effectId, nil);
```
--------------------------------
### Add or Remove Participants from Group Chat (Objective-C)
Source: https://docs.bluebubbles.app/private-api/imcore-documentation
This function allows adding or removing participants from an iMessage group chat. It requires IMHandle objects for the participants and checks if they can be added/removed. The `reason` argument must be 0. Note: Participants must exist in `chat.db`.
```objectivec
NSArray *handles = [[IMHandleRegistrar sharedInstance] getIMHandlesForID:(@"some address here")];
// when removing participants, you don't need to do this if block
if (handles != nil) {
IMAccountController *sharedAccountController = [IMAccountController sharedInstance];
IMAccount *myAccount = [sharedAccountController mostLoggedInAccount];
IMHandle *handle = [[IMHandle alloc] initWithAccount:(myAccount) ID:(@"some address here") alreadyCanonical:(YES)];
handles = @[handle];
}
if([chat canAddParticipants:(handles)]) {
// to add
[chat inviteParticipantsToiMessageChat:(handles) reason:(0)];
// to remove
[chat removeParticipantsFromiMessageChat:(handles) reason:(0)];
}
```
--------------------------------
### Parse Tapback Reaction Type to ID (Objective-C)
Source: https://docs.bluebubbles.app/private-api/imcore-documentation
This Objective-C function, `parseReactionType`, converts human-readable tapback reaction types (e.g., 'love', 'like') into their corresponding integer IDs used by iMessage. It handles both positive and negative reactions by checking the lowercase version of the input string. If the reaction type is not recognized, it returns 0.
```objectivec
+(long long) parseReactionType:(NSString *)reactionType {
NSString *lowerCaseType = [reactionType lowercaseString];
if([@"love" isEqualToString:(lowerCaseType)]) return 2000;
if([@"like" isEqualToString:(lowerCaseType)]) return 2001;
if([@"dislike" isEqualToString:(lowerCaseType)]) return 2002;
if([@"laugh" isEqualToString:(lowerCaseType)]) return 2003;
if([@"emphasize" isEqualToString:(lowerCaseType)]) return 2004;
if([@"question" isEqualToString:(lowerCaseType)]) return 2005;
if([@"-love" isEqualToString:(lowerCaseType)]) return 3000;
if([@"-like" isEqualToString:(lowerCaseType)]) return 3001;
if([@"-dislike" isEqualToString:(lowerCaseType)]) return 3002;
if([@"-laugh" isEqualToString:(lowerCaseType)]) return 3003;
if([@"-emphasize" isEqualToString:(lowerCaseType)]) return 3004;
if([@"-question" isEqualToString:(lowerCaseType)]) return 3005;
return 0;
}
```
--------------------------------
### Disable SIP (macOS Recovery)
Source: https://docs.bluebubbles.app/private-api/installation
This command disables System Integrity Protection (SIP) when executed from the Terminal within macOS Recovery Mode. SIP must be disabled to enable certain functionalities required by the Private API. After execution, your Mac should be restarted.
```bash
csrutil disable
```
--------------------------------
### Update Chat Pinned Status (Objective-C)
Source: https://docs.bluebubbles.app/private-api/imcore-documentation
Toggles the pinned status of a chat. It retrieves the list of pinned conversations, adds or removes the chat's pinning identifier, and updates the controller. The `withUpdateReason` argument must be 'contextMenu'. This code is only compatible with macOS Big Sur.
```objectivec
// if the chat is pinned, unpin it, otherwise pin it
if (!chat.isPinned) {
// get the pinned conversation set, make it mutable, and then add the chat to be pinned
NSArray* arr = [[[IMPinnedConversationsController sharedInstance] pinnedConversationIdentifierSet] array];
NSMutableArray* chatArr = [[NSMutableArray alloc] initWithArray:(arr)];
[chatArr addObject:(chat.pinningIdentifier)];
// convert mutable back to immutable
NSArray* finalArr = [chatArr copy];
// update the pinned conversation array
IMPinnedConversationsController* controller = [IMPinnedConversationsController sharedInstance];
// contextMenu is an arbitrary value, other values may work as well
[controller setPinnedConversationIdentifiers:(finalArr) withUpdateReason:( @"contextMenu")];
} else {
// get the pinned conversation set, make it mutable, and then remove the chat to be unpinned
NSArray* arr = [[[IMPinnedConversationsController sharedInstance] pinnedConversationIdentifierSet] array];
NSMutableArray* chatArr = [[NSMutableArray alloc] initWithArray:(arr)];
[chatArr removeObject:(chat.pinningIdentifier)];
// convert mutable back to immutable
NSArray* finalArr = [chatArr copy];
// update the pinned conversation array
IMPinnedConversationsController* controller = [IMPinnedConversationsController sharedInstance];
// contextMenu is an arbitrary value, other values may work as well
[controller setPinnedConversationIdentifiers:(finalArr) withUpdateReason:( @"contextMenu")];
}
```
--------------------------------
### Disable Library Validation (macOS)
Source: https://docs.bluebubbles.app/private-api/installation
This command disables library validation on macOS, a prerequisite for enabling the Private API. It requires administrator privileges and prompts for a password. Ensure you are running this in the Terminal application.
```bash
sudo defaults write /Library/Preferences/com.apple.security.libraryvalidation.plist DisableLibraryValidation -bool true
```
--------------------------------
### Unsend Message Objective-C
Source: https://docs.bluebubbles.app/private-api/imcore-documentation
This Objective-C code snippet demonstrates how to unsend a message using the BlueBubbles private API. It retrieves the chat and message, identifies the specific message part, and then uses the `retractMessagePart` method to unsend it. The `partIndex` is critical for correctly identifying the message part to be retracted. This functionality may fail for older messages where the `IMMessagePartChatItem` might not be available.
```objectivec
// get the chat
IMChat *chat = [BlueBubblesHelper getChat: data[@"chatGuid"] :transaction];
// get the message
[BlueBubblesHelper getMessageItem:(chat) :(data[@"messageGuid"]) completionBlock:^(IMMessage *message) {
// find the message item corresponding to the part index
IMMessageItem *messageItem = (IMMessageItem *)message._imMessageItem;
NSObject *items = messageItem._newChatItems;
IMMessagePartChatItem *item;
// sometimes items is an array so we need to account for that
if ([items isKindOfClass:[NSArray class]]) {
for (IMMessagePartChatItem *i in (NSArray *) items) {
if ([i index] == [data[@"partIndex"] integerValue]) {
item = i;
break;
}
}
} else {
item = (IMMessagePartChatItem *)items;
}
// retract (unsend) the message
[chat retractMessagePart:(item)];
}];
```
--------------------------------
### Mark Chat as Read
Source: https://docs.bluebubbles.app/private-api/imcore-documentation
Marks all messages in a given chat as read, removing the unread indicator in the macOS iMessage client. This is a straightforward method call on the chat object.
```objectivec
[chat markAllMessagesAsRead];
```
--------------------------------
### Change Group Chat Name
Source: https://docs.bluebubbles.app/private-api/imcore-documentation
Sets a new display name for a group chat. This method is prefixed with an underscore, suggesting it might be an internal or less commonly used API.
```objectivec
[chat _setDisplayName:(@"new name here")];
```
--------------------------------
### Mark Chat as Unread
Source: https://docs.bluebubbles.app/private-api/imcore-documentation
Marks the last message in a given chat as unread, adding an unread indicator to the chat in the macOS iMessage client. This method requires macOS Ventura (13.0) or higher.
```objectivec
[chat markLastMessageAsUnread];
```
--------------------------------
### Check if Receiving Typing Indicator
Source: https://docs.bluebubbles.app/private-api/imcore-documentation
Determines if the last incoming message in a chat is a typing indicator. Returns a boolean value. Note: This does not provide live updates and may require ZKSwizzle for real-time status.
```objectivec
chat.lastIncomingMessage.isTypingMessage
```
--------------------------------
### Edit Message - Objective-C
Source: https://docs.bluebubbles.app/private-api/imcore-documentation
Edits an existing message in a chat. It requires the chat object, the message to be edited, the part index of the message segment to change, and the new attributed strings for the message body and backward compatibility. Uses BlueBubblesHelper to retrieve chat and message objects.
```objectivec
// get the chat
IMChat *chat = [BlueBubblesHelper getChat: data[@"chatGuid"] :transaction];
// get the message
[BlueBubblesHelper getMessageItem:(chat) :(data[@"messageGuid"]) completionBlock:^(IMMessage *message) {
// generate the two NSMutableAttributedStrings
NSMutableAttributedString *editedString = [[NSMutableAttributedString alloc] initWithString: data[@"editedMessage"]];
NSMutableAttributedString *bcString = [[NSMutableAttributedString alloc] initWithString: data[@"backwardsCompatibilityMessage"]];
NSInteger index = data["@partIndex"];
// send the edit
[chat editMessage:(message) atPartIndex:(index) withNewPartText:(editedString) backwardCompatabilityText:(bcString)];
}];
```
=== COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.