Skip to content

Conversation

JohnsonEricAtSalesforce
Copy link
Contributor

🎸 Ready For Review 🥁

This is the iOS counterpart to forcedotcom/SalesforceMobileSDK-Android#2785.

This adds a new process argument which may be used for debug app builds only to authenticate a user via test credentials in automated tests. The new code is wrapped in a #ifdef DEBUG preprocessor condition so that it will not be present for release builds.

This new means of automating authentication for debug testing does not expose production builds to risk and also minimizes access since usernames and passwords are not used. The test credentials JSON provided by Salesforce Mobile SDK's REST Explorer sample app uses refresh tokens which can be remotely expired and cannot be used for privilege elevation beyond the public APIs.

Here's an example of testing the new parameter interactively in Xcode by adding it to the app's scheme:
Screenshot 2025-10-15 at 18 56 39

Here's an example for XCUITest:

super.launchArguments = ["creds: \(STRING_PACKAGE)"]
super.launch()

This is an Appium example:

capabilities = {
     "platformName": "iOS",
     "deviceName": "iPhone 17 Pro Max",
     "platformVersion": "26.0",
     "app": "/path/to/your/app.app",
     "automationName": "XCUITest",
     "processArguments": {
       "args": ["creds", "<STRING PACKAGE>"]
     }
   }

And, finally, an example at the shell:

xcrun simctl launch booted <YOUR APP> -creds "<STRING PACKAGE>"

if ([arguments containsObject:@"-creds"]) {
NSString *creds = arguments[[arguments indexOfObject:@"-creds"] + 1];

[TestSetupUtils populateAuthCredentialsFromString:creds];
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The crux is retrieving the new parameter and passing it to mostly existing test harness logic. @bbirman, is this a logical place to accomplish this? I tried few other ideas, but ran into issues and circular dependencies on the manager singleton.

Copy link
Member

Choose a reason for hiding this comment

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

Is SFUserAccountManager a possibility? If not, this class sounds good to me, though wondering about using load, does it have something to do with the runtime?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I looked through SFUserAccountManager just now. It doesn't seem to have any logical place to add this. I tried a couple, but they also trigger some circular initialization overflows a lot like some of the entry points in the manager I tried. TestSetupUtils is dependent on the SDK and user account manager objects.

load is nice in this instance since it's the class level initializer for SalesforceSDKManager and is outside the other initialization methods. You're correct that it's part of the Objective-C spec. I recall there is a way to do something like that in Swift, but it has been a few years since I exercised that.

@return The configuration data used to configure SFUserAccountManager (useful e.g. for hybrid
apps which need the data to bootstrap SFHybridViewController).
*/
+ (SFSDKTestCredentialsData *)populateAuthCredentialsFromString:(NSString *)testCredentialsJsonString;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This new method lets us reuse the non-file logic around ingesting the JSON.

return credsData;
}

+ (void)synchronousAuthRefresh
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This method didn't change, though Github marked it as a diff due to other logic moving.

authListener.returnStatus);
}

+ (SFOAuthCredentials *)newClientCredentials {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This method did not change. This method didn't change, though Github marked it as a diff due to other logic moving.

return [TestSetupUtils authCredentialsFromJson:[SFJsonUtils objectFromJSONString:testCredentialsJsonString]];
}

+ (void)synchronousAuthRefresh
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This method didn't change, though Github marked it as a diff due to other logic moving.

authListener.returnStatus);
}

+ (SFOAuthCredentials *)newClientCredentials {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This method didn't change, though Github marked it as a diff due to other logic moving.

Copy link

1 Warning
⚠️ Static Analysis found an issue with one or more files you modified. Please fix the issue(s).

Clang Static Analysis Issues

File Type Category Description Line Col
SalesforceSDKManager Nil value used as mutex for @synchronized() (no synchronization will occur) Logic error Nil value used as mutex for @synchronized() (no synchronization will occur) 144 5
SalesforceSDKManager Nil value used as mutex for @synchronized() (no synchronization will occur) Logic error Nil value used as mutex for @synchronized() (no synchronization will occur) 156 5

Generated by 🚫 Danger

Copy link

codecov bot commented Oct 16, 2025

Codecov Report

❌ Patch coverage is 57.14286% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 63.00%. Comparing base (f71eda5) to head (b780b88).

Files with missing lines Patch % Lines
...forceSDKCore/Classes/Common/SalesforceSDKManager.m 57.14% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##              dev    #3944      +/-   ##
==========================================
- Coverage   63.04%   63.00%   -0.04%     
==========================================
  Files         249      249              
  Lines       22455    22462       +7     
==========================================
- Hits        14156    14152       -4     
- Misses       8299     8310      +11     
Components Coverage Δ
Analytics 70.78% <ø> (ø)
Common 71.09% <ø> (ø)
Core 53.11% <57.14%> (-0.06%) ⬇️
SmartStore 73.66% <ø> (ø)
MobileSync 87.66% <ø> (ø)
Files with missing lines Coverage Δ
...forceSDKCore/Classes/Common/SalesforceSDKManager.m 57.70% <57.14%> (-0.01%) ⬇️

... and 2 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

if ([arguments containsObject:@"-creds"]) {
NSString *creds = arguments[[arguments indexOfObject:@"-creds"] + 1];

[TestSetupUtils populateAuthCredentialsFromString:creds initializeSdk:NO];
Copy link
Contributor Author

Choose a reason for hiding this comment

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

@bbirman - Now we have Local Dev Instant Login hosted in SalesforceSDKManager.init so we can avoid using load. Notice the two new boolean parameters on these two statements which allow TestSetupUtils to execute correctly here in initializeSDK while still running exactly the same in existing tests.

Copy link
Member

Choose a reason for hiding this comment

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

nice!

if (postUserDidLogIn) {
NSDictionary *userInfo = @{kSFNotificationUserInfoAccountKey: userAccount,
kSFNotificationUserInfoAuthTypeKey: authInfo};
[[NSNotificationCenter defaultCenter] postNotificationName:kSFNotificationUserDidLogIn
Copy link
Contributor Author

Choose a reason for hiding this comment

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

These two lines allow TestSetupUtils to stop the default login from being shown and deliver the test app directly to its authenticated view.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That matches the behavior for Local Dev Instant Login for Android.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

There is similar notification login in the user account manager, but its only a few lines and not visible to this scope, so I thought to leave that alone and replicate the notification here so we minimize authentication regression risk. All the changes in this pull request are non-production code so far.

@JohnsonEricAtSalesforce
Copy link
Contributor Author

@bbirman - After really researching exactly how TestSetupUtils compares with the user's interactive authentication experience in SFUserAccountManager, I was able to make a few tweaks to the non-production logic in TestSetupUtils to make it work perfectly. It's nice that this test harness update doesn't touch any of the production authentication logic 👍🏻

@JohnsonEricAtSalesforce
Copy link
Contributor Author

I ran this using the RestAPIExplorer sample and giving it the new parameter shown in the screenshot above. After a fresh install, it brought my test user directly to the app's authenticated view in all my test runs ⭐️

@JohnsonEricAtSalesforce
Copy link
Contributor Author

Local tests are passing as well ✅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants