Skip to content

Commit 01a3ac3

Browse files
authored
Merge pull request #3955 from wmathurin/better_authflow_tester
Enhancements for AuthFlowTester
2 parents ee6a970 + a168286 commit 01a3ac3

File tree

6 files changed

+362
-66
lines changed

6 files changed

+362
-66
lines changed

native/SampleApps/AuthFlowTester/AuthFlowTester.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10+
4F5945B82ED51C00003C5BDE /* InfoSectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F5945B72ED51C00003C5BDE /* InfoSectionView.swift */; };
1011
4F95A89C2EA806E700C98D18 /* SalesforceAnalytics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4F95A8962EA801DC00C98D18 /* SalesforceAnalytics.framework */; };
1112
4F95A89D2EA806E700C98D18 /* SalesforceAnalytics.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 4F95A8962EA801DC00C98D18 /* SalesforceAnalytics.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
1213
4F95A89F2EA806E900C98D18 /* SalesforceSDKCommon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4F95A8982EA801DC00C98D18 /* SalesforceSDKCommon.framework */; };
@@ -44,6 +45,7 @@
4445
/* End PBXCopyFilesBuildPhase section */
4546

4647
/* Begin PBXFileReference section */
48+
4F5945B72ED51C00003C5BDE /* InfoSectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InfoSectionView.swift; sourceTree = "<group>"; };
4749
4F95A8962EA801DC00C98D18 /* SalesforceAnalytics.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SalesforceAnalytics.framework; sourceTree = BUILT_PRODUCTS_DIR; };
4850
4F95A8982EA801DC00C98D18 /* SalesforceSDKCommon.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SalesforceSDKCommon.framework; sourceTree = BUILT_PRODUCTS_DIR; };
4951
4F95A89A2EA801DC00C98D18 /* SalesforceSDKCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = SalesforceSDKCore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -149,6 +151,7 @@
149151
AUTH039 /* Views */ = {
150152
isa = PBXGroup;
151153
children = (
154+
4F5945B72ED51C00003C5BDE /* InfoSectionView.swift */,
152155
4FEBAF282EA9B91500D4880A /* RevokeView.swift */,
153156
AUTH038 /* UserCredentialsView.swift */,
154157
AUTH041 /* RestApiTestView.swift */,
@@ -246,6 +249,7 @@
246249
4FEBAF292EA9B91500D4880A /* RevokeView.swift in Sources */,
247250
AUTH049 /* JwtAccessView.swift in Sources */,
248251
AUTH051 /* InfoRowView.swift in Sources */,
252+
4F5945B82ED51C00003C5BDE /* InfoSectionView.swift in Sources */,
249253
);
250254
};
251255
/* End PBXSourcesBuildPhase section */

native/SampleApps/AuthFlowTester/AuthFlowTester/ViewControllers/SessionDetailViewController.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,7 @@ struct SessionDetailView: View {
6060
})
6161

6262
// User Credentials Section
63-
UserCredentialsView(isExpanded: $isUserCredentialsExpanded)
64-
.id(refreshTrigger)
63+
UserCredentialsView(isExpanded: $isUserCredentialsExpanded, refreshTrigger: refreshTrigger)
6564

6665
// JWT Access Token Details Section (if applicable)
6766
if let credentials = UserAccountManager.shared.currentUserAccount?.credentials,

native/SampleApps/AuthFlowTester/AuthFlowTester/Views/InfoRowView.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ struct InfoRowView: View {
4040
.font(.caption)
4141
.foregroundColor(.secondary)
4242

43-
if isSensitive && !isRevealed {
43+
if isSensitive && !isRevealed && !value.isEmpty {
4444
HStack {
4545
Text(maskedValue)
4646
.font(.system(.caption, design: .monospaced))
@@ -54,9 +54,8 @@ struct InfoRowView: View {
5454
HStack {
5555
Text(value.isEmpty ? "(empty)" : value)
5656
.font(.system(.caption, design: .monospaced))
57-
.foregroundColor(value.isEmpty ? .secondary : .primary)
5857
Spacer()
59-
if isSensitive {
58+
if isSensitive && !value.isEmpty {
6059
Button(action: { isRevealed.toggle() }) {
6160
Image(systemName: "eye.slash")
6261
.foregroundColor(.blue)
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
InfoSectionView.swift
3+
AuthFlowTester
4+
5+
Copyright (c) 2025-present, salesforce.com, inc. All rights reserved.
6+
7+
Redistribution and use of this software in source and binary forms, with or without modification,
8+
are permitted provided that the following conditions are met:
9+
* Redistributions of source code must retain the above copyright notice, this list of conditions
10+
and the following disclaimer.
11+
* Redistributions in binary form must reproduce the above copyright notice, this list of
12+
conditions and the following disclaimer in the documentation and/or other materials provided
13+
with the distribution.
14+
* Neither the name of salesforce.com, inc. nor the names of its contributors may be used to
15+
endorse or promote products derived from this software without specific prior written
16+
permission of salesforce.com, inc.
17+
18+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
19+
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20+
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
21+
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24+
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
25+
WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26+
*/
27+
28+
import SwiftUI
29+
30+
struct InfoSectionView<Content: View>: View {
31+
let title: String
32+
@Binding var isExpanded: Bool
33+
let content: Content
34+
35+
init(title: String, isExpanded: Binding<Bool>, @ViewBuilder content: () -> Content) {
36+
self.title = title
37+
self._isExpanded = isExpanded
38+
self.content = content()
39+
}
40+
41+
var body: some View {
42+
VStack(alignment: .leading, spacing: 8) {
43+
Button(action: {
44+
withAnimation {
45+
isExpanded.toggle()
46+
}
47+
}) {
48+
HStack {
49+
Text(title)
50+
.font(.subheadline)
51+
.fontWeight(.semibold)
52+
.foregroundColor(.primary)
53+
Spacer()
54+
Image(systemName: isExpanded ? "chevron.up" : "chevron.down")
55+
.font(.caption2)
56+
.foregroundColor(.secondary)
57+
}
58+
}
59+
.padding(.horizontal, 8)
60+
.padding(.vertical, 6)
61+
.background(Color(.tertiarySystemBackground))
62+
.cornerRadius(6)
63+
64+
if isExpanded {
65+
content
66+
.padding(.leading, 8)
67+
}
68+
}
69+
}
70+
}
71+

native/SampleApps/AuthFlowTester/AuthFlowTester/Views/RestApiTestView.swift

Lines changed: 63 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,15 @@ import SwiftUI
2929
import SalesforceSDKCore
3030

3131
struct RestApiTestView: View {
32+
enum AlertType {
33+
case success
34+
case error(String)
35+
}
36+
3237
@State private var isLoading = false
3338
@State private var lastRequestResult: String = ""
3439
@State private var isResultExpanded = false
40+
@State private var alertType: AlertType?
3541

3642
let onRequestCompleted: () -> Void
3743

@@ -59,57 +65,73 @@ struct RestApiTestView: View {
5965
}
6066
.disabled(isLoading)
6167

62-
// Result section - always visible
63-
VStack(alignment: .leading, spacing: 8) {
64-
Button(action: {
65-
if !lastRequestResult.isEmpty {
68+
// Response details section - collapsible
69+
if !lastRequestResult.isEmpty {
70+
VStack(alignment: .leading, spacing: 8) {
71+
Button(action: {
6672
withAnimation {
6773
isResultExpanded.toggle()
6874
}
69-
}
70-
}) {
71-
HStack {
72-
Text("Last Request Result:")
73-
.font(.subheadline)
74-
.fontWeight(.semibold)
75-
.foregroundColor(.primary)
76-
Spacer()
77-
if !lastRequestResult.isEmpty {
75+
}) {
76+
HStack {
77+
Text("Response Details")
78+
.font(.subheadline)
79+
.fontWeight(.semibold)
80+
.foregroundColor(.primary)
81+
Spacer()
7882
Image(systemName: isResultExpanded ? "chevron.up" : "chevron.down")
7983
.font(.caption)
8084
.foregroundColor(.secondary)
8185
}
82-
}
83-
}
84-
.disabled(lastRequestResult.isEmpty)
85-
86-
if lastRequestResult.isEmpty {
87-
Text("No request made yet")
88-
.font(.system(.caption, design: .monospaced))
89-
.foregroundColor(.secondary)
9086
.padding(8)
91-
.frame(maxWidth: .infinity, alignment: .leading)
87+
.background(Color(.tertiarySystemBackground))
88+
.cornerRadius(6)
89+
}
90+
91+
if isResultExpanded {
92+
ScrollView([.vertical, .horizontal], showsIndicators: true) {
93+
Text(lastRequestResult)
94+
.font(.system(.caption, design: .monospaced))
95+
.foregroundColor(.primary)
96+
.padding(8)
97+
.frame(maxWidth: .infinity, alignment: .leading)
98+
.textSelection(.enabled)
99+
}
100+
.frame(minHeight: 200, maxHeight: 400)
92101
.background(Color(.systemGray6))
93102
.cornerRadius(4)
94-
} else if isResultExpanded {
95-
ScrollView([.vertical, .horizontal], showsIndicators: true) {
96-
Text(lastRequestResult)
97-
.font(.system(.caption, design: .monospaced))
98-
.foregroundColor(lastRequestResult.hasPrefix("") ? .green : .red)
99-
.padding(8)
100-
.frame(maxWidth: .infinity, alignment: .leading)
101-
.textSelection(.enabled)
102103
}
103-
.frame(minHeight: 200, maxHeight: 400)
104-
.background(Color(.systemGray6))
105-
.cornerRadius(4)
106104
}
105+
.padding(.vertical, 4)
107106
}
108-
.padding(.vertical, 4)
109107
}
110108
.padding()
111109
.background(Color(.secondarySystemBackground))
112110
.cornerRadius(8)
111+
.alert(item: Binding(
112+
get: { alertType.map { AlertItem(type: $0) } },
113+
set: { alertType = $0?.type }
114+
)) { alertItem in
115+
switch alertItem.type {
116+
case .success:
117+
return Alert(
118+
title: Text("Request Successful"),
119+
message: Text("The REST API request completed successfully. Expand 'Response Details' below to see the full response."),
120+
dismissButton: .default(Text("OK"))
121+
)
122+
case .error(let message):
123+
return Alert(
124+
title: Text("Request Failed"),
125+
message: Text(message),
126+
dismissButton: .default(Text("OK"))
127+
)
128+
}
129+
}
130+
}
131+
132+
struct AlertItem: Identifiable {
133+
let id = UUID()
134+
let type: AlertType
113135
}
114136

115137
// MARK: - REST API Request
@@ -118,22 +140,25 @@ struct RestApiTestView: View {
118140
private func makeRestRequest() async {
119141
isLoading = true
120142
lastRequestResult = ""
143+
isResultExpanded = false // Start collapsed
121144

122145
do {
123146
let request = RestClient.shared.cheapRequest("v63.0")
124147
let response = try await RestClient.shared.send(request: request)
125148

126149
// Request succeeded - pretty print the JSON
127150
let prettyJSON = prettyPrintJSON(response.asString())
128-
lastRequestResult = "✓ Success:\n\n\(prettyJSON)"
129-
isResultExpanded = true // Auto-expand on new result
151+
lastRequestResult = prettyJSON
152+
alertType = .success
153+
// Response starts collapsed - user can expand to see details
130154

131155
// Notify parent to refresh fields
132156
onRequestCompleted()
133157
} catch {
134158
// Request failed
135-
lastRequestResult = "✗ Error: \(error.localizedDescription)"
136-
isResultExpanded = true // Auto-expand on error
159+
lastRequestResult = error.localizedDescription
160+
alertType = .error(error.localizedDescription)
161+
// Error details start collapsed - user can expand to see details
137162
}
138163

139164
isLoading = false

0 commit comments

Comments
 (0)