Skip to content

Commit 33074eb

Browse files
committed
Merge branch 'dev' of github.com:wmathurin/SalesforceMobileSDK-iOS into auth_ui_tests
2 parents 5d081dc + d124a37 commit 33074eb

File tree

9 files changed

+163
-107
lines changed

9 files changed

+163
-107
lines changed

.github/workflows/nightly.yaml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ jobs:
2020
xcode: ^16
2121
- ios: ^17
2222
xcode: ^16
23-
uses: ./.github/workflows/reusable-workflow.yaml
23+
uses: ./.github/workflows/reusable-test-workflow.yaml
2424
with:
2525
lib: ${{ matrix.lib }}
2626
ios: ${{ matrix.ios }}
@@ -40,10 +40,9 @@ jobs:
4040
xcode: ^16
4141
- ios: ^17
4242
xcode: ^16
43-
uses: ./.github/workflows/reusable-workflow.yaml
43+
uses: ./.github/workflows/reusable-build-workflow.yaml
4444
with:
45-
lib: ${{ matrix.app }}
45+
app: ${{ matrix.app }}
4646
ios: ${{ matrix.ios }}
4747
xcode: ${{ matrix.xcode }}
48-
build_only: true
4948
secrets: inherit

.github/workflows/pr.yaml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ jobs:
108108
xcode: ^26
109109
- ios: ^18
110110
xcode: ^16
111-
uses: ./.github/workflows/reusable-workflow.yaml
111+
uses: ./.github/workflows/reusable-test-workflow.yaml
112112
with:
113113
lib: ${{ matrix.lib }}
114114
ios: ${{ matrix.ios }}
@@ -127,11 +127,10 @@ jobs:
127127
xcode: ^26
128128
- ios: ^18
129129
xcode: ^16
130-
uses: ./.github/workflows/reusable-workflow.yaml
130+
uses: ./.github/workflows/reusable-build-workflow.yaml
131131
with:
132-
lib: ${{ matrix.app }}
132+
app: ${{ matrix.app }}
133133
ios: ${{ matrix.ios }}
134134
xcode: ${{ matrix.xcode }}
135135
is_pr: true
136-
build_only: true
137136
secrets: inherit
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
on:
2+
workflow_call:
3+
inputs:
4+
app:
5+
required: true
6+
type: string
7+
ios:
8+
default: "^18"
9+
required: false
10+
type: string
11+
xcode:
12+
default: "^16"
13+
required: false
14+
type: string
15+
macos:
16+
default: macos-latest
17+
required: false
18+
type: string
19+
is_pr:
20+
type: boolean
21+
default: false
22+
23+
jobs:
24+
build-sample-app:
25+
runs-on: ${{ inputs.macos }}
26+
steps:
27+
- uses: actions/checkout@v4
28+
if: ${{ inputs.is_pr }}
29+
with:
30+
ref: ${{ github.event.pull_request.head.sha }}
31+
- uses: actions/checkout@v4
32+
if: ${{ !inputs.is_pr }}
33+
with:
34+
ref: ${{ github.head_ref }}
35+
- name: Install Dependencies
36+
run: ./install.sh
37+
- name: Install iOS 17 runtime if needed
38+
if: ${{ inputs.ios == '^17' }}
39+
run: xcodes runtimes install "iOS 17.5"
40+
- uses: mxcl/xcodebuild@v3
41+
with:
42+
xcode: ${{ inputs.xcode }}
43+
platform: iOS
44+
platform-version: ${{ inputs.ios }}
45+
workspace: SalesforceMobileSDK.xcworkspace
46+
scheme: ${{ inputs.app }}
47+
action: 'build'
48+
verbosity: xcbeautify

.github/workflows/reusable-workflow.yaml renamed to .github/workflows/reusable-test-workflow.yaml

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,6 @@ on:
1919
is_pr:
2020
type: boolean
2121
default: false
22-
build_only:
23-
type: boolean
24-
default: false
2522

2623
jobs:
2724
test-ios:
@@ -32,7 +29,7 @@ jobs:
3229
with:
3330
ref: ${{ github.event.pull_request.head.sha }}
3431
- uses: actions/checkout@v4
35-
if: ${{ ! inputs.is_pr }}
32+
if: ${{ !inputs.is_pr }}
3633
with:
3734
ref: ${{ github.head_ref }}
3835
- name: Install Dependencies
@@ -45,33 +42,42 @@ jobs:
4542
if: ${{ inputs.ios == '^17' }}
4643
run: xcodes runtimes install "iOS 17.5"
4744
- uses: mxcl/xcodebuild@v3
45+
id: xcodebuild
4846
with:
4947
xcode: ${{ inputs.xcode }}
5048
platform: iOS
5149
platform-version: ${{ inputs.ios }}
5250
workspace: SalesforceMobileSDK.xcworkspace
5351
scheme: ${{ inputs.lib }}
54-
action: ${{ inputs.build_only && 'build' || 'test' }}
55-
code-coverage: ${{ ! inputs.build_only }}
52+
code-coverage: true
5653
verbosity: xcbeautify
57-
- uses: slidoapp/[email protected]
58-
with:
59-
path: test.xcresult
60-
title: "${{ inputs.lib }} iOS ${{ inputs.ios }}"
61-
show-code-coverage: false
62-
upload-bundles: false
63-
show-passed-tests: false
64-
# xcresulttool currently fails for iOS 26
65-
if: ${{ ! inputs.build_only && (success() || failure()) && inputs.ios != '^26' }}
66-
- name: Upload artifact
67-
uses: actions/upload-artifact@v4
54+
- name: Parse test results
55+
if: success() || failure()
56+
run: |
57+
brew install xcresultparser
58+
xcresultparser -o junit test.xcresult > test-results-${{ inputs.lib }}-ios${{ inputs.ios }}.xml
59+
- name: Test Report
60+
uses: mikepenz/action-junit-report@v5
61+
if: success() || failure()
6862
with:
69-
name: test-results-${{ inputs.lib }}-ios${{ inputs.ios }}
70-
path: test.xcresult
71-
if: ${{ ! inputs.build_only && (success() || failure()) }}
63+
check_name: ${{ inputs.lib }} iOS ${{ inputs.ios }} Test Results
64+
job_name: ${{ inputs.lib }} iOS ${{ inputs.ios }} Test Results
65+
require_tests: true
66+
include_empty_in_summary: false
67+
simplified_summary: true
68+
detailed_summary: true
69+
comment: true
70+
job_summary: ${{ steps.xcodebuild.outcome == 'failure' }}
71+
report_paths: 'test-results-${{ inputs.lib }}-ios${{ inputs.ios }}.xml'
7272
- uses: codecov/codecov-action@v4
73+
if: success() || failure()
7374
with:
7475
flags: ${{ inputs.lib }}
7576
env:
7677
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
77-
if: ${{ ! inputs.build_only && (success() || failure()) }}
78+
- name: Upload test results artifact
79+
if: success() || failure()
80+
uses: actions/upload-artifact@v4
81+
with:
82+
name: test-results-${{ inputs.lib }}-ios${{ inputs.ios }}
83+
path: test-results-${{ inputs.lib }}-ios${{ inputs.ios }}.xml

libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Extensions/PushNotificationManager+ActionableNotifications.swift

Lines changed: 1 addition & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ internal extension PushNotificationManager {
153153
}
154154

155155
let categories = filterTypes
156-
.flatMap { createNotificationCategories(from: $0) }
156+
.flatMap { NotificationCategoryFactory.shared.createCategories(from: [$0]) }
157157
UNUserNotificationCenter.current().setNotificationCategories(Set(categories))
158158
}
159159

@@ -163,34 +163,7 @@ internal extension PushNotificationManager {
163163
}
164164

165165
private extension PushNotificationManager {
166-
167-
func createNotificationCategories(from type: NotificationType) -> [UNNotificationCategory] {
168-
guard let actionGroups = type.actionGroups else { return [] }
169166

170-
return actionGroups.map { group in
171-
let actions = createActions(from: group)
172-
return UNNotificationCategory(
173-
identifier: group.name,
174-
actions: actions,
175-
intentIdentifiers: []
176-
)
177-
}
178-
}
179-
180-
func createActions(from actionGroup: ActionGroup?) -> [UNNotificationAction] {
181-
guard let actionGroup = actionGroup else {
182-
return []
183-
}
184-
185-
return actionGroup.actions.compactMap { action in
186-
UNNotificationAction(
187-
identifier: action.identifier,
188-
title: action.label,
189-
options: [.foreground] // Ensures the app opens if needed
190-
)
191-
}
192-
}
193-
194167
func getNotificationType(apiName: String, account: UserAccount) -> NotificationType? {
195168
guard let notificationTypes = account.notificationTypes else { return nil }
196169
return notificationTypes.first { $0.apiName == apiName }

libs/SalesforceSDKCore/SalesforceSDKCore/Classes/Extensions/RestClient.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -329,3 +329,28 @@ extension RestClient {
329329
return self.records(forRequest: request, withDecoder: decoder)
330330
}
331331
}
332+
333+
// MARK: Requests
334+
extension RestClient {
335+
336+
/// Creates a RestRequest object that revokes the access token for a user
337+
/// - Parameters:
338+
/// - user: User to revoken the token for, current user will be used if none provided
339+
/// - Returns: A `RestRequest` object to revoke the access token if access token is present, nil otherwise
340+
@objc
341+
public func requestForRevokeAccessToken(user: UserAccount? = UserAccountManager.shared.currentUserAccount) -> RestRequest? {
342+
guard let user = user ?? UserAccountManager.shared.currentUserAccount,
343+
let accessToken = user.credentials.accessToken,
344+
let encodedToken = accessToken.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {
345+
return nil
346+
}
347+
348+
let request = RestRequest(method: .POST, path: "/services/oauth2/revoke", queryParams: nil)
349+
request.endpoint = ""
350+
351+
// Set the request body with URL-encoded token
352+
let bodyString = "token=\(encodedToken)"
353+
request.setCustomRequestBodyString(bodyString, contentType: "application/x-www-form-urlencoded")
354+
return request
355+
}
356+
}

libs/SalesforceSDKCore/SalesforceSDKCore/Classes/RestAPI/SFRestAPI.m

Lines changed: 17 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -287,14 +287,14 @@ - (void)send:(SFRestRequest *)request
287287
[[SFUserAccountManager sharedInstance] loginWithCompletion:^(SFOAuthInfo *authInfo, SFUserAccount *userAccount) {
288288
__strong typeof(weakSelf) strongSelf = weakSelf;
289289
strongSelf.user = userAccount;
290-
[strongSelf enqueueRequest:request failureBlock:failureBlock successBlock:successBlock shouldRetry:shouldRetry];
290+
[strongSelf enqueueRequest:request shouldRetry:shouldRetry];
291291
} failure:^(SFOAuthInfo *authInfo, NSError *error) {
292292
__strong typeof(weakSelf) strongSelf = weakSelf;
293293
[SFSDKCoreLogger e:[strongSelf class] format:@"Authentication failed in SFRestAPI: %@. Logging out.", error];
294294
[[SFUserAccountManager sharedInstance] logout:SFLogoutReasonUnexpected];
295295
}];
296296
} else {
297-
[self enqueueRequest:request failureBlock:failureBlock successBlock:successBlock shouldRetry:shouldRetry];
297+
[self enqueueRequest:request shouldRetry:shouldRetry];
298298
}
299299
}
300300

@@ -312,10 +312,7 @@ - (SFOAuthSessionRefresher *)sessionRefresherForUser:(SFUserAccount *)user {
312312
return self.oauthSessionRefresher;
313313
}
314314

315-
- (void)enqueueRequest:(SFRestRequest *)request
316-
failureBlock:(SFRestRequestFailBlock)failureBlock
317-
successBlock:(SFRestResponseBlock)successBlock
318-
shouldRetry:(BOOL)shouldRetry {
315+
- (void)enqueueRequest:(SFRestRequest *)request shouldRetry:(BOOL)shouldRetry {
319316
__weak __typeof(self) weakSelf = self;
320317
NSURLRequest *finalRequest = [request prepareRequestForSend:self.user];
321318
if (finalRequest) {
@@ -335,16 +332,16 @@ - (void)enqueueRequest:(SFRestRequest *)request
335332
if (error) {
336333
[SFSDKCoreLogger d:[strongSelf class] format:@"REST request failed with error: Error Code: %ld, Description: %@, URL: %@", (long) error.code, error.localizedDescription, finalRequest.URL];
337334
id dataForDelegate = [strongSelf prepareDataForDelegate:data request:request response:response];
338-
if (failureBlock) {
339-
failureBlock(dataForDelegate, error, response);
335+
if (request.failureBlock) {
336+
request.failureBlock(dataForDelegate, error, response);
340337
}
341338
return;
342339
}
343340

344341
// Timeout.
345342
if (!response) {
346-
if (failureBlock) {
347-
failureBlock(nil, nil, nil);
343+
if (request.failureBlock) {
344+
request.failureBlock(nil, nil, nil);
348345
}
349346
return;
350347
}
@@ -353,18 +350,18 @@ - (void)enqueueRequest:(SFRestRequest *)request
353350
// 2xx indicates success.
354351
if ([SFRestAPI isStatusCodeSuccess:statusCode]) {
355352
id dataForDelegate = [strongSelf prepareDataForDelegate:data request:request response:response];
356-
if (successBlock) {
357-
successBlock(dataForDelegate, response);
353+
if (request.successBlock) {
354+
request.successBlock(dataForDelegate, response);
358355
}
359356
} else {
360357
if (shouldRetry && [self shouldRetryTask:dataTask withData:data]) {
361-
[strongSelf replayRequest:request response:response failureBlock:failureBlock successBlock:successBlock];
358+
[strongSelf replayRequest:request response:response];
362359
} else {
363360
// Other status codes indicate failure.
364361
NSError *errorForDelegate = [strongSelf prepareErrorForDelegate:data response:response];
365362
id dataForDelegate = [strongSelf prepareDataForDelegate:data request:request response:response];
366-
if (failureBlock) {
367-
failureBlock(dataForDelegate, errorForDelegate, response);
363+
if (request.failureBlock) {
364+
request.failureBlock(dataForDelegate, errorForDelegate, response);
368365
}
369366
}
370367
}
@@ -444,10 +441,7 @@ - (NSError*) prepareErrorForDelegate:(NSData *)data response:(NSURLResponse *)re
444441
return [[NSError alloc] initWithDomain:kSFRestErrorDomain code:statusCode userInfo:errorDict];
445442
}
446443

447-
- (void)replayRequest:(SFRestRequest *)request
448-
response:(NSURLResponse *)response
449-
failureBlock:(SFRestRequestFailBlock)failureBlock
450-
successBlock:(SFRestResponseBlock)successBlock {
444+
- (void)replayRequest:(SFRestRequest *)request response:(NSURLResponse *)response {
451445
[SFSDKCoreLogger i:[self class] format:@"%@: REST request failed due to expired credentials. Attempting to refresh credentials.", NSStringFromSelector(_cmd)];
452446

453447
/*
@@ -467,22 +461,13 @@ - (void)replayRequest:(SFRestRequest *)request
467461
@synchronized (strongSelf) {
468462
if (!strongSelf.pendingRequestsBeingProcessed) {
469463
strongSelf.pendingRequestsBeingProcessed = YES;
470-
[strongSelf resendActiveRequestsRequiringAuthenticationWithFailureBlock:failureBlock
471-
successBlock:successBlock];
464+
[strongSelf resendActiveRequestsRequiringAuthentication];
472465
}
473466
}
474467
} error:^(NSError *refreshError) {
475468
__strong typeof(weakSelf) strongSelf = weakSelf;
476469
[SFSDKCoreLogger e:[strongSelf class] format:@"Failed to refresh expired session. Error: %@", refreshError];
477-
478-
// Call the failure block for the triggering request first
479-
if (failureBlock) {
480-
failureBlock(nil, refreshError, response);
481-
}
482-
483470
strongSelf.pendingRequestsBeingProcessed = YES;
484-
// Remove the triggering request from active requests to avoid double callback
485-
[strongSelf.activeRequests removeObject:request];
486471
[strongSelf flushPendingRequestQueue:refreshError rawResponse:response];
487472
strongSelf.sessionRefreshInProgress = NO;
488473
strongSelf.oauthSessionRefresher = nil;
@@ -511,14 +496,13 @@ - (void)flushPendingRequestQueue:(NSError *)error rawResponse:(NSURLResponse *)r
511496
}
512497
}
513498

514-
- (void)resendActiveRequestsRequiringAuthenticationWithFailureBlock:(SFRestRequestFailBlock)failureBlock
515-
successBlock:(SFRestResponseBlock)successBlock {
499+
- (void)resendActiveRequestsRequiringAuthentication {
516500
@synchronized (self) {
517501
NSSet *pendingRequests = [self.activeRequests asSet];
518502
for (SFRestRequest *request in pendingRequests) {
519503
[self send:request
520-
failureBlock:failureBlock
521-
successBlock:successBlock
504+
failureBlock:request.failureBlock
505+
successBlock:request.successBlock
522506
shouldRetry:NO];
523507
}
524508
self.pendingRequestsBeingProcessed = NO;

0 commit comments

Comments
 (0)