Creating custom Link preview instead of LPLinkView with SwiftUI.

Arseni
3 min readJun 27, 2023

--

Hello! I want to share with you use cases, which can be useful for some who wants create customizable link previews from URL.

So in generally to create Preview link with SwiftUI we going with UIViewRepresentable, adopted it with makeUIView, updateUIView to LPLinkView. I’m not share this code there, you can find it very quickly.
The main problem was in customizing our preview. For example we cannot set a right dimensions for our frame or for example we can’t change positions of image view, title, url. I know for someone it can be enough but when it comes from design team or with custom dimensions we have to deal with it.

Now I want to share blueprint, the main goal was just to load preview from URL and set the custom dimension, image position, etc. Also you can go further (if you find it necessary) and add open link action.

We can start with example of ObservableObject, it will be something like View Model or Preview Manager for our view. Firstly we need import necessary libraries:

import SwiftUI
import LinkPresentation
import UniformTypeIdentifiers

In our object I place some published properties, custom init from our string value:

final class PreviewViewModel: ObservableObject {

@Published var image: UIImage?
@Published var title: String?
@Published var url: String?

let previewURL: URL?

init(_ url: String) {
self.previewURL = URL(string: url)
}
}

Next our main part, so if we go directly with LPLinkView and UIViewRepresentable, after receiving metadata from URL we linking it with LPLinkView.metadata property and our view ready to show. Nice, but we want some custom.

Okay, in that case we need to implement only LPMetadataProvider object, fetch metadata from our previewURL and finally set it with our properties:

    private func fetchMetadata() {
guard let previewURL else { return }
let provider = LPMetadataProvider()

Task {
let metadata = try await provider.startFetchingMetadata(for: previewURL)

image = try await convertToImage(metadata.imageProvider)
title = metadata.title

url = metadata.url?.host()
}
}

Dont forget to put this method in our init:

    init(_ url: String) {
self.previewURL = URL(string: url)

fetchMetadata()
}

Already you can notice, that we need to do some steps to take image from our imageProvider (that is NSItemProviver object). There is some general implementation, you can use your own:

    private func convertToImage(_ imageProvider: NSItemProvider?) async throws -> UIImage? {
var image: UIImage?

if let imageProvider {
let type = String(describing: UTType.image)

if imageProvider.hasItemConformingToTypeIdentifier(type) {
let item = try await imageProvider.loadItem(forTypeIdentifier: type)

if item is UIImage {
image = item as? UIImage
}

if item is URL {
guard let url = item as? URL,
let data = try? Data(contentsOf: url) else { return nil }

image = UIImage(data: data)
}

if item is Data {
guard let data = item as? Data else { return nil }

image = UIImage(data: data)
}
}
}

return image
}

Nice, done with our view model. Now we can implement it with our PreviewLinkView. I prepared a little example where we place our image, title and link from our URL. Firstly I added a ObservedObject which is our view model for today, generally combination with HStack & VStack.

import SwiftUI

struct FileLinkView: View {

@ObservedObject var viewModel: PreviewViewModel

var body: some View {
HStack(spacing: 15) {
if let image = viewModel.image {
Image(uiImage: image)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(maxWidth: 107, maxHeight: 107)
.clipped()
.cornerRadius(16)
}

VStack(alignment: .leading, spacing: 1, content: {
if let title = viewModel.title {
Text(title)
.font(.body)
.foregroundColor(.black)
.multilineTextAlignment(.leading)
}

if let url = viewModel.url {
Text(url)
.font(.footnote)
.foregroundColor(.gray)
.multilineTextAlignment(.leading)
}
})
.padding(.top, 16)
.padding(.bottom, 9)
.padding(.trailing, 40)
}
.frame(maxWidth: .infinity, maxHeight: 100)
}
}

struct FileLinkView_Previews: PreviewProvider {
static var previews: some View {
FileLinkView(
viewModel: PreviewViewModel("PASTE YOUR URL")
)
}
}

As we can see, our result is perfect, neat and fully customizable. You can put elements in way you want.

Thanks for reading, I hope you find it interesting and useful, good luck! :)

--

--