Last active
January 13, 2024 03:34
-
-
Save Aayush9029/b49b0128f64a2bfd599e6cd75e27e35c to your computer and use it in GitHub Desktop.
Swift URLMeta Data Fetcher
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import LinkPresentation | |
| import UIKit | |
| struct LinkMetadata { | |
| let title: String | |
| let image: Data? | |
| let url: URL? | |
| init(_ metadata: LPLinkMetadata, imageData: Data? = nil) { | |
| self.title = metadata.title ?? "Untitled Website" | |
| self.url = metadata.url | |
| self.image = imageData | |
| } | |
| } | |
| enum MetadataFetcher { | |
| static func fetchMetadata(for url: URL) async throws -> LinkMetadata { | |
| let metadataProvider = LPMetadataProvider() | |
| let metadata = try await metadataProvider.startFetchingMetadata(for: url) | |
| var imageData: Data? = nil | |
| if let imageProvider = metadata.imageProvider { | |
| imageData = try await loadImageData(imageProvider: imageProvider) | |
| } | |
| return LinkMetadata(metadata, imageData: imageData) | |
| } | |
| private static func loadImageData(imageProvider: NSItemProvider) async throws -> Data? { | |
| try await withCheckedThrowingContinuation { continuation in | |
| imageProvider.loadObject(ofClass: UIImage.self) { image, error in | |
| DispatchQueue.main.async { | |
| if let error = error { | |
| continuation.resume(throwing: error) | |
| } else if let image = image as? UIImage, let data = image.jpegData(compressionQuality: 1.0) { | |
| continuation.resume(returning: data) | |
| } else { | |
| continuation.resume(returning: nil) | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| // MARK: - View | |
| import SwiftUI | |
| struct LinkView: View { | |
| let url: URL | |
| @State private var metadata: LinkMetadata? | |
| init(_ url: URL) { | |
| self.url = url | |
| } | |
| var body: some View { | |
| ZStack(alignment: .bottom) { | |
| if let metadata { | |
| Group { | |
| if let data = metadata.image { | |
| Image(uiImage: UIImage(data: data)!) | |
| } else { | |
| Image(.appIcon) | |
| } | |
| } | |
| .frame( | |
| width: UIScreen.main.bounds.width - 24, | |
| height: 240 | |
| ) | |
| VStack { | |
| Text(metadata.title) | |
| .font(.headline) | |
| .lineLimit(2) | |
| if let url = metadata.url { | |
| Text(url.absoluteString) | |
| .font(.subheadline) | |
| .foregroundStyle(.tertiary) | |
| .lineLimit(1) | |
| } | |
| } | |
| .padding(12) | |
| .hSpacing(.leading) | |
| .background(.ultraThinMaterial) | |
| } else { | |
| Rectangle() | |
| .fill(.ultraThinMaterial) | |
| Text(url.absoluteString) | |
| .foregroundStyle(.secondary) | |
| .padding() | |
| } | |
| } | |
| .frame(height: (metadata?.image != nil ? 240 : 48)) | |
| .clipShape(.rect(cornerRadius: 18)) | |
| .overlay( | |
| RoundedRectangle(cornerRadius: 18) | |
| .stroke(.primary.opacity(0.05), lineWidth: 4) | |
| ) | |
| .animation(.bouncy, value: metadata?.image) | |
| .task { | |
| self.metadata = try? await MetadataFetcher.fetchMetadata(for: url) | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment