Files
linkding-ios/Marks/Services/AnalyticsService.swift
Krishna Kumar 96ea5fe6f6
All checks were successful
CI / build-and-deploy (push) Successful in 22s
feat(ai): on-device RAG over bookmarks + Spotlight indexing + unified logging
- SpotlightIndexer: actually push BookmarkEntity into the Spotlight index via
  indexAppEntities (IndexedEntity conformance alone indexes nothing). Wired into
  every sync/mutation point. This is what lets Apple Intelligence answer
  free-form Siri questions grounded in the user's bookmarks.
- On-device RAG: BookmarkAssistant (LanguageModelSession) + BookmarkSearchTool
  + SpotlightBookmarkSearch (CSSearchQuery retrieval), surfaced via AskView with
  lightweight Markdown rendering of answers.
- Log: os.Logger facility (com.magicive.marks) replacing ad-hoc print(); shared
  with ShareExtension. ClaudeService now logs server error bodies on non-2xx.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 12:55:32 -05:00

29 lines
1.4 KiB
Swift

import Foundation
enum Analytics {
static func track(_ event: String, _ properties: [String: Any] = [:]) {
// Serialize before crossing the task boundary Data is Sendable, [String: Any] is not
var body: [String: Any] = ["event": event]
if !properties.isEmpty { body["properties"] = properties }
guard let payload = try? JSONSerialization.data(withJSONObject: body) else { return }
Task.detached {
do {
let token = try await MarksAuth.validToken()
guard let url = URL(string: MarksAuth.baseURL + "/v1/marks/events") else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = payload
let (_, response) = try await URLSession.shared.data(for: request)
let status = (response as? HTTPURLResponse)?.statusCode ?? 0
if status < 200 || status > 299 {
Log.analytics.error("\(event, privacy: .public) → HTTP \(status, privacy: .public)")
}
} catch {
Log.analytics.error("\(event, privacy: .public) failed: \(error.localizedDescription, privacy: .public)")
}
}
}
}