当通过参数传递泛型类型时,Swift无法推断泛型类型 [英] Swift can't infer generic type when generic type is being passed through a parameter
问题描述
我正在为核心数据编写通用包装类.
I'm writing a generic wrapper class for core data.
这是我的一些基本类型.没什么特别的.
Here are some of my basic types. Nothing special.
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写入了协议中.如果您让我知道您对我要提出的抽象的意见,我将不胜感激. 但是在扩展程序中,我遇到了以下错误:
I've abstracted my coredata writes in a protocol. I'd appreciate if you let me know of your comments about the abstraction I'm trying to pull off. Yet in the extension I run into the following error:
无法将"NSFetchRequest"类型的值转换为 预期参数类型'NSFetchRequest< _>'
Cannot convert value of type 'NSFetchRequest' to expected argument type 'NSFetchRequest<_>'
不确定我该如何解决.我尝试过各种更改代码的尝试,但没有成功...
Not sure exactly how I can fix it. I've tried variations of changing my code but didn't find success...
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)
}
}
}
另外,如果我更改行:
let fetchRequest : NSFetchRequest = storableClass.fetchRequest()
收件人:
let fetchRequest : NSFetchRequest<storableClass> = storableClass.fetchRequest()
我收到以下错误:
使用未声明的类型'storableClass'`
Use of undeclared type 'storableClass'`
我的直觉告诉我,编译器无法映射类型的参数",即它不理解storableClass
实际上是类型.相反,它只能映射泛型参数或实际类型.因此,这是行不通的.
My intuition tells me that the compiler can't map 'parameters that are types' ie it doesn't understand that storableClass
is actually a type. Instead it can only map generics parameters or actual types. Hence this doesn't work.
我使用静态方法Vadian并将其编写为:
I used static approach Vadian and wrote this:
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:
But the following piece of it is not generic as in I'm casting it with as? ManagedObject
. Basically it's not Swifty as Vadian puts it:
guard let managedObj = NSEntityDescription.insertNewObject(forEntityName: entityName, into: Self.persistentContainer.viewContext) as? ManagedObject else {
throw CoreDataError.entityNotDeclared(name: entityName)
}
有什么办法解决吗?
推荐答案
我的建议有些不同.它使用静态方法
My suggestion is a bit different. It uses static methods
在NSManagedObject
子类上调用loadFromDB
和fetch
.这样做的好处是,始终返回关联的类型,而无需任何其他类型的强制转换.
Call loadFromDB
and fetch
on the NSManagedObject
subclass. The benefit is that always the associated type is returned without any further type cast.
另一个变化是throw
错误.由于Core Data API广泛依赖throw
错误,因此我建议删除CoreDataResult<Value>
.所有错误均已通过.成功返回对象,失败返回错误.
Another change is throw
ing errors. As the Core Data API relies widely on throw
ing errors my suggestion is to drop CoreDataResult<Value>
. All errors are passed through. On success the object is returned, on failure an error is thrown.
我省略了id
相关代码和update
方法.您可以添加static func predicate(for id : ID)
I left out the id
related code and the update
method. You can add a static func predicate(for id : ID)
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
}
}
这篇关于当通过参数传递泛型类型时,Swift无法推断泛型类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!