Use atproto actions with ease in iOS shortcuts
at main 5.3 kB view raw
1// 2// FileIntentToImageQuery.swift 3// shortcut 4// 5// Created by Bailey Townsend on 6/30/25. 6// 7 8import ATProtoKit 9import AppIntents 10import CoreGraphics 11import Foundation 12import ImageIO 13import Photos 14import UIKit 15 16public func IntentFileToImageQuery(files: [IntentFile], altText: [String]) async throws 17 -> [ATProtoTools.ImageQuery] 18{ 19 var imageQueries: [ATProtoTools.ImageQuery] = [] 20 21 for (index, image) in files.enumerated() { 22 23 if image.availableContentTypes.contains(.heic) { 24 let heicImage = try await image.withFile( 25 contentType: .heic, 26 fileHandler: { url, openInPlace in 27 guard 28 let image = UIImage(contentsOfFile: url.absoluteURL.path()) 29 else { 30 throw GenericIntentError.message( 31 "Failed to load image \(image.filename).") 32 } 33 return image 34 } 35 ) 36 let fileName = image.filename 37 if let jpegData = heicImage.jpegData(compressionQuality: 0.1) { 38 imageQueries.append( 39 40 ATProtoTools.ImageQuery( 41 imageData: jpegData, fileName: fileName, 42 altText: altText[safe: index], 43 aspectRatio: AppBskyLexicon.Embed.AspectRatioDefinition( 44 width: Int(heicImage.size.width), 45 height: Int(heicImage.size.height)) 46 )) 47 } 48 49 } else if image.availableContentTypes.contains(.jpeg) { 50 let jpeg = try await image.withFile( 51 contentType: .jpeg, 52 fileHandler: { url, openInPlace in 53 guard 54 let image = UIImage(contentsOfFile: url.absoluteURL.path()) 55 else { 56 throw GenericIntentError.message( 57 "Failed to load image \(image.filename).") 58 } 59 return image 60 } 61 ) 62 if let jpegData = jpeg.jpegData(compressionQuality: 0.1) { 63 imageQueries.append( 64 ATProtoTools.ImageQuery( 65 imageData: jpegData, fileName: image.filename, 66 altText: altText[safe: index], 67 aspectRatio: AppBskyLexicon.Embed.AspectRatioDefinition( 68 width: Int(jpeg.size.width), height: Int(jpeg.size.height)) 69 )) 70 } 71 72 } else if image.availableContentTypes.contains(.png) { 73 let png = try await image.withFile( 74 contentType: .png, 75 fileHandler: { url, openInPlace in 76 guard 77 let image = UIImage(contentsOfFile: url.absoluteURL.path()) 78 else { 79 throw GenericIntentError.message( 80 "Failed to load image \(image.filename).") 81 } 82 return image 83 } 84 ) 85 86 if let jpegData = png.jpegData(compressionQuality: 0.1) { 87 imageQueries.append( 88 ATProtoTools.ImageQuery( 89 imageData: jpegData, fileName: image.filename, 90 altText: altText[safe: index], 91 aspectRatio: AppBskyLexicon.Embed.AspectRatioDefinition( 92 width: Int(png.size.width), height: Int(png.size.height)) 93 )) 94 } 95 96 } else { 97 //Just yolo it 98 imageQueries.append( 99 ATProtoTools.ImageQuery( 100 imageData: image.data, fileName: image.filename, 101 altText: altText[safe: index], aspectRatio: nil 102 )) 103 } 104 } 105 return imageQueries 106} 107 108private func extractCaptionFromImageData(_ data: Data) -> String? { 109 guard let imageSource = CGImageSourceCreateWithData(data as CFData, nil) else { 110 return nil 111 } 112 113 guard let metadata = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil) as? [String: Any] 114 else { 115 return nil 116 } 117 118 // Check EXIF UserComment 119 // if let exifDict = metadata[kCGImagePropertyExifDictionary as String] as? [String: Any], 120 // let userComment = exifDict[kCGImagePropertyExifUserComment as String] as? String 121 // { 122 // return userComment.isEmpty ? nil : userComment 123 // } 124 125 // Check IPTC Caption 126 // if let iptcDict = metadata[kCGImagePropertyIPTCDictionary as String] as? [String: Any], 127 // let caption = iptcDict[kCGImagePropertyIPTCCaptionAbstract as String] as? String 128 // { 129 // return caption.isEmpty ? nil : caption 130 // } 131 132 // Check TIFF ImageDescription 133 if let tiffDict = metadata[kCGImagePropertyTIFFDictionary as String] as? [String: Any], 134 let description = tiffDict[kCGImagePropertyTIFFImageDescription as String] as? String 135 { 136 return description.isEmpty ? nil : description 137 } 138 139 return nil 140} 141 142extension Array { 143 subscript(safe index: Int) -> Element? { 144 return indices.contains(index) ? self[index] : nil 145 } 146}