2017-08-30 2 views
0

Je suis en train de concevoir un gestionnaire de données pour mon modèle de base de données et j'aimerais créer une fonction générique pour extraire les parents d'une classe.Obtenir les données de base de l'entité de données avec une fonction générique

J'ai créé un protocole permettant de créer des gestionnaires pour chaque type de données. Dans ce protocole j'ai déjà défini deux types associés T et K et plusieurs fonctions simples. Maintenant, je suis coincé avec une méthode de récupération de la classe des parents - Je dois indiquer d'une manière ou d'une autre que T a K des parents. J'ai essayé en vain de créer un protocole indiquant cette relation à travers des propriétés mutuelles, afin que les deux classes puissent se conformer à ce protocole. Une idée, est-ce même possible?

import Foundation 
import CoreData 

protocol DataManager { 

    associatedtype T: NSManagedObject, NSFetchRequestResult 
    associatedtype K: NSManagedObject, NSFetchRequestResult // Relative 

    static var sharedInstance: Self { get } 

    static func getAll(sorted: [NSSortDescriptor]?, context: NSManagedObjectContext) -> [T]? 
    static func insert(item: T) 
    static func update(item: T) 
    static func clean() 
    static func deleteById(id: String) 

    // Relatives 
    static func getRelatives(by: T) -> [K]? 
    static func get(byRelative: K) -> [T]? 
} 

extension DataManager { 

    static func getAll(sorted: [NSSortDescriptor]?, context: NSManagedObjectContext) -> [T]? { 

     guard let fetchRequest: NSFetchRequest<T> = T.fetchRequest() as? NSFetchRequest<T> else { return nil } 
     fetchRequest.sortDescriptors = sorted 

     var results: [T]? = nil 

     do { 
      results = try context.fetch(fetchRequest) 
     } catch { 
      assert(false, error.localizedDescription) 
     } //TODO: Handle Errors 

     return results 
    } 
} 


protocol Identifiable { 
    typealias Identity = String 
    var id: Identity? { get } 
} 


extension DataManager where Self.T: Identifiable { 

    static func get(by id: T.Identity, context: NSManagedObjectContext) -> T? { 

     guard let fetchRequest: NSFetchRequest<T> = T.fetchRequest() as? NSFetchRequest<T> else { return nil } 

     fetchRequest.predicate = NSPredicate(format: "%K == %@", "id", id) 

     var rawResults: [T]? = nil 

     do { 
      rawResults = try context.fetch(fetchRequest) 
     } catch { 
      assert(false, error.localizedDescription) 
     } //TODO: Handle Errors 


     if let result = rawResults?.first { 
      return result } 
     else { return nil } 
    } 
} 

Répondre

0

Eh bien, j'ai créé une solution. Nous pouvons identifier toutes les relations avec une classe particulière:

let relationships = T.entity().relationships(forDestination: K.entity()) 

Il nous permet de trouver tous les identifiants d'un élément pour chaque relation (nous pouvons avoir de nombreuses relations pour la même entité relative):

let relativesIDs = item.objectIDs(forRelationshipNamed: relationship.name) 

Donc, nous pouvons utiliser ces ID pour récupérer des enregistrements d'une autre classe.

static func getRelatives(of item: T, context:NSManagedObjectContext) -> [K]? { 

    guard let fetchRequest: NSFetchRequest<K> = K.fetchRequest() as? NSFetchRequest<K> else { return nil } 
    fetchRequest.fetchBatchSize = 100 

    var results: [K]? = nil 

    var resultSet: Set<K> = [] // doesn't allow duplicates 

    let relationships = T.entity().relationships(forDestination: K.entity()) 

    for relationship in relationships { 

     let relativesIDs = item.objectIDs(forRelationshipNamed: relationship.name) 
     let predicate = NSPredicate(format: "self IN %@", relativesIDs) 
     fetchRequest.predicate = predicate 

     var batchResults: [K] = [] 

     do { 
      batchResults = try context.fetch(fetchRequest) 
     } catch { 
      assert(false, error.localizedDescription) 
     } //TODO: Handle Errors 

     if batchResults.count > 0 { resultSet = resultSet.union(Set(batchResults)) } 
    } 

    if resultSet.count > 0 { results = Array(resultSet) } 

    return results 
} 

Je ne suis pas sûr que ce soit la solution la plus élégante, mais il fonctionne :-)