Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- //
- // Created by Sergey Kovalenko on 9/8/16.
- // Copyright (c) 2016 Techery. All rights reserved.
- //
- #import "SCSmartCardSettingsViewModel.h"
- #import "SCSwitcherRowController.h"
- #import "SCFirmwareUpdateRowController.h"
- #import "SCAlertCommandRowController.h"
- #import "SCSettingsDataSource.h"
- #import "SCApplicationDomainProtocol.h"
- #import "SCSmartCardsStore.h"
- #import "SCTimeIntervalSetting.h"
- #import "SCSmartCardUserSetting.h"
- #import "SCOperationEvent.h"
- #import "SCSettingsSectionInfo.h"
- #import "SCSmartCardCharacteristicsServiceProtocol.h"
- #import "SCSmartCardSecurityServiceProtocol.h"
- #import "SCSmartCardConnectionServiceProtocol.h"
- #import "SCSmartCardUserServiceProtocol.h"
- #import "SCFirmwareServiceProtocol.h"
- #import "SCViewModelAlertFactory.h"
- #import <Core/DTSignal.h>
- #import <SmartCardSDKIsolation/SmartCardSDKIsolation.h>
- #import "RACSignal+SCOperationEvent.h"
- #import "SCWalletSettingsAnalyticsHelper.h"
- NSErrorDomain const SCSettingsViewModelErrorDomain = @"SCSettingsViewModelErrorDomain";
- @interface SCSmartCardSettingsViewModel ()
- @property (nonatomic, strong) id<SCSmartCardCharacteristicsServiceProtocol> ioc_smartcardCharacteristicsService;
- @property (nonatomic, strong) id<SCSmartCardSecurityServiceProtocol> ioc_smartCardSecurityService;
- @property (nonatomic, strong) id<SCSmartCardConnectionServiceProtocol> ioc_smartCardConnectionService;
- @property (nonatomic, strong) id<SCFirmwareServiceProtocol> ioc_firmwareService;
- @property (nonatomic, strong) id<SCSmartCardUserServiceProtocol> ioc_smartCardUserService;
- @property (nonatomic, strong) id<SCWalletSettingsAnalyticsHelperProtocol> ioc_analyticsHelper;
- @property (nonatomic, copy, readwrite) NSString *firmwareVersionTitle;
- @property (nonatomic, copy, readwrite) NSString *batteryLevelTitle;
- @property (nonatomic, strong) SCSmartCardUserSetting *settings;
- @property (nonatomic, strong) RACSubject *operationEventsSubject;
- @property (nonatomic, strong) RACSubject *alertsSubject;
- @property (nonatomic, assign) BOOL smartCardConnected;
- @property (nonatomic, strong) SCSettingsRouter *router;
- @property (nonatomic, strong, readwrite) SCSettingsViewModel *settingsViewModel;
- @end
- @implementation SCSmartCardSettingsViewModel
- - (instancetype)initWithRouter:(SCSettingsRouter *)router {
- DTParameterAssertNotNil(router);
- self = [super init];
- if (self) {
- self.router = router;
- self.settingsViewModel = [[SCSettingsViewModel alloc] init];
- self.settingsViewModel.title = DTBundleLocalizedString(@"settings.list.title");
- self.operationEventsSubject = [RACReplaySubject replaySubjectWithCapacity:1];
- self.alertsSubject = [RACReplaySubject replaySubjectWithCapacity:1];
- [self synchronizeSettings];
- [self setupSettingsDataSource];
- RAC(self, smartCardConnected) = [[self.ioc_smartCardConnectionService smartCardIsConnectedState] deliverOnMainThread];
- @weakify(self);
- RAC(self, firmwareVersionTitle) = [[[self.ioc_firmwareService currentFirmwareVersion] deliverOnMainThread] map:^id(SCFirmwareVersionsBundle *version) {
- @strongify(self);
- return [NSString stringWithFormat:@"%@ %@", DTBundleLocalizedString(@"settings.card_profile.fimware_version"), version.appNordicVersion];
- }];
- RAC(self, batteryLevelTitle) = [[[self.ioc_smartcardCharacteristicsService batteryLevelStatus] deliverOnMainThread] map:^id(NSNumber *value) {
- @strongify(self);
- CGFloat percentage = value.doubleValue * 100;
- return [NSString stringWithFormat:@"%@ %.0f\%%",DTBundleLocalizedString(@"settings.card_profile.battery_level"),percentage];
- }];
- }
- return self;
- }
- - (void)synchronizeSettings {
- @weakify(self);
- RACSignal *settingsSignal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
- @strongify(self);
- NSMutableArray *settingsSignals = [NSMutableArray array];
- [settingsSignals addObject:[self.ioc_smartcardCharacteristicsService lockDeviceStatus]];
- [settingsSignals addObject:[self.ioc_smartcardCharacteristicsService resetDefaultCardPeriod]];
- [settingsSignals addObject:[self.ioc_smartcardCharacteristicsService clearSmartCardPeriod]];
- [settingsSignals addObject:[self.ioc_smartcardCharacteristicsService stealthModeStatusSubscribe:YES]];
- return [[[RACSignal combineLatest:settingsSignals] map:^id(RACTuple *settingsTuple) {
- RACTupleUnpack(NSNumber *locked, NSNumber *resetDefaultCardInterval, NSNumber *resetClearSmartCardPeriod, NSNumber *stealthModeEnabled) = settingsTuple;
- SCSmartCardUserSetting *settings = [[SCSmartCardUserSetting alloc] init];
- settings.stealthModeEnabled = stealthModeEnabled.boolValue;
- settings.locked = locked.boolValue;
- settings.disableDefaultPaymentInterval = [SCTimeIntervalSetting settingWithTimeInterval:resetDefaultCardInterval.doubleValue];
- settings.clearPaymentCardsInterval = [SCTimeIntervalSetting settingWithTimeInterval:resetClearSmartCardPeriod.doubleValue];
- return settings;
- }] subscribe:subscriber];
- }];
- RACSignal *settingsWhileConnectedSignal = [[[[[self.ioc_smartCardConnectionService smartCardIsConnectedState] filter:^BOOL(NSNumber *status) {
- return status.boolValue;
- }] deliverOnMainThread] flattenMap:^RACStream *(id value) {
- @strongify(self);
- [self.operationEventsSubject sendNext:[SCOperationEvent eventWithLoadingMessage:nil]];
- return [[[settingsSignal deliverOnMainThread] doNext:^(id x) {
- @strongify(self);
- [self.operationEventsSubject sendNext:[SCOperationEvent eventWithCompleteMessage:@""]];
- }] doError:^(NSError *error) {
- @strongify(self);
- [self.operationEventsSubject sendNext:[SCOperationEvent eventWithError:error]];
- }];
- }] takeUntil:self.rac_willDeallocSignal];
- RAC(self, settings) = [[settingsWhileConnectedSignal catchTo:[RACSignal empty]] deliverOnMainThread];
- }
- - (void)setupSettingsDataSource {
- [self createSmartCardProfileSection];
- [self createGeneralSection];
- [self createSecuritySection];
- [self createFirmwareUpdateSection];
- [self createSetupSection];
- [self createRestartDeviceSection];
- }
- - (void)createSmartCardProfileSection {
- SCCommandRowController *smartCardProfileRowController = [self smartCardProfileRowController];
- SCSettingsSectionInfo *section = [[SCSettingsSectionInfo alloc] initWithRowControllers:@[smartCardProfileRowController]];
- section.heightForHeader = 0.0f;
- [self.settingsViewModel.dataSource addSection:section];
- }
- - (void)createGeneralSection {
- NSArray *settings = @[
- [self about],
- [self offlineMode],
- [self locateSmartcard]
- ];
- SCSettingsSectionInfo *section = [[SCSettingsSectionInfo alloc] initWithRowControllers:settings];
- section.headerTitle = DTBundleLocalizedString(@"settings.general");
- section.heightForHeader = 38.0f;
- section.headerTitleFontSize = 13.0;
- [self.settingsViewModel.dataSource addSection:section];
- }
- - (void)createSecuritySection {
- NSMutableArray *settings = [NSMutableArray array];
- [settings addObject:[self lock]];
- [settings addObject:[self disableDefaultPayment]];
- [settings addObject:[self resetPin]];
- [settings addObject:[self autoClearPaymentInfo]];
- [settings addObject:[self stealthMode]];
- if(settings.count > 0) {
- SCSettingsSectionInfo *section = [[SCSettingsSectionInfo alloc] initWithRowControllers:settings];
- section.headerTitle = DTBundleLocalizedString(@"settings.section.security");
- section.heightForHeader = 38.0f;
- section.headerTitleFontSize = 13.0;
- [self.settingsViewModel.dataSource addSection:section];
- }
- }
- - (void)createFirmwareUpdateSection {
- SCFirmwareUpdateRowController *firmwareUpdateSetting = [[SCFirmwareUpdateRowController alloc] initWithRouter:self.router.firmwareUpdateRouter];
- firmwareUpdateSetting.subtitleText = DTBundleLocalizedString(@"settings.firmware_update.detail_text");
- SCSettingsSectionInfo *section = [[SCSettingsSectionInfo alloc] initWithRowControllers:@[firmwareUpdateSetting]];
- section.headerTitle = DTBundleLocalizedString(@"settings.section.update");
- section.heightForHeader = 38.0f;
- section.headerTitleFontSize = 13.0;
- [self.settingsViewModel.dataSource addSection:section];
- }
- - (void)createSetupSection {
- NSMutableArray *settings = [NSMutableArray array];
- [settings addObject:[self setupNewSmartCard]];
- [settings addObject:[self factoryReset]];
- if(settings.count > 0) {
- SCSettingsSectionInfo *section = [[SCSettingsSectionInfo alloc] initWithRowControllers:settings];
- section.headerTitle = DTBundleLocalizedString(@"settings.section.setup");
- section.heightForHeader = 38.0f;
- section.headerTitleFontSize = 13.0;
- [self.settingsViewModel.dataSource addSection:section];
- }
- }
- #pragma mark - Row controllers - SmartCard profile
- - (SCCommandRowController *)smartCardProfileRowController {
- @weakify(self);
- SCCommandRowController *smartCardProfileRowController = [[SCCommandRowController alloc] init];
- smartCardProfileRowController.title = DTBundleLocalizedString(@"settings.smartcard_profile");
- smartCardProfileRowController.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
- smartCardProfileRowController.executeCommandOnSelection = YES;
- smartCardProfileRowController.command = [DTCommand withSignalBlock:^RACSignal *(id input) {
- @strongify(self);
- return [self showConnectionLostAlertOrExecuteBlockSignal:^{
- @strongify(self);
- [self.router showSmartCardProfile];
- }];
- }];
- return smartCardProfileRowController;
- }
- #pragma mark - Row controllers - Performance
- - (void)createRestartDeviceSection {
- SCSettingsSectionInfo *section = [[SCSettingsSectionInfo alloc] initWithRowControllers:@[[self restartDevice]]];
- [self.settingsViewModel.dataSource addSection:section];
- }
- #pragma mark - Row controllers - General
- - (SCCommandRowController *)about {
- SCCommandRowController *aboutSetting = [[SCCommandRowController alloc] initWithStyle:UITableViewCellStyleDefault];
- aboutSetting.title = DTBundleLocalizedString(@"settings.about");
- aboutSetting.executeCommandOnSelection = YES;
- aboutSetting.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
- @weakify(self);
- aboutSetting.command = [DTCommand withBlock:^(id input) {
- @strongify(self);
- [self.router showAboutScreen];
- }];
- return aboutSetting;
- }
- - (SCCommandRowController *)offlineMode {
- SCCommandRowController *offlineModeSetting = [[SCCommandRowController alloc] initWithStyle:UITableViewCellStyleDefault];
- offlineModeSetting.title = DTBundleLocalizedString(@"offline_mode.section.title");
- offlineModeSetting.subtitleText = DTBundleLocalizedString(@"offline_mode.section.subtitle");
- offlineModeSetting.executeCommandOnSelection = YES;
- offlineModeSetting.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
- @weakify(self);
- offlineModeSetting.command = [DTCommand withBlock:^(id input) {
- @strongify(self);
- [self.router showOfflineModeScreen];
- }];
- return offlineModeSetting;
- }
- - (SCCommandRowController *)locateSmartcard {
- SCCommandRowController *locateSmartcardSetting = [[SCCommandRowController alloc] init];
- locateSmartcardSetting.title = DTBundleLocalizedString(@"settings.locate_smartcard");
- locateSmartcardSetting.subtitleText = DTBundleLocalizedString(@"settings.locate_smartcard.detail_text");
- locateSmartcardSetting.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
- @weakify(self);
- locateSmartcardSetting.command = [DTCommand withBlock:^(id input) {
- @strongify(self);
- [self.router showLocateSmartCard];
- }];
- locateSmartcardSetting.executeCommandOnSelection = YES;
- return locateSmartcardSetting;
- }
- #pragma mark - Row controllers - Security
- - (SCCommandRowController *)lock {
- SCSwitcherRowController *lockSetting = [[SCSwitcherRowController alloc] init];
- lockSetting.title = DTBundleLocalizedString(@"settings.lock.card");
- lockSetting.subtitleText = DTBundleLocalizedString(@"settings.lock.card.detailText");
- RAC(lockSetting, switcher.on, @NO) = RACObserve(self, settings.locked);
- @weakify(self);
- lockSetting.command = [DTCommand withSignalBlock:^RACSignal *(NSNumber *locked) {
- @strongify(self);
- DTParameterAssertClass(locked, NSNumber);
- [self.ioc_analyticsHelper trackWalletSettingsLockStatusChanging:locked.boolValue];
- if (locked.boolValue) {
- return [self.ioc_smartCardSecurityService lockDevice];
- } else {
- return [self.ioc_smartCardSecurityService unlockDevice];
- }
- }];
- [[lockSetting.command.errors map:^id(NSError *error) {
- return [SCOperationEvent eventWithError:error];
- }] subscribe:self.operationEventsSubject];
- return lockSetting;
- }
- - (SCCommandRowController *)disableDefaultPayment {
- SCCommandRowController *disableDefaultPaymentSetting = [[SCCommandRowController alloc] initWithStyle:UITableViewCellStyleValue1];
- disableDefaultPaymentSetting.title = DTBundleLocalizedString(@"settings.disable.default.card.title");
- disableDefaultPaymentSetting.executeCommandOnSelection = YES;
- disableDefaultPaymentSetting.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
- RAC(disableDefaultPaymentSetting, detailText) = RACObserve(self, settings.disableDefaultPaymentInterval.intervalDescription);
- @weakify(self);
- disableDefaultPaymentSetting.command = [DTCommand withBlock:^(id input) {
- @strongify(self);
- [self.router showResetDefaultCardSettingsWithSettings:self.settings];
- }];
- return disableDefaultPaymentSetting;
- }
- - (SCCommandRowController *)resetPin {
- SCCommandRowController *resetPinSetting = [[SCCommandRowController alloc] init];
- resetPinSetting.title = DTBundleLocalizedString(@"settings.reset.pin");
- resetPinSetting.executeCommandOnSelection = YES;
- resetPinSetting.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
- @weakify(self);
- resetPinSetting.command = [DTCommand withSignalBlock:^RACSignal *(id input) {
- @strongify(self);
- return [self showConnectionLostAlertOrExecuteBlockSignal:^{
- @strongify(self);
- SCSmartCard *smartCard = [self.ioc_smartCardConnectionService smartCard];
- [self.router showSetTouchPin:smartCard];
- }];
- }];
- return resetPinSetting;
- }
- - (SCCommandRowController *)autoClearPaymentInfo {
- SCCommandRowController *clearPaymentInfoSetting = [[SCCommandRowController alloc] initWithStyle:UITableViewCellStyleValue1];
- clearPaymentInfoSetting.title = DTBundleLocalizedString(@"settings.clear.smart.card.title");
- clearPaymentInfoSetting.subtitleText = DTBundleLocalizedString(@"settings.clear.smartcard.detailText");
- clearPaymentInfoSetting.executeCommandOnSelection = YES;
- clearPaymentInfoSetting.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
- RAC(clearPaymentInfoSetting, detailText) = RACObserve(self, settings.clearPaymentCardsInterval.intervalDescription);
- @weakify(self);
- clearPaymentInfoSetting.command = [DTCommand withBlock:^(id input) {
- @strongify(self);
- [self.router showClearCardSettingsWithSettings:self.settings];
- }];
- return clearPaymentInfoSetting;
- }
- - (SCCommandRowController *)stealthMode {
- @weakify(self);
- SCSwitcherRowController *stealthModeSetting = [[SCSwitcherRowController alloc] init];
- stealthModeSetting.title = DTBundleLocalizedString(@"settings.stealth.mode");
- stealthModeSetting.subtitleText = DTBundleLocalizedString(@"settings.slealth.mode.detailText");
- stealthModeSetting.switcher.enabled = YES;
- RAC(stealthModeSetting, switcher.on, @NO) = RACObserve(self, settings.stealthModeEnabled);
- stealthModeSetting.command = [DTCommand withSignalBlock:^RACSignal *(NSNumber *input) {
- @strongify(self);
- DTParameterAssertClass(input, NSNumber);
- return [[self.ioc_smartcardCharacteristicsService setStealthModeEnabled:input.boolValue] doNext:^(id x) {
- @strongify(self);
- [self.ioc_analyticsHelper trackWalletSettingsPrivacyChanging:input.boolValue];
- self.settings.stealthModeEnabled = input.boolValue;
- }];
- }];
- [[stealthModeSetting.command.errors map:^id(NSError *error) {
- return [SCOperationEvent eventWithError:error];
- }] subscribe:self.operationEventsSubject];
- return stealthModeSetting;
- }
- - (NSError *)unableToUnlockError {
- return [NSError errorWithDomain:SCSettingsViewModelErrorDomain
- code:SCRootSettingsErrorUnlockDenied
- userInfo:@{NSLocalizedDescriptionKey: DTBundleLocalizedString(@"settings.list.unlock_smart_card_denied")}];
- }
- - (NSError *)timeoutError {
- return [NSError errorWithDomain:SCSettingsViewModelErrorDomain
- code:SCRootSettingsErrorTimeout
- userInfo:@{NSLocalizedDescriptionKey: DTBundleLocalizedString(@"connection.smartcard.error")}];
- }
- - (NSError *)smartCardDisconnectedError {
- return [NSError errorWithDomain:SCSettingsViewModelErrorDomain
- code:SCRootSettingsSmartCardDisconnected
- userInfo:@{NSLocalizedDescriptionKey: DTBundleLocalizedString(@"connection.describing.disconnected")}];
- }
- #pragma mark - Row controllers - Setup
- - (SCCommandRowController *)factoryReset {
- SCCommandRowController *factoryResetSetting = [[SCCommandRowController alloc] initWithStyle:UITableViewCellStyleDefault];
- factoryResetSetting.title = DTBundleLocalizedString(@"settings.factory_reset.title");
- factoryResetSetting.executeCommandOnSelection = YES;
- factoryResetSetting.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
- factoryResetSetting.subtitleText = DTBundleLocalizedString(@"settings.factory_reset.detail_title");
- @weakify(self);
- factoryResetSetting.command = [DTCommand withSignalBlock:^RACSignal *(id input) {
- @strongify(self);
- return [self showConnectionLostAlertOrExecuteBlockSignal:^{
- @strongify(self);
- [self sendConfirmResetSettingSmartCard];
- }];
- }];
- return factoryResetSetting;
- }
- - (SCCommandRowController *)setupNewSmartCard {
- SCCommandRowController *setupNewSmartCardSetting = [[SCCommandRowController alloc] initWithStyle:UITableViewCellStyleDefault];
- setupNewSmartCardSetting.title = DTBundleLocalizedString(@"settings.factory_reset.setup_new_smart_card");
- setupNewSmartCardSetting.executeCommandOnSelection = YES;
- setupNewSmartCardSetting.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
- @weakify(self);
- setupNewSmartCardSetting.command = [DTCommand withBlock:^(id x) {
- @strongify(self);
- [self.router showSetupNewSmartCardWithConnectedState:self.smartCardConnected];
- }];
- return setupNewSmartCardSetting;
- }
- #pragma mark - Row controllers - Restart Device
- - (SCCommandRowController *)restartDevice {
- SCCommandRowController *restartDeviceSetting = [[SCCommandRowController alloc] initWithStyle:UITableViewCellStyleDefault];
- restartDeviceSetting.title = DTBundleLocalizedString(@"settings.reset_device.title");
- restartDeviceSetting.executeCommandOnSelection = YES;
- @weakify(self);
- restartDeviceSetting.command = [DTCommand withSignalBlock:^RACSignal *(id input) {
- @strongify(self);
- return [self showConnectionLostAlertOrExecuteBlockSignal:^{
- @strongify(self);
- [self sendConfirmRestartingSmartCard];
- }];
- }];
- return restartDeviceSetting;
- }
- - (void)restartSmartCard {
- [[[self.ioc_smartCardSecurityService restartDevice] sc_operationEventSignal] subscribe:self.operationEventsSubject];
- }
- - (SCAlertCommandRowController *)unpairSmartCard {
- SCAlertCommandRowController *restartSmartCard = [[SCAlertCommandRowController alloc] init];
- restartSmartCard.cellTitleColor = [UIColor redColor];
- restartSmartCard.title = DTBundleLocalizedString(@"settings.unpair_smartcard");
- return restartSmartCard;
- }
- - (RACSignal *)smartCardConnectedConditionSignal {
- return [[self.ioc_smartCardConnectionService smartCardIsConnectedState] take:1];
- }
- - (RACSignal *)showConnectionLostAlertOrExecuteBlockSignal:(void (^)())block {
- return [RACSignal if:[self smartCardConnectedConditionSignal] then:[DTSignal createSignalWithBlock:block] else:[self sendConnectionLostAlertSignal]];
- }
- #pragma mark - Alerts
- - (RACSignal *)sendConnectionLostAlertSignal {
- @weakify(self);
- return [DTSignal createSignalWithBlock:^{
- @strongify(self);
- [self sendConnectionLostAlert];
- }];
- }
- - (void)sendConnectionLostAlert {
- [self.alertsSubject sendNext:[SCViewModelAlertFactory noSmartCardConnectionAlert]];
- }
- - (void)sendConfirmRestartingSmartCard {
- SCViewModelAlert *alert = [[SCViewModelAlert alloc] initWithTitle:DTBundleLocalizedString(@"settings.reset_device.title") message:DTBundleLocalizedString(@"settings.reset_device.alert.message")];
- alert.style = SCViewModelAlertStyleActionSheet;
- @weakify(self);
- [alert addAction:[SCViewModelAlertAction okActionWithCallback:^{
- @strongify(self);
- [self.ioc_analyticsHelper trackWalletSettingsRestartSmartcard];
- [self restartSmartCard];
- }]];
- SCViewModelAlertAction *cancelAction = [SCViewModelAlertAction cancelActionWithCallback:nil];
- cancelAction.style = SCViewModelAlertActionStyleCancel;
- [alert addAction:cancelAction];
- [self.alertsSubject sendNext:alert];
- }
- - (void)sendConfirmResetSettingSmartCard {
- SCViewModelAlert *alert = [[SCViewModelAlert alloc] initWithTitle:nil message:DTBundleLocalizedString(@"settings.reset_smartcard.alert.message")];
- [alert addAction:[SCViewModelAlertAction cancelActionWithCallback:nil]];
- @weakify(self);
- [alert addAction:[SCViewModelAlertAction continueActionWithCallback:^{
- @strongify(self);
- [self.router showFactoryResetScreen];
- }]];
- [self.alertsSubject sendNext:alert];
- }
- #pragma mark - SCOperationEventProducer
- - (RACSignal *)operationEventsSignal {
- return [self.operationEventsSubject map:^SCOperationEvent *(SCOperationEvent *operationEvent) {
- if ([operationEvent.error.domain isEqualToString:SCSmartCardFrameworkErrorDomain] &&
- operationEvent.error.code == SCSmartCardFrameworkErrorNoActiveConnection) {
- return [SCOperationEvent eventWithCompleteMessage:nil];
- }
- return operationEvent;
- }];
- }
- #pragma mark - SCViewModelAlertProducer
- - (RACSignal *)viewModelAlertsStream {
- return [self.alertsSubject deliverOnMainThread];
- }
- #pragma mark - SCAnalyticsTrackingProtocol
- - (void)trackScreenOpened {
- [self.ioc_analyticsHelper trackWalletSettingsScreen];
- }
- @end
Add Comment
Please, Sign In to add comment