Launch Spend Manager
Basic Integration
An integration with default options only requires a host UIViewController
to be provided. In this case Sensibill UI will appear with animation, and will be presented modally, over the provided host. It will start on Dashboard view.
You should also set a SensibillUICoordinatorDelegate
and implement its required protocol function coordinatorWillTerminateWebUI
Step 1: Define and retain an instance of SensibillUICoordinator
private var sensibillUICoordinator: SensibillUICoordinator?
Step 2: Instantiate SensibillUICoordinator
by specifying host of type UIViewController
. Specify delegate and invoke start function.
Following code snippet will attempt to start spend manager in modal presentation mode on provided host and lands on default view i.e. dashboard.
sensibillUICoordinator = SensibillUICoordinator(host: self)
sensibillUICoordinator?.delegate = self
sensibillUICoordinator?.start()
Step 3: Implement required functions of SensibillUICoordinatorDelegate
protocol and cleanup local instance of SensibillUICoordinator
when notified via coordinatorWillTerminateWebUI(_:)
function.
extension MyViewController: SensibillUICoordinatorDelegate {
// Called when the SDK requests termination
func coordinatorWillTerminateWebUI(_ coordinator: SensibillUICoordinator) {
// Clean up the sensibillUICoordinator
sensibillUICoordinator = nil
}
}
Startup Customization Options
Presentation Methods
The Sensibill UI can be presented in 3 different ways:
- Modal (default)
- Embed
- Push
The presentation method can be specified in SensibillUICoordinator
initializer by specifying one of the value of PresentationMethod
for presentationMethod
parameter.
Modal
This option presents a new UINavigationController
with Sensibill’s UIViewController
set as its root view. The navigation controller uses the integrator specified host view controller as its PresentingViewController
.
To present the UI modally, no additional options are required, as it is the default value. If you wanted to explicitly include it for clarity you can do the following:
private var sensibillUICoordinator = SensibillUICoordinator(host: self, presentationMethod: .modal)
sensibillUICoordinator.start()
Embed
The embed presentation option will embed Sensibill’s NavigationController into the provided host UIViewController
. Sensibill’s NavigationController will be added as the top-most subview in the provided host controller, and will constrain itself to fill the full size of the host controller’s view.
Note: This presentation method requires the integrator to manually dismiss Sensibill’s UI as outlined in ‘Handling a termination request’ below.
let sensibillUICoordinator = SensibillUICoordinator(host: self, presentationMethod: .embed)
sensibillUICoordinator.start()
Push
The push presentation option will push Sensibill’s UIViewController’s UIView
onto the provided host navigation controller’s navigation stack. This method requires the host view controller be a UINavigationController
, or else it will default to a modal presentation.
Note: This presentation method requires the integrator to manually dismiss Sensibill’s UI as outlined in Handling a termination request below.
let sensibillUICoordinator = SensibillUICoordinator(host: self, presentationMethod: .push)
sensibillUICoordinator.start()
Animation
By default the animation on appearance is enabled. You can disable the animation by setting it to false
in the start function:
let options = SensibillUICoordinator.StartOptions.Builder()
.add(animated: false)
.build()
sensibillUICoordinator.start(options: options)
Initial Page
By default, the Sensibill UI will start on Dashboard page. If you would like to start Sensibill UI on a different page, specify the navigationIntent
argument of the start
:
let options = SensibillUICoordinator.StartOptions.Builder()
.add(navigationIntent: .dashboard)
.build()
sensibillUICoordinator.start(options: options)
See the Starting On a Specific Page page for more information.
Launching Spend Manager to Edit Receipt Metadata
You can launch Spend Manager upon completion of the Capture to allow a user to specify folders and notes for the captured receipts. For more information, see Launching Spend Manager to Edit Receipt Metadata .
Handling A Termination Request
If a SensibillUICoordinator
is initialized with either the push
or embed
presentation mode , it will not know how to safely dismiss itself. This requires the integrator to handle the dismissal of Sensibill’s UI manually inside of the coordinatorWillTerminateWebUI
delegate function.
Example:
func coordinatorWillTerminateWebUI(_ coordinator: SensibillUICoordinator) {
// sensibillUIPresentationMethod can be local variable storing current presentation mode used to launch sensibillUICoordinator
switch sensibillUIPresentationMethod {
// Remove the SensibillUI here
// EXAMPLES:
switch webUIPresentationMethod {
case .embed:
referenceToSensibillUIViewController.view.removeFromSuperview()
referenceToSensibillUIViewController.removeFromParent()
referenceToSensibillUIViewController.didMove(toParent: nil)
case .push:
myUINavigationController.popToRootViewController(animated: true)
}
// Required clean up
sensibillUICoordinator = nil
}
Optional Delegate Functions
The SensibillUICoordinatorDelegate
provides one or more optional functions you can implement to be notified about, and act upon, key events that occur within the SDK.
Will Present Sensibill UI
These functions are called before and after Sensibill’s UI is presented. They provide the integrator the opportunity to access a reference to the presented UINavigationController
to apply any desired customization.
func coordinator(_ coordinator: SensibillUICoordinator, willPresent webUINavigationController: UINavigationController) {
// Declare Bar Button Item
let leftBarButtonItem = UIBarButtonItem(...)
// You can add BarButtonItems
webUINavigationController.navigationBar.topItem?.leftBarButtonItem = leftBarButtonItem
// Or hide the NavigationBar altogether
webUINavigationController.setNavigationBarHidden(true, animated: false)
}
Did Present Sensibill UI
func coordinator(_ coordinator: SensibillUICoordinator, didPresent webUINavigationController: UINavigationController) {
// start method has completed presentation of Sensibill UI on host.
}
Should show error view when fatal error occurs
Some errors that the SDK will encounter are deemed to be “unrecoverable”, such as having no network connection. When one of these errors occur, the SDK will automatically swap out its current view with an internal view that contains user-friendly messaging, a retry button, and a close button. The retry button will trigger an internal retry, and the close button will trigger the coordinatorWillTerminateWebUI
function.
If you do not wish for this ErrorView to be automatically displayed and instead want to handle it yourself, you can implement the following:
func coordinatorShouldShowFatalErrorView(_ coordinator: SensibillUICoordinator) -> Bool {
// Handle the error manually here
return false
}
Will Present Capture
This function is called when a user attempts to launch the capture flow. If the SDK’s attachLocationData
option has been enabled then this is an ideal time to request location permissions if you have not already done so previously in your app.
func coordinatorWillPresentCapture(_ coordinator: SensibillUICoordinator) {
// Example: Request Location Permission.
}
Will Restart Sensibill UI
This function is called when the SDK encounters an internal error it believes it can recover from. The SDK attempts an internal restart of the module, and will throw a non-recoverable error, attempting to terminate if it fails to restart 5 times.
func coordinatorWillRestartWebUI(_ coordinator: SensibillUICoordinator) {
// Debug log
}
Encountered Internal Error
This function is called when an internal error occurs while trying to start the SensibillUICoordinator
. It can be used for debugging purposes.
func coordinator(_ coordinator: SensibillUICoordinator, encountered error: Error) {
// Log error information for internal debugging.
}
Info
Please make sure you have correctly started the SensibillSDK
before launching the new Spend Manager. Follow Initializing the SDK
documentation as reference.
Warning
If you are seeing errors similar to “Unresolved reference: WebUiActivity”, please reference Installation Options
and make sure you are using version >= 2021.X.X
.
There are three ways to launch the Spend Manager UI. They range from the simplest integrations with the least customization available, to more complex integrations with much more customization available. See below for instructions on each.
Basic Integration
The most basic way to integrate the new Spend Manager is to directly start the WebUiActivity
. This will use the default configurations, navigating directly to the dashboard.
Example
Kotlin
startActivity(Intent(this, WebUiActivity::class.java))
Java
startActivity(new Intent(this, WebUiActivity.class));
Reference WebUiActivity
Subclass WebUiActivity Integration
Alternately, you can subclass the WebUiActivity
into your own activity which will allow you to override several listeners. This gives you more customization over how the activity and fragment should react to specific events.
When you subclass the WebUiActivity, you do not need to override anything for it to work. It will use all the defaults provided by the WebUiActivity
and the WebUiFragment
. If you are using your own layout, then its important to override the callSetContentView
and webContainerId
so that the WebUiActivity
knows where to place the WebUiFragment
.
Example
Kotlin
class SubclassKotlinActivity : WebUiActivity(), WebUiFragment.Listener {
private lateinit var binding: ActivitySubclassKotlinBinding
override fun callSetContentView() {
binding = ActivitySubclassKotlinBinding.inflate(layoutInflater)
setContentView(binding.root)
}
override val webContainerId: Int
get() = R.id.web_container
}
Java
public class SubclassJavaActivity extends WebUiActivity implements WebUiFragment.Listener {
private ActivitySubclassJavaBinding binding;
@Override
protected void callSetContentView() {
binding = ActivitySubclassJavaBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
}
@Override
protected int getWebContainerId() {
return R.id.web_container;
}
}
Reference
WebUiActivity
callSetContentView()
getWebContainerId
Direct Fragment Integration
Integration can also be done using just the WebUiFragment
. Create an instance of the fragment and add it to the fragment manager within your activity.
When using WebUiFragment
directly, please ensure to properly implement the Listener
methods. Also, please use, or see WebUiNetworkErrorFragment
as reference for what to display when Listener.onDisplayNetworkError
is called.
Example
Kotlin
class FragmentKotlinActivity : AppCompatActivity(), WebUiFragment.Listener {
private lateinit var binding: ActivityFragmentKotlinBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityFragmentKotlinBinding.inflate(layoutInflater)
setContentView(binding.root)
if(savedInstanceState == null) {
supportFragmentManager.commit {
replace(binding.fragmentContainer.id, WebUiFragment(), "FRAGMENT_TAG")
}
}
}
override fun onDisplayNetworkError(networkNotAvailable: Boolean) {
supportFragmentManager.commit {
replace(binding.fragmentContainer.id, WebUiNetworkErrorFragment(), "NETWORK_ERROR_FRAGMENT_TAG")
}
}
override fun onRequestFinish(reason: UiFinishReason) {
finish()
}
}
Java
public class FragmentJavaActivity extends AppCompatActivity implements WebUiFragment.Listener {
private ActivityFragmentJavaBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityFragmentJavaBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
if(savedInstanceState == null) {
getSupportFragmentManager()
.beginTransaction()
.replace(
binding.fragmentContainer.getId(),
new WebUiFragment(),
"FRAGMENT_TAG")
.commit();
}
}
@Override
public void onDisplayNetworkError(boolean networkNotAvailable) {
getSupportFragmentManager()
.beginTransaction()
.replace(
binding.fragmentContainer.getId(),
new WebUiNetworkErrorFragment(),
"NETWORK_ERROR_FRAGMENT_TAG")
.commit();
}
@Override
public void onRequestFinish(@NotNull UiFinishReason uiFinishReason) {
finish();
}
}
Reference
WebUiActivity
onDisplayNetworkError(networkNotAvailable: Boolean)
onRequestFinish(reason: UiFinishReason)
Demo
For a working example of each implementation, please refer to our demo application sample-spend-manager-android .