How we developed and integrated ‘share’ module into LINE app – 2

Introduction

In Part I, we defined the following six categories to share our experience, difficulties and solutions of the Share module development for LINE iOS and covered the first two categories.

  1. Integrating conventions on data models
  2. Integrating logic on sharing data
  3. Identifying a list of eligible services
  4. Providing customized/additional actions
  5. Generalizing exceptions
  6. Improving UI/UX 

In this Part II, we will cover the remaining four categories. We will continue our story with the thinking process we went through to implement a structure for easier maintenance and expansion, on top of meeting various requirements for many LINE services.

 

3. Identifying a list of eligible services

As shown below, a list of services eligible for sharing appears at the bottom of the share sheet.

Figure 5. List of eligible service

This list depends on the type of the source service and selected content. Let’s take a look at how it is designed to filter eligible services.

 

Issues

The list of eligible services on the share sheet is selected based on the following two conditions.

  • The source service determines which target services can be selected for sharing.
    • For example, Album and Notes can be used only in Chats. → Displayed only when the share sheet is opened in a chatroom.
  • The target service determines the type of content for sharing.
    • For example, Album can handle images only. → Block sharing when texts or videos are selected.

LINE supports various types of services and content. Figure 6 below lists up eligible services per source service and content type. The Area column on the far left shows a list of source services and Source type indicates the type of selected content for sharing. Eligible services based on the combination of Area and Source type are marked on the right.

Figure 6. List of eligible services per source service and content type

Figure 6 lists eligible services for a single content type. In reality, however, users can select multiple content with more than one content type, resulting in many more scenarios.

In the past, such a combined list of possible scenarios did not exist nor implemented. Therefore, it was complicated to identify and manage eligible services for each service or content type in the previous code.

 

Solution

We decided to create an intersection set of eligible services based on the combination of two factors, namely type of source services and content.

1. Type of source services

We designed it so that each service could enter a service wish list when the Share module was called. For example, when Album calls the Share module, it comprehensively designates “Keep, Timeline and other Apps” for sharing, using the includedShareActivityTypes parameter. Services designated with includedShareActivityTypes are filtered by content type as explained below. Consequently, it is not guaranteed that these designated services will be displayed on the share sheet, and they are more like service candidates. As the wish list is included in includedShareActivityTypes, it is now easier to find out relations between services in the code. It also makes adding or deleting a service in the wish list easier for each source service.

The share system in iOS also allows a source service to control the service list for sharing. UIActivityViewController class is the one responsible for sharing feature in iOS, and this class uses  excludedActivityTypes property that contains an array of strings to specify services to be excluded. As opposed to excludedActivityTypes in iOS, LINE uses includedShareActivityTypes to declare which services to include. Such a discrepancy is attributable to the inherent characteristics of iOS and the LINE Share module. 

As for the share system in iOS, each service independently determines which services to be included or excluded from the list. Moreover, services on the iOS share sheet can be numerous and change frequently, depending on the apps installed on the device. Because of these two factors, it cannot be identified from the code of a source service which services will appear on the list on the share sheet. That’s why it is more efficient to designate which services to exclude, rather than which services to include, when the iOS share system is called. If the service calling the iOS share sheet were to designate which services are to be displayed, it should specify numerous services in the iOS ecosystem and frequently add recently released apps in the code. Sharing content will become a chaos. 

As for LINE, on the other hand, the Share module already has a list of services to display in the list, and it is clearly included in the code of the source service. In addition, LINE has less number of services for selection, compared to iOS, and new services are added less frequently. Therefore, it is more efficient for LINE’s Share module to consider which services to be displayed. With this approach, the code can clearly indicate that content sharing is available between which services, without digging up specification documents. Another advantage is that a new service can be added without influencing existing services. 

2. Type of content

Eligible services for sharing such as Chat, Keep and Timeline are defined as Swift’s enum ShareActivityType.

As the list of services eligible for sharing was determined by ShareActivityType enum, we added a function that would return an array of services eligible for ShareActivity as shown above. As the list of target services per source service is kept at a single location, it is now simpler to check the specifications in the code and maintain the code, just like the case under “1. Type of source services”.

3. Custom services

Most of the cases, it is sufficient to check those two factors explained above. Sometimes, however, it requires a custom action that is not supported by the Share module. Consequently, we need to add a service to the list created from 1 and 2 above.

If you want to add a service to the list of eligible services based on the type of source service and content at all times, you can specify a custom action under customShareActivityTypes for the source service. Any service included in customShareActivityTypes will always appear on the service list regardless of the type of content.

Finalize the list of eligible services

You can finalize the list based on steps 1, 2 and 3 as described in Figure 7 below.

Figure 7. Workflow for creating the list of eligible services

When a user selects content for sharing and opens the share sheet, the intersection of the following two lists is created first.

  • List of services designated by the source service
  • Intersection of services allowed for respective types of content 

In the example above, the user selects multiple texts and images form a chatroom and touches the share button (a). From a regular chatroom, not Open Chat, content can be shared with Keep, Timeline, Album, Notes or other apps, except for Open Chat Note. Texts can be shared to all services, except for Album, while images can be shared with all services. As a result, the final list of eligible services includes the intersection of three lists, namely Keep, Timeline, Notes and other apps (b). If the source service designated a custom service, it is added to the list from (b). 

The order of service icons on the page is relative. For instance, Keep always appears on the left side of Timeline while Timeline always appears on the left of Copy link. So, ShareActivityType complies with the Comparable protocol, and it is implemented to simply line up services as defined in Enum.

In short, when you add the custom action to the result of (b) in Figure 7 and sort service icons based on the rule, you will get the final list just like the one in (c) of Figure 7.

 

4. Providing customized/additional actions

Let’s take a look at custom actions provided to meet various requirements from different services.

 

 Issues

We were able to eliminate redundant logic and code as we integrated the share feature developed and maintained by different services. However, we faced a new problem. There were cases where each source service had different requirements even for the same target service. Basic features provided by the integrated Share module was just not enough.

For example, “Copy link” sometimes needs to take different actions, depending on a service.

  • General cases: Simply copy the URL selected by the user for sharing
  • Keep: Extract the URL from the text selected by the user and copy it for sharing
  • Timeline posts: Get the link of the selected post from the server and copy it for sharing

We needed a means to run different code based on the type of source service even if the same icon was selected.

 

Solution

Currently various services use the Share module. Services can be added or closed anytime. That’s why we didn’t choose to differentiate actions per source service in the Share module. Instead, we allow each source service to customize actions in the share sheet.

Just like how the service wish list is designated by each source service, the services can be selected for customization with the customShareActivityTypes parameter. The share sheet always displays the services designated by customShareActivityTypes regardless of the selected content type. You can define the customized action, using a closure in Swift. When calling the Share module, a closure named completionHandler runs after the share action, and you can add code for the custom service in this closure. When the service defined in customShareActivityTypes is selected, the Share module do not run its own code but call completionHandler, which allows each source service to run their own code. As shown in the orange box above, completionHandler has the code for either copyLink or otherApps as written by the source service.

With this approach, services can use the same Share module for basic features but add a custom action regardless of content type, if necessary.

As of now, there are limited use of custom actions for sharing. Later when custom actions are more frequently used, this method can be improved by creating a data structure containing a closure for a specific custom action and service and sending it to the Share module, instead of completionHandler handling different custom share actions.

 

5. Generalizing exceptions

As the Share module is used by various services in various ways, we could not continue to use exception handling separately developed by each service. We generalized exceptions in phases in consideration of all possible exceptions and convenience for development and maintenance. This is how we did it.

 

Issues

When various content such as texts, images, contacts and locations are being shared between many services, sometimes an exception can occur. The following is a list of possible causes considered by the Share module.

  • Content selected for sharing cannot be used. 
    • Expired image or video files
    • Message canceled by the counterpart
    • Network errors
  • Eligible content types and the maximum number of content are different for each service.
  • Different services are linked to each service.
    • Chat -> Notes, Album, Timeline and other apps
    • Timeline -> Chat
    • Keep -> Chat, Timeline

If it is still not possible to share after checking these factors, it is necessary to alert users in a user friendly way. With abundant cases separately handled by each service in the past, it called for a more organized approach to manage exceptions.

 

Solution

Going back to exceptions described above, you can note that exception handling is required at different points across the overall sharing process.

  • Check if the selected content is valid for sharing → Can be checked before opening the Share module
  • Check if the target service selected by the user is eligible for sharing → Can be checked when entering the Share module
  • Check if the target service selected by the user can process the content → Can be checked when the user selects the target service

It is always better to let users know about an exception as soon as possible to minimize inconvenience caused by service interruption. If an alert is sent at a later stage with complicated details on the exception, it would be difficult for users to recognize what went wrong. Moreover, it would require more complicated code to compose an alert with details, which makes testing more difficult as well. 

Against this backdrop, we concluded that the timing was critical in exception handling. We analyzed the characteristics of all possible scenarios and defined four phases of exception handling. In order to minimize factors to consider at each phase, we made it a rule to handle exceptions at the earliest possible timing. 

Phase Timing Exception handling
1Prior to starting the Share module
  • Check if content is eligible for sharing
    • Is there a file in the server or on the device’s cache?
    • Does the user have the right to use the content?
  • Prevent invalid content from being selected based on the characteristics of each service
    • If such a measure is not in place, check the status of the selected content prior to starting the Share module and show an alert for invalid content, if any.
  • Create a list of target services
    • Each source service designates target services.
    • Check service access rights, if necessary (from OpenChat notes or Official Account)
2When entering the share sheet
  • Compose a service list
    • Extract a service list based on the type of selected content
  • Compose a chatroom list
    • Recent chatrooms and recent target chatrooms for sharing
    • Filter chatrooms eligible for sharing
    • Determine the number of chatrooms for sharing based on the size of the screen
3When selecting a target service
  • Check the number of content per content type
    • Is it below the maximum limit of the selected service (For example, up to 100 messages or 20 images)?
  • Check the network status, if necessary
  • Download the content, if necessary
4When sharing is canceled or completed
  • Proceed with post-processing from each service for successful sharing
  • When cancelled, maintain the previously selected content on the previous page and allow users to edit and share again.
  • When failed, take necessary actions for each error.

 

6. Improving UI/UX

We reviewed and improved the UX of sharing in each service.

 

Adopt new UI and integrate the share sheet

Figure 8. UI of the share sheet

Previously LINE’s services provided the share feature with their own UI/UX. For instance, users need to click “Save” to download a picture to Album in one service while the order of services such as Chat, Keep and Timeline on the share sheet was different in other services. Inconsistent UI/UX of the share feature caused confusions among users. We introduced the same share sheet for all services in order to eliminate inconvenience and make it more user-friendly.

When a user clicks the share button in most of the services, an action sheet appeared for service selection. It was a basic UI component provided by iOS so it was simple to implement and users were familiar with this UI. As there was still some room for improvement, we upgraded the existing UI.

First, we chose to optimize sharing in Chat as it was most frequently selected for sharing, compared to Keep or Timeline. We wanted to streamline the steps required for sharing in the service most frequently used by users. The new UI lists recent chatrooms that the user participated and shared at the top of the share sheet. When a chatroom is selected, users can add a message at the bottom and click on the share button to immediately complete the process without moving to another page. This makes sharing more approachable as users can stay on the same page even after sharing the selected content.

Previously as for sharing in Chat, it could take long to load, especially for users with a long list of chatrooms as the share sheet brought up the entire list of chatrooms. The new share sheet UI lists just a few recent chatrooms, which reduces the loading time.

Moreover, the target service list is displayed with icons and text, compared to the previous text-only list, allowing users to use the service more intuitively.

Lastly, users found it confusing as the order of the action sheet was different in each service. We defined and aligned the order of icons throughout all services.

 

 Notification of successful sharing

Figure 9-1. Previous notification

In the past, each service conducted different actions after sharing was completed.

  • Stayed on the same page with the notification of completed sharing at the top and moved to the resulting page when the notification was clicked
  • Stayed on the same page and users needed to move to the resulting page on their own.
  • Automatically moved to the page with completed sharing

Figure 9-2. Uniform notification of successful sharing

In order to eliminate discrepancies in UX between services, we implemented the Share module to display the notification for all services after sharing was completed. Previous notification UI was used in Chat only, thus, handling chatroom related data only. We redesigned it for each service to freely adjust the icon section on the left and the text section at the center so that all services could use it as well. 

As this new UI allows users to stay on the same page after receiving the notification of successful sharing, it does not interrupt with the user’s flow of action. In addition, users can easily move to the resulting page by clicking on the notification.

 

Structure before and after implementing the Share module

The structure of the share feature within the app was changed after adopting the Share module as follows.

Figure 10-1. Before implementing the Share module
Figure 10-2. After implementing the Share module

Figure 10-1 shows how it looked like when the share feature of all services was put together. You can see that connection between services almost look like a spider web. After implementing the Share module as shown in Figure 10-2, all services go through InAppSharing and InAppShareContext classes and use a uniform data model called SharableObject. Consequently, the code of the share feature can be found easily in one place, which makes it more convenient for the maintenance of the code.

Most of the share features in LINE were replaced with this new module, and we were able to achieve mainly two accomplishments. First from the UX perspective, which was one of the main initiating factors, we can provide consistent experience wherever users click on the share button. For developers, maintenance costs have been saved and it will be easier to add new services or content type to the Share module. 

 

Wrap-up

As this project could have an impact on the overall LINE iOS project, we faced extra difficulties that were not mentioned above. Sometimes there were clashes in code as the composition of the LINE iOS project was changed during Share module development. We had to change code many times due to changes in the specifications as other services were under development as well. We had to apply it to the iOS share extension since it had a different logic for transfer despite the same UI. We also took into consideration Dark Mode or Siri suggestions, introduced with iOS 13. Nevertheless, we were able to complete overall development without much difficulties as we invested significantly in the early designing phase and refactoring. We learned the importance of the project structure and design from this project.

Related Post