【发布时间】:2021-08-11 02:34:17
【问题描述】:
我正在学习 iOS 编程书中的示例。书中的示例从 Flickr 下载照片并将它们放置在集合视图中。每张照片都通过链接作为 json 代码中对象的元素之一下载。该示例使用 Core Data 进行数据持久化,并涉及从 Core Data 托管对象类型到自定义对象类型的转换。
编译器报告“无法将 '[Photo]' 类型的值转换为预期的参数类型 '[FlickrPhoto]'”。 [Photo] 是由 Core Data 根据我提供的 Entity 和 Attributes 信息自动生成的 Photo 对象数组,而 [FlickrPhoto] 是 FlickrPhoto 对象数组,它们是自定义对象。
请让我知道存在什么问题并提出一些解决方案。谢谢!
相关代码如下:
照片+CoreDataClass.swift
import Foundation
import CoreData
@objc(Photo)
public class Photo: NSManagedObject {
}
照片+CoreDataProperties.swift
import Foundation
import CoreData
extension Photo {
@nonobjc public class func fetchRequest() -> NSFetchRequest<Photo> {
return NSFetchRequest<Photo>(entityName: "Photo")
}
@NSManaged public var datetaken: String?
@NSManaged public var datetakengranularity: String?
@NSManaged public var datetakenunknow: String?
@NSManaged public var farm: Int64
@NSManaged public var heightZ: Int64
@NSManaged public var id: String?
@NSManaged public var isfamily: Int64
@NSManaged public var isfriend: Int64
@NSManaged public var ispublic: Int64
@NSManaged public var owner: String?
@NSManaged public var secret: String?
@NSManaged public var server: String?
@NSManaged public var title: String?
@NSManaged public var urlZ: String?
@NSManaged public var widthZ: Int64
}
extension Photo : Identifiable {
}
照片.swift
import Foundation
struct FlickrPhoto: Codable {
let id, owner, secret, server: String
let farm: Int
let title: String
let ispublic, isfriend, isfamily: Int
let datetaken: String
let datetakengranularity: Datetakengranularity
let datetakenunknown: String
let urlZ: String?
let heightZ, widthZ: Int?
enum CodingKeys: String, CodingKey {
case id, owner, secret, server, farm, title, ispublic, isfriend, isfamily, datetaken, datetakengranularity, datetakenunknown
case urlZ = "url_z"
case heightZ = "height_z"
case widthZ = "width_z"
}
}
enum Datetakengranularity: Codable {
case integer(Int)
case string(String)
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let x = try? container.decode(Int.self) {
self = .integer(x)
return
}
if let x = try? container.decode(String.self) {
self = .string(x)
return
}
throw DecodingError.typeMismatch(Datetakengranularity.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for Datetakengranularity"))
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .integer(let x):
try container.encode(x)
case .string(let x):
try container.encode(x)
}
}
}
extension FlickrPhoto: Equatable {
static func == (lhs: FlickrPhoto, rhs: FlickrPhoto) -> Bool {
// Two Photos are the same if they have the same photoID
return lhs.id == rhs.id
}
}
PhotoStore.swift
import UIKit
import CoreData
class PhotoStore {
private let session: URLSession = {
let config = URLSessionConfiguration.default
return URLSession(configuration: config)
}()
let imageStore = ImageStore()
let persistenContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "Photorama")
container.loadPersistentStores { (description, error) in
if let error = error {
print("Error setting up Core Data (\(error))")
}
}
return container
}()
private func processPhotosRequest (data: Data?, error: Error?) ->
Result<[FlickrPhoto], Error> {
guard let jsonData = data else {
return .failure(error!)
}
//return FlickrAPI.photos(fromJSON: jsonData)
let context = persistenContainer.viewContext
switch FlickrAPI.photos(fromJSON: jsonData) {
case let .success(flickrPhotos):
let photos = flickrPhotos.map { flickrPhoto -> Photo in
var photo: Photo!
context.performAndWait {
do {
photo = Photo(context: context)
photo.id = flickrPhoto.id
photo.owner = flickrPhoto.owner
photo.secret = flickrPhoto.secret
photo.server = flickrPhoto.server
photo.farm = Int64(flickrPhoto.farm)
photo.title = flickrPhoto.title
photo.ispublic = Int64(flickrPhoto.ispublic)
photo.isfriend = Int64(flickrPhoto.isfriend)
photo.isfamily = Int64(flickrPhoto.isfamily)
photo.datetaken = flickrPhoto.datetaken
photo.datetakengranularity = try String(from: flickrPhoto.datetakengranularity as! Decoder)
photo.datetakenunknow = flickrPhoto.datetakenunknown
photo.urlZ = flickrPhoto.urlZ
photo.heightZ = Int64(flickrPhoto.heightZ!)
photo.widthZ = Int64(flickrPhoto.widthZ!)
} catch {
print("error encountered")
}
}
return photo
}
return .success(photos) // Compiler reports the above mentioned error here
case let .failure(error):
return .failure(error)
}
}
func fetchInterestingPhotos (completion: @escaping (Result<[FlickrPhoto], Error>) -> Void) {
let url = FlickrAPI.interestingPhotoURL
let request = URLRequest(url: url)
let task = session.dataTask(with: request) {
(data, response, error) in
let result = self.processPhotosRequest(data: data, error: error)
OperationQueue.main.addOperation {
completion(result)
}
}
task.resume()
}
func fetchImage (for photo: FlickrPhoto, completion: @escaping (Result<UIImage, Error>) -> Void) {
let photoKey = photo.id
if let image = imageStore.image(forKey: photoKey) {
OperationQueue.main.addOperation {
completion(.success(image))
}
return
}
guard let photoURL = photo.urlZ else { return }
guard let requestURL = URL(string: photoURL) else {
completion(.failure(PhotoError.missingImageURL))
return
}
let request = URLRequest(url: requestURL)
let task = session.dataTask(with: request) {
(data, response, error) in
let result = self.processImageRequest(data: data, error: error)
if case let .success(image) = result {
self.imageStore.setImage(image, forKey: photoKey)
}
OperationQueue.main.addOperation {
completion(result)
}
}
task.resume()
}
private func processImageRequest (data: Data?, error: Error?) -> Result<UIImage, Error> {
guard let imageData = data,
let image = UIImage(data: imageData) else {
// Couldn't create an image
if data == nil {
return .failure(error!)
} else {
return .failure(PhotoError.imageCreationError)
}
}
return .success(image)
}
}
enum PhotoError: Error {
case imageCreationError
case missingImageURL
}
最后是 xcdatamodeld 设置的快照:
将根据需要提供更多代码。
【问题讨论】:
-
感谢@JoakimDanielson 的回复。我的问题正如标题所暗示的那样,照片是由核心数据模型自动生成的照片类型,程序通过根据 FlickrPhoto 结构的属性添加实体及其属性来配置该模型。但是,当 'processPhotosRequest' 方法中的 'return .success(photos)' 语句中的代码打算使用 '[Photo]' 类型的照片作为 '.success()' 的参数时,然而,应该是“[FlickrPhoto]”类型,编译器报告给出的投诉如我的问题所示。