Swift Testing の画像添付 API をプラットフォーム間で統合する
Consolidate Swift Testing’s image attachments API across platforms
このダイジェストはClaude Opus 4.7 / 4.8によって生成されたものです(License)。原文はこちら↗。
01 何が問題だったのか
Swift Testing の画像添付機能は、Apple プラットフォーム向けの ST-0014 と Windows 向けの ST-0015 で別々に導入されました。両者は同じ「画像をテストに添付する」という機能を提供するものの、画像を表すプロトコルがプラットフォームごとに分かれており(Apple は AttachableAsCGImage、Windows は AttachableAsIWICBitmapSource)、AttachableImageFormat の初期化子もプラットフォームごとに微妙に異なる API になっていました。
両プラットフォームでインターフェイスがそろっていないと、クロスプラットフォームで動くテストコードを書きづらく、ライブラリ側の実装やドキュメントもプラットフォームごとに分岐させる必要があります。また、ST-0014 で導入された AttachableImageFormat.init(_:encodingQuality:) は第 1 引数のラベルが省略されており、Windows 側で導入された init(encoderCLSID:encodingQuality:) と並べたときに引数ラベルの形が不揃いでした。
これらは Swift 6.3 で導入されたばかりの機能であり、開発者に広く出荷される前にインターフェイスを整える機会が残されている、というのが本 Proposal の出発点です。
02 どのように解決されるのか
画像添付の API を次の 3 点で整理します。
- プラットフォームごとに分かれていた
AttachableAsCGImageとAttachableAsIWICBitmapSourceを、単一のプロトコルAttachableAsImageに統合 AttachableImageFormatの Apple 向け初期化子に明示的な引数ラベルを与え、Equatable/Hashable/CustomStringConvertible/CustomDebugStringConvertibleへの適合を追加Attachmentに、画像として記録する際のAttachableImageFormatを取り出すためのimageFormatプロパティを追加
これらは Swift 6.3 でのリリース前の調整であり、ST-0014 / ST-0015 の AttachableAsCGImage / AttachableAsIWICBitmapSource や AttachableImageFormat.init(_:encodingQuality:) を利用していたコードには破壊的変更となります。
AttachableAsImage への統合
プラットフォーム固有のプロトコルである AttachableAsCGImage と AttachableAsIWICBitmapSource は撤去され、共通のプロトコル AttachableAsImage に置き換えられます。これに伴い、両プロトコルが持っていた次の要求も削除されます。
- var attachableCGImage: CGImage { get throws }
- func copyAttachableIWICBitmapSource() throws -> UnsafeMutablePointer<IWICBitmapSource>
代わりに、画像のエンコード操作そのものを表す要求が AttachableAsImage に置かれます。CoreGraphics オーバーレイと WinSDK オーバーレイがこの要求を実装するため、CGImage / CIImage / NSImage / UIImage や HBITMAP / HICON / IWICBitmapSource といった標準型はこれまでと同様にそのまま添付できます。要求は public なので、CGImage や IWICBitmapSource を内部で持つ独自の画像型に対して AttachableAsImage への適合を追加する用途にも使えます。
public protocol AttachableAsImage {
// ...
/// Encode a representation of this image in a given image format.
///
/// - Parameters:
/// - imageFormat: The image format to use when encoding this image.
/// - body: A function to call. A temporary buffer containing a data
/// representation of this instance is passed to it.
///
/// - Returns: Whatever is returned by `body`.
///
/// - Throws: Whatever is thrown by `body`, or any error that prevented the
/// creation of the buffer.
borrowing func withUnsafeBytes<R>(as imageFormat: AttachableImageFormat, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R
}
CGImage や IWICBitmapSource を内部に持つ独自の画像型に適合を与える場合は、内部の値の withUnsafeBytes(as:_:) に処理を委譲するだけで済みます。
import Testing
import CoreGraphics
struct MyImage {
var cgImage: CGImage
// ...
}
extension MyImage: AttachableAsImage {
func withUnsafeBytes<R>(as imageFormat: AttachableImageFormat, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
try cgImage.withUnsafeBytes(as: imageFormat, body)
}
}
AttachableImageFormat の調整
Apple プラットフォーム向けの AttachableImageFormat 初期化子は、第 1 引数に明示的なラベル contentType: を持つように改名されます。Windows 側の init(encoderCLSID:encodingQuality:) と引数ラベルの形をそろえるためです。
public struct AttachableImageFormat {
// ...
- public init(_ contentType: UTType, encodingQuality: Float = 1.0)
+ public init(contentType: UTType, encodingQuality: Float = 1.0)
}
加えて、AttachableImageFormat に次の適合が追加されます。
extension AttachableImageFormat: Equatable, Hashable {}
extension AttachableImageFormat: CustomStringConvertible, CustomDebugStringConvertible {}
Equatable への適合は、上で示した withUnsafeBytes(as:_:) を正しく実装するうえでも必要です。Hashable は併せて自然に実装でき、CustomStringConvertible / CustomDebugStringConvertible への適合はエンコード失敗時などの診断出力を読みやすくします。
Attachment.imageFormat
Attachment には、添付対象が画像のときにそのエンコードに使う AttachableImageFormat を取り出すためのプロパティが追加されます。値が指定されていない場合は nil を返します。
extension Attachment where AttachableValue: AttachableWrapper,
AttachableValue.Wrapped: AttachableAsImage {
/// The image format to use when encoding the represented image.
public var imageFormat: AttachableImageFormat? { get }
}
03 今後の見通し
将来の発展として、画像添付の API で利用しているバッファ型を UnsafeRawBufferPointer から RawSpan に置き換える方向が挙げられています。attachment 機能は RawSpan が利用可能になる前、特に古い Apple プラットフォームへ back-deploy される前にリリースされた経緯があり、現状の API は UnsafeRawBufferPointer を使っています。attachment 全体で扱うバッファ型を統一したいという考えから、画像添付の層だけを RawSpan 化することは目指されていません。将来的には、UnsafeRawBufferPointer を使う既存 API を非推奨化し、RawSpan を使う新しい API を導入することが検討されています。
これは将来の構想であり、実現を約束するものではありません。