如何解决使用 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,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()
}
}
这是我的 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()
}
}
最后是我使用 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
}
解决方法
您需要将线程切换到您被允许(并且只能从它!)在 iOS 中进行 UI 更改的 main thread。要修复该错误,您需要使用 GCD 并在 async
闭包块中简单地换行您更改状态的行。
DispatchQueue.main.async {
self.state = .loaded(response) // error should not be triggered anymore
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。