Links

Account Linking

Good to know: account linking is helpful if you wish to use AIS (Account Information Service) or skip authentication steps when initiating payments via the in-app payments module.
Firstly, you will need to integrate kevin. API and fetch account linking state. Instructions on how to do it can be found here:

Basic flow

Customise linking flow by tweaking our configuration:
// initialize configuration with state fetched from the API
let configuration = KevinAccountLinkingSessionConfiguration.Builder(state: "state")
.setPreselectedCountry(.lithuania)
.setCountryFilter([.lithuania, .latvia, .estonia])
.setPreselectedBank("SOME_BANK_ID")
.setSkipBankSelection(false)
.build()
Implement KevinAccountLinkingSessionDelegate protocol. Make sure you show returned UIViewController somewhere:
protocol KevinAccountLinkingSessionDelegate: AnyObject {
func onKevinAccountLinkingStarted(controller: UINavigationController)
func onKevinAccountLinkingCanceled(error: Error?)
func onKevinAccountLinkingSucceeded(authorizationCode: String, bank: ApiBank)
}
Call KevinAccountLinkingSession and listen to delegate:
KevinAccountLinkingSession.shared.delegate = self
try KevinAccountLinkingSession.shared.initiateAccountLinking(
configuration: configuration
)

SwiftUI

Library is built on UIKit, however it can be easily used within SwiftUI.
Firstly, create representable for kevin. UIViewController:
import SwiftUI
struct KevinViewControllerRepresentable: UIViewControllerRepresentable {
let controller: UIViewController
func makeUIViewController(context: Context) -> some UIViewController {
return controller
}
func updateUIViewController(
_ uiViewController: UIViewControllerType,
context: Context
) { }
}
Define your view from which you will be showing representable:
import Kevin
import SwiftUI
struct SampleView: View {
@ObservedObject var viewModel: SampleViewModel
var body: some View {
VStack(spacing: 20) {
Spacer()
Button {
viewModel.invokeAccountLinkingSession()
} label: {
Text("Link Account")
}.buttonStyle(MainButtonStyle())
.sheet(isPresented: $viewModel.viewState.openKevin, content: {
if let controller = viewModel.kevinController {
KevinViewControllerRepresentable(controller: controller)
.presentation(canDismissSheet: false)
}
})
Spacer()
}
.padding(20)
}
}
Define your ViewModel which will subscribe to KevinAccountLinkingSessionDelegate and invoke the SDK:
import Kevin
import UIKit
class SampleViewModel: ObservableObject, KevinAccountLinkingSessionDelegate {
@Published var viewState = SampleViewState()
var kevinController: UIViewController? = nil
private let apiClient = ApiClient()
func invokeAccountLinkingSession() {
apiClient.getAuthState().done { state in
do {
KevinAccountLinkingSession.shared.delegate = self
try KevinAccountLinkingSession.shared.initiateAccountLinking(
configuration: KevinAccountLinkingSessionConfiguration.Builder(
state: state.state
)
.setPreselectedCountry(.lithuania)
.build()
)
} catch {
// do something
}
}.catch { error in
// do something
}
}
//MARK: KevinAccountLinkingSessionDelegate
func onKevinAccountLinkingStarted(controller: UINavigationController) {
self.kevinController = controller
self.viewState = MainViewState(openKevin: true)
}
func onKevinAccountLinkingCanceled(error: Error?) {
// notify user if error has been returned
}
func onKevinAccountLinkingSucceeded(authorizationCode: String, bank: ApiBank) {
// notify user
}
}

Customise session configuration

KevinAccountLinkingSessionConfiguration has multiple settings to tackle:
  • setPreselectedCountry - preselects country in our prebuilt UI
  • setDisableCountrySelection - uses only preselected country and does not allow to change it
  • setCountryFilter - allows only certain countries to be supported
  • setPreselectedBank - preselects bank in our prebuilt UI
  • setSkipBankSelection - skips bank and country selection and redirects user to the bank SCA immediately
Example:
KevinAccountLinkingSession.shared.initiateAccountLinking(
configuration: KevinAccountLinkingSessionConfiguration.Builder(
state: state
)
.setPreselectedCountry(.lithuania)
.setCountryFilter([.lithuania, .latvia, .estonia])
.setPreselectedBank("SOME_BANK_ID")
.build()
)