February 23, 2021
I recently got a bug report for Transparent App Icons to the effect of "I tried to paste in a transparent image, but it didn't work." After cropping the background to create a "transparent" image users can use for their Shortcuts icons, the app allows the user to paste an image from the clipboard to overlay and create a semi-transparent icon - this user was trying to paste a WebP image.
Turns out that WebP, the web-optimized image format developed by Google, isn't supported natively by UIKit. Luckily it's not too hard to implement support for it with the help of a small library and some deeper UIKit APIs.
Looking for a
UIPasteboard
extension which loads WebP images? Skip to the bottom!
I haven't had to use the more complicated parts of UIPasteboard
very much in my time as an iOS developer - in Transparent App Icons, I was just checking UIPasteboard.general.image
in order to figure out whether there was an image copied. In iOS 14, you can copy a WebP image no problem, but there's no UIImage
initializer representation for it, and as such it doesn't get reported as a UIImage
in the same way it would if you had copied a PNG.
Luckily, there's a higher-effort, higher-reward way to interact with UIPasteboard
. When the user copies data, the app it copied from might choose to write multiple representations of the data to the pasteboard - for example, if I'm writing an HTML editor and the user copies part of the document, I could write both HTML and string repreesentations to the pasteboard. Then, an app like notes doesn't have to worry about the HTML representation, it can just read the string value.
Each representation of an object in the pasteboard is denoted by a type, usually a Uniform Type Identifier. We can request the raw data for a pasteboard item using UIPasteboard
's' data(forPasteboardType:)
method, and since iOS 14 there's even a more strongly typed way, using UTType
- here's the UTType
identifier for WebP.
Note: When I was trying to figure this out, I first tried using
loadItem
on the pasteboard'sitemProviders
, but this returns aURL
that you can't read the data of since it's restricted (annoyingly, only on device will you get permission denied errors - this URL is perfectly readable on simulator). I then moved on toloadData
, before realizing I could just usedata(forPasteboardType:)
directly.
Once we have the data, we'll need to convert it into a UIImage
- UIImage
doesn't support this out of the box, but luckily Mattt has a great little library for encoding/decoding UIImage
s with WebP. Taking that with our data representation, we can write an extension to load a WebP image if one exists:
extension UIPasteboard {
func loadImage() -> UIImage? {
if self.types.contains(UTType.webP.identifier),
let provider = self.itemProviders.first,
self.image == nil,
let data = self.data(forPasteboardType: UTType.webP.identifier) {
do {
let webpImage = try WebPImageSerialization.image(with: data)
return webpImage
} catch {
// Log error
return nil
}
}
return UIPasteboard.general.image
}
}
This ended up working great, and Transparent App Icons will support WebP in the coming version.
I'm Noah, a software developer based in the San Francisco Bay Area. I focus mainly on full stack web and iOS development