Skip to main content

Location Messages

IMKit provides a location plugin that enables sending location messages and displaying location thumbnails. This document also describes how to implement in-app location sharing.

  • The SDK sends location messages containing a [RCLocationMessage] object by default (message type identifier: RC:LBSMsg).
  • Real-time location sharing is also message-based. The SDK uses message type identifiers RC:RL, RC:RLStart, RC:RLJoin, and RC:RLQuit by default.

message-location(width=250) message-location-share(width=250)

Limitations

  • IMKit's location plugin is based on MapKit. To use other map services, you can customize the plugin, construct location messages manually, and send them. For details on adding custom plugins, see Input Area.
  • The location plugin supports tap-to-send by default.

Usage

IMKit supports the LocationKit plugin starting from version 5.2.3. If upgrading from an IMKit version below 5.2.3, refer to Upgrading the Legacy Location Plugin.

Integrating the Location Plugin

Choose the integration method for the LocationKit plugin based on how IMKit is imported.

  • Using CocoaPods

    Framework (requires SDK ≥ 5.2.3)

    pod 'RongCloudIM/LocationKit',' x.y.z'     # Location plugin  

    Source code (requires IMKit also as source code, SDK ≥ 5.2.3)

    pod 'RongCloudOpenSource/LocationKit', 'x.y.z'   # Location plugin  
  • Manual integration. If IMKit source code is manually imported into your project, download the LocationKit plugin from the [RC SDK Download Page]. Since IMKit's LocationKit plugin depends on the Location library provided by IMLib SDK, you also need to add the Location library. You can choose to import the Location Framework via CocoaPods or use the RongLocation.xcframework file from the download package.

    pod 'RongCloudIM/Location',' x.y.z'     # IMLib SDK Location base library  
tip

x.y.z represents the specific version. Check the latest version on the [RC SDK Download Page] or CocoaPods repository.

Sending Location Messages

After integrating the location plugin, a location message entry will automatically appear in the extension panel. Users can tap the + button on the right side of the input bar to expand the panel and tap the location icon to send a location message.

extension(width=250)

By default, the location plugin only supports tap-to-send.

Using Real-Time Location Sharing

IMKit SDK does not provide a real-time location sharing plugin. You can refer to the code in the RC sample app SealTalk to implement real-time location sharing in your app.

tip

User avatars and nicknames for participants in real-time location sharing must be provided by your app to IMKit. For details, see Setting User Information.

alt(width=250) alt(width=250)

The following steps demonstrate how to implement location sharing in a one-to-one chat session using the SealTalk sample app's open-source code. After completing these steps, real-time location sharing will be available in one-to-one chats. You can also refer to [RCDChatViewController.m] in SealTalk.

  1. Download the SealTalk project source code and import the Sections/Chat/RealTimeLocation folder into your project.

    https://github.com/rongcloud/sealtalk-ios  
  2. Import the following headers in the chat view controller.

    #import <objc/runtime.h>  
    #import "RealTimeLocationEndCell.h"
    #import "RealTimeLocationStartCell.h"
    #import "RealTimeLocationStatusView.h"
    #import "RealTimeLocationViewController.h"
    #import "RealTimeLocationDefine.h"
    static const char *kRealTimeLocationKey = "kRealTimeLocationKey";
    static const char *kRealTimeLocationStatusViewKey = "kRealTimeLocationStatusViewKey";
  3. Set up relevant properties.

    @interface RCDChatViewController () < RCRealTimeLocationObserver,  
    RealTimeLocationStatusViewDelegate>

    @property(nonatomic, weak) id<RCRealTimeLocationProxy> realTimeLocation;

    @property(nonatomic, strong) RealTimeLocationStatusView *realTimeLocationStatusView;

    @end
  4. Get the location sharing service and register messages.

    - (void)viewDidLoad{  
    [super viewDidLoad];
    [self registerRealTimeLocationCell];
    [self getRealTimeLocationProxy];
    }
    - (void)registerRealTimeLocationCell {  
    [self initRealTimeLocationStatusView];
    [self registerClass:[RealTimeLocationStartCell class] forMessageClass:[RCRealTimeLocationStartMessage class]];
    [self registerClass:[RealTimeLocationEndCell class] forMessageClass:[RCRealTimeLocationEndMessage class]];
    }

    - (void)getRealTimeLocationProxy {
    __weak typeof(self) weakSelf = self;
    [[RCRealTimeLocationManager sharedManager] getRealTimeLocationProxy:self.conversationType
    targetId:self.targetId
    success:^(id<RCRealTimeLocationProxy> realTimeLocation){
    weakSelf.realTimeLocation = realTimeLocation;
    [weakSelf.realTimeLocation addRealTimeLocationObserver:weakSelf];
    [weakSelf updateRealTimeLocationStatus];
    } error:^(RCRealTimeLocationErrorCode status) {
    NSLog(@"get location share failure with code %d",(int)status);
    }];
    }

    - (void)initRealTimeLocationStatusView {
    self.realTimeLocationStatusView =
    [[RealTimeLocationStatusView alloc] initWithFrame:CGRectMake(0, 62, self.view.frame.size.width, 0)];
    self.realTimeLocationStatusView.delegate = self;
    [self.view addSubview:self.realTimeLocationStatusView];
    }
  5. Show real-time location sharing options when the location icon is tapped.

    //RCDChatViewController Class  
    - (void)pluginBoardView:(RCPluginBoardView *)pluginBoardView clickedItemWithTag:(NSInteger)tag {
    switch (tag) {
    case PLUGIN_BOARD_ITEM_LOCATION_TAG: {
    if (self.realTimeLocation) {
    [RCActionSheetView showActionSheetView:nil cellArray:@[RTLLocalizedString(@"send_location"), RTLLocalizedString(@"location_share")]
    cancelTitle:RTLLocalizedString(@"cancel")
    selectedBlock:^(NSInteger index) {
    if (index == 0) {
    [super pluginBoardView:self.chatSessionInputBarControl.pluginBoardView
    clickedItemWithTag:PLUGIN_BOARD_ITEM_LOCATION_TAG];
    }else{
    [self showRealTimeLocationViewController];
    }
    } cancelBlock:^{

    }];
    } else {
    [super pluginBoardView:pluginBoardView clickedItemWithTag:tag];
    }
    }break;
    default:
    [super pluginBoardView:pluginBoardView clickedItemWithTag:tag];
    break;
    }
    }
  6. Real-time location sharing delegate methods.

    - (void)onRealTimeLocationStatusChange:(RCRealTimeLocationStatus)status {  
    __weak typeof(self) weakSelf = self;
    dispatch_async(dispatch_get_main_queue(), ^{
    [weakSelf updateRealTimeLocationStatus];
    });
    }

    - (void)onReceiveLocation:(CLLocation *)location type:(RCRealTimeLocationType)type fromUserId:(NSString *)userId {
    __weak typeof(self) weakSelf = self;
    dispatch_async(dispatch_get_main_queue(), ^{
    [weakSelf updateRealTimeLocationStatus];
    });
    }

    - (void)onParticipantsJoin:(NSString *)userId {
    __weak typeof(self) weakSelf = self;
    if ([userId isEqualToString:[RCCoreClient sharedCoreClient].currentUserInfo.userId]) {
    [self notifyParticipantChange:RTLLocalizedString(@"you_join_location_share")];
    } else {
    [[RCIM sharedRCIM]
    .userInfoDataSource
    getUserInfoWithUserId:userId
    completion:^(RCUserInfo *userInfo) {
    if (userInfo.name.length) {
    [weakSelf notifyParticipantChange:[NSString stringWithFormat:RTLLocalizedString(@"someone_join_share_location"),userInfo.name]];
    } else {
    [weakSelf notifyParticipantChange:[NSString stringWithFormat:RTLLocalizedString(@"user_join_share_location"),userId]];
    }
    }];
    }
    }

    - (void)onParticipantsQuit:(NSString *)userId {
    __weak typeof(self) weakSelf = self;
    if ([userId isEqualToString:[RCCoreClient sharedCoreClient].currentUserInfo.userId]) {
    [self notifyParticipantChange:RTLLocalizedString(@"you_quit_location_share")];
    } else {
    [[RCIM sharedRCIM]
    .userInfoDataSource
    getUserInfoWithUserId:userId
    completion:^(RCUserInfo *userInfo) {
    if (userInfo.name.length) {
    [weakSelf
    notifyParticipantChange:[NSString stringWithFormat:RTLLocalizedString(@"someone_quit_location_share"),userInfo.name]];
    } else {
    [weakSelf
    notifyParticipantChange:[NSString stringWithFormat:RTLLocalizedString(@"user_quit_location_share"),userId]];
    }
    }];
    }
    }

    - (void)onRealTimeLocationStartFailed:(long)messageId {
    dispatch_async(dispatch_get_main_queue(), ^{
    for (int i = 0; i < self.conversationDataRepository.count; i++) {
    RCMessageModel *model = [self.conversationDataRepository objectAtIndex:i];
    if (model.messageId == messageId) {
    model.sentStatus = SentStatus_FAILED;
    }
    }
    NSArray *visibleItem = [self.conversationMessageCollectionView indexPathsForVisibleItems];
    for (int i = 0; i < visibleItem.count; i++) {
    NSIndexPath *indexPath = visibleItem[i];
    RCMessageModel *model = [self.conversationDataRepository objectAtIndex:indexPath.row];
    if (model.messageId == messageId) {
    [self.conversationMessageCollectionView reloadItemsAtIndexPaths:@[ indexPath ]];
    }
    }
    });
    }

    - (void)notifyParticipantChange:(NSString *)text {
    __weak typeof(self) weakSelf = self;
    dispatch_async(dispatch_get_main_queue(), ^{
    [weakSelf.realTimeLocationStatusView updateText:text];
    [weakSelf performSelector:@selector(updateRealTimeLocationStatus) withObject:nil afterDelay:0.5];
    });
    }

    - (void)onFailUpdateLocation:(NSString *)description {

    }

    #pragma mark - Real-time location sharing status view delegate methods
    - (void)onJoin {
    [self showRealTimeLocationViewController];
    }

    - (RCRealTimeLocationStatus)getStatus {
    return [self.realTimeLocation getStatus];
    }

    - (void)onShowRealTimeLocationView {
    [self showRealTimeLocationViewController];
    }

    - (void)setRealTimeLocation:(id<RCRealTimeLocationProxy>)realTimeLocation{
    objc_setAssociatedObject(self, kRealTimeLocationKey, realTimeLocation, OBJC_ASSOCIATION_ASSIGN);
    }

    - (id<RCRealTimeLocationProxy>)realTimeLocation {
    return objc_getAssociatedObject(self, kRealTimeLocationKey);
    }

    - (void)setRealTimeLocationStatusView:(RealTimeLocationStatusView *)realTimeLocationStatusView {
    objc_setAssociatedObject(self, kRealTimeLocationStatusViewKey, realTimeLocationStatusView,OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }

    - (RealTimeLocationStatusView *)realTimeLocationStatusView {
    return objc_getAssociatedObject(self, kRealTimeLocationStatusViewKey);
    }

    // Show the real-time location sharing view
    - (void)showRealTimeLocationViewController {
    RealTimeLocationViewController *lsvc = [[RealTimeLocationViewController alloc] init];
    lsvc.realTimeLocationProxy = self.realTimeLocation;
    if ([self.realTimeLocation getStatus] == RC_REAL_TIME_LOCATION_STATUS_INCOMING) {
    [self.realTimeLocation joinRealTimeLocation];
    } else if ([self.realTimeLocation getStatus] == RC_REAL_TIME_LOCATION_STATUS_IDLE) {
    [self.realTimeLocation startRealTimeLocation];
    }
    lsvc.modalPresentationStyle = UIModalPresentationFullScreen;
    [self.navigationController presentViewController:lsvc
    animated:YES
    completion:^{

    }];
    }

    // Update real-time location sharing status
    - (void)updateRealTimeLocationStatus {
    if (self.realTimeLocation) {
    [self.realTimeLocationStatusView updateRealTimeLocationStatus];
    __weak typeof(self) weakSelf = self;
    NSArray *participants = nil;
    switch ([self.realTimeLocation getStatus]) {
    case RC_REAL_TIME_LOCATION_STATUS_OUTGOING:
    [self.realTimeLocationStatusView updateText:RTLLocalizedString(@"you_location_sharing")];
    break;
    case RC_REAL_TIME_LOCATION_STATUS_CONNECTED:
    case RC_REAL_TIME_LOCATION_STATUS_INCOMING:
    participants = [self.realTimeLocation getParticipants];
    if (participants.count == 1) {
    NSString *userId = participants[0];
    [weakSelf.realTimeLocationStatusView
    updateText:[NSString stringWithFormat:RTLLocalizedString(@"user_location_sharing"), userId]];
    [[RCIM sharedRCIM]
    .userInfoDataSource
    getUserInfoWithUserId:userId
    completion:^(RCUserInfo *userInfo) {
    if (userInfo.name.length) {
    dispatch_async(dispatch_get_main_queue(), ^{
    [weakSelf.realTimeLocationStatusView updateText:[NSString stringWithFormat:RTLLocalizedString(@"someone_location_sharing"),userInfo.name]];
    });
    }
    }];
    } else {
    if (participants.count < 1)
    [self.realTimeLocationStatusView removeFromSuperview];
    else
    [self.realTimeLocationStatusView
    updateText:[NSString stringWithFormat:@"%d people sharing locations", (int)participants.count]];
    }
    break;
    default:
    break;
    }
    }
    }
  7. (Optional) Add the following configuration to the RCConfig.plist file in IMLibCore to enable real-time location sharing in group chats. Group chats support up to 5 participants simultaneously.

    <key>RealTimeLocationShare</key>  
    <dict>
    <key>SupportConversationTypes</key>
    <array>
    <string>1</string>
    <string>3</string>
    </array>
    </dict>

Upgrading the Legacy Location Plugin

tip

When upgrading from IMKit versions below 5.2.3 to 5.2.3, the legacy location plugin will no longer function.

  • Before 5.2.3, IMKit included a built-in location plugin.
  • Starting with 5.2.3, IMKit no longer includes the location plugin. If location functionality is not needed, you can avoid integrating the LocationKit plugin to prevent App Store review issues related to location permissions.
  • When upgrading from IMKit versions below 5.2.3 to 5.2.3, the original location-sending functionality will cease to work.

Customization

Adjusting Location Thumbnail Compression

When sending location messages from the client, the SDK automatically generates a preview map image, uploads it to the file server (default: Qiniu Cloud Storage), and includes the remote URL of the image in the message body before sending. The SDK compresses the image to a maximum width of 408 pixels and height of 240 pixels.

Generally, it is not recommended to modify the SDK's default compression settings. To adjust compression quality, refer to the knowledge base article How to Modify SDK Default Image and Video Compression Settings.

Customizing Location Message UI

Location messages (non-real-time location sharing messages) are displayed in the message list using RCLocationMessageCell. To customize the built-in message style, create a custom message Cell and provide it to the SDK. All message templates in IMKit inherit from RCMessageCell, so custom message Cells must also inherit from RCMessageCell. For details, see Modifying Message Display Styles.

You can also directly replace the style resources, string resources, and icon resources referenced in the message display template within `RongCloud