web-dev-qa-db-fra.com

Meilleure pratique pour stocker des données temporaires dans swift

J'ai développé une application qui obtient beaucoup de données JSON du serveur et affiche sur différents VC. Ce que j'ai fait était d'obtenir les données et en définissant les données sur des variables statiquesdans toute mon application.

Maintenant, j'ai remarqué que j'aurais pu utiliser CoreData ou peut-être UserDefaults à la place de Static variables.

Ma question est "Quelle est la meilleure pratique pour ce type d'applications et pourquoi exactement?"

NOTE: l'application n'a pas besoin de travailler hors connexion ni d'enregistrer des données pour s'afficher en mode hors connexion.

5
Saeed Hajizade

Vous ne devez pas stocker de données dans UserDefaults. Par défaut, cet utilisateur est simplement un fichier basé sur une valeur de clé qui est principalement utilisé pour stocker des données sur l'utilisateur, par exemple:

doNotShowAdds : true
isUserLogedIn :true.

Vous ne devez pas non plus utiliser de trousseau, car ce dernier est un conteneur chiffré dans lequel vous stockez des données critiques sur l'utilisateur, par exemple: mot de passe.

Vous n'avez pas besoin d'utiliser la base de données

Mon opinion est que vous ne devriez pas utiliser de variables globales, principalement j'essaie de rendre les données de viewController spécifiques pour elles-mêmes, les données ne devraient pas être accessibles de n'importe où dans votre projet. Quand utiliser des variables globales dans Swift

La plupart du temps, je crée des protocoles de service avec des implémentations pour des contrôleurs de vue spécifiques. Quoi qu'il en soit, chaque contrôleur devrait avoir sa propre partie de données, par exemple

class MyViewControllerOne {
    private var data :Array<DataExampleOne>;
}

class MyViewControllerTwo {
    private var data :Array<DataExampleTwo>;
}

Une fois que vous avez chargé des données à partir de votre API, les données seront là jusqu'à la fermeture de votre application.

Vous pouvez toujours créer une autre classe qui contiendra ces données et peut-être des prédicats pour une approche plus propre, par exemple:

protocol MyViewControllerOneService {
    func search() -> Array<DataExampleOne>;
}

class MyViewControllerOneServiceImpl :MyViewControllerService {
public var data :Array<DataExampleOne>;

public func search(searchString :String) -> Array<DataExampleOne>{
    let predicate = NSPredicate() ...
    let filteredArray = self.data.filter { evaluate...}
    return filteredArray;
    }
}

J'utilise une approche similaire, au lieu de sauvegarder des données dans mes classes de service (logique métier), j'ai des référentiels que j'utilise pour obtenir des données de base de données. Puisque vous n'avez pas de base de données, cette approche est acceptable.

et puis au lieu de

class MyViewControllerOne {
    private var data :Array<DataExampleOne>;
}

vous pouvez utiliser

class MyViewController {
    private var myViewControllerService :MyViewControllerOneServiceImpl;
}

J'espère que ça aide, Cordialement!

3
LightinDarknessJava

Discussion:

"Quelle est la meilleure pratique pour ce type d'applications et pourquoi exactement?" C'est une très bonne question. Cela vous aiderait si vous décriviez le type d'application que vous développez.

Étant donné que vous semblez n'avoir besoin que de vouloir les données lorsque l'application est en cours d'exécution, le meilleur moyen est de le conserver en mémoire sur certaines variables singleton ou statiques que vous êtes déjà en train de faire.

Mais si vous ne voulez pas stocker vos données après le redémarrage de l'application, vous souhaiterez peut-être que vos données expirent après une certaine durée, étant donné que l'application peut rester ouverte très longtemps.

Ensuite, la question est de savoir comment vous souhaitez recevoir vos données au moment de l'exécution. Supposons que vous avez mis en cache des données et que vous souhaitez quand même actualiser les données. Ensuite, vous pouvez concevoir votre gestion de données afin que votre fermeture pour récupérer des éléments puisse être appelée plusieurs fois. Supposons avoir quelque chose comme ça:

    func fetchUsers(callback: ((_ users: [Any]?, _ isFromCache: Bool) -> Void)) {
        if let cachedUsers = MyCache.fetch(.users) {
            callback(cachedUsers, true)
        }

        APIManager.fetch(.users, { users, error in
            if let users = users {
                MyCache.insert(.users, items: users)
            }
            callback(users, false)
        })
    }

Ensuite, supposons que vous vouliez montrer à ces utilisateurs dans une vue de table que vous feriez quelque chose comme:

    func refreshData() {
        self.startActivityIndicator() // Start some minor activity indicator
        fetchUsers { (users, isFromCache) in
            self.users = users // Assign users whatever they are
            if users == nil {
                // Show error here
            }

            if  isFromCache == false {
                self.stopActivityIndicator() // Only stop activity when fresh data were received so user know he may expect the UI to change
            }

            tableView.reloadData()
        }
    }

Ceci concevra votre interface utilisateur dans la récupération des données mais n'aura rien à voir avec où et comment vos données sont stockées. Par exemple, MyCache doit toujours choisir si la réponse en cache est toujours valide ou obsolète. Et c'est MyCache qui décide de stocker les données dans une base de données ou uniquement en mémoire.

Quelques procédures de stockage:

Pour conserver la conception dans les extraits précédents, vous pouvez avoir une classe statique MyCache qui comporte une énumération de chaîne pour les types stockés. Il a ensuite un dictionnaire statique [String: Any] qui contient les données en cache de sorte qu'une méthode d'extraction ressemble à

func fetch(type: FetchType) {
    return cacheDictionary[type]
}

L'insertion est assez similaire alors.

Pour ajouter une date (ou toute autre donnée ici), vous pouvez le définir en tant que [String: [String: Any]] et utiliser des clés statiques afin que vos données soient alors cacheDictionary[type]["date"] as? Date et les données en tant que cacheDictionary[type]["data"].

Vous pouvez ensuite développer votre cache pour utiliser, par exemple, les paramètres utilisateur par défaut, tels que:

func fetch(type: FetchType) {
    if let inMemory = cacheDictionary[type] {
        return inMemory
    } else if let inDefaults = UserDefaults.standard.object(forKey: type) {
        cacheDictionary[type] = inDefaults // Keep it in memory for net time
        return inDefaults
    } else {
        return nil
    }    
}

C'est pour stocker des données ou des objets JSON directs. Idem peut être utilisé pour stocker des données (en les sérialisant avec JSONSerializer) dans des fichiers. Vous pouvez créer un chemin de base dans un répertoire temporaire ou dans une bibliothèque, puis utiliser les noms de fichiers de type. Le répertoire temporaire peut être effacé par le système que vous souhaitez et le répertoire de la bibliothèque sera persistant.

Ensuite il y a la base de données:

La base de données est une toute autre histoire. Il n’a aucun sens d’appliquer une base de données à la même procédure que celle décrite ci-dessus, car il s’agit d’un excès et on n’utilise aucun avantage de la base de données.

Pour la base de données, créez des modèles pour chacune de vos réponses et insérez les données dans la base de données, puis informez vos contrôleurs d'interface utilisateur de les mettre à jour. Cela signifie que les rappels ne sont pas la meilleure pratique mais plutôt des délégués. Par exemple, vous auriez DatabaseManager et à l'écran, vous appelleriez DatabaseManager.shared.delegate = self puis Users.fetchNewUsers() et attendriez que le délégué se déclenche. Ensuite, par délégation, vous récupérez des objets de la base de données ou créez un contrôleur de résultat de récupération ...

Il existe probablement de très nombreuses autres manières, mais j'espère que cela vous donnera une idée de la manière de stocker des données pour les utiliser.

2
Matic Oblak

Fondamentalement, je propose trois options:

Variable globale: Je dois définir globalement la structure:

struct GlobalUser {
    static var name : String

}

maintenant, je peux simplement appeler la variable globale de n’importe où dans mon projet:

GlobalUser.name = "Nazmul Hasan"

UserDefaults

il utilise pour le magasin de données mais qui ne sont pas des données sensibles:

      //key
       struct defaultsKeys {
        static var name = "name"   
       }

     //Setting 
        let defaults = UserDefaults.standard
        defaults.set("Nazmul", forKey: defaultsKeys.name)

    // Getting
    let defaults = UserDefaults.standard
    if let userName = defaults.string(forKey: defaultsKeys.name) {
        print(userName) // print what you store 
    }

keyChain il utilise pour le magasin de données mais il s'agit de données sensibles telles que nom d'utilisateur, mot de passe ou jetons.

Sauvegarder et récupérer une valeur via KeyChain Swift

mais je recommande personnellement de diffuser ce blog publié par Apple

Directives de stockage de données Apple iOS

plus intéressé lire ce fil de question:

Comment enregistrer des données locales dans une application Swift?

2
Nazmul Hasan

Vous pouvez créer une classe de modèle de données pour stocker des données temporaires à partir d'une réponse JSON.

Exemple de classe de modèle de données: -

class RootClass : NSObject, NSCoding{

    var array : [Int]!
    var booleanField : Bool!
    var custArray : [CustArray]!
    var nullField : AnyObject!
    var number : Int!
    var object : Object!
    var string : String!


    /**
     * Instantiate the instance using the passed dictionary values to set the properties values
     */
    init(fromDictionary dictionary: [String:Any]){
        booleanField = dictionary["Boolean"] as? Bool
        nullField = dictionary["Null"] as? AnyObject
        number = dictionary["Number"] as? Int
        string = dictionary["String"] as? String
        if let objectData = dictionary["Object"] as? [String:Any]{
            object = Object(fromDictionary: objectData)
        }
        custArray = [CustArray]()
        if let custArrayArray = dictionary["CustArray"] as? [[String:Any]]{
            for dic in custArrayArray{
                let value = CustArray(fromDictionary: dic)
                custArray.append(value)
            }
        }
    }

    /**
     * Returns all the available property values in the form of [String:Any] object where the key is the approperiate json key and the value is the value of the corresponding property
     */
    func toDictionary() -> [String:Any]
    {
        var dictionary = [String:Any]()
        if booleanField != nil{
            dictionary["Boolean"] = booleanField
        }
        if nullField != nil{
            dictionary["Null"] = nullField
        }
        if number != nil{
            dictionary["Number"] = number
        }
        if string != nil{
            dictionary["String"] = string
        }
        if object != nil{
            dictionary["object"] = object.toDictionary()
        }
        if custArray != nil{
            var dictionaryElements = [[String:Any]]()
            for custArrayElement in custArray {
                dictionaryElements.append(custArrayElement.toDictionary())
            }
            dictionary["custArray"] = dictionaryElements
        }
        return dictionary
    }

    /**
     * NSCoding required initializer.
     * Fills the data from the passed decoder
     */
    @objc required init(coder aDecoder: NSCoder)
    {
        array = aDecoder.decodeObject(forKey: "Array") as? [Int]
        booleanField = aDecoder.decodeObject(forKey: "Boolean") as? Bool
        custArray = aDecoder.decodeObject(forKey: "CustArray") as? [CustArray]
        nullField = aDecoder.decodeObject(forKey: "Null") as? AnyObject
        number = aDecoder.decodeObject(forKey: "Number") as? Int
        object = aDecoder.decodeObject(forKey: "Object") as? Object
        string = aDecoder.decodeObject(forKey: "String") as? String
    }

    /**
     * NSCoding required method.
     * Encodes mode properties into the decoder
     */
    @objc func encode(with aCoder: NSCoder)
    {
        if array != nil{
            aCoder.encode(array, forKey: "Array")
        }
        if booleanField != nil{
            aCoder.encode(booleanField, forKey: "Boolean")
        }
        if custArray != nil{
            aCoder.encode(custArray, forKey: "CustArray")
        }
        if nullField != nil{
            aCoder.encode(nullField, forKey: "Null")
        }
        if number != nil{
            aCoder.encode(number, forKey: "Number")
        }
        if object != nil{
            aCoder.encode(object, forKey: "Object")
        }
        if string != nil{
            aCoder.encode(string, forKey: "String")
        }
    }
}

Un moyen simple de créer une classe de modèle de données: - jsoncafe.com

0
teja_D