Use atproto actions with ease in iOS shortcuts
1//
2// PersistentUserSessionRegistry.swift
3// shortcut
4//
5// Created by Bailey Townsend on 6/25/25.
6//
7
8import ATProtoKit
9import Foundation
10import SwiftData
11
12@MainActor
13public struct PersistentUserSessionRegistry: UserSessionRegistry {
14
15 private var modelContext: ModelContext
16
17 init(modelContext: ModelContext) {
18 self.modelContext = modelContext
19 }
20
21 public func register(_ id: UUID, session: UserSession) async {
22 let model = UserSessionToModel(id, session: session)
23 self.modelContext.insert(model)
24 do {
25 try self.modelContext.save()
26 } catch {
27 print("Error saving user session: \(error)")
28 }
29
30 }
31
32 public func getSession(for id: UUID) async -> UserSession? {
33 let descriptor = FetchDescriptor<UserSessionModel>(
34 predicate: #Predicate { $0.id == id }
35 )
36
37 do {
38 let models = try self.modelContext.fetch(descriptor)
39 guard let model = models.first else { return nil }
40 return ModelToUserSession(model)
41 } catch {
42 return nil
43 }
44 }
45
46 public func containsSession(for id: UUID) async -> Bool {
47 let descriptor = FetchDescriptor<UserSessionModel>(
48 predicate: #Predicate { $0.id == id }
49 )
50
51 do {
52 let count = try self.modelContext.fetchCount(descriptor)
53 return count > 0
54 } catch {
55 return false
56 }
57 }
58
59 public func removeSession(for id: UUID) async {
60 let descriptor = FetchDescriptor<UserSessionModel>(
61 predicate: #Predicate { $0.id == id }
62 )
63
64 do {
65 let models = try self.modelContext.fetch(descriptor)
66 for model in models {
67 self.modelContext.delete(model)
68 }
69 try self.modelContext.save()
70 } catch {
71 // Handle error silently or log as needed
72 }
73 }
74
75 public func removeAllSessions() async {
76 let descriptor = FetchDescriptor<UserSessionModel>()
77
78 do {
79 let models = try self.modelContext.fetch(descriptor)
80 for model in models {
81 self.modelContext.delete(model)
82 }
83 try self.modelContext.save()
84 } catch {
85 // Handle error silently or log as needed
86 }
87 }
88
89 public func getAllSessions() async -> [UUID: UserSession] {
90 let descriptor = FetchDescriptor<UserSessionModel>()
91
92 do {
93 let models = try self.modelContext.fetch(descriptor)
94 var sessions: [UUID: UserSession] = [:]
95
96 for model in models {
97 let session = ModelToUserSession(model)
98 sessions[model.id] = session
99 }
100
101 return sessions
102 } catch {
103 return [:]
104 }
105 }
106
107}
108
109private func ModelToUserSession(_ model: UserSessionModel) -> UserSession {
110 return UserSession(
111 handle: model.handle,
112 sessionDID: model.sessionDID,
113 email: model.email,
114 isEmailConfirmed: model.isEmailConfirmed,
115 isEmailAuthenticationFactorEnabled: model.isEmailAuthenticationFactorEnabled,
116 // didDocument: model.didDocument,
117 isActive: model.isActive,
118 status: model.status,
119 serviceEndpoint: model.serviceEndpoint,
120 pdsURL: model.pdsURL
121 )
122}
123
124private func UserSessionToModel(_ id: UUID, session: UserSession) -> UserSessionModel {
125 return UserSessionModel(
126 sessionId: id,
127 handle: session.handle,
128 sessionDID: session.sessionDID,
129 email: session.email,
130 isEmailConfirmed: session.isEmailConfirmed,
131 isEmailAuthenticationFactorEnabled: session.isEmailAuthenticationFactorEnabled,
132 // didDocument: session.didDocument,
133 isActive: session.isActive,
134 status: session.status,
135 serviceEndpoint: session.serviceEndpoint,
136 pdsURL: session.pdsURL)
137}