简体   繁体   English

有没有办法将 Swift 枚举类型的变量分配给 NSObject 类型的变量?

[英]Is there a way to assign a variable of Swift enum type to a variable of NSObject type?

In my program code which is posted below, I need to assign a variable of Swift enum type to a variable of NSObject type.在下面发布的程序代码中,我需要将 Swift 枚举类型的变量分配给 NSObject 类型的变量。 However, the compiler doesn't allow this.但是,编译器不允许这样做。 I know this is not allowed.我知道这是不允许的。 So, I wonder whether there is a way to change the enum somehow so that it can be assigned to an NSObject variable.所以,我想知道是否有办法以某种方式更改枚举,以便可以将其分配给 NSObject 变量。 Thank you!谢谢!

Photo+CoreDataClass.swift照片+CoreDataClass.swift

import Foundation
import CoreData

@objc(Photo)
public class Photo: NSManagedObject {

}

Photo+CoreDataProperties.swift照片+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: NSObject?
    @NSManaged public var datetakenunknow: String?
    @NSManaged public var farm: Int32
    @NSManaged public var heightZ: Int32
    @NSManaged public var photoID: String?
    @NSManaged public var isfamily: Int32
    @NSManaged public var isfriend: Int32
    @NSManaged public var ispublic: Int32
    @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: Int32

}

extension Photo : Identifiable {

}

FlickrPhoto.swift FlickrPhoto.swift

import Foundation

// MARK: - Photo
struct FlickrPhoto: Codable {
    let photoID, 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 owner, secret, server, farm, title, ispublic, isfriend, isfamily, datetaken, datetakengranularity, datetakenunknown
        case photoID = "id"
        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.photoID == rhs.photoID
    }
}

PhotoStore.swift 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 _ = flickrPhotos.map { flickrPhoto -> Photo in
                let fetchRequest: NSFetchRequest<Photo> = Photo.fetchRequest()
                let predicate = NSPredicate(format: "\(#keyPath(Photo.photoID)) == \(flickrPhoto.photoID)")
                fetchRequest.predicate = predicate
                var photo: Photo!
                context.performAndWait {
                        photo = Photo(context: context)
                        photo.photoID = flickrPhoto.photoID
                        photo.owner = flickrPhoto.owner
                        photo.secret = flickrPhoto.secret
                        photo.server = flickrPhoto.server
                        photo.farm = Int32(flickrPhoto.farm)
                        photo.title = flickrPhoto.title
                        photo.ispublic = Int32(flickrPhoto.ispublic)
                        photo.isfriend = Int32(flickrPhoto.isfriend)
                        photo.isfamily = Int32(flickrPhoto.isfamily)
                        photo.datetaken = flickrPhoto.datetaken
                        photo.datetakengranularity = flickrPhoto.datetakengranularity // The compiler reports error here: 
                             // Cannot assign value of type 'Datetakengranularity' to type 'NSObject?'
                        photo.datetakenunknow = flickrPhoto.datetakenunknown
                        photo.urlZ = flickrPhoto.urlZ
                        photo.heightZ = Int32(flickrPhoto.heightZ!)
                        photo.widthZ = Int32(flickrPhoto.widthZ!)
                }
                return photo
            }
            return .success(flickrPhotos)
        case let .failure(error):
            return .failure(error)
        }
    }
    func fetchAllPhotos (completion: @escaping (Result<[Photo], Error>) -> Void) -> Void {
        let fetchRequest: NSFetchRequest<Photo> = Photo.fetchRequest()
        let sortByDataTaken = NSSortDescriptor(key: #keyPath(Photo.datetaken), ascending: true)
        fetchRequest.sortDescriptors = [sortByDataTaken]
        let viewContext = persistenContainer.viewContext
        viewContext.perform {
            do {
                let allPhotos = try viewContext.fetch(fetchRequest)
                completion(.success(allPhotos))
            } catch {
                completion(.failure(error))
            }
        }
    }
    func fetchRecentPhotos (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
            
            var result = self.processPhotosRequest(data: data, error: error)
            if case .success = result {
                do {
                    try self.persistenContainer.viewContext.save()
                } catch {
                    result = .failure(error)
                }
            }
            OperationQueue.main.addOperation {
                completion(result)
            }
        }
        task.resume()
    }
    func fetchImage (for photo: Photo, completion: @escaping (Result<UIImage, Error>) -> Void) {
        
        let photoKey = photo.photoID
        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
}

A snapshot of the Photorama.xcdatamodeld interface is shown below: Photorama.xcdatamodeld 接口的快照如下所示:

在此处输入图像描述

More code will be provided if necessary.如有必要,将提供更多代码。 Thanks very much!非常感谢!

You could do it using hacky enum byte serialization stored as NSData property in your NSObject .您可以使用NSObject中存储为NSData属性的 hacky enum字节序列化来完成此操作。

enum Test {
    case testCase
    case testCase2
}

func bytes<T>(of value: T) -> [UInt8]{
    var value = value
    let size = MemoryLayout<T>.size
    return withUnsafePointer(to: &value, {
        $0.withMemoryRebound(to: UInt8.self,
            capacity: size,
            {
                Array(UnsafeBufferPointer(start: $0, count: size))
            })
        })
}

let testCase: Test = .testCase
let testCase2: Test = .testCase2

var testBytes = bytes(of: testCase)
var testBytes2 = bytes(of: testCase2)

let data = NSData(bytes: &testBytes, length: testBytes.count)
let data2 = NSData(bytes: &testBytes2, length: testBytes2.count)

let testCaseDeserialized = data.bytes.bindMemory(to: Test.self, capacity: 1).pointee
let testCase2Deserialized = data2.bytes.bindMemory(to: Test.self, capacity: 1).pointee

I tried bridging the flickrPhoto.datatakengranularity variable to NSObject via AnyObject like this:我尝试通过 AnyObject 将 flickrPhoto.datatakengranularity 变量桥接到 NSObject,如下所示:

photo.datetakengranularity = (flickrPhoto.datetakengranularity as AnyObject as! NSObject) 

And the compiler made no complaints.编译器没有抱怨。 This may be because in essence AnyObject is equivalent to NSObject and flickrPhoto.datetakengranularity is convertible to AnyObject.这可能是因为本质上 AnyObject 等价于 NSObject,而 flickrPhoto.datetakengranularity 可以转换为 AnyObject。 I don't know what happened underneath but it worked anyway.我不知道下面发生了什么,但它仍然有效。 I don't know why.我不知道为什么。

Thanks to all for your concern and support!感谢大家的关心和支持!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM