Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
adbcda5
Not crashing if bringing up dev infos before logging in
wmathurin Nov 7, 2025
9159566
Dev menu now has a Login Options button to bring up a screen to chang…
wmathurin Nov 8, 2025
2199596
Merge from dev
wmathurin Nov 8, 2025
216c632
Adding supports welcome discovery in login options screen
wmathurin Nov 8, 2025
df68b37
When fixing groups in Xcode - I lost the public visibility on some he…
wmathurin Nov 8, 2025
2ffda35
Showing login options during advanced auth also
wmathurin Nov 8, 2025
a668f6f
Adding some tests
wmathurin Nov 8, 2025
4ba4d12
Converting dev infos screen to swift UI - now it's in action sheet
wmathurin Nov 11, 2025
1afbe29
Fixing warnings
wmathurin Nov 11, 2025
d9e49e8
Removing unnecessary if #available
wmathurin Nov 11, 2025
796a518
Support for sections in dev info
wmathurin Nov 11, 2025
2538b76
More infos in the dev menu
wmathurin Nov 11, 2025
0330aa9
Restart authentication after presenting login options
wmathurin Nov 11, 2025
a21a187
For now - not showing login options when doing advanced auth
wmathurin Nov 11, 2025
9a893b4
Adding tests
wmathurin Nov 11, 2025
f9a9f08
Removing references to ThirdPartyDependencies (I thought I did that a…
wmathurin Nov 12, 2025
02644c6
Making sure we don't try and insert a nil in the dev info array
wmathurin Nov 12, 2025
1e601a4
Fixing compilation errors on vision os
wmathurin Nov 12, 2025
913b8c8
Attempting to fix the test flapping
wmathurin Nov 14, 2025
5dcc5d5
Tests to improve coverage of new view classes
wmathurin Nov 15, 2025
7f9d347
Another (low value) test for coverage
wmathurin Nov 15, 2025
4d2a6e4
Tests to cover change to SmartStoreSDKManager
wmathurin Nov 15, 2025
ace155d
Removed low value tests
wmathurin Nov 15, 2025
a1d8029
More time for test that use expectations
wmathurin Nov 17, 2025
8dd9bbc
Changed identity test to check the parsing of a row response (instead…
wmathurin Nov 17, 2025
dfda66a
Changing failing test - error should be easier to read
wmathurin Nov 17, 2025
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
2 changes: 1 addition & 1 deletion SalesforceSDKCore.podspec

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion libs/MobileSync/Configuration/MobileSync-Common.xcconfig
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ GCC_PREFIX_HEADER = $(PROJECT_NAME)/$(PROJECT_NAME)-Prefix.pch

PRODUCT_NAME = MobileSync

LIBRARY_SEARCH_PATHS = $(SRCROOT)/../../external/ThirdPartyDependencies/sqlcipher $(inherited)
LIBRARY_SEARCH_PATHS = $(inherited)

// Make sure to use only API that are safe for application extensions
APPLICATION_EXTENSION_API_ONLY = YES
2 changes: 2 additions & 0 deletions libs/MobileSync/MobileSync.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1610,6 +1610,7 @@
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = (
"$(inherited)",
"-framework",
Expand Down Expand Up @@ -1640,6 +1641,7 @@
"@executable_path/Frameworks",
"@loader_path/Frameworks",
);
LIBRARY_SEARCH_PATHS = "$(inherited)";
OTHER_LDFLAGS = (
"$(inherited)",
"-framework",
Expand Down
286 changes: 155 additions & 131 deletions libs/SalesforceSDKCore/SalesforceSDKCore.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,6 @@ API_UNAVAILABLE(visionos)
- (BOOL)isSnapshotPresented:(nonnull UIScene *)scene API_UNAVAILABLE(visionos);
- (void)dismissSnapshot:(nonnull UIScene *)scene completion:(void (^ __nullable)(void))completion API_UNAVAILABLE(visionos);

- (nonnull NSArray<SFSDKDevAction *> *)getDevActions:(nonnull UIViewController *)presentedViewController;

@end
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@
#import "SFManagedPreferences.h"
#import "SFApplicationHelper.h"
#import "SFSDKAppFeatureMarkers.h"
#import "SFSDKDevInfoViewController.h"
#import "SFDefaultUserManagementViewController.h"
#import "SFSDKAuthRootController.h"
#import <SalesforceSDKCommon/SFSwiftDetectUtil.h>
#import "SFSDKEncryptedURLCache.h"
#import "SFSDKNullURLCache.h"
Expand Down Expand Up @@ -471,25 +471,66 @@ - (NSString *)devInfoTitleString

- (NSArray<SFSDKDevAction *>*) getDevActions:(UIViewController *)presentedViewController
{
return @[
[[SFSDKDevAction alloc]initWith:@"Show dev info" handler:^{
SFSDKDevInfoViewController *devInfo = [[SFSDKDevInfoViewController alloc] init];
[presentedViewController presentViewController:devInfo animated:NO completion:nil];
}],
[[SFSDKDevAction alloc]initWith:@"Logout" handler:^{
[[SFUserAccountManager sharedInstance] logout:SFLogoutReasonUserInitiated];
}],
[[SFSDKDevAction alloc]initWith:@"Switch user" handler:^{
SFDefaultUserManagementViewController *umvc = [[SFDefaultUserManagementViewController alloc] initWithCompletionBlock:^(SFUserManagementAction action) {
[presentedViewController dismissViewControllerAnimated:YES completion:nil];
}];
[presentedViewController presentViewController:umvc animated:YES completion:nil];
}],
[[SFSDKDevAction alloc]initWith:@"Inspect Key-Value Store" handler:^{
UIViewController *keyValueStoreInspector = [[SFSDKKeyValueEncryptedFileStoreViewController new] createUI];
[presentedViewController presentViewController:keyValueStoreInspector animated:YES completion:nil];
}]
];
NSMutableArray<SFSDKDevAction *> *actions = [NSMutableArray array];
SFUserAccountManager *userAccountManager = [SFUserAccountManager sharedInstance];
SFUserAccount *currentUser = userAccountManager.currentUser;

// Check if we're showing the login screen
BOOL isShowingLogin = [presentedViewController isKindOfClass:[SFLoginViewController class]];
// TODO uncomment to support advanced auth case (once we add code to restart auth in that case below)
// || [presentedViewController.presentingViewController isKindOfClass:[SFSDKAuthRootController class]];
Comment on lines +480 to +481
Copy link
Contributor

Choose a reason for hiding this comment

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

Restarting Auth is included now, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Only if it's regular auth - for now I commented out the case it's advanced auth.


// Show dev info - always available
[actions addObject:[[SFSDKDevAction alloc]initWith:@"Show dev info" handler:^{
UIViewController *devInfo = [SFSDKDevInfoViewController makeViewController];
[presentedViewController presentViewController:devInfo animated:YES completion:nil];
}]];

// Login Options - only show on login screen
if (isShowingLogin) {
Copy link
Contributor

Choose a reason for hiding this comment

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

NIT: Is there a reason to only show it on the login screen?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's only useful if you do a login right after - and if you do a login (let's say after a logout or when adding a new user), then you will get a chance to bring up the screen when the login screen is shown. So it feels like login is the right time to show the login options screen. The oauth settings should be inspectable through dev infos at any time though.

[actions addObject:[[SFSDKDevAction alloc]initWith:@"Login Options" handler:^{
UIViewController *configPicker = [BootConfigPickerViewController makeViewControllerOnConfigurationCompleted:^{
[presentedViewController dismissViewControllerAnimated:YES completion:^{
// Restart authentication with the updated configuration
if ([presentedViewController isKindOfClass:[SFLoginViewController class]]) {
[[SFUserAccountManager sharedInstance] restartAuthenticationForViewController:(SFLoginViewController *)presentedViewController];
}
// TODO support advanced auth case
}];
}];
[presentedViewController presentViewController:configPicker animated:YES completion:nil];
}]];
}

// Logout - only show if there's a current user and not on login screen
if (currentUser && !isShowingLogin) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I was thinking about exactly this the other day. Android doesn't take deferred auth into account, I need to also check for a user.

[actions addObject:[[SFSDKDevAction alloc]initWith:@"Logout" handler:^{
[[SFUserAccountManager sharedInstance] logout:SFLogoutReasonUserInitiated];
}]];
}

// Switch user - only show if there's a current user and not on login screen
if (currentUser && !isShowingLogin) {
[actions addObject:[[SFSDKDevAction alloc]initWith:@"Switch user" handler:^{
SFDefaultUserManagementViewController *umvc = [[SFDefaultUserManagementViewController alloc] initWithCompletionBlock:^(SFUserManagementAction action) {
[presentedViewController dismissViewControllerAnimated:YES completion:nil];
}];
[presentedViewController presentViewController:umvc animated:YES completion:nil];
}]];
}

// Inspect Key-Value Store - only show if there are stores
BOOL hasGlobalStores = [SFSDKKeyValueEncryptedFileStore allGlobalStoreNames].count > 0;
BOOL hasUserStores = currentUser && [SFSDKKeyValueEncryptedFileStore allStoreNames].count > 0;

if (hasGlobalStores || hasUserStores) {
[actions addObject:[[SFSDKDevAction alloc]initWith:@"Inspect Key-Value Store" handler:^{
UIViewController *keyValueStoreInspector = [[SFSDKKeyValueEncryptedFileStoreViewController new] createUI];
[presentedViewController presentViewController:keyValueStoreInspector animated:YES completion:nil];
}]];
}

return [actions copy];
}

- (NSArray<NSString *>*) getDevSupportInfos
Expand All @@ -498,25 +539,62 @@ - (NSString *)devInfoTitleString
NSMutableArray * devInfos = [NSMutableArray arrayWithArray:@[
@"SDK Version", SALESFORCE_SDK_VERSION,
@"App Type", [self getAppTypeAsString],
@"User Agent", self.userAgentString(@""),
@"User Agent", self.userAgentString(@"")
]];
NSArray* allUsers = userAccountManager.allUserAccounts;
if ([allUsers count] > 0) {
[devInfos addObjectsFromArray:@[
@"Authenticated Users", [self usersToString:allUsers]
]];
}

// Auth configuration
[devInfos addObject:@"section:Auth Config"];
[devInfos addObjectsFromArray:@[
@"Use Web Server Authentication", [self useWebServerAuthentication] ? @"YES" : @"NO",
@"Use Hybrid Authentication", [self useHybridAuthentication] ? @"YES" : @"NO",
@"Supports Welcome Discovery", [self supportsWelcomeDiscovery] ? @"YES" : @"NO",
@"Browser Login Enabled", [SFUserAccountManager sharedInstance].useBrowserAuth? @"YES" : @"NO",
@"IDP Enabled", [self idpEnabled] ? @"YES" : @"NO",
@"Identity Provider", [self isIdentityProvider] ? @"YES" : @"NO",
@"Current User", [self userToString:userAccountManager.currentUser],
@"Scopes", [userAccountManager.currentUser.credentials.scopes componentsJoinedByString:@" "],
@"Access Token Expiration", [self accessTokenExpiration],
@"Authenticated Users", [self usersToString:userAccountManager.allUserAccounts],
@"User Key-Value Stores", [self safeJoin:[SFSDKKeyValueEncryptedFileStore allStoreNames] separator:@", "],
@"Global Key-Value Stores", [self safeJoin:[SFSDKKeyValueEncryptedFileStore allGlobalStoreNames] separator:@", "]
@"Identity Provider", [self isIdentityProvider] ? @"YES" : @"NO"
]];

[devInfos addObjectsFromArray:[self dictToDevInfos:self.appConfig.configDict keyPrefix:@"BootConfig"]];
// Static bootconfig
[devInfos addObject:@"section:Bootconfig"];
[devInfos addObjectsFromArray:[self dictToDevInfos:self.appConfig.configDict]];

// Current user info
SFUserAccount* currentUser = userAccountManager.currentUser;
if (currentUser) {
SFOAuthCredentials* creds = currentUser.credentials;
[devInfos addObject:@"section:Current User"];
[devInfos addObjectsFromArray:@[
@"Username", [self userToString:currentUser],
@"Consumer Key", creds.clientId ?: @"(nil)",
@"Redirect URI", creds.redirectUri ?: @"(nil)",
@"Scopes", [self scopesToString:currentUser],
@"Instance URL", [creds.instanceUrl absoluteString] ?: @"(nil)",
@"Token format", [creds.tokenFormat isEqualToString:@"jwt"] ? @"jwt" : @"opaque",
@"Access Token Expiration", [self accessTokenExpiration],
@"Beacon Child Consumer Key", creds.beaconChildConsumerKey ?: @"(empty)"
]];
}

// Key Value Stores
NSArray<NSString*>* globalKeyValueStores = [SFSDKKeyValueEncryptedFileStore allGlobalStoreNames];
NSArray<NSString*>* userKeyValueStores = [SFSDKKeyValueEncryptedFileStore allStoreNames];
if ([userKeyValueStores count] > 0 || [globalKeyValueStores count] > 0) {
[devInfos addObject:@"section:Key Value Stores"];
[devInfos addObjectsFromArray:@[@"Global stores", [self safeJoin:globalKeyValueStores separator:@", "]]];
[devInfos addObjectsFromArray:@[@"User stores", [self safeJoin:globalKeyValueStores separator:@", "]]];
}

// Managed prefs
SFManagedPreferences *managedPreferences = [SFManagedPreferences sharedPreferences];
[devInfos addObjectsFromArray:@[@"Managed", [managedPreferences hasManagedPreferences] ? @"YES" : @"NO"]];
if ([managedPreferences hasManagedPreferences]) {
[devInfos addObjectsFromArray:[self dictToDevInfos:managedPreferences.rawPreferences keyPrefix:@"Managed Pref"]];
[devInfos addObject:@"section:Managed Pref"];
[devInfos addObjectsFromArray:@[@"Managed", [managedPreferences hasManagedPreferences] ? @"YES" : @"NO"]];
[devInfos addObjectsFromArray:[self dictToDevInfos:managedPreferences.rawPreferences]];
}

return devInfos;
Expand All @@ -542,21 +620,31 @@ - (NSString *) accessTokenExpiration {
}

- (NSString*) userToString:(SFUserAccount*)user {
return user ? user.idData.username : @"";
return user.idData.username != nil ? user.idData.username : @"";
}

- (NSString*) scopesToString:(SFUserAccount*)user {
return user.credentials.scopes != nil ? [user.credentials.scopes componentsJoinedByString:@" "] : @"";
}

- (NSString*) usersToString:(NSArray<SFUserAccount*>*)userAccounts {
if (userAccounts == nil) {
return @"";
}
NSMutableArray* usernames = [NSMutableArray new];
for (SFUserAccount *userAccount in userAccounts) {
[usernames addObject:[self userToString:userAccount]];
}
return [usernames componentsJoinedByString:@", "];
}

- (NSArray*) dictToDevInfos:(NSDictionary*)dict keyPrefix:(NSString*)keyPrefix {
- (NSArray*) dictToDevInfos:(NSDictionary*)dict {
if (dict == nil) {
return @[];
}
NSMutableArray * devInfos = [NSMutableArray new];
[dict enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
[devInfos addObject:[NSString stringWithFormat:@"%@ - %@", keyPrefix, key]];
[devInfos addObject:key];
[devInfos addObject:[[NSString stringWithFormat:@"%@", obj] stringByReplacingOccurrencesOfString:@"\n" withString:@""]];
}];
return devInfos;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
FlowTypesView.swift
AuthFlowTester
AuthFlowTypesView.swift
SalesforceSDKCore

Copyright (c) 2025-present, salesforce.com, inc. All rights reserved.

Expand All @@ -26,18 +26,19 @@
*/

import SwiftUI
import SalesforceSDKCore

struct FlowTypesView: View {
public struct AuthFlowTypesView: View {
@State private var useWebServerFlow: Bool
@State private var useHybridFlow: Bool
@State private var supportsWelcomeDiscovery: Bool

init() {
public init() {
_useWebServerFlow = State(initialValue: SalesforceManager.shared.useWebServerAuthentication)
_useHybridFlow = State(initialValue: SalesforceManager.shared.useHybridAuthentication)
_supportsWelcomeDiscovery = State(initialValue: SalesforceManager.shared.supportsWelcomeDiscovery)
}

var body: some View {
public var body: some View {
VStack(alignment: .leading, spacing: 12) {
Text("Authentication Flow Types")
.font(.headline)
Expand All @@ -48,7 +49,7 @@ struct FlowTypesView: View {
Text("Use Web Server Flow")
.font(.body)
}
.onChange(of: useWebServerFlow) { newValue in
.onChange(of: useWebServerFlow) { _, newValue in
SalesforceManager.shared.useWebServerAuthentication = newValue
}
.padding(.horizontal)
Expand All @@ -57,10 +58,19 @@ struct FlowTypesView: View {
Text("Use Hybrid Flow")
.font(.body)
}
.onChange(of: useHybridFlow) { newValue in
.onChange(of: useHybridFlow) { _, newValue in
SalesforceManager.shared.useHybridAuthentication = newValue
}
.padding(.horizontal)

Toggle(isOn: $supportsWelcomeDiscovery) {
Text("Support Welcome Discovery")
.font(.body)
}
.onChange(of: supportsWelcomeDiscovery) { _, newValue in
SalesforceManager.shared.supportsWelcomeDiscovery = newValue
}
.padding(.horizontal)
}
}
.padding(.vertical)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
BootConfigEditor.swift
AuthFlowTester
SalesforceSDKCore

Copyright (c) 2025-present, salesforce.com, inc. All rights reserved.

Expand All @@ -26,9 +26,8 @@
*/

import SwiftUI
import SalesforceSDKCore

struct BootConfigEditor: View {
public struct BootConfigEditor: View {
let title: String
let buttonLabel: String
let buttonColor: Color
Expand All @@ -40,7 +39,29 @@ struct BootConfigEditor: View {
let initiallyExpanded: Bool
@State private var isExpanded: Bool = false

var body: some View {
public init(
title: String,
buttonLabel: String,
buttonColor: Color,
consumerKey: Binding<String>,
callbackUrl: Binding<String>,
scopes: Binding<String>,
isLoading: Bool,
onUseConfig: @escaping () -> Void,
initiallyExpanded: Bool
) {
self.title = title
self.buttonLabel = buttonLabel
self.buttonColor = buttonColor
self._consumerKey = consumerKey
self._callbackUrl = callbackUrl
self._scopes = scopes
self.isLoading = isLoading
self.onUseConfig = onUseConfig
self.initiallyExpanded = initiallyExpanded
}

public var body: some View {
VStack(alignment: .leading, spacing: 12) {
Button(action: {
withAnimation {
Expand Down
Loading
Loading