Как я могу набрать check дженерик и затем быстро привести его к нему? - PullRequest
0 голосов
/ 01 июня 2019

Мне нравятся идеи , представленные в этом посте , о создании независимого от базы данных, ориентированного на протокол кода.

Так, скажем, у меня есть протокол, такой как:

protocol Database {
    func loadObjects<T>(matching query: Query) -> [T]
    func loadObject<T>(withID id: String) -> T?
    func save<T>(_ object: T)
}

, где Query - это структура, которая имеет спецификаторы фильтра и сортировки.

Затем, учитывая постоянную структуруНапример, Realm или CoreData, я могу просто поддерживать этот протокол следующим образом:

extension NSManagedObjectContext: Database {
    ...
}

extension Realm: Database {
    ...
}

extension MockedDatabase: Database {
    ...
}

extension UITestingDatabase: Database {
    ...
}

Проблема возникает, когда я хочу использовать CoreData.

Если мы посмотрим на метод:

func loadObjects<T>(matching query: Query) -> [T]

У меня нет возможности «привести» T к NSManagedObject.

Например, моя желаемая реализация может выглядеть примерно так:

extension NSManagedObjectContext: Database {

    func loadObjects<T>(matching query: Query<T>) -> [T] {

        // you need a fetch request for these models.  This guard statement compiles.  How do we make it work with NSFetchRequestResult however?
        guard T.self is NSManagedObject.Type else {
            return []
        }

        // This line below Fails compiling.  Type 'T' does not conform to protocol 'NSFetchRequestResult'
        var request = NSFetchRequest<T>(entityName: String(describing: T))

        // then set sortDescriptors and predicate, etc.

        var objects: [T] = []
        self.performAndWait {
            do {
                if let results = try self.fetch(request!) as? [T] {
                    objects = results
                }

            } catch let error {
                print("Error fetching: \(error.localizedDescription)")
            }
        }
        return objects
    }
}

Так что, если я могу определить, является ли T типом своего рода NSManagedObject, то возможно ли создать экземпляр NSFetchRequest путем «приведения» T к чему-то, что будет работать?Казалось бы, я не могу привести или заставить T быть чем-либо.

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

Как я могу сделать это в Swift?Должен ли я добавить к протоколу Model, чтобы вернуть дополнительные параметры, которые будут использоваться для заданных платформ, которые я буду поддерживать?Или заставить их поддерживать NSFetchRequestResult?Я бы предпочел, чтобы только реализация протокола требовала внимания к деталям структуры персистентности.

Ответы [ 2 ]

0 голосов
/ 02 июня 2019

Похоже, я могу ответить на свой вопрос. Можно дополнительно ограничить универсальные типы, перегрузив их, так что вызывающий код может остаться прежним, но то, что вызывается , зависит от типа, который вы передаете в него.

Этот код ниже демонстрирует это на детской площадке:

public protocol Action {
    func doSomething<T>(to object: T)
}

public class MyActor {

}

extension MyActor: Action {

    // works for any type
    public func doSomething<T>(to object: T) {
        print("was generic")
    }

    // but if you constrain the type and your object fits that constraint...
    // this code is called (same method signature)
    public func doSomething<T: NSObject>(to object: T) {
        print("was an object")
    }
}

class MyObject: NSObject {
    var name: String = "Object"
}

struct MyStruct {
    var name: String = "Struct"
}

let actor = MyActor()
let object = MyObject()
let value = MyStruct()

actor.doSomething(to: value) // prints 'was generic'
actor.doSomething(to: object)  // prints 'was an object'

Таким образом, в исходном примере я бы поддержал Database для CoreData с:

extension NSManagedObjectContext: Database {

    func loadObjects<T>(matching query: Query<T>) -> [T] {
       return [] // return an empty array because we only support NSManagedObject types
    }

    func loadObjects<T: NSManagedObject>(matching query: Query<T>) -> [T] {

        let request = NSFetchRequest<T>(entityName: String(describing: T.self))

        var objects = [T]()
        self.performAndWait {
            do {
                objects = try self.fetch(request) // a generic fetch request returns an array of the generic type or throws an error
            } catch let error {
                print("Error fetching: \(error.localizedDescription)")
            }
        }
        return objects
    }
}
0 голосов
/ 01 июня 2019

Вместо проверки типа в время выполнения ограничить тип в время компиляции , например

extension NSManagedObjectContext: Database {

    func loadObjects<T: NSManagedObject>(matching query: Query<T>) -> [T] {

        let request = NSFetchRequest<T>(entityName: String(describing: T.self))

        var objects = [T]()
        self.performAndWait {
            do {
                objects = try self.fetch(request) // a generic fetch request returns an array of the generic type or throws an error
            } catch let error {
                print("Error fetching: \(error.localizedDescription)")
            }
        }
        return objects
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...