Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public class SFSDKWebViewStateManager: NSObject {

@objc
@MainActor
@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.")
public static func removeSession() {
if sessionCookieManagementDisabled {
SFSDKCoreLogger.d(SFSDKWebViewStateManager.self, message: "[\(Self.self) removeSession]: Cookie Management disabled. Will do nothing.")
Expand All @@ -62,6 +63,22 @@ public class SFSDKWebViewStateManager: NSObject {
}
}

@objc
@MainActor
public static func clearCache() async {
let dataStore = WKWebsiteDataStore.default()
let nonCookieDataTypes: Set<String> = [WKWebsiteDataTypeDiskCache,
WKWebsiteDataTypeMemoryCache,
WKWebsiteDataTypeFetchCache,
WKWebsiteDataTypeLocalStorage,
WKWebsiteDataTypeSessionStorage,
WKWebsiteDataTypeIndexedDBDatabases,
WKWebsiteDataTypeWebSQLDatabases,
WKWebsiteDataTypeOfflineWebApplicationCache,
WKWebsiteDataTypeServiceWorkerRegistrations]
await dataStore.removeData(ofTypes: nonCookieDataTypes, modifiedSince: Date.distantPast)
}

@objc
@MainActor
public static func removeSessionForcefully() async {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,12 @@ NS_SWIFT_NAME(SalesforceLoginViewControllerDelegate)
*/
- (void)loginViewController:(nonnull SFLoginViewController *)loginViewController didChangeLoginHost:(nonnull SFSDKLoginHost *)newLoginHost;

- (void)loginViewControllerDidClearCache:(nonnull SFLoginViewController *)loginViewController;

- (void)loginViewControllerDidClearCookies:(nonnull SFLoginViewController *)loginViewController;

- (void)loginViewControllerDidReload:(nonnull SFLoginViewController *)loginViewController;

@end

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

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

/// Specify the visibility of the server picker option in the settings menu.
@property (nonatomic) BOOL showServerPicker NS_SWIFT_NAME(showsServerPicker);

/** Specify all display properties in a config. All the above properties are backed by
a config object */
@property (nonatomic, strong, nonnull) SFSDKLoginViewControllerConfig *config;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,14 @@ - (void)setShowSettingsIcon:(BOOL)showSettingsIcon {
self.config.showSettingsIcon = showSettingsIcon;
}

- (BOOL)showServerPicker {
return self.config.showServerPicker;
}

- (void)setShowServerPicker:(BOOL)showServerPicker {
self.config.showServerPicker = showServerPicker;
}

- (SFSDKLoginViewControllerConfig *)config {
return _config;
}
Expand All @@ -200,20 +208,10 @@ - (void)setupNavigationBar {
if (self.showNavbar) {
self.navBar = self.navigationController.navigationBar;
self.navBar.topItem.titleView = [self createTitleItem];
// Hides the gear icon if there are no hosts to switch to.
SFManagedPreferences *managedPreferences = [SFManagedPreferences sharedPreferences];
if (managedPreferences.onlyShowAuthorizedHosts && managedPreferences.loginHosts.count == 0) {
self.config.showSettingsIcon = NO;
}

if(self.showSettingsIcon) {
// Setup right bar button.
UIBarButtonItem *button = [self createSettingsButton];
if (!button.target){
[button setTarget:self];
}
if (!button.action){
[button setAction:@selector(showLoginHost:)];
}
self.navBar.topItem.rightBarButtonItem = button;
}
[self styleNavigationBar:self.navBar];
Expand Down Expand Up @@ -265,9 +263,57 @@ - (UIBarButtonItem *)createBackButton {

- (UIBarButtonItem *)createSettingsButton {
UIImage *image = [[SFSDKResourceUtils imageNamed:@"login-window-gear"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
UIBarButtonItem *settingsButton = [[UIBarButtonItem alloc] initWithImage:image style:UIBarButtonItemStylePlain target:self action:@selector(showLoginHost:)];
settingsButton.accessibilityLabel = [SFSDKResourceUtils localizedString:@"LOGIN_CHOOSE_SERVER"];
settingsButton.accessibilityIdentifier = @"choose connection button";

NSMutableArray *menuActions = [[NSMutableArray alloc] initWithCapacity:3];

// Don't show the change server option if there are no hosts to switch to.
SFManagedPreferences *managedPreferences = [SFManagedPreferences sharedPreferences];
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I moved this check to just be around the “Choose Connection” option instead of the settings icon but that would just be for MDM. @wmathurin what do you think about having another property like showServerPicker on the login config in case an app wants to show the settings icon but hide the change server option? Here’s the existing configuration doc: https://developer.salesforce.com/docs/platform/mobile-sdk/guide/oauth-hide-gear-icon.html

if (managedPreferences.onlyShowAuthorizedHosts && managedPreferences.loginHosts.count == 0) {
self.showServerPicker = NO;
}

if (self.showServerPicker) {
[menuActions addObject:[UIAction actionWithTitle:[SFSDKResourceUtils localizedString:@"LOGIN_CHOOSE_SERVER"]
image:nil
identifier:nil
handler:^(__kindof UIAction* _Nonnull action) {
[self showLoginHost:self];
}]];
}


[menuActions addObject:[UIAction actionWithTitle:[SFSDKResourceUtils localizedString:@"LOGIN_CLEAR_COOKIES"]
image:nil
identifier:nil
handler:^(__kindof UIAction* _Nonnull action) {
if ([self.delegate respondsToSelector:@selector(loginViewControllerDidClearCookies:)]) {
[self.delegate loginViewControllerDidClearCookies:self];
}
}]];

[menuActions addObject:[UIAction actionWithTitle:[SFSDKResourceUtils localizedString:@"LOGIN_CLEAR_CACHE"]
image:nil
identifier:nil
handler:^(__kindof UIAction* _Nonnull action) {
if ([self.delegate respondsToSelector:@selector(loginViewControllerDidClearCache:)]) {
[self.delegate loginViewControllerDidClearCache:self];
}
}]];

[menuActions addObject:[UIAction actionWithTitle:[SFSDKResourceUtils localizedString:@"LOGIN_RELOAD"]
image:nil
identifier:nil
handler:^(__kindof UIAction* _Nonnull action) {
if ([self.delegate respondsToSelector:@selector(loginViewControllerDidReload:)]) {
[self.delegate loginViewControllerDidReload:self];
}
}]];

UIMenu *menu = [UIMenu menuWithTitle:@"" // No title
children:menuActions];
UIBarButtonItem *settingsButton = [[UIBarButtonItem alloc] initWithImage:image menu:menu];
settingsButton.accessibilityLabel = [SFSDKResourceUtils localizedString:@"LOGIN_SETTINGS_BUTTON"];
settingsButton.accessibilityIdentifier = @"settings";
return settingsButton;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,17 @@ NS_SWIFT_NAME(SalesforceLoginViewControllerConfig)
/** Specify visibility of nav bar. This property will be used to hide/show the nav bar*/
@property (nonatomic) BOOL showNavbar NS_SWIFT_NAME(showsNavigationBar);

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

/** 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.
/// Specify the visibility of the server picker option in the settings menu.
@property (nonatomic) BOOL showServerPicker NS_SWIFT_NAME(showsServerPicker);

/** 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.
*/
@property (nonatomic,readonly) BOOL shouldDisplayBackButton NS_SWIFT_NAME(showsBackButton);

/** Specifiy a delegate for LoginViewController. */
/** Specify a delegate for LoginViewController. */
@property (nonatomic, weak, nullable) id<SFLoginViewControllerDelegate> delegate;

@property (nonatomic, copy, nullable) SFLoginViewControllerCreationBlock loginViewControllerCreationBlock;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ - (instancetype)init {
self.navBarFont = nil;
_showNavbar = YES;
_showSettingsIcon = YES;
_showServerPicker = YES;
}
return self;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -730,7 +730,7 @@ - (void)postPushUnregistration:(SFUserAccount *)user logoutReason:(SFLogoutReaso
[self setCurrentUserInternal:nil];
}

[SFSDKWebViewStateManager removeSession];
[SFSDKWebViewStateManager resetSessionCookie];

//restore these id's inorder to enable post logout cleanup of components
// TODO: Revisit the userInfo data structure of kSFNotificationUserDidLogout in 7.0.
Expand Down Expand Up @@ -1044,6 +1044,25 @@ - (void)loginViewController:(SFLoginViewController *)loginViewController didChan
[[NSNotificationCenter defaultCenter] postNotification:loginHostChangedNotification];
NSString *sceneId = loginViewController.view.window.windowScene.session.persistentIdentifier;
self.authSessions[sceneId].oauthRequest.loginHost = newLoginHost.host;
[self restartAuthenticationForViewController:loginViewController];
}

- (void)loginViewControllerDidClearCache:(SFLoginViewController *)loginViewController {
Copy link
Member Author

@bbirman bbirman Oct 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From this comment about reloading, I used the same restart method as the one that happens after the login host is changed

[SFSDKWebViewStateManager clearCacheWithCompletionHandler:^{}];
[self restartAuthenticationForViewController:loginViewController];
}

- (void)loginViewControllerDidClearCookies:(SFLoginViewController *)loginViewController {
[SFSDKWebViewStateManager removeSessionForcefullyWithCompletionHandler:^{}];
[self restartAuthenticationForViewController:loginViewController];
}

- (void)loginViewControllerDidReload:(SFLoginViewController *)loginViewController {
[self restartAuthenticationForViewController:loginViewController];
}

- (void)restartAuthenticationForViewController:(SFLoginViewController *)loginViewController {
NSString *sceneId = loginViewController.view.window.windowScene.session.persistentIdentifier;
[self restartAuthentication:self.authSessions[sceneId]];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
@MainActor
func testProcessPoolIsNil() {
// TODO remove this test in 14.0 when we remove sharedProcessPool from SFSDKWebViewStateManager
XCTAssertNil(SFSDKWebViewStateManager.sharedProcessPool)

Check warning on line 19 in libs/SalesforceSDKCore/SalesforceSDKCoreTests/WebViewStateManagerTests.swift

View workflow job for this annotation

GitHub Actions / ios-pr (SalesforceSDKCore, ^18) / test-ios

'sharedProcessPool' is deprecated: Deprecated in Salesforce Mobile SDK 13.2 and will be removed in Salesforce Mobile SDK 14.0. WKProcessPool creation has no effect on iOS 15+ and this property will be removed.
}

@MainActor
Expand All @@ -35,4 +35,38 @@
SFSDKWebViewStateManager.sessionCookieManagementDisabled = false
XCTAssertFalse(SFSDKWebViewStateManager.sessionCookieManagementDisabled)
}

@MainActor
func testClearCache() async throws {
// Add some test data
let html = "<html><body><script>localStorage.setItem('test', 'value');</script></body></html>"
let webView = WKWebView(frame: CGRect(x: 0, y: 0, width: 100, height: 100), configuration: WKWebViewConfiguration())

// Load HTML and wait for JavaScript to execute
webView.loadHTMLString(html, baseURL: URL(string: "https://test.salesforce.com"))
try await Task.sleep(for: .seconds(1))

// Define data types to check
let dataTypes: Set<String> = [WKWebsiteDataTypeDiskCache,
WKWebsiteDataTypeMemoryCache,
WKWebsiteDataTypeFetchCache,
WKWebsiteDataTypeLocalStorage,
WKWebsiteDataTypeSessionStorage,
WKWebsiteDataTypeIndexedDBDatabases,
WKWebsiteDataTypeWebSQLDatabases,
WKWebsiteDataTypeOfflineWebApplicationCache,
WKWebsiteDataTypeServiceWorkerRegistrations]

let dataStore = WKWebsiteDataStore.default()
// Verify data exists before clearing
let initialRecords = await dataStore.dataRecords(ofTypes: dataTypes)
XCTAssertFalse(initialRecords.isEmpty, "Expected data to exist before clearing")

Check failure on line 63 in libs/SalesforceSDKCore/SalesforceSDKCoreTests/WebViewStateManagerTests.swift

View workflow job for this annotation

GitHub Actions / ios-pr (SalesforceSDKCore, ^26) / test-ios

testClearCache, XCTAssertFalse failed - Expected data to exist before clearing

// Clear the cache
await SFSDKWebViewStateManager.clearCache()

// Verify data was cleared
let records = await dataStore.dataRecords(ofTypes: dataTypes)
XCTAssertTrue(records.isEmpty, "Expected data to be cleared")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,13 +59,18 @@
"LOGIN_SERVER_PRODUCTION" = "Production";
"LOGIN_SERVER_SANDBOX" = "Sandbox";
"LOGIN_SERVER_WELCOME" = "Welcome";
"LOGIN_CHOOSE_SERVER" = "Choose Connection";
"LOGIN_CHOOSE_SERVER" = "Change Server";
"LOGIN_ADD_SERVER" = "Add Connection";
"LOGIN_SERVER_URL" = "Host";
"LOGIN_SERVER_NAME" = "Label";
"LOGIN_SERVER_URL_PLACEHOLDER" = "Example: login.salesforce.com";
"LOGIN_SERVER_NAME_PLACEHOLDER" = "Optional";
"DONE_BUTTON" = "Done";
"LOGIN_SETTINGS_BUTTON" = "Settings";

"LOGIN_CLEAR_COOKIES" = "Clear Cookies";
"LOGIN_CLEAR_CACHE" = "Clear Cache";
"LOGIN_RELOAD" = "Reload";

// Account Switcher
"ACCOUNT_SWITCHER_TITLE" = "Manage Accounts";
Expand Down
Loading