Как обновить все внедренные объекты новыми живыми данными - PullRequest
2 голосов
/ 09 мая 2020

Я использую внедрение зависимостей, координаторов и шаблон проектирования MVP.

Как только пользователь входит в мое приложение, я получаю его объект учетной записи из Firestore в моем AppCoordinator. Этот объект учетной записи и другие службы вводятся в мой TabBarCoodinator, который вводит их во всех дочерних координаторов, и они делают то же самое с докладчиками, и т. Д. c.

init(_ navigationController: UINavigationController,
     _ firestoreRepository: FirestoreRepositoryProtocol,
     _ firebaseCloudStorageService: FirebaseCloudStorageServiceProtocol,
     _ firebaseCrashlyticsService: FirebaseCrashlyticsServiceProtocol,
     _ notificationService: NotificationServiceProtocol,
     _ account: FIRAccount,
     _ alertHandlerService: AlertHandlerServiceProtocol) {

    self.navigationController = navigationController

    super.init()

    self.coord1 = Coord1(
        firestoreRepository,
        firebaseCloudStorageService,
        geoLocationService,
        alertHandlerService,
        account,
        delegate: self)

    self.coord2 = Coord2(
        firestoreRepository,
        notificationService,
        alertHandlerService,
        account)

    self.coord3 = Coord3(
        firestoreRepository,
        firebaseCloudStorageService,
        firebaseCrashlyticsService,
        geoLocationService,
        alertHandlerService,
        account,
        delegate: self)
}

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

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

Каждая часть приложения, например EditProfilePresenter, воспроизводит роль в редактировании части объекта счета. В объект учетной записи конкретного докладчика вносятся изменения, и изменения передаются в Firestore.

Это приводит к огромной проблеме: все объекты учетных записей, которые были введены другим координаторам и презентаторам из AppCoordinator, теперь устарели. Единственный актуальный объект - это объект учетной записи EditProfilePresenter и объект, который хранится в AppCoordinator из-за прослушивателя.

Как я могу либо распространить обновленную информацию на все объекты с помощью объекта учетной записи, либо, как мне настроить мои объекты так, чтобы они имели единый источник истины для объекта учетной записи пользователя? В идеале я хочу, чтобы внедренная учетная запись ссылалась на оригинал, хранящийся в AppCoordinator, а не на его копию или клон.

Я учел:

  1. Настройте три индивидуальные слушатели для трех основных частей приложения. Каждый слушатель будет управлять этим фрагментом объекта учетной записи приложения. Однако это приводит к 3 операциям чтения для одного обновления, что совсем не способствует масштабируемости.

  2. Сохраните объект учетной записи в FirestoreRepositoryService. Большинство классов, требующих этой службы, используют объект учетной записи, и его экземпляр создается в координаторе приложения, поэтому он должен использоваться всеми классами. Однако это звучит плохо, поскольку это нарушает ответственность службы, просто выполняя запросы, связанные с Firestore.

  3. Используйте синглтон для управления состоянием учетной записи, которое совместно используется и используется всеми классами, которые в нем нуждаются. Это означает, что объект учетной записи всегда обновляется в одном месте с помощью одного слушателя. Однако это резко снизит тестируемость и удобочитаемость из-за того, что объект учетной записи больше не будет внедряться, а также создаст тесную связь во всем приложении с одним объектом, состояние которого используется повсюду.

  4. Обратите внимание на реструктуризацию объекта учетной записи, при которой информация разбивается на определенные c разделы объекта учетной записи. В идеале одна вкладка должна иметь дело только с назначенным ей фрагментом информации, а не со всем объектом учетной записи. Скорее, у него будет подколлекция внутри объекта учетной записи. Однако общая информация, которая обычно используется повсюду, например имя, будет храниться либо в объекте учетной записи, либо в подгруппе, что приведет к еще большему количеству операций чтения, необходимых для сохранения данных, и к некоторым другим недостаткам.

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

I я действительно не уверен, что делать, любая помощь о том, как с этим справиться, будет оценена!

1 Ответ

0 голосов
/ 27 мая 2020

Я не смог найти ответ на этот вопрос, поэтому вместо этого я решил go использовать Singleton-маршрут, и все работает отлично.

Моя основная проблема с использованием singleton была связана с тестируемостью кода Однако я обошел это, предоставив метод расширения в синглтоне, который позволил мне настроить фиктивные данные для пользовательского объекта в моих модульных тестах. кода, но мне достаточно метко названной функции, указывающей на то, что она предназначена для создания фиктивных данных. что является огромным плюсом.

Вот код. Если бы кто-нибудь придумал решение для обновления данных внедренного объекта, это было бы здорово.

class CurrentUserService: CurrentUserServiceProtocol {

// Vars so I can override them in unit tests
var firebaseCrashlytics: FirebaseCrashlyticsServiceProtocol
var firestoreRepository: FirestoreRepositoryProtocol

private (set) var userListener: ListenerRegistration?
private (set) var user: FIRAccount?

static let shared = CurrentUserService()

init() {
    self.firebaseCrashlytics = FirebaseCrashlyticsService()
    self.firestoreRepository = FirestoreRepository()
}

func setUser(user: FIRAccount, ref: CollecitonRef) throws {
    self.user = user
    setupAccountListener(docId: user.id, ref)
}

func setupAccountListener(docId: String, _ ref: CollectionReference) {

    userListener?.remove()

// Setup listener however you need to do it in your code
    userListener = self.firestoreRepository.readById(
        docId: docId,
        from: ref,
        returning: FIRAccount.self,
        withSnapshotListener: true) { (result, account) in

            switch result.succeeded {
            case false:
// Log error
            case true:
                if let account = account {
                    self.user = account
                } else {
                    // Log error
                }
            }
    }
}

func updateUser(_ account: FIRAccount) {
    self.user = account
}

func logoutCurrentUser() {
    self.user = nil

    userListener?.remove()
    self.userListener = nil
}


}

extension CurrentUserService {
    func setMockUserData(_ account: FIRAccount) {
        self.user = account
    }
}
...