Use atproto actions with ease in iOS shortcuts
at main 4.1 kB view raw
1// 2// CreateARecordIntent.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 UpdateProfileIntent: AppIntent { 14 15 @Parameter( 16 title: "AT Identifier", 17 description: 18 "The saved AT Identifier of the account you want to use to authenticate with and update the profile of", 19 requestValueDialog: IntentDialog("Choose a logged in AT Identifier")) 20 public var atIdentifier: AtIdentifierAppEntity 21 22 @Parameter( 23 title: "Display Name", 24 description: 25 "Enter a value if you would like to update your display name. Leave empty to not update your display name" 26 ) 27 var displayName: String? 28 29 @Parameter( 30 title: "Description", 31 description: 32 "Enter a value if you would like to update your profile's description. Leave empty to not update your profile's description", 33 default: nil) 34 var description: String? 35 36 @Parameter( 37 title: "Profile Picture", 38 description: "Updates your profile picture if provided, if not keeps the current one", 39 supportedContentTypes: [.jpeg, .png, .heic]) 40 var profilePic: IntentFile? 41 42 @Parameter( 43 title: "Banner Picture", 44 description: "Updates your banner picture if provided, if not keeps the current one", 45 supportedContentTypes: [.jpeg, .png, .heic]) 46 var bannerPic: IntentFile? 47 48 static let title: LocalizedStringResource = "Update your Bluesky profile" 49 50 static let description: IntentDescription = 51 "Updates your Bluesky profile with the provided data. If the field does not have a value it keeps the current value" 52 53 static var parameterSummary: some ParameterSummary { 54 Summary("Update \(\.$atIdentifier)'s profile") { 55 \.$displayName 56 \.$description 57 \.$profilePic 58 \.$bannerPic 59 } 60 } 61 62 func perform() async throws -> some ReturnsValue<StrongReferenceAppEntity> { 63 64 var profileUpdates: [ATProtoBluesky.UpdatedProfileRecordField] = [] 65 66 if let displayName = self.displayName { 67 profileUpdates.append(.displayName(with: displayName)) 68 } 69 70 if let description = self.description { 71 profileUpdates.append(.description(with: description)) 72 } 73 74 if let profilePic = self.profilePic { 75 let imageQuery = try await IntentFileToImageQuery(files: [profilePic], altText: []) 76 profileUpdates.append(.avatarImage(with: imageQuery.first)) 77 } 78 79 if let bannerPic = self.bannerPic { 80 let imageQuery = try await IntentFileToImageQuery(files: [bannerPic], altText: []) 81 profileUpdates.append(.bannerImage(with: imageQuery.first)) 82 } 83 84 if profileUpdates.isEmpty { 85 throw GenericIntentError.message("No updates provided") 86 } 87 88 let atProtoManager = AtProtocolManager() 89 90 do { 91 let recordref = try await atProtoManager.updateProfile( 92 sessionId: self.atIdentifier.id, usersDID: self.atIdentifier.did, 93 updates: profileUpdates) 94 95 let strongRef: StrongReferenceAppEntity = StrongReferenceAppEntity() 96 strongRef.recordCID = recordref.recordCID 97 strongRef.recordURI = recordref.recordURI 98 99 return .result( 100 value: strongRef) 101 } catch let shortCutError as ShortcutErrors { 102 switch shortCutError { 103 case .NoSession: 104 throw GenericIntentError.message("No session found") 105 case .ErrorCreatingARecord(let errorMessage): 106 throw GenericIntentError.message(errorMessage) 107 case .AuthError(let authError): 108 throw GenericIntentError.message(authError) 109 } 110 } catch let shortCutError as GenericIntentError { 111 throw shortCutError 112 } catch { 113 throw GenericIntentError.general 114 } 115 } 116}