ViewController.m 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  1. // Copyright 2019 Google
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #import "ViewController.h"
  15. #import <FirebaseAnalytics/FirebaseAnalytics.h>
  16. #import <FirebaseCore/FIROptions.h>
  17. #import <FirebaseCore/FirebaseCore.h>
  18. #import <FirebaseInstallations/FirebaseInstallations.h>
  19. #import <FirebaseRemoteConfig/FIRRemoteConfig_Private.h>
  20. #import <FirebaseRemoteConfig/FirebaseRemoteConfig.h>
  21. #import <FirebaseRemoteConfig/RCNConfigSettings.h>
  22. #import "FRCLog.h"
  23. static NSString *const FIRPerfNamespace = @"fireperf";
  24. static NSString *const FIRDefaultFIRAppName = @"__FIRAPP_DEFAULT";
  25. static NSString *const FIRSecondFIRAppName = @"secondFIRApp";
  26. @interface ViewController ()
  27. @property(nonatomic, strong) IBOutlet UIButton *fetchButton;
  28. @property(nonatomic, strong) IBOutlet UIButton *applyButton;
  29. @property(nonatomic, strong) IBOutlet UIButton *refreshButton;
  30. @property(nonatomic, strong) IBOutlet UIButton *clearLogsButton;
  31. @property(nonatomic, strong) IBOutlet UITextView *mainTextView;
  32. /// Key of custom variable to be added by user.
  33. @property(nonatomic, weak) IBOutlet UITextField *keyLabel;
  34. /// Value of custom variable to be added by user.
  35. @property(nonatomic, weak) IBOutlet UITextField *valueLabel;
  36. /// Expiration in seconds to be set by user.
  37. @property(nonatomic, weak) IBOutlet UITextField *expirationLabel;
  38. /// Config Defaults.
  39. @property(nonatomic, strong) NSMutableDictionary *configDefaults;
  40. /// developer mode switch.
  41. @property(strong, nonatomic) IBOutlet UISwitch *developerModeEnabled;
  42. /// Current selected namespace.
  43. @property(nonatomic, copy) NSString *currentNamespace;
  44. /// Curret selected FIRApp instance name.
  45. @property(nonatomic, copy) NSString *FIRAppName;
  46. /// Selected namespace picker control view.
  47. @property(nonatomic, strong) IBOutlet UIPickerView *namespacePicker;
  48. /// Selected app picker control view.
  49. @property(nonatomic, strong) IBOutlet UIPickerView *appPicker;
  50. /// Array of prepopulated namespaces supported by this app.
  51. @property(nonatomic, strong) NSArray<NSString *> *namespacePickerData;
  52. /// Array of prepopulated FIRApp names supported by this app.
  53. @property(nonatomic, strong) NSArray<NSString *> *appPickerData;
  54. /// Array of Remote Config instances.
  55. @property(nonatomic, strong) NSMutableDictionary *RCInstances;
  56. @end
  57. @implementation ViewController
  58. - (void)viewDidLoad {
  59. [super viewDidLoad];
  60. [[FRCLog sharedInstance] setLogView:self.mainTextView];
  61. [[FRCLog sharedInstance] logToConsole:@"Viewcontroller loaded"];
  62. [[FRCLog sharedInstance] logToConsole:[[NSBundle mainBundle] bundleIdentifier]];
  63. // Setup UI
  64. self.expirationLabel.text = [NSString stringWithFormat:@"0"];
  65. self.configDefaults = [[NSMutableDictionary alloc] init];
  66. self.keyLabel.delegate = self;
  67. self.valueLabel.delegate = self;
  68. self.expirationLabel.delegate = self;
  69. self.mainTextView.editable = NO;
  70. // TODO(mandard): Add support for deleting and adding namespaces in the app.
  71. self.namespacePickerData =
  72. [[NSArray alloc] initWithObjects:FIRNamespaceGoogleMobilePlatform, FIRPerfNamespace, nil];
  73. self.appPickerData =
  74. [[NSArray alloc] initWithObjects:FIRDefaultFIRAppName, FIRSecondFIRAppName, nil];
  75. self.RCInstances = [[NSMutableDictionary alloc] init];
  76. for (NSString *namespaceString in self.namespacePickerData) {
  77. for (NSString *appString in self.appPickerData) {
  78. // Check for the default instance.
  79. if (!self.RCInstances[namespaceString]) {
  80. self.RCInstances[namespaceString] = [[NSMutableDictionary alloc] init];
  81. }
  82. if ([namespaceString isEqualToString:FIRNamespaceGoogleMobilePlatform] &&
  83. [appString isEqualToString:FIRDefaultFIRAppName]) {
  84. self.RCInstances[namespaceString][appString] = [FIRRemoteConfig remoteConfig];
  85. } else {
  86. FIRApp *firebaseApp = ([appString isEqualToString:FIRDefaultFIRAppName])
  87. ? [FIRApp defaultApp]
  88. : [FIRApp appNamed:appString];
  89. self.RCInstances[namespaceString][appString] =
  90. [FIRRemoteConfig remoteConfigWithFIRNamespace:namespaceString app:firebaseApp];
  91. }
  92. FIRRemoteConfigSettings *settings = [[FIRRemoteConfigSettings alloc] init];
  93. settings.fetchTimeout = 300;
  94. settings.minimumFetchInterval = 0;
  95. ((FIRRemoteConfig *)(self.RCInstances[namespaceString][appString])).configSettings = settings;
  96. }
  97. }
  98. [[FRCLog sharedInstance] logToConsole:@"RC instances inited"];
  99. self.namespacePicker.dataSource = self;
  100. self.namespacePicker.delegate = self;
  101. self.appPicker.dataSource = self;
  102. self.appPicker.delegate = self;
  103. [self.developerModeEnabled setOn:true animated:false];
  104. }
  105. - (IBAction)fetchButtonPressed:(id)sender {
  106. [[FRCLog sharedInstance] logToConsole:@"Fetch button pressed"];
  107. // fetchConfig api callback, this is triggered when client receives response from server
  108. ViewController *__weak weakSelf = self;
  109. FIRRemoteConfigFetchCompletion completion = ^(FIRRemoteConfigFetchStatus status, NSError *error) {
  110. ViewController *strongSelf = weakSelf;
  111. if (!strongSelf) {
  112. return;
  113. }
  114. [[FRCLog sharedInstance]
  115. logToConsole:[NSString stringWithFormat:@"Fetch completed. Status=%@",
  116. [strongSelf statusString:status]]];
  117. if (error) {
  118. [[FRCLog sharedInstance] logToConsole:[NSString stringWithFormat:@"Fetch Error=%@", error]];
  119. }
  120. NSMutableString *output = [NSMutableString
  121. stringWithFormat:@"Fetch status : %@.\n\n", [strongSelf statusString:status]];
  122. if (error) {
  123. [output appendFormat:@"%@\n", error];
  124. }
  125. if (status == FIRRemoteConfigFetchStatusFailure) {
  126. [output appendString:[NSString stringWithFormat:@"Fetch Error :%@.\n",
  127. [strongSelf errorString:error.code]]];
  128. if (error.code == FIRRemoteConfigErrorThrottled) {
  129. NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
  130. NSNumber *throttledTime =
  131. (NSNumber *)error.userInfo[FIRRemoteConfigThrottledEndTimeInSecondsKey];
  132. NSDate *date = [NSDate dateWithTimeIntervalSince1970:[throttledTime doubleValue]];
  133. [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
  134. NSString *timeString = [dateFormatter stringFromDate:date];
  135. [output appendString:[NSString stringWithFormat:@"Throttled end at: %@ \n", timeString]];
  136. }
  137. }
  138. [[FRCLog sharedInstance] logToConsole:output];
  139. };
  140. // fetchConfig api call
  141. [[FRCLog sharedInstance] logToConsole:@"Calling fetchWithExpirationDuration.."];
  142. [self.RCInstances[self.currentNamespace][self.FIRAppName]
  143. fetchWithExpirationDuration:self.expirationLabel.text.integerValue
  144. completionHandler:completion];
  145. }
  146. - (IBAction)fetchAndActivateButtonPressed:(id)sender {
  147. // fetchConfig api callback, this is triggered when client receives response from server
  148. ViewController *__weak weakSelf = self;
  149. FIRRemoteConfigFetchAndActivateCompletion fetchAndActivateCompletion = ^(
  150. FIRRemoteConfigFetchAndActivateStatus status, NSError *error) {
  151. ViewController *strongSelf = weakSelf;
  152. if (!strongSelf) {
  153. return;
  154. }
  155. NSMutableString *output = [@"Fetch and activate status :" mutableCopy];
  156. if (status == FIRRemoteConfigFetchAndActivateStatusSuccessFetchedFromRemote) {
  157. [output appendString:@"Success from remote fetch."];
  158. } else if (status == FIRRemoteConfigFetchAndActivateStatusSuccessUsingPreFetchedData) {
  159. [output appendString:@"Success using pre-fetched data."];
  160. } else if (status == FIRRemoteConfigFetchAndActivateStatusError) {
  161. [output
  162. appendString:[NSString stringWithFormat:@"Failure: %@", [error localizedDescription]]];
  163. }
  164. if (error) {
  165. [output appendFormat:@"%@\n", error];
  166. }
  167. if (status == FIRRemoteConfigFetchAndActivateStatusError) {
  168. [output appendString:[NSString stringWithFormat:@"Fetch And Activate Error :%@.\n",
  169. [strongSelf errorString:error.code]]];
  170. if (error.code == FIRRemoteConfigErrorThrottled) {
  171. [output appendString:[NSString stringWithFormat:@"Throttled.\n"]];
  172. }
  173. }
  174. // activate status
  175. [[FRCLog sharedInstance] logToConsole:output];
  176. if (status == FIRRemoteConfigFetchAndActivateStatusSuccessFetchedFromRemote ||
  177. status == FIRRemoteConfigFetchAndActivateStatusSuccessUsingPreFetchedData) {
  178. [strongSelf printResult:[[NSMutableString alloc] init]];
  179. }
  180. };
  181. // fetchConfig api call
  182. [self.RCInstances[self.currentNamespace][self.FIRAppName]
  183. fetchAndActivateWithCompletionHandler:fetchAndActivateCompletion];
  184. }
  185. - (IBAction)activateButtonPressed:(id)sender {
  186. [self apply];
  187. }
  188. - (IBAction)refreshButtonPressed:(id)sender {
  189. NSMutableString *output = [[NSMutableString alloc] init];
  190. [self printResult:output];
  191. }
  192. - (IBAction)setDefaultFromPlistButtonPressed:(id)sender {
  193. [self.RCInstances[self.currentNamespace][self.FIRAppName]
  194. setDefaultsFromPlistFileName:@"Defaults"];
  195. [self printDefaultConfigs];
  196. }
  197. - (IBAction)setDefaultButtonPressed:(id)sender {
  198. if (self.configDefaults.count) {
  199. [self.RCInstances[self.currentNamespace][self.FIRAppName] setDefaults:self.configDefaults];
  200. [self.configDefaults removeAllObjects];
  201. [self printDefaultConfigs];
  202. } else {
  203. [[FRCLog sharedInstance] logToConsole:@"Nothing to set for defaults."];
  204. }
  205. }
  206. - (IBAction)onClearLogsButtonPressed:(id)sender {
  207. self.mainTextView.text = @"";
  208. }
  209. - (void)printDefaultConfigs {
  210. NSMutableString *output = [[NSMutableString alloc] init];
  211. [output appendString:@"\n-------Default config------\n"];
  212. NSArray<NSString *> *result = [self.RCInstances[self.currentNamespace][self.FIRAppName]
  213. allKeysFromSource:FIRRemoteConfigSourceDefault];
  214. if (result) {
  215. NSString *stringPerNs = @"";
  216. for (NSString *key in result) {
  217. FIRRemoteConfigValue *value =
  218. [self.RCInstances[self.currentNamespace][self.FIRAppName] defaultValueForKey:key];
  219. stringPerNs = [NSString stringWithFormat:@"%@%@ : %@ : %@\n", stringPerNs,
  220. self.currentNamespace, key, value.stringValue];
  221. }
  222. [output appendString:stringPerNs];
  223. }
  224. [[FRCLog sharedInstance] logToConsole:output];
  225. }
  226. - (IBAction)getValueButtonPressed:(id)sender {
  227. [[FRCLog sharedInstance] logToConsole:[self.RCInstances[self.currentNamespace][self.FIRAppName]
  228. configValueForKey:self.keyLabel.text]
  229. .debugDescription];
  230. }
  231. - (IBAction)logValueButtonPressed:(id)sender {
  232. [[FRCLog sharedInstance]
  233. logToConsole:[NSString stringWithFormat:@"key: %@ logged", self.keyLabel.text]];
  234. }
  235. // add default variable button pressed
  236. - (IBAction)addButtonPressed:(id)sender {
  237. [self addNewEntryToVariables:self.configDefaults isDefaults:YES];
  238. }
  239. - (IBAction)developerModeSwitched:(id)sender {
  240. FIRRemoteConfigSettings *configSettings =
  241. [[FIRRemoteConfigSettings alloc] initWithDeveloperModeEnabled:self.developerModeEnabled.isOn];
  242. ((FIRRemoteConfig *)(self.RCInstances[self.currentNamespace][self.FIRAppName])).configSettings =
  243. configSettings;
  244. [[FRCLog sharedInstance]
  245. logToConsole:[NSString
  246. stringWithFormat:@"Developer Mode Enabled: %d\n",
  247. ((FIRRemoteConfig *)(self.RCInstances[self.currentNamespace]
  248. [self.FIRAppName]))
  249. .configSettings.isDeveloperModeEnabled]];
  250. }
  251. - (void)addNewEntryToVariables:(NSMutableDictionary *)variables isDefaults:(BOOL)isDefaults {
  252. if ([self.keyLabel.text length]) {
  253. variables[self.keyLabel.text] = self.valueLabel.text;
  254. NSString *showText = @"custom variables ";
  255. if (isDefaults) {
  256. showText = @"config defaults";
  257. }
  258. [[FRCLog sharedInstance]
  259. logToConsole:[NSString stringWithFormat:@"New %@ added %@ : %@\n", showText,
  260. self.keyLabel.text, self.valueLabel.text]];
  261. self.keyLabel.text = @"";
  262. self.valueLabel.text = @"";
  263. }
  264. }
  265. - (void)apply {
  266. [self.RCInstances[self.currentNamespace][self.FIRAppName]
  267. activateWithCompletionHandler:^(NSError *_Nullable error) {
  268. NSMutableString *output = [[NSMutableString alloc] init];
  269. [output appendString:[NSString stringWithFormat:@"ActivateFetched = %@\n",
  270. error ? @"NO" : @"YES"]];
  271. [[FRCLog sharedInstance] logToConsole:output];
  272. if (!error) {
  273. [self printResult:output];
  274. } else {
  275. [self printResult:[[NSString stringWithFormat:@"Activate failed. Error: %@",
  276. error.localizedDescription] mutableCopy]];
  277. }
  278. }];
  279. }
  280. // print out fetch result
  281. - (void)printResult:(NSMutableString *)output {
  282. FIRRemoteConfig *currentRCInstance = self.RCInstances[self.currentNamespace][self.FIRAppName];
  283. NSString *namespace_p = self.currentNamespace;
  284. [output appendString:@"-------Active config------\n"];
  285. NSArray<NSString *> *result = [self.RCInstances[self.currentNamespace][self.FIRAppName]
  286. allKeysFromSource:FIRRemoteConfigSourceRemote];
  287. if (result) {
  288. NSString *stringPerNs = @"";
  289. for (NSString *key in result) {
  290. FIRRemoteConfigValue *value = [currentRCInstance configValueForKey:key];
  291. stringPerNs = [NSString
  292. stringWithFormat:@"%@%@ : %@ : %@\n", stringPerNs, namespace_p, key, value.stringValue];
  293. }
  294. [output appendString:stringPerNs];
  295. }
  296. [output appendString:@"\n-------Default config------\n"];
  297. result = [currentRCInstance allKeysFromSource:FIRRemoteConfigSourceDefault];
  298. if (result) {
  299. NSString *stringPerNs = @"";
  300. for (NSString *key in result) {
  301. FIRRemoteConfigValue *value = [currentRCInstance defaultValueForKey:key];
  302. stringPerNs = [NSString
  303. stringWithFormat:@"%@%@ : %@ : %@\n", stringPerNs, namespace_p, key, value.stringValue];
  304. }
  305. [output appendString:stringPerNs];
  306. }
  307. [output appendString:@"\n--------Custom Variables--------\n"];
  308. [output
  309. appendString:[NSString
  310. stringWithFormat:@"Developer Mode Enabled: %d\n",
  311. currentRCInstance.configSettings.isDeveloperModeEnabled]];
  312. [output appendString:@"\n----------Last fetch time----------------\n"];
  313. NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
  314. [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
  315. [output
  316. appendString:[NSString stringWithFormat:@"%@\n",
  317. [dateFormatter
  318. stringFromDate:currentRCInstance.lastFetchTime]]];
  319. [output appendString:@"\n-----------Last fetch status------------\n"];
  320. [output appendString:[NSString
  321. stringWithFormat:@"%@\n",
  322. [self statusString:currentRCInstance.lastFetchStatus]]];
  323. FIRInstallations *installations = [FIRInstallations installations];
  324. [installations installationIDWithCompletion:^(NSString *_Nullable identifier,
  325. NSError *_Nullable error) {
  326. [output appendString:@"\n-----------Installation ID------------------\n"];
  327. [output appendString:[NSString stringWithFormat:@"%@\n", identifier]];
  328. [output appendString:@"\n-----------Installation ID token------------\n"];
  329. [installations authTokenWithCompletion:^(FIRInstallationsAuthTokenResult *_Nullable tokenResult,
  330. NSError *_Nullable error) {
  331. [output appendString:[NSString stringWithFormat:@"%@\n", tokenResult.authToken]];
  332. [[FRCLog sharedInstance] logToConsole:output];
  333. }];
  334. }];
  335. }
  336. - (BOOL)textFieldShouldReturn:(UITextField *)textField {
  337. [textField resignFirstResponder];
  338. return YES;
  339. }
  340. - (NSString *)statusString:(FIRRemoteConfigFetchStatus)status {
  341. switch (status) {
  342. case FIRRemoteConfigFetchStatusSuccess:
  343. return @"Success";
  344. case FIRRemoteConfigFetchStatusNoFetchYet:
  345. return @"NotFetchYet";
  346. case FIRRemoteConfigFetchStatusFailure:
  347. return @"Failure";
  348. case FIRRemoteConfigFetchStatusThrottled:
  349. return @"Throttled";
  350. default:
  351. return @"Unknown";
  352. }
  353. return @"";
  354. }
  355. - (NSString *)errorString:(FIRRemoteConfigError)error {
  356. switch (error) {
  357. case FIRRemoteConfigErrorInternalError:
  358. return @"Internal Error";
  359. case FIRRemoteConfigErrorUnknown:
  360. return @"Unknown Error";
  361. case FIRRemoteConfigErrorThrottled:
  362. return @"Throttled";
  363. default:
  364. return @"unknown";
  365. }
  366. return @"";
  367. }
  368. - (IBAction)fetchIIDButtonClicked:(id)sender {
  369. FIRInstallations *installations =
  370. [FIRInstallations installationsWithApp:[FIRApp appNamed:self.FIRAppName]];
  371. [installations installationIDWithCompletion:^(NSString *_Nullable identifier,
  372. NSError *_Nullable error) {
  373. if (error) {
  374. [[FRCLog sharedInstance] logToConsole:[NSString stringWithFormat:@"%@", error]];
  375. } else {
  376. [installations authTokenWithCompletion:^(
  377. FIRInstallationsAuthTokenResult *_Nullable tokenResult,
  378. NSError *_Nullable error) {
  379. if (tokenResult.authToken) {
  380. ((FIRRemoteConfig *)self.RCInstances[self.currentNamespace][self.FIRAppName])
  381. .settings.configInstallationsToken = tokenResult.authToken;
  382. [[FRCLog sharedInstance]
  383. logToConsole:[NSString
  384. stringWithFormat:
  385. @"Successfully got installation ID : \n\n%@\n\nToken : \n\n%@\n",
  386. identifier, tokenResult.authToken]];
  387. }
  388. }];
  389. }
  390. }];
  391. }
  392. - (IBAction)searchButtonClicked:(id)sender {
  393. NSString *output = @"-------Active Config------\n";
  394. for (NSString *key in [self.RCInstances[self.currentNamespace][self.FIRAppName]
  395. keysWithPrefix:self.keyLabel.text]) {
  396. FIRRemoteConfigValue *value =
  397. ((FIRRemoteConfig *)(self.RCInstances[self.currentNamespace][self.FIRAppName]))[key];
  398. output = [NSString stringWithFormat:@"%@%@ : %@ : %@\n", output, self.currentNamespace, key,
  399. value.stringValue];
  400. }
  401. [[FRCLog sharedInstance] logToConsole:output];
  402. }
  403. - (IBAction)userPropertyButtonClicked:(id)sender {
  404. [FIRAnalytics setUserPropertyString:self.valueLabel.text forName:self.keyLabel.text];
  405. NSString *output = [NSString
  406. stringWithFormat:@"Set User Property => %@ : %@\n", self.keyLabel.text, self.valueLabel.text];
  407. [[FRCLog sharedInstance] logToConsole:output];
  408. }
  409. #pragma mark - picker
  410. // The number of columns of data
  411. - (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
  412. // App and currentNamespace pickers.
  413. return 2;
  414. }
  415. // The number of rows of data
  416. - (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
  417. NSInteger rowCount = (component == 0) ? self.namespacePickerData.count : self.appPickerData.count;
  418. return rowCount;
  419. }
  420. // The data to return for the row and component (column) that's being passed in
  421. - (NSString *)pickerView:(UIPickerView *)pickerView
  422. titleForRow:(NSInteger)row
  423. forComponent:(NSInteger)component {
  424. if (component == 0) {
  425. self.currentNamespace = self.namespacePickerData[row];
  426. return self.namespacePickerData[row];
  427. } else {
  428. self.FIRAppName = self.appPickerData[row];
  429. return self.appPickerData[row];
  430. }
  431. }
  432. - (UIView *)pickerView:(UIPickerView *)pickerView
  433. viewForRow:(NSInteger)row
  434. forComponent:(NSInteger)component
  435. reusingView:(UIView *)view {
  436. UILabel *tView = (UILabel *)view;
  437. if (!tView) {
  438. tView = [[UILabel alloc] init];
  439. [tView setFont:[UIFont fontWithName:@"Helvetica" size:15]];
  440. tView.numberOfLines = 3;
  441. }
  442. if (component == 0) {
  443. self.currentNamespace = self.namespacePickerData[row];
  444. tView.text = self.namespacePickerData[row];
  445. } else {
  446. self.FIRAppName = self.appPickerData[row];
  447. tView.text = self.appPickerData[row];
  448. }
  449. return tView;
  450. }
  451. @end