Use atproto actions with ease in iOS shortcuts
at main 3.2 kB view raw
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 12import UniformTypeIdentifiers 13 14struct GetRepoIntent: AppIntent { 15 16 @Parameter( 17 title: "Repo", 18 description: "The handle or DID of the repo" 19 ) 20 var atIdentifier: String 21 22 @Parameter( 23 title: "Since", 24 description: "A TID created from a timestamp of since when to get the record") 25 var since: String? 26 27 static let title: LocalizedStringResource = "Get Repo" 28 29 static let description: IntentDescription = IntentDescription( 30 stringLiteral: 31 "Get's the users repo as a .CAR file. This can also be used as a backup of your repo, but will not include the account's blobs (pictures, videos, etc)" 32 ) 33 34 static var parameterSummary: some ParameterSummary { 35 Summary( 36 "Download a CAR export of \(\.$atIdentifier)'s repo" 37 ) { 38 \.$since 39 } 40 } 41 42 @Dependency 43 private var blobDownloader: BlobDownloader 44 45 func perform() async throws -> some ReturnsValue<RepoCarEntity> { 46 let atProtoManager = AtProtocolManager() 47 let lowerCaseAtIdentifier = self.atIdentifier.lowercased().trim() 48 let info = try await GetRemoteInfo.getRemoteRepoInfo( 49 possibleRepo: lowerCaseAtIdentifier, atProtoManager: atProtoManager) 50 51 let fileManager = FileManager.default 52 let saveLocation = fileManager.temporaryDirectory 53 let date = Date() 54 let formatter = ISO8601DateFormatter() 55 let utcString = formatter.string(from: date) 56 let fileName = "\(info.handle)-\(utcString).car" 57 58 do { 59 60 ///Just to be on the safe side 61 await self.blobDownloader.CancelAll() 62 let response = try await self.blobDownloader.getCar( 63 from: info.repo, since: self.since, fileManager: fileManager, 64 saveLocation: saveLocation, fileName: fileName, pdsURL: info.pdsURL) 65 let record: RepoCarEntity = RepoCarEntity() 66 67 record.car = IntentFile( 68 fileURL: response 69 ) 70 71 return .result( 72 value: record) 73 } catch let shortCutError as ShortcutErrors { 74 switch shortCutError { 75 case .NoSession: 76 throw GenericIntentError.message("No session found") 77 case .ErrorCreatingARecord(let errorMessage): 78 throw GenericIntentError.message(errorMessage) 79 case .AuthError(let authError): 80 throw GenericIntentError.message(authError) 81 } 82 } catch let shortCutError as GenericIntentError { 83 throw shortCutError 84 } catch let blobDownloadError as BlobDownloadError { 85 switch blobDownloadError { 86 case .apiError(let error): 87 throw GenericIntentError.message(error.message) 88 default: 89 throw GenericIntentError.general 90 } 91 } catch { 92 throw GenericIntentError.general 93 } 94 } 95 96}