如何解决对 Unsplash api 的异步请求无法正常工作
我的 swift 包 UnsplashSwiftUI 遇到了一些问题 在 WWDC 之前,我遇到了一些问题,导致我的 View 重新加载(正如您在主分支上看到的那样),但是当 async/await 被宣布时,这似乎是我的包的绝佳机会。
我正在开发分支上使用 async/await 处理包。
但是,我现在在处理异步 API 请求时遇到了一些问题。
这是我的最小可重现示例,我从异步函数 getURL() 的 catch 块中得到打印错误“无法获取图像”。我也尝试在内部使用异步调用任务。
//From this
.task {
await getURL()
}
//To this
.task {
async {
await getURL()
}
}
import SwiftUI
import PlaygroundSupport
struct ContentView: View {
var body: some View {
VStack {
UnsplashRandom(clientId: "TSozaArCYtCWcXnnUkh4KvKJ5ZfmVOn_FYbIVVn76Ew")
.frame(width: 500,height: 500)
}
}
}
PlaygroundPage.current.setLiveView(ContentView())
import SwiftUI
@available(iOS 15,OSX 12,*)
public struct UnsplashRandom: View {
//MARK: Parameters
//Required parameters
var clientId: String //Unsplash API access key
@State private var unsplashData: UnsplashData? = nil
@State private var requestURL: URL? = nil
//MARK: Init
public init(clientId: String) {
self.clientId = clientId
let url = URL(string: "https://api.unsplash.com/")!
guard var components = URLComponents(url: url.appendingPathComponent("photos/random"),resolvingAgainstBaseURL: true)
else { fatalError("Couldn't append path component")}
components.queryItems = [URLQueryItem(name: "client_id",value: clientId)]
_requestURL = State(initialValue: components.url!)
}
//MARK: Body
public var body: some View {
//MARK: Main View
ZStack(alignment: .bottomTrailing) {
//MARK: Remote Image
AsyncImage (url: URL(string: unsplashData?.urls!.raw! ?? "https://images.unsplash.com/photo-1626643590239-4d5051bafbcc?ixid=MnwxOTUzMTJ8MHwxfHJhbmRvbXx8fHx8fHx8fDE2MjY5Njc0MjI&ixlib=rb-1.2.1")!)
.aspectRatio(contentMode: .fit)
}
.task {
await getURL()
}
}
func getURL() async {
do {
let (data,_) = try await URLSession.shared.data(from: requestURL!)
unsplashData = try JSONDecoder().decode(UnsplashData.self,from: data)
} catch {
print("Failed to fetch image")
}
}
}
import Foundation
// MARK: - UnsplashData
struct UnsplashData: Codable {
let id: String?
let createdAt,updatedAt,promotedAt: Date?
let width,height: Int?
let color,blurHash: String?
let unsplashDataDescription: String?
let altDescription: String?
let urls: Urls?
let links: UnsplashDataLinks?
let categories: [String]?
let likes: Int?
let likedByUser: Bool?
let currentUserCollections: [String]?
let sponsorship: JSONNull?
let user: User?
let exif: Exif?
let location: Location?
let views,downloads: Int?
enum CodingKeys: String,CodingKey {
case id
case createdAt = "created_at"
case updatedAt = "updated_at"
case promotedAt = "promoted_at"
case width,height,color
case blurHash = "blur_hash"
case unsplashDataDescription = "description"
case altDescription = "alt_description"
case urls,links,categories,likes
case likedByUser = "liked_by_user"
case currentUserCollections = "current_user_collections"
case sponsorship,user,exif,location,views,downloads
}
}
// MARK: - Exif
struct Exif: Codable {
let make,model,exposureTime,aperture: String?
let focalLength: String?
let iso: Int?
enum CodingKeys: String,CodingKey {
case make,model
case exposureTime = "exposure_time"
case aperture
case focalLength = "focal_length"
case iso
}
}
// MARK: - UnsplashDataLinks
struct UnsplashDataLinks: Codable {
let linksSelf,html,download,downloadLocation: String?
enum CodingKeys: String,CodingKey {
case linksSelf = "self"
case html,download
case downloadLocation = "download_location"
}
}
// MARK: - Location
struct Location: Codable {
let title,name,city,country: String?
let position: Position?
}
// MARK: - Position
struct Position: Codable {
let latitude,longitude: Double?
}
// MARK: - Urls
struct Urls: Codable {
let raw,full,regular,small: String?
let thumb: String?
}
// MARK: - User
struct User: Codable {
let id: String?
let updatedAt: Date?
let username,firstName,lastName: String?
let twitterUsername: String?
let portfolioURL: String?
let bio: String?
let location: String?
let links: UserLinks?
let profileImage: ProfileImage?
let instagramUsername: String?
let totalCollections,totalLikes,totalPhotos: Int?
let acceptedTos: Bool?
enum CodingKeys: String,CodingKey {
case id
case updatedAt = "updated_at"
case username,name
case firstName = "first_name"
case lastName = "last_name"
case twitterUsername = "twitter_username"
case portfolioURL = "portfolio_url"
case bio,links
case profileImage = "profile_image"
case instagramUsername = "instagram_username"
case totalCollections = "total_collections"
case totalLikes = "total_likes"
case totalPhotos = "total_photos"
case acceptedTos = "accepted_tos"
}
}
// MARK: - UserLinks
struct UserLinks: Codable {
let linksSelf,photos,likes: String?
let portfolio,following,followers: String?
enum CodingKeys: String,likes,portfolio,followers
}
}
// MARK: - ProfileImage
struct ProfileImage: Codable {
let small,medium,large: String?
}
// MARK: - Encode/decode helpers
class JSONNull: Codable,Hashable {
public static func == (lhs: JSONNull,rhs: JSONNull) -> Bool {
return true
}
public var hashValue: Int {
return 0
}
public func hash(into hasher: inout Hasher) {
// No-op
}
public init() {}
public required init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if !container.decodeNil() {
throw DecodingError.typeMismatch(JSONNull.self,DecodingError.Context(codingPath: decoder.codingPath,debugDescription: "Wrong type for JSONNull"))
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encodeNil()
}
}
解决方法
在您的模型 UnsplashData 和 User 中,替换 Date?与字符串?。
在那之后,这就是我测试我的答案的方式:
import SwiftUI
@main
struct TestApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct ContentView: View {
@State var unsplashData: UnsplashData?
var body: some View {
VStack {
if let unsplash = unsplashData {
Text("user is \(unsplash.user?.name ?? "no name")")
} else {
Text("testing testing")
}
}
.task {
await getUnsplashData()
}
}
func getUnsplashData() async {
let fetchResponse: UnsplashData? = await fetchIt()
if let theResponse = fetchResponse {
self.unsplashData = theResponse
print("\n-----> getUnsplashData: \(theResponse)")
}
}
func fetchIt<T: Decodable>() async -> T? {
let url = URL(string: "https://api.unsplash.com/photos/random?client_id=TSozaArCYtCWcXnnUkh4KvKJ5ZfmVOn_FYbIVVn76Ew")!
let request = URLRequest(url: url)
do {
let (data,response) = try await URLSession.shared.data(for: request)
guard let httpResponse = response as? HTTPURLResponse,httpResponse.statusCode == 200 else {
// throw URLError(.badServerResponse) // todo
print(URLError(.badServerResponse))
return nil
}
let results = try JSONDecoder().decode(T.self,from: data)
return results
}
catch {
return nil
}
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。