Events
The ContactsManager SDK provides comprehensive event management features to enhance your app with social events and activities. Events can be used for scheduling meetups, announcing product launches, organizing team activities, or any other social interactions within your app. This guide explains how to implement these features using the SDK.Event Flexibility
The event system in ContactsManager is designed to be highly flexible, allowing you to model various types of social interactions beyond traditional calendar events. Using theeventType field (a free-form string) and the metadata field (a customizable JSON object), you can implement a wide range of social features:
Use Cases for Events
- Calendar Events: Traditional meetups, appointments, or gatherings with time and location
- Social Posts: Status updates, photos, or announcements similar to Instagram or Facebook posts
- Activity Feed Items: Transactions or actions like those in Venmo (“Alex paid Bob $20”)
- Product Launches: Announcements of new features or products
- Milestones: Achievements or important dates for users
- Content Recommendations: Suggested articles, videos, or other content
- Group Activities: Team events, group challenges, or collaborative projects
Customizing Events with Metadata
Themetadata field accepts any JSON data, making it extremely versatile:
// Social post with image URL
let postMetadata: [String: Any] = [
"postType": "image",
"imageUrl": "https://example.com/images/vacation.jpg",
"filter": "summer",
"likes": 0,
"commentsEnabled": true
]
// Transaction activity
let transactionMetadata: [String: Any] = [
"amount": 25.50,
"currency": "USD",
"transactionId": "tx_12345",
"category": "dining"
]
// Product recommendation
let recommendationMetadata: [String: Any] = [
"productId": "prod_789",
"discount": "15%",
"expiryDate": "2025-05-01",
"targetSegment": "new_users"
]
// Social post with image URL
val postMetadata = mapOf(
"postType" to "image",
"imageUrl" to "https://example.com/images/vacation.jpg",
"filter" to "summer",
"likes" to 0,
"commentsEnabled" to true
)
// Transaction activity
val transactionMetadata = mapOf(
"amount" to 25.50,
"currency" to "USD",
"transactionId" to "tx_12345",
"category" to "dining"
)
// Product recommendation
val recommendationMetadata = mapOf(
"productId" to "prod_789",
"discount" to "15%",
"expiryDate" to "2025-05-01",
"targetSegment" to "new_users"
)
// Social post with image URL
const postMetadata = {
postType: "image",
imageUrl: "https://example.com/images/vacation.jpg",
filter: "summer",
likes: 0,
commentsEnabled: true
};
// Transaction activity
const transactionMetadata = {
amount: 25.50,
currency: "USD",
transactionId: "tx_12345",
category: "dining"
};
// Product recommendation
const recommendationMetadata = {
productId: "prod_789",
discount: "15%",
expiryDate: "2025-05-01",
targetSegment: "new_users"
};
// Social post with image URL
NSDictionary *postMetadata = @{
@"postType": @"image",
@"imageUrl": @"https://example.com/images/vacation.jpg",
@"filter": @"summer",
@"likes": @0,
@"commentsEnabled": @YES
};
// Transaction activity
NSDictionary *transactionMetadata = @{
@"amount": @25.50,
@"currency": @"USD",
@"transactionId": @"tx_12345",
@"category": @"dining"
};
// Product recommendation
NSDictionary *recommendationMetadata = @{
@"productId": @"prod_789",
@"discount": @"15%",
@"expiryDate": @"2025-05-01",
@"targetSegment": @"new_users"
};
Creating an Event
Creating events allows users to share activities and gatherings with their network. Events contain essential information like title, location, time, and visibility settings that determine who can see them.do {
let eventData = CreateEventRequest(
eventType: "meeting",
title: "Team Sync",
description: "Weekly team synchronization",
location: "Conference Room A",
startTime: Date().addingTimeInterval(86400), // Tomorrow
endTime: Date().addingTimeInterval(90000), // Tomorrow + 1 hour
metadata: ["department": "Engineering"],
isPublic: true
)
let result = try await ContactsService.shared.socialService.createEvent(
eventData: eventData
)
if let eventId = result.eventId, let created = result.created, created {
print("Created event with ID: \(eventId)")
}
} catch {
print("Error creating event: \(error.localizedDescription)")
}
// Get instance of SocialService
val socialService = SocialService.getInstance(context)
// Create event data
val eventData = CreateEventRequest(
eventType = "meeting",
title = "Team Sync",
description = "Weekly team synchronization",
location = "Conference Room A",
startTime = Calendar.getInstance().apply { add(Calendar.DAY_OF_YEAR, 1) }.time,
endTime = Calendar.getInstance().apply { add(Calendar.DAY_OF_YEAR, 1); add(Calendar.HOUR, 1) }.time,
metadata = mapOf("department" to "Engineering"),
isPublic = true
)
// Launch in a coroutine
lifecycleScope.launch {
try {
val result = socialService.createEvent(eventData).getOrThrow()
if (result.eventId != null && result.created == true) {
println("Created event with ID: ${result.eventId}")
}
} catch (e: Exception) {
println("Error creating event: ${e.message}")
}
}
import { SocialService } from '@contactsmanager/rn';
try {
const eventData = {
eventType: 'meeting',
title: 'Team Sync',
description: 'Weekly team synchronization',
location: 'Conference Room A',
startTime: new Date(Date.now() + 86400000), // Tomorrow
endTime: new Date(Date.now() + 90000000), // Tomorrow + 1 hour
metadata: { department: 'Engineering' },
isPublic: true
};
const result = await SocialService.createEvent(eventData);
if (result.eventId && result.created) {
console.log(`Created event with ID: ${result.eventId}`);
}
} catch (error) {
console.error('Error creating event:', error);
}
// Create event data
CMCreateEventRequest *eventData = [[CMCreateEventRequest alloc] init];
eventData.eventType = @"meeting";
eventData.title = @"Team Sync";
eventData.description = @"Weekly team synchronization";
eventData.location = @"Conference Room A";
eventData.startTime = [[NSDate date] dateByAddingTimeInterval:86400]; // Tomorrow
eventData.endTime = [[NSDate date] dateByAddingTimeInterval:90000]; // Tomorrow + 1 hour
eventData.metadata = @{@"department": @"Engineering"};
eventData.isPublic = YES;
// Get social service from contact service
CMSocialService *socialService = [CMContactService sharedInstance].socialService;
// Create event
[socialService createEvent:eventData
completion:^(CMEventActionResponse * _Nullable response, NSError * _Nullable error) {
if (error) {
NSLog(@"Error creating event: %@", error.localizedDescription);
return;
}
if (response.eventId && response.created) {
NSLog(@"Created event with ID: %@", response.eventId);
}
}];
CreateEventRequest model includes these key properties:
eventType: A categorical identifier for the event (e.g., meeting, social, post, transaction)title: The main title of the eventdescription: Detailed information about the eventlocation: Where the event will take placestartTimeandendTime: When the event begins and endsmetadata: Additional custom data as key-value pairsisPublic: Determines whether the event is visible to all users or only specific contacts
Example: Creating a Social Post Event
// Create a social post similar to Instagram
let socialPostEvent = CreateEventRequest(
eventType: "social_post",
title: "Beach day!",
description: "Having an amazing time at Malibu",
location: "Malibu Beach, CA",
startTime: Date(), // Post time is now
metadata: [
"postType": "photo",
"imageUrl": "https://example.com/photos/beach.jpg",
"filter": "vivid",
"mentions": ["user_123", "user_456"],
"hashtags": ["summer", "beach", "vacation"]
],
isPublic: true
)
// Create a social post similar to Instagram
val socialPostEvent = CreateEventRequest(
eventType = "social_post",
title = "Beach day!",
description = "Having an amazing time at Malibu",
location = "Malibu Beach, CA",
startTime = Date(), // Post time is now
metadata = mapOf(
"postType" to "photo",
"imageUrl" to "https://example.com/photos/beach.jpg",
"filter" to "vivid",
"mentions" to listOf("user_123", "user_456"),
"hashtags" to listOf("summer", "beach", "vacation")
),
isPublic = true
)
lifecycleScope.launch {
try {
val result = socialService.createEvent(socialPostEvent).getOrThrow()
if (result.eventId != null && result.created == true) {
println("Created social post with ID: ${result.eventId}")
}
} catch (e: Exception) {
println("Error creating social post: ${e.message}")
}
}
// Create a social post similar to Instagram
const socialPostEvent = {
eventType: 'social_post',
title: 'Beach day!',
description: 'Having an amazing time at Malibu',
location: 'Malibu Beach, CA',
startTime: new Date(), // Post time is now
metadata: {
postType: 'photo',
imageUrl: 'https://example.com/photos/beach.jpg',
filter: 'vivid',
mentions: ['user_123', 'user_456'],
hashtags: ['summer', 'beach', 'vacation']
},
isPublic: true
};
try {
const result = await SocialService.createEvent(socialPostEvent);
console.log(`Created social post with ID: ${result.eventId}`);
} catch (error) {
console.error('Error creating social post:', error);
}
// Create a social post similar to Instagram
CMCreateEventRequest *socialPostEvent = [[CMCreateEventRequest alloc] init];
socialPostEvent.eventType = @"social_post";
socialPostEvent.title = @"Beach day!";
socialPostEvent.description = @"Having an amazing time at Malibu";
socialPostEvent.location = @"Malibu Beach, CA";
socialPostEvent.startTime = [NSDate date]; // Post time is now
socialPostEvent.metadata = @{
@"postType": @"photo",
@"imageUrl": @"https://example.com/photos/beach.jpg",
@"filter": @"vivid",
@"mentions": @[@"user_123", @"user_456"],
@"hashtags": @[@"summer", @"beach", @"vacation"]
};
socialPostEvent.isPublic = YES;
Example: Creating a Transaction Activity
// Create a transaction event like Venmo
let transactionEvent = CreateEventRequest(
eventType: "transaction",
title: "Dinner payment",
description: "Thanks for dinner last night!",
startTime: Date(),
metadata: [
"amount": 45.50,
"currency": "USD",
"paymentMethod": "credit_card",
"category": "dining",
"recipients": ["user_789"],
"emoji": "🍕"
],
isPublic: true
)
// Create a transaction event like Venmo
val transactionEvent = CreateEventRequest(
eventType = "transaction",
title = "Dinner payment",
description = "Thanks for dinner last night!",
startTime = Date(),
metadata = mapOf(
"amount" to 45.50,
"currency" to "USD",
"paymentMethod" to "credit_card",
"category" to "dining",
"recipients" to listOf("user_789"),
"emoji" to "🍕"
),
isPublic = true
)
lifecycleScope.launch {
try {
val result = socialService.createEvent(transactionEvent).getOrThrow()
if (result.eventId != null && result.created == true) {
println("Created transaction with ID: ${result.eventId}")
}
} catch (e: Exception) {
println("Error creating transaction: ${e.message}")
}
}
// Create a transaction event like Venmo
const transactionEvent = {
eventType: 'transaction',
title: 'Dinner payment',
description: 'Thanks for dinner last night!',
startTime: new Date(),
metadata: {
amount: 45.50,
currency: 'USD',
paymentMethod: 'credit_card',
category: 'dining',
recipients: ['user_789'],
emoji: '🍕'
},
isPublic: true
};
try {
const result = await SocialService.createEvent(transactionEvent);
console.log(`Created transaction with ID: ${result.eventId}`);
} catch (error) {
console.error('Error creating transaction:', error);
}
// Create a transaction event like Venmo
CMCreateEventRequest *transactionEvent = [[CMCreateEventRequest alloc] init];
transactionEvent.eventType = @"transaction";
transactionEvent.title = @"Dinner payment";
transactionEvent.description = @"Thanks for dinner last night!";
transactionEvent.startTime = [NSDate date];
transactionEvent.metadata = @{
@"amount": @45.50,
@"currency": @"USD",
@"paymentMethod": @"credit_card",
@"category": @"dining",
@"recipients": @[@"user_789"],
@"emoji": @"🍕"
};
transactionEvent.isPublic = YES;
Retrieving Event Details
Once an event is created, users can view its details. This is useful for displaying event information on dedicated event pages or when users receive event invitations.do {
let event = try await ContactsService.shared.socialService.getEvent(
eventId: "event-id"
)
print("Event: \(event.title)")
print("Type: \(event.eventType)")
print("Description: \(event.description ?? "No description")")
print("Location: \(event.location ?? "No location")")
if let startTime = event.startTime {
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .short
print("Start time: \(formatter.string(from: startTime))")
}
// Access custom metadata
if let metadata = event.metadata as? [String: Any] {
// Handle different event types based on the metadata
switch event.eventType {
case "social_post":
if let imageUrl = metadata["imageUrl"] as? String {
print("Post image: \(imageUrl)")
}
if let hashtags = metadata["hashtags"] as? [String] {
print("Hashtags: \(hashtags.joined(separator: ", "))")
}
case "transaction":
if let amount = metadata["amount"] as? Double,
let currency = metadata["currency"] as? String {
print("Amount: \(amount) \(currency)")
}
default:
print("Other event type with metadata: \(metadata)")
}
}
} catch {
print("Error retrieving event: \(error.localizedDescription)")
}
lifecycleScope.launch {
try {
val event = socialService.getEvent("event-id").getOrThrow()
println("Event: ${event.title}")
println("Type: ${event.eventType}")
println("Description: ${event.description ?: "No description"}")
println("Location: ${event.location ?: "No location"}")
event.startTime?.let { startTime ->
val formatter = SimpleDateFormat("MMM d, yyyy h:mm a", Locale.getDefault())
println("Start time: ${formatter.format(startTime)}")
}
// Access custom metadata
event.metadata?.let { metadata ->
when (event.eventType) {
"social_post" -> {
metadata["imageUrl"]?.let { imageUrl ->
println("Post image: $imageUrl")
}
(metadata["hashtags"] as? List<String>)?.let { hashtags ->
println("Hashtags: ${hashtags.joinToString(", ")}")
}
}
"transaction" -> {
val amount = metadata["amount"] as? Double
val currency = metadata["currency"] as? String
if (amount != null && currency != null) {
println("Amount: $amount $currency")
}
}
else -> println("Other event type with metadata: $metadata")
}
}
} catch (e: Exception) {
println("Error retrieving event: ${e.message}")
}
}
import { SocialService } from '@contactsmanager/rn';
try {
const event = await SocialService.getEvent('event-id');
console.log(`Event: ${event.title}`);
console.log(`Type: ${event.eventType}`);
console.log(`Description: ${event.description || 'No description'}`);
console.log(`Location: ${event.location || 'No location'}`);
if (event.startTime) {
const date = new Date(event.startTime);
console.log(`Start time: ${date.toLocaleString()}`);
}
// Access custom metadata
if (event.metadata) {
switch (event.eventType) {
case 'social_post':
if (event.metadata.imageUrl) {
console.log(`Post image: ${event.metadata.imageUrl}`);
}
if (event.metadata.hashtags) {
console.log(`Hashtags: ${event.metadata.hashtags.join(', ')}`);
}
break;
case 'transaction':
if (event.metadata.amount && event.metadata.currency) {
console.log(`Amount: ${event.metadata.amount} ${event.metadata.currency}`);
}
break;
default:
console.log('Other event type with metadata:', event.metadata);
}
}
} catch (error) {
console.error('Error retrieving event:', error);
}
[socialService getEvent:@"event-id"
completion:^(CMSocialEvent * _Nullable event, NSError * _Nullable error) {
if (error) {
NSLog(@"Error retrieving event: %@", error.localizedDescription);
return;
}
NSLog(@"Event: %@", event.title);
NSLog(@"Type: %@", event.eventType);
NSLog(@"Description: %@", event.description ?: @"No description");
NSLog(@"Location: %@", event.location ?: @"No location");
if (event.startTime) {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateStyle = NSDateFormatterMediumStyle;
formatter.timeStyle = NSDateFormatterShortStyle;
NSLog(@"Start time: %@", [formatter stringFromDate:event.startTime]);
}
// Access custom metadata
if (event.metadata) {
if ([event.eventType isEqualToString:@"social_post"]) {
NSString *imageUrl = event.metadata[@"imageUrl"];
if (imageUrl) {
NSLog(@"Post image: %@", imageUrl);
}
NSArray *hashtags = event.metadata[@"hashtags"];
if (hashtags) {
NSLog(@"Hashtags: %@", [hashtags componentsJoinedByString:@", "]);
}
} else if ([event.eventType isEqualToString:@"transaction"]) {
NSNumber *amount = event.metadata[@"amount"];
NSString *currency = event.metadata[@"currency"];
if (amount && currency) {
NSLog(@"Amount: %@ %@", amount, currency);
}
} else {
NSLog(@"Other event type with metadata: %@", event.metadata);
}
}
}];
Updating an Event
Event details may need to be modified after creation. Changes to time, location, or other details can be easily updated using the update method.do {
let updateData = UpdateEventRequest(
title: "Updated Team Sync",
description: "Updated weekly team synchronization",
location: "Virtual Meeting Room",
startTime: Date().addingTimeInterval(90000),
endTime: Date().addingTimeInterval(93600),
metadata: ["department": "Engineering", "priority": "High"],
isPublic: true
)
let result = try await ContactsService.shared.socialService.updateEvent(
eventId: "event-id",
eventData: updateData
)
if let success = result.success, success {
print("Successfully updated event")
}
} catch {
print("Error updating event: \(error.localizedDescription)")
}
// Get instance of SocialService
val socialService = SocialService.getInstance(context)
// Create update data
val updateData = UpdateEventRequest(
title = "Updated Team Sync",
description = "Updated weekly team synchronization",
location = "Virtual Meeting Room",
startTime = Calendar.getInstance().apply { add(Calendar.DAY_OF_YEAR, 1) }.time,
endTime = Calendar.getInstance().apply { add(Calendar.DAY_OF_YEAR, 1); add(Calendar.HOUR, 1) }.time,
metadata = mapOf("department" to "Engineering", "priority" to "High"),
isPublic = true
)
// Launch in a coroutine
lifecycleScope.launch {
try {
val result = socialService.updateEvent("event-id", updateData).getOrThrow()
if (result.success == true) {
println("Successfully updated event")
}
} catch (e: Exception) {
println("Error updating event: ${e.message}")
}
}
import { SocialService } from '@contactsmanager/rn';
try {
const updateData = {
title: 'Updated Team Sync',
description: 'Updated weekly team synchronization',
location: 'Virtual Meeting Room',
startTime: new Date(Date.now() + 90000000),
endTime: new Date(Date.now() + 93600000),
metadata: { department: 'Engineering', priority: 'High' },
isPublic: true
};
const result = await SocialService.updateEvent('event-id', updateData);
if (result.success) {
console.log('Successfully updated event');
}
} catch (error) {
console.error('Error updating event:', error);
}
// Create update data
CMUpdateEventRequest *updateData = [[CMUpdateEventRequest alloc] init];
updateData.title = @"Updated Team Sync";
updateData.description = @"Updated weekly team synchronization";
updateData.location = @"Virtual Meeting Room";
updateData.startTime = [[NSDate date] dateByAddingTimeInterval:90000];
updateData.endTime = [[NSDate date] dateByAddingTimeInterval:93600];
updateData.metadata = @{@"department": @"Engineering", @"priority": @"High"};
updateData.isPublic = YES;
// Get social service from contact service
CMSocialService *socialService = [CMContactService sharedInstance].socialService;
// Update event
[socialService updateEvent:@"event-id"
eventData:updateData
completion:^(CMEventActionResponse * _Nullable response, NSError * _Nullable error) {
if (error) {
NSLog(@"Error updating event: %@", error.localizedDescription);
return;
}
if (response.success) {
NSLog(@"Successfully updated event");
}
}];
UpdateEventRequest model is similar to the creation model, but all fields are optional. Only the properties you want to change need to be included, and existing properties will remain unchanged if not specified.
Example: Updating a Social Post Event
// Update a social post with likes and comments
let updateSocialPost = UpdateEventRequest(
metadata: [
"likes": 42,
"comments": [
["userId": "user_123", "text": "Amazing view!", "timestamp": Date().timeIntervalSince1970],
["userId": "user_456", "text": "Wish I was there!", "timestamp": Date().timeIntervalSince1970]
]
]
)
Deleting an Event
When events are cancelled or no longer needed, they can be removed from the system, which prevents them from appearing in feeds and search results.do {
let result = try await ContactsService.shared.socialService.deleteEvent(
eventId: "event-id"
)
if let success = result.success, success {
print("Successfully deleted event")
}
} catch {
print("Error deleting event: \(error.localizedDescription)")
}
lifecycleScope.launch {
try {
val result = socialService.deleteEvent("event-id").getOrThrow()
if (result.success == true) {
println("Successfully deleted event")
}
} catch (e: Exception) {
println("Error deleting event: ${e.message}")
}
}
import { SocialService } from '@contactsmanager/rn';
try {
const result = await SocialService.deleteEvent('event-id');
if (result.success) {
console.log('Successfully deleted event');
}
} catch (error) {
console.error('Error deleting event:', error);
}
[socialService deleteEvent:@"event-id"
completion:^(CMEventActionResponse * _Nullable response, NSError * _Nullable error) {
if (error) {
NSLog(@"Error deleting event: %@", error.localizedDescription);
return;
}
if (response.success) {
NSLog(@"Successfully deleted event");
}
}];
Event Feeds
The SDK provides different feed types to display events from users. These feeds are essential for creating engaging social experiences within your app.Following Feed
The Following Feed shows events from users the user follows, creating a personalized stream of relevant activities. This helps users stay informed about what people in their network are doing.do {
let feed = try await ContactsService.shared.socialService.getFeed(
skip: 0,
limit: 20
)
print("Your feed has \(feed.total) events")
for event in feed.items {
print("\(event.title) by \(event.creatorName ?? "Unknown")")
if let startTime = event.startTime {
let formatter = DateFormatter()
formatter.dateStyle = .medium
formatter.timeStyle = .short
print("When: \(formatter.string(from: startTime))")
}
// Handle different event types in the feed
if let metadata = event.metadata as? [String: Any] {
switch event.eventType {
case "social_post":
print("Post: \(event.description ?? "")")
if let imageUrl = metadata["imageUrl"] as? String {
// Load and display image
print("Image: \(imageUrl)")
}
case "transaction":
if let amount = metadata["amount"] as? Double,
let currency = metadata["currency"] as? String,
let emoji = metadata["emoji"] as? String {
print("\(emoji) \(amount) \(currency)")
}
default:
print("Event: \(event.eventType)")
}
}
}
} catch {
print("Error retrieving feed: \(error.localizedDescription)")
}
lifecycleScope.launch {
try {
val feed = socialService.getFeed(
skip = 0,
limit = 20
).getOrThrow()
println("Your feed has ${feed.total} events")
feed.items.forEach { event ->
println("${event.title} by ${event.creatorName ?: "Unknown"}")
event.startTime?.let { startTime ->
val formatter = SimpleDateFormat("MMM d, yyyy h:mm a", Locale.getDefault())
println("When: ${formatter.format(startTime)}")
}
// Handle different event types in the feed
event.metadata?.let { metadata ->
when (event.eventType) {
"social_post" -> {
println("Post: ${event.description ?: ""}")
if (metadata["imageUrl"] != null) {
// Load and display image
println("Image: ${metadata["imageUrl"]}")
}
}
"transaction" -> {
val amount = metadata["amount"] as? Double
val currency = metadata["currency"] as? String
val emoji = metadata["emoji"] as? String
if (amount != null && currency != null && emoji != null) {
println("$emoji $amount $currency")
}
}
else -> println("Event: ${event.eventType}")
}
}
}
} catch (e: Exception) {
println("Error retrieving feed: ${e.message}")
}
}
import { SocialService } from '@contactsmanager/rn';
try {
const feed = await SocialService.getFeed(0, 20);
console.log(`Your feed has ${feed.total} events`);
for (const event of feed.items) {
console.log(`${event.title} by ${event.creatorName || 'Unknown'}`);
if (event.startTime) {
const date = new Date(event.startTime);
console.log(`When: ${date.toLocaleString()}`);
}
// Handle different event types in the feed
if (event.metadata) {
switch (event.eventType) {
case 'social_post':
console.log(`Post: ${event.description || ''}`);
if (event.metadata.imageUrl) {
// Load and display image
console.log(`Image: ${event.metadata.imageUrl}`);
}
break;
case 'transaction':
if (event.metadata.amount && event.metadata.currency && event.metadata.emoji) {
console.log(`${event.metadata.emoji} ${event.metadata.amount} ${event.metadata.currency}`);
}
break;
default:
console.log(`Event: ${event.eventType}`);
}
}
}
} catch (error) {
console.error('Error retrieving feed:', error);
}
[socialService getFeedWithSkip:0
limit:20
completion:^(CMPaginatedEventList * _Nullable feed, NSError * _Nullable error) {
if (error) {
NSLog(@"Error retrieving feed: %@", error.localizedDescription);
return;
}
NSLog(@"Your feed has %ld events", (long)feed.total);
for (CMSocialEvent *event in feed.items) {
NSLog(@"%@ by %@", event.title, event.creatorName ?: @"Unknown");
if (event.startTime) {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateStyle = NSDateFormatterMediumStyle;
formatter.timeStyle = NSDateFormatterShortStyle;
NSLog(@"When: %@", [formatter stringFromDate:event.startTime]);
}
// Handle different event types in the feed
if (event.metadata) {
if ([event.eventType isEqualToString:@"social_post"]) {
NSString *description = event.description;
if (description) {
NSLog(@"Post: %@", description);
}
if (event.metadata[@"imageUrl"]) {
// Load and display image
NSLog(@"Image: %@", event.metadata[@"imageUrl"]);
}
} else if ([event.eventType isEqualToString:@"transaction"]) {
NSNumber *amount = event.metadata[@"amount"];
NSString *currency = event.metadata[@"currency"];
NSString *emoji = event.metadata[@"emoji"];
if (amount && currency && emoji) {
NSLog(@"%@ %@ %@", emoji, amount, currency);
}
} else {
NSLog(@"Event: %@", event.eventType);
}
}
}
}];
”For You” Feed
The “For You” feed includes all public events in the user’s network, not limited to people they follow.do {
let forYouFeed = try await ContactsService.shared.socialService.getForYouFeed(
skip: 0,
limit: 20
)
print("For You feed has \(forYouFeed.total) events")
for event in forYouFeed.items {
print("\(event.title) - \(event.eventType)")
}
} catch {
print("Error retrieving For You feed: \(error.localizedDescription)")
}
lifecycleScope.launch {
try {
val forYouFeed = socialService.getForYouFeed(
skip = 0,
limit = 20
).getOrThrow()
println("For You feed has ${forYouFeed.total} events")
forYouFeed.items.forEach { event ->
println("${event.title} - ${event.eventType}")
}
} catch (e: Exception) {
println("Error retrieving For You feed: ${e.message}")
}
}
import { SocialService } from '@contactsmanager/rn';
try {
const forYouFeed = await SocialService.getForYouFeed(0, 20);
console.log(`For You feed has ${forYouFeed.total} events`);
for (const event of forYouFeed.items) {
console.log(`${event.title} - ${event.eventType}`);
}
} catch (error) {
console.error('Error retrieving For You feed:', error);
}
[socialService getForYouFeedWithSkip:0
limit:20
completion:^(CMPaginatedEventList * _Nullable feed, NSError * _Nullable error) {
if (error) {
NSLog(@"Error retrieving For You feed: %@", error.localizedDescription);
return;
}
NSLog(@"For You feed has %ld events", (long)feed.total);
for (CMSocialEvent *event in feed.items) {
NSLog(@"%@ - %@", event.title, event.eventType);
}
}];
Upcoming Events
The Upcoming Events feed shows events scheduled in the future, helping users plan ahead.do {
let upcomingEvents = try await ContactsService.shared.socialService.getUpcomingEvents(
skip: 0,
limit: 20
)
print("Found \(upcomingEvents.total) upcoming events")
} catch {
print("Error retrieving upcoming events: \(error.localizedDescription)")
}
lifecycleScope.launch {
try {
val upcomingEvents = socialService.getUpcomingEvents(
skip = 0,
limit = 20
).getOrThrow()
println("Found ${upcomingEvents.total} upcoming events")
upcomingEvents.items.forEach { event ->
val formatter = SimpleDateFormat("MMM d, yyyy", Locale.getDefault())
val eventDate = formatter.format(event.startTime)
println("${event.title} - $eventDate")
}
} catch (e: Exception) {
println("Error retrieving upcoming events: ${e.message}")
}
}
import { SocialService } from '@contactsmanager/rn';
try {
const upcomingEvents = await SocialService.getUpcomingEvents(0, 20);
console.log(`Found ${upcomingEvents.total} upcoming events`);
for (const event of upcomingEvents.items) {
const eventDate = new Date(event.startTime);
console.log(`${event.title} - ${eventDate.toLocaleDateString()}`);
}
} catch (error) {
console.error('Error retrieving upcoming events:', error);
}
[socialService getUpcomingEventsWithSkip:0
limit:20
completion:^(CMPaginatedEventList * _Nullable events, NSError * _Nullable error) {
if (error) {
NSLog(@"Error retrieving upcoming events: %@", error.localizedDescription);
return;
}
NSLog(@"Found %ld upcoming events", (long)events.total);
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.dateStyle = NSDateFormatterMediumStyle;
formatter.timeStyle = NSDateFormatterNoStyle;
for (CMSocialEvent *event in events.items) {
NSString *eventDate = [formatter stringFromDate:event.startTime];
NSLog(@"%@ - %@", event.title, eventDate);
}
}];
User Events
This feed shows events created by a specific user, which is useful for profile pages.do {
let userEvents = try await ContactsService.shared.socialService.getUserEvents(
userId: "user-id",
skip: 0,
limit: 20
)
print("User has created \(userEvents.total) events")
} catch {
print("Error retrieving user events: \(error.localizedDescription)")
}
lifecycleScope.launch {
try {
val userEvents = socialService.getUserEvents(
userId = "user-id",
skip = 0,
limit = 20
).getOrThrow()
println("User has created ${userEvents.total} events")
userEvents.items.forEach { event ->
println("${event.title} (${event.eventType})")
}
} catch (e: Exception) {
println("Error retrieving user events: ${e.message}")
}
}
import { SocialService } from '@contactsmanager/rn';
try {
const userEvents = await SocialService.getUserEvents('user-id', 0, 20);
console.log(`User has created ${userEvents.total} events`);
for (const event of userEvents.items) {
console.log(`${event.title} (${event.eventType})`);
}
} catch (error) {
console.error('Error retrieving user events:', error);
}
[socialService getUserEventsWithUserId:@"user-id"
skip:0
limit:20
completion:^(CMPaginatedEventList * _Nullable events, NSError * _Nullable error) {
if (error) {
NSLog(@"Error retrieving user events: %@", error.localizedDescription);
return;
}
NSLog(@"User has created %ld events", (long)events.total);
for (CMSocialEvent *event in events.items) {
NSLog(@"%@ (%@)", event.title, event.eventType);
}
}];
skip and limit parameters, allowing you to implement infinite scrolling or load-more functionality.
Filtering Events by Type
The API provides server-side filtering by event type, which is more efficient than client-side filtering. Use theeventType parameter in feed requests to get only the events you need:
// Get only social post events
func getSocialPosts() async throws -> [SocialEvent] {
let feed = try await ContactsService.shared.socialService.getFeed(
skip: 0,
limit: 50,
eventType: "social_post" // Server-side filtering
)
return feed.items
}
// Get only transaction events
func getTransactions() async throws -> [SocialEvent] {
let feed = try await ContactsService.shared.socialService.getFeed(
skip: 0,
limit: 50,
eventType: "transaction" // Server-side filtering
)
return feed.items
}
// Get all event types (no filtering)
func getAllEvents() async throws -> [SocialEvent] {
let feed = try await ContactsService.shared.socialService.getFeed(
skip: 0,
limit: 50
// No eventType parameter = all events
)
return feed.items
}
// Get only social post events
suspend fun getSocialPosts(): List<SocialEvent> {
val feed = socialService.getFeed(
skip = 0,
limit = 50,
eventType = "social_post" // Server-side filtering
).getOrThrow()
return feed.items
}
// Get only transaction events
suspend fun getTransactions(): List<SocialEvent> {
val feed = socialService.getFeed(
skip = 0,
limit = 50,
eventType = "transaction" // Server-side filtering
).getOrThrow()
return feed.items
}
// Get all event types (no filtering)
suspend fun getAllEvents(): List<SocialEvent> {
val feed = socialService.getFeed(
skip = 0,
limit = 50
// No eventType parameter = all events
).getOrThrow()
return feed.items
}
// Get only social post events
const getSocialPosts = async (): Promise<SocialEvent[]> => {
const feed = await SocialService.getFeed(
0,
50,
'social_post' // Server-side filtering
);
return feed.items;
};
// Get only transaction events
const getTransactions = async (): Promise<SocialEvent[]> => {
const feed = await SocialService.getFeed(
0,
50,
'transaction' // Server-side filtering
);
return feed.items;
};
// Get all event types (no filtering)
const getAllEvents = async (): Promise<SocialEvent[]> => {
const feed = await SocialService.getFeed(
0,
50
// No eventType parameter = all events
);
return feed.items;
};
// Get only social post events
- (void)getSocialPostsWithCompletion:(void (^)(NSArray<CMSocialEvent *> *events, NSError *error))completion {
[socialService getFeedWithSkip:0
limit:50
eventType:@"social_post" // Server-side filtering
completion:^(CMPaginatedEventList *feed, NSError *error) {
if (error) {
completion(nil, error);
return;
}
completion(feed.items, nil);
}];
}
// Get only transaction events
- (void)getTransactionsWithCompletion:(void (^)(NSArray<CMSocialEvent *> *events, NSError *error))completion {
[socialService getFeedWithSkip:0
limit:50
eventType:@"transaction" // Server-side filtering
completion:^(CMPaginatedEventList *feed, NSError *error) {
if (error) {
completion(nil, error);
return;
}
completion(feed.items, nil);
}];
}
// Get all event types (no filtering)
- (void)getAllEventsWithCompletion:(void (^)(NSArray<CMSocialEvent *> *events, NSError *error))completion {
[socialService getFeedWithSkip:0
limit:50
eventType:nil // No filtering
completion:^(CMPaginatedEventList *feed, NSError *error) {
if (error) {
completion(nil, error);
return;
}
completion(feed.items, nil);
}];
}
Available Event Types
The API supports filtering by these common event types:"social_post"- Social media style posts with images, text, hashtags"transaction"- Payment or financial transactions"meeting"- Calendar events and appointments"activity"- General activities and milestones"announcement"- Product launches and announcements"recommendation"- Content or product recommendations
Performance Benefits
Server-side filtering provides several advantages over client-side filtering:- Reduced bandwidth: Only relevant events are transferred
- Better performance: No need to fetch and filter large datasets
- Consistent pagination: Skip/limit work correctly with filtered results
- Database optimization: Indexes can be used for efficient filtering
Building an Event Interface
Here’s an example of building an event detail view with SwiftUI, demonstrating how to create a polished and functional event interface:This example shows a complete event detail view with dynamic content based on event type. You can adapt this interface to match your app’s design and requirements.
EventDetailView.swift
struct EventDetailView: View {
let eventId: String
@State private var event: SocialEvent?
@State private var isLoading = true
@State private var error: Error?
var body: some View {
ScrollView {
if isLoading {
ProgressView("Loading event...")
.padding()
} else if let error = error {
VStack(spacing: 12) {
Image(systemName: "exclamationmark.triangle")
.font(.largeTitle)
.foregroundColor(.red)
Text("Failed to load event")
.font(.headline)
Text(error.localizedDescription)
.font(.subheadline)
.multilineTextAlignment(.center)
.foregroundColor(.secondary)
Button("Try Again") {
loadEvent()
}
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(8)
}
.padding()
} else if let event = event {
// Event header
VStack(alignment: .leading, spacing: 16) {
// Event type badge
Text(event.eventType.uppercased())
.font(.caption)
.padding(.horizontal, 12)
.padding(.vertical, 6)
.background(Color.blue.opacity(0.2))
.foregroundColor(.blue)
.cornerRadius(16)
// Title
Text(event.title)
.font(.largeTitle)
.fontWeight(.bold)
// Creator info
HStack {
Circle()
.fill(Color.gray.opacity(0.3))
.frame(width: 36, height: 36)
VStack(alignment: .leading) {
Text("Created by")
.font(.caption)
.foregroundColor(.secondary)
Text(event.creatorName ?? "Unknown")
.font(.subheadline)
}
Spacer()
// Public/private indicator
if event.isPublic {
Label("Public", systemImage: "globe")
.font(.subheadline)
} else {
Label("Private", systemImage: "lock")
.font(.subheadline)
}
}
Divider()
// Event time
if let startTime = event.startTime {
HStack(alignment: .top) {
Image(systemName: "calendar")
.frame(width: 24)
VStack(alignment: .leading) {
Text(formattedDate(startTime))
.font(.headline)
if let endTime = event.endTime {
Text("\(formattedTime(startTime)) - \(formattedTime(endTime))")
.font(.subheadline)
.foregroundColor(.secondary)
} else {
Text(formattedTime(startTime))
.font(.subheadline)
.foregroundColor(.secondary)
}
}
Spacer()
Button(action: {
// Add to calendar action
}) {
Text("Add to Calendar")
.font(.subheadline)
}
.buttonStyle(.bordered)
}
Divider()
}
// Location
if let location = event.location, !location.isEmpty {
HStack(alignment: .top) {
Image(systemName: "location.fill")
.frame(width: 24)
VStack(alignment: .leading) {
Text("Location")
.font(.caption)
.foregroundColor(.secondary)
Text(location)
.font(.subheadline)
}
Spacer()
Button(action: {
// Open maps action
}) {
Text("Directions")
.font(.subheadline)
}
.buttonStyle(.bordered)
}
Divider()
}
// Description
if let description = event.description, !description.isEmpty {
VStack(alignment: .leading, spacing: 8) {
Text("About")
.font(.headline)
Text(description)
.font(.body)
}
Divider()
}
// Custom metadata display based on event type
if let metadata = event.metadata as? [String: Any] {
VStack(alignment: .leading, spacing: 8) {
Text("Details")
.font(.headline)
// Render different UI elements based on event type
switch event.eventType {
case "social_post":
if let imageUrl = metadata["imageUrl"] as? String {
// This would be an AsyncImage in a real app
Text("Image: \(imageUrl)")
.font(.caption)
}
if let hashtags = metadata["hashtags"] as? [String] {
Text(hashtags.map { "#\($0)" }.joined(separator: " "))
.font(.caption)
.foregroundColor(.blue)
}
case "transaction":
if let amount = metadata["amount"] as? Double,
let currency = metadata["currency"] as? String {
HStack {
Text("Amount:")
Spacer()
Text("\(amount) \(currency)")
.fontWeight(.semibold)
}
}
if let category = metadata["category"] as? String {
HStack {
Text("Category:")
Spacer()
Text(category.capitalized)
}
}
default:
ForEach(Array(metadata.keys.sorted()), id: \.self) { key in
if let value = metadata[key] {
HStack {
Text("\(key.capitalized):")
Spacer()
Text("\(String(describing: value))")
}
}
}
}
}
Divider()
}
// Action buttons
HStack {
Button(action: {
// Share event
}) {
HStack {
Image(systemName: "square.and.arrow.up")
Text("Share")
}
.frame(maxWidth: .infinity)
}
.buttonStyle(.bordered)
Spacer()
.frame(width: 16)
Button(action: {
// RSVP action
}) {
HStack {
Image(systemName: "checkmark.circle")
Text("RSVP")
}
.frame(maxWidth: .infinity)
}
.buttonStyle(.borderedProminent)
}
}
.padding()
}
}
.navigationTitle("Event Details")
.navigationBarTitleDisplayMode(.inline)
.onAppear {
loadEvent()
}
}
private func loadEvent() {
isLoading = true
error = nil
Task {
do {
let loadedEvent = try await ContactsService.shared.socialService.getEvent(
eventId: eventId
)
await MainActor.run {
self.event = loadedEvent
self.isLoading = false
}
} catch {
await MainActor.run {
self.error = error
self.isLoading = false
}
}
}
}
private func formattedDate(_ date: Date) -> String {
let formatter = DateFormatter()
formatter.dateStyle = .long
formatter.timeStyle = .none
return formatter.string(from: date)
}
private func formattedTime(_ date: Date) -> String {
let formatter = DateFormatter()
formatter.dateStyle = .none
formatter.timeStyle = .short
return formatter.string(from: date)
}
}
- Load and display event details
- Handle loading states and errors
- Format date and time information
- Create an intuitive layout with proper visual hierarchy
- Implement action buttons for user interaction
- Dynamically display different UI based on event type and metadata
Best Practices for Events
- Validation: Implement client-side validation for event data to ensure all required fields are filled correctly before submission
- Timezone Handling: Be explicit about timezones when displaying event times to avoid confusion for users in different locations
- Cancellation Flow: Implement a proper flow for cancelling or rescheduling events, including notifications to affected users
- Event Reminders: Provide options for users to set reminders for events, improving user engagement and attendance
- Calendar Integration: Make it easy for users to add events to their device calendars with a simple tap
- Pagination: Implement proper pagination for event lists to maintain performance with large datasets
- Event Types: Create a consistent taxonomy of event types across your app for better filtering and display
- Metadata Structure: Document your metadata structure for different event types to ensure consistency
Troubleshooting
Common Issues
-
Date Formatting Issues
- Ensure proper date formatting across different locales by testing on various device settings
- Be explicit about timezones when working with event dates, especially for events with global attendance
-
Event Visibility
- Check the
isPublicflag when creating events to ensure proper visibility settings - Ensure proper permissions are set for viewing private events, especially in multi-user contexts
- Check the
-
Loading Performance
- Use pagination to improve performance when loading large event lists
- Implement caching for frequently accessed events to reduce network requests
- Consider prefetching upcoming events data to improve perceived performance