Arn*_*ani 2 json swift urlsession
我正在尝试从 Unsplash API 获取数据,但收到以下错误:“不允许从后台线程发布更改;请确保在模型更新时从主线程发布值(通过 receive(on:) 等运算符)” ”。
这是模型结构:
// 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, name, 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, location, 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, html, photos, likes: String
let portfolio, following, followers: String
enum CodingKeys: String, CodingKey {
case linksSelf = "self"
case html, photos, likes, portfolio, following, 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()
}
}
Run Code Online (Sandbox Code Playgroud)
这是我的 ObservableObject:
class UnsplashAPI: ObservableObject {
enum State {
case loading
case loaded(UnsplashData)
}
@Published var state = State.loading
let url = URL(string: "https://api.unsplash.com/")!
func request() {
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: "vMDQ3Vzix8FN6MJL5Qpl3y0F7GdQsTtOjBe_L-IG2ro")
]
let request = URLRequest(url: components.url!)
let urlSession = URLSession(configuration: URLSessionConfiguration.default)
urlSession.dataTask(with: request) { data, urlResponse, error in
if let data = data {
let decoder = JSONDecoder()
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ssZ"
decoder.dateDecodingStrategy = .formatted(dateFormatter)
do {
let response = try decoder.decode(UnsplashData.self, from: data)
self.state = .loaded(response) //error here
} catch {
print(error)
fatalError("Couldn't decode")
}
} else if let error = error {
print(error.localizedDescription)
} else {
fatalError("Didn't receive data")
}
}.resume()
}
}
Run Code Online (Sandbox Code Playgroud)
最后这是我使用 Postman 请求的示例响应:
{
"id": "HWx5PYGudcI",
"created_at": "2020-12-08T22:11:11-05:00",
"updated_at": "2020-12-26T23:19:29-05:00",
"promoted_at": "2020-12-09T03:14:06-05:00",
"width": 4000,
"height": 6000,
"color": "#8ca6a6",
"blur_hash": "LAD,r_D*_M?^%ER4%$-oyYp0m+WE",
"description": null,
"alt_description": "boy in gray crew neck shirt",
"urls": {
"raw": "https://images.unsplash.com/photo-1607483421673-181fb79394b3?ixid=MXwxMTU5MTR8MHwxfHJhbmRvbXx8fHx8fHx8&ixlib=rb-1.2.1",
"full": "https://images.unsplash.com/photo-1607483421673-181fb79394b3?crop=entropy&cs=srgb&fm=jpg&ixid=MXwxMTU5MTR8MHwxfHJhbmRvbXx8fHx8fHx8&ixlib=rb-1.2.1&q=85",
"regular": "https://images.unsplash.com/photo-1607483421673-181fb79394b3?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MXwxMTU5MTR8MHwxfHJhbmRvbXx8fHx8fHx8&ixlib=rb-1.2.1&q=80&w=1080",
"small": "https://images.unsplash.com/photo-1607483421673-181fb79394b3?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MXwxMTU5MTR8MHwxfHJhbmRvbXx8fHx8fHx8&ixlib=rb-1.2.1&q=80&w=400",
"thumb": "https://images.unsplash.com/photo-1607483421673-181fb79394b3?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MXwxMTU5MTR8MHwxfHJhbmRvbXx8fHx8fHx8&ixlib=rb-1.2.1&q=80&w=200"
},
"links": {
"self": "https://api.unsplash.com/photos/HWx5PYGudcI",
"html": "https://unsplash.com/photos/HWx5PYGudcI",
"download": "https://unsplash.com/photos/HWx5PYGudcI/download",
"download_location": "https://api.unsplash.com/photos/HWx5PYGudcI/download"
},
"categories": [],
"likes": 51,
"liked_by_user": false,
"current_user_collections": [],
"sponsorship": null,
"user": {
"id": "3Bj-zCFL4-g",
"updated_at": "2020-12-26T14:58:36-05:00",
"username": "owensito",
"name": "Owen Vangioni",
"first_name": "Owen",
"last_name": "Vangioni",
"twitter_username": null,
"portfolio_url": null,
"bio": "Capturing magical moments...\nInstagram: @owensitens 18 years",
"location": "Argentina ",
"links": {
"self": "https://api.unsplash.com/users/owensito",
"html": "https://unsplash.com/@owensito",
"photos": "https://api.unsplash.com/users/owensito/photos",
"likes": "https://api.unsplash.com/users/owensito/likes",
"portfolio": "https://api.unsplash.com/users/owensito/portfolio",
"following": "https://api.unsplash.com/users/owensito/following",
"followers": "https://api.unsplash.com/users/owensito/followers"
},
"profile_image": {
"small": "https://images.unsplash.com/profile-1583211530737-0c1a46227535image?ixlib=rb-1.2.1&q=80&fm=jpg&crop=faces&cs=tinysrgb&fit=crop&h=32&w=32",
"medium": "https://images.unsplash.com/profile-1583211530737-0c1a46227535image?ixlib=rb-1.2.1&q=80&fm=jpg&crop=faces&cs=tinysrgb&fit=crop&h=64&w=64",
"large": "https://images.unsplash.com/profile-1583211530737-0c1a46227535image?ixlib=rb-1.2.1&q=80&fm=jpg&crop=faces&cs=tinysrgb&fit=crop&h=128&w=128"
},
"instagram_username": "owensitens",
"total_collections": 1,
"total_likes": 13,
"total_photos": 135,
"accepted_tos": true
},
"exif": {
"make": "NIKON CORPORATION",
"model": "NIKON D3300",
"exposure_time": "1/320",
"aperture": "5.3",
"focal_length": "45.0",
"iso": 200
},
"location": {
"title": null,
"name": null,
"city": null,
"country": null,
"position": {
"latitude": null,
"longitude": null
}
},
"views": 536045,
"downloads": 1267
}
Run Code Online (Sandbox Code Playgroud)
您需要将线程切换到主线程,您可以从该主线程(并且只能从主线程!)在 iOS 中进行 UI 更改。要修复该错误,您需要使用GCD并将更改状态的行包装在async闭包块中。
DispatchQueue.main.async {
self.state = .loaded(response) // error should not be triggered anymore
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3886 次 |
| 最近记录: |