首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >当泛型类型通过参数传递时,Swift无法推断泛型类型

当泛型类型通过参数传递时,Swift无法推断泛型类型
EN

Stack Overflow用户
提问于 2018-12-13 00:03:58
回答 2查看 914关注 0票数 6

我正在为核心数据编写一个通用的包装类。

下面是我的一些基本类型。没有什么特别事情。

代码语言:javascript
复制
typealias CoreDataSuccessLoad = (_: NSManagedObject) -> Void
typealias CoreDataFailureLoad = (_: CoreDataResponseError?) -> Void
typealias ID = String


enum CoreDataResult<Value> {
    case success(Value)
    case failure(Error)
}

enum CoreDataResponseError : Error {
    typealias Minute = Int
    typealias Key = String
    case idDoesNotExist
    case keyDoesNotExist(key: Key)
    case fetch(entityName: String)
}

我已经将我的coredata写入抽象到一个协议中。如果您能让我知道您对我正在尝试实现的抽象的意见,我将不胜感激。然而,在扩展中,我遇到了以下错误:

无法将类型为“NSFetchRequest”的值转换为所需的参数类型“NSFetchRequest<_>”

我不确定我该怎么解决它。我尝试过更改代码的各种变体,但没有成功……

代码语言:javascript
复制
protocol CoreDataWriteManagerProtocol {
    associatedtype ManagedObject : NSManagedObject

    var persistentContainer : NSPersistentContainer {get}
    var idName : String {get}
    func loadFromDB(storableClass : ManagedObject.Type, id: ID) throws -> CoreDataResult<ManagedObject>
    func update(storableClass : ManagedObject.Type, id: ID, fields: [String : Any]) throws
    func fetch(request: NSFetchRequest<ManagedObject>, from context: NSManagedObjectContext)
    init(persistentContainer : NSPersistentContainer)
}

extension CoreDataWriteManagerProtocol {
    private func loadFromDB(storableClass : ManagedObject.Type, id: ID) -> CoreDataResult<ManagedObject>{
        let predicate = NSPredicate(format: "%@ == %@", idName, id)

        let fetchRequest : NSFetchRequest = storableClass.fetchRequest()
        fetchRequest.predicate = predicate

        // ERROR at below line!
        return fetch(request: fetchRequest, from: persistentContainer.viewContext) 
    }

    func fetch<ManagedObject: NSManagedObject>(request: NSFetchRequest<ManagedObject>, from context: NSManagedObjectContext) -> CoreDataResult<ManagedObject>{
        guard let results = try? context.fetch(request) else {
            return .failure(CoreDataResponseError.fetch(entityName: request.entityName ?? "Empty Entity Name")) // @TODO not sure if entityName gets passed or not.
        }
        if let result = results.first {
            return .success(result)
        }else{
            return .failure(CoreDataResponseError.idDoesNotExist)
        }
    }
}

另外,如果我更改了行:

代码语言:javascript
复制
let fetchRequest : NSFetchRequest = storableClass.fetchRequest()

至:

代码语言:javascript
复制
let fetchRequest : NSFetchRequest<storableClass> = storableClass.fetchRequest()

我得到以下错误:

未声明类型“storableClass”的

用法

我的直觉告诉我,编译器无法映射“属于类型的参数”,即它不理解storableClass实际上是一个类型。相反,它只能映射泛型参数或实际类型。因此,这是行不通的。

编辑:

我使用静态方法Vadian,并写道:

代码语言:javascript
复制
private func create(_ entityName: String, json : [String : Any]) throws -> ManagedObject {

    guard let entityDescription = NSEntityDescription.entity(forEntityName: entityName, in: Self.persistentContainer.viewContext) else {
        print("entityName: \(entityName) doesn't exist!")
        throw CoreDataError.entityNotDeclared(name: entityName)
    }

    let _ = entityDescription.relationships(forDestination: NSEntityDescription.entity(forEntityName: "CountryEntity", in: Self.persistentContainer.viewContext)!)
    let relationshipsByName = entityDescription.relationshipsByName

    let propertiesByName = entityDescription.propertiesByName

    guard let managedObj = NSEntityDescription.insertNewObject(forEntityName: entityName, into: Self.persistentContainer.viewContext) as? ManagedObject else {
        throw CoreDataError.entityNotDeclared(name: entityName)
    }

    for (propertyName,_) in propertiesByName {
        if let value = json[propertyName] {
            managedObj.setValue(value, forKey: propertyName)
        }
    }
    // set all the relationships
    guard !relationshipsByName.isEmpty else {
        return managedObj
    }

    for (relationshipName, _ ) in relationshipsByName {
        if let object = json[relationshipName], let objectDict = object as? [String : Any] {
            let entity = try create(relationshipName, json: objectDict)
            managedObj.setValue(entity, forKey: relationshipName)
        }
    }
    return managedObj
}

但是下面的部分不是通用的,因为我用as? ManagedObject来转换它。基本上,它不是Vadian所说的Swifty:

代码语言:javascript
复制
guard let managedObj = NSEntityDescription.insertNewObject(forEntityName: entityName, into: Self.persistentContainer.viewContext) as? ManagedObject else {
    throw CoreDataError.entityNotDeclared(name: entityName)
}

有什么办法可以解决这个问题吗?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-12-13 02:00:06

我的建议有点不同。它使用静态方法

NSManagedObject子类上调用loadFromDBfetch。这样做的好处是,总是返回关联的类型,而不进行任何进一步的类型转换。

另一个变化是throw错误。由于核心数据API广泛依赖于throw错误,我的建议是放弃CoreDataResult<Value>。所有错误都会被传递。如果成功,则返回对象,如果失败,则抛出错误。

我省略了与id相关的代码和update方法。您可以添加static func predicate(for id : ID)

代码语言:javascript
复制
protocol CoreDataWriteManagerProtocol {
    associatedtype ManagedObject : NSManagedObject = Self

    static var persistentContainer : NSPersistentContainer { get }
    static var entityName : String { get }
    static func loadFromDB(predicate: NSPredicate?) throws -> ManagedObject
    static func fetch(request: NSFetchRequest<ManagedObject>) throws -> ManagedObject
    static func insertNewObject() -> ManagedObject
}

extension CoreDataWriteManagerProtocol where Self : NSManagedObject {

    static var persistentContainer : NSPersistentContainer {
        return (UIApplication.delegate as! AppDelegate).persistentContainer
    }

    static var entityName : String {
        return String(describing:self)
    }

    static func loadFromDB(predicate: NSPredicate?) throws -> ManagedObject {
        let request = NSFetchRequest<ManagedObject>(entityName: entityName)
        request.predicate = predicate
        return try fetch(request: request)
    }

    static func fetch(request: NSFetchRequest<ManagedObject>) throws -> ManagedObject {
        guard let results = try? persistentContainer.viewContext.fetch(request) else {
            throw CoreDataResponseError.fetch(entityName: entityName)
        }
        if let result = results.first {
            return result
        } else {
            throw CoreDataResponseError.idDoesNotExist
        }
    }

    static func insertNewObject() -> ManagedObject {
        return NSEntityDescription.insertNewObject(forEntityName: entityName, into: persistentContainer.viewContext) as! ManagedObject
    }
}
票数 8
EN

Stack Overflow用户

发布于 2018-12-13 00:49:57

问题是NSManagedObject.fetchRequest()的返回类型为NSFetchRequest<NSFetchRequestResult>,它是非泛型的。您需要更新fetch函数的定义来说明这一点。顺便说一句,协议扩展中默认实现的函数签名实际上与协议定义中的函数签名不匹配,因此这些签名也需要更新。

您还需要更改fetch(request:,from:)的实现,因为NSManagedObjectContext.fetch()返回一个[Any]类型的值,因此您需要将其强制转换为[ManagedObject],以匹配您自己的fetch方法的类型签名。

代码语言:javascript
复制
protocol CoreDataWriteManagerProtocol {
    associatedtype ManagedObject : NSManagedObject

    var persistentContainer : NSPersistentContainer {get}
    var idName : String {get}
    func loadFromDB(storableClass : ManagedObject.Type, id: ID) throws -> CoreDataResult<ManagedObject>
    func update(storableClass : ManagedObject.Type, id: ID, fields: [String : Any]) throws
    func fetch(request: NSFetchRequest<NSFetchRequestResult>, from: NSManagedObjectContext) -> CoreDataResult<ManagedObject>
    init(persistentContainer : NSPersistentContainer)
}

extension CoreDataWriteManagerProtocol {
    private func loadFromDB(storableClass : ManagedObject.Type, id: ID) -> CoreDataResult<ManagedObject>{
        let predicate = NSPredicate(format: "%@ == %@", idName, id)

        let fetchRequest = storableClass.fetchRequest()
        fetchRequest.predicate = predicate

        return fetch(request: fetchRequest, from: persistentContainer.viewContext)
    }

    func fetch(request: NSFetchRequest<NSFetchRequestResult>, from context: NSManagedObjectContext) -> CoreDataResult<ManagedObject> {
        guard let results = (try? context.fetch(request)) as? [ManagedObject] else {
            return .failure(CoreDataResponseError.fetch(entityName: request.entityName ?? "Empty Entity Name")) // @TODO not sure if entityName gets passed or not.
        }
        if let result = results.first {
            return .success(result)
        }else{
            return .failure(CoreDataResponseError.idDoesNotExist)
        }
    }
}
票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/53746921

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档