Как реализовать userDefaults с массивом глобальных переменных

У меня есть пустой массив, установленный как глобальная переменная, которая заполняется элементами массива из таблицы. Это используется для заполнения другого табличного представления. Эти данные должны сохраняться, чтобы, когда пользователь возвращается в приложение, его табличные данные находились в том же состоянии, в котором они его оставили, т. е. заполнялись данными из массива.

Хотя я искал десятки руководств и примеров. Я также сам взломал его, чтобы заставить его работать, и каждый раз, когда я перезагружаю приложение, массив пуст. Как я могу заставить этот глобальный массив переменных хранить данные массива?

var sharedData = [String]()

Это мой первый ВК, где у меня есть функции настройки для UserDefaults. И я выполнял свою функцию saveArray() каждый раз, когда в массив вносились изменения. Затем я выполнял функцию retrieveArray() каждый раз, когда мне нужно было загрузиться из массива.

import UIKit

var sharedData = [String]()
struct Keys {

    static let arrayKey = "arrayKey"
}

let defaults = UserDefaults.standard

func saveArray() {
    defaults.set(sharedData, forKey: Keys.arrayKey)
}

func retrieveArray() {
    var savedData = defaults.object(forKey: Keys.arrayKey) as? [String] ?? []
    savedData.append(contentsOf: sharedData)
}

class ViewController: UIViewController {

    var effect:UIVisualEffect!

    @IBOutlet weak var searchBar: UISearchBar!

    @IBOutlet var tableView: UITableView!

    @IBOutlet weak var activityIndicator: UIActivityIndicatorView!

    @IBOutlet weak var visualEffectView: UIVisualEffectView!

    let materialData = ["One", "Two", "Three", "Four"]

    var searchMaterial = [String]()
    var searching = false

    @IBAction func favoritesButtonArrayUpdate(_ sender: UIBarButtonItem) {
        print(sharedData)

    }

    override func viewDidLoad() {
        super.viewDidLoad()

    tableView.delegate = self
        tableView.dataSource = self

        saveArray()
        retrieveArray()

        print(sharedData)

    }

extension ViewController: UITableViewDelegate {

    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

        tableView.deselectRow(at: indexPath, animated: true)
        print(self.materialData[indexPath.row], "selected!")
    }

    func tableView(_ tableView: UITableView, editActionsForRowAt indexPath: IndexPath) -> [UITableViewRowAction]? {

        let favorite = UITableViewRowAction(style: .default, title: "Favorite") { (action, indexPath) in

            var data: String

            if self.searching {
                data = self.searchMaterial[indexPath.row]
            } else {
                data = self.materialData[indexPath.row]
            }

            sharedData.append(data)
            saveArray()
            print(sharedData)

        }

        favorite.backgroundColor = UIColor.orange
        return [favorite] 
    }

}

Это мой второй VC, который отображает данные массива, хранящиеся в глобальном массиве переменных sharedData. Я снова добавил все функции при внесении изменений в массив и извлечении данных из массива.

import UIKit

class FavoritesViewController: UIViewController {

    @IBOutlet var tableView: UITableView!

    override func viewDidLoad() {
        super.viewDidLoad()

        saveArray()
        retrieveArray()
    }
}

extension FavoritesViewController: UITableViewDelegate, UITableViewDataSource {

    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        retrieveArray()
        return sharedData.count
    }

    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        retrieveArray()
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        cell.textLabel?.numberOfLines = 0
        cell.textLabel?.text = sharedData[indexPath.row]
        return cell
    }

    func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
        return true
    }

    func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCell.EditingStyle, forRowAt indexPath: IndexPath) {
        if editingStyle == .delete {

            sharedData.remove(at: indexPath.row)
            saveArray()
            tableView.beginUpdates()
            tableView.deleteRows(at: [indexPath], with: .automatic)
            tableView.endUpdates()
        }
    }
}

person Sean Patterson    schedule 14.05.2020    source источник


Ответы (2)


Проблема может быть здесь:

let savedData: [String] = userDefaults.object(forKey: "arrayKey") as? [String] ?? []

Попробуйте изменить его с помощью:

let savedData: [String] = userDefaults?.object(forKey: "arrayKey") as? [String] ?? []

Это связано с тем, что UserDefaults должны быть развернуты, чтобы ссылаться на объект-член. Попробуйте

person MrHim    schedule 14.05.2020
comment
Xcode отвечает Невозможно использовать необязательную цепочку для необязательных значений типа «UserDefaults» и рекомендует удалить добавленный? - person Sean Patterson; 15.05.2020
comment
так в чем именно проблема у вас с оригинальной версией? (возможно, обновить вопрос с точной проблемой). Что именно вы имеете в виду, говоря, что мне нужно восстановить данные, присутствующие в массиве, при перезапуске приложения, хотите ли вы, чтобы они присутствовали при перезапуске или нет? (восстановление немного сложно понять). где вы собираетесь использовать userDefaults? Возможно, вам следует подумать об использовании suiteName. Взгляните на документацию Apple - person MrHim; 15.05.2020
comment
Прошу прощения, я не думаю, что у меня есть правильный словарный запас, чтобы быть очень ясным. В основном приложение начинается с пустого массива (называемого sharedData), который заполняется пользователем. Когда пользователь выходит из приложения и снова открывает его, массив снова становится пустым. Мне нужно, чтобы эти данные оставались в массиве. Или для сохранения в другом месте (userDefaults) и повторного заполнения массива при повторном открытии приложения. Я думаю, мне нужно иметь возможность сохранять состояние массива, если это так. - person Sean Patterson; 15.05.2020
comment
Это нормально. Если вы пишете userDefaults.set(sharedData, forKey: arrayKey), вы заполняете ключ arrayKey userDefaults данными в sharedData, но если вы только что запустили приложение, sharedData пуст, поэтому, если вы попытаетесь сразу после этого заполнить saveDate с помощью let saveData: [String] = userDefaults.object (forKey: arrayKey), это будет пусто, потому что arrayKey в userDefaults пуст. Таким образом, вам нужно заполнить arrayKey в пользовательских значениях по умолчанию с помощью shareData, когда shareData заполняется пользовательскими входами, а затем при запуске приложения заполнить сохраненные данные из пользовательских значений по умолчанию (где-то при запуске). - person MrHim; 15.05.2020
comment
недостаточно символов, чтобы быть достаточно ясным. Я имею в виду, что вы должны заполнить пользовательские значения по умолчанию в другой момент, чем заполнение сохраненных данных, или вы получите сохраненные данные, равные общим данным, которые пусты, если вы только что запустили приложение (A = B, B = C, поэтому A = C, если A = пусто, C = пусто). Поэтому найдите подходящий момент для сохранения sharedData в userDefaults и подходящий момент для заполнения saveData содержимым UserDefaults. Возможно, сделайте вторую вещь в классе запуска, если он у вас есть, или в делегате сцены для запуска, или что-то еще на основе вашего проекта. - person MrHim; 15.05.2020
comment
Спасибо за совет. Хорошо, я обновил свой пост и добавил, в основном, весь код. Я считаю, что сделал то, что вы упомянули. Но это все еще не работает. Я уверен, что это что-то маленькое, что я делаю неправильно, и я уверен, что это связано с тем, что вы говорите об использовании UserDefaults в неподходящее время для сохранения пустого массива. Я просто не могу определить точку, где я это делаю. - person Sean Patterson; 15.05.2020
comment
Это связано с тем, что вы вызываете saveArray() для viewDidLoad, поэтому первое, что вы делаете, это берете данные из sharedData (то есть пустого) и помещаете их в UserDefaults, поэтому пользовательские значения по умолчанию теперь будут пустыми, и когда вы вызываете retrieveArray(), он будет заполнять saveData с пустым значением (userdefaults arraykey). Возможно, неясно, что Userdefaults будут хранить постоянные данные, а не общие данные. И подумайте о необходимости UserDefaults(suitename:), если вам нужно обмениваться данными с расширениями. Посмотрите здесь ссылка - person MrHim; 15.05.2020
comment
Я разобрался. Нет необходимости в имени набора:. Мне просто нужно было наладить рабочий процесс. Я пытался слишком все усложнить. Спасибо за вашу помощь! - person Sean Patterson; 15.05.2020

Основываясь на рекомендациях MrHim, я удалил функции saveArray и retrieveArray из viewDidLoad моего первого VC и оставил retrieveArray в viewDidLoad моего второго ВК. Наличие saveArray в моем представленииDidLoads перезаписывало массив пустыми данными. Затем мне нужно было получить данные массива в нужном месте во втором VC. Затем в моем numberOfRowsInSection я удалил retrieveArray.

person Sean Patterson    schedule 15.05.2020