Skip to content

Commit 158419b

Browse files
authored
Merge pull request #2985 from swiftlang/jgrynspan/image-attachment-tweaks
[ST-0017] Consolidate Swift Testing's image attachments API across platforms
2 parents b25862c + 623c223 commit 158419b

File tree

1 file changed

+166
-0
lines changed

1 file changed

+166
-0
lines changed
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
# Consolidate Swift Testing's image attachments API across platforms
2+
3+
* Proposal: [ST-0017](0017-image-attachment-consolidation.md)
4+
* Authors: [Jonathan Grynspan](https://github.com/grynspan)
5+
* Review Manager: [Rachel Brindle](https://github.com/younata)
6+
* Status: **Active Review (October 22-30, 2025)**
7+
* Implementation: [swiftlang/swift-testing#1359](https://github.com/swiftlang/swift-testing/pull/1359)
8+
* Review: ([pitch](https://forums.swift.org/t/pitch-adjustments-to-image-attachments-in-swift-testing/82581), Review TBA)
9+
10+
## Introduction
11+
12+
This proposal includes a small number of adjustments to the API surface of Swift
13+
Testing's image attachments feature introduced in [ST-0014](0014-image-attachments-in-swift-testing-apple-platforms.md)
14+
and [ST-0015](0015-image-attachments-in-swift-testing-windows.md).
15+
16+
## Motivation
17+
18+
These changes will help to align the platform-specific interfaces of the feature
19+
more closely.
20+
21+
## Proposed solution
22+
23+
The `AttachableAsCGImage` and `AttachableAsIWICBitmapSource` protocols are
24+
combined into a single protocol, `AttachableAsImage` with adjusted protocol
25+
requirements; a change is made to `AttachableImageFormat` to more closely
26+
align its interface between Darwin and Windows; `AttachableImageFormat` is made
27+
to conform to `Equatable` and `Hashable`; and an additional property is added to
28+
`Attachment` to query its image format.
29+
30+
## Detailed design
31+
32+
The following changes are proposed:
33+
34+
### Combining AttachableAsCGImage and AttachableAsIWICBitmapSource
35+
36+
The `AttachableAsCGImage` and `AttachableAsIWICBitmapSource` protocols are
37+
combined into a single protocol, `AttachableAsImage`.
38+
39+
These platform-specific requirements are removed:
40+
41+
```diff
42+
- var attachableCGImage: CGImage { get throws }
43+
- func copyAttachableIWICBitmapSource() throws -> UnsafeMutablePointer<IWICBitmapSource>
44+
```
45+
46+
They are replaced with a new requirement that encapsulates the image encoding
47+
operation. This requirement is implemented by the CoreGraphics and WinSDK
48+
overlays and is made publicly available for test authors who wish to declare
49+
additional conformances to this protocol for types that are not based on
50+
`CGImage` or `IWICBitmapSource`:
51+
52+
```swift
53+
public protocol AttachableAsImage {
54+
// ...
55+
56+
/// Encode a representation of this image in a given image format.
57+
///
58+
/// - Parameters:
59+
/// - imageFormat: The image format to use when encoding this image.
60+
/// - body: A function to call. A temporary buffer containing a data
61+
/// representation of this instance is passed to it.
62+
///
63+
/// - Returns: Whatever is returned by `body`.
64+
///
65+
/// - Throws: Whatever is thrown by `body`, or any error that prevented the
66+
/// creation of the buffer.
67+
///
68+
/// The testing library uses this function when saving an image as an
69+
/// attachment. The implementation should use `imageFormat` to determine what
70+
/// encoder to use.
71+
borrowing func withUnsafeBytes<R>(as imageFormat: AttachableImageFormat, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R
72+
}
73+
```
74+
75+
If a developer has an image type that should conform to `AttachableAsImage` and
76+
wraps an instance of `CGImage` or `IWICBitmapSource`, it is straightforward for
77+
them to delegate to that object. For example:
78+
79+
```swift
80+
import Testing
81+
import CoreGraphics
82+
83+
struct MyImage {
84+
var cgImage: CGImage
85+
// ...
86+
}
87+
88+
extension MyImage: AttachableAsImage {
89+
func withUnsafeBytes<R>(as imageFormat: AttachableImageFormat, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
90+
try cgImage.withUnsafeBytes(as: imageFormat, body)
91+
}
92+
}
93+
```
94+
95+
### Adjusting AttachableImageFormat
96+
97+
The following Apple-specific `AttachableImageFormat` initializer is renamed so
98+
that its first argument has an explicit label:
99+
100+
```diff
101+
public struct AttachableImageFormat {
102+
// ...
103+
- public init(_ contentType: UTType, encodingQuality: Float = 1.0)
104+
+ public init(contentType: UTType, encodingQuality: Float = 1.0)
105+
}
106+
```
107+
108+
This change makes the type's interface more consistent between Darwin and
109+
Windows (where it has an `init(encoderCLSID:encodingQuality:)` initializer.)
110+
111+
As well, conformances to `Equatable`, `Hashable`, `CustomStringConvertible`, and
112+
`CustomDebugStringConvertible` are added:
113+
114+
```swift
115+
extension AttachableImageFormat: Equatable, Hashable {}
116+
extension AttachableImageFormat: CustomStringConvertible, CustomDebugStringConvertible {}
117+
```
118+
119+
Conformance to `Equatable` is necessary to correctly implement the
120+
`withUnsafeBytes(as:_:)` protocol requirement mentioned above, and conformance
121+
to `Hashable` is generally useful and straightforward to implement. Conformance
122+
to `CustomStringConvertible` and `CustomDebugStringConvertible` allows for
123+
better diagnostic output (especially if an encoding failure occurs.)
124+
125+
### Adding an imageFormat property to Attachment
126+
127+
The following property is added to `Attachment` when the attachable value is an
128+
image:
129+
130+
```swift
131+
extension Attachment where AttachableValue: AttachableWrapper,
132+
AttachableValue.Wrapped: AttachableAsImage {
133+
/// The image format to use when encoding the represented image.
134+
public var imageFormat: AttachableImageFormat? { get }
135+
}
136+
```
137+
138+
## Source compatibility
139+
140+
These changes are breaking for anyone who has created a type that conforms to
141+
either `AttachableAsCGImage` or `AttachableAsIWICBitmapSource`, or anyone who
142+
has adopted `AttachableImageFormat.init(_:encodingQuality:)`.
143+
144+
This feature is new in Swift 6.3 and has not shipped to developers outside of
145+
nightly toolchain builds. As such, we feel confident that any real-world impact
146+
to developers will be both minimal and manageable.
147+
148+
## Integration with supporting tools
149+
150+
No changes.
151+
152+
## Future directions
153+
154+
N/A
155+
156+
## Alternatives considered
157+
158+
- Leaving the two protocols separate. Combining them allows us to lower more
159+
code into the main Swift Testing library and improves our ability to generate
160+
DocC documentation, while also simplifying the story for developers who want
161+
to use this feature across platforms.
162+
163+
## Acknowledgments
164+
165+
Thanks to my colleagues for their feedback on the image attachments feature and
166+
to the Swift community for putting up with the churn!

0 commit comments

Comments
 (0)