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.
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 the eventType field (a free-form string) and the metadata field (a customizable JSON object), you can implement a wide range of social features:
By creatively using these fields, you can build rich social features that perfectly match your app’s requirements without being limited to a rigid event structure.
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.
Copy
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)")}
The 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 event
description: Detailed information about the event
location: Where the event will take place
startTime and endTime: When the event begins and ends
metadata: Additional custom data as key-value pairs
isPublic: Determines whether the event is visible to all users or only specific contacts
// Create a social post similar to Instagramlet 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)
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.
Copy
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)")}
Event details may need to be modified after creation. Changes to time, location, or other details can be easily updated using the update method.
Copy
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)")}
The 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.
The SDK provides different feed types to display events from users. These feeds are essential for creating engaging social experiences within your app.
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.
Copy
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)")}
The “For You” feed includes all public events in the user’s network, not limited to people they follow.
Copy
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)")}
When building specialized feeds, you may want to filter events by type. This can be done by processing the events returned by the feed methods:
Copy
// Get only social post eventsfunc getSocialPosts() async throws -> [SocialEvent] { let feed = try await ContactsService.shared.socialService.getFeed( skip: 0, limit: 50 ) // Filter to only include social posts return feed.items.filter { $0.eventType == "social_post" }}// Get only transaction eventsfunc getTransactions() async throws -> [SocialEvent] { let feed = try await ContactsService.shared.socialService.getFeed( skip: 0, limit: 50 ) // Filter to only include transactions return feed.items.filter { $0.eventType == "transaction" }}
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
Copy
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) }}
This example demonstrates how to:
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