хранение временных данных от пользователя через разные контроллеры перед их сохранением

Лучшая практика для хранения временных данных во время сеанса? У меня есть приложение, которое позволяет мне регистрировать клиента в медицинском учреждении, вводя биометрические и личные данные (имя, вес, личные параметры и т. Д.), После сохранения данных вы переходите на другие страницы, где вы можете выполнить какую-либо операцию с помощью данные. Только в самом конце этого пути (через 3-4 контроллера) я сохраняю Заказчика в локальную базу через Realm.

на этапе регистрации мне нужно сохранить данные TempUser, к которым можно получить доступ с нескольких контроллеров.

Я думаю, что мог бы использовать синглтоны, но не уверен, ЕСЛИ и КАК

Я финансирую это, но не уверен, какое решение подходит лучше.

моя идея:

class AppSession {
    
    static var shared = AppSession()
    
    private init() {}
    
    var currentPatient: Patient?
    
}

class Patient {
    
    let shared = Patient()
    
    private init() {}
    
    var name: String? = ""
    var weight: Double = 0.0
}

и в моем делегате текстового поля:

AppSession.shared.currentPatient?.name = data //text from textfield

в противном случае использование Patient как структуры, а не класса


person biggreentree    schedule 08.03.2021    source источник


Ответы (1)


Трудно сказать, какое решение подходит для каждого отдельного случая. Это действительно зависит от того, насколько широко данные используются в вашем приложении. Это общее для каждой части или только для некоторых конкретных ViewController? Сколько раз он извлекается и обновляется? Также это зависит от используемого вами архитектурного шаблона и многих других факторов. Короче говоря, ваше решение с использованием одноэлементного шаблона может быть действительным, но вы не можете сказать наверняка, не внимательно изучив ваш проект. ​Если вы решите придерживаться своего решения, вы можете опустить класс AppSession и получить прямой доступ к вашему Patient.

class Patient {
    static let shared = Patient()

    var name: String? = ""
    var weight: Double = 0.0
}

И обновите его:

Patient.shared.name = ""

Несмотря на то, что ваше решение с использованием одноэлементного шаблона может быть правильным, как упоминалось выше, вы также должны учитывать точку зрения пользователя: хотите ли вы, чтобы пользователь мог продолжить поток даже после того, как приложение было уничтожено? Если да, то вам следует сохранять данные после каждого шага (так что, возможно, KeyChain, поскольку то, что вы описываете, является конфиденциальными данными). Или лучшим решением является хранение данных на стороне сервера, что дает пользователю возможность продолжать работу даже на другом устройстве (возможно, Android).

Но вы можете сказать, что вы действительно не заботитесь ни о KeyChain, ни об общении с сервером, и хотите делать это локально. В этом случае лично я бы передал данные между контроллерами и избегал синглтонов. Давайте возьмем ваш объект Patient и создадим протокол, который потребует, чтобы каждый UIViewController, соответствующий ему, имел сохраненное свойство Patient.

struct Patient {
    var name: String? = ""
    var weight: Double = 0.0
}

protocol MyOnboardingProtocol: class {
    var data: Patient { get set }
}

С помощью MyOnboardingProtocol вы отметите каждый контроллер, который потребует хранения данных Patient. Таким образом, вы заставите ViewControllers удерживать объект Patient и с первого взгляда будете знать, принадлежит ли ваш ViewController потоку Onboarding.

В вашем первом UIViewController, где начинается ваш поток, вы инициализируете свой объект Patient (возможно, также сделаете некоторые обновления на основе пользовательского ввода) и пройдете для следующего UIViewController:

class MyFirstViewController: UIViewController, MyOnboardingProtocol {
    var data: Patient = Patient()

    func nextViewController() {
        let viewController = MySecondViewController(data: data)
        navigationController?.pushViewController(viewController, animated: true)
    }
}

Для каждого другого UIViewController в будущем вы будете передавать обновленный объект в инициализацию:

class MySecondViewController: UIViewController, MyOnboardingProtocol {
    var data: Patient

    init(data: Patient) {
        self.data = data
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
    }
}

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

person πter    schedule 08.03.2021
comment
спасибо за вашу помощь, ваше решение с использованием протокола очень интересно, но нет проблем с тем, что Struct является типом значения? Я боюсь, что может возникнуть проблема с копированием объектов вместо передачи. - person biggreentree; 09.03.2021
comment
ну, вы также можете использовать для этого class, чтобы ссылка была передана. Это избавит вас от некоторых проблем, например, при переходе назад в потоке, если вы используете struct, вам нужно будет передать текущий объект обратно предыдущему UIViewController. Так что используйте class, если вам это больше подходит. Я просто лично привык использовать structs всякий раз, когда могу. Но это всего лишь личное предпочтение. - person πter; 09.03.2021
comment
Благодарность!. последний вопрос, ваш код во втором контроллере падает из-за // фатальной ошибки (init(coder:) не был реализован), не так ли, поскольку мой контроллер находится в раскадровке? - person biggreentree; 09.03.2021
comment
Да. Это из-за раскадровки. Вы можете просто удалить оба метода инициализации, сделать data: Patient либо необязательным (?), либо неявно развернутым (!), а при переходе назначать данные ViewController viewController?.data = data - person πter; 09.03.2021