Swift не может вывести универсальный тип, когда универсальный тип передается через параметр - PullRequest
0 голосов
/ 12 декабря 2018

Я пишу универсальный класс-обертку для основных данных.

Вот некоторые из моих основных типов.Ничего особенного.

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 <_>'

Не совсем точнокак я могу это исправить.Я пробовал варианты изменения моего кода, но не нашел успеха ...

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'`

Моя интуиция говорит мне, что компилятор не может отобразить "параметры, которыеявляются типами 'т.е. он не понимает, что storableClass на самом деле является типом.Вместо этого он может отображать только общие параметры или фактические типы.Следовательно, это не работает.

РЕДАКТИРОВАТЬ:

Я использовал статический подход Vadian и написал это:

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
}

Но следующий фрагмент не является общим, как в IКастую с as? ManagedObject.По сути, это не Swifty, как говорит Вадиан:

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

Есть ли способ обойти это?

Ответы [ 2 ]

0 голосов
/ 12 декабря 2018

Мое предложение немного отличается.Он использует статические методы

Вызов loadFromDB и fetch в подклассе NSManagedObject.Преимущество заключается в том, что всегда связанный тип возвращается без дальнейшего приведения типа.

Еще одно изменение - throw ошибки.Поскольку Core Data API широко использует ошибки throw, я предлагаю отказаться от CoreDataResult<Value>.Все ошибки пропущены.В случае успеха объект возвращается, в случае ошибки выдается ошибка.

Я пропустил связанный код id и метод update.Вы можете добавить 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
    }
}
0 голосов
/ 12 декабря 2018

Проблема в том, что NSManagedObject.fetchRequest() имеет тип возврата NSFetchRequest<NSFetchRequestResult>, который не является универсальным.Вам необходимо обновить определение вашей функции fetch, чтобы учесть это.Кстати, сигнатуры функций реализаций по умолчанию в расширении протокола фактически не совпадают с сигнатурами функций в определении протокола, поэтому их также необходимо обновить.

Вам также необходимо изменить реализацию fetch(request:,from:), поскольку NSManagedObjectContext.fetch() возвращает значение типа [Any], поэтому вам необходимо привести его к [ManagedObject], чтобы соответствовать сигнатуре типа вашего собственного fetch метода.

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)
        }
    }
}
...