Utilize the Event Timeline API

Accessing some of the API classes that are mentioned on this page requires additional Sentiance SDK dependencies. See this page for more information.

On this page, you can find examples of how to query the Sentiance SDK for historic timeline events, and how to register to receive real time timeline updates in your app, as new events are detected or existing ones are updated.

Query for Historic Events

let from = Date.distantPast
let to = Date.distantFuture

Sentiance.shared.getTimelineEvents(from: from, to: to).forEach { event in
    print("Event ID: \(event.eventId)")
    print("Started on: \(event.startDate)")
    print("Ended on: \(event.endDate)")
    
    if event.type == .inTransport {
        let transport =  event as! SENTTransportEvent
        
        print("Type: transport")
        print("Mode: \(transport.transportMode)")
        
        if let distance = transport.distanceInMeters {
            print("Distance: \(distance)")
        }
        
        print("Waypoints: \(transport.waypoints)")
        
    }
    else if event.type == .stationary {
        let stationary =  event as! SENTStationaryEvent
        
        print("Type: stationary")
        print("Location: \(stationary.location)")
        print("Venue: \(stationary.venue)")
    }
    else if event.type == .offTheGrid {
        print("Type: off-the-grid")
    }
    else {
        print("Type: unknown")
    }
}

Subscribe for Event Timeline Updates

public class EventTimelineUpdateReceiver: EventTimelineDelegate {
    private let LAST_UPDATE_KEY = "SentianceTimelineEventLastUpdateTime"
    private let timelineStore: TimelineStore
    private let queue: OperationQueue
    
    init() {
        timelineStore = TimelineStore() // your own store implementation
        queue = OperationQueue()
        queue.maxConcurrentOperationCount = 1
    }    
        
    public func listenForUpdates() {
        // Save a local copy of the most recent event update time, before 
        // setting the delegate below. This way, we make sure that this 
        // value does not change by the delegate method, by the time we query
        // for updates.
        let mostRecentEventUpdateTime = self.mostRecentEventUpdateTime
                
        // Set the delegate to start listening for timeline updates.
        Sentiance.shared.eventTimelineDelegate = self
        
        // Finally, let's make sure we haven't missed any events since the 
        // last time our app ran (e.g. due to unexpected app termination).
        let afterDate = Date(timeIntervalSince1970: mostRecentEventUpdateTime)
        Sentiance.shared.getTimelineUpdates(after: afterDate)
          .forEach { [weak self] timelineEvent in
            self?.updateOrInsert(event: timelineEvent, 
                                 updateMostRecentEventUpdateTime: false)
        }
    }
    
    public func onEventTimelineUpdate(event: SENTTimelineEvent) {
        // Note: this delegate method invocation happens on the 
        // main/UI thread.
        updateOrInsert(event: event, updateMostRecentEventUpdateTime: true)
    }
    
    private func updateOrInsert(event: SENTTimelineEvent,
                                updateMostRecentEventUpdateTime: Bool) {
        // The non-concurrent queue makes sure we don't process incoming 
        // updates via the delegate method and the result of calling
        // getTimelineUpdates() at the same time. Plus, it makes sure
        // we do the processing off of the main thread.
        queue.addOperation { [weak self] in
            guard let self = self else { return }
            
            if let existingEvent = timelineStore.get(id: event.eventId) {
                // Make sure the event we're about to update isn't older 
                // than the one we already have.
                if (existingEvent.lastUpdateDate.timeIntervalSince1970 < 
                     event.lastUpdateDate.timeIntervalSince1970) {
                    timelineStore.update(event: event)
                }
            } else {
                timelineStore.insert(event: event)
            }
            
            if updateMostRecentEventUpdateTime {
                // Note: events arriving via the onEventTimelineUpdate 
                // delegate method have a monotonically increasing 
                // lastUpdateDate.
                self.mostRecentEventUpdateTime 
                   = event.lastUpdateDate.timeIntervalSince1970
            }
        }
    }
    
    // We keep track of the most recent lastUpdateDate of the received
    // events, so that on the next run, we can use it to process missed
    // events (see listenForUpdates()).
    private var mostRecentEventUpdateTime : TimeInterval {
        get {
            // We use UserDefaults. But if your app uses the iOS
            // DataProtection capability, this won't work for you
            // when the device is locked. Use something that will
            // be accessible in this case, such as the keychain or
            // a file with a specific protection option.
            let defaults = UserDefaults.standard
            var updateTime = defaults.double(forKey: LAST_UPDATE_KEY)
            if updateTime == 0 {
                updateTime = Date().timeIntervalSince1970
                self.mostRecentEventUpdateTime = updateTime
            }
            return updateTime
        }
        set {
            let defaults = UserDefaults.standard
            defaults.set(newValue, forKey: LAST_UPDATE_KEY)
        }
    }
}

Last updated