Skip to content

Commit 362007b

Browse files
authored
Merge pull request forcedotcom#3940 from bbirman/settings-menu
Add settings menu to login screen
2 parents 12143c2 + 8afb61a commit 362007b

File tree

8 files changed

+154
-20
lines changed

8 files changed

+154
-20
lines changed

libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Common/WebViewStateManager.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ public class SFSDKWebViewStateManager: NSObject {
3838

3939
@objc
4040
@MainActor
41+
@available(*, deprecated, renamed: "resetSessionCookie", message: "Deprecated in Salesforce Mobile SDK 13.2 and will be removed in Salesforce Mobile SDK 14.0. Use resetSessionCookie instead.")
4142
public static func removeSession() {
4243
if sessionCookieManagementDisabled {
4344
SFSDKCoreLogger.d(SFSDKWebViewStateManager.self, message: "[\(Self.self) removeSession]: Cookie Management disabled. Will do nothing.")
@@ -62,6 +63,22 @@ public class SFSDKWebViewStateManager: NSObject {
6263
}
6364
}
6465

66+
@objc
67+
@MainActor
68+
public static func clearCache() async {
69+
let dataStore = WKWebsiteDataStore.default()
70+
let nonCookieDataTypes: Set<String> = [WKWebsiteDataTypeDiskCache,
71+
WKWebsiteDataTypeMemoryCache,
72+
WKWebsiteDataTypeFetchCache,
73+
WKWebsiteDataTypeLocalStorage,
74+
WKWebsiteDataTypeSessionStorage,
75+
WKWebsiteDataTypeIndexedDBDatabases,
76+
WKWebsiteDataTypeWebSQLDatabases,
77+
WKWebsiteDataTypeOfflineWebApplicationCache,
78+
WKWebsiteDataTypeServiceWorkerRegistrations]
79+
await dataStore.removeData(ofTypes: nonCookieDataTypes, modifiedSince: Date.distantPast)
80+
}
81+
6582
@objc
6683
@MainActor
6784
public static func removeSessionForcefully() async {

libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Login/SFLoginViewController.h

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ NS_SWIFT_NAME(SalesforceLoginViewControllerDelegate)
4848
*/
4949
- (void)loginViewController:(nonnull SFLoginViewController *)loginViewController didChangeLoginHost:(nonnull SFSDKLoginHost *)newLoginHost;
5050

51+
- (void)loginViewControllerDidClearCache:(nonnull SFLoginViewController *)loginViewController;
52+
53+
- (void)loginViewControllerDidClearCookies:(nonnull SFLoginViewController *)loginViewController;
54+
55+
- (void)loginViewControllerDidReload:(nonnull SFLoginViewController *)loginViewController;
56+
5157
@end
5258

5359
/** The Salesforce login screen view.
@@ -85,9 +91,12 @@ NS_SWIFT_NAME(SalesforceLoginViewController)
8591
/** Specify visibility of nav bar. This property will be used to hide/show the nav bar*/
8692
@property (nonatomic) BOOL showNavbar NS_SWIFT_NAME(showsNavigationBar);
8793

88-
/** Specifiy the visibility of the settings icon. This property will be used to hide/show the settings icon*/
94+
/** Specify the visibility of the settings icon. This property will be used to hide/show the settings icon*/
8995
@property (nonatomic) BOOL showSettingsIcon NS_SWIFT_NAME(showsSettingsIcon);
9096

97+
/// Specify the visibility of the server picker option in the settings menu.
98+
@property (nonatomic) BOOL showServerPicker NS_SWIFT_NAME(showsServerPicker);
99+
91100
/** Specify all display properties in a config. All the above properties are backed by
92101
a config object */
93102
@property (nonatomic, strong, nonnull) SFSDKLoginViewControllerConfig *config;

libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Login/SFLoginViewController.m

Lines changed: 60 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,14 @@ - (void)setShowSettingsIcon:(BOOL)showSettingsIcon {
184184
self.config.showSettingsIcon = showSettingsIcon;
185185
}
186186

187+
- (BOOL)showServerPicker {
188+
return self.config.showServerPicker;
189+
}
190+
191+
- (void)setShowServerPicker:(BOOL)showServerPicker {
192+
self.config.showServerPicker = showServerPicker;
193+
}
194+
187195
- (SFSDKLoginViewControllerConfig *)config {
188196
return _config;
189197
}
@@ -200,20 +208,10 @@ - (void)setupNavigationBar {
200208
if (self.showNavbar) {
201209
self.navBar = self.navigationController.navigationBar;
202210
self.navBar.topItem.titleView = [self createTitleItem];
203-
// Hides the gear icon if there are no hosts to switch to.
204-
SFManagedPreferences *managedPreferences = [SFManagedPreferences sharedPreferences];
205-
if (managedPreferences.onlyShowAuthorizedHosts && managedPreferences.loginHosts.count == 0) {
206-
self.config.showSettingsIcon = NO;
207-
}
211+
208212
if(self.showSettingsIcon) {
209213
// Setup right bar button.
210214
UIBarButtonItem *button = [self createSettingsButton];
211-
if (!button.target){
212-
[button setTarget:self];
213-
}
214-
if (!button.action){
215-
[button setAction:@selector(showLoginHost:)];
216-
}
217215
self.navBar.topItem.rightBarButtonItem = button;
218216
}
219217
[self styleNavigationBar:self.navBar];
@@ -265,9 +263,57 @@ - (UIBarButtonItem *)createBackButton {
265263

266264
- (UIBarButtonItem *)createSettingsButton {
267265
UIImage *image = [[SFSDKResourceUtils imageNamed:@"login-window-gear"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
268-
UIBarButtonItem *settingsButton = [[UIBarButtonItem alloc] initWithImage:image style:UIBarButtonItemStylePlain target:self action:@selector(showLoginHost:)];
269-
settingsButton.accessibilityLabel = [SFSDKResourceUtils localizedString:@"LOGIN_CHOOSE_SERVER"];
270-
settingsButton.accessibilityIdentifier = @"choose connection button";
266+
267+
NSMutableArray *menuActions = [[NSMutableArray alloc] initWithCapacity:3];
268+
269+
// Don't show the change server option if there are no hosts to switch to.
270+
SFManagedPreferences *managedPreferences = [SFManagedPreferences sharedPreferences];
271+
if (managedPreferences.onlyShowAuthorizedHosts && managedPreferences.loginHosts.count == 0) {
272+
self.showServerPicker = NO;
273+
}
274+
275+
if (self.showServerPicker) {
276+
[menuActions addObject:[UIAction actionWithTitle:[SFSDKResourceUtils localizedString:@"LOGIN_CHOOSE_SERVER"]
277+
image:nil
278+
identifier:nil
279+
handler:^(__kindof UIAction* _Nonnull action) {
280+
[self showLoginHost:self];
281+
}]];
282+
}
283+
284+
285+
[menuActions addObject:[UIAction actionWithTitle:[SFSDKResourceUtils localizedString:@"LOGIN_CLEAR_COOKIES"]
286+
image:nil
287+
identifier:nil
288+
handler:^(__kindof UIAction* _Nonnull action) {
289+
if ([self.delegate respondsToSelector:@selector(loginViewControllerDidClearCookies:)]) {
290+
[self.delegate loginViewControllerDidClearCookies:self];
291+
}
292+
}]];
293+
294+
[menuActions addObject:[UIAction actionWithTitle:[SFSDKResourceUtils localizedString:@"LOGIN_CLEAR_CACHE"]
295+
image:nil
296+
identifier:nil
297+
handler:^(__kindof UIAction* _Nonnull action) {
298+
if ([self.delegate respondsToSelector:@selector(loginViewControllerDidClearCache:)]) {
299+
[self.delegate loginViewControllerDidClearCache:self];
300+
}
301+
}]];
302+
303+
[menuActions addObject:[UIAction actionWithTitle:[SFSDKResourceUtils localizedString:@"LOGIN_RELOAD"]
304+
image:nil
305+
identifier:nil
306+
handler:^(__kindof UIAction* _Nonnull action) {
307+
if ([self.delegate respondsToSelector:@selector(loginViewControllerDidReload:)]) {
308+
[self.delegate loginViewControllerDidReload:self];
309+
}
310+
}]];
311+
312+
UIMenu *menu = [UIMenu menuWithTitle:@"" // No title
313+
children:menuActions];
314+
UIBarButtonItem *settingsButton = [[UIBarButtonItem alloc] initWithImage:image menu:menu];
315+
settingsButton.accessibilityLabel = [SFSDKResourceUtils localizedString:@"LOGIN_SETTINGS_BUTTON"];
316+
settingsButton.accessibilityIdentifier = @"settings";
271317
return settingsButton;
272318
}
273319

libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Login/SFSDKLoginViewControllerConfig.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,17 @@ NS_SWIFT_NAME(SalesforceLoginViewControllerConfig)
4343
/** Specify visibility of nav bar. This property will be used to hide/show the nav bar*/
4444
@property (nonatomic) BOOL showNavbar NS_SWIFT_NAME(showsNavigationBar);
4545

46-
/** Specifiy the visibility of the settings icon. This property will be used to hide/show the settings icon*/
46+
/** Specify the visibility of the settings icon. This property will be used to hide/show the settings icon*/
4747
@property (nonatomic) BOOL showSettingsIcon NS_SWIFT_NAME(showsSettingsIcon);
4848

49-
/** Specifiy the visibility of the back icon. This property value can be changed by changing the value of shouldAuthenticate in bootconfig or by subclasssing SFLoginViewController.
49+
/// Specify the visibility of the server picker option in the settings menu.
50+
@property (nonatomic) BOOL showServerPicker NS_SWIFT_NAME(showsServerPicker);
51+
52+
/** Specify the visibility of the back icon. This property value can be changed by changing the value of shouldAuthenticate in bootconfig or by subclasssing SFLoginViewController.
5053
*/
5154
@property (nonatomic,readonly) BOOL shouldDisplayBackButton NS_SWIFT_NAME(showsBackButton);
5255

53-
/** Specifiy a delegate for LoginViewController. */
56+
/** Specify a delegate for LoginViewController. */
5457
@property (nonatomic, weak, nullable) id<SFLoginViewControllerDelegate> delegate;
5558

5659
@property (nonatomic, copy, nullable) SFLoginViewControllerCreationBlock loginViewControllerCreationBlock;

libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Login/SFSDKLoginViewControllerConfig.m

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ - (instancetype)init {
4545
self.navBarFont = nil;
4646
_showNavbar = YES;
4747
_showSettingsIcon = YES;
48+
_showServerPicker = YES;
4849
}
4950
return self;
5051
}

libs/SalesforceSDKCore/SalesforceSDKCore/Classes/UserAccount/SFUserAccountManager.m

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -730,7 +730,7 @@ - (void)postPushUnregistration:(SFUserAccount *)user logoutReason:(SFLogoutReaso
730730
[self setCurrentUserInternal:nil];
731731
}
732732

733-
[SFSDKWebViewStateManager removeSession];
733+
[SFSDKWebViewStateManager resetSessionCookie];
734734

735735
//restore these id's inorder to enable post logout cleanup of components
736736
// TODO: Revisit the userInfo data structure of kSFNotificationUserDidLogout in 7.0.
@@ -1044,6 +1044,25 @@ - (void)loginViewController:(SFLoginViewController *)loginViewController didChan
10441044
[[NSNotificationCenter defaultCenter] postNotification:loginHostChangedNotification];
10451045
NSString *sceneId = loginViewController.view.window.windowScene.session.persistentIdentifier;
10461046
self.authSessions[sceneId].oauthRequest.loginHost = newLoginHost.host;
1047+
[self restartAuthenticationForViewController:loginViewController];
1048+
}
1049+
1050+
- (void)loginViewControllerDidClearCache:(SFLoginViewController *)loginViewController {
1051+
[SFSDKWebViewStateManager clearCacheWithCompletionHandler:^{}];
1052+
[self restartAuthenticationForViewController:loginViewController];
1053+
}
1054+
1055+
- (void)loginViewControllerDidClearCookies:(SFLoginViewController *)loginViewController {
1056+
[SFSDKWebViewStateManager removeSessionForcefullyWithCompletionHandler:^{}];
1057+
[self restartAuthenticationForViewController:loginViewController];
1058+
}
1059+
1060+
- (void)loginViewControllerDidReload:(SFLoginViewController *)loginViewController {
1061+
[self restartAuthenticationForViewController:loginViewController];
1062+
}
1063+
1064+
- (void)restartAuthenticationForViewController:(SFLoginViewController *)loginViewController {
1065+
NSString *sceneId = loginViewController.view.window.windowScene.session.persistentIdentifier;
10471066
[self restartAuthentication:self.authSessions[sceneId]];
10481067
}
10491068

libs/SalesforceSDKCore/SalesforceSDKCoreTests/WebViewStateManagerTests.swift

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,38 @@ final class WebViewStateManagerTests: XCTestCase {
3535
SFSDKWebViewStateManager.sessionCookieManagementDisabled = false
3636
XCTAssertFalse(SFSDKWebViewStateManager.sessionCookieManagementDisabled)
3737
}
38+
39+
@MainActor
40+
func testClearCache() async throws {
41+
// Add some test data
42+
let html = "<html><body><script>localStorage.setItem('test', 'value');</script></body></html>"
43+
let webView = WKWebView(frame: CGRect(x: 0, y: 0, width: 100, height: 100), configuration: WKWebViewConfiguration())
44+
45+
// Load HTML and wait for JavaScript to execute
46+
webView.loadHTMLString(html, baseURL: URL(string: "https://test.salesforce.com"))
47+
try await Task.sleep(for: .seconds(1))
48+
49+
// Define data types to check
50+
let dataTypes: Set<String> = [WKWebsiteDataTypeDiskCache,
51+
WKWebsiteDataTypeMemoryCache,
52+
WKWebsiteDataTypeFetchCache,
53+
WKWebsiteDataTypeLocalStorage,
54+
WKWebsiteDataTypeSessionStorage,
55+
WKWebsiteDataTypeIndexedDBDatabases,
56+
WKWebsiteDataTypeWebSQLDatabases,
57+
WKWebsiteDataTypeOfflineWebApplicationCache,
58+
WKWebsiteDataTypeServiceWorkerRegistrations]
59+
60+
let dataStore = WKWebsiteDataStore.default()
61+
// Verify data exists before clearing
62+
let initialRecords = await dataStore.dataRecords(ofTypes: dataTypes)
63+
XCTAssertFalse(initialRecords.isEmpty, "Expected data to exist before clearing")
64+
65+
// Clear the cache
66+
await SFSDKWebViewStateManager.clearCache()
67+
68+
// Verify data was cleared
69+
let records = await dataStore.dataRecords(ofTypes: dataTypes)
70+
XCTAssertTrue(records.isEmpty, "Expected data to be cleared")
71+
}
3872
}

shared/resources/SalesforceSDKResources.bundle/en.lproj/Localizable.strings

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,18 @@
5959
"LOGIN_SERVER_PRODUCTION" = "Production";
6060
"LOGIN_SERVER_SANDBOX" = "Sandbox";
6161
"LOGIN_SERVER_WELCOME" = "Welcome";
62-
"LOGIN_CHOOSE_SERVER" = "Choose Connection";
62+
"LOGIN_CHOOSE_SERVER" = "Change Server";
6363
"LOGIN_ADD_SERVER" = "Add Connection";
6464
"LOGIN_SERVER_URL" = "Host";
6565
"LOGIN_SERVER_NAME" = "Label";
6666
"LOGIN_SERVER_URL_PLACEHOLDER" = "Example: login.salesforce.com";
6767
"LOGIN_SERVER_NAME_PLACEHOLDER" = "Optional";
6868
"DONE_BUTTON" = "Done";
69+
"LOGIN_SETTINGS_BUTTON" = "Settings";
70+
71+
"LOGIN_CLEAR_COOKIES" = "Clear Cookies";
72+
"LOGIN_CLEAR_CACHE" = "Clear Cache";
73+
"LOGIN_RELOAD" = "Reload";
6974

7075
// Account Switcher
7176
"ACCOUNT_SWITCHER_TITLE" = "Manage Accounts";

0 commit comments

Comments
 (0)