Skip to content
This repository has been archived by the owner on Jan 11, 2024. It is now read-only.

Latest commit

 

History

History
executable file
·
343 lines (283 loc) · 15.7 KB

File metadata and controls

executable file
·
343 lines (283 loc) · 15.7 KB

BlackBerry Spark Communications Services

Location Sharing App for iOS

The Location Sharing example application demonstrates how to share a user's real-time location with others using BlackBerry Spark Communications Services. The application monitors the user's location updates using the iOS CoreLocation APIs. The user's locations are then shared with other users in a chat using chat messages. The application defines a custom message type that includes the latitude and longitude coordinates of the user's location. The application marks all of the the user's locations on a map with pins.


Integrate

Demo video: Integrate location sharing into your Apps

Features

The following features are demonstrated:

  • Starting a new chat and sharing a user's location using a chat message with custom type
  • Extracting a user's location from a chat message and showing it on a map

Getting Started

This example requires the Spark Communications SDK, which you can find along with related resources at the location below.

YouTube Getting Started Video

Getting started video

Configuration

By default, this example application is configured to work in a domain with user authentication disabled and the BlackBerry Key Management Service enabled. See the Download & Configure section of the Developer Guide to get started configuring a domain in the sandbox.

Once you have a domain in the sandbox, edit Location Sharing's ConfigSettings.plist file to configure the example with your domain ID.

<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
	<key>authProvider</key>
	<string>testAuth<string>
	<key>useBlackBerryKMS</key>
	<true/>
	<key>testAuth</key>
	<dict>
		<key>clientId</key>
		<string>not_used</string>
		<key>domain</key>
		<string>UPDATE_WITH_YOUR_DOMAIN</string>
		<key>environment</key>
		<string>sandbox</string>
	</dict>
</dict>
</plist>

When you run Location Sharing, it will prompt you for a user ID and a password. Since you've configured your domain to have user authentication disabled, you can enter any string you like for the user ID and an SDK identity will be created for it. Other applications that you run in the same domain will be able to find this identity by this user ID. The password is used to protected the keys stored in the BlackBerry Key Management Service.

Walkthrough

The following explains how to use the SDK to send location information embedded in chat messages.

Getting Started

The SDK is used to create chats and send chat messages with custom application data embedded in them. In this application, the location of each participant in a chat is shared with other participants. As the user's location changes, a new chat messages is sent with the new location. These locations are then displayed on a map. Tapping on a location pin displays the time it was shared and the name of the user who shared it.

Starting a chat

To start a chat, the regIds of the participants are needed. The class BBMChatCreator is used to create a new chat.

- (void)startChatWithRegIds:(NSArray *)regIds subject:(NSString *)subject
{
    if(regIds.count <= 0) {
        return;
    }
    //User chat creator to start a chat with one or multiple contacts
    [self.chatCreator startConferenceWithRegIds:regIds subject:subject callback:^(NSString *chatId, BBMChatStartFailedMessageReason failReason) {
        if(chatId)
        {
            //If a chat with the given regId already exists there is no need to create a new one.
            self.lastChatId = chatId;
            //Show the chat screen
            [self performSegueWithIdentifier:@"ShowChatSegue" sender:self];
        }
        else {
            NSLog(@"Chat creation failed failReason = %d", failReason);
        }
    }];
}

Sharing current location

The current location is obtained by using CLLocationManager. When there is a location change, a new chat message that includes the latitude and longitude of the user is created and sent.

- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations
{
    CLLocation *location = [locations lastObject];
    NSDictionary *locationData = @{kLatitudeKey : [NSString stringWithFormat:@"%f", location.coordinate.latitude],
                                   kLongitudeKey : [NSString stringWithFormat:@"%f", location.coordinate.longitude]};
    if([locationData[kLatitudeKey] isEqualToString:self.previousLocationData[kLatitudeKey]] &&
       [locationData[kLongitudeKey] isEqualToString:self.previousLocationData[kLongitudeKey]]) {
        return;
    }
    // Send the new location data to all chats.
    for (NSString *chatId in self.chatIds) {
        [self sendLocation:locationData toChatId:chatId];
    }
    self.previousLocationData = locationData;
}

The location data is a dictionary that contains the latitude and the longitude. This is embedded in the rawData field in a BBMChatMessageSendMessage object and then sent. The tag for the messages is set to the custom value "Location".

- (void)sendLocation:(NSDictionary *)locationData toChatId:(NSString *)chatId
{
    BBMChatMessageSendMessage *msg = [[BBMChatMessageSendMessage alloc] initWithChatId:chatId tag:kMessageTag_Location];
    msg.rawData = locationData;
    [[BBMEnterpriseService service] sendMessageToService:msg];
}

Showing locations on a map

The first step to showing locations on a map is to load the location messages for a chat. This is done in the class LocationMessageLoader by creating an instance of BBMChatMessageSendMessage and setting the tag and chatId properties. This is then passed to the model and a list containing the chat messages that match the criteria is returned. For this application we need to sort messages by user so the regId is used for that purpose. The sort operation is done inside a monitor so that changes are detected and the messages are sorted by user again.

-(void)observeMessages
{
    //We are only interested in messages whose tag is set to kMessageTag_Location. To fetch such
    //messages an instance of BBMChatMessageCriteria is created and the chatId and tag values are set.
    BBMChatMessageCriteria *chatMessageCriteria = [[BBMChatMessageCriteria alloc] init];
    chatMessageCriteria.tag = kMessageTag_Location;
    chatMessageCriteria.chatId = self.chat.chatId;

    self.chatMessageList = [[BBMAccess model] chatMessageWithCriteria:chatMessageCriteria];
    typeof(self) __weak weakSelf = self;
    __block NSUInteger messageCount = 0;
    self.messageMonitor = [ObservableMonitor monitorActivatedWithName:@"messageMonitor" block:^{
        //If the number of messages in the list changes, the monitor will trigger.
        if(messageCount != weakSelf .chatMessageList.count) {
            [weakSelf sortMessagesByRegId];
            messageCount = weakSelf.chatMessageList.count;
        }
     }];
}

-(void)sortMessagesByRegId {
    NSMutableDictionary *locationMessagesByRegId = [[NSMutableDictionary alloc] init];
    //Wait for the list to be current to avoid unnecessary processing.
    if(self.chatMessageList.bbmState == kBBMStatePending) {
        return;
    }
    for(BBMChatMessage *message in self.chatMessageList ) {
        //Timed message that have expired are marked as deleted so they are still displayed as expired messages.
        if([message.tag isEqualToString:kMessageTag_Location]) {
            NSNumber *regId = message.resolvedSenderUri.regId;
            NSMutableArray *locationMessagesForRegid = locationMessagesByRegId[regId];
            if(!locationMessagesForRegid) {
                locationMessagesForRegid = [[NSMutableArray alloc] init];
            }
            //Add message to array
            [locationMessagesForRegid addObject:message];
            locationMessagesByRegId[regId] = locationMessagesForRegid;
        }
    }
    if(self.callback) {
        self.callback(locationMessagesByRegId);
    }
}

Every time a new location is loaded the map view gets refreshed.

-(void)observeMessages
{
    typeof(self) __weak weakSelf = self;
    //This loads the location messages for this chat. Every time a new message arrives this
    //block will run.
    self.messageLoader = [LocationMessageLoader messageLoaderForConversation:self.chat callback:^(NSDictionary *locationMessagesByRegId) {
        //Update the map with the locations received from the message loader
        weakSelf.locationMessagesByRegId = locationMessagesByRegId;
        [weakSelf refreshMap];
    }];
}

The latitude and longitude are stored in the rawData property in every instance of BBMChatMessage.

- (void)setMessage:(BBMChatMessage *)message
{
    _message = message;
    
    if ([message.tag isEqualToString:kMessageTag_Location]) {
        [self willChangeValueForKey:@"coordinate"];
        self.locationData = message.rawData;
        [self didChangeValueForKey:@"coordinate"];
    }
    
    [self willChangeValueForKey:@"title"];
    self.user = [[LocationSharingApp application].userManager userForRegId:_message.resolvedSenderUri.regId];
    [self didChangeValueForKey:@"title"];
}

- (CLLocationCoordinate2D)coordinate
{
    CLLocationCoordinate2D coord;
    coord.latitude = [self.locationData[kLatitudeKey] doubleValue];
    coord.longitude = [self.locationData[kLongitudeKey] doubleValue];
    return coord;
}

There are two kinds of items drawn on the map: overlays and annotations. Overlays are used to draw lines connecting each of the locations of a user. Annotations are used to display a pin at each location. Tapping one of these pins will bring up the name and email address of the user as well as the time at which the location was generated.

- (void)refreshMap
{
    //Remove old overlays(lines) and annotations(pins)
    [self.mapView removeOverlays:self.mapView.overlays];
    [self.mapView removeAnnotations:self.mapView.annotations];

    for(NSNumber *regId in [self.locationMessagesByRegId allKeys]) {
        //The locations for each user will be connected by a line, each user has its own color.
        self.currentColor = [LocationMapAnnotation colorForRegId:regId];

        NSArray *locationMessages = self.locationMessagesByRegId[regId];
        //coordinates will be used to draw the lines.
        CLLocationCoordinate2D coordinates[locationMessages.count];
        for(int i=0; i < locationMessages.count; i++) {
            //Each message contains a latitude and longitude
            BBMChatMessage *message = locationMessages[i];
            coordinates[i] = CLLocationCoordinate2DMake(message.latitude, message.longitude);

            //Either a pin for each message is displayed or just for the most recent one.
            if(self.showAllLocations || i == locationMessages.count - 1) {
                LocationMapAnnotation *annotation  = [[LocationMapAnnotation alloc] init];
                [self.mapView addAnnotation:annotation];
                annotation.message = message;
            }
        }

        //Draw lines to connect each user's locations
        MKPolyline *polyLine = [MKPolyline polylineWithCoordinates:coordinates count:locationMessages.count];
        [self.mapView addOverlay:polyLine];
    }
}

- (MKOverlayRenderer *)mapView:(MKMapView *)mapView rendererForOverlay:(id<MKOverlay>)overlay
{
    if ([overlay isKindOfClass:[MKPolyline class]])
    {
        MKPolylineRenderer *renderer = [[MKPolylineRenderer alloc] initWithPolyline:overlay];
        renderer.strokeColor = self.currentColor;
        renderer.lineWidth   = 2;

        return renderer;
    }

    return nil;
}

There is a toggle option to display a pin for each location or just for the latest known location.

- (IBAction)toggle:(id)sender
{
    self.showAllLocations = !self.showAllLocations;
    self.showAllLocationsBarButton.title = self.showAllLocations ? @"View last location" : @"View all locations" ;
    [self refreshMap];
}

License

These examples are released as Open Source and licensed under the Apache 2.0 License.

These examples were created using SDKs from Apple Inc. and may contain code licensed for use only with Apple products. Please review your Apple SDK Agreement for additional details.

This page includes icons from: https://material.io/icons/ used under the Apache 2.0 License.

Reporting Issues and Feature Requests

If you find a issue in one of the Samples or have a Feature Request, simply file an issue.