Categories
iOS LinkPresentation macOS Swift UIKit

Loading URL previews using LinkPresentation framework in Swift

iOS and macOS got a new framework in WWDC’19 named LinkPresentation. LinkPresentation enables fetching URL previews.

Adding LPLinkView for presenting preview

LPLinkView is a view subclass meant for rendering LPLinkMetadata. LPLinkMetadata contains information about the link: title, icon, image, video.

import LinkPresentation
let linkView = LPLinkView(metadata: LPLinkMetadata())
linkView.translatesAutoresizingMaskIntoConstraints = false
stackView.insertArrangedSubview(linkView, at: 0)
Adding LPLinkView to stack view

Fetching previews with LPMetadataProvider

Instances of LPLinkMetadata are fetched using LPLinkMetadataProvider for a given url. LPLinkMetadata is conforming to NSSecureCoding what enables a way of converting it to Data and storing the metadata on disk. Next time we need metadata for this url, we can use a locally cached data instead. Archiving and unarchiving is done with help of NSKeyedArchiver and NSKeyedUnarchiver and in the example archived data is stored in UserDefaults. In real apps it makes sense to store the data in separate files instead and not polluting UserDefaults with preview data.

private let metadataStorage = MetadataStorage()
private lazy var metadataProvider = LPMetadataProvider()
private weak var linkView: LPLinkView?
@IBAction func loadPreview(_ sender: UIButton) {
if let text = textField.text, let url = URL(string: text) {
// Avoid fetching LPLinkMetadata every time and archieve it disk
if let metadata = metadataStorage.metadata(for: url) {
linkView?.metadata = metadata
return
}
metadataProvider.startFetchingMetadata(for: url) { [weak self] (metadata, error) in
if let error = error {
print(error)
}
else if let metadata = metadata {
DispatchQueue.main.async { [weak self] in
self?.metadataStorage.store(metadata)
self?.linkView?.metadata = metadata
}
}
}
}
}
Fetching and caching LPMetadata
struct MetadataStorage {
private let storage = UserDefaults.standard
func store(_ metadata: LPLinkMetadata) {
do {
let data = try NSKeyedArchiver.archivedData(withRootObject: metadata, requiringSecureCoding: true)
var metadatas = storage.dictionary(forKey: "Metadata") as? [String: Data] ?? [String: Data]()
while metadatas.count > 10 {
metadatas.removeValue(forKey: metadatas.randomElement()!.key)
}
metadatas[metadata.originalURL!.absoluteString] = data
storage.set(metadatas, forKey: "Metadata")
}
catch {
print("Failed storing metadata with error \(error as NSError)")
}
}
func metadata(for url: URL) -> LPLinkMetadata? {
guard let metadatas = storage.dictionary(forKey: "Metadata") as? [String: Data] else { return nil }
guard let data = metadatas[url.absoluteString] else { return nil }
do {
return try NSKeyedUnarchiver.unarchivedObject(ofClass: LPLinkMetadata.self, from: data)
}
catch {
print("Failed to unarchive metadata with error \(error as NSError)")
return nil
}
}
}
Local LPMetadata storage using UserDefaults as storage

Summary

LinkPresentation framework adds a easy way of fetching previews for web pages. It provides a LPLinkView class making it extremely easy to render the preview.

If this was helpful, please let me know on Mastodon@toomasvahter or Twitter @toomasvahter. Feel free to subscribe to RSS feed. Thank you for reading.

Example Project

LinkPresentationView (Xcode 11 GM)

Leave a comment