Overview
The SalesforceClient actor provides a Swift interface to Salesforce’s APIs. It handles authentication and provides type-safe access to various API endpoints.
This is a lower-level client that requires manual authentication management. For a higher-level interface, use CongregationKit .
Initialization
init(httpClient:)
Creates a new Salesforce API client.
The AsyncHTTPClient instance to use for making requests
import AsyncHTTPClient
import SalesforceClient
let httpClient = HTTPClient ( eventLoopGroupProvider : . shared )
let salesforce = SalesforceClient ( httpClient : httpClient)
Properties
auth
Routes for Salesforce authentication.
Authentication routes for OAuth 2.0 password flow
let credentials = SalesforceCredentials (
clientId : "your_client_id" ,
clientSecret : "your_client_secret" ,
username : "your_username" ,
password : "your_password"
)
let authResponse = try await salesforce. auth . authenticate ( credentials : credentials)
print ( "Access token: \( authResponse. accessToken ) " )
print ( "Instance URL: \( authResponse. instanceUrl ) " )
members
Routes for Salesforce member management.
Member routes for fetching and managing member data
let authResponse = try await salesforce. auth . authenticate ( credentials : credentials)
let response = try await salesforce. members . fetchAll (
accessToken : authResponse. accessToken ,
instanceUrl : authResponse. instanceUrl ,
pageNumber : 1 ,
pageSize : 50
)
for member in response.members {
print (member. memberName ?? "Unknown" )
}
seekers
Routes for Salesforce seeker management.
Seeker routes for fetching and managing seeker data
let seekerResponse = try await salesforce. seekers . fetchAll (
accessToken : authResponse. accessToken ,
instanceUrl : authResponse. instanceUrl ,
pageNumber : 1 ,
pageSize : 50 ,
seekerId : nil ,
name : nil ,
campus : . eastCampus ,
leadStatus : . followUp ,
email : nil ,
leadId : nil ,
contactNumber : nil
)
files
Routes for Salesforce file downloads.
File routes for downloading files from Salesforce
let fileResponse = try await salesforce. files . download (
recordId : "a0x2w000002jxqn" ,
accessToken : authResponse. accessToken ,
instanceUrl : authResponse. instanceUrl
)
Authentication Methods
authenticate(credentials:)
Authenticates with Salesforce using OAuth 2.0 password flow.
credentials
SalesforceCredentials
required
The Salesforce credentials Show SalesforceCredentials properties
OAuth client ID from your Salesforce connected app
OAuth client secret from your Salesforce connected app
Salesforce username for authentication
Salesforce password for authentication
Authentication response containing access token and instance URL Show SalesforceAuthResponse properties
Access token for API authentication
Salesforce instance URL for API calls
Identity URL for user identification
Token type (typically “Bearer”)
When the signature was created (Unix epoch seconds)
Base64-encoded HMAC-SHA256 signature
Throws if authentication fails
let credentials = SalesforceCredentials (
clientId : "your_client_id" ,
clientSecret : "your_client_secret" ,
username : "your_username" ,
password : "your_password"
)
let authResponse = try await salesforce. auth . authenticate ( credentials : credentials)
Member Methods
fetchAll(accessToken:instanceUrl:pageNumber:pageSize:)
Fetches all members from Salesforce with pagination support.
The Salesforce instance URL
The page number to fetch (optional, default 1)
The page size to fetch (optional, default 50)
MemberResponse containing members and pagination info
let response = try await salesforce. members . fetchAll (
accessToken : authResponse. accessToken ,
instanceUrl : authResponse. instanceUrl ,
pageNumber : 1 ,
pageSize : 100
)
print ( "Total members: \( response. metadata ? . total ?? 0 ) " )
for member in response.members {
print (member. memberName ?? "Unknown" )
}
fetchAll(accessToken:instanceUrl:pageNumber:pageSize:nextPageToken:)
Fetches all members with cursor-based pagination support.
The Salesforce instance URL
The page number to fetch (optional, default 1)
The page size to fetch (optional, default 50)
The next page token for cursor-based pagination (optional)
MemberResponse containing members and pagination info
// First page
var response = try await salesforce. members . fetchAll (
accessToken : authResponse. accessToken ,
instanceUrl : authResponse. instanceUrl ,
pageNumber : 1 ,
pageSize : 50 ,
nextPageToken : nil
)
// Next page using token
if let nextToken = response.metadata ? .nextPageToken {
response = try await salesforce. members . fetchAll (
accessToken : authResponse. accessToken ,
instanceUrl : authResponse. instanceUrl ,
pageNumber : nil ,
pageSize : 50 ,
nextPageToken : nextToken
)
}
fetch(memberId:accessToken:instanceUrl:)
Fetches a specific member by ID.
The member ID to fetch (must start with TKT)
The Salesforce instance URL
Throws if member not found or fetch fails
let memberId = try MemberID ( validating : "TKT123456" )
let member = try await salesforce. members . fetch (
memberId : memberId,
accessToken : authResponse. accessToken ,
instanceUrl : authResponse. instanceUrl
)
print ( "Member: \( member. memberName ?? "Unknown" ) " )
fetchAll(accessToken:instanceUrl:pageNumber:pageSize:expanded:)
Fetches all members with optional expanded fields.
The Salesforce instance URL
The page number to fetch (optional, default 1)
The page size to fetch (optional, default 50)
Array specifying which related information to expand
MemberResponse containing members with expanded fields
let response = try await salesforce. members . fetchAll (
accessToken : authResponse. accessToken ,
instanceUrl : authResponse. instanceUrl ,
pageNumber : 1 ,
pageSize : 50 ,
expanded : [. contactInformation , . employmentInformation ]
)
fetch(memberId:accessToken:instanceUrl:expanded:)
Fetches a specific member by ID with optional expanded information.
The Salesforce instance URL
Array specifying which related information to expand
The member if found with expanded fields
let memberId = try MemberID ( validating : "TKT123456" )
let member = try await salesforce. members . fetch (
memberId : memberId,
accessToken : authResponse. accessToken ,
instanceUrl : authResponse. instanceUrl ,
expanded : [. contactInformation , . discipleshipInformation ]
)
Seeker Methods
fetchAll(accessToken:instanceUrl:pageNumber:pageSize:seekerId:name:campus:leadStatus:email:leadId:contactNumber:)
Fetches a paginated list of seekers from Salesforce.
The Salesforce instance URL
The page number to fetch (optional, default 1)
The page size to fetch (optional, default 50)
Filter by seeker ID (optional)
Filter by name (optional)
Filter by campus (optional)
Filter by lead status (optional)
Filter by email (optional)
Filter by lead ID (optional)
Filter by contact number (optional)
SeekerResponse containing seekers and pagination info
let response = try await salesforce. seekers . fetchAll (
accessToken : authResponse. accessToken ,
instanceUrl : authResponse. instanceUrl ,
pageNumber : 1 ,
pageSize : 50 ,
seekerId : nil ,
name : "John" ,
campus : . eastCampus ,
leadStatus : . followUp ,
email : nil ,
leadId : nil ,
contactNumber : nil
)
fetch(identifier:accessToken:instanceUrl:)
Fetches a specific seeker by identifier (leadId or phone).
The identifier to fetch (leadId or phone, case-insensitive)
The Salesforce instance URL
Throws if seeker not found or fetch fails
let seeker = try await salesforce. seekers . fetch (
identifier : "LEAD001" ,
accessToken : authResponse. accessToken ,
instanceUrl : authResponse. instanceUrl
)
fetchAll(accessToken:instanceUrl:)
Fetches all seekers from Salesforce (non-paginated).
The Salesforce instance URL
let seekers = try await salesforce. seekers . fetchAll (
accessToken : authResponse. accessToken ,
instanceUrl : authResponse. instanceUrl
)
File Methods
download(recordId:accessToken:instanceUrl:)
Downloads a file from Salesforce using a record ID.
The record ID to download the file from (15-18 character alphanumeric Salesforce ID)
The Salesforce instance URL
File download response containing file data and metadata
let fileResponse = try await salesforce. files . download (
recordId : "a0x2w000002jxqn" ,
accessToken : authResponse. accessToken ,
instanceUrl : authResponse. instanceUrl
)
print ( "Downloaded: \( fileResponse. fullFilename ) " )
print ( "Size: \( fileResponse. fileSize ) bytes" )
// Save to disk
try fileResponse. data . write ( to : URL ( fileURLWithPath : "/tmp/ \( fileResponse. fullFilename ) " ))
Convenience Methods
authenticateAndFetchMembers(credentials:)
Authenticates with Salesforce and fetches all members in one operation.
credentials
SalesforceCredentials
required
The Salesforce credentials
throws
SalesforceAuthError | MemberError
Throws if operation fails
let members = try await salesforce. authenticateAndFetchMembers (
credentials : credentials
)
for member in members {
print (member. memberName ?? "Unknown" )
}
authenticateAndFetchMember(credentials:memberId:)
Authenticates with Salesforce and fetches a specific member.
credentials
SalesforceCredentials
required
The Salesforce credentials
throws
SalesforceAuthError | MemberError
Throws if operation fails
let member = try await salesforce. authenticateAndFetchMember (
credentials : credentials,
memberId : "TKT123456"
)
authenticateAndDownloadFile(credentials:recordId:)
Authenticates with Salesforce and downloads a file.
credentials
SalesforceCredentials
required
The Salesforce credentials
The record ID to download the file from
File download response containing file data and metadata
throws
SalesforceAuthError | FileDownloadError
Throws if operation fails
let fileResponse = try await salesforce. authenticateAndDownloadFile (
credentials : credentials,
recordId : "a0x2w000002jxqn"
)
Complete Example
import AsyncHTTPClient
import SalesforceClient
func main () async throws {
let httpClient = HTTPClient ( eventLoopGroupProvider : . shared )
defer { try ? httpClient. syncShutdown () }
let salesforce = SalesforceClient ( httpClient : httpClient)
// Authenticate
let credentials = SalesforceCredentials (
clientId : ProcessInfo. processInfo . environment [ "SALESFORCE_CLIENT_ID" ] ! ,
clientSecret : ProcessInfo. processInfo . environment [ "SALESFORCE_CLIENT_SECRET" ] ! ,
username : ProcessInfo. processInfo . environment [ "SALESFORCE_USERNAME" ] ! ,
password : ProcessInfo. processInfo . environment [ "SALESFORCE_PASSWORD" ] !
)
let authResponse = try await salesforce. auth . authenticate ( credentials : credentials)
print ( "Authenticated successfully" )
// Fetch members
let memberResponse = try await salesforce. members . fetchAll (
accessToken : authResponse. accessToken ,
instanceUrl : authResponse. instanceUrl ,
pageNumber : 1 ,
pageSize : 50
)
print ( "Total members: \( memberResponse. metadata ? . total ?? 0 ) " )
for member in memberResponse.members {
print ( " \( member. memberName ?? "Unknown" ) " )
}
// Fetch a specific member
let memberId = try MemberID ( validating : "TKT123456" )
let member = try await salesforce. members . fetch (
memberId : memberId,
accessToken : authResponse. accessToken ,
instanceUrl : authResponse. instanceUrl
)
print ( "Found member: \( member. memberName ?? "Unknown" ) " )
// Fetch seekers with filters
let seekerResponse = try await salesforce. seekers . fetchAll (
accessToken : authResponse. accessToken ,
instanceUrl : authResponse. instanceUrl ,
pageNumber : 1 ,
pageSize : 50 ,
seekerId : nil ,
name : nil ,
campus : . eastCampus ,
leadStatus : . followUp ,
email : nil ,
leadId : nil ,
contactNumber : nil
)
for seeker in seekerResponse.seekers {
print ( "Seeker: \( seeker. fullName ?? "Unknown" ) " )
}
// Download a file
let file = try await salesforce. files . download (
recordId : "a0x2w000002jxqn" ,
accessToken : authResponse. accessToken ,
instanceUrl : authResponse. instanceUrl
)
try file. data . write ( to : URL ( fileURLWithPath : "/tmp/ \( file. fullFilename ) " ))
print ( "Downloaded \( file. fullFilename ) " )
}
try await main ()
Error Handling
do {
let salesforce = SalesforceClient ( httpClient : httpClient)
let authResponse = try await salesforce. auth . authenticate ( credentials : credentials)
let member = try await salesforce. members . fetch (
memberId : try MemberID ( validating : "TKT123456" ),
accessToken : authResponse. accessToken ,
instanceUrl : authResponse. instanceUrl
)
} catch let error as SalesforceAuthError {
switch error {
case . invalidCredentials :
print ( "Invalid credentials" )
case . networkError ( let underlying) :
print ( "Network error: \( underlying ) " )
case . invalidResponse :
print ( "Invalid response from Salesforce" )
case . serverError ( let message) :
print ( "Server error: \( message ) " )
case . rateLimitExceeded :
print ( "Rate limit exceeded" )
}
} catch let error as MemberError {
switch error {
case . memberNotFound :
print ( "Member not found" )
case . invalidMemberID :
print ( "Invalid member ID format" )
case . fetchFailed ( let underlying) :
print ( "Fetch failed: \( underlying ) " )
case . invalidMemberData :
print ( "Invalid member data" )
}
} catch let error as FileDownloadError {
switch error {
case . fileNotFound :
print ( "File not found" )
case . invalidRecordId :
print ( "Invalid record ID" )
case . downloadFailed ( let underlying) :
print ( "Download failed: \( underlying ) " )
default :
print ( "File error: \( error. localizedDescription ) " )
}
} catch {
print ( "Unexpected error: \( error ) " )
}
Comparison with CongregationKit
SalesforceClient is a lower-level client that requires manual authentication management. Use it when you need:
Fine-grained control over authentication
Custom authentication flows
Direct access to Salesforce APIs
For most use cases, CongregationKit provides a simpler, higher-level interface:
// SalesforceClient (manual auth)
let salesforce = SalesforceClient ( httpClient : httpClient)
let authResponse = try await salesforce. auth . authenticate ( credentials : credentials)
let members = try await salesforce. members . fetchAll (
accessToken : authResponse. accessToken ,
instanceUrl : authResponse. instanceUrl ,
pageNumber : 1 ,
pageSize : 50
)
// CongregationKit (automatic auth)
let congregation = try await CongregationKit (
httpClient : httpClient,
credentials : credentials
)
let members = try await congregation. members . fetchAll (
pageNumber : 1 ,
pageSize : 50
)