Skip to main content
Version: 1.3.0

Integration into Your App

Since you know best how to approach your customers, the product presentation and selection is done by your app and not the ip.labs Mobile SDK. But we provide you with some convenience APIs to map the ip.labs product portfolio to the product information of your app in order to create a seamless experience for your users.

Retrieving the Product Portfolio

You can retrieve all products in your portfolio with a simple call to retrieveProductPortfolio, which returns either a portfolio or a corresponding error:

when (val serverPortfolioResponse = IplabsMobileSdk.retrieveProductPortfolio()) {
is PortfolioRetrievalResult.Success -> {
with(serverPortfolioResponse.portfolio) {
// Display product data in your app…
}
}
is PortfolioRetrievalResult.ConnectionError,
is PortfolioRetrievalResult.HttpError -> null
is PortfolioRetrievalResult.DecodingError -> null
}

Since you can modify the portfolio content in our backend systems, this call will always fetch the latest version of the portfolio and therefore requires an internet connection. You can however persist returned data if you have a need for it.

The portfolio contains a list of type Product.

Products & Options

Every customizable photo product in the ip.labs infrastructure is identified by a numeric ID, which you should map to an equivalent of a product in your shop system. This product ID is also necessary to launch our editor with this product.

A product can also have a set of options, which are used to describe different variants of it. Wall décor products for example can have an option for the material (like canvas, aluminum, or Forex®). Typically these options are presented to the user in the product selection UI, e.g. in a dropdown component.

The data types provided by the ip.labs Mobile SDK also include some additional information, like name or description, which contain translated strings depending on the locale:

@Serializable
data class Product(
val id: Int,
val name: String,
val options: List<ProductOption>,
val bestPrice: Double,
val description: String?
)

Option Values

Each product option is identified through an id string and can have a certain set of option values:

@Serializable
data class ProductOption(
val id: String,
val name: String,
val values: List<ProductOptionValue>,
val defaultValue: String,
val description: String? = null
)

name and description contain translated strings depending on the locale. The defaultValue maps to the id of one of the values. When building a UI for product selection, you should use this to initialize the components for this option (e.g. a dropdown) with the corresponding value as a default.

The type ProductOptionValue has a structure similar to the option itself:

@Serializable
data class ProductOptionValue(
val id: String,
val name: String,
val description: String? = null
)

Initializing the Editor With an Empty Product

To let your users design a product and personalize it to their needs with photos and other decorative elements, you need to initialize an EditorView first. The essential parameter, that you have to provide to properly set it up during initialization, is a ProductConfiguration. With that, you control, which product in which variant will be loaded. On Android an additional set of EditorCallbacks is required, defining the interaction between editor and your app. Take a look at these examples:

val callbacks = object : EditorCallbacks {
override fun getFilePicker(
multipleFilesMode: Boolean
): ActivityResultLauncher<Array<String>> {
// Connect to your file picker here, updating the filePathsCallback
// property of the editor with file(s) selected…
}

override fun requestSessionId(
authenticateCallback: (String) -> Unit,
cancelAuthenticationCallback: () -> Unit
) {
// Retrieve session ID here and answer by triggering matching
// callback accordingly…
}

override fun authenticationFailed(errorMessage: String) {
// Handle failed authentication here…
}

override fun transferCartProject(cartProject: CartProject) {
// Add the project to your cart here…
}

override fun editCartProjectFailed(
reason: CartProjectEditingError
) {
// Handle failures during CartProject editing attempts here…
}

override fun handleLoadProjectTapped() {
// Navigate to an overview page with saved projects here…
}

override fun handleCleanTerminationRequest(
targetDestinationId: Int? = null
) {
// Handle requests to leave the editor without any unsaved work here…
}

override fun handleDirtyTerminationRequest(
targetDestinationId: Int? = null
) {
// Handle requests to leave the editor while it still contains unsaved
// work here…
}
}

// “editor” is a binding to an EditorView in your frontend
editor.initialize(
// Use proper values for id and options instead, based on your Portfolio and
// the user’s choices
productConfiguration = ProductConfiguration(
id = 50046830,
options = hashMapOf(
"color" to "red",
"handedness" to "right"
)
),
callbacks = callbacks
)

Editor Configuration

Optionally, you can also pass an EditorConfiguration to the initialization call, in case you want to adjust the behavior of some of the editor’s features. For details on that please refer to the corresponding documentation of available customizations.

Checking for Unsaved Changes in the Editor

Before leaving the editor e.g. because a navigation to a different screen in your app was triggered, we recommend to check whether the user has unsaved changes in the current project first. You can do so with the requestTermination method of EditorView. This will display a dialog asking the user to either save or discard pending changes in case there are any (or even cancel the process if they change their mind):

Dialog asking to save changes

To handle the result of such a termination request you have to implement the handleTerminationRequest method from the EditorCallbacks interface. This method will be called as a response to a termination request and indicate whether you can safely leave the current project via its state parameter, containing an EditorTerminationState. On top of that both the requestTermination as well as the handleTerminationRequest methods offer an optional targetDestinationId integer parameter in case you need to correlate the original termination request with its response. You can e.g. pass in an ID that refers to the fragment which should be shown instead of the current one with the editor on it.

// Binding to an EditorView in your front-end
val editor = binding.editor

// Define “handleTerminationRequest” among others
val callbacks = object : EditorCallbacks {
override fun handleTerminationRequest(
state: EditorTerminationState,
targetDestinationId: Int?
) {
when (state) {
EditorTerminationState.READY -> {
// Handle ready for termination scenario here, e.g. by
// navigating to a destination indicated by
// “targetDestinationId”…
}
EditorTerminationState.PENDING_CHANGES -> {
// Handle pending changes blocking sage editor termination
// scenario here, e.g. by aborting navigation to the destination
// indicated by “targetDestinationId”…
}
}
}

// other callbacks…
}

// Pass those callbacks to the editor during its initialization
editor.initialize(
productConfiguration = yourProductConfiguration,
callbacks = callbacks
)

// Request an editor termination whenever appropriate, resulting in the
// previously defined “handleTerminationRequest” callback getting called
editor.requestTermination(targetDestinationId = yourNavigationId)

Displaying Third Party Component Licenses

The Mobile SDK makes use of a few open source third party software components. To be fully legally compliant you have to make these licenses accessible in your application (e.g. in an “About…” screen). Please refer to the section for your platform below to find out how this can be done.

To make this task as convenient as possible for you, we offer an end-point that gives you a listing of all utilized components and their respective licenses as follows:

when (
val iplabsMobileSdkThirdPartyComponentLicenses = IplabsMobileSdk.listThirdPartyComponentLicenses()
) {
is ThirdPartyComponentLicenseListingResult.Success -> {
with(iplabsMobileSdkThirdPartyComponentLicenses.thirdPartyComponentLicenses) {
// Process component and license information…
}
}

is ThirdPartyComponentLicenseListingResult.UnknownError -> {
// Handle listing error…
}
}

There is a good chance that you already use some of those components yourself in your app, so we recommend to deduplicate this listing when merging it with your own one. This way each unique component and version with all meta data and license text is only denoted once in your legal information section.