iOS (Swift/Obj-C)

Interacting with Particle Auth on iOS using Swift or Objective-C

Particle Auth for iOS

The Particle Auth iOS SDK is the primary means of integrating Particle's Wallet-as-a-Service within applications on iOS. From initialization and configuration to onboarding and account utilization, this SDK acts as the master library for interacting with Particle on iOS.

The Particle Auth iOS SDK follows configuration and implementation mechanisms similar to the Particle Auth Android SDK. The specifics of integration, along with several examples, will be covered below.

Getting Started

Implementation of the Particle Auth iOS SDK only takes a few lines of code, although before getting to that point, you'll need to meet a few key prerequisites in mind to ensure compatibility.

Prerequisites

  • Xcode 14.1 or later.
  • CocoaPods 1.11.0 or higher.
  • Your project must target these platform versions or later:
    • iOS 14.

Configuration

To begin the initialization process, you must head over to the Particle dashboard and create a project alongside a corresponding application. These will control specific customizations, statistics, and more for your instance of Particle's Wallet-as-a-Service. After creating an application, you'll need to retrieve three key values: projectId, clientKey, and appId. We'll be using these down the road.

  1. Sign up/log in to the Particle dashboard.

  1. Create a new project or enter an existing project.

  1. Create a new iOS application, or skip this step if you already have one.

  1. Retrieve the project ID (projectId), the client key (clientKey), and the application ID (appId).

‎‎

‎‎

Installation

Now that you've created a project and an application within the Particle dashboard, you'll need to go ahead and install the Particle Auth SDK using CocoaPads (assuming you meet the previously mentioned minimum version requirements).

  1. If you don't already have a Podfile, create one from the root of your project directory with:

    pod init
    
  2. Within the newly created Podfile, add the ParticleAuthService pod.

    pod 'ParticleAuthService'
    
    // Alternatively, to use Auth Core
    //pod 'ParticleAuthCore'
    //pod 'ParticleMPCCore'
    //pod 'Thresh'
    
  3. Install the pods, then open your .xcworkspace file to see the project in Xcode.

    pod install --repo-update
    open your-project.xcworkspace
    

📘

For release updates, subscribe to the GitHub repository.

Podfile Configuration

Additionally, you'll need to ensure that your resulting Podfile is properly configured and contains a snippet similar to the example below.

post_install do |installer|
  installer.pods_project.targets.each do |target|
    target.build_configurations.each do |config|
      config.build_settings['BUILD_LIBRARY_FOR_DISTRIBUTION'] = 'YES'
    end
  end
end

Initialization

Now that you've configured your Particle dashboard, installed the SDK, and configured a Podfile, it's time to initialize the SDK with the aforementioned projectId, clientKey, and appId retrieved from the Particle dashboard.

  1. Create a ParticleNetwork-Info.plist file from the root of your corresponding Xcode project.

  2. Paste the following text into this file:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
    <plist version="1.0">
    <dict>
    	<key>PROJECT_UUID</key>
    	<string>YOUR_PROJECT_UUID</string>
    	<key>PROJECT_CLIENT_KEY</key>
    	<string>YOUR_PROJECT_CLIENT_KEY</string>
    	<key>PROJECT_APP_UUID</key>
    	<string>YOUR_PROJECT_APP_UUID</string>
    </dict>
    </plist>
    
  3. Replace the placeholders with the values retrieved from your project and application within the Particle dashboard.

  4. Import the ParticleNetworkBase and ParticleAuthService module in your UIApplicationDelegate file.

    import ParticleNetworkBase
    import ParticleAuthService
    
  5. Initialize the ParticleNetwork service, which is typically located within your app's application:didFinishLaunchingWithOptions method:

    // Set a primary network, derived from ChainInfo
    let chainInfo = ParticleNetwork.ChainInfo.ethereum(.mainnet)
    let devEnv = ParticleNetwork.DevEnvironment.debug // For release, this can be set to DevEnvironment.production
    let config = ParticleNetworkConfiguration(chainInfo: chainInfo, devEnv: devEnv)
    ParticleNetwork.initialize(config: config)
    
    // If applicable, set a custom EVM network
    let chainInfo = ParticleNetwork.ChainInfo.customEvmNetwork(fullName: "Ethereum", network: "rinkeby", chainId: 4, explorePath: "https://rinkeby.etherscan.io/", symbol: "ETH")
    
  6. Add the scheme URL handle in your app's application(\_:open:options:) method.

    return ParticleAuthService.handleUrl(url) 
    
  7. Then, to configure your application's scheme URL, follow the below process:

    1. Select your app from "TARGETS" within the "Info" section.
    2. Click the plus icon to add the URL types.
    3. Paste your scheme in "URL Schemes."

📘

Defining a scheme URL

Your scheme URL should be "pn" concatenated with your projectId.

For example, if your project app id is 63bfa427-cf5f-4742-9ff1-e8f5a1b9828f, your scheme URL is pn63bfa427-cf5f-4742-9ff1-e8f5a1b9828f.

Switch Chain

To switch the used chain after initial configuration, ParticleAuthService.switchChain is used. This enables asynchronous switching. Similar to the initial configuration, chain information derived from ParticleNetwork.ChainInfo can be passed as the primary parameter, determining the destination chain.

let chainInfo = ParticleNetwork.ChainInfo.ethereum(.mainnet)
ParticleAuthService.switchChain(chainInfo).subscribe { [weak self] result in
  guard let self = self else { return }
  switch result {
  case .failure(let error):
      print(error)
  case .success(let userInfo):
      print(userInfo)
  }
}.disposed(by: bag)

// For switching chains synchronously, without checking login status
let chainInfo = ParticleNetwork.ChainInfo.ethereum(.mainnet)
ParticleNetwork.setChainInfo(chainInfo)

Examples of utilization

Login

ParticleAuthService.login is the main mechanism for facilitating login (account creation or sign in) with Particle's Wallet-as-a-Service. Upon calling this method, an authentication UI will be thrown, and the user will be asked to sign in with a specified login method. Specifically, five key parameters are available within ParticleAuthService.login:

  • type, the specific social login to be used. This can be either .email, .phone, .google, .apple, .jwt, or .facebook.
  • account, when type is set to either .email, .phone, or .jwt, you can use the account parameter to pass in an expected email, phone number, or JWT. This is optional for the former two, but required for .jwt.
  • supportAuthType, the methods of authentication visible on the authentication popup UI. By default, this will be exclusive to the chosen social login method, although by passing in additional types, you can expand the UI to include the ability to login with those as an alternative to type.
  • socialLoginPrompt, either 'none', 'consent', or 'select_account'.
  • authorization, optionally, a hex string prompting an authentication signature upon sign-in.
ParticleAuthService.login(type: .email)
  .subscribe { [weak self] result in
    guard let self = self else { return }
    switch result {
    case .failure(let error):
        // Handle failure
    case .success(let userinfo):
        // Handle success - returns userinfo
    }
}.disposed(by: bag)

/// If using custom authentication with a JWT
let account = "json web token"
ParticleAuthService.login(type: .jwt, account: account)
  .subscribe { [weak self] result in
    guard let self = self else { return }
    switch result {
    case .failure(let error):
        // Handle failure
    case .success(let userinfo):
        // Handle success, returns userinfo
    }
}.disposed(by: bag)

After login is successful, detailed user information can be retrieved by calling ParticleAuthService.getUserInfo().

Is Login

ParticleAuthService.isLogin returns a Boolean based upon the current state of a session (whether or not a user is currently logged into an account). Additionally, an adjacent method, ParticleAuthService.isLoginAsync can be called and subscribed to, returning userInfo upon a status of true.

ParticleAuthService.isLogin()
ParticleAuthService.isLoginAsync()
  .subscribe { [weak self] result in
    guard let self = self else { return }
    switch result {
    case .success(let userInfo):
        print(userInfo)
    case .failure(let error):
        print(error)
    }
}.disposed(by: bag)

Logout

ParticleAuthService.logout will simply log a user out of their existing session, assuming they're logged in. A parallel method to this exists, ParticleAuthService.fastLogout, which hides loading UI elements for immediate (silent) logout. Both of these methods facilitate simple session exits for logged-in users.

// This method presents a Safari view controller, keep the presented view controller alive.
// The Safari view controller will close automatically
ParticleAuthService.logout()
  .subscribe { [weak self] result in
    guard let self = self else { return }
    switch result {
    case .failure(let error):
        // Handle failure
    case .success(let success):
        // Handle success
    }
}.disposed(by: bag)
ParticleAuthService.fastLogout()
  .subscribe { [weak self] result in
    guard let self = self else { return }
    switch result {
    case .failure(let error):
        // Handle failure
    case .success(let success):
        // Handle success
    }
}.disposed(by: bag)

UserInfo and Address Retrieval (post-login)

Once a user has logged in, you'll have access to various methods such as ParticleAuthService.getUserInfo and ParticleAuthService.getAddressto retrieve specific user details. The getUserInfo response will, by default, include both an associated EVM and Solana address, while getAddress will return an address depending on whether an EVM chain or Solana is chosen within the prior configuration.

ParticleAuthService.getUserInfo()
ParticleAuthService.getAddress()

Custom Modal Style

For the Particle Wallet modal popup, you can also specify presentation styling upon entry of the modal. This includes .fullScreen for a fullscreen overlay (covers the underlying application), or .formSheet for a more concise, embedded display. Additionally, if .fullScreen is set, then this can be further configured by using medium screen with ParticleAuthService.setMediumScreen.

// formSheet by default
ParticleAuthService.setModalPresentStyle(.fullScreen)

// Set Safari page to show in medium screen, this method works from iOS 15
// false by default
ParticleAuthService.setMediumScreen(true)

Set and Get Language

You can forcibly set a specific language to be used within the UI using ParticleNetwork.setLanguage, with retrieval of the current active language facilitated by ParticleNetwork.getLanguage. By default, this is set to English.

ParticleNetwork.setLanguage(Language.en)

ParticleNetwork.getLanguage()

Signatures

Once ParticleNetwork has been initialized and a user has successfully logged in, signatures can be proposed for confirmation through a popup; this can be facilitated via a few different methods:

  • signAndSendTransaction: Requests a signature for a given transaction and, upon receiving it, sends the transaction and returns the signature. For EVM chains, this expects a hex-encoded string containing transaction parameters. For Solana, this expects a base58 string.
  • signTransaction: Solana-specific. It requests a signature for a given transaction. Upon receiving a signature, it gets immediately returned (a transaction is not automatically sent). Expects a base58 string.
  • signMessage: Requests a signature for a basic message. On EVM chains, this can be passed as a hex-encoded string prefixed by '0x'. For Solana, this can be passed directly as a string.
  • signTypedData: EVM-specific. Requests a signature for structured (typed) data --eth_signTypedData. For more information on signTypedData, see the Web SDK reference.
ParticleAuthService.signAndSendTransaction(transaction)
  .subscribe { [weak self] result in
    guard let self = self else { return }
    switch result {
    case .failure(let error):
        // Handle failure
    case .success(let signature):
        // Handle success - returns a signature
    }
}.disposed(by: bag)
ParticleAuthService.signtransaction(transaction)
  .subscribe { [weak self] result in
    guard let self = self else { return }
    switch result {
    case .failure(let error):
        // Handle failure
    case .success(let signedMessage):
        // Handle success - returns a signature
    }
}.disposed(by: bag)

// Plural method of the former
let transactions: [String] = []
ParticleAuthService.signAllTransactions(transactions)
  .subscribe { [weak self] result in
    switch result {
    case .failure(let error):
        // Handle failure
    case .success(let signature):
        // Handle success - returns a singature
    }
}.disposed(by: self.bag)
ParticleAuthService.signMessage(message)
  .subscribe { [weak self] result in
    guard let self = self else { return }
    switch result {
    case .failure(let error):
        // Handle error
    case .success(let signedMessage):
        // Handle success - returns a signature
    }
}.disposed(by: bag)

// Or, alternatively
ParticleAuthService.signMessageUnique(message)
  .subscribe { [weak self] result in
    guard let self = self else { return }
    switch result {
    case .failure(let error):
        // handle error
    case .success(let signedMessage):
        // handle signed message
    }
}.disposed(by: bag)
ParticleAuthService.signTypedData(message, version: .v1) // Supports all versions up to and including v4
  .subscribe { [weak self] result in
    switch result {
    case .failure(let error):
        // Handle failure
    case .success(let signed):
        // Handle success - returns a signature
    }
}.disposed(by: bag)

You can create a transaction with TxData and FeeMarketEIP1559TxData.

Open Web Wallet

If you'd like to open an instance of the Wallet UI (Particle Wallet, a unified mechanism of interacting with accounts generated by ParticleAuthService.login), then you can do so by calling ParticleAuthService.openWebWallet and passing in various configurations.

For specific details regarding custom style configurations, see the Web reference page.

let styleJsonString = """
    {
      "supportAddToken": false,
      "supportChains": [{
          "id": 1,
          "name": "Ethereum"
        },
        {
          "id": 5,
          "name": "Ethereum"
        }
      ]
    }
"""
ParticleAuthService.openWebWallet(styleJsonString: styleJsonString)

Open Account and Security

If you'd like to force the "Account and Security" options to be opened on-screen (from which you can control master passwords, payment passwords, additional accounts, and so on), then you can call ParticleAuthService.openAccountAndSecurity. Upon calling this method, a popup allowing the user to control security settings will appear.

ParticleAuthService.openAccountAndSecurity()

Set Security Account Config

Within the security settings, specific popups can be set to display upon confirmation or wallet UI entrance. These popups include promptSettingWhenSign, which refers to payment (signature) passwords, and promptMasterPasswordSettingWhenLogin, which refers to a login-based master password.

By default, promptSettingWhenSign is set to 1, which will show the prompt upon the first signature a given account conducts. If it's set to 0, it will never be shown; if it's set to 2, it will be shown upon every signature confirmation.

This same logic applies to promptMasterPasswordSettingWhenLogin, which by default is set to 0.

ParticleNetwork.setSecurityAccountConfig(config:
  .init(promptSettingWhenSign: 1, promptMasterPasswordSettingWhenLogin: 2))

let securityAccountConfig = PartilceNetwork.getSecurityAccountConfig()

Particle Provider

Additionally, to send a request directly to Particle's Wallet-as-a-Service, not through an SDK method but rather via JSON-RPC. Then, you can push a request to ParticleProvider following standard JSON-RPC format. Only a handful of methods are supported in this context:

  • eth_sendTransaction - returns either a signature or an error.
  • eth_signTypedData - returns either a signature or an error.
  • personal_sign - returns either a signature or an error.
  • wallet_switchEthereumChain - returns either a signature or an error.
  • eth_chainId - returns either a chain ID string (such as 0x1, 0x5, and so on) or an error.
  • eth_signTypedData_v1 - returns either a signature or an error.
  • eth_signTypedData_v3 - returns either a signature or an error.
  • eth_signTypedData_v4 - returns either a signature or an error.
public static func request(method: String, params: [Encodable]) -> Single<Data?>

ParticleProvider.request(method: method, params: params)
  .subscribe { data in
     // Handle data
  } onFailure: { error in
      // Handle failure
  }.disposed(by: bag)