Nutrient

Home

SDK

Software Development Kits

Low-Code

IT Document Solutions

Workflow

Workflow Automation Platform

DWS API

Document Web Services

T
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

Company

About

Team

Careers

Contact

Security

Partners

Legal

Resources

Blog

Events

Try for free

Contact Sales
Contact sales
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

products

Web

Web

Document Authoring

AI Assistant

Salesforce

Mobile

iOS

Android

visionOS

Flutter

React Native

MAUI

Server

Document Engine

Document Converter Services

.NET

Java

Node.js

AIDocument Processing

All products

solutions

USECASES

Viewing

Editing

OCR and Data Extraction

Signing

Forms

Scanning & Barcodes

Markup

Generation

Document Conversion

Redaction

Intelligent Doc. Processing

Collaboration

Authoring

Security

INdustries

Aviation

Construction

Education

Financial Services

Government

Healthcare

Legal

Life Sciences

All Solutions

Docs

Guides overview

Web

AIAssistant

Document Engine

iOS

Android

visionOS

Java

Node.js

.NET

Document Converter Services

Downloads

Demo

Support

Log in

Resources

Blog

Events

Pricing

Try for free

Free Trial

Company

About

Security

Partners

Legal

Contact Sales
Contact Sales
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

products

Products overview

Document Converter

Document Editor

Document Searchability

Document Automation Server

Integrations

SharePoint

Power Automate

Nintex

OneDrive

Teams

Window Servers

solutions

USECASES

Conversion

Editing

OCR Data Extraction

Tagging

Security Compliance

Workflow Automation

Solutions For

Overview

Legal

Public Sector

Finance

All Solutions

resources

Help center

Document Converter

Document Editor

Document Searchability

Document Automation Server

learn

Blog

Customer stories

Events

Support

Log in

Pricing

Try for free

Company

About

Security

Partners

Legal

Contact Sales
Contact Sales
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

Product

Product overview

Process Builder

Form Designer

Document Viewer

Office Templating

Customization

Reporting

solutions

Industries

Healthcare

Financial

Manufacturing

Pharma

Education

Construction

Nonprofit

Local Government

Food and Beverage

Departments

ITServices

Finance

Compliance

Human Resources

Sales

Marketing

Services

Overview

Capex-accelerator

Process Consulting

Workflow Prototype

All Solutions

resources

Help center

guides

Admin guides

End user guides

Workflow templates

Form templates

Training

learn

Blog

Customer stories

Events

Support

Pricing

Support

Company

About

Security

Partners

Legal

Try for Free
Contact Sales
Try for Free
Contact Sales
Thank you! Your submission has been received!
Oops! Something went wrong while submitting the form.

Services

Generation

Editing

Conversion

Watermarking

OCR

Table Extraction

Pricing

Docs

Log in

Try for Free
Try for Free

Free trial

Blog post

Think First, Code Later

Tomáš Šurín Tomáš Šurín

Table of contents

  • Support for Tabs in PSPDFKit for Android
  • First Approach
  • Now for the Proper Solution
  • Don’t Fear Starting Over
  • Introducing Proposals
  • Conclusion
Illustration: Think First, Code Later

In this article, I’ll present a case study of a process I went through when implementing the tabs feature in PSPDFKit for Android, how it led to a multi-document API, and how it eventually helped improve my code and API design skills.

Support for Tabs in PSPDFKit for Android

Tabs have been a highly requested feature for quite some time, both from our customers and from PDF Viewer users. When implementing tabs, the requirements were simple: Make it possible for users to open multiple documents in a single activity and provide a simple UI that will allow switching between these loaded documents.

Tabs strip

You might be wondering why we need tabs. Opening documents in multiple activities and switching between them is fast, and users can even pop these activities into a multi-window mode and work on multiple documents at the same time, side by side. Well, if PSPDFKit were a simple PDF viewing app, then you might be correct. However, we are not developing just an app but also a framework used in hundreds of different apps. These apps have different use cases that require fast document switching, and many of them have end users who expect tabs.

When we decided to implement tabs, I was the person who was responsible for working on this feature. It seemed to me that such a simple feature could be done in a few days without many issues. It turned out I was wrong. I wanted to share my experience as an example of what could go wrong if you don’t design your code and APIs in advance and rely only on your ballpark approximation of the apparent complexity of a task at hand.

First Approach

My first approach was highly influenced by the desired final UI we wanted to have for our tab component. I began with PdfTabBar encapsulating the tabs bar and its UI behavior. This was then controlled via DocumentTabsController, which is responsible for all business logic and interaction with documents loaded in PdfActivity.

Tabs could be controlled programmatically via the DocumentTabsController:

// Add new tab to the end of the tabs bar.
val documentTab = activity.documentTabsController.addTab(documentSource)

// The returned tab object can be made visible later on.
activity.documentTabsController.setVisibleTab(documentTab)

The document switching that happened when making tabs visible was handled by replacing the document inside PdfActivity and correctly restoring its UI state. This resulted in noticeably slow tab switches. In addition, the whole solution had a messy code structure, including unclear responsibilities of its main classes and an unnecessary complex callback flow (callback hell). This all resulted in multiple bugs in the initial implementation.

Now for the Proper Solution

After a week of continuing in this direction, I grew increasingly frustrated about my approach and took a day to step back, reevaluate, and come up with a cleaner code design.

The main problem I wanted to solve was document switching performance, as it didn’t match the quality standards of PSPDFKit or me. After looking at the problem as a whole, I tried to stop being influenced by the tabs UI and started treating tabs as only one possible use case for the document switching API. I ended up with a separate multi-document API for managing multiple documents in a single PdfActivity.

The previous solution worked on a higher level using the public API of PdfActivity for switching documents. Implementing this on the lower level where we have full control over PSPDFKit’s internals made for a much more optimized solution in contrast to leaving this to our customers who only have access to public APIs.

The solution I decided upon consists of two separate parts:

  1. DocumentCoordinator is responsible for managing multiple documents inside a single PdfActivity. The document coordinator does not care about the existence of the tab bar; it just emits events about changes to managed documents to any interested consumer. This makes it possible to build an entirely custom UI for document switching.

// Documents in the document coordinator and modeled via the `DocumentDescriptor`
// class, containing all information required to load the document,
// as well as to store and restore its state.
val descriptor = DocumentDescriptor.fromUri(documentUri)

// Add a new document.
activity.documentCoordinator.addDocument(descriptor)

// Display the newly added document.
activity.documentCoordinator.setVisibleDocument(descriptor)

// Remove the document that is no longer needed.
activity.documentCoordinator.removeDocument(otherDescriptor)
  1. PdfTabBar is responsible for the tabs UI. This is instantiated in the PdfActivity and bound to DocumentCoordinator when tabs are enabled in configuration.

Don’t Fear Starting Over

Let me sidetrack here a bit to explain one aspect of human psychology that is applicable to this situation. The sunk cost fallacy is a human behavior pattern “where investments (i.e., sunk costs) justify further expenditures.” This concept can be easily adapted to software development. Even if you have already spent a lot of time working on a solution, you should not be scared of throwing it out and starting from scratch. Time already spent is not totally lost either — at least you learned something and can use this knowledge to improve by iterating upon your solution.

My initial tabs implementation wasn’t very elegant, led to loads of bugs, and had a far-too-complex control flow for the desired business logic. Throwing it into a bin does not mean that I should feel bad; the time spent helped me learn from my mistakes and design a superior solution.

Introducing Proposals

As you can see, I eventually managed to get to a really nice and powerful API. This was only one of the instances where something similar happened to me or one of my colleagues: Underestimating the complexity of a problem and skipping the formal design process can happen to all of us. Moving forward, we wanted to improve our workflows and try to solve (or at least improve upon) this issue. So we dedicated some time to refactor our development practices.

We ended up adapting a proposal-based approach when working on any larger feature. Nowadays, long before we start working on a feature (it could even be months before, in some cases) we write a proposal. This proposal is usually fairly in-depth in its scope and includes intended use cases, proposed public API design, and explanation of the high-level implementation. While working on the proposal, we usually also identify multiple anticipated implementation roadblocks, which can be discussed and decided upon in advance before the development starts.

Even so, these rigorous preparations could still miss some issues we encounter during the actual development. But in our experience, these are only isolated cases of mostly minor details that won’t lead to major architectural changes.

You could argue against spending your team’s time on writing and reviewing proposals, but we find it’s totally worth it, because:

  • Our developers have less stress. In addition, we avoid getting into situations where multiple days of work are scrapped because the solution does not meet our high standards.

  • Multiple people reviewing the proposed solution leads to a better solution, as it naturally brings multiple perspectives of people with different areas of expertise and levels of experience to the table.

  • Reviewers are not forced into agreeing with the acceptable solution just because its implementation has already burned too much time — we can be much more critical in the proposal stage.

  • Proposals help us plan our future work since we can do much more realistic time estimates after deciding on the solution we want to take and identifying the most anticipated issues.

Conclusion

I hope I gave you a useful sneak peek into how we work on PSPDFKit for Android. Development is not a linear process, and you should not be scared of throwing your hard-earned solution away as soon as you identify that it is not the best solution. Moreover, you should consider spending more time in the design phase before diving into actual coding. You might even take inspiration from us and adopt a proposal workflow.

But with anything you do, keep in mind that you should always choose a workflow that fits your personality and the personality of your team — maybe you prefer a more rigid approach with formal architectural processes, or maybe you want even more freedom than what we do here at PSPDFKit. The choice is yours!

Keep coding, and best of luck designing code and APIs that won’t haunt you at night.

Author
Tomáš Šurín
Tomáš Šurín Server and Services Engineer

Tomáš has a deep interest in building (and breaking) stuff both in the digital and physical world. In his spare time, you’ll find him relaxing off the grid, cooking good food, playing board games, and discussing science and philosophy.

Explore related topics

Android Kotlin Tips Productivity Development
Free trial Ready to get started?
Free trial

Related articles

Explore more
SDKDEVELOPMENTTUTORIALSiOSAndroidWebHow ToPDF

Understanding fonts in PDFs: A comprehensive guide

SDKRELEASESNutrient AI AssistantNutrient iOS SDKNutrient Android SDKiOSAndroid

AI Assistant for iOS and Android: Intelligent document interaction, now in your users’ pockets

SDKDEVELOPMENTAndroidJetpack ComposeDevelopment

Drag-to-reorder with Jetpack Compose

Company
About
Security
Team
Careers
We're hiring
Partners
Legal
Products
SDK
Low-Code
Workflow
DWS API
resources
Blog
Events
Customer Stories
Tutorials
News
connect
Contact
LinkedIn
YouTube
Discord
X
Facebook
Popular
Java PDF Library
Tag Text
PDF SDK Viewer
Tag Text
React Native PDF SDK
Tag Text
PDF SDK
Tag Text
iOS PDF Viewer
Tag Text
PDF Viewer SDK/Library
Tag Text
PDF Generation
Tag Text
SDK
Web
Tag Text
Mobile/VR
Tag Text
Server
Tag Text
Use Cases
Tag Text
Industries
Tag Text
Resources
Blog
Tag Text
Events
Customer Stories
Tag Text
Tutorials
Tag Text
Features List
Tag Text
Compare
Tag Text
community
Free Trial
Tag Text
Documentation
Tag Text
Nutrient Portal
Tag Text
Contact Support
Tag Text
Company
About
Tag Text
Security
Tag Text
Careers
Tag Text
Legal
Tag Text
Pricing
Tag Text
Partners
Tag Text
connect
Contact
Tag Text
LinkedIn
Tag Text
YouTube
Tag Text
Discord
Tag Text
X
Tag Text
Facebook
Tag Text
low-code
Document Converter
Tag Text
Document Editor
Tag Text
Document Automation Server
Tag Text
Document Searchability
Tag Text
Use Cases
Tag Text
Industries
Tag Text
Resources
Blog
Tag Text
Events
Customer Stories
Tag Text
Support
Help Center
Tag Text
Contact Support
Tag Text
Log In
Tag Text
Company
About
Tag Text
Careers
Tag Text
Security
Tag Text
Legal
Tag Text
Pricing
Tag Text
Partners
Tag Text
connect
Contact
Tag Text
LinkedIn
Tag Text
YouTube
Tag Text
Discord
Tag Text
X
Tag Text
Facebook
Tag Text
Popular
Approvals matrix
Tag Text
BPMS
Tag Text
Budgeting process
Tag Text
CapEx approval
Tag Text
CapEx automation
Tag Text
Document approval
Tag Text
Task automation
Tag Text
workflow
Overview
Tag Text
Services
Tag Text
Industries
Tag Text
Departments
Tag Text
Resources
Blog
Tag Text
Events
Customer Stories
Tag Text
Support
Help Center
Tag Text
FAQ
Tag Text
Troubleshooting
Tag Text
Contact Support
Tag Text
Company
About
Tag Text
Careers
Tag Text
Security
Tag Text
Legal
Tag Text
Pricing
Tag Text
Partners
Tag Text
connect
Contact
Tag Text
LinkedIn
Tag Text
YouTube
Tag Text
Discord
Tag Text
X
Tag Text
Facebook
Tag Text
DWS api
PDF Generator
Tag Text
Editor
Tag Text
Converter API
Tag Text
Watermark
Tag Text
OCR
Tag Text
Table Extraction
Tag Text
Resources
Log in
Tag Text
Help Center
Tag Text
Support
Tag Text
Blog
Tag Text
Company
About
Tag Text
Careers
Tag Text
Security
Tag Text
Pricing
Tag Text
Legal
Privacy
Tag Text
Terms
Tag Text
connect
Contact
Tag Text
X
Tag Text
YouTube
Tag Text
Discord
Tag Text
LinkedIn
Tag Text
Facebook
Tag Text

Copyright 2025 Nutrient. All rights reserved.

Thank you for subscribing to our newsletter!

We’re thrilled to have you join our community. You’re now one step closer to receiving the latest updates, exclusive content, and special offers directly in your inbox.

This builtin is not currently supported: DOM

PSPDFKit is now Nutrient. We've consolidated our group of trusted companies into one unified brand: Nutrient. Learn more

This builtin is not currently supported: DOM

PSPDFKit is now Nutrient. We've consolidated our group of trusted companies into one unified brand: Nutrient. Learn more

This builtin is not currently supported: DOM

New Feature Release. Tap into revolutionary AI technology to instantly complete tasks, analyze text, and redact key information across your documents. Learn More or View Showcase

This builtin is not currently supported: DOM

Aquaforest and Muhimbi are now Nutrient. We've consolidated our group of trusted companies into one unified brand: Nutrient. Learn more

This builtin is not currently supported: DOM

Integrify is now Nutrient. We've consolidated our group of trusted companies into one unified brand: Nutrient. Learn more

This builtin is not currently supported: DOM

Join us on April 15th. Join industry leaders, product experts, and fellow professionals at our exclusive user conference. Register for conference