According to WWDC20 and other articles it seems its quite easy to fetch the image url from a given url. Below is my starter code. That just lists a random list of urls and is supposed to fetch the imageurl for the rich link previews of the urls. One simply fetches the metadata using the LPMetadataProvider. But i can't get it to show the image url. Does someone know how its done in SwiftUI?
import SwiftUI
import LinkPresentation
struct ExampleHTTPLinks: View {
var links = [ "https://www.google.com", "https://www.hotmail.com"]
let metadataProvider = LPMetadataProvider()
var body: some View {
List(links, id:\.self) { item in
HStack {
Text(item)
Image(systemName: "heart.fill")
metadataProvider.startFetchingMetadata(for: URL(string: item)!) { metadata, error in
if error != nil {
// The fetch failed; handle the error.
// Examples: server doesn't respond, is too slow, user doesn't have network.
return
}
let linkView = LPLinkView(metadata: metadata)
Image(linkView.image)
// Make use of the fetched metadata.
}
}
}
}
}
struct ExampleHTTPLinks_Previews: PreviewProvider {
static var previews: some View {
ExampleHTTPLinks()
}
}
Here's a working version:
class LinkViewModel : ObservableObject {
let metadataProvider = LPMetadataProvider()
@Published var metadata: LPLinkMetadata?
@Published var image: UIImage?
init(link : String) {
guard let url = URL(string: link) else {
return
}
metadataProvider.startFetchingMetadata(for: url) { (metadata, error) in
guard error == nil else {
assertionFailure("Error")
return
}
DispatchQueue.main.async {
self.metadata = metadata
}
guard let imageProvider = metadata?.imageProvider else { return }
imageProvider.loadObject(ofClass: UIImage.self) { (image, error) in
guard error == nil else {
// handle error
return
}
if let image = image as? UIImage {
// do something with image
DispatchQueue.main.async {
self.image = image
}
} else {
print("no image available")
}
}
}
}
}
struct MetadataView : View {
@StateObject var vm : LinkViewModel
var body: some View {
VStack {
if let metadata = vm.metadata {
Text(metadata.title ?? "")
}
if let uiImage = vm.image {
Image(uiImage: uiImage)
.resizable()
.frame(width: 100, height: 100)
}
}
}
}
struct ContentView: View {
var links = [ "https://www.google.com", "https://www.hotmail.com"]
let metadataProvider = LPMetadataProvider()
var body: some View {
List(links, id:\.self) { item in
HStack {
Text(item)
Image(systemName: "heart.fill")
MetadataView(vm: LinkViewModel(link: item))
}
}
}
}
LPMetadataProvider complains if you try to use it for multiple calls, so I've moved it to a view model.
The image is vended by an NSImageProvider -- you can see the loadObject call is what gets the UIImage out of it.
Note that you could use LPLinkView if you wanted to use the out-of-the-box presentation that Apple gives you. Because it's a UIView, to use it in SwiftUI, you'd have to wrap it with UIViewRepresentable:
struct LPLinkViewRepresented: UIViewRepresentable {
var metadata: LPLinkMetadata
func makeUIView(context: Context) -> LPLinkView {
return LPLinkView(metadata: metadata)
}
func updateUIView(_ uiView: LPLinkView, context: Context) {
}
}
struct MetadataView : View {
@StateObject var vm : LinkViewModel
var body: some View {
if let metadata = vm.metadata {
LPLinkViewRepresented(metadata: metadata)
} else {
EmptyView()
}
}
}
class LinkViewModel : ObservableObject {
let metadataProvider = LPMetadataProvider()
@Published var metadata: LPLinkMetadata?
init(link : String) {
guard let url = URL(string: link) else {
return
}
metadataProvider.startFetchingMetadata(for: url) { (metadata, error) in
guard error == nil else {
assertionFailure("Error")
return
}
DispatchQueue.main.async {
self.metadata = metadata
}
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With