Use atproto actions with ease in iOS shortcuts
1//
2// GetARecordIntent.swift
3// shortcut
4//
5// Created by Bailey Townsend on 6/30/25.
6//
7
8import ATProtoKit
9import AppIntents
10import SwiftData
11import SwiftUI
12
13struct GetARecordIntent: AppIntent {
14
15 @Parameter(
16 title: "Repo",
17 description: "The handle or DID of the repo"
18 )
19 var atIdentifier: String
20
21 @Parameter(
22 title: "Collection",
23 description: "The collection you want to write to, like app.bsky.feed.post")
24 var collection: String
25
26 @Parameter(
27 title: "Record Key",
28 description:
29 "The record key for record",
30 )
31 var recordKey: String
32
33 @Parameter(
34 title: "Record CID",
35 description:
36 "The CID of the version of the record. If not specified, then return the most recent version",
37 default: nil
38 )
39 var cid: String?
40
41 static let title: LocalizedStringResource = "Get a Record"
42
43 static let description: IntentDescription = IntentDescription(
44 stringLiteral:
45 "Get a single record from a repository. Does not require auth"
46 )
47
48 static var parameterSummary: some ParameterSummary {
49 Summary(
50 "Get a record from \(\.$collection) in \(\.$atIdentifier)'s repo with the Record key \(\.$recordKey)"
51 ) {
52 \.$cid
53 }
54 }
55
56 func perform() async throws -> some ReturnsValue<RecordAppEntity> {
57 let atProtoManager = AtProtocolManager()
58 let lowerCaseCollection = self.collection.lowercased()
59 let lowerCaseAtIdentifier = self.atIdentifier.lowercased()
60
61 let info = try await GetRemoteInfo.getRemoteRepoInfo(
62 possibleRepo: lowerCaseAtIdentifier, atProtoManager: atProtoManager)
63
64 do {
65 let response = try await atProtoManager.getARecord(
66 pdsURL: info.pdsURL,
67 repositoryDID: info.repo,
68 collection: lowerCaseCollection,
69 recordKey: self.recordKey,
70 recordCID: self.cid)
71
72 let record: RecordAppEntity = RecordAppEntity()
73 record.cid = response.cid
74 record.uri = response.uri
75 if let attempt = try response.value?.toJSON() {
76 record.value = IntentFile(
77 data: attempt, filename: "\(record.uri).json")
78 }
79
80 return .result(
81 value: record)
82 } catch let shortCutError as ShortcutErrors {
83 switch shortCutError {
84 case .NoSession:
85 throw GenericIntentError.message("No session found")
86 case .ErrorCreatingARecord(let errorMessage):
87 throw GenericIntentError.message(errorMessage)
88 case .AuthError(let authError):
89 throw GenericIntentError.message(authError)
90 }
91 } catch let shortCutError as GenericIntentError {
92 throw shortCutError
93 } catch {
94 throw GenericIntentError.general
95 }
96 }
97
98}