diff --git a/SalesforceSDKCore.podspec b/SalesforceSDKCore.podspec index f13da7ec2e..543420c963 100644 --- a/SalesforceSDKCore.podspec +++ b/SalesforceSDKCore.podspec @@ -25,7 +25,7 @@ Pod::Spec.new do |s| sdkcore.resources = ['shared/resources/SalesforceSDKAssets.xcassets'] sdkcore.source_files = 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/**/*.{h,m,swift}', 'libs/SalesforceSDKCore/SalesforceSDKCore/SalesforceSDKCore.h' # public_header_files is automatically populated by update_podspec_headers.sh which is run when building SalesforceSDKCore - sdkcore.public_header_files = 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Analytics/SFSDKAILTNPublisher.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Analytics/SFSDKAnalyticsPublisher.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Analytics/SFSDKEventBuilderHelper.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Analytics/SFSDKSalesforceAnalyticsManager.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Common/NSData+SFAdditions.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Common/NSData+SFSDKUtils.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Common/NSDictionary+SFAdditions.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Common/NSString+SFAdditions.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Common/NSURL+SFAdditions.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Common/NSURLResponse+SFAdditions.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Common/SFFormatUtils.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Common/SFSDKAppConfig.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Common/SFSDKAppFeatureMarkers.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Common/SalesforceSDKConstants.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Common/SalesforceSDKManager.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Common/UIDevice+SFHardware.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Common/UIScreen+SFAdditions.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/IDP/SFSDKLoginFlowSelectionView.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/IDP/SFSDKUITableViewCell.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/IDP/SFSDKUserSelectionNavViewController.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/IDP/SFSDKUserSelectionTableViewController.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/IDP/SFSDKUserSelectionView.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Identity/SFIdentityCoordinator.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Identity/SFIdentityData.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Instrumentation/SFInstrumentation.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Instrumentation/SFMethodInterceptor.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Instrumentation/SFSDKInstrumentationHelper.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Login/LoginHost/SFSDKLoginHost.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Login/LoginHost/SFSDKLoginHostDelegate.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Login/LoginHost/SFSDKLoginHostListViewController.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Login/LoginHost/SFSDKLoginHostStorage.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Login/SFLoginViewController.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Login/SFSDKLoginViewControllerConfig.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/OAuth/SFOAuthCoordinator.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/OAuth/SFOAuthCredentials.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/OAuth/SFOAuthInfo.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/OAuth/SFOAuthOrgAuthConfiguration.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/OAuth/SFOAuthSessionRefresher.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/PushNotification/SFSDKPushNotificationDecryption.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/PushNotification/SFSDKPushNotificationError.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/PushNotification/SFSDKPushNotificationFieldsConstants.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/RestAPI/SFNetwork.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/RestAPI/SFRestAPI+Blocks.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/RestAPI/SFRestAPI+Files.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/RestAPI/SFRestAPI+Notifications.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/RestAPI/SFRestAPI+QueryBuilder.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/RestAPI/SFRestAPI.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/RestAPI/SFRestRequest.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/RestAPI/SFSDKBatchRequest.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/RestAPI/SFSDKBatchResponse.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/RestAPI/SFSDKCollectionResponse.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/RestAPI/SFSDKCompositeRequest.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/RestAPI/SFSDKCompositeResponse.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/RestAPI/SFSDKPrimingRecordsResponse.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/RestAPI/SFSObjectTree.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Security/SFSDKCryptoUtils.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Test/SFSDKAsyncProcessListener.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Test/SFSDKTestCredentialsData.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Test/SFSDKTestRequestListener.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Test/TestSetupUtils.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/UserAccount/SFAuthErrorHandlerList.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/UserAccount/SFUserAccount.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/UserAccount/SFUserAccountConstants.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/UserAccount/SFUserAccountIdentity.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/UserAccount/SFUserAccountManager.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/UserAccount/ViewControllers/SFDefaultUserManagementDetailViewController.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/UserAccount/ViewControllers/SFDefaultUserManagementListViewController.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/UserAccount/ViewControllers/SFDefaultUserManagementViewController.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Util/NSURL+SFStringUtils.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Util/SFApplicationHelper.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Util/SFDirectoryManager.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Util/SFManagedPreferences.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Util/SFPreferences.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Util/SFSDKAuthConfigUtil.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Util/SFSDKAuthHelper.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Util/SFSDKCoreLogger.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Util/SFSDKOAuth2.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Util/SFSDKOAuthConstants.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Util/SFSDKResourceUtils.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Util/SFSDKSoqlBuilder.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Util/SFSDKSoslBuilder.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Util/SFSDKSoslReturningBuilder.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Util/SFSDKViewControllerConfig.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Util/SFSDKWebUtils.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Util/SalesforceSDKCoreDefines.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Util/UIColor+SFColors.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Views/SFSDKAlertMessage.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Views/SFSDKAlertMessageBuilder.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Views/SFSDKDevInfoViewController.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Views/SFSDKNavigationController.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Views/SFSDKViewController.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Views/SFSDKWindowContainer.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Views/SFSDKWindowManager.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/SalesforceSDKCore.h' + sdkcore.public_header_files = 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Analytics/SFSDKAILTNPublisher.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Analytics/SFSDKAnalyticsPublisher.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Analytics/SFSDKEventBuilderHelper.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Analytics/SFSDKSalesforceAnalyticsManager.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Common/NSData+SFAdditions.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Common/NSData+SFSDKUtils.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Common/NSDictionary+SFAdditions.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Common/NSString+SFAdditions.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Common/NSURL+SFAdditions.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Common/NSURLResponse+SFAdditions.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Common/SFFormatUtils.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Common/SFSDKAppConfig.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Common/SFSDKAppFeatureMarkers.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Common/SalesforceSDKConstants.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Common/SalesforceSDKManager.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Common/UIDevice+SFHardware.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Common/UIScreen+SFAdditions.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/IDP/SFSDKLoginFlowSelectionView.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/IDP/SFSDKUITableViewCell.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/IDP/SFSDKUserSelectionNavViewController.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/IDP/SFSDKUserSelectionTableViewController.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/IDP/SFSDKUserSelectionView.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Identity/SFIdentityCoordinator.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Identity/SFIdentityData.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Instrumentation/SFInstrumentation.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Instrumentation/SFMethodInterceptor.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Instrumentation/SFSDKInstrumentationHelper.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Login/LoginHost/SFSDKLoginHost.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Login/LoginHost/SFSDKLoginHostDelegate.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Login/LoginHost/SFSDKLoginHostListViewController.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Login/LoginHost/SFSDKLoginHostStorage.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Login/SFLoginViewController.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Login/SFSDKLoginViewControllerConfig.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/OAuth/SFOAuthCoordinator.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/OAuth/SFOAuthCredentials.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/OAuth/SFOAuthInfo.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/OAuth/SFOAuthOrgAuthConfiguration.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/OAuth/SFOAuthSessionRefresher.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/PushNotification/SFSDKPushNotificationDecryption.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/PushNotification/SFSDKPushNotificationError.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/PushNotification/SFSDKPushNotificationFieldsConstants.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/RestAPI/SFNetwork.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/RestAPI/SFRestAPI+Blocks.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/RestAPI/SFRestAPI+Files.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/RestAPI/SFRestAPI+Notifications.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/RestAPI/SFRestAPI+QueryBuilder.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/RestAPI/SFRestAPI.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/RestAPI/SFRestRequest.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/RestAPI/SFSDKBatchRequest.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/RestAPI/SFSDKBatchResponse.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/RestAPI/SFSDKCollectionResponse.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/RestAPI/SFSDKCompositeRequest.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/RestAPI/SFSDKCompositeResponse.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/RestAPI/SFSDKPrimingRecordsResponse.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/RestAPI/SFSObjectTree.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Security/SFSDKCryptoUtils.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Test/SFSDKAsyncProcessListener.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Test/SFSDKTestCredentialsData.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Test/SFSDKTestRequestListener.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Test/TestSetupUtils.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/UserAccount/SFAuthErrorHandlerList.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/UserAccount/SFUserAccount.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/UserAccount/SFUserAccountConstants.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/UserAccount/SFUserAccountIdentity.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/UserAccount/SFUserAccountManager.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/UserAccount/ViewControllers/SFDefaultUserManagementDetailViewController.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/UserAccount/ViewControllers/SFDefaultUserManagementListViewController.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/UserAccount/ViewControllers/SFDefaultUserManagementViewController.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Util/NSURL+SFStringUtils.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Util/SFApplicationHelper.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Util/SFDirectoryManager.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Util/SFManagedPreferences.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Util/SFPreferences.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Util/SFSDKAuthConfigUtil.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Util/SFSDKAuthHelper.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Util/SFSDKCoreLogger.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Util/SFSDKOAuth2.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Util/SFSDKOAuthConstants.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Util/SFSDKResourceUtils.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Util/SFSDKSoqlBuilder.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Util/SFSDKSoslBuilder.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Util/SFSDKSoslReturningBuilder.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Util/SFSDKViewControllerConfig.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Util/SFSDKWebUtils.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Util/SalesforceSDKCoreDefines.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Util/UIColor+SFColors.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Views/SFSDKAlertMessage.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Views/SFSDKAlertMessageBuilder.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Views/SFSDKNavigationController.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Views/SFSDKViewController.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Views/SFSDKWindowContainer.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Views/SFSDKWindowManager.h', 'libs/SalesforceSDKCore/SalesforceSDKCore/SalesforceSDKCore.h' sdkcore.requires_arc = true sdkcore.prefix_header_contents = '#import "SFSDKCoreLogger.h"', '#import "SalesforceSDKConstants.h"' diff --git a/libs/MobileSync/Configuration/MobileSync-Common.xcconfig b/libs/MobileSync/Configuration/MobileSync-Common.xcconfig index 68f62acc97..3891149e1c 100644 --- a/libs/MobileSync/Configuration/MobileSync-Common.xcconfig +++ b/libs/MobileSync/Configuration/MobileSync-Common.xcconfig @@ -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 diff --git a/libs/MobileSync/MobileSync.xcodeproj/project.pbxproj b/libs/MobileSync/MobileSync.xcodeproj/project.pbxproj index 4acdf57fa6..712e78d37c 100644 --- a/libs/MobileSync/MobileSync.xcodeproj/project.pbxproj +++ b/libs/MobileSync/MobileSync.xcodeproj/project.pbxproj @@ -1610,6 +1610,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); + LIBRARY_SEARCH_PATHS = "$(inherited)"; OTHER_LDFLAGS = ( "$(inherited)", "-framework", @@ -1640,6 +1641,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); + LIBRARY_SEARCH_PATHS = "$(inherited)"; OTHER_LDFLAGS = ( "$(inherited)", "-framework", diff --git a/libs/SalesforceSDKCore/SalesforceSDKCore.xcodeproj/project.pbxproj b/libs/SalesforceSDKCore/SalesforceSDKCore.xcodeproj/project.pbxproj index 83ae6460d1..34348a5c3e 100644 --- a/libs/SalesforceSDKCore/SalesforceSDKCore.xcodeproj/project.pbxproj +++ b/libs/SalesforceSDKCore/SalesforceSDKCore.xcodeproj/project.pbxproj @@ -42,8 +42,6 @@ 23A4C7462D0CAFA000DF55EB /* ScreenLockManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23A4C7452D0CAFA000DF55EB /* ScreenLockManagerTests.swift */; }; 23A4C7492D0CAFCF00DF55EB /* NativeLoginManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23A4C7482D0CAFCF00DF55EB /* NativeLoginManagerTests.swift */; }; 23A4C74A2D0CAFCF00DF55EB /* JwtAccessTokenTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23A4C7472D0CAFCF00DF55EB /* JwtAccessTokenTests.swift */; }; - 23A4C74E2D0CB68B00DF55EB /* NativeLoginManagerInternal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23A4C74D2D0CB68B00DF55EB /* NativeLoginManagerInternal.swift */; }; - 23A4C74F2D0CB68B00DF55EB /* NativeLoginManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23A4C74C2D0CB68B00DF55EB /* NativeLoginManager.swift */; }; 23B2FDEC2DB03496006ADC07 /* NotificationCategoryFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23B2FDE62DB03486006ADC07 /* NotificationCategoryFactory.swift */; }; 23CAB0F92DCBE51F00B8929B /* MockRestClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23CAB0F72DCBE51F00B8929B /* MockRestClient.swift */; }; 23CAB1312DD515B500B8929B /* SFLoginViewController+Deep-Linking.swift in Sources */ = {isa = PBXBuildFile; fileRef = 23CAB1302DD515B500B8929B /* SFLoginViewController+Deep-Linking.swift */; }; @@ -61,7 +59,6 @@ 4F06AF731C49A16A00F70798 /* NSURL+SFStringUtilsTests.h in Headers */ = {isa = PBXBuildFile; fileRef = 4F06AF5D1C49A16A00F70798 /* NSURL+SFStringUtilsTests.h */; }; 4F06AF751C49A16A00F70798 /* SalesforceOAuthUnitTests.h in Headers */ = {isa = PBXBuildFile; fileRef = 4F06AF5F1C49A16A00F70798 /* SalesforceOAuthUnitTests.h */; }; 4F06AF771C49A16A00F70798 /* SalesforceOAuthUnitTestsCoordinatorDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 4F06AF611C49A16A00F70798 /* SalesforceOAuthUnitTestsCoordinatorDelegate.h */; }; - 4F06AF7A1C49A16A00F70798 /* SalesforceSDKIdentityTests.h in Headers */ = {isa = PBXBuildFile; fileRef = 4F06AF641C49A16A00F70798 /* SalesforceSDKIdentityTests.h */; }; 4F06AF811C49A16A00F70798 /* SFOAuthTestFlowCoordinatorDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 4F06AF6B1C49A16A00F70798 /* SFOAuthTestFlowCoordinatorDelegate.h */; }; 4F06AF861C49A16A00F70798 /* SFTestSDKManagerFlow.h in Headers */ = {isa = PBXBuildFile; fileRef = 4F06AF701C49A16A00F70798 /* SFTestSDKManagerFlow.h */; }; 4F06AF891C49A18E00F70798 /* NSURL+SFStringUtilsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4F06AF5E1C49A16A00F70798 /* NSURL+SFStringUtilsTests.m */; }; @@ -98,6 +95,24 @@ 4F8A3B012CEC202F00ECDC76 /* JwtAccessToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F8A3B002CEC202F00ECDC76 /* JwtAccessToken.swift */; }; 4F9E05322DD6A08000548985 /* SFSDKOAuthTokenEndpointResponseTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4F9E052C2DD6A06F00548985 /* SFSDKOAuthTokenEndpointResponseTests.m */; }; 4F9E05342DD7BE1500548985 /* SFOAuthCredentialsTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4F9E05332DD7BE0A00548985 /* SFOAuthCredentialsTests.m */; }; + 4FAUTHFLOW001234567890ABC /* AuthFlowTypesViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FAUTHFLOW112345678901ABC /* AuthFlowTypesViewTests.swift */; }; + 4FBOOTCP001234567890ABCD /* BootConfigPickerViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FBOOTCP112345678901ABCD /* BootConfigPickerViewControllerTests.swift */; }; + 4FDEVINFO001234567890ABCD /* DevInfoViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FDEVINFO112345678901ABCD /* DevInfoViewControllerTests.swift */; }; + 4FE006B12EBEB65900CFD66F /* AuthFlowTypesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FE006B02EBEB65900CFD66F /* AuthFlowTypesView.swift */; }; + 4FE006B22EBEB65900CFD66F /* BootConfigEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FE006AE2EBEB65900CFD66F /* BootConfigEditor.swift */; }; + 4FE006B32EBEB65900CFD66F /* BootConfigPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FE006AF2EBEB65900CFD66F /* BootConfigPickerViewController.swift */; }; + 4FE006D42EBEBBF300CFD66F /* SFSDKLoginHostListViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 4FE006CE2EBEBBF300CFD66F /* SFSDKLoginHostListViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4FE006D52EBEBBF300CFD66F /* SFSDKTextFieldTableViewCell.h in Headers */ = {isa = PBXBuildFile; fileRef = 4FE006D22EBEBBF300CFD66F /* SFSDKTextFieldTableViewCell.h */; }; + 4FE006D62EBEBBF300CFD66F /* SFSDKLoginHostStorage.h in Headers */ = {isa = PBXBuildFile; fileRef = 4FE006D02EBEBBF300CFD66F /* SFSDKLoginHostStorage.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4FE006D82EBEBBF300CFD66F /* SFSDKLoginHostDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = 4FE006CD2EBEBBF300CFD66F /* SFSDKLoginHostDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 4FE006D92EBEBBF300CFD66F /* SFSDKTextFieldTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 4FE006D32EBEBBF300CFD66F /* SFSDKTextFieldTableViewCell.m */; }; + 4FE006DA2EBEBBF300CFD66F /* NewLoginHostView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FE006CA2EBEBBF300CFD66F /* NewLoginHostView.swift */; }; + 4FE006DB2EBEBBF300CFD66F /* SFSDKLoginHost.m in Sources */ = {isa = PBXBuildFile; fileRef = 4FE006CC2EBEBBF300CFD66F /* SFSDKLoginHost.m */; }; + 4FE006DC2EBEBBF300CFD66F /* SFSDKLoginHostListViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4FE006CF2EBEBBF300CFD66F /* SFSDKLoginHostListViewController.m */; }; + 4FE006DD2EBEBBF300CFD66F /* SFSDKLoginHostStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = 4FE006D12EBEBBF300CFD66F /* SFSDKLoginHostStorage.m */; }; + 4FE006E02EBEBC0000CFD66F /* NativeLoginManagerInternal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FE006DF2EBEBC0000CFD66F /* NativeLoginManagerInternal.swift */; }; + 4FE006E12EBEBC0000CFD66F /* NativeLoginManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FE006DE2EBEBC0000CFD66F /* NativeLoginManager.swift */; }; + 4FE0070F2EBED6C000CFD66F /* SFSDKLoginHost.h in Headers */ = {isa = PBXBuildFile; fileRef = 4FE006CB2EBEBBF300CFD66F /* SFSDKLoginHost.h */; settings = {ATTRIBUTES = (Public, ); }; }; 4FE5332A1BFFE70600814D2A /* Main_iPad.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4F7EB4981BFFCEF600768720 /* Main_iPad.storyboard */; }; 4FE5332B1BFFE70600814D2A /* Main_iPhone.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4F7EB49A1BFFCEF600768720 /* Main_iPhone.storyboard */; }; 6900B62C24B64DD800500923 /* NSURLResponse+SFAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 6900B62A24B64DD800500923 /* NSURLResponse+SFAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -113,7 +128,6 @@ 6938392723C82F38008E8E9A /* SFSDKNullURLCache.m in Sources */ = {isa = PBXBuildFile; fileRef = 6938392523C82F38008E8E9A /* SFSDKNullURLCache.m */; }; 693E623124A287DB0017B222 /* KeyValueEncryptedFileStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 693E623024A287DB0017B222 /* KeyValueEncryptedFileStore.swift */; }; 693E623B24A29B6B0017B222 /* SFSDKKeyValueEncryptedFileStoreTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 693E623A24A29B6B0017B222 /* SFSDKKeyValueEncryptedFileStoreTests.m */; }; - 6941BE662D2F0E1B00CEC59B /* NewLoginHostView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6941BE652D2F0E1B00CEC59B /* NewLoginHostView.swift */; }; 694490D025B8F4C4007747CD /* SFSDKWindowManager+Internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 694490C725B8E567007747CD /* SFSDKWindowManager+Internal.h */; }; 695E86A829EE24D0002BDEA6 /* SPConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 695E86A729EE24D0002BDEA6 /* SPConfig.swift */; }; 695E86B929EF8D6E002BDEA6 /* SFSDKIDPAuthCodeLoginRequestCommand.h in Headers */ = {isa = PBXBuildFile; fileRef = 695E86B729EF8D6E002BDEA6 /* SFSDKIDPAuthCodeLoginRequestCommand.h */; }; @@ -434,22 +448,12 @@ D3D675E52D39EF01008E468E /* EmbeddingsResponseBody.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3D675D22D39EF01008E468E /* EmbeddingsResponseBody.swift */; }; E1C80CDF1C5AEBFA001B3A21 /* SFLoginViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = E1C80CDD1C5AEBFA001B3A21 /* SFLoginViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; E1C80CE01C5AEBFA001B3A21 /* SFLoginViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E1C80CDE1C5AEBFA001B3A21 /* SFLoginViewController.m */; }; - E1C80CEC1C5AEE31001B3A21 /* SFSDKLoginHost.h in Headers */ = {isa = PBXBuildFile; fileRef = E1C80CE11C5AEE31001B3A21 /* SFSDKLoginHost.h */; settings = {ATTRIBUTES = (Public, ); }; }; - E1C80CED1C5AEE31001B3A21 /* SFSDKLoginHost.m in Sources */ = {isa = PBXBuildFile; fileRef = E1C80CE21C5AEE31001B3A21 /* SFSDKLoginHost.m */; }; - E1C80CEE1C5AEE31001B3A21 /* SFSDKLoginHostDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = E1C80CE31C5AEE31001B3A21 /* SFSDKLoginHostDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; }; - E1C80CEF1C5AEE31001B3A21 /* SFSDKLoginHostListViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = E1C80CE41C5AEE31001B3A21 /* SFSDKLoginHostListViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; - E1C80CF01C5AEE31001B3A21 /* SFSDKLoginHostListViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E1C80CE51C5AEE31001B3A21 /* SFSDKLoginHostListViewController.m */; }; - E1C80CF11C5AEE31001B3A21 /* SFSDKLoginHostStorage.h in Headers */ = {isa = PBXBuildFile; fileRef = E1C80CE61C5AEE31001B3A21 /* SFSDKLoginHostStorage.h */; settings = {ATTRIBUTES = (Public, ); }; }; - E1C80CF21C5AEE31001B3A21 /* SFSDKLoginHostStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = E1C80CE71C5AEE31001B3A21 /* SFSDKLoginHostStorage.m */; }; - E1C80CF51C5AEE31001B3A21 /* SFSDKTextFieldTableViewCell.h in Headers */ = {isa = PBXBuildFile; fileRef = E1C80CEA1C5AEE31001B3A21 /* SFSDKTextFieldTableViewCell.h */; }; - E1C80CF61C5AEE31001B3A21 /* SFSDKTextFieldTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = E1C80CEB1C5AEE31001B3A21 /* SFSDKTextFieldTableViewCell.m */; }; E1C80D081C5AF029001B3A21 /* SalesforceSDKAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E1C80D071C5AF029001B3A21 /* SalesforceSDKAssets.xcassets */; }; E1DDC0FE1CAA2A8B002F51DD /* UIColor+SFColors.m in Sources */ = {isa = PBXBuildFile; fileRef = E1DDC0FD1CAA2A8B002F51DD /* UIColor+SFColors.m */; }; E1DDC1441CAEEB34002F51DD /* SFSDKLoginHostTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E1DDC1431CAEEB34002F51DD /* SFSDKLoginHostTests.m */; }; FDCEC95C5AD1CFAD130DDCCF /* SFSObjectTree.h in Headers */ = {isa = PBXBuildFile; fileRef = FDCEC40C4B860474C90D02AF /* SFSObjectTree.h */; settings = {ATTRIBUTES = (Public, ); }; }; FDCECBC89437CBD2378AC8CC /* SFSObjectTree.m in Sources */ = {isa = PBXBuildFile; fileRef = FDCECBE6688B51C1B9BDC524 /* SFSObjectTree.m */; }; - FDCECC6D14B002687225A4E6 /* SFSDKDevInfoViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = FDCEC75DBDF00FBA8810BA72 /* SFSDKDevInfoViewController.m */; }; - FDCECE47E9586450F036FD3B /* SFSDKDevInfoViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = FDCEC2F8558C9BA9D9684E4A /* SFSDKDevInfoViewController.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FDCECF001234567890ABCDEF /* DevInfoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDCECF112345678901ABCDEF /* DevInfoViewController.swift */; }; FDD7D7A1232039D000F5FB2D /* SFUserAccountPhotoTests.m in Sources */ = {isa = PBXBuildFile; fileRef = FDD7D7A0232039D000F5FB2D /* SFUserAccountPhotoTests.m */; }; FDED97271CAA16EB009D80F2 /* SFApplicationHelper.h in Headers */ = {isa = PBXBuildFile; fileRef = FDED971D1CAA16EB009D80F2 /* SFApplicationHelper.h */; settings = {ATTRIBUTES = (Public, ); }; }; FDED97291CAA16EB009D80F2 /* SFApplicationHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = FDED971E1CAA16EB009D80F2 /* SFApplicationHelper.m */; }; @@ -566,11 +570,9 @@ 23A4C7452D0CAFA000DF55EB /* ScreenLockManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ScreenLockManagerTests.swift; path = SalesforceSDKCoreTests/ScreenLockManagerTests.swift; sourceTree = SOURCE_ROOT; }; 23A4C7472D0CAFCF00DF55EB /* JwtAccessTokenTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = JwtAccessTokenTests.swift; path = SalesforceSDKCoreTests/JwtAccessTokenTests.swift; sourceTree = SOURCE_ROOT; }; 23A4C7482D0CAFCF00DF55EB /* NativeLoginManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = NativeLoginManagerTests.swift; path = SalesforceSDKCoreTests/NativeLoginManagerTests.swift; sourceTree = SOURCE_ROOT; }; - 23A4C74C2D0CB68B00DF55EB /* NativeLoginManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = NativeLoginManager.swift; path = Login/NativeLoginManager.swift; sourceTree = ""; }; - 23A4C74D2D0CB68B00DF55EB /* NativeLoginManagerInternal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = NativeLoginManagerInternal.swift; path = Login/NativeLoginManagerInternal.swift; sourceTree = ""; }; 23B2FDE62DB03486006ADC07 /* NotificationCategoryFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationCategoryFactory.swift; sourceTree = ""; }; 23CAB0F72DCBE51F00B8929B /* MockRestClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockRestClient.swift; sourceTree = ""; }; - 23CAB1302DD515B500B8929B /* SFLoginViewController+Deep-Linking.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "SFLoginViewController+Deep-Linking.swift"; path = "Login/SFLoginViewController+Deep-Linking.swift"; sourceTree = ""; }; + 23CAB1302DD515B500B8929B /* SFLoginViewController+Deep-Linking.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SFLoginViewController+Deep-Linking.swift"; sourceTree = ""; }; 23CE44202DEE257900ADC770 /* RestClient+WebSocket.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "RestClient+WebSocket.swift"; sourceTree = ""; }; 23D626982DF9DF1E00B898D0 /* URLSessionWebSocketTask+WebSocketClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLSessionWebSocketTask+WebSocketClient.swift"; sourceTree = ""; }; 23D96B6E2E145AC20004B06A /* DomainDiscoveryCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DomainDiscoveryCoordinator.swift; sourceTree = ""; }; @@ -588,7 +590,6 @@ 4F06AF601C49A16A00F70798 /* SalesforceOAuthUnitTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SalesforceOAuthUnitTests.m; path = SalesforceSDKCoreTests/SalesforceOAuthUnitTests.m; sourceTree = SOURCE_ROOT; }; 4F06AF611C49A16A00F70798 /* SalesforceOAuthUnitTestsCoordinatorDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SalesforceOAuthUnitTestsCoordinatorDelegate.h; path = SalesforceSDKCoreTests/SalesforceOAuthUnitTestsCoordinatorDelegate.h; sourceTree = SOURCE_ROOT; }; 4F06AF621C49A16A00F70798 /* SalesforceOAuthUnitTestsCoordinatorDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SalesforceOAuthUnitTestsCoordinatorDelegate.m; path = SalesforceSDKCoreTests/SalesforceOAuthUnitTestsCoordinatorDelegate.m; sourceTree = SOURCE_ROOT; }; - 4F06AF641C49A16A00F70798 /* SalesforceSDKIdentityTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SalesforceSDKIdentityTests.h; path = SalesforceSDKCoreTests/SalesforceSDKIdentityTests.h; sourceTree = SOURCE_ROOT; }; 4F06AF651C49A16A00F70798 /* SalesforceSDKIdentityTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SalesforceSDKIdentityTests.m; path = SalesforceSDKCoreTests/SalesforceSDKIdentityTests.m; sourceTree = SOURCE_ROOT; }; 4F06AF661C49A16A00F70798 /* SalesforceSDKManagerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SalesforceSDKManagerTests.m; path = SalesforceSDKCoreTests/SalesforceSDKManagerTests.m; sourceTree = SOURCE_ROOT; }; 4F06AF681C49A16A00F70798 /* SFOAuthSessionRefresherTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SFOAuthSessionRefresherTests.m; path = SalesforceSDKCoreTests/SFOAuthSessionRefresherTests.m; sourceTree = SOURCE_ROOT; }; @@ -706,6 +707,24 @@ 4F96FD491BFD32140022F021 /* SFSDKWebUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SFSDKWebUtils.m; sourceTree = ""; }; 4F9E052C2DD6A06F00548985 /* SFSDKOAuthTokenEndpointResponseTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SFSDKOAuthTokenEndpointResponseTests.m; path = ../SalesforceSDKCoreTests/SFSDKOAuthTokenEndpointResponseTests.m; sourceTree = ""; }; 4F9E05332DD7BE0A00548985 /* SFOAuthCredentialsTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SFOAuthCredentialsTests.m; path = ../SalesforceSDKCoreTests/SFOAuthCredentialsTests.m; sourceTree = ""; }; + 4FAUTHFLOW112345678901ABC /* AuthFlowTypesViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AuthFlowTypesViewTests.swift; path = SalesforceSDKCoreTests/AuthFlowTypesViewTests.swift; sourceTree = SOURCE_ROOT; }; + 4FBOOTCP112345678901ABCD /* BootConfigPickerViewControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = BootConfigPickerViewControllerTests.swift; path = SalesforceSDKCoreTests/BootConfigPickerViewControllerTests.swift; sourceTree = SOURCE_ROOT; }; + 4FDEVINFO112345678901ABCD /* DevInfoViewControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = DevInfoViewControllerTests.swift; path = SalesforceSDKCoreTests/DevInfoViewControllerTests.swift; sourceTree = SOURCE_ROOT; }; + 4FE006AE2EBEB65900CFD66F /* BootConfigEditor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = BootConfigEditor.swift; path = SalesforceSDKCore/Classes/Login/DevConfig/BootConfigEditor.swift; sourceTree = SOURCE_ROOT; }; + 4FE006AF2EBEB65900CFD66F /* BootConfigPickerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = BootConfigPickerViewController.swift; path = SalesforceSDKCore/Classes/Login/DevConfig/BootConfigPickerViewController.swift; sourceTree = SOURCE_ROOT; }; + 4FE006B02EBEB65900CFD66F /* AuthFlowTypesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AuthFlowTypesView.swift; path = SalesforceSDKCore/Classes/Login/DevConfig/AuthFlowTypesView.swift; sourceTree = SOURCE_ROOT; }; + 4FE006CA2EBEBBF300CFD66F /* NewLoginHostView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewLoginHostView.swift; sourceTree = ""; }; + 4FE006CB2EBEBBF300CFD66F /* SFSDKLoginHost.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SFSDKLoginHost.h; sourceTree = ""; }; + 4FE006CC2EBEBBF300CFD66F /* SFSDKLoginHost.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SFSDKLoginHost.m; sourceTree = ""; }; + 4FE006CD2EBEBBF300CFD66F /* SFSDKLoginHostDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SFSDKLoginHostDelegate.h; sourceTree = ""; }; + 4FE006CE2EBEBBF300CFD66F /* SFSDKLoginHostListViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SFSDKLoginHostListViewController.h; sourceTree = ""; }; + 4FE006CF2EBEBBF300CFD66F /* SFSDKLoginHostListViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SFSDKLoginHostListViewController.m; sourceTree = ""; }; + 4FE006D02EBEBBF300CFD66F /* SFSDKLoginHostStorage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SFSDKLoginHostStorage.h; sourceTree = ""; }; + 4FE006D12EBEBBF300CFD66F /* SFSDKLoginHostStorage.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SFSDKLoginHostStorage.m; sourceTree = ""; }; + 4FE006D22EBEBBF300CFD66F /* SFSDKTextFieldTableViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SFSDKTextFieldTableViewCell.h; sourceTree = ""; }; + 4FE006D32EBEBBF300CFD66F /* SFSDKTextFieldTableViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SFSDKTextFieldTableViewCell.m; sourceTree = ""; }; + 4FE006DE2EBEBC0000CFD66F /* NativeLoginManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeLoginManager.swift; sourceTree = ""; }; + 4FE006DF2EBEBC0000CFD66F /* NativeLoginManagerInternal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeLoginManagerInternal.swift; sourceTree = ""; }; 4FEE438A1BFD488900F09C43 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; 4FF945BD1BFFF47D005368C5 /* NSData+SFAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData+SFAdditions.h"; sourceTree = ""; }; 4FF945BE1BFFF47D005368C5 /* NSData+SFAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSData+SFAdditions.m"; sourceTree = ""; }; @@ -735,7 +754,6 @@ 6938392523C82F38008E8E9A /* SFSDKNullURLCache.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SFSDKNullURLCache.m; sourceTree = ""; }; 693E623024A287DB0017B222 /* KeyValueEncryptedFileStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyValueEncryptedFileStore.swift; sourceTree = ""; }; 693E623A24A29B6B0017B222 /* SFSDKKeyValueEncryptedFileStoreTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SFSDKKeyValueEncryptedFileStoreTests.m; path = SalesforceSDKCoreTests/SFSDKKeyValueEncryptedFileStoreTests.m; sourceTree = SOURCE_ROOT; }; - 6941BE652D2F0E1B00CEC59B /* NewLoginHostView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = NewLoginHostView.swift; path = Login/LoginHost/NewLoginHostView.swift; sourceTree = ""; }; 694490C725B8E567007747CD /* SFSDKWindowManager+Internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SFSDKWindowManager+Internal.h"; sourceTree = ""; }; 695E86A729EE24D0002BDEA6 /* SPConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SPConfig.swift; sourceTree = ""; }; 695E86B729EF8D6E002BDEA6 /* SFSDKIDPAuthCodeLoginRequestCommand.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SFSDKIDPAuthCodeLoginRequestCommand.h; sourceTree = ""; }; @@ -765,7 +783,7 @@ 69BDD82D26F90AA400C26D77 /* DecryptStream.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DecryptStream.swift; sourceTree = ""; }; 69BDD82E26F90AA400C26D77 /* EncryptStream.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EncryptStream.swift; sourceTree = ""; }; 69CEBC7D22F368CF00F16218 /* SFNetworkTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SFNetworkTests.m; path = SalesforceSDKCoreTests/SFNetworkTests.m; sourceTree = SOURCE_ROOT; }; - 69D03E252D2C6D0B004381EC /* SFLoginViewController+QrCodeLogin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = "SFLoginViewController+QrCodeLogin.swift"; path = "Login/SFLoginViewController+QrCodeLogin.swift"; sourceTree = ""; }; + 69D03E252D2C6D0B004381EC /* SFLoginViewController+QrCodeLogin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SFLoginViewController+QrCodeLogin.swift"; sourceTree = ""; }; 69DFE0622B968D8F000906E4 /* CryptoUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CryptoUtils.swift; sourceTree = ""; }; 69DFE0672B969C1D000906E4 /* PushNotificationDecryptionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PushNotificationDecryptionTests.swift; path = SalesforceSDKCoreTests/PushNotificationDecryptionTests.swift; sourceTree = SOURCE_ROOT; }; 69DFE0682B969C1D000906E4 /* CryptoUtilsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CryptoUtilsTests.swift; path = SalesforceSDKCoreTests/CryptoUtilsTests.swift; sourceTree = SOURCE_ROOT; }; @@ -853,8 +871,8 @@ B7895D8123469DE200765D85 /* SFSDKCompositeResponse+Internal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SFSDKCompositeResponse+Internal.h"; sourceTree = ""; }; B78A238921014FCA00B19AB3 /* SFSDKAuthHelper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SFSDKAuthHelper.h; sourceTree = ""; }; B78A238A21014FCA00B19AB3 /* SFSDKAuthHelper.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SFSDKAuthHelper.m; sourceTree = ""; }; - B78C27B71FBCFFCB00742CD5 /* SFSDKLoginViewControllerConfig.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SFSDKLoginViewControllerConfig.h; path = Login/SFSDKLoginViewControllerConfig.h; sourceTree = ""; }; - B78C27B81FBCFFCB00742CD5 /* SFSDKLoginViewControllerConfig.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SFSDKLoginViewControllerConfig.m; path = Login/SFSDKLoginViewControllerConfig.m; sourceTree = ""; }; + B78C27B71FBCFFCB00742CD5 /* SFSDKLoginViewControllerConfig.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SFSDKLoginViewControllerConfig.h; sourceTree = ""; }; + B78C27B81FBCFFCB00742CD5 /* SFSDKLoginViewControllerConfig.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SFSDKLoginViewControllerConfig.m; sourceTree = ""; }; B79F03EB20D4684400BC7D6F /* UIFont+SFSDKIDP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIFont+SFSDKIDP.h"; sourceTree = ""; }; B79F03EC20D4684400BC7D6F /* SFSDKUITableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SFSDKUITableViewCell.m; sourceTree = ""; }; B79F03EF20D4684500BC7D6F /* UIFont+SFSDKIDP.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIFont+SFSDKIDP.m"; sourceTree = ""; }; @@ -978,25 +996,15 @@ D3D675D72D39EF01008E468E /* SfapClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SfapClient.swift; sourceTree = ""; }; D3D675D82D39EF01008E468E /* SfapError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SfapError.swift; sourceTree = ""; }; D3D675D92D39EF01008E468E /* SfapErrorResponseBody.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SfapErrorResponseBody.swift; sourceTree = ""; }; - E1C80CDD1C5AEBFA001B3A21 /* SFLoginViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SFLoginViewController.h; path = Login/SFLoginViewController.h; sourceTree = ""; }; - E1C80CDE1C5AEBFA001B3A21 /* SFLoginViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SFLoginViewController.m; path = Login/SFLoginViewController.m; sourceTree = ""; }; - E1C80CE11C5AEE31001B3A21 /* SFSDKLoginHost.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SFSDKLoginHost.h; path = Login/LoginHost/SFSDKLoginHost.h; sourceTree = ""; }; - E1C80CE21C5AEE31001B3A21 /* SFSDKLoginHost.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SFSDKLoginHost.m; path = Login/LoginHost/SFSDKLoginHost.m; sourceTree = ""; }; - E1C80CE31C5AEE31001B3A21 /* SFSDKLoginHostDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SFSDKLoginHostDelegate.h; path = Login/LoginHost/SFSDKLoginHostDelegate.h; sourceTree = ""; }; - E1C80CE41C5AEE31001B3A21 /* SFSDKLoginHostListViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SFSDKLoginHostListViewController.h; path = Login/LoginHost/SFSDKLoginHostListViewController.h; sourceTree = ""; }; - E1C80CE51C5AEE31001B3A21 /* SFSDKLoginHostListViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SFSDKLoginHostListViewController.m; path = Login/LoginHost/SFSDKLoginHostListViewController.m; sourceTree = ""; }; - E1C80CE61C5AEE31001B3A21 /* SFSDKLoginHostStorage.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SFSDKLoginHostStorage.h; path = Login/LoginHost/SFSDKLoginHostStorage.h; sourceTree = ""; }; - E1C80CE71C5AEE31001B3A21 /* SFSDKLoginHostStorage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SFSDKLoginHostStorage.m; path = Login/LoginHost/SFSDKLoginHostStorage.m; sourceTree = ""; }; - E1C80CEA1C5AEE31001B3A21 /* SFSDKTextFieldTableViewCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SFSDKTextFieldTableViewCell.h; path = Login/LoginHost/SFSDKTextFieldTableViewCell.h; sourceTree = ""; }; - E1C80CEB1C5AEE31001B3A21 /* SFSDKTextFieldTableViewCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SFSDKTextFieldTableViewCell.m; path = Login/LoginHost/SFSDKTextFieldTableViewCell.m; sourceTree = ""; }; + E1C80CDD1C5AEBFA001B3A21 /* SFLoginViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SFLoginViewController.h; sourceTree = ""; }; + E1C80CDE1C5AEBFA001B3A21 /* SFLoginViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SFLoginViewController.m; sourceTree = ""; }; E1C80D071C5AF029001B3A21 /* SalesforceSDKAssets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = SalesforceSDKAssets.xcassets; path = ../../shared/resources/SalesforceSDKAssets.xcassets; sourceTree = ""; }; E1DDC0EE1CAA29A1002F51DD /* UIColor+SFColors.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIColor+SFColors.h"; sourceTree = ""; }; E1DDC0FD1CAA2A8B002F51DD /* UIColor+SFColors.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIColor+SFColors.m"; sourceTree = ""; }; E1DDC1431CAEEB34002F51DD /* SFSDKLoginHostTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SFSDKLoginHostTests.m; path = SalesforceSDKCoreTests/SFSDKLoginHostTests.m; sourceTree = SOURCE_ROOT; }; - FDCEC2F8558C9BA9D9684E4A /* SFSDKDevInfoViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SFSDKDevInfoViewController.h; sourceTree = ""; }; FDCEC40C4B860474C90D02AF /* SFSObjectTree.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SFSObjectTree.h; sourceTree = ""; }; - FDCEC75DBDF00FBA8810BA72 /* SFSDKDevInfoViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SFSDKDevInfoViewController.m; sourceTree = ""; }; FDCECBE6688B51C1B9BDC524 /* SFSObjectTree.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SFSObjectTree.m; sourceTree = ""; }; + FDCECF112345678901ABCDEF /* DevInfoViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DevInfoViewController.swift; sourceTree = ""; }; FDD7D7A0232039D000F5FB2D /* SFUserAccountPhotoTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SFUserAccountPhotoTests.m; path = SalesforceSDKCoreTests/SFUserAccountPhotoTests.m; sourceTree = SOURCE_ROOT; }; FDED971D1CAA16EB009D80F2 /* SFApplicationHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SFApplicationHelper.h; sourceTree = ""; }; FDED971E1CAA16EB009D80F2 /* SFApplicationHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SFApplicationHelper.m; sourceTree = ""; }; @@ -1050,93 +1058,95 @@ 4F7EB3F61BFFC84700768720 /* SalesforceSDKCoreTests */ = { isa = PBXGroup; children = ( - 4F3ECD8B2EBBD182005020A6 /* SFOAuthInfoTests.m */, - 4F3ECD892EBBD150005020A6 /* SFOAuthCoordinatorTests.m */, - 4F5A49572E98B0F800C89DDD /* ScopeParserTests.swift */, 23F200AB2E551C890091C5F5 /* ActionTypeTests.swift */, + 4FAUTHFLOW112345678901ABC /* AuthFlowTypesViewTests.swift */, + 696E91452AD0A14A00205884 /* BiometricAuthenticationManagerTests.swift */, + 4FBOOTCP112345678901ABCD /* BootConfigPickerViewControllerTests.swift */, 23F200AD2E551C890091C5F5 /* BootconfigTests.swift */, - 23EED8892E2ACD3300646B10 /* SFOAuthCoordinatorTests.swift */, - 23D96B752E145B400004B06A /* DomainDiscoveryCoordinatorTests.swift */, + 69DFE0682B969C1D000906E4 /* CryptoUtilsTests.swift */, 237C18722E450B710008015C /* DecryptStreamTests.swift */, + 4FDEVINFO112345678901ABCD /* DevInfoViewControllerTests.swift */, + 23D96B752E145B400004B06A /* DomainDiscoveryCoordinatorTests.swift */, + 697F5C4E267BE29A00F382A9 /* EncryptionTests.swift */, 237C186B2E44FCAE0008015C /* EncryptStreamTests.swift */, - 230834872DF8A8F300C7CBF7 /* WebSocketClientTests.swift */, - 230834852DF8938D00C7CBF7 /* URLSessionTask+RetryPolicyTests.swift */, - 23EDDF012DE0F9EF0024AD39 /* URLRequest+RestRequestTests.swift */, - 4F9E05332DD7BE0A00548985 /* SFOAuthCredentialsTests.m */, - 4F9E052C2DD6A06F00548985 /* SFSDKOAuthTokenEndpointResponseTests.m */, - 696D6C3D2DD7E0AD00138888 /* NewLoginHostTests.swift */, - 238AA57F2D827AF80036667C /* PushNotificationManagerTests.swift */, - 2394F4712D930C5400B9686A /* WebViewStateManagerTests.swift */, 23A4C7472D0CAFCF00DF55EB /* JwtAccessTokenTests.swift */, + 23CAB0F82DCBE51F00B8929B /* Mocks */, 23A4C7482D0CAFCF00DF55EB /* NativeLoginManagerTests.swift */, - 23A4C7452D0CAFA000DF55EB /* ScreenLockManagerTests.swift */, - 69DFE0682B969C1D000906E4 /* CryptoUtilsTests.swift */, - 69DFE0672B969C1D000906E4 /* PushNotificationDecryptionTests.swift */, - 696E91452AD0A14A00205884 /* BiometricAuthenticationManagerTests.swift */, - 696E91462AD0A14A00205884 /* ScreenLockManagerTests.m */, - 697F5C4E267BE29A00F382A9 /* EncryptionTests.swift */, - CE02ACD6202E19B100C6A714 /* SFSDKAuthConfigUtilTests.m */, - CEB98ED31F86E76A0083AB9C /* SFSDKAuthErrorCommandTest.m */, - CEB98ED41F86E76A0083AB9C /* SFSDKAuthRequestCommandTest.m */, - CEB98ED61F86E76B0083AB9C /* SFSDKSPLoginResponseCommandTest.m */, - CEB98ED71F86E76B0083AB9C /* SFSDKIDPLoginRequestCommandTest.m */, - CEB98EBC1F86E7690083AB9C /* SFSDKURLHandlerManagerTest.m */, - 8214D954205316BA0007349E /* SFSDKSalesforceAnalyticsManagerTests.m */, - CE81A9C61E9C26EF00F3D0AD /* SFUserAccountManagerNotificationsTests.m */, - CED452E71D808DEE009266EB /* SalesforceRestAPITests.h */, - CED452E81D808DEE009266EB /* SalesforceRestAPITests.m */, - CED452E91D808DEE009266EB /* SFNativeRestRequestListener.h */, - CED452EA1D808DEE009266EB /* SFNativeRestRequestListener.m */, - E1DDC1431CAEEB34002F51DD /* SFSDKLoginHostTests.m */, + 696D6C3D2DD7E0AD00138888 /* NewLoginHostTests.swift */, 4F755F4120D48D6700CE4E0E /* NSString+SFAdditionsTests.m */, 4F06AF5D1C49A16A00F70798 /* NSURL+SFStringUtilsTests.h */, 4F06AF5E1C49A16A00F70798 /* NSURL+SFStringUtilsTests.m */, + 69DFE0672B969C1D000906E4 /* PushNotificationDecryptionTests.swift */, + 238AA57F2D827AF80036667C /* PushNotificationManagerTests.swift */, + B7E66AE823763278005A652E /* RestClientPublisherTests.swift */, + B7A6ED3A236B49A100DBA451 /* RestClientTest.swift */, 4F06AF5F1C49A16A00F70798 /* SalesforceOAuthUnitTests.h */, 4F06AF601C49A16A00F70798 /* SalesforceOAuthUnitTests.m */, 4F06AF611C49A16A00F70798 /* SalesforceOAuthUnitTestsCoordinatorDelegate.h */, 4F06AF621C49A16A00F70798 /* SalesforceOAuthUnitTestsCoordinatorDelegate.m */, - 4F06AF641C49A16A00F70798 /* SalesforceSDKIdentityTests.h */, + CED452E71D808DEE009266EB /* SalesforceRestAPITests.h */, + CED452E81D808DEE009266EB /* SalesforceRestAPITests.m */, + B7156B8722DE3603003AB69D /* SalesforceSDKCoreTests-Bridging-Header.h */, 4F06AF651C49A16A00F70798 /* SalesforceSDKIdentityTests.m */, 4F06AF661C49A16A00F70798 /* SalesforceSDKManagerTests.m */, + 4F5A49572E98B0F800C89DDD /* ScopeParserTests.swift */, + 696E91462AD0A14A00205884 /* ScreenLockManagerTests.m */, + 23A4C7452D0CAFA000DF55EB /* ScreenLockManagerTests.swift */, + 4F7EB3F71BFFC87600768720 /* SDKCommonNSDataTests.m */, + B759CD881F8BDBAC0081AA87 /* SDSDKAlertMessageTest.m */, + 010A9B511CC1A131002AF4D3 /* SFCryptoStreamTestUtils.h */, + 010A9B521CC1A131002AF4D3 /* SFCryptoStreamTestUtils.m */, + 4F7EB3F81BFFC87600768720 /* SFEncryptionKeyTests.m */, + B7352CA422761D8400DA2CFF /* SFManagedPreferencesTest.m */, + CED452E91D808DEE009266EB /* SFNativeRestRequestListener.h */, + CED452EA1D808DEE009266EB /* SFNativeRestRequestListener.m */, + 69CEBC7D22F368CF00F16218 /* SFNetworkTests.m */, + 4F3ECD892EBBD150005020A6 /* SFOAuthCoordinatorTests.m */, + 23EED8892E2ACD3300646B10 /* SFOAuthCoordinatorTests.swift */, + 4F9E05332DD7BE0A00548985 /* SFOAuthCredentialsTests.m */, + 4F3ECD8B2EBBD182005020A6 /* SFOAuthInfoTests.m */, 4F06AF681C49A16A00F70798 /* SFOAuthSessionRefresherTests.m */, 4F06AF6B1C49A16A00F70798 /* SFOAuthTestFlowCoordinatorDelegate.h */, 4F06AF6C1C49A16A00F70798 /* SFOAuthTestFlowCoordinatorDelegate.m */, 4F06AF6D1C49A16A00F70798 /* SFPreferencesTests.m */, - 4F06AF701C49A16A00F70798 /* SFTestSDKManagerFlow.h */, - 4F06AF711C49A16A00F70798 /* SFTestSDKManagerFlow.m */, - 4F06AF721C49A16A00F70798 /* SFUserAccountManagerTests.m */, - 4F7EB3F71BFFC87600768720 /* SDKCommonNSDataTests.m */, - 4F7EB3F81BFFC87600768720 /* SFEncryptionKeyTests.m */, - 4F7EB3FC1BFFC87600768720 /* SFSDKCryptoUtilsTests.m */, - 010A9B511CC1A131002AF4D3 /* SFCryptoStreamTestUtils.h */, - 010A9B521CC1A131002AF4D3 /* SFCryptoStreamTestUtils.m */, - 4F7EB4571BFFC9D900768720 /* Supporting Files */, 009D77511CA4A92200D5183A /* SFPushNotificationManagerTests.m */, 8263FED21E7336BF0038F694 /* SFSDKAppFeatureMarkersTests.m */, - 444B95CF1E83251900908C61 /* UIColor+SFColorsTests.m */, - B7E8A2AE1E77062E007C0D92 /* SFUserAccountManagerPersisterTests.m */, - B7E8A2B01E770A57007C0D92 /* SFUserAccountPersisterEphemeral.h */, - B7E8A2B11E770A57007C0D92 /* SFUserAccountPersisterEphemeral.m */, - B7E1A50D1F43B0FE007AC36A /* SFSDKWindowManagerTests.m */, - B759CD881F8BDBAC0081AA87 /* SDSDKAlertMessageTest.m */, - B759CD8A1F8C10DC0081AA87 /* SFSDKErrorManagerTests.m */, - B7352CA422761D8400DA2CFF /* SFManagedPreferencesTest.m */, - B7A901BD228E4DFA0036D749 /* SFSDKLogoutBlocker.m */, - B7355248228E84AF001C7759 /* SFSDKLogoutBlocker.h */, + CE02ACD6202E19B100C6A714 /* SFSDKAuthConfigUtilTests.m */, + CEB98ED31F86E76A0083AB9C /* SFSDKAuthErrorCommandTest.m */, + CEB98ED41F86E76A0083AB9C /* SFSDKAuthRequestCommandTest.m */, B7156B8822DE3603003AB69D /* SFSDKAuthUtilTests.swift */, - B7156B8722DE3603003AB69D /* SalesforceSDKCoreTests-Bridging-Header.h */, - 69CEBC7D22F368CF00F16218 /* SFNetworkTests.m */, - FDD7D7A0232039D000F5FB2D /* SFUserAccountPhotoTests.m */, - 691D129F23296A93000D6D41 /* SFSDKURLCacheTests.m */, + 4F7EB3FC1BFFC87600768720 /* SFSDKCryptoUtilsTests.m */, 69848CB72364035300893E57 /* SFSDKEncryptedPushNotificationTests.m */, + B759CD8A1F8C10DC0081AA87 /* SFSDKErrorManagerTests.m */, + 6990CBBF29F5AF56004A5F8D /* SFSDKIDPAuthCodeLoginRequestCommandTest.m */, + CEB98ED71F86E76B0083AB9C /* SFSDKIDPLoginRequestCommandTest.m */, + 693E623A24A29B6B0017B222 /* SFSDKKeyValueEncryptedFileStoreTests.m */, + E1DDC1431CAEEB34002F51DD /* SFSDKLoginHostTests.m */, + B7355248228E84AF001C7759 /* SFSDKLogoutBlocker.h */, + B7A901BD228E4DFA0036D749 /* SFSDKLogoutBlocker.m */, + 4F9E052C2DD6A06F00548985 /* SFSDKOAuthTokenEndpointResponseTests.m */, 69848CBB2364063E00893E57 /* SFSDKPushNotificationDataProvider.h */, 69848CBC2364063E00893E57 /* SFSDKPushNotificationDataProvider.m */, - B7A6ED3A236B49A100DBA451 /* RestClientTest.swift */, - B7E66AE823763278005A652E /* RestClientPublisherTests.swift */, + 8214D954205316BA0007349E /* SFSDKSalesforceAnalyticsManagerTests.m */, + CEB98ED61F86E76B0083AB9C /* SFSDKSPLoginResponseCommandTest.m */, + 691D129F23296A93000D6D41 /* SFSDKURLCacheTests.m */, + CEB98EBC1F86E7690083AB9C /* SFSDKURLHandlerManagerTest.m */, + B7E1A50D1F43B0FE007AC36A /* SFSDKWindowManagerTests.m */, + 4F06AF701C49A16A00F70798 /* SFTestSDKManagerFlow.h */, + 4F06AF711C49A16A00F70798 /* SFTestSDKManagerFlow.m */, + CE81A9C61E9C26EF00F3D0AD /* SFUserAccountManagerNotificationsTests.m */, + B7E8A2AE1E77062E007C0D92 /* SFUserAccountManagerPersisterTests.m */, + 4F06AF721C49A16A00F70798 /* SFUserAccountManagerTests.m */, + B7E8A2B01E770A57007C0D92 /* SFUserAccountPersisterEphemeral.h */, + B7E8A2B11E770A57007C0D92 /* SFUserAccountPersisterEphemeral.m */, + FDD7D7A0232039D000F5FB2D /* SFUserAccountPhotoTests.m */, 6931EA48248F000600417362 /* SFUserIdUpgradeTests.m */, - 693E623A24A29B6B0017B222 /* SFSDKKeyValueEncryptedFileStoreTests.m */, - 6990CBBF29F5AF56004A5F8D /* SFSDKIDPAuthCodeLoginRequestCommandTest.m */, - 23CAB0F82DCBE51F00B8929B /* Mocks */, + 4F7EB4571BFFC9D900768720 /* Supporting Files */, + 444B95CF1E83251900908C61 /* UIColor+SFColorsTests.m */, + 23EDDF012DE0F9EF0024AD39 /* URLRequest+RestRequestTests.swift */, + 230834852DF8938D00C7CBF7 /* URLSessionTask+RetryPolicyTests.swift */, + 230834872DF8A8F300C7CBF7 /* WebSocketClientTests.swift */, + 2394F4712D930C5400B9686A /* WebViewStateManagerTests.swift */, ); path = SalesforceSDKCoreTests; sourceTree = ""; @@ -1352,6 +1362,16 @@ path = Util; sourceTree = ""; }; + 4FE006A82EBEB5E900CFD66F /* DevConfig */ = { + isa = PBXGroup; + children = ( + 4FE006AE2EBEB65900CFD66F /* BootConfigEditor.swift */, + 4FE006AF2EBEB65900CFD66F /* BootConfigPickerViewController.swift */, + 4FE006B02EBEB65900CFD66F /* AuthFlowTypesView.swift */, + ); + path = DevConfig; + sourceTree = ""; + }; 6935FB9823FDE919002BEFCC /* UserAccount */ = { isa = PBXGroup; children = ( @@ -1562,10 +1582,10 @@ A37530152B2A6C6B006DFE69 /* NativeLogin */ = { isa = PBXGroup; children = ( - 23A4C74C2D0CB68B00DF55EB /* NativeLoginManager.swift */, - 23A4C74D2D0CB68B00DF55EB /* NativeLoginManagerInternal.swift */, + 4FE006DE2EBEBC0000CFD66F /* NativeLoginManager.swift */, + 4FE006DF2EBEBC0000CFD66F /* NativeLoginManagerInternal.swift */, ); - name = NativeLogin; + path = NativeLogin; sourceTree = ""; }; A3C7475D29F7085E00D72B7F /* BiometricAuthentication */ = { @@ -1616,6 +1636,7 @@ B7A20F7C1F25850E00D1E4B0 /* Views */ = { isa = PBXGroup; children = ( + FDCECF112345678901ABCDEF /* DevInfoViewController.swift */, B711290A1F8A780800436CFB /* SFSDKAlertMessage.h */, B711290C1F8A780800436CFB /* SFSDKAlertMessage.m */, B71129081F8A780800436CFB /* SFSDKAlertMessageBuilder.h */, @@ -1629,8 +1650,6 @@ B7A20F821F25850E00D1E4B0 /* SFSDKWindowManager.m */, B7A20FAB1F26C39700D1E4B0 /* SFSDKRootController.h */, B7A20FAC1F26C39700D1E4B0 /* SFSDKRootController.m */, - FDCEC75DBDF00FBA8810BA72 /* SFSDKDevInfoViewController.m */, - FDCEC2F8558C9BA9D9684E4A /* SFSDKDevInfoViewController.h */, B767368F20A4AB0200F04103 /* SFSDKNavigationController.h */, B767369020A4AB0200F04103 /* SFSDKNavigationController.m */, B7C5125720C188AE00B39DAA /* SFSDKViewController.h */, @@ -1770,33 +1789,34 @@ E139509E1C535F9C00575C03 /* Login */ = { isa = PBXGroup; children = ( + 4FE006A82EBEB5E900CFD66F /* DevConfig */, + E13950A21C53620B00575C03 /* LoginHost */, A37530152B2A6C6B006DFE69 /* NativeLogin */, E1C80CDD1C5AEBFA001B3A21 /* SFLoginViewController.h */, E1C80CDE1C5AEBFA001B3A21 /* SFLoginViewController.m */, 69D03E252D2C6D0B004381EC /* SFLoginViewController+QrCodeLogin.swift */, 23CAB1302DD515B500B8929B /* SFLoginViewController+Deep-Linking.swift */, - E13950A21C53620B00575C03 /* LoginHost */, B78C27B71FBCFFCB00742CD5 /* SFSDKLoginViewControllerConfig.h */, B78C27B81FBCFFCB00742CD5 /* SFSDKLoginViewControllerConfig.m */, ); - name = Login; + path = Login; sourceTree = ""; }; E13950A21C53620B00575C03 /* LoginHost */ = { isa = PBXGroup; children = ( - 6941BE652D2F0E1B00CEC59B /* NewLoginHostView.swift */, - E1C80CE11C5AEE31001B3A21 /* SFSDKLoginHost.h */, - E1C80CE21C5AEE31001B3A21 /* SFSDKLoginHost.m */, - E1C80CE31C5AEE31001B3A21 /* SFSDKLoginHostDelegate.h */, - E1C80CE41C5AEE31001B3A21 /* SFSDKLoginHostListViewController.h */, - E1C80CE51C5AEE31001B3A21 /* SFSDKLoginHostListViewController.m */, - E1C80CE61C5AEE31001B3A21 /* SFSDKLoginHostStorage.h */, - E1C80CE71C5AEE31001B3A21 /* SFSDKLoginHostStorage.m */, - E1C80CEA1C5AEE31001B3A21 /* SFSDKTextFieldTableViewCell.h */, - E1C80CEB1C5AEE31001B3A21 /* SFSDKTextFieldTableViewCell.m */, - ); - name = LoginHost; + 4FE006CA2EBEBBF300CFD66F /* NewLoginHostView.swift */, + 4FE006CB2EBEBBF300CFD66F /* SFSDKLoginHost.h */, + 4FE006CC2EBEBBF300CFD66F /* SFSDKLoginHost.m */, + 4FE006CD2EBEBBF300CFD66F /* SFSDKLoginHostDelegate.h */, + 4FE006CE2EBEBBF300CFD66F /* SFSDKLoginHostListViewController.h */, + 4FE006CF2EBEBBF300CFD66F /* SFSDKLoginHostListViewController.m */, + 4FE006D02EBEBBF300CFD66F /* SFSDKLoginHostStorage.h */, + 4FE006D12EBEBBF300CFD66F /* SFSDKLoginHostStorage.m */, + 4FE006D22EBEBBF300CFD66F /* SFSDKTextFieldTableViewCell.h */, + 4FE006D32EBEBBF300CFD66F /* SFSDKTextFieldTableViewCell.m */, + ); + path = LoginHost; sourceTree = ""; }; /* End PBXGroup section */ @@ -1823,7 +1843,6 @@ 6900D804243D521C00888336 /* SFRestAPI+Notifications.h in Headers */, 829DA2951C1266340040F5F1 /* SalesforceSDKCore.h in Headers */, CE4CE3501C0E5252009F6029 /* SFOAuthSessionRefresher+Internal.h in Headers */, - E1C80CF51C5AEE31001B3A21 /* SFSDKTextFieldTableViewCell.h in Headers */, B7FB26DA1F78096300FB25A2 /* SFSDKIDPErrorHandler.h in Headers */, CE4CE3451C0E5252009F6029 /* SFOAuthCredentials+Internal.h in Headers */, CED452ED1D808DEE009266EB /* SFNativeRestRequestListener.h in Headers */, @@ -1837,7 +1856,6 @@ B72171562353BFF20022510F /* SFSDKAuthRequest.h in Headers */, CE4CE33E1C0E524B009F6029 /* SFInstrumentation.h in Headers */, 4F06AF811C49A16A00F70798 /* SFOAuthTestFlowCoordinatorDelegate.h in Headers */, - E1C80CEC1C5AEE31001B3A21 /* SFSDKLoginHost.h in Headers */, CE4CE34C1C0E5252009F6029 /* SFOAuthKeychainCredentials.h in Headers */, CE4CE39F1C0E5272009F6029 /* TestSetupUtils.h in Headers */, 6924966A2444E50500D3F63B /* SFFormatUtils.h in Headers */, @@ -1880,6 +1898,7 @@ 4F5727E327F27F1A0008CDA4 /* SFSDKPrimingRecordsResponse.h in Headers */, 8214D96D205317BC0007349E /* SFSDKSalesforceAnalyticsManager+Internal.h in Headers */, B7A20F871F25850E00D1E4B0 /* SFSDKWindowManager.h in Headers */, + 4FE0070F2EBED6C000CFD66F /* SFSDKLoginHost.h in Headers */, CE4CE3B11C0E5279009F6029 /* SFSDKWebUtils.h in Headers */, B79F03F520D4684600BC7D6F /* UIFont+SFSDKIDP.h in Headers */, CE4CE35F1C0E526A009F6029 /* SFAuthErrorHandlerList.h in Headers */, @@ -1930,13 +1949,16 @@ CED452BE1D808D0C009266EB /* SFRestAPI.h in Headers */, CE4CE3871C0E526A009F6029 /* SFSDKCryptoUtils.h in Headers */, CE4CE3921C0E526A009F6029 /* SFUserAccountIdentity.h in Headers */, + 4FE006D42EBEBBF300CFD66F /* SFSDKLoginHostListViewController.h in Headers */, + 4FE006D52EBEBBF300CFD66F /* SFSDKTextFieldTableViewCell.h in Headers */, + 4FE006D62EBEBBF300CFD66F /* SFSDKLoginHostStorage.h in Headers */, + 4FE006D82EBEBBF300CFD66F /* SFSDKLoginHostDelegate.h in Headers */, B711290F1F8A780800436CFB /* SFSDKAlertView.h in Headers */, CE4CE32B1C0E523B009F6029 /* SalesforceSDKManager+Internal.h in Headers */, 6931E954248B5C7100417362 /* SFDirectoryManager+Internal.h in Headers */, CE4CE34A1C0E5252009F6029 /* SFOAuthInfo.h in Headers */, B72171522353BFD90022510F /* SFSDKAuthSession.h in Headers */, CE4CE36A1C0E526A009F6029 /* SFDefaultUserManagementViewController.h in Headers */, - 4F06AF7A1C49A16A00F70798 /* SalesforceSDKIdentityTests.h in Headers */, B7FB26C21F78094A00FB25A2 /* SFSDKLoginFlowSelectionView.h in Headers */, B7FB26DE1F78096300FB25A2 /* SFSDKIDPRequestHandler.h in Headers */, B7C2744E1F814E7200CE539D /* SFSDKAuthCommand+Internal.h in Headers */, @@ -1944,7 +1966,6 @@ B7F6909022C27CEF00E52575 /* SFSDKOAuth2.h in Headers */, CE4CE3911C0E526A009F6029 /* SFUserAccountConstants.h in Headers */, CED452BB1D808D0C009266EB /* SFRestAPI+Internal.h in Headers */, - E1C80CEF1C5AEE31001B3A21 /* SFSDKLoginHostListViewController.h in Headers */, CED452C21D808D0C009266EB /* SFRestRequest+Internal.h in Headers */, CE4CE3A31C0E5279009F6029 /* SalesforceSDKCoreDefines.h in Headers */, 69848CA52363F8AF00893E57 /* SFSDKPushNotificationFieldsConstants.h in Headers */, @@ -1953,14 +1974,12 @@ CE4CE3651C0E526A009F6029 /* SFDefaultUserManagementDetailViewController.h in Headers */, B7FB26DC1F78096300FB25A2 /* SFSDKIDPLoginRequestHandler.h in Headers */, CE4CE3231C0E523B009F6029 /* UIDevice+SFHardware.h in Headers */, - E1C80CF11C5AEE31001B3A21 /* SFSDKLoginHostStorage.h in Headers */, 69848CB52363FA3E00893E57 /* SFSDKPushNotificationError.h in Headers */, CE4CE3381C0E5245009F6029 /* SFIdentityData+Internal.h in Headers */, B7677053223AE5E400545C90 /* SFUserAccountManager+Instrumentation.h in Headers */, 4F06AF751C49A16A00F70798 /* SalesforceOAuthUnitTests.h in Headers */, CE4CE3421C0E5252009F6029 /* SFOAuthCoordinator+Internal.h in Headers */, CED452BC1D808D0C009266EB /* SFRestAPI+QueryBuilder.h in Headers */, - E1C80CEE1C5AEE31001B3A21 /* SFSDKLoginHostDelegate.h in Headers */, CE4CE3301C0E523B009F6029 /* SFSDKAppConfig.h in Headers */, CE675A371E0B2CC6002DBF5A /* SFSDKSoslReturningBuilder.h in Headers */, 4F06AF861C49A16A00F70798 /* SFTestSDKManagerFlow.h in Headers */, @@ -1969,7 +1988,6 @@ CE675A351E0B2CC6002DBF5A /* SFSDKSoslBuilder.h in Headers */, B7FB26E01F78096300FB25A2 /* SFSDKSPLoginResponseHandler.h in Headers */, FDCEC95C5AD1CFAD130DDCCF /* SFSObjectTree.h in Headers */, - FDCECE47E9586450F036FD3B /* SFSDKDevInfoViewController.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2253,7 +2271,9 @@ 4F7EB4161BFFC8D700768720 /* SDKCommonNSDataTests.m in Sources */, CE81A9C81E9C26F900F3D0AD /* SFUserAccountManagerNotificationsTests.m in Sources */, 23F200AC2E551C890091C5F5 /* ActionTypeTests.swift in Sources */, + 4FAUTHFLOW001234567890ABC /* AuthFlowTypesViewTests.swift in Sources */, 23F200AE2E551C890091C5F5 /* BootconfigTests.swift in Sources */, + 4FBOOTCP001234567890ABCD /* BootConfigPickerViewControllerTests.swift in Sources */, 4F7EB4171BFFC8D700768720 /* SFEncryptionKeyTests.m in Sources */, 69848CBD2364063E00893E57 /* SFSDKPushNotificationDataProvider.m in Sources */, 23A4C7492D0CAFCF00DF55EB /* NativeLoginManagerTests.swift in Sources */, @@ -2291,6 +2311,7 @@ 237C18732E450B710008015C /* DecryptStreamTests.swift in Sources */, CED452EF1D808E0A009266EB /* SFNativeRestRequestListener.m in Sources */, 230834862DF8938D00C7CBF7 /* URLSessionTask+RetryPolicyTests.swift in Sources */, + 4FDEVINFO001234567890ABCD /* DevInfoViewControllerTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2329,11 +2350,13 @@ B7895D0E2345015B00765D85 /* SFSDKCompositeRequest.m in Sources */, CE145A9D1D138386003BCC20 /* SFSDKSalesforceAnalyticsManager.m in Sources */, B7A20F881F25850E00D1E4B0 /* SFSDKWindowManager.m in Sources */, - E1C80CF21C5AEE31001B3A21 /* SFSDKLoginHostStorage.m in Sources */, B72171572353BFF20022510F /* SFSDKAuthRequest.m in Sources */, B78A238D21014FCA00B19AB3 /* SFSDKAuthHelper.m in Sources */, A3C7475F29F7087B00D72B7F /* BiometricAuthenticationManager.swift in Sources */, CE4CE33F1C0E524B009F6029 /* SFInstrumentation.m in Sources */, + 4FE006B12EBEB65900CFD66F /* AuthFlowTypesView.swift in Sources */, + 4FE006B22EBEB65900CFD66F /* BootConfigEditor.swift in Sources */, + 4FE006B32EBEB65900CFD66F /* BootConfigPickerViewController.swift in Sources */, 697A91A12363C3D800D2836F /* SFSDKPushNotificationDecryption.m in Sources */, CE675A341E0B2CC6002DBF5A /* SFSDKSoqlBuilder.m in Sources */, 69E2FD9F22FB937F008E0AF0 /* SFSDKEncryptedURLCache.m in Sources */, @@ -2358,6 +2381,11 @@ D3D675E42D39EF01008E468E /* FeedbackRequestBody.swift in Sources */, D3D675E52D39EF01008E468E /* EmbeddingsResponseBody.swift in Sources */, 695E86BA29EF8D6E002BDEA6 /* SFSDKIDPAuthCodeLoginRequestCommand.m in Sources */, + 4FE006D92EBEBBF300CFD66F /* SFSDKTextFieldTableViewCell.m in Sources */, + 4FE006DA2EBEBBF300CFD66F /* NewLoginHostView.swift in Sources */, + 4FE006DB2EBEBBF300CFD66F /* SFSDKLoginHost.m in Sources */, + 4FE006DC2EBEBBF300CFD66F /* SFSDKLoginHostListViewController.m in Sources */, + 4FE006DD2EBEBBF300CFD66F /* SFSDKLoginHostStorage.m in Sources */, B7C2745C1F8151E300CE539D /* SFSDKAuthErrorCommand.m in Sources */, CE4CE30A1C0E523B009F6029 /* NSURL+SFAdditions.m in Sources */, E1DDC0FE1CAA2A8B002F51DD /* UIColor+SFColors.m in Sources */, @@ -2373,9 +2401,7 @@ 693E623124A287DB0017B222 /* KeyValueEncryptedFileStore.swift in Sources */, CE4CE3A21C0E5279009F6029 /* NSURL+SFStringUtils.m in Sources */, 69BDD82F26F90AA400C26D77 /* DecryptStream.swift in Sources */, - 23A4C74E2D0CB68B00DF55EB /* NativeLoginManagerInternal.swift in Sources */, 23D96B6F2E145AC20004B06A /* DomainDiscoveryCoordinator.swift in Sources */, - 23A4C74F2D0CB68B00DF55EB /* NativeLoginManager.swift in Sources */, B7CD6D671F79CFC900F99F81 /* SFUserAccountManager+URLHandlers.m in Sources */, 69848CB62363FA3E00893E57 /* SFSDKPushNotificationError.m in Sources */, 23CAB1312DD515B500B8929B /* SFLoginViewController+Deep-Linking.swift in Sources */, @@ -2402,7 +2428,6 @@ A32C854A25268005000FFA42 /* KeyValueEncryptedFileStoreInspector.swift in Sources */, B79F03FD20D4684600BC7D6F /* UIFont+SFSDKIDP.m in Sources */, CE4CE30E1C0E523B009F6029 /* NSData+SFAdditions.m in Sources */, - E1C80CF61C5AEE31001B3A21 /* SFSDKTextFieldTableViewCell.m in Sources */, CE4CE3471C0E5252009F6029 /* SFOAuthCredentials.m in Sources */, CE4CE3141C0E523B009F6029 /* NSString+SFAdditions.m in Sources */, A3C7475C29F7047400D72B7F /* ScreenLockManager.swift in Sources */, @@ -2424,6 +2449,8 @@ 2337559D2D9738AB0040E414 /* PushNotificationManager+ActionableNotifications.swift in Sources */, B7A20FAF1F26C39700D1E4B0 /* SFSDKRootController.m in Sources */, CE50CE921DD3DD1F00F1297B /* SFSDKEventBuilderHelper.m in Sources */, + 4FE006E02EBEBC0000CFD66F /* NativeLoginManagerInternal.swift in Sources */, + 4FE006E12EBEBC0000CFD66F /* NativeLoginManager.swift in Sources */, 4F3139652331C5A1007B3705 /* SFSDKAuthRootController.m in Sources */, 23CE44212DEE258800ADC770 /* RestClient+WebSocket.swift in Sources */, CE4CE3A61C0E5279009F6029 /* SFDirectoryManager.m in Sources */, @@ -2458,7 +2485,6 @@ CED452BF1D808D0C009266EB /* SFRestAPI.m in Sources */, B7460C0F2347DD4E00C7512E /* SFSDKBatchRequest.m in Sources */, 4F5727E427F27F1A0008CDA4 /* SFSDKPrimingRecordsResponse.m in Sources */, - E1C80CED1C5AEE31001B3A21 /* SFSDKLoginHost.m in Sources */, 695E86A829EE24D0002BDEA6 /* SPConfig.swift in Sources */, CE4CE3961C0E526A009F6029 /* SFUserAccountManager.m in Sources */, CE4CE36B1C0E526A009F6029 /* SFDefaultUserManagementViewController.m in Sources */, @@ -2468,9 +2494,7 @@ A3C7476129F709EB00D72B7F /* BiometricAuthenticationManagerInternal.swift in Sources */, B7FB26DB1F78096300FB25A2 /* SFSDKIDPErrorHandler.m in Sources */, 4F5A49502E98711600C89DDD /* ScopeParser.swift in Sources */, - 6941BE662D2F0E1B00CEC59B /* NewLoginHostView.swift in Sources */, B7677051223AE5E400545C90 /* SFUserAccountManager+Instrumentation.m in Sources */, - E1C80CF01C5AEE31001B3A21 /* SFSDKLoginHostListViewController.m in Sources */, B773CCF81F8200BD00D2D1B2 /* SFSDKIDPLoginRequestCommand.m in Sources */, CE4CE3931C0E526A009F6029 /* SFUserAccountIdentity.m in Sources */, 23945B712D78E4A60060B195 /* NotificationType.swift in Sources */, @@ -2479,7 +2503,7 @@ CE4CE3101C0E523B009F6029 /* NSDictionary+SFAdditions.m in Sources */, B7FB26DD1F78096300FB25A2 /* SFSDKIDPLoginRequestHandler.m in Sources */, FDCECBC89437CBD2378AC8CC /* SFSObjectTree.m in Sources */, - FDCECC6D14B002687225A4E6 /* SFSDKDevInfoViewController.m in Sources */, + FDCECF001234567890ABCDEF /* DevInfoViewController.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Common/SalesforceSDKManager+Internal.h b/libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Common/SalesforceSDKManager+Internal.h index a247ebc83d..33c4e6963a 100644 --- a/libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Common/SalesforceSDKManager+Internal.h +++ b/libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Common/SalesforceSDKManager+Internal.h @@ -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 *)getDevActions:(nonnull UIViewController *)presentedViewController; + @end diff --git a/libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Common/SalesforceSDKManager.m b/libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Common/SalesforceSDKManager.m index 7dd85e3390..b80f2a4297 100644 --- a/libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Common/SalesforceSDKManager.m +++ b/libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Common/SalesforceSDKManager.m @@ -29,8 +29,8 @@ #import "SFManagedPreferences.h" #import "SFApplicationHelper.h" #import "SFSDKAppFeatureMarkers.h" -#import "SFSDKDevInfoViewController.h" #import "SFDefaultUserManagementViewController.h" +#import "SFSDKAuthRootController.h" #import #import "SFSDKEncryptedURLCache.h" #import "SFSDKNullURLCache.h" @@ -471,25 +471,66 @@ - (NSString *)devInfoTitleString - (NSArray*) 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 *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]]; + + // 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) { + [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) { + [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*) getDevSupportInfos @@ -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* globalKeyValueStores = [SFSDKKeyValueEncryptedFileStore allGlobalStoreNames]; + NSArray* 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; @@ -542,10 +620,17 @@ - (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*)userAccounts { + if (userAccounts == nil) { + return @""; + } NSMutableArray* usernames = [NSMutableArray new]; for (SFUserAccount *userAccount in userAccounts) { [usernames addObject:[self userToString:userAccount]]; @@ -553,10 +638,13 @@ - (NSString*) usersToString:(NSArray*)userAccounts { 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; diff --git a/native/SampleApps/AuthFlowTester/AuthFlowTester/Views/FlowTypesView.swift b/libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Login/DevConfig/AuthFlowTypesView.swift similarity index 77% rename from native/SampleApps/AuthFlowTester/AuthFlowTester/Views/FlowTypesView.swift rename to libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Login/DevConfig/AuthFlowTypesView.swift index 6003dfbb0d..327e949f66 100644 --- a/native/SampleApps/AuthFlowTester/AuthFlowTester/Views/FlowTypesView.swift +++ b/libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Login/DevConfig/AuthFlowTypesView.swift @@ -1,6 +1,6 @@ /* - FlowTypesView.swift - AuthFlowTester + AuthFlowTypesView.swift + SalesforceSDKCore Copyright (c) 2025-present, salesforce.com, inc. All rights reserved. @@ -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) @@ -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) @@ -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) diff --git a/native/SampleApps/AuthFlowTester/AuthFlowTester/Views/BootConfigEditor.swift b/libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Login/DevConfig/BootConfigEditor.swift similarity index 86% rename from native/SampleApps/AuthFlowTester/AuthFlowTester/Views/BootConfigEditor.swift rename to libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Login/DevConfig/BootConfigEditor.swift index 8c5d2d7ffb..fa532277d2 100644 --- a/native/SampleApps/AuthFlowTester/AuthFlowTester/Views/BootConfigEditor.swift +++ b/libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Login/DevConfig/BootConfigEditor.swift @@ -1,6 +1,6 @@ /* BootConfigEditor.swift - AuthFlowTester + SalesforceSDKCore Copyright (c) 2025-present, salesforce.com, inc. All rights reserved. @@ -26,9 +26,8 @@ */ import SwiftUI -import SalesforceSDKCore -struct BootConfigEditor: View { +public struct BootConfigEditor: View { let title: String let buttonLabel: String let buttonColor: Color @@ -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, + callbackUrl: Binding, + scopes: Binding, + 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 { diff --git a/libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Login/DevConfig/BootConfigPickerViewController.swift b/libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Login/DevConfig/BootConfigPickerViewController.swift new file mode 100644 index 0000000000..4413ca8b86 --- /dev/null +++ b/libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Login/DevConfig/BootConfigPickerViewController.swift @@ -0,0 +1,254 @@ +/* + BootConfigPickerViewController.swift + SalesforceSDKCore + + Copyright (c) 2025-present, salesforce.com, inc. All rights reserved. + + Redistribution and use of this software in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of salesforce.com, inc. nor the names of its contributors may be used to + endorse or promote products derived from this software without specific prior written + permission of salesforce.com, inc. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import SwiftUI +import UIKit + +public struct BootConfigPickerView: View { + @State internal var staticConsumerKey = "" + @State internal var staticCallbackUrl = "" + @State internal var staticScopes = "" + @State internal var dynamicConsumerKey = "" + @State internal var dynamicCallbackUrl = "" + @State internal var dynamicScopes = "" + @Environment(\.dismiss) private var dismiss + + let onConfigurationCompleted: () -> Void + + public init(onConfigurationCompleted: @escaping () -> Void) { + self.onConfigurationCompleted = onConfigurationCompleted + } + + // Internal initializer for testing with pre-set state values + internal init( + onConfigurationCompleted: @escaping () -> Void, + staticConsumerKey: String = "", + staticCallbackUrl: String = "", + staticScopes: String = "", + dynamicConsumerKey: String = "", + dynamicCallbackUrl: String = "", + dynamicScopes: String = "" + ) { + self.onConfigurationCompleted = onConfigurationCompleted + self._staticConsumerKey = State(initialValue: staticConsumerKey) + self._staticCallbackUrl = State(initialValue: staticCallbackUrl) + self._staticScopes = State(initialValue: staticScopes) + self._dynamicConsumerKey = State(initialValue: dynamicConsumerKey) + self._dynamicCallbackUrl = State(initialValue: dynamicCallbackUrl) + self._dynamicScopes = State(initialValue: dynamicScopes) + } + + public var body: some View { + VStack(spacing: 0) { + // Custom title bar with close button + TitleBarView(title: "Login Options", onDismiss: { + dismiss() + }) + + // Content + ScrollView { + VStack(spacing: 30) { + // Flow types section + AuthFlowTypesView() + .padding(.top, 20) + + Divider() + + // Static config section + BootConfigEditor( + title: "Static Configuration", + buttonLabel: "Use static config", + buttonColor: .blue, + consumerKey: $staticConsumerKey, + callbackUrl: $staticCallbackUrl, + scopes: $staticScopes, + isLoading: false, + onUseConfig: handleStaticConfig, + initiallyExpanded: false + ) + + Divider() + + // Dynamic config section + BootConfigEditor( + title: "Dynamic Configuration", + buttonLabel: "Use dynamic config", + buttonColor: .green, + consumerKey: $dynamicConsumerKey, + callbackUrl: $dynamicCallbackUrl, + scopes: $dynamicScopes, + isLoading: false, + onUseConfig: handleDynamicBootconfig, + initiallyExpanded: false + ) + } + .padding(.bottom, 40) + } + .background(Color(.systemBackground)) + } + .onAppear { + loadConfigDefaults() + } + } + + // MARK: - Helper Methods + + private func loadConfigDefaults() { + // Load static config from bootconfig.plist (via SalesforceManager) + if let config = SalesforceManager.shared.bootConfig { + staticConsumerKey = config.remoteAccessConsumerKey + staticCallbackUrl = config.oauthRedirectURI + staticScopes = config.oauthScopes.sorted().joined(separator: " ") + } + + // Load dynamic config defaults from bootconfig2.plist + if let config = BootConfig("/bootconfig2.plist") { + dynamicConsumerKey = config.remoteAccessConsumerKey + dynamicCallbackUrl = config.oauthRedirectURI + dynamicScopes = config.oauthScopes.sorted().joined(separator: " ") + } + } + + // MARK: - Button Actions + + internal func handleStaticConfig() { + // Parse scopes from space-separated string + let scopesArray = staticScopes + .split(separator: " ") + .map { String($0) } + .filter { !$0.isEmpty } + + // Create BootConfig with values from the editor + var configDict: [String: Any] = [ + "remoteAccessConsumerKey": staticConsumerKey, + "oauthRedirectURI": staticCallbackUrl, + "shouldAuthenticate": true + ] + + // Only add scopes if not empty + if !scopesArray.isEmpty { + configDict["oauthScopes"] = scopesArray + } + + // Set as the bootConfig + SalesforceManager.shared.bootConfig = BootConfig(configDict) + SalesforceManager.shared.bootConfigRuntimeSelector = nil + + // Update UserAccountManager properties + UserAccountManager.shared.oauthClientID = staticConsumerKey + UserAccountManager.shared.oauthCompletionURL = staticCallbackUrl + UserAccountManager.shared.scopes = scopesArray.isEmpty ? [] : Set(scopesArray) + + // Proceed with login + onConfigurationCompleted() + } + + internal func handleDynamicBootconfig() { + SalesforceManager.shared.bootConfigRuntimeSelector = { _, callback in + // Create dynamic BootConfig from user-entered values + // Parse scopes from space-separated string + let scopesArray = self.dynamicScopes + .split(separator: " ") + .map { String($0) } + .filter { !$0.isEmpty } + + var configDict: [String: Any] = [ + "remoteAccessConsumerKey": self.dynamicConsumerKey, + "oauthRedirectURI": self.dynamicCallbackUrl, + "shouldAuthenticate": true + ] + + // Only add scopes if not empty + if !scopesArray.isEmpty { + configDict["oauthScopes"] = scopesArray + } + + callback(BootConfig(configDict)) + } + + // Proceed with login + onConfigurationCompleted() + } +} + +// MARK: - Title Bar View + +struct TitleBarView: View { + let title: String + let onDismiss: () -> Void + + var body: some View { + ZStack { + Color(UIColor.salesforceBlue) + + HStack { + Spacer() + + Text(title) + .font(.system(size: 18, weight: .regular)) + .foregroundColor(.white) + + Spacer() + } + + HStack { + Spacer() + + Button(action: onDismiss) { + Image(systemName: "xmark") + .font(.system(size: 16, weight: .medium)) + .foregroundColor(.white) + .padding(12) + } + } + } + .frame(height: 44) + .frame(maxWidth: .infinity) + } +} + +// MARK: - Objective-C Bridge + +@objc public class BootConfigPickerViewController: NSObject { + + @objc public static func makeViewController(onConfigurationCompleted: @escaping () -> Void) -> UIViewController { + let view = BootConfigPickerView(onConfigurationCompleted: onConfigurationCompleted) + let hostingController = UIHostingController(rootView: view) + + // Use pageSheet for slide-up presentation + #if !os(visionOS) + if let sheet = hostingController.sheetPresentationController { + sheet.detents = [.medium(), .large()] + sheet.prefersGrabberVisible = true + sheet.preferredCornerRadius = 16 + } + #endif + + return hostingController + } +} + diff --git a/libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Login/LoginHost/SFSDKLoginHostStorage.m b/libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Login/LoginHost/SFSDKLoginHostStorage.m index 27fc6e1641..f9111efab6 100644 --- a/libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Login/LoginHost/SFSDKLoginHostStorage.m +++ b/libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Login/LoginHost/SFSDKLoginHostStorage.m @@ -107,7 +107,7 @@ - (id)init { */ if (![self loginHostForHostAddress:customHost]) { [self.loginHostList removeAllObjects]; - if ([SFUserAccountManager sharedInstance].loginViewControllerConfig.showSettingsIcon) { + if ([SFUserAccountManager sharedInstance].loginViewControllerConfig.showSettingsIcon && [SFUserAccountManager sharedInstance].loginViewControllerConfig.showServerPicker) { [self.loginHostList addObject:production]; [self.loginHostList addObject:sandbox]; } diff --git a/libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Login/NativeLoginManager.swift b/libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Login/NativeLogin/NativeLoginManager.swift similarity index 100% rename from libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Login/NativeLoginManager.swift rename to libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Login/NativeLogin/NativeLoginManager.swift diff --git a/libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Login/NativeLoginManagerInternal.swift b/libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Login/NativeLogin/NativeLoginManagerInternal.swift similarity index 100% rename from libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Login/NativeLoginManagerInternal.swift rename to libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Login/NativeLogin/NativeLoginManagerInternal.swift diff --git a/libs/SalesforceSDKCore/SalesforceSDKCore/Classes/UserAccount/SFUserAccountManager+Internal.h b/libs/SalesforceSDKCore/SalesforceSDKCore/Classes/UserAccount/SFUserAccountManager+Internal.h index 45af0b2ce6..05c7928468 100644 --- a/libs/SalesforceSDKCore/SalesforceSDKCore/Classes/UserAccount/SFUserAccountManager+Internal.h +++ b/libs/SalesforceSDKCore/SalesforceSDKCore/Classes/UserAccount/SFUserAccountManager+Internal.h @@ -201,8 +201,6 @@ Set this block to handle presentation of the Authentication View Controller. - (SFSDKAuthRequest *)defaultAuthRequest; -- (SFSDKAuthRequest *)authRequestWithLoginHost:(nullable NSString *)loginHost appConfig:(nullable SFSDKAppConfig*)appConfig; - - (BOOL)loginWithCompletion:(nullable SFUserAccountManagerSuccessCallbackBlock)completionBlock failure:(nullable SFUserAccountManagerFailureCallbackBlock)failureBlock @@ -218,6 +216,8 @@ Set this block to handle presentation of the Authentication View Controller. - (SFSDKAuthRequest *)migrateRefreshAuthRequest:(SFSDKAppConfig *)newAppConfig; +- (void)restartAuthenticationForViewController:(SFLoginViewController *)loginViewController; + @end NS_ASSUME_NONNULL_END diff --git a/libs/SalesforceSDKCore/SalesforceSDKCore/Classes/UserAccount/SFUserAccountManager.m b/libs/SalesforceSDKCore/SalesforceSDKCore/Classes/UserAccount/SFUserAccountManager.m index ac0bbaf615..5318114449 100644 --- a/libs/SalesforceSDKCore/SalesforceSDKCore/Classes/UserAccount/SFUserAccountManager.m +++ b/libs/SalesforceSDKCore/SalesforceSDKCore/Classes/UserAccount/SFUserAccountManager.m @@ -1104,13 +1104,15 @@ - (void)loginViewController:(SFLoginViewController *)loginViewController didChan } - (void)loginViewControllerDidClearCache:(SFLoginViewController *)loginViewController { - [SFSDKWebViewStateManager clearCacheWithCompletionHandler:^{}]; - [self restartAuthenticationForViewController:loginViewController]; + [SFSDKWebViewStateManager clearCacheWithCompletionHandler:^{ + [self restartAuthenticationForViewController:loginViewController]; + }]; } - (void)loginViewControllerDidClearCookies:(SFLoginViewController *)loginViewController { - [SFSDKWebViewStateManager removeSessionForcefullyWithCompletionHandler:^{}]; - [self restartAuthenticationForViewController:loginViewController]; + [SFSDKWebViewStateManager removeSessionForcefullyWithCompletionHandler:^{ + [self restartAuthenticationForViewController:loginViewController]; + }]; } - (void)loginViewControllerDidReload:(SFLoginViewController *)loginViewController { diff --git a/libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Views/DevInfoViewController.swift b/libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Views/DevInfoViewController.swift new file mode 100644 index 0000000000..5958d53c15 --- /dev/null +++ b/libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Views/DevInfoViewController.swift @@ -0,0 +1,220 @@ +/* + Copyright (c) 2025-present, salesforce.com, inc. All rights reserved. + + Redistribution and use of this software in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of salesforce.com, inc. nor the names of its contributors may be used to + endorse or promote products derived from this software without specific prior written + permission of salesforce.com, inc. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import SwiftUI +import UIKit + +// MARK: - Data Model + +struct DevInfoRow: Identifiable { + let id = UUID() + let headline: String + let text: String +} + +struct DevInfoSection: Identifiable { + let id = UUID() + let title: String? + let rows: [DevInfoRow] +} + +// MARK: - SwiftUI View + +public struct DevInfoView: View { + @Environment(\.dismiss) private var dismiss + + let sections: [DevInfoSection] + let title: String + + @State private var showingAlert = false + @State private var alertTitle: String? + @State private var alertMessage = "" + + public init(infoData: [String], title: String) { + self.sections = Self.extractSections(from: infoData) + self.title = title + } + + private static func extractSections(from infoData: [String]) -> [DevInfoSection] { + var sections: [DevInfoSection] = [] + var currentSectionTitle: String? = nil + var currentRows: [DevInfoRow] = [] + var index = 0 + + while index < infoData.count { + let item = infoData[index] + + // Check if this is a section marker + if item.hasPrefix("section:") { + // Save previous section if it has rows + if !currentRows.isEmpty { + sections.append(DevInfoSection(title: currentSectionTitle, rows: currentRows)) + currentRows = [] + } + + // Extract section title (everything after "section:") + let sectionTitle = String(item.dropFirst("section:".count)) + currentSectionTitle = sectionTitle.isEmpty ? nil : sectionTitle + index += 1 + } else { + // This should be a headline, followed by text + if index + 1 < infoData.count { + let headline = infoData[index] + let text = infoData[index + 1] + currentRows.append(DevInfoRow(headline: headline, text: text)) + index += 2 + } else { + // Odd number of items, skip the last one + index += 1 + } + } + } + + // Add the last section if it has rows + if !currentRows.isEmpty { + sections.append(DevInfoSection(title: currentSectionTitle, rows: currentRows)) + } + + return sections + } + + public var body: some View { + VStack(spacing: 0) { + // Custom Title Bar + DevInfoTitleBarView(title: title) { + dismiss() + } + + // Info List with Sections + List { + ForEach(sections) { section in + if let sectionTitle = section.title { + // Section with title - collapsible + DisclosureGroup { + ForEach(section.rows) { row in + rowView(for: row) + } + } label: { + Text(sectionTitle) + .font(.headline) + .foregroundColor(.primary) + } + } else { + // Title-less section - no header + ForEach(section.rows) { row in + rowView(for: row) + } + } + } + } + .listStyle(.plain) + } + .alert(alertTitle ?? "", isPresented: $showingAlert) { + Button(SFSDKResourceUtils.localizedString("devInfoOKKey")) { + showingAlert = false + } + } message: { + Text(alertMessage) + } + } + + @ViewBuilder + private func rowView(for row: DevInfoRow) -> some View { + VStack(alignment: .leading, spacing: 4) { + Text(row.headline) + .font(.headline) + Text(row.text) + .font(.subheadline) + .foregroundColor(.secondary) + } + .padding(.vertical, 4) + .contentShape(Rectangle()) + .onTapGesture { + alertTitle = nil + alertMessage = row.text + showingAlert = true + } + } +} + +// MARK: - Title Bar View + +struct DevInfoTitleBarView: View { + let title: String + let onDismiss: () -> Void + + var body: some View { + ZStack { + Color(UIColor.salesforceBlue) + + HStack { + Spacer() + + Text(title) + .font(.system(size: 18, weight: .regular)) + .foregroundColor(.white) + + Spacer() + } + + HStack { + Spacer() + + Button(action: onDismiss) { + Image(systemName: "xmark") + .font(.system(size: 16, weight: .medium)) + .foregroundColor(.white) + .padding(12) + } + } + } + .frame(height: 44) + .frame(maxWidth: .infinity) + } +} + +// MARK: - Objective-C Bridge + +@objc(SFSDKDevInfoViewController) +public class DevInfoViewController: NSObject { + + @objc public static func makeViewController() -> UIViewController { + let infoData = SalesforceManager.shared.devSupportInfoList() + let title = SalesforceManager.shared.devInfoTitleString() + let view = DevInfoView(infoData: infoData, title: title) + let hostingController = UIHostingController(rootView: view) + + // Use pageSheet for slide-up presentation + #if !os(visionOS) + if let sheet = hostingController.sheetPresentationController { + sheet.detents = [.medium(), .large()] + sheet.prefersGrabberVisible = true + sheet.preferredCornerRadius = 16 + } + #endif + + return hostingController + } +} + diff --git a/libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Views/SFSDKDevInfoViewController.h b/libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Views/SFSDKDevInfoViewController.h deleted file mode 100644 index 3e7fdb72dc..0000000000 --- a/libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Views/SFSDKDevInfoViewController.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - Copyright (c) 2017-present, salesforce.com, inc. All rights reserved. - - Redistribution and use of this software in source and binary forms, with or without modification, - are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this list of conditions - and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, this list of - conditions and the following disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of salesforce.com, inc. nor the names of its contributors may be used to - endorse or promote products derived from this software without specific prior written - permission of salesforce.com, inc. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR - IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - -#import -NS_ASSUME_NONNULL_BEGIN -@interface SFSDKDevInfoViewController : UIViewController - -@end -NS_ASSUME_NONNULL_END diff --git a/libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Views/SFSDKDevInfoViewController.m b/libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Views/SFSDKDevInfoViewController.m deleted file mode 100644 index f7494a0114..0000000000 --- a/libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Views/SFSDKDevInfoViewController.m +++ /dev/null @@ -1,202 +0,0 @@ -/* - Copyright (c) 2017-present, salesforce.com, inc. All rights reserved. - - Redistribution and use of this software in source and binary forms, with or without modification, - are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this list of conditions - and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, this list of - conditions and the following disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of salesforce.com, inc. nor the names of its contributors may be used to - endorse or promote products derived from this software without specific prior written - permission of salesforce.com, inc. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR - IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - -#import "SFSDKDevInfoViewController.h" -#import "SalesforceSDKManager.h" -#import "SFSDKResourceUtils.h" -#import "UIColor+SFColors.h" - -// Nav bar -static CGFloat const kNavBarHeight = 44.0; -static CGFloat const kNavBarTitleFontSize = 18.0; - -// Resource keys -static NSString * const kDevInfoBackButtonTitleKey = @"devInfoBackButtonTitle"; -static NSString * const kDevInfoOKKey = @"devInfoOKKey"; - -@interface SFSDKDevInfoViewController () - -@property (nonatomic, strong) UINavigationBar *navBar; -@property (nonatomic, strong) UITableView *infoTable; -@property (nonatomic, strong) NSArray *infoData; - -@end - -@implementation SFSDKDevInfoViewController - -#pragma mark - Constructor - -- (instancetype) init -{ - self = [super init]; - if (self) { - self.infoData = [[SalesforceSDKManager sharedManager] getDevSupportInfos]; - } - return self; -} - -#pragma mark - Actions handlers - -- (void) backButtonClicked -{ - [self.presentingViewController dismissViewControllerAnimated:NO completion:NULL]; -} - -- (void) showAlert:(NSString*)message title:(NSString*)title -{ - UIAlertController* alert = [UIAlertController alertControllerWithTitle:title - message:message - preferredStyle:UIAlertControllerStyleAlert]; - - - UIAlertAction *okAction = [UIAlertAction - actionWithTitle:[SFSDKResourceUtils localizedString:kDevInfoOKKey] - style:UIAlertActionStyleDefault - handler:nil]; - - [alert addAction:okAction]; - [self presentViewController:alert animated:YES completion:nil]; -} - -#pragma mark - View layout - -- (void)loadView -{ - [super loadView]; - - // Background color - self.view.backgroundColor = [UIColor salesforceBlueColor]; - - // Nav bar - self.navBar = [self createNavBar]; - - // Table view - self.infoTable = [self createTableView]; -} - -- (UINavigationBar*) createNavBar -{ - UINavigationBar* navBar = [[UINavigationBar alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, kNavBarHeight)]; - navBar.delegate = self; - UINavigationItem *navItem = [[UINavigationItem alloc] initWithTitle:[[SalesforceSDKManager sharedManager] devInfoTitleString]]; - UIBarButtonItem *backItem = [[UIBarButtonItem alloc] initWithTitle:[SFSDKResourceUtils localizedString:kDevInfoBackButtonTitleKey] style:UIBarButtonItemStylePlain target:self action:@selector(backButtonClicked)]; - [navItem setLeftBarButtonItem:backItem]; - [navBar setItems:@[navItem] animated:YES]; - navBar.translucent = NO; - navBar.barTintColor = [UIColor salesforceBlueColor]; - navBar.tintColor = [UIColor salesforceNavBarTintColor]; - navBar.titleTextAttributes = @{NSForegroundColorAttributeName: [UIColor whiteColor], NSFontAttributeName:[UIFont systemFontOfSize:kNavBarTitleFontSize]}; - [self.view addSubview:navBar]; - return navBar; -} - -- (UITableView*) createTableView -{ - UITableView *infoTable = [[UITableView alloc] initWithFrame:CGRectZero]; - [infoTable setDataSource:self]; - [infoTable setDelegate:self]; - [self.view addSubview:infoTable]; - return infoTable; -} - -- (void)viewDidLoad -{ - [super viewDidLoad]; - [self layoutSubviews]; -} - -- (void)viewWillLayoutSubviews -{ - [self layoutSubviews]; - [super viewWillLayoutSubviews]; -} - -- (void)layoutSubviews -{ - [self layoutNavBar]; - [self layoutTableView]; -} - -- (UIInterfaceOrientationMask)supportedInterfaceOrientations { - return UIInterfaceOrientationMaskAll; -} - -- (CGFloat) belowFrame:(CGRect) frame { - return frame.origin.y + frame.size.height; -} - -- (void) layoutNavBar -{ - CGFloat x = 0; - CGFloat y = self.view.safeAreaInsets.top; - CGFloat w = self.view.bounds.size.width; - CGFloat h = kNavBarHeight; - self.navBar.frame = CGRectMake(x, y, w, h); -} - -- (void) layoutTableView -{ - CGFloat x = 0; - CGFloat y = [self belowFrame:self.navBar.frame]; - CGFloat w = self.view.bounds.size.width; - CGFloat h = self.view.bounds.size.height - y; - self.infoTable.frame = CGRectMake(x, y, w, h); -} - -#pragma mark - Table view data source - -- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView -{ - return 1; -} - -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section -{ - return self.infoData.count / 2; -} - -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath -{ - static NSString *CellIdentifier = @"Cell"; - - UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; - if (cell == nil) { - cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier]; - } - - cell.textLabel.text = self.infoData[indexPath.row * 2]; - cell.detailTextLabel.text = self.infoData[indexPath.row * 2 + 1]; - return cell; -} - -#pragma mark - Table view delegate - -- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath -{ - [self showAlert:self.infoData[indexPath.row * 2 + 1] title:nil]; -} - - -@end diff --git a/libs/SalesforceSDKCore/SalesforceSDKCore/SalesforceSDKCore.h b/libs/SalesforceSDKCore/SalesforceSDKCore/SalesforceSDKCore.h index 8c1b4d873d..4d7b09f7c4 100644 --- a/libs/SalesforceSDKCore/SalesforceSDKCore/SalesforceSDKCore.h +++ b/libs/SalesforceSDKCore/SalesforceSDKCore/SalesforceSDKCore.h @@ -2,7 +2,7 @@ SalesforceSDKCore.h SalesforceSDKCore - Created by Riley Crebs on Thu Apr 17 11:54:18 MDT 2025. + Created by Wolfgang Mathurin on Fri Nov 7 17:40:48 PST 2025. Copyright (c) 2025-present, salesforce.com, inc. All rights reserved. @@ -38,7 +38,6 @@ #import #import #import -#import #import #import #import diff --git a/libs/SalesforceSDKCore/SalesforceSDKCoreTests/AuthFlowTypesViewTests.swift b/libs/SalesforceSDKCore/SalesforceSDKCoreTests/AuthFlowTypesViewTests.swift new file mode 100644 index 0000000000..c00da7e2f7 --- /dev/null +++ b/libs/SalesforceSDKCore/SalesforceSDKCoreTests/AuthFlowTypesViewTests.swift @@ -0,0 +1,95 @@ +/* + Copyright (c) 2025-present, salesforce.com, inc. All rights reserved. + + Redistribution and use of this software in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of salesforce.com, inc. nor the names of its contributors may be used to + endorse or promote products derived from this software without specific prior written + permission of salesforce.com, inc. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import XCTest +import SwiftUI +@testable import SalesforceSDKCore + +class AuthFlowTypesViewTests: XCTestCase { + + var originalUseWebServerAuth: Bool! + var originalUseHybridAuth: Bool! + var originalSupportsWelcomeDiscovery: Bool! + + override func setUp() { + super.setUp() + + // Save original state to restore in tearDown + originalUseWebServerAuth = SalesforceManager.shared.useWebServerAuthentication + originalUseHybridAuth = SalesforceManager.shared.useHybridAuthentication + originalSupportsWelcomeDiscovery = SalesforceManager.shared.supportsWelcomeDiscovery + } + + override func tearDown() { + // Restore original state + SalesforceManager.shared.useWebServerAuthentication = originalUseWebServerAuth + SalesforceManager.shared.useHybridAuthentication = originalUseHybridAuth + SalesforceManager.shared.supportsWelcomeDiscovery = originalSupportsWelcomeDiscovery + + super.tearDown() + } + + func testAuthFlowTypesViewRendersSuccessfully() { + let expectation = XCTestExpectation(description: "View renders without crashing") + + // Set specific toggle states before creating the view + SalesforceManager.shared.useWebServerAuthentication = true + SalesforceManager.shared.useHybridAuthentication = false + SalesforceManager.shared.supportsWelcomeDiscovery = true + + let view = AuthFlowTypesView() + let hostingController = UIHostingController(rootView: view) + + // Create a window and add the view to trigger full rendering + let window = UIWindow(frame: CGRect(x: 0, y: 0, width: 375, height: 667)) + window.rootViewController = hostingController + window.makeKeyAndVisible() + + // Trigger view lifecycle + hostingController.viewWillAppear(false) + hostingController.viewDidAppear(false) + + // Give the view a moment to render + DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { + XCTAssertNotNil(hostingController.view, "View should be rendered") + + // Verify the toggle states are still as set (view was initialized with these values) + XCTAssertTrue(SalesforceManager.shared.useWebServerAuthentication, + "Web server authentication should be enabled") + XCTAssertFalse(SalesforceManager.shared.useHybridAuthentication, + "Hybrid authentication should be disabled") + XCTAssertTrue(SalesforceManager.shared.supportsWelcomeDiscovery, + "Welcome discovery should be enabled") + + // Clean up + window.rootViewController = nil + window.isHidden = true + + expectation.fulfill() + } + + wait(for: [expectation], timeout: 2.0) + } +} + diff --git a/libs/SalesforceSDKCore/SalesforceSDKCoreTests/BootConfigPickerViewControllerTests.swift b/libs/SalesforceSDKCore/SalesforceSDKCoreTests/BootConfigPickerViewControllerTests.swift new file mode 100644 index 0000000000..b8a639ebd4 --- /dev/null +++ b/libs/SalesforceSDKCore/SalesforceSDKCoreTests/BootConfigPickerViewControllerTests.swift @@ -0,0 +1,191 @@ +/* + Copyright (c) 2025-present, salesforce.com, inc. All rights reserved. + + Redistribution and use of this software in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of salesforce.com, inc. nor the names of its contributors may be used to + endorse or promote products derived from this software without specific prior written + permission of salesforce.com, inc. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import XCTest +import SwiftUI +@testable import SalesforceSDKCore + +class BootConfigPickerViewControllerTests: XCTestCase { + + var originalBootConfig: BootConfig? + var originalRuntimeSelector: BootConfigRuntimeSelector? + var originalOAuthClientID: String! + var originalOAuthCompletionURL: String! + var originalScopes: Set! + + override func setUp() { + super.setUp() + + // Save original state to restore in tearDown + originalBootConfig = SalesforceManager.shared.bootConfig + originalRuntimeSelector = SalesforceManager.shared.bootConfigRuntimeSelector + originalOAuthClientID = UserAccountManager.shared.oauthClientID + originalOAuthCompletionURL = UserAccountManager.shared.oauthCompletionURL + originalScopes = UserAccountManager.shared.scopes + } + + override func tearDown() { + // Restore original state + SalesforceManager.shared.bootConfig = originalBootConfig + SalesforceManager.shared.bootConfigRuntimeSelector = originalRuntimeSelector + UserAccountManager.shared.oauthClientID = originalOAuthClientID + UserAccountManager.shared.oauthCompletionURL = originalOAuthCompletionURL + UserAccountManager.shared.scopes = originalScopes + + super.tearDown() + } + + func testMakeViewControllerHasSheetPresentationConfiguration() { + let viewController = BootConfigPickerViewController.makeViewController { + // No-op callback + } + + #if !os(visionOS) + XCTAssertNotNil(viewController.sheetPresentationController, + "ViewController should have sheet presentation controller") + + if let sheet = viewController.sheetPresentationController { + XCTAssertTrue(sheet.detents.contains(.medium()) || sheet.detents.count > 0, + "Sheet should have medium detent configured") + XCTAssertTrue(sheet.prefersGrabberVisible, + "Sheet should show grabber") + XCTAssertEqual(sheet.preferredCornerRadius, 16, + "Sheet should have corner radius of 16") + } + #endif + } + + func testBootConfigPickerViewRendered() { + let expectation = XCTestExpectation(description: "View works with existing BootConfig") + + // Create a test BootConfig + let testConfig: [String: Any] = [ + "remoteAccessConsumerKey": "test_boot_config_key", + "oauthRedirectURI": "test://boot/callback", + "oauthScopes": ["api", "web", "refresh_token"], + "shouldAuthenticate": true + ] + SalesforceManager.shared.bootConfig = BootConfig(testConfig) + + let view = BootConfigPickerView { + // No-op callback + } + + let hostingController = UIHostingController(rootView: view) + + // Create a window and add the view controller + let window = UIWindow(frame: CGRect(x: 0, y: 0, width: 375, height: 667)) + window.rootViewController = hostingController + window.makeKeyAndVisible() + + // Trigger view lifecycle + hostingController.viewWillAppear(false) + hostingController.viewDidAppear(false) + + DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { + // Verify view rendered with BootConfig + XCTAssertNotNil(hostingController.view) + + // Clean up + window.rootViewController = nil + window.isHidden = true + + expectation.fulfill() + } + + wait(for: [expectation], timeout: 2.0) + } + + func testStaticConfigButtonAction() { + let expectation = XCTestExpectation(description: "Static config button triggers handler") + var completionCalled = false + + // Create view with test static config values + let view = BootConfigPickerView( + onConfigurationCompleted: { + completionCalled = true + expectation.fulfill() + }, + staticConsumerKey: "test_static_key", + staticCallbackUrl: "test://static/callback", + staticScopes: "api refresh_token web" + ) + + // Trigger the static config handler + view.handleStaticConfig() + + // Verify the completion callback was called + wait(for: [expectation], timeout: 2.0) + XCTAssertTrue(completionCalled, "Completion callback should be called") + + // Verify SalesforceManager was updated + XCTAssertNotNil(SalesforceManager.shared.bootConfig, "BootConfig should be set") + XCTAssertEqual(SalesforceManager.shared.bootConfig?.remoteAccessConsumerKey, "test_static_key") + XCTAssertEqual(SalesforceManager.shared.bootConfig?.oauthRedirectURI, "test://static/callback") + XCTAssertEqual(SalesforceManager.shared.bootConfig?.oauthScopes.sorted(), ["api", "refresh_token", "web"]) + XCTAssertNil(SalesforceManager.shared.bootConfigRuntimeSelector, "Runtime selector should be nil for static config") + + // Verify UserAccountManager was updated + XCTAssertEqual(UserAccountManager.shared.oauthClientID, "test_static_key") + XCTAssertEqual(UserAccountManager.shared.oauthCompletionURL, "test://static/callback") + XCTAssertEqual(UserAccountManager.shared.scopes.sorted(), ["api", "refresh_token", "web"]) + } + + func testDynamicConfigButtonAction() { + let expectation = XCTestExpectation(description: "Dynamic config button triggers handler") + var completionCalled = false + + let view = BootConfigPickerView( + onConfigurationCompleted: { + completionCalled = true + expectation.fulfill() + }, + dynamicConsumerKey: "test_dynamic_key", + dynamicCallbackUrl: "test://dynamic/callback", + dynamicScopes: "api id" + ) + + // Trigger the dynamic config handler + view.handleDynamicBootconfig() + + wait(for: [expectation], timeout: 2.0) + XCTAssertTrue(completionCalled, "Completion callback should be called") + + // Verify runtime selector was set + XCTAssertNotNil(SalesforceManager.shared.bootConfigRuntimeSelector, "Runtime selector should be set for dynamic config") + + // Test the runtime selector + let selectorExpectation = XCTestExpectation(description: "Runtime selector provides config") + SalesforceManager.shared.bootConfigRuntimeSelector?("https://loginhost.salesforce.com") { bootConfig in + XCTAssertNotNil(bootConfig, "BootConfig should be provided by runtime selector") + XCTAssertEqual(bootConfig?.remoteAccessConsumerKey, "test_dynamic_key") + XCTAssertEqual(bootConfig?.oauthRedirectURI, "test://dynamic/callback") + XCTAssertEqual(bootConfig?.oauthScopes.sorted(), ["api", "id"]) + selectorExpectation.fulfill() + } + + wait(for: [selectorExpectation], timeout: 1.0) + } +} + diff --git a/libs/SalesforceSDKCore/SalesforceSDKCoreTests/DevInfoViewControllerTests.swift b/libs/SalesforceSDKCore/SalesforceSDKCoreTests/DevInfoViewControllerTests.swift new file mode 100644 index 0000000000..7a4ee51e0f --- /dev/null +++ b/libs/SalesforceSDKCore/SalesforceSDKCoreTests/DevInfoViewControllerTests.swift @@ -0,0 +1,518 @@ +/* + Copyright (c) 2025-present, salesforce.com, inc. All rights reserved. + + Redistribution and use of this software in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of salesforce.com, inc. nor the names of its contributors may be used to + endorse or promote products derived from this software without specific prior written + permission of salesforce.com, inc. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import XCTest +import SwiftUI +@testable import SalesforceSDKCore + +class DevInfoViewControllerTests: XCTestCase { + + func testMakeViewControllerHasSheetPresentationConfiguration() { + let viewController = DevInfoViewController.makeViewController() + + #if !os(visionOS) + XCTAssertNotNil(viewController.sheetPresentationController, + "ViewController should have sheet presentation controller") + + if let sheet = viewController.sheetPresentationController { + XCTAssertTrue(sheet.detents.contains(.medium()) || sheet.detents.count > 0, + "Sheet should have detents configured") + XCTAssertTrue(sheet.prefersGrabberVisible, + "Sheet should show grabber") + XCTAssertEqual(sheet.preferredCornerRadius, 16, + "Sheet should have corner radius of 16") + } + #endif + } + + func testViewInitializationWithEmptyData() { + let view = DevInfoView(infoData: [], title: "Test Title") + + XCTAssertEqual(view.sections.count, 0, "Empty data should result in no sections") + XCTAssertEqual(view.title, "Test Title", "Title should be preserved") + } + + func testViewInitializationWithData() { + let infoData = ["Key", "Value"] + let view = DevInfoView(infoData: infoData, title: "My Title") + + XCTAssertEqual(view.sections.count, 1, "Should create one section") + XCTAssertEqual(view.title, "My Title", "Title should be preserved") + } + + func testViewInitializationWithMultipleSections() { + let infoData = [ + "section:Section 1", + "Key1", "Value1", + "section:Section 2", + "Key2", "Value2" + ] + let view = DevInfoView(infoData: infoData, title: "Test") + + XCTAssertEqual(view.sections.count, 2, "Should create two sections") + } + + func testDevInfoRowHasUniqueIDs() { + let row1 = DevInfoRow(headline: "Test", text: "Value") + let row2 = DevInfoRow(headline: "Test", text: "Value") + + XCTAssertNotEqual(row1.id, row2.id, + "Each DevInfoRow should have a unique ID") + } + + func testDevInfoRowStoresData() { + let headline = "Test Headline" + let text = "Test Text" + let row = DevInfoRow(headline: headline, text: text) + + XCTAssertEqual(row.headline, headline, "Headline should be stored") + XCTAssertEqual(row.text, text, "Text should be stored") + } + + func testDevInfoSectionHasUniqueIDs() { + let section1 = DevInfoSection(title: "Section", rows: []) + let section2 = DevInfoSection(title: "Section", rows: []) + + XCTAssertNotEqual(section1.id, section2.id, + "Each DevInfoSection should have a unique ID") + } + + func testDevInfoSectionWithTitle() { + let title = "Test Section" + let rows = [DevInfoRow(headline: "Key", text: "Value")] + let section = DevInfoSection(title: title, rows: rows) + + XCTAssertEqual(section.title, title, "Title should be stored") + XCTAssertEqual(section.rows.count, 1, "Rows should be stored") + XCTAssertEqual(section.rows[0].headline, "Key") + } + + func testDevInfoSectionWithoutTitle() { + let rows = [DevInfoRow(headline: "Key", text: "Value")] + let section = DevInfoSection(title: nil, rows: rows) + + XCTAssertNil(section.title, "Title should be nil") + XCTAssertEqual(section.rows.count, 1, "Rows should be stored") + } + + func testDevInfoSectionWithMultipleRows() { + let rows = [ + DevInfoRow(headline: "Key1", text: "Value1"), + DevInfoRow(headline: "Key2", text: "Value2"), + DevInfoRow(headline: "Key3", text: "Value3") + ] + let section = DevInfoSection(title: "Test", rows: rows) + + XCTAssertEqual(section.rows.count, 3, "Should store all rows") + XCTAssertEqual(section.rows[0].headline, "Key1") + XCTAssertEqual(section.rows[1].headline, "Key2") + XCTAssertEqual(section.rows[2].headline, "Key3") + } + + func testDevInfoSectionWithEmptyRows() { + let section = DevInfoSection(title: "Empty", rows: []) + + XCTAssertEqual(section.rows.count, 0, "Should handle empty rows array") + } + + func testExtractSectionsWithNoSectionMarkers() { + let infoData = [ + "Key1", "Value1", + "Key2", "Value2", + "Key3", "Value3" + ] + + let sections = DevInfoView.testExtractSections(from: infoData) + + XCTAssertEqual(sections.count, 1, "Should have exactly one section") + XCTAssertNil(sections[0].title, "Section should have no title") + XCTAssertEqual(sections[0].rows.count, 3, "Section should have 3 rows") + + XCTAssertEqual(sections[0].rows[0].headline, "Key1") + XCTAssertEqual(sections[0].rows[0].text, "Value1") + XCTAssertEqual(sections[0].rows[1].headline, "Key2") + XCTAssertEqual(sections[0].rows[1].text, "Value2") + XCTAssertEqual(sections[0].rows[2].headline, "Key3") + XCTAssertEqual(sections[0].rows[2].text, "Value3") + } + + func testExtractSectionsWithSectionAtStart() { + let infoData = [ + "section:First Section", + "Key1", "Value1", + "Key2", "Value2" + ] + + let sections = DevInfoView.testExtractSections(from: infoData) + + XCTAssertEqual(sections.count, 1, "Should have exactly one section") + XCTAssertEqual(sections[0].title, "First Section", "Section should have correct title") + XCTAssertEqual(sections[0].rows.count, 2, "Section should have 2 rows") + + XCTAssertEqual(sections[0].rows[0].headline, "Key1") + XCTAssertEqual(sections[0].rows[0].text, "Value1") + + XCTAssertEqual(sections[0].rows[1].headline, "Key2") + XCTAssertEqual(sections[0].rows[1].text, "Value2") + + } + + func testExtractSectionsWithSectionInMiddle() { + let infoData = [ + "Key1", "Value1", + "section:Middle Section", + "Key2", "Value2" + ] + + let sections = DevInfoView.testExtractSections(from: infoData) + + XCTAssertEqual(sections.count, 2, "Should have exactly two sections") + + // First section (no title) + XCTAssertNil(sections[0].title, "First section should have no title") + XCTAssertEqual(sections[0].rows.count, 1, "First section should have 1 row") + XCTAssertEqual(sections[0].rows[0].headline, "Key1") + XCTAssertEqual(sections[0].rows[0].text, "Value1") + + // Second section (with title) + XCTAssertEqual(sections[1].title, "Middle Section", "Second section should have correct title") + XCTAssertEqual(sections[1].rows.count, 1, "Second section should have 1 row") + XCTAssertEqual(sections[1].rows[0].headline, "Key2") + XCTAssertEqual(sections[1].rows[0].text, "Value2") + } + + func testExtractSectionsWithMultipleSections() { + let infoData = [ + "section:Section 1", + "Key1", "Value1", + "Key2", "Value2", + "section:Section 2", + "Key3", "Value3", + "section:Section 3", + "Key4", "Value4", + "Key5", "Value5", + "Key6", "Value6" + ] + + let sections = DevInfoView.testExtractSections(from: infoData) + + XCTAssertEqual(sections.count, 3, "Should have exactly three sections") + + // Section 1 + XCTAssertEqual(sections[0].title, "Section 1") + XCTAssertEqual(sections[0].rows.count, 2) + XCTAssertEqual(sections[0].rows[0].headline, "Key1") + XCTAssertEqual(sections[0].rows[0].text, "Value1") + XCTAssertEqual(sections[0].rows[1].headline, "Key2") + XCTAssertEqual(sections[0].rows[1].text, "Value2") + + // Section 2 + XCTAssertEqual(sections[1].title, "Section 2") + XCTAssertEqual(sections[1].rows.count, 1) + XCTAssertEqual(sections[1].rows[0].headline, "Key3") + XCTAssertEqual(sections[1].rows[0].text, "Value3") + + // Section 3 + XCTAssertEqual(sections[2].title, "Section 3") + XCTAssertEqual(sections[2].rows.count, 3) + XCTAssertEqual(sections[2].rows[0].headline, "Key4") + XCTAssertEqual(sections[2].rows[0].text, "Value4") + XCTAssertEqual(sections[2].rows[1].headline, "Key5") + XCTAssertEqual(sections[2].rows[1].text, "Value5") + XCTAssertEqual(sections[2].rows[2].headline, "Key6") + XCTAssertEqual(sections[2].rows[2].text, "Value6") + } + + func testExtractSectionsWithEmptyArray() { + let infoData: [String] = [] + + let sections = DevInfoView.testExtractSections(from: infoData) + + XCTAssertEqual(sections.count, 0, "Should have no sections for empty array") + } + + func testExtractSectionsWithOddNumberOfItems() { + let infoData = [ + "Key1", "Value1", + "Key2" // Odd item, should be skipped + ] + + let sections = DevInfoView.testExtractSections(from: infoData) + + XCTAssertEqual(sections.count, 1, "Should have exactly one section") + XCTAssertEqual(sections[0].rows.count, 1, "Should only have 1 complete row (odd item skipped)") + XCTAssertEqual(sections[0].rows[0].headline, "Key1") + XCTAssertEqual(sections[0].rows[0].text, "Value1") + } + + func testExtractSectionsWithSectionButNoRows() { + let infoData = [ + "Key1", "Value1", + "section:Empty Section", + "section:Another Section", + "Key2", "Value2" + ] + + let sections = DevInfoView.testExtractSections(from: infoData) + + XCTAssertEqual(sections.count, 2, "Should have two sections (empty section should be skipped)") + + // First section (no title) + XCTAssertNil(sections[0].title) + XCTAssertEqual(sections[0].rows.count, 1) + + // Second section (Another Section) - Empty Section was skipped + XCTAssertEqual(sections[1].title, "Another Section") + XCTAssertEqual(sections[1].rows.count, 1) + } + + func testExtractSectionsWithEmptySectionTitle() { + let infoData = [ + "section:", // Empty section title + "Key1", "Value1" + ] + + let sections = DevInfoView.testExtractSections(from: infoData) + + XCTAssertEqual(sections.count, 1, "Should have exactly one section") + XCTAssertNil(sections[0].title, "Empty section title should become nil") + XCTAssertEqual(sections[0].rows.count, 1) + } + + func testExtractSectionsWithRealWorldData() { + let infoData = [ + "SDK Version", "12.0.0", + "App Type", "Native", + "section:Auth Config", + "Use Web Server Authentication", "YES", + "Use Hybrid Authentication", "NO", + "section:Current User", + "Username", "test@example.com", + "Consumer Key", "test_key", + "Instance URL", "https://test.salesforce.com" + ] + + let sections = DevInfoView.testExtractSections(from: infoData) + + XCTAssertEqual(sections.count, 3, "Should have three sections") + + // First section (no title) + XCTAssertNil(sections[0].title) + XCTAssertEqual(sections[0].rows.count, 2) + XCTAssertEqual(sections[0].rows[0].headline, "SDK Version") + XCTAssertEqual(sections[0].rows[1].headline, "App Type") + + // Auth Config section + XCTAssertEqual(sections[1].title, "Auth Config") + XCTAssertEqual(sections[1].rows.count, 2) + XCTAssertEqual(sections[1].rows[0].headline, "Use Web Server Authentication") + XCTAssertEqual(sections[1].rows[0].text, "YES") + + // Current User section + XCTAssertEqual(sections[2].title, "Current User") + XCTAssertEqual(sections[2].rows.count, 3) + XCTAssertEqual(sections[2].rows[0].headline, "Username") + XCTAssertEqual(sections[2].rows[0].text, "test@example.com") + } + + func testDevInfoViewCreationWithSimpleData() { + let infoData = ["Key", "Value"] + let view = DevInfoView(infoData: infoData, title: "Test Title") + + // Verify view properties + XCTAssertEqual(view.title, "Test Title") + XCTAssertEqual(view.sections.count, 1) + XCTAssertNil(view.sections[0].title) + XCTAssertEqual(view.sections[0].rows.count, 1) + XCTAssertEqual(view.sections[0].rows[0].headline, "Key") + XCTAssertEqual(view.sections[0].rows[0].text, "Value") + } + + func testDevInfoViewCreationWithMultipleRows() { + let infoData = [ + "Key1", "Value1", + "Key2", "Value2", + "Key3", "Value3" + ] + let view = DevInfoView(infoData: infoData, title: "Multi Row Test") + + XCTAssertEqual(view.title, "Multi Row Test") + XCTAssertEqual(view.sections.count, 1) + XCTAssertEqual(view.sections[0].rows.count, 3) + + // Verify all rows + XCTAssertEqual(view.sections[0].rows[0].headline, "Key1") + XCTAssertEqual(view.sections[0].rows[0].text, "Value1") + XCTAssertEqual(view.sections[0].rows[1].headline, "Key2") + XCTAssertEqual(view.sections[0].rows[1].text, "Value2") + XCTAssertEqual(view.sections[0].rows[2].headline, "Key3") + XCTAssertEqual(view.sections[0].rows[2].text, "Value3") + } + + func testDevInfoViewCreationWithSections() { + let infoData = [ + "section:Section 1", + "Key1", "Value1", + "section:Section 2", + "Key2", "Value2" + ] + let view = DevInfoView(infoData: infoData, title: "Sectioned Data") + + XCTAssertEqual(view.title, "Sectioned Data") + XCTAssertEqual(view.sections.count, 2) + + // Section 1 + XCTAssertEqual(view.sections[0].title, "Section 1") + XCTAssertEqual(view.sections[0].rows.count, 1) + XCTAssertEqual(view.sections[0].rows[0].headline, "Key1") + + // Section 2 + XCTAssertEqual(view.sections[1].title, "Section 2") + XCTAssertEqual(view.sections[1].rows.count, 1) + XCTAssertEqual(view.sections[1].rows[0].headline, "Key2") + } + + func testDevInfoViewCreationWithEmptyData() { + let view = DevInfoView(infoData: [], title: "Empty") + + XCTAssertEqual(view.title, "Empty") + XCTAssertEqual(view.sections.count, 0) + } + + func testDevInfoViewCreationWithMixedSections() { + let infoData = [ + "Global1", "Value1", + "Global2", "Value2", + "section:Named Section", + "Sectioned1", "SValue1", + "Sectioned2", "SValue2", + "section:Another Section", + "Another1", "AValue1" + ] + let view = DevInfoView(infoData: infoData, title: "Mixed") + + XCTAssertEqual(view.sections.count, 3) + + // First section (no title) + XCTAssertNil(view.sections[0].title) + XCTAssertEqual(view.sections[0].rows.count, 2) + + // Named Section + XCTAssertEqual(view.sections[1].title, "Named Section") + XCTAssertEqual(view.sections[1].rows.count, 2) + + // Another Section + XCTAssertEqual(view.sections[2].title, "Another Section") + XCTAssertEqual(view.sections[2].rows.count, 1) + } + + func testDevInfoViewWithRealWorldData() { + let infoData = [ + "SDK Version", "13.0.0", + "App Type", "Native iOS", + "section:Current User", + "Username", "test@salesforce.com", + "User ID", "005xx000001X8Uz", + "Org ID", "00Dxx0000001gPL", + "Instance URL", "https://na1.salesforce.com", + "section:OAuth Configuration", + "Client ID", "3MVG9PhR6g6B7ps6aoQEJ8h_", + "Redirect URI", "testapp://mobilesdk/detect/oauth/done", + "Scopes", "api web refresh_token" + ] + let view = DevInfoView(infoData: infoData, title: "Dev Support") + + XCTAssertEqual(view.title, "Dev Support") + XCTAssertEqual(view.sections.count, 3) + + // General info section (no title) + XCTAssertNil(view.sections[0].title) + XCTAssertEqual(view.sections[0].rows.count, 2) + XCTAssertEqual(view.sections[0].rows[0].headline, "SDK Version") + XCTAssertEqual(view.sections[0].rows[0].text, "13.0.0") + + // Current User section + XCTAssertEqual(view.sections[1].title, "Current User") + XCTAssertEqual(view.sections[1].rows.count, 4) + XCTAssertEqual(view.sections[1].rows[0].headline, "Username") + XCTAssertEqual(view.sections[1].rows[0].text, "test@salesforce.com") + + // OAuth Configuration section + XCTAssertEqual(view.sections[2].title, "OAuth Configuration") + XCTAssertEqual(view.sections[2].rows.count, 3) + XCTAssertEqual(view.sections[2].rows[2].headline, "Scopes") + XCTAssertEqual(view.sections[2].rows[2].text, "api web refresh_token") + } + + func testDevInfoViewRendersRealWorldData() { + let expectation = XCTestExpectation(description: "View renders real-world SDK data") + + let infoData = [ + "SDK Version", "13.0.0", + "App Type", "Native iOS", + "section:Current User", + "Username", "test@salesforce.com", + "User ID", "005xx000001X8Uz", + "Org ID", "00Dxx0000001gPL", + "Instance URL", "https://na1.salesforce.com", + "section:OAuth Configuration", + "Client ID", "3MVG9PhR6g6B7ps6aoQEJ8h_", + "Redirect URI", "testapp://mobilesdk/detect/oauth/done", + "Scopes", "api web refresh_token" + ] + let view = DevInfoView(infoData: infoData, title: "Dev Support") + let hostingController = UIHostingController(rootView: view) + + let window = UIWindow(frame: CGRect(x: 0, y: 0, width: 375, height: 667)) + window.rootViewController = hostingController + window.makeKeyAndVisible() + + hostingController.viewWillAppear(false) + hostingController.viewDidAppear(false) + + DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) { + // Verify the complex real-world view renders successfully + XCTAssertNotNil(hostingController.view) + XCTAssertNotNil(hostingController.view.superview) + + // Clean up + window.rootViewController = nil + window.isHidden = true + + expectation.fulfill() + } + + wait(for: [expectation], timeout: 2.0) + } +} + +// MARK: - Test Helper Extension + +extension DevInfoView { + /// Public test-only method to expose extractSections for testing + static func testExtractSections(from infoData: [String]) -> [DevInfoSection] { + return DevInfoView(infoData: infoData, title: "").sections + } +} + diff --git a/libs/SalesforceSDKCore/SalesforceSDKCoreTests/DomainDiscoveryCoordinatorTests.swift b/libs/SalesforceSDKCore/SalesforceSDKCoreTests/DomainDiscoveryCoordinatorTests.swift index c941fe16ce..ac85cb7580 100644 --- a/libs/SalesforceSDKCore/SalesforceSDKCoreTests/DomainDiscoveryCoordinatorTests.swift +++ b/libs/SalesforceSDKCore/SalesforceSDKCoreTests/DomainDiscoveryCoordinatorTests.swift @@ -25,7 +25,7 @@ final class DomainDiscoveryCoordinatorTests: XCTestCase { // Given let mockWebView = MockWKWebView() let coordinator = DomainDiscoveryCoordinator() - let credentials = OAuthCredentials(identifier: "test", clientId: "client123", encrypted: false) + let credentials = OAuthCredentials(identifier: "test", clientId: "client123", encrypted: false)! let expectedDomain = "foo.my.salesforce.com" let mockDomain = "https://\(expectedDomain)" @@ -35,7 +35,7 @@ final class DomainDiscoveryCoordinatorTests: XCTestCase { mockWebView.simulatedCallbackURL = callbackURL // When - coordinator.runMyDomainsDiscovery(on: mockWebView, with: credentials!) + coordinator.runMyDomainsDiscovery(on: mockWebView, with: credentials) let results = coordinator.handle(action: mockWebView.mockAction!) // Then @@ -47,14 +47,14 @@ final class DomainDiscoveryCoordinatorTests: XCTestCase { // Given let mockWebView = MockWKWebView() let coordinator = DomainDiscoveryCoordinator() - let credentials = OAuthCredentials(identifier: "test", clientId: "client123", encrypted: false) + let credentials = OAuthCredentials(identifier: "test", clientId: "client123", encrypted: false)! let expectedLoginHint = "testuser@example.com" let callbackURLString = "sfdc://discocallback?login_hint=\(expectedLoginHint.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!)" let callbackURL = URL(string: callbackURLString)! mockWebView.simulatedCallbackURL = callbackURL // When - coordinator.runMyDomainsDiscovery(on: mockWebView, with: credentials!) + coordinator.runMyDomainsDiscovery(on: mockWebView, with: credentials) let results = coordinator.handle(action: mockWebView.mockAction!) // Then @@ -65,7 +65,7 @@ final class DomainDiscoveryCoordinatorTests: XCTestCase { // Given let mockWebView = MockWKWebView() let coordinator = DomainDiscoveryCoordinator() - let credentials = OAuthCredentials(identifier: "test", clientId: "client123", encrypted: false) + let credentials = OAuthCredentials(identifier: "test", clientId: "client123", encrypted: false)! let expectedDomain = "foo.my.salesforce.com" let mockDomain = "https://\(expectedDomain)" let callbackURLString = "sfdc://discocallback?my_domain=\(mockDomain.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!)" @@ -73,7 +73,7 @@ final class DomainDiscoveryCoordinatorTests: XCTestCase { mockWebView.simulatedCallbackURL = callbackURL // When - coordinator.runMyDomainsDiscovery(on: mockWebView, with: credentials!) + coordinator.runMyDomainsDiscovery(on: mockWebView, with: credentials) let results = coordinator.handle(action: mockWebView.mockAction!) // Then @@ -84,13 +84,13 @@ final class DomainDiscoveryCoordinatorTests: XCTestCase { // Given let mockWebView = MockWKWebView() let coordinator = DomainDiscoveryCoordinator() - let credentials = OAuthCredentials(identifier: "test", clientId: "client123", encrypted: false) + let credentials = OAuthCredentials(identifier: "test", clientId: "client123", encrypted: false)! let callbackURLString = "sfdc://discocallback?my_domain=&login_hint=" let callbackURL = URL(string: callbackURLString)! mockWebView.simulatedCallbackURL = callbackURL // When - coordinator.runMyDomainsDiscovery(on: mockWebView, with: credentials!) + coordinator.runMyDomainsDiscovery(on: mockWebView, with: credentials) let results = coordinator.handle(action: mockWebView.mockAction!) // Then @@ -102,12 +102,12 @@ final class DomainDiscoveryCoordinatorTests: XCTestCase { // Given let mockWebView = MockWKWebView() let coordinator = DomainDiscoveryCoordinator() - let credentials = OAuthCredentials(identifier: "test", clientId: "client123", encrypted: false) + let credentials = OAuthCredentials(identifier: "test", clientId: "client123", encrypted: false)! let nonCallbackURL = URL(string: "https://example.com")! mockWebView.simulatedCallbackURL = nonCallbackURL // When - coordinator.runMyDomainsDiscovery(on: mockWebView, with: credentials!) + coordinator.runMyDomainsDiscovery(on: mockWebView, with: credentials) let results = coordinator.handle(action: mockWebView.mockAction!) // Then @@ -139,7 +139,7 @@ final class DomainDiscoveryCoordinatorTests: XCTestCase { // Given let mockWebView = MockWKWebView() let coordinator = DomainDiscoveryCoordinator() - let credentials = OAuthCredentials(identifier: "test", clientId: "client123", encrypted: false) + let credentials = OAuthCredentials(identifier: "test", clientId: "client123", encrypted: false)! let expectedDomain = "foo.my.salesforce.com" let mockDomain = "https://\(expectedDomain)" let expectedLoginHint = "testuser@example.com" @@ -148,7 +148,7 @@ final class DomainDiscoveryCoordinatorTests: XCTestCase { mockWebView.simulatedCallbackURL = callbackURL // When - coordinator.runMyDomainsDiscovery(on: mockWebView, with: credentials!) + coordinator.runMyDomainsDiscovery(on: mockWebView, with: credentials) let results = coordinator.handle(action: mockWebView.mockAction!) // Then diff --git a/libs/SalesforceSDKCore/SalesforceSDKCoreTests/SalesforceSDKIdentityTests.h b/libs/SalesforceSDKCore/SalesforceSDKCoreTests/SalesforceSDKIdentityTests.h deleted file mode 100644 index b4f6a19dcc..0000000000 --- a/libs/SalesforceSDKCore/SalesforceSDKCoreTests/SalesforceSDKIdentityTests.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - Copyright (c) 2012-present, salesforce.com, inc. All rights reserved. - - Redistribution and use of this software in source and binary forms, with or without modification, - are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this list of conditions - and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, this list of - conditions and the following disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of salesforce.com, inc. nor the names of its contributors may be used to - endorse or promote products derived from this software without specific prior written - permission of salesforce.com, inc. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR - IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#import -#import "SFIdentityCoordinator.h" -@class SFSDKTestRequestListener; -@class SFOAuthCoordinator; - - -/** - * The unit tests associated with SFIdentityCoordinator requests. - */ -@interface SalesforceSDKIdentityTests : XCTestCase -{ - SFSDKTestRequestListener *_requestListener; -} - -@end diff --git a/libs/SalesforceSDKCore/SalesforceSDKCoreTests/SalesforceSDKIdentityTests.m b/libs/SalesforceSDKCore/SalesforceSDKCoreTests/SalesforceSDKIdentityTests.m index cd7aa89d68..beb558455f 100644 --- a/libs/SalesforceSDKCore/SalesforceSDKCoreTests/SalesforceSDKIdentityTests.m +++ b/libs/SalesforceSDKCore/SalesforceSDKCoreTests/SalesforceSDKIdentityTests.m @@ -22,145 +22,84 @@ WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#import "SalesforceSDKIdentityTests.h" -#import "SFSDKTestRequestListener.h" -#import "TestSetupUtils.h" -#import "SFOAuthCoordinator.h" +#import #import "SFIdentityCoordinator.h" #import "SFUserAccountManager.h" #import "SFIdentityData.h" -/** - * Private interface for this tests module. - */ -@interface SalesforceSDKIdentityTests() -/** - * Synchronous wrapper around the asynchronous request to the identity service. - */ -- (void)sendSyncIdentityRequest; +@interface SFIdentityCoordinator () -/** - * Does a cursory pass on the identity data, to sanity check values. - */ -- (void)validateIdentityData; +- (void)processResponse:(NSData *)data; - -@property (nonatomic,strong) SFUserAccount *account; - -@property (nonatomic,strong) XCTestExpectation *expectation; - -@property (nonatomic,assign) BOOL requestHasFailed; +@end +@interface SalesforceSDKIdentityTests : XCTestCase @end -static NSException *authException = nil; - @implementation SalesforceSDKIdentityTests -#pragma mark - Test / class setup - -+ (void)setUp -{ - @try { - [TestSetupUtils populateAuthCredentialsFromConfigFileForClass:[self class]]; - [TestSetupUtils synchronousAuthRefresh]; - - } - @catch (NSException *exception) { - authException = exception; - } - [super setUp]; -} - -- (void)setUp -{ - if (authException) { - XCTFail(@"Setting up authentication failed: %@", authException); - } - - // Set-up code here. - _requestListener = nil; - self.expectation = nil; - self.account = nil; - [super setUp]; -} - -#pragma mark - Helper methods - -- (void)sendSyncIdentityRequest -{ - XCTestExpectation *expectation = [self expectationWithDescription:@"retrieveIdentityData"]; - self.expectation = expectation; - self.account = [SFUserAccountManager sharedInstance].currentUser; - SFIdentityCoordinator *idCoordinator = [[SFIdentityCoordinator alloc] initWithCredentials:self.account.credentials]; - idCoordinator.delegate = self; - [idCoordinator initiateIdentityDataRetrieval]; - [self waitForExpectations:@[expectation] timeout:10]; - XCTAssertFalse(self.requestHasFailed); - -} - -#pragma mark - Tests - /** - * Tests that identity data can be successfully retrieved with valid credentials. + * Tests that identity data can be successfully processed */ -- (void)testRetrieveIdentitySuccess +- (void)testProcessIdentityData { - [self sendSyncIdentityRequest]; - [self validateIdentityData]; -} - + NSString *identityResponse = @"{\"id\":\"https://login.salesforce.com/id/some-org-id/some-user-id\",\"asserted_user\":true,\"user_id\":\"some-user-id\",\"organization_id\":\"some-org-id\",\"username\":\"user@example.com\",\"nick_name\":\"nickname\",\"display_name\":\"Example User\",\"email\":\"user@example.com\",\"email_verified\":true,\"first_name\":\"Example\",\"last_name\":\"User\",\"timezone\":\"America/Los_Angeles\",\"photos\":{\"picture\":\"https://example.com/profilephoto/full\",\"thumbnail\":\"https://example.com/profilephoto/thumb\"},\"addr_street\":null,\"addr_city\":null,\"addr_state\":null,\"addr_country\":null,\"addr_zip\":null,\"mobile_phone\":null,\"mobile_phone_verified\":false,\"is_lightning_login_user\":false,\"status\":{\"created_date\":null,\"body\":null},\"urls\":{\"enterprise\":\"https://example.my.salesforce.com/services/Soap/c/some-version/some-org-id\",\"metadata\":\"https://example.my.salesforce.com/services/Soap/m/some-version/some-org-id\",\"partner\":\"https://example.my.salesforce.com/services/Soap/u/some-version/some-org-id\",\"rest\":\"https://example.my.salesforce.com/services/data/vsome-version/\",\"sobjects\":\"https://example.my.salesforce.com/services/data/vsome-version/sobjects/\",\"search\":\"https://example.my.salesforce.com/services/data/vsome-version/search/\",\"query\":\"https://example.my.salesforce.com/services/data/vsome-version/query/\",\"recent\":\"https://example.my.salesforce.com/services/data/vsome-version/recent/\",\"tooling_soap\":\"https://example.my.salesforce.com/services/Soap/T/some-version/some-org-id\",\"tooling_rest\":\"https://example.my.salesforce.com/services/data/vsome-version/tooling/\",\"profile\":\"https://example.my.salesforce.com/some-user-id\",\"feeds\":\"https://example.my.salesforce.com/services/data/vsome-version/chatter/feeds\",\"groups\":\"https://example.my.salesforce.com/services/data/vsome-version/chatter/groups\",\"users\":\"https://example.my.salesforce.com/services/data/vsome-version/chatter/users\",\"feed_items\":\"https://example.my.salesforce.com/services/data/vsome-version/chatter/feed-items\",\"feed_elements\":\"https://example.my.salesforce.com/services/data/vsome-version/chatter/feed-elements\",\"custom_domain\":\"https://example.my.salesforce.com\"},\"active\":true,\"user_type\":\"STANDARD\",\"language\":\"en_US\",\"locale\":\"en_US\",\"utcOffset\":-28800000,\"last_modified_date\":\"2024-12-23T18:40:50Z\"}"; -- (void)identityCoordinator:(nonnull SFIdentityCoordinator *)coordinator didFailWithError:(nonnull NSError *)error { - self.requestHasFailed = true; - [self.expectation fulfill]; -} - -- (void)identityCoordinatorRetrievedData:(nonnull SFIdentityCoordinator *)coordinator { - self.account.idData = coordinator.idData; - [self.expectation fulfill]; -} - -#pragma mark - Private helper methods - -- (void)validateIdentityData -{ - SFIdentityData *idData = [SFUserAccountManager sharedInstance].currentUser.idData; - XCTAssertNotNil(idData, @"Identity data is nil."); - XCTAssertNotNil(idData.dictRepresentation, @"idData.dictRepresentation should not be nil."); - XCTAssertNotNil(idData.idUrl, @"idUrl should not be nil."); - XCTAssertTrue(idData.assertedUser, @"assertedUser should be true."); - XCTAssertNotNil(idData.userId, @"userId should not be nil."); - XCTAssertNotNil(idData.orgId, @"orgId should not be nil."); - XCTAssertNotNil(idData.username, @"username should not be nil."); - XCTAssertNotNil(idData.nickname, @"nickname should not be nil."); - XCTAssertNotNil(idData.displayName, @"displayName should not be nil."); - XCTAssertNotNil(idData.email, @"email should not be nil."); - XCTAssertNotNil(idData.firstName, @"firstName should not be nil."); - XCTAssertNotNil(idData.lastName, @"lastName should not be nil."); - XCTAssertNotNil(idData.pictureUrl, @"pictureUrl should not be nil."); - XCTAssertNotNil(idData.thumbnailUrl, @"thumbnailUrl should not be nil."); - XCTAssertNotNil(idData.enterpriseSoapUrl, @"enterpriseSoapUrl should not be nil."); - XCTAssertNotNil(idData.metadataSoapUrl, @"metadataSoapUrl should not be nil."); - XCTAssertNotNil(idData.partnerSoapUrl, @"partnerSoapUrl should not be nil."); - XCTAssertNotNil(idData.restUrl, @"restUrl should not be nil."); - XCTAssertNotNil(idData.restSObjectsUrl, @"restSObjectsUrl should not be nil."); - XCTAssertNotNil(idData.restSearchUrl, @"restSearchUrl should not be nil."); - XCTAssertNotNil(idData.restQueryUrl, @"restQueryUrl should not be nil."); - XCTAssertNotNil(idData.restRecentUrl, @"restRecentUrl should not be nil."); - XCTAssertNotNil(idData.profileUrl, @"profileUrl should not be nil."); - XCTAssertNotNil(idData.chatterFeedsUrl, @"chatterFeedsUrl should not be nil."); - XCTAssertNotNil(idData.chatterGroupsUrl, @"chatterGroupsUrl should not be nil."); - XCTAssertNotNil(idData.chatterUsersUrl, @"chatterUsersUrl should not be nil."); - XCTAssertNotNil(idData.chatterFeedItemsUrl, @"chatterFeedItemsUrl should not be nil."); - XCTAssertTrue(idData.isActive, @"isActive should be true."); - XCTAssertNotNil(idData.userType, @"userType should not be nil."); - XCTAssertNotNil(idData.language, @"language should not be nil."); - XCTAssertNotNil(idData.locale, @"locale should not be nil."); - XCTAssertFalse(idData.utcOffset == -1, @"No value determined for utcOffset."); - XCTAssertNotNil(idData.lastModifiedDate, @"lastModifiedDate should not be nil."); + + SFIdentityCoordinator* coordinator = [[SFIdentityCoordinator alloc] init]; + NSData* identityResponseData = [identityResponse dataUsingEncoding:NSUTF8StringEncoding]; + [coordinator processResponse:identityResponseData]; + SFIdentityData *idData = coordinator.idData; + + + // Basic identity fields + XCTAssertEqualObjects(idData.idUrl, [NSURL URLWithString:@"https://login.salesforce.com/id/some-org-id/some-user-id"], @"idUrl should match"); + XCTAssertTrue(idData.assertedUser, @"assertedUser should be true"); + XCTAssertEqualObjects(idData.userId, @"some-user-id", @"userId should match"); + XCTAssertEqualObjects(idData.orgId, @"some-org-id", @"orgId should match"); + + // User information + XCTAssertEqualObjects(idData.username, @"user@example.com", @"username should match"); + XCTAssertEqualObjects(idData.nickname, @"nickname", @"nickname should match"); + XCTAssertEqualObjects(idData.displayName, @"Example User", @"displayName should match"); + XCTAssertEqualObjects(idData.email, @"user@example.com", @"email should match"); + XCTAssertEqualObjects(idData.firstName, @"Example", @"firstName should match"); + XCTAssertEqualObjects(idData.lastName, @"User", @"lastName should match"); + + // Photos (NSURL* properties) + XCTAssertEqualObjects(idData.pictureUrl, [NSURL URLWithString:@"https://example.com/profilephoto/full"], @"pictureUrl should match"); + XCTAssertEqualObjects(idData.thumbnailUrl, [NSURL URLWithString:@"https://example.com/profilephoto/thumb"], @"thumbnailUrl should match"); + + // SOAP URLs + XCTAssertEqualObjects(idData.enterpriseSoapUrl, @"https://example.my.salesforce.com/services/Soap/c/some-version/some-org-id", @"enterpriseSoapUrl should match"); + XCTAssertEqualObjects(idData.metadataSoapUrl, @"https://example.my.salesforce.com/services/Soap/m/some-version/some-org-id", @"metadataSoapUrl should match"); + XCTAssertEqualObjects(idData.partnerSoapUrl, @"https://example.my.salesforce.com/services/Soap/u/some-version/some-org-id", @"partnerSoapUrl should match"); + + // REST URLs + XCTAssertEqualObjects(idData.restUrl, @"https://example.my.salesforce.com/services/data/vsome-version/", @"restUrl should match"); + XCTAssertEqualObjects(idData.restSObjectsUrl, @"https://example.my.salesforce.com/services/data/vsome-version/sobjects/", @"restSObjectsUrl should match"); + XCTAssertEqualObjects(idData.restSearchUrl, @"https://example.my.salesforce.com/services/data/vsome-version/search/", @"restSearchUrl should match"); + XCTAssertEqualObjects(idData.restQueryUrl, @"https://example.my.salesforce.com/services/data/vsome-version/query/", @"restQueryUrl should match"); + XCTAssertEqualObjects(idData.restRecentUrl, @"https://example.my.salesforce.com/services/data/vsome-version/recent/", @"restRecentUrl should match"); + + // Profile and Chatter URLs + XCTAssertEqualObjects(idData.profileUrl, [NSURL URLWithString:@"https://example.my.salesforce.com/some-user-id"], @"profileUrl should match (NSURL)"); + XCTAssertEqualObjects(idData.chatterFeedsUrl, @"https://example.my.salesforce.com/services/data/vsome-version/chatter/feeds", @"chatterFeedsUrl should match"); + XCTAssertEqualObjects(idData.chatterGroupsUrl, @"https://example.my.salesforce.com/services/data/vsome-version/chatter/groups", @"chatterGroupsUrl should match"); + XCTAssertEqualObjects(idData.chatterUsersUrl, @"https://example.my.salesforce.com/services/data/vsome-version/chatter/users", @"chatterUsersUrl should match"); + XCTAssertEqualObjects(idData.chatterFeedItemsUrl, @"https://example.my.salesforce.com/services/data/vsome-version/chatter/feed-items", @"chatterFeedItemsUrl should match"); + + // User status and preferences + XCTAssertTrue(idData.isActive, @"isActive should be true"); + XCTAssertEqualObjects(idData.userType, @"STANDARD", @"userType should match"); + XCTAssertEqualObjects(idData.language, @"en_US", @"language should match"); + XCTAssertEqualObjects(idData.locale, @"en_US", @"locale should match"); + XCTAssertEqual(idData.utcOffset, -28800000, @"utcOffset should match"); + + // Date parsing + XCTAssertNotNil(idData.lastModifiedDate, @"lastModifiedDate should not be nil"); + // Note: lastModifiedDate is parsed from "2024-12-23T18:40:50Z", exact value comparison would need date formatter } @end diff --git a/libs/SalesforceSDKCore/SalesforceSDKCoreTests/SalesforceSDKManagerTests.m b/libs/SalesforceSDKCore/SalesforceSDKCoreTests/SalesforceSDKManagerTests.m index f592a8c540..d4a46b4ba4 100644 --- a/libs/SalesforceSDKCore/SalesforceSDKCoreTests/SalesforceSDKManagerTests.m +++ b/libs/SalesforceSDKCore/SalesforceSDKCoreTests/SalesforceSDKManagerTests.m @@ -32,6 +32,8 @@ #import "SFUserAccount+Internal.h" #import "SFOAuthCredentials+Internal.h" #import "SFOAuthTestFlowCoordinatorDelegate.h" +#import "SFLoginViewController.h" +#import "SFSDKAuthRootController.h" static NSString* const kTestAppName = @"OverridenAppName"; @@ -420,6 +422,9 @@ - (void)createTestAppIdentity [SalesforceSDKManager sharedManager].appConfig.remoteAccessConsumerKey = @"test_connected_app_id"; [SalesforceSDKManager sharedManager].appConfig.oauthRedirectURI = @"test_connected_app_callback_uri"; [SalesforceSDKManager sharedManager].appConfig.oauthScopes = [NSSet setWithArray:@[ @"web", @"api" ]]; + + // Set oauthClientId for SFUserAccountManager (needed for createUserAccount) + [SFUserAccountManager sharedInstance].oauthClientId = @"test_connected_app_id"; } - (SFUserAccount *)createUserAccount @@ -430,6 +435,25 @@ - (SFUserAccount *)createUserAccount NSString *userId = [NSString stringWithFormat:@"user_%u", userIdentifier]; NSString *orgId = [NSString stringWithFormat:@"org_%u", userIdentifier]; user.credentials.identityUrl = [NSURL URLWithString:[NSString stringWithFormat:@"https://login.salesforce.com/id/%@/%@", orgId, userId]]; + + // Set additional credential fields required by getDevSupportInfos + user.credentials.redirectUri = @"testapp://auth/callback"; + user.credentials.instanceUrl = [NSURL URLWithString:@"https://test.salesforce.com"]; + user.credentials.tokenFormat = nil; + user.credentials.accessToken = @"test_access_token"; + user.credentials.scopes = @[@"api", @"web", @"refresh_token"]; + + // Set id data using JSON dictionary + NSDictionary *idDataDict = @{ + @"user_id": userId, + @"organization_id": orgId, + @"username": [NSString stringWithFormat:@"testuser_%u@example.com", userIdentifier], + @"email": [NSString stringWithFormat:@"testuser_%u@example.com", userIdentifier], + @"first_name": @"Test", + @"last_name": [NSString stringWithFormat:@"User%u", userIdentifier] + }; + user.idData = [[SFIdentityData alloc] initWithJsonDict:idDataDict]; + [user transitionToLoginState:SFUserAccountLoginStateLoggedIn]; NSError *error = nil; [[SFUserAccountManager sharedInstance] saveAccountForUser:user error:&error]; @@ -599,4 +623,502 @@ - (void)testAppConfigForLoginHostReturnsDefaultWhenBlockReturnsNil { }]; } +#pragma mark - Dev Actions Tests + +- (void)testGetDevActionsAlwaysShowsDevInfo { + [self createTestAppIdentity]; + + // Test with a regular view controller (not login) + UIViewController *regularVC = [[UIViewController alloc] init]; + NSArray *actions = [[SalesforceSDKManager sharedManager] getDevActions:regularVC]; + + XCTAssertGreaterThan(actions.count, 0, @"Should have at least one action"); + XCTAssertEqualObjects(actions[0].name, @"Show dev info", @"First action should always be dev info"); +} + +- (void)testGetDevActionsShowsLoginOptionsOnLoginViewController { + [self createTestAppIdentity]; + + // Test with SFLoginViewController + SFLoginViewController *loginVC = [[SFLoginViewController alloc] init]; + NSArray *actions = [[SalesforceSDKManager sharedManager] getDevActions:loginVC]; + + // Find "Login Options" action + BOOL hasLoginOptions = NO; + for (SFSDKDevAction *action in actions) { + if ([action.name isEqualToString:@"Login Options"]) { + hasLoginOptions = YES; + break; + } + } + + XCTAssertTrue(hasLoginOptions, @"Should show Login Options when on login view controller"); +} + +- (void)testGetDevActionsDoesNotShowLoginOptionsOnRegularViewController { + [self createTestAppIdentity]; + + // Test with a regular view controller + UIViewController *regularVC = [[UIViewController alloc] init]; + NSArray *actions = [[SalesforceSDKManager sharedManager] getDevActions:regularVC]; + + // Check that "Login Options" is not present + BOOL hasLoginOptions = NO; + for (SFSDKDevAction *action in actions) { + if ([action.name isEqualToString:@"Login Options"]) { + hasLoginOptions = YES; + break; + } + } + + XCTAssertFalse(hasLoginOptions, @"Should not show Login Options on regular view controller"); +} + +- (void)testGetDevActionsShowsLogoutWhenUserLoggedIn { + [self createTestAppIdentity]; + + // Create and set a current user + SFUserAccount *user = [self createUserAccount]; + [[SFUserAccountManager sharedInstance] setCurrentUserInternal:user]; + + // Test with a regular view controller (not login) + UIViewController *regularVC = [[UIViewController alloc] init]; + NSArray *actions = [[SalesforceSDKManager sharedManager] getDevActions:regularVC]; + + // Find "Logout" action + BOOL hasLogout = NO; + for (SFSDKDevAction *action in actions) { + if ([action.name isEqualToString:@"Logout"]) { + hasLogout = YES; + break; + } + } + + XCTAssertTrue(hasLogout, @"Should show Logout when user is logged in and not on login screen"); + + // Clean up + [[SFUserAccountManager sharedInstance] setCurrentUserInternal:nil]; +} + +- (void)testGetDevActionsDoesNotShowLogoutOnLoginViewController { + [self createTestAppIdentity]; + + // Create and set a current user + SFUserAccount *user = [self createUserAccount]; + [[SFUserAccountManager sharedInstance] setCurrentUserInternal:user]; + + // Test with SFLoginViewController + SFLoginViewController *loginVC = [[SFLoginViewController alloc] init]; + NSArray *actions = [[SalesforceSDKManager sharedManager] getDevActions:loginVC]; + + // Check that "Logout" is not present + BOOL hasLogout = NO; + for (SFSDKDevAction *action in actions) { + if ([action.name isEqualToString:@"Logout"]) { + hasLogout = YES; + break; + } + } + + XCTAssertFalse(hasLogout, @"Should not show Logout when on login view controller"); + + // Clean up + [[SFUserAccountManager sharedInstance] setCurrentUserInternal:nil]; +} + +- (void)testGetDevActionsDoesNotShowLogoutWhenNoUser { + [self createTestAppIdentity]; + + // Ensure no current user + [[SFUserAccountManager sharedInstance] setCurrentUserInternal:nil]; + + // Test with a regular view controller + UIViewController *regularVC = [[UIViewController alloc] init]; + NSArray *actions = [[SalesforceSDKManager sharedManager] getDevActions:regularVC]; + + // Check that "Logout" is not present + BOOL hasLogout = NO; + for (SFSDKDevAction *action in actions) { + if ([action.name isEqualToString:@"Logout"]) { + hasLogout = YES; + break; + } + } + + XCTAssertFalse(hasLogout, @"Should not show Logout when no user is logged in"); +} + +- (void)testGetDevActionsShowsSwitchUserWhenUserLoggedIn { + [self createTestAppIdentity]; + + // Create and set a current user + SFUserAccount *user = [self createUserAccount]; + [[SFUserAccountManager sharedInstance] setCurrentUserInternal:user]; + + // Test with a regular view controller (not login) + UIViewController *regularVC = [[UIViewController alloc] init]; + NSArray *actions = [[SalesforceSDKManager sharedManager] getDevActions:regularVC]; + + // Find "Switch user" action + BOOL hasSwitchUser = NO; + for (SFSDKDevAction *action in actions) { + if ([action.name isEqualToString:@"Switch user"]) { + hasSwitchUser = YES; + break; + } + } + + XCTAssertTrue(hasSwitchUser, @"Should show Switch user when user is logged in and not on login screen"); + + // Clean up + [[SFUserAccountManager sharedInstance] setCurrentUserInternal:nil]; +} + +- (void)testGetDevActionsDoesNotShowSwitchUserOnLoginViewController { + [self createTestAppIdentity]; + + // Create and set a current user + SFUserAccount *user = [self createUserAccount]; + [[SFUserAccountManager sharedInstance] setCurrentUserInternal:user]; + + // Test with SFLoginViewController + SFLoginViewController *loginVC = [[SFLoginViewController alloc] init]; + NSArray *actions = [[SalesforceSDKManager sharedManager] getDevActions:loginVC]; + + // Check that "Switch user" is not present + BOOL hasSwitchUser = NO; + for (SFSDKDevAction *action in actions) { + if ([action.name isEqualToString:@"Switch user"]) { + hasSwitchUser = YES; + break; + } + } + + XCTAssertFalse(hasSwitchUser, @"Should not show Switch user when on login view controller"); + + // Clean up + [[SFUserAccountManager sharedInstance] setCurrentUserInternal:nil]; +} + +#pragma mark - Dev Support Infos Tests + +- (void)testGetDevSupportInfosReturnsArray { + [self createTestAppIdentity]; + + NSArray *infos = [[SalesforceSDKManager sharedManager] getDevSupportInfos]; + + XCTAssertNotNil(infos, @"Dev support infos should not be nil"); + XCTAssertGreaterThan(infos.count, 0, @"Should have at least some info entries"); +} + +- (void)testGetDevSupportInfosContainsSDKVersion { + [self createTestAppIdentity]; + + NSArray *infos = [[SalesforceSDKManager sharedManager] getDevSupportInfos]; + + // Find SDK Version in the array (skip section: entries) + BOOL hasSDKVersion = NO; + for (NSUInteger i = 0; i < infos.count - 1; i++) { + NSString *item = infos[i]; + if (![item hasPrefix:@"section:"] && [item isEqualToString:@"SDK Version"]) { + // Make sure next item is not a section marker + if (i + 1 < infos.count && ![infos[i + 1] hasPrefix:@"section:"]) { + hasSDKVersion = YES; + XCTAssertNotNil(infos[i + 1], @"SDK Version value should not be nil"); + break; + } + } + } + + XCTAssertTrue(hasSDKVersion, @"Dev support infos should contain SDK Version"); +} + +- (void)testGetDevSupportInfosContainsAppType { + [self createTestAppIdentity]; + + NSArray *infos = [[SalesforceSDKManager sharedManager] getDevSupportInfos]; + + // Find App Type in the array (skip section: entries) + BOOL hasAppType = NO; + for (NSUInteger i = 0; i < infos.count - 1; i++) { + NSString *item = infos[i]; + if (![item hasPrefix:@"section:"] && [item isEqualToString:@"App Type"]) { + // Make sure next item is not a section marker + if (i + 1 < infos.count && ![infos[i + 1] hasPrefix:@"section:"]) { + hasAppType = YES; + XCTAssertNotNil(infos[i + 1], @"App Type value should not be nil"); + break; + } + } + } + + XCTAssertTrue(hasAppType, @"Dev support infos should contain App Type"); +} + +- (void)testGetDevSupportInfosContainsUserInfoWhenLoggedIn { + [self createTestAppIdentity]; + + // Create and set a current user + SFUserAccount *user = [self createUserAccount]; + [[SFUserAccountManager sharedInstance] setCurrentUserInternal:user]; + + NSArray *infos = [[SalesforceSDKManager sharedManager] getDevSupportInfos]; + + // Find Username in the array (it's in the "Current User" section, but we search for "Username" key) + BOOL hasCurrentUser = NO; + for (NSUInteger i = 0; i < infos.count - 1; i++) { + NSString *item = infos[i]; + if (![item hasPrefix:@"section:"] && [item isEqualToString:@"Username"]) { + // Make sure next item is not a section marker + if (i + 1 < infos.count && ![infos[i + 1] hasPrefix:@"section:"]) { + hasCurrentUser = YES; + XCTAssertNotNil(infos[i + 1], @"Username value should not be nil"); + break; + } + } + } + + XCTAssertTrue(hasCurrentUser, @"Dev support infos should contain Username when logged in"); + + // Clean up + [[SFUserAccountManager sharedInstance] setCurrentUserInternal:nil]; +} + +- (void)testGetDevSupportInfosDoesNotContainUserInfoWhenNotLoggedIn { + [self createTestAppIdentity]; + + // Ensure no current user + [[SFUserAccountManager sharedInstance] setCurrentUserInternal:nil]; + + NSArray *infos = [[SalesforceSDKManager sharedManager] getDevSupportInfos]; + + // Check that "section:Current User" is not in the array + BOOL hasCurrentUserSection = NO; + for (NSUInteger i = 0; i < infos.count; i++) { + if ([infos[i] isEqualToString:@"section:Current User"]) { + hasCurrentUserSection = YES; + break; + } + } + + XCTAssertFalse(hasCurrentUserSection, @"Dev support infos should not contain Current User section when not logged in"); +} + +- (void)testGetDevSupportInfosContainsAuthConfigSection { + [self createTestAppIdentity]; + + NSArray *infos = [[SalesforceSDKManager sharedManager] getDevSupportInfos]; + + // Check that "section:Auth Config" exists + BOOL hasAuthConfigSection = NO; + for (NSUInteger i = 0; i < infos.count; i++) { + if ([infos[i] isEqualToString:@"section:Auth Config"]) { + hasAuthConfigSection = YES; + break; + } + } + + XCTAssertTrue(hasAuthConfigSection, @"Dev support infos should contain Auth Config section"); +} + +- (void)testGetDevSupportInfosAuthConfigContainsExpectedFields { + [self createTestAppIdentity]; + + NSArray *infos = [[SalesforceSDKManager sharedManager] getDevSupportInfos]; + + // Expected fields in Auth Config section + NSArray *expectedFields = @[ + @"Use Web Server Authentication", + @"Use Hybrid Authentication", + @"Supports Welcome Discovery", + @"Browser Login Enabled", + @"IDP Enabled", + @"Identity Provider" + ]; + + for (NSString *field in expectedFields) { + BOOL hasField = NO; + for (NSUInteger i = 0; i < infos.count - 1; i++) { + NSString *item = infos[i]; + if (![item hasPrefix:@"section:"] && [item isEqualToString:field]) { + if (i + 1 < infos.count && ![infos[i + 1] hasPrefix:@"section:"]) { + hasField = YES; + // Verify value is YES or NO + NSString *value = infos[i + 1]; + XCTAssertTrue([value isEqualToString:@"YES"] || [value isEqualToString:@"NO"], + @"Auth Config field %@ should have YES/NO value, got: %@", field, value); + break; + } + } + } + XCTAssertTrue(hasField, @"Auth Config should contain field: %@", field); + } +} + +- (void)testGetDevSupportInfosContainsBootconfigSection { + [self createTestAppIdentity]; + + NSArray *infos = [[SalesforceSDKManager sharedManager] getDevSupportInfos]; + + // Check that "section:Bootconfig" exists + BOOL hasBootconfigSection = NO; + for (NSUInteger i = 0; i < infos.count; i++) { + if ([infos[i] isEqualToString:@"section:Bootconfig"]) { + hasBootconfigSection = YES; + break; + } + } + + XCTAssertTrue(hasBootconfigSection, @"Dev support infos should contain Bootconfig section"); +} + +- (void)testGetDevSupportInfosCurrentUserSectionContainsAllCredentialFields { + [self createTestAppIdentity]; + + // Create and set a current user + SFUserAccount *user = [self createUserAccount]; + [[SFUserAccountManager sharedInstance] setCurrentUserInternal:user]; + + NSArray *infos = [[SalesforceSDKManager sharedManager] getDevSupportInfos]; + + // Expected fields in Current User section + NSArray *expectedFields = @[ + @"Username", + @"Consumer Key", + @"Redirect URI", + @"Scopes", + @"Instance URL", + @"Token format", + @"Access Token Expiration", + @"Beacon Child Consumer Key" + ]; + + for (NSString *field in expectedFields) { + BOOL hasField = NO; + for (NSUInteger i = 0; i < infos.count - 1; i++) { + NSString *item = infos[i]; + if (![item hasPrefix:@"section:"] && [item isEqualToString:field]) { + if (i + 1 < infos.count && ![infos[i + 1] hasPrefix:@"section:"]) { + hasField = YES; + XCTAssertNotNil(infos[i + 1], @"Current User field %@ should not be nil", field); + break; + } + } + } + XCTAssertTrue(hasField, @"Current User should contain field: %@", field); + } + + // Clean up + [[SFUserAccountManager sharedInstance] setCurrentUserInternal:nil]; +} + +- (void)testGetDevSupportInfosCurrentUserCredentialsHaveCorrectValues { + [self createTestAppIdentity]; + + // Create and set a current user + SFUserAccount *user = [self createUserAccount]; + [[SFUserAccountManager sharedInstance] setCurrentUserInternal:user]; + + NSArray *infos = [[SalesforceSDKManager sharedManager] getDevSupportInfos]; + + // Find and verify specific values + for (NSUInteger i = 0; i < infos.count - 1; i++) { + NSString *item = infos[i]; + if (![item hasPrefix:@"section:"]) { + NSString *value = infos[i + 1]; + + if ([item isEqualToString:@"Redirect URI"]) { + XCTAssertEqualObjects(value, @"testapp://auth/callback", @"Redirect URI should match"); + } else if ([item isEqualToString:@"Instance URL"]) { + XCTAssertEqualObjects(value, @"https://test.salesforce.com", @"Instance URL should match"); + } else if ([item isEqualToString:@"Token format"]) { + XCTAssertTrue([value isEqualToString:@"jwt"] || [value isEqualToString:@"opaque"], + @"Token format should be jwt or opaque"); + } + } + } + + // Clean up + [[SFUserAccountManager sharedInstance] setCurrentUserInternal:nil]; +} + +- (void)testGetDevSupportInfosContainsUserAgentString { + [self createTestAppIdentity]; + + NSArray *infos = [[SalesforceSDKManager sharedManager] getDevSupportInfos]; + + // Find User Agent in the array + BOOL hasUserAgent = NO; + for (NSUInteger i = 0; i < infos.count - 1; i++) { + NSString *item = infos[i]; + if (![item hasPrefix:@"section:"] && [item isEqualToString:@"User Agent"]) { + if (i + 1 < infos.count && ![infos[i + 1] hasPrefix:@"section:"]) { + hasUserAgent = YES; + XCTAssertNotNil(infos[i + 1], @"User Agent value should not be nil"); + XCTAssertGreaterThan([infos[i + 1] length], 0, @"User Agent should not be empty"); + break; + } + } + } + + XCTAssertTrue(hasUserAgent, @"Dev support infos should contain User Agent"); +} + +- (void)testGetDevSupportInfosContainsAuthenticatedUsersWhenUsersExist { + [self createTestAppIdentity]; + + // Create and save a user (not current, just exists) + [self createUserAccount]; + + NSArray *infos = [[SalesforceSDKManager sharedManager] getDevSupportInfos]; + + // Find Authenticated Users in the array + BOOL hasAuthenticatedUsers = NO; + for (NSUInteger i = 0; i < infos.count - 1; i++) { + NSString *item = infos[i]; + if (![item hasPrefix:@"section:"] && [item isEqualToString:@"Authenticated Users"]) { + if (i + 1 < infos.count && ![infos[i + 1] hasPrefix:@"section:"]) { + hasAuthenticatedUsers = YES; + XCTAssertNotNil(infos[i + 1], @"Authenticated Users value should not be nil"); + break; + } + } + } + + XCTAssertTrue(hasAuthenticatedUsers, @"Dev support infos should contain Authenticated Users when users exist"); + + // Clean up + [[SFUserAccountManager sharedInstance] setCurrentUserInternal:nil]; +} + +- (void)testGetDevSupportInfosSectionsAreProperlyStructured { + [self createTestAppIdentity]; + + // Create and set a current user + SFUserAccount *user = [self createUserAccount]; + [[SFUserAccountManager sharedInstance] setCurrentUserInternal:user]; + + NSArray *infos = [[SalesforceSDKManager sharedManager] getDevSupportInfos]; + + // Verify that after each "section:" marker, there are key-value pairs + for (NSUInteger i = 0; i < infos.count; i++) { + NSString *item = infos[i]; + if ([item hasPrefix:@"section:"]) { + // After a section marker, next items should be key-value pairs until next section + // Verify at least one key-value pair exists after this section + if (i + 2 < infos.count) { + NSString *nextItem = infos[i + 1]; + // Next item should not be a section marker (should be a key) + if (![nextItem hasPrefix:@"section:"]) { + // This is good - we have at least one key after the section + XCTAssertTrue(YES, @"Section %@ has at least one key-value pair", item); + } + } + } + } + + // Clean up + [[SFUserAccountManager sharedInstance] setCurrentUserInternal:nil]; +} + @end diff --git a/libs/SalesforceSDKCore/SalesforceSDKCoreTests/WebViewStateManagerTests.swift b/libs/SalesforceSDKCore/SalesforceSDKCoreTests/WebViewStateManagerTests.swift index 5d68ac57e0..cfbf1d9a24 100644 --- a/libs/SalesforceSDKCore/SalesforceSDKCoreTests/WebViewStateManagerTests.swift +++ b/libs/SalesforceSDKCore/SalesforceSDKCoreTests/WebViewStateManagerTests.swift @@ -35,38 +35,4 @@ final class WebViewStateManagerTests: XCTestCase { SFSDKWebViewStateManager.sessionCookieManagementDisabled = false XCTAssertFalse(SFSDKWebViewStateManager.sessionCookieManagementDisabled) } - - @MainActor - func testClearCache() async throws { - // Add some test data - let 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 = [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") - - // 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") - } } diff --git a/libs/SmartStore/Configuration/SmartStore-Common.xcconfig b/libs/SmartStore/Configuration/SmartStore-Common.xcconfig index 8bd854ce57..b6481bb205 100644 --- a/libs/SmartStore/Configuration/SmartStore-Common.xcconfig +++ b/libs/SmartStore/Configuration/SmartStore-Common.xcconfig @@ -16,8 +16,8 @@ PRODUCT_NAME = SmartStore OTHER_LDFLAGS = -ObjC $(inherited) OTHER_CFLAGS = -DSQLITE_HAS_CODEC -HEADER_SEARCH_PATHS = $(BUILT_PRODUCTS_DIR)/Headers $(TARGET_BUILD_DIR)/Headers $(SRCROOT)/../../external/ThirdPartyDependencies/sqlcipher $(inherited) -LIBRARY_SEARCH_PATHS = $(SRCROOT)/../../external/ThirdPartyDependencies/sqlcipher $(inherited) +HEADER_SEARCH_PATHS = $(BUILT_PRODUCTS_DIR)/Headers $(TARGET_BUILD_DIR)/Headers $(inherited) +LIBRARY_SEARCH_PATHS = $(inherited) // Make sure to use only API that are safe for application extensions APPLICATION_EXTENSION_API_ONLY = YES diff --git a/libs/SmartStore/SmartStore.xcodeproj/project.pbxproj b/libs/SmartStore/SmartStore.xcodeproj/project.pbxproj index 22f5de8d89..877815a04d 100644 --- a/libs/SmartStore/SmartStore.xcodeproj/project.pbxproj +++ b/libs/SmartStore/SmartStore.xcodeproj/project.pbxproj @@ -48,6 +48,7 @@ 4FEE44881BFD5CD900F09C43 /* SFSmartStoreFullTextSearchTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4F96015A1BFD33B30022F021 /* SFSmartStoreFullTextSearchTests.m */; }; 4FEE44891BFD5CDC00F09C43 /* SFSmartStoreTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = 4F96015C1BFD33B30022F021 /* SFSmartStoreTestCase.m */; }; 4FEE448A1BFD5CE500F09C43 /* SFSmartStoreTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4F96015E1BFD33B30022F021 /* SFSmartStoreTests.m */; }; + 4FSSSDKMGR001234567890AB /* SmartStoreSDKManagerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4FSSSDKMGR112345678901AB /* SmartStoreSDKManagerTests.m */; }; 4FF4DC671CF51D9300385C2C /* SFQuerySpecTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 4FF4DC661CF51D9300385C2C /* SFQuerySpecTests.m */; }; 4FFEE6351BFE98E100B7AA8A /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = CEAAAE61195911E600CBBFE9 /* InfoPlist.strings */; }; 696E912D2AD0977600205884 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 696E91232AD0975B00205884 /* PrivacyInfo.xcprivacy */; }; @@ -243,6 +244,7 @@ 4F96015C1BFD33B30022F021 /* SFSmartStoreTestCase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SFSmartStoreTestCase.m; path = SmartStoreTests/SFSmartStoreTestCase.m; sourceTree = SOURCE_ROOT; }; 4F96015D1BFD33B30022F021 /* SFSmartStoreTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SFSmartStoreTests.h; path = SmartStoreTests/SFSmartStoreTests.h; sourceTree = SOURCE_ROOT; }; 4F96015E1BFD33B30022F021 /* SFSmartStoreTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SFSmartStoreTests.m; path = SmartStoreTests/SFSmartStoreTests.m; sourceTree = SOURCE_ROOT; }; + 4FSSSDKMGR112345678901AB /* SmartStoreSDKManagerTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SmartStoreSDKManagerTests.m; path = SmartStoreTests/SmartStoreSDKManagerTests.m; sourceTree = SOURCE_ROOT; }; 4F96FBC21BFD30030022F021 /* SFAlterSoupLongOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SFAlterSoupLongOperation.h; path = SmartStore/Classes/SFAlterSoupLongOperation.h; sourceTree = SOURCE_ROOT; }; 4F96FBC31BFD30030022F021 /* SFAlterSoupLongOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SFAlterSoupLongOperation.m; path = SmartStore/Classes/SFAlterSoupLongOperation.m; sourceTree = SOURCE_ROOT; }; 4F96FBC41BFD30030022F021 /* SFQuerySpec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SFQuerySpec.h; path = SmartStore/Classes/SFQuerySpec.h; sourceTree = SOURCE_ROOT; }; @@ -525,6 +527,7 @@ 4F96015C1BFD33B30022F021 /* SFSmartStoreTestCase.m */, 4F96015D1BFD33B30022F021 /* SFSmartStoreTests.h */, 4F96015E1BFD33B30022F021 /* SFSmartStoreTests.m */, + 4FSSSDKMGR112345678901AB /* SmartStoreSDKManagerTests.m */, CEAAAE5F195911E600CBBFE9 /* Supporting Files */, 4F75747122B9BA9000528BE2 /* SFSmartSqlCacheTests.m */, 4F5BBBB3235E1FD900E6D619 /* SmartStoreTests.swift */, @@ -925,6 +928,7 @@ C03DE7E11D1B6B8E00BFA6BD /* SFSmartSqlTests.m in Sources */, 4FEE44881BFD5CD900F09C43 /* SFSmartStoreFullTextSearchTests.m in Sources */, 4FEE448A1BFD5CE500F09C43 /* SFSmartStoreTests.m in Sources */, + 4FSSSDKMGR001234567890AB /* SmartStoreSDKManagerTests.m in Sources */, 4FEE44871BFD5CD500F09C43 /* SFSmartStoreFullTextSearchSpeedTests.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1066,6 +1070,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); + LIBRARY_SEARCH_PATHS = "$(inherited)"; MTL_ENABLE_DEBUG_INFO = YES; PRODUCT_BUNDLE_IDENTIFIER = com.salesforce.mobilesdk.SmartStore; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -1108,6 +1113,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); + LIBRARY_SEARCH_PATHS = "$(inherited)"; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = com.salesforce.mobilesdk.SmartStore; PRODUCT_NAME = "$(TARGET_NAME)"; diff --git a/libs/SmartStore/SmartStore/Classes/SmartStoreSDKManager.m b/libs/SmartStore/SmartStore/Classes/SmartStoreSDKManager.m index 398ae87331..dc5c8354c9 100644 --- a/libs/SmartStore/SmartStore/Classes/SmartStoreSDKManager.m +++ b/libs/SmartStore/SmartStore/Classes/SmartStoreSDKManager.m @@ -95,6 +95,7 @@ - (NSArray*) getDevSupportInfos { SFSmartStore *store = [SFSmartStore sharedGlobalStoreWithName:kDefaultSmartStoreName]; NSMutableArray * devInfos = [NSMutableArray arrayWithArray:[super getDevSupportInfos]]; + [devInfos addObject:@"section:SmartStore"]; [devInfos addObjectsFromArray:@[ @"SQLCipher version", [store getSQLCipherVersion], @"SQLCipher Compile Options", [[store getCompileOptions] componentsJoinedByString:@", "], diff --git a/libs/SmartStore/SmartStoreTests/SmartStoreSDKManagerTests.m b/libs/SmartStore/SmartStoreTests/SmartStoreSDKManagerTests.m new file mode 100644 index 0000000000..ac9a783154 --- /dev/null +++ b/libs/SmartStore/SmartStoreTests/SmartStoreSDKManagerTests.m @@ -0,0 +1,199 @@ +/* + Copyright (c) 2025-present, salesforce.com, inc. All rights reserved. + + Redistribution and use of this software in source and binary forms, with or without modification, + are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of salesforce.com, inc. nor the names of its contributors may be used to + endorse or promote products derived from this software without specific prior written + permission of salesforce.com, inc. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY + WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "SFSmartStoreTestCase.h" +#import "SmartStoreSDKManager.h" +#import "SFSmartStore.h" +#import + +@interface SmartStoreSDKManagerTests : SFSmartStoreTestCase + +@end + +@implementation SmartStoreSDKManagerTests + +- (void)testGetDevSupportInfosContainsSmartStoreSection { + SmartStoreSDKManager *manager = [[SmartStoreSDKManager alloc] init]; + + NSArray *devInfos = [manager getDevSupportInfos]; + + BOOL foundSmartStoreSection = NO; + for (id item in devInfos) { + if ([item isKindOfClass:[NSString class]] && [item isEqualToString:@"section:SmartStore"]) { + foundSmartStoreSection = YES; + break; + } + } + + XCTAssertTrue(foundSmartStoreSection, @"Dev support infos should contain 'section:SmartStore'"); +} + +- (void)testGetDevSupportInfosContainsSQLCipherVersion { + SmartStoreSDKManager *manager = [[SmartStoreSDKManager alloc] init]; + + NSArray *devInfos = [manager getDevSupportInfos]; + + BOOL foundVersionLabel = NO; + BOOL foundVersionValue = NO; + + for (NSUInteger i = 0; i < devInfos.count - 1; i++) { + if ([devInfos[i] isEqualToString:@"SQLCipher version"]) { + foundVersionLabel = YES; + // The next item should be the version value + if (i + 1 < devInfos.count && [devInfos[i + 1] isKindOfClass:[NSString class]]) { + NSString *version = devInfos[i + 1]; + foundVersionValue = (version.length > 0); + } + break; + } + } + + XCTAssertTrue(foundVersionLabel, @"Dev support infos should contain 'SQLCipher version' label"); + XCTAssertTrue(foundVersionValue, @"Dev support infos should contain SQLCipher version value"); +} + +- (void)testGetDevSupportInfosContainsCompileOptions { + SmartStoreSDKManager *manager = [[SmartStoreSDKManager alloc] init]; + + NSArray *devInfos = [manager getDevSupportInfos]; + + BOOL foundCompileOptions = NO; + + for (NSUInteger i = 0; i < devInfos.count - 1; i++) { + if ([devInfos[i] isEqualToString:@"SQLCipher Compile Options"]) { + foundCompileOptions = YES; + // The next item should be the compile options (may be empty string) + XCTAssertTrue(i + 1 < devInfos.count, @"Compile options label should be followed by a value"); + XCTAssertTrue([devInfos[i + 1] isKindOfClass:[NSString class]], @"Compile options value should be a string"); + break; + } + } + + XCTAssertTrue(foundCompileOptions, @"Dev support infos should contain 'SQLCipher Compile Options'"); +} + +- (void)testGetDevSupportInfosContainsRuntimeSettings { + SmartStoreSDKManager *manager = [[SmartStoreSDKManager alloc] init]; + + NSArray *devInfos = [manager getDevSupportInfos]; + + BOOL foundRuntimeSettings = NO; + + for (NSUInteger i = 0; i < devInfos.count - 1; i++) { + if ([devInfos[i] isEqualToString:@"SQLCipher Runtime Settings"]) { + foundRuntimeSettings = YES; + // The next item should be the runtime settings + XCTAssertTrue(i + 1 < devInfos.count, @"Runtime settings label should be followed by a value"); + XCTAssertTrue([devInfos[i + 1] isKindOfClass:[NSString class]], @"Runtime settings value should be a string"); + break; + } + } + + XCTAssertTrue(foundRuntimeSettings, @"Dev support infos should contain 'SQLCipher Runtime Settings'"); +} + +- (void)testGetDevSupportInfosWithGlobalStores { + SmartStoreSDKManager *manager = [[SmartStoreSDKManager alloc] init]; + + // Create a global store to ensure we have something to report + SFSmartStore *globalStore = [SFSmartStore sharedGlobalStoreWithName:@"testGlobalStore"]; + XCTAssertNotNil(globalStore, @"Should be able to create global store"); + + NSArray *devInfos = [manager getDevSupportInfos]; + + // Find the Global SmartStores entry + BOOL foundGlobalStores = NO; + NSString *globalStoresValue = nil; + + for (NSUInteger i = 0; i < devInfos.count - 1; i++) { + if ([devInfos[i] isEqualToString:@"Global SmartStores"]) { + foundGlobalStores = YES; + globalStoresValue = devInfos[i + 1]; + break; + } + } + + XCTAssertTrue(foundGlobalStores, @"Should find Global SmartStores entry"); + XCTAssertNotNil(globalStoresValue, @"Global SmartStores value should not be nil"); + XCTAssertTrue([globalStoresValue containsString:@"testGlobalStore"], + @"Global SmartStores should include our test store (found: %@)", globalStoresValue); + + // Clean up + [SFSmartStore removeSharedGlobalStoreWithName:@"testGlobalStore"]; +} + +- (void)testGetDevSupportInfosWithUserStore { + SmartStoreSDKManager *manager = [[SmartStoreSDKManager alloc] init]; + + // Set up a test user + SFUserAccount *testUser = [self setUpSmartStoreUser]; + + // Create a user store + SFSmartStore *userStore = [SFSmartStore sharedStoreWithName:@"testUserStore" user:testUser]; + XCTAssertNotNil(userStore, @"Should be able to create user store"); + + NSArray *devInfos = [manager getDevSupportInfos]; + + // Find the User SmartStores entry + BOOL foundUserStores = NO; + NSString *userStoresValue = nil; + + for (NSUInteger i = 0; i < devInfos.count - 1; i++) { + if ([devInfos[i] isEqualToString:@"User SmartStores"]) { + foundUserStores = YES; + userStoresValue = devInfos[i + 1]; + break; + } + } + + XCTAssertTrue(foundUserStores, @"Should find User SmartStores entry"); + XCTAssertNotNil(userStoresValue, @"User SmartStores value should not be nil"); + + // Clean up + [SFSmartStore removeSharedStoreWithName:@"testUserStore" forUser:testUser]; + [self tearDownSmartStoreUser:testUser]; +} + +- (void)testGetDevActionsIncludesInspectSmartStore { + SmartStoreSDKManager *manager = [[SmartStoreSDKManager alloc] init]; + UIViewController *vc = [[UIViewController alloc] init]; + + NSArray *devActions = [manager getDevActions:vc]; + + BOOL foundInspectAction = NO; + for (id action in devActions) { + if ([action isKindOfClass:[SFSDKDevAction class]]) { + SFSDKDevAction *devAction = (SFSDKDevAction *)action; + if ([devAction.name isEqualToString:@"Inspect SmartStore"]) { + foundInspectAction = YES; + break; + } + } + } + + XCTAssertTrue(foundInspectAction, @"Dev actions should include 'Inspect SmartStore' action"); +} + +@end + diff --git a/native/SampleApps/AuthFlowTester/AuthFlowTester.xcodeproj/project.pbxproj b/native/SampleApps/AuthFlowTester/AuthFlowTester.xcodeproj/project.pbxproj index 43856d0a19..e4b7791884 100644 --- a/native/SampleApps/AuthFlowTester/AuthFlowTester.xcodeproj/project.pbxproj +++ b/native/SampleApps/AuthFlowTester/AuthFlowTester.xcodeproj/project.pbxproj @@ -7,17 +7,16 @@ objects = { /* Begin PBXBuildFile section */ - 4F1A8CCD2EAFEA7C0037DC89 /* BootConfigEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F1A8CCC2EAFEA7C0037DC89 /* BootConfigEditor.swift */; }; 4F95A89C2EA806E700C98D18 /* SalesforceAnalytics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4F95A8962EA801DC00C98D18 /* SalesforceAnalytics.framework */; }; 4F95A89D2EA806E700C98D18 /* SalesforceAnalytics.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 4F95A8962EA801DC00C98D18 /* SalesforceAnalytics.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 4F95A89F2EA806E900C98D18 /* SalesforceSDKCommon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4F95A8982EA801DC00C98D18 /* SalesforceSDKCommon.framework */; }; 4F95A8A02EA806E900C98D18 /* SalesforceSDKCommon.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 4F95A8982EA801DC00C98D18 /* SalesforceSDKCommon.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 4F95A8A12EA806EA00C98D18 /* SalesforceSDKCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4F95A89A2EA801DC00C98D18 /* SalesforceSDKCore.framework */; }; 4F95A8A22EA806EA00C98D18 /* SalesforceSDKCore.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 4F95A89A2EA801DC00C98D18 /* SalesforceSDKCore.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 4FE006B52EBEB94700CFD66F /* InitialViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FE006B42EBEB94100CFD66F /* InitialViewController.swift */; }; 4FEBAF292EA9B91500D4880A /* RevokeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4FEBAF282EA9B91500D4880A /* RevokeView.swift */; }; AUTH001 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = AUTH002 /* AppDelegate.swift */; }; AUTH003 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = AUTH004 /* SceneDelegate.swift */; }; - AUTH005 /* ConfigPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AUTH006 /* ConfigPickerViewController.swift */; }; AUTH007 /* SessionDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AUTH008 /* SessionDetailViewController.swift */; }; AUTH009 /* bootconfig.plist in Resources */ = {isa = PBXBuildFile; fileRef = AUTH010 /* bootconfig.plist */; }; AUTH011 /* bootconfig2.plist in Resources */ = {isa = PBXBuildFile; fileRef = AUTH036 /* bootconfig2.plist */; }; @@ -28,7 +27,6 @@ AUTH042 /* OAuthConfigurationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AUTH043 /* OAuthConfigurationView.swift */; }; AUTH049 /* JwtAccessView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AUTH050 /* JwtAccessView.swift */; }; AUTH051 /* InfoRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AUTH052 /* InfoRowView.swift */; }; - AUTH053 /* FlowTypesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AUTH054 /* FlowTypesView.swift */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -46,14 +44,13 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 4F1A8CCC2EAFEA7C0037DC89 /* BootConfigEditor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BootConfigEditor.swift; sourceTree = ""; }; 4F95A8962EA801DC00C98D18 /* SalesforceAnalytics.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SalesforceAnalytics.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 4F95A8982EA801DC00C98D18 /* SalesforceSDKCommon.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SalesforceSDKCommon.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 4F95A89A2EA801DC00C98D18 /* SalesforceSDKCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SalesforceSDKCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 4FE006B42EBEB94100CFD66F /* InitialViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InitialViewController.swift; sourceTree = ""; }; 4FEBAF282EA9B91500D4880A /* RevokeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RevokeView.swift; sourceTree = ""; }; AUTH002 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; AUTH004 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; - AUTH006 /* ConfigPickerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigPickerViewController.swift; sourceTree = ""; }; AUTH008 /* SessionDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionDetailViewController.swift; sourceTree = ""; }; AUTH010 /* bootconfig.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = bootconfig.plist; sourceTree = ""; }; AUTH012 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -67,7 +64,6 @@ AUTH043 /* OAuthConfigurationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OAuthConfigurationView.swift; sourceTree = ""; }; AUTH050 /* JwtAccessView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JwtAccessView.swift; sourceTree = ""; }; AUTH052 /* InfoRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoRowView.swift; sourceTree = ""; }; - AUTH054 /* FlowTypesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FlowTypesView.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -153,14 +149,12 @@ AUTH039 /* Views */ = { isa = PBXGroup; children = ( - 4F1A8CCC2EAFEA7C0037DC89 /* BootConfigEditor.swift */, 4FEBAF282EA9B91500D4880A /* RevokeView.swift */, AUTH038 /* UserCredentialsView.swift */, AUTH041 /* RestApiTestView.swift */, AUTH043 /* OAuthConfigurationView.swift */, AUTH050 /* JwtAccessView.swift */, AUTH052 /* InfoRowView.swift */, - AUTH054 /* FlowTypesView.swift */, ); path = Views; sourceTree = ""; @@ -168,7 +162,7 @@ AUTH044 /* ViewControllers */ = { isa = PBXGroup; children = ( - AUTH006 /* ConfigPickerViewController.swift */, + 4FE006B42EBEB94100CFD66F /* InitialViewController.swift */, AUTH008 /* SessionDetailViewController.swift */, ); path = ViewControllers; @@ -244,16 +238,14 @@ files = ( AUTH001 /* AppDelegate.swift in Sources */, AUTH003 /* SceneDelegate.swift in Sources */, - AUTH005 /* ConfigPickerViewController.swift in Sources */, AUTH007 /* SessionDetailViewController.swift in Sources */, AUTH037 /* UserCredentialsView.swift in Sources */, AUTH040 /* RestApiTestView.swift in Sources */, + 4FE006B52EBEB94700CFD66F /* InitialViewController.swift in Sources */, AUTH042 /* OAuthConfigurationView.swift in Sources */, 4FEBAF292EA9B91500D4880A /* RevokeView.swift in Sources */, AUTH049 /* JwtAccessView.swift in Sources */, AUTH051 /* InfoRowView.swift in Sources */, - 4F1A8CCD2EAFEA7C0037DC89 /* BootConfigEditor.swift in Sources */, - AUTH053 /* FlowTypesView.swift in Sources */, ); }; /* End PBXSourcesBuildPhase section */ diff --git a/native/SampleApps/AuthFlowTester/AuthFlowTester/Classes/SceneDelegate.swift b/native/SampleApps/AuthFlowTester/AuthFlowTester/Classes/SceneDelegate.swift index 6db5aa4bd4..f6e399a8ce 100644 --- a/native/SampleApps/AuthFlowTester/AuthFlowTester/Classes/SceneDelegate.swift +++ b/native/SampleApps/AuthFlowTester/AuthFlowTester/Classes/SceneDelegate.swift @@ -3,7 +3,7 @@ // AuthFlowTester // // Copyright (c) 2025-present, salesforce.com, inc. All rights reserved. -// +// // Redistribution and use of this software in source and binary forms, with or without modification, // are permitted provided that the following conditions are met: // * Redistributions of source code must retain the above copyright notice, this list of conditions @@ -14,7 +14,7 @@ // * Neither the name of salesforce.com, inc. nor the names of its contributors may be used to // endorse or promote products derived from this software without specific prior written // permission of salesforce.com, inc. -// +// // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR @@ -31,7 +31,7 @@ import SalesforceSDKCore import SwiftUI class SceneDelegate: UIResponder, UIWindowSceneDelegate { - + public var window: UIWindow? func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { @@ -45,30 +45,33 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { } } self.initializeAppViewState() + AuthHelper.loginIfRequired(scene) { + self.setupRootViewController() + } } - + func sceneDidDisconnect(_ scene: UIScene) { // Called as the scene is being released by the system. // This occurs shortly after the scene enters the background, or when its session is discarded. // Release any resources associated with this scene that can be re-created the next time the scene connects. // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). } - + func sceneDidBecomeActive(_ scene: UIScene) { // Called when the scene has moved from an inactive state to an active state. // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. } - + func sceneWillResignActive(_ scene: UIScene) { // Called when the scene will move from an active state to an inactive state. // This may occur due to temporary interruptions (ex. an incoming phone call). } - + func sceneWillEnterForeground(_ scene: UIScene) { // Called as the scene transitions from the background to the foreground. // Use this method to undo the changes made on entering the background. } - + func sceneDidEnterBackground(_ scene: UIScene) { // Called as the scene transitions from the foreground to the background. // Use this method to save data, release shared resources, and store enough scene-specific state information @@ -77,54 +80,37 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { func scene(_ scene: UIScene, openURLContexts URLContexts: Set) { // Uncomment following block to enable IDP Login flow -// if let urlContext = URLContexts.first { -// UserAccountManager.shared.handleIdentityProviderResponse(from: urlContext.url, with: [UserAccountManager.IDPSceneKey: scene.session.persistentIdentifier]) -// } + // if let urlContext = URLContexts.first { + // UserAccountManager.shared.handleIdentityProviderResponse(from: urlContext.url, with: [UserAccountManager.IDPSceneKey: scene.session.persistentIdentifier]) + // } } // MARK: - Private methods - func initializeAppViewState() { - if (!Thread.isMainThread) { - DispatchQueue.main.async { - self.initializeAppViewState() - } - return - } - - // Check ProcessInfo arguments for CONFIG_PICKER flag - let shouldShowConfigPicker = ProcessInfo.processInfo.arguments.contains("CONFIG_PICKER") - - // Check if user is already logged in - if UserAccountManager.shared.currentUserAccount != nil && !shouldShowConfigPicker { - // User is already logged in and not in config picker mode, go directly to session detail - self.setupRootViewController() - } else { - // User is not logged in or config picker mode is enabled, show config picker - self.window?.rootViewController = UIHostingController(rootView: ConfigPickerView(onConfigurationCompleted: onConfigurationCompleted)) - } - self.window?.makeKeyAndVisible() - } - - func setupRootViewController() { + func initializeAppViewState() { + if (!Thread.isMainThread) { + DispatchQueue.main.async { + self.initializeAppViewState() + } + return + } + + self.window?.rootViewController = InitialViewController(nibName: nil, bundle: nil) + self.window?.makeKeyAndVisible() + } + + func setupRootViewController() { let rootVC = SessionDetailViewController() let navVC = UINavigationController(rootViewController: rootVC) self.window!.rootViewController = navVC } - - func resetViewState(_ postResetBlock: @escaping () -> ()) { - if let rootViewController = self.window?.rootViewController { - if let _ = rootViewController.presentedViewController { - rootViewController.dismiss(animated: false, completion: postResetBlock) - return - } - } - postResetBlock() - } - - func onConfigurationCompleted() { - guard let windowScene = self.window?.windowScene else { return } - AuthHelper.loginIfRequired(windowScene) { - self.setupRootViewController() - } - } + + func resetViewState(_ postResetBlock: @escaping () -> ()) { + if let rootViewController = self.window?.rootViewController { + if let _ = rootViewController.presentedViewController { + rootViewController.dismiss(animated: false, completion: postResetBlock) + return + } + } + postResetBlock() + } } diff --git a/native/SampleApps/AuthFlowTester/AuthFlowTester/ViewControllers/ConfigPickerViewController.swift b/native/SampleApps/AuthFlowTester/AuthFlowTester/ViewControllers/ConfigPickerViewController.swift deleted file mode 100644 index f4ad703567..0000000000 --- a/native/SampleApps/AuthFlowTester/AuthFlowTester/ViewControllers/ConfigPickerViewController.swift +++ /dev/null @@ -1,204 +0,0 @@ -/* - ConfigPickerViewController.swift - AuthFlowTester - - Copyright (c) 2025-present, salesforce.com, inc. All rights reserved. - - Redistribution and use of this software in source and binary forms, with or without modification, - are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright notice, this list of conditions - and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright notice, this list of - conditions and the following disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of salesforce.com, inc. nor the names of its contributors may be used to - endorse or promote products derived from this software without specific prior written - permission of salesforce.com, inc. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR - IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND - FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR - CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY - WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -import SwiftUI -import SalesforceSDKCore - -struct ConfigPickerView: View { - @State private var isLoading = false - @State private var staticConsumerKey = "" - @State private var staticCallbackUrl = "" - @State private var staticScopes = "" - @State private var dynamicConsumerKey = "" - @State private var dynamicCallbackUrl = "" - @State private var dynamicScopes = "" - - let onConfigurationCompleted: () -> Void - - var body: some View { - NavigationView { - ScrollView { - VStack(spacing: 30) { - // Flow types section - FlowTypesView() - .padding(.top, 20) - - Divider() - - // Static config section - BootConfigEditor( - title: "Static Configuration", - buttonLabel: "Use static config", - buttonColor: .blue, - consumerKey: $staticConsumerKey, - callbackUrl: $staticCallbackUrl, - scopes: $staticScopes, - isLoading: isLoading, - onUseConfig: handleStaticConfig, - initiallyExpanded: false - ) - - Divider() - - // Dynamic config section - BootConfigEditor( - title: "Dynamic Configuration", - buttonLabel: "Use dynamic config", - buttonColor: .green, - consumerKey: $dynamicConsumerKey, - callbackUrl: $dynamicCallbackUrl, - scopes: $dynamicScopes, - isLoading: isLoading, - onUseConfig: handleDynamicBootconfig, - initiallyExpanded: false - ) - - // Loading indicator - if isLoading { - ProgressView("Authenticating...") - .padding() - } - } - .padding(.bottom, 40) - } - .background(Color(.systemBackground)) - .navigationTitle(appName) - .navigationBarTitleDisplayMode(.large) - } - .navigationViewStyle(.stack) - .onAppear { - loadConfigDefaults() - } - } - - // MARK: - Computed Properties - - private var appName: String { - guard let info = Bundle.main.infoDictionary, - let name = info[kCFBundleNameKey as String] as? String else { - return "AuthFlowTester" - } - return name - } - - // MARK: - Helper Methods - - private func loadConfigDefaults() { - // Load static config from bootconfig.plist (via SalesforceManager) - if let config = SalesforceManager.shared.bootConfig { - staticConsumerKey = config.remoteAccessConsumerKey - staticCallbackUrl = config.oauthRedirectURI - staticScopes = config.oauthScopes.sorted().joined(separator: " ") - } - - // Load dynamic config defaults from bootconfig2.plist - if let config = BootConfig("/bootconfig2.plist") { - dynamicConsumerKey = config.remoteAccessConsumerKey - dynamicCallbackUrl = config.oauthRedirectURI - dynamicScopes = config.oauthScopes.sorted().joined(separator: " ") - } - } - - // MARK: - Button Actions - - private func handleStaticConfig() { - isLoading = true - - // Parse scopes from space-separated string - let scopesArray = staticScopes - .split(separator: " ") - .map { String($0) } - .filter { !$0.isEmpty } - - // Create BootConfig with values from the editor - var configDict: [String: Any] = [ - "remoteAccessConsumerKey": staticConsumerKey, - "oauthRedirectURI": staticCallbackUrl, - "shouldAuthenticate": true - ] - - // Only add scopes if not empty - if !scopesArray.isEmpty { - configDict["oauthScopes"] = scopesArray - } - - // Set as the bootConfig - SalesforceManager.shared.bootConfig = BootConfig(configDict) - SalesforceManager.shared.bootConfigRuntimeSelector = nil - - // Update UserAccountManager properties - UserAccountManager.shared.oauthClientID = staticConsumerKey - UserAccountManager.shared.oauthCompletionURL = staticCallbackUrl - UserAccountManager.shared.scopes = scopesArray.isEmpty ? [] : Set(scopesArray) - - // Proceed with login - onConfigurationCompleted() - } - - private func handleDynamicBootconfig() { - isLoading = true - - SalesforceManager.shared.bootConfigRuntimeSelector = { _, callback in - // Create dynamic BootConfig from user-entered values - // Parse scopes from space-separated string - let scopesArray = self.dynamicScopes - .split(separator: " ") - .map { String($0) } - .filter { !$0.isEmpty } - - var configDict: [String: Any] = [ - "remoteAccessConsumerKey": self.dynamicConsumerKey, - "oauthRedirectURI": self.dynamicCallbackUrl, - "shouldAuthenticate": true - ] - - // Only add scopes if not empty - if !scopesArray.isEmpty { - configDict["oauthScopes"] = scopesArray - } - - callback(BootConfig(configDict)) - } - - // Proceed with login - onConfigurationCompleted() - } -} - -// MARK: - UIViewControllerRepresentable - -struct ConfigPickerViewController: UIViewControllerRepresentable { - let onConfigurationCompleted: () -> Void - - func makeUIViewController(context: Context) -> UIHostingController { - return UIHostingController(rootView: ConfigPickerView(onConfigurationCompleted: onConfigurationCompleted)) - } - - func updateUIViewController(_ uiViewController: UIHostingController, context: Context) { - // No updates needed - } -} diff --git a/native/SampleApps/AuthFlowTester/AuthFlowTester/ViewControllers/InitialViewController.swift b/native/SampleApps/AuthFlowTester/AuthFlowTester/ViewControllers/InitialViewController.swift new file mode 100644 index 0000000000..aaf7e2df55 --- /dev/null +++ b/native/SampleApps/AuthFlowTester/AuthFlowTester/ViewControllers/InitialViewController.swift @@ -0,0 +1,56 @@ +// +// InitialViewController.swift +// AuthFlowTester +// +// Created by Wolfgang Mathurin on 11/7/25. +// Copyright (c) 2025-present, salesforce.com, inc. All rights reserved. +// +// Redistribution and use of this software in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// * Redistributions of source code must retain the above copyright notice, this list of conditions +// and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, this list of +// conditions and the following disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of salesforce.com, inc. nor the names of its contributors may be used to +// endorse or promote products derived from this software without specific prior written +// permission of salesforce.com, inc. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR +// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +// FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY +// WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +import UIKit +import SalesforceSDKCore.UIColor_SFColors + +class InitialViewController: UIViewController { + + override func viewDidLoad() { + super.viewDidLoad() + + self.view.backgroundColor = UIColor.salesforceSystemBackground + + let label = UILabel() + label.translatesAutoresizingMaskIntoConstraints = false + guard let info = Bundle.main.infoDictionary, let name = info[kCFBundleNameKey as String] else { return } + label.font = UIFont.systemFont(ofSize: 29) + label.textColor = UIColor.black + label.text = name as? String + + self.view.addSubview(label) + label.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true + label.centerYAnchor.constraint(equalTo: self.view.centerYAnchor).isActive = true + // Do any additional setup after loading the view, typically from a nib. + } + + override func didReceiveMemoryWarning() { + super.didReceiveMemoryWarning() + // Dispose of any resources that can be recreated. + } + +} diff --git a/native/SampleApps/AuthFlowTester/AuthFlowTester/ViewControllers/SessionDetailViewController.swift b/native/SampleApps/AuthFlowTester/AuthFlowTester/ViewControllers/SessionDetailViewController.swift index 461a704a68..aa7f8b0002 100644 --- a/native/SampleApps/AuthFlowTester/AuthFlowTester/ViewControllers/SessionDetailViewController.swift +++ b/native/SampleApps/AuthFlowTester/AuthFlowTester/ViewControllers/SessionDetailViewController.swift @@ -31,7 +31,7 @@ import SalesforceSDKCore struct SessionDetailView: View { @State private var refreshTrigger = UUID() @State private var showMigrateRefreshToken = false - @State private var showLogoutConfigPicker = false + @State private var showLogoutConfirmation = false @State private var isUserCredentialsExpanded = false @State private var isJwtDetailsExpanded = false @State private var isOAuthConfigExpanded = false @@ -102,28 +102,19 @@ struct SessionDetailView: View { Spacer() Button(action: { - showLogoutConfigPicker = true + showLogoutConfirmation = true }) { Label("Logout", systemImage: "rectangle.portrait.and.arrow.right") } } } - .sheet(isPresented: $showLogoutConfigPicker) { - NavigationView { - ConfigPickerView(onConfigurationCompleted: { - showLogoutConfigPicker = false - onLogout() - }) - .navigationTitle("Select Config for Re-login") - .navigationBarTitleDisplayMode(.inline) - .toolbar { - ToolbarItem(placement: .cancellationAction) { - Button("Cancel") { - showLogoutConfigPicker = false - } - } - } + .alert("Logout", isPresented: $showLogoutConfirmation) { + Button("Cancel", role: .cancel) { } + Button("Logout", role: .destructive) { + onLogout() } + } message: { + Text("Are you sure you want to logout?") } .sheet(isPresented: $showMigrateRefreshToken) { NavigationView { @@ -135,7 +126,7 @@ struct SessionDetailView: View { consumerKey: $migrateConsumerKey, callbackUrl: $migrateCallbackUrl, scopes: $migrateScopes, - isLoading: isMigrating, + isLoading: false, onUseConfig: { handleMigrateRefreshToken() },