Post

Retrospective: Ara

I developed Ara, the official KAIST community app, using Flutter. It took six months to complete, and version 1.0.0 was launched on February 22, 2024.

Retrospective: Ara

App Store

Google Play

GitHub Repository

1. Key Implementation Requirements

The web-based Ara frontend and backend have been in service since 1991. However, there was no mobile app for Ara, so I developed one.

login page login page login page login page login page login page
login page login page login page login page login page login page
  • Login Logic: Integrate KAIST SSO authentication so that only KAIST students can log in.
  • Post Viewing: Users should be able to view posts organized by board.

  • Post Creation and Editing: Users can add and edit posts in HTML format, with support for attachments.
  • Comments: Enable users to comment on posts.

  • Multilingual Design: The interface should support both English and Korean.

2. Why did I choose Flutter to implement a mobile app?

2.1 Pros

  • Previous Experience: My co-worker Sang-oh Kim and I have some familiarity with Flutter.

  • Cross-Platform: Since the app needs to be available on both iOS and Android, Flutter is a good option.

2.2 Cons

  • Not a Native app: Using advanced and the latest native features can be challenging. I rely solely on the Flutter SDK and its latest updates.

  • Relatively Small Flutter Community: Fewer available libraries and community resources.

3. Structure

3.1 Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
NEW-ARA-APP
├── ios
│   ├── ...
│   └── fastlane
│       ├── Appfile
│       ├── Fastfile
│       ├── README.md
│       └── report.xml
├── android
│   ├── ...
│   └── fastlane
│       ├── Appfile
│       ├── Fastfile
│       ├── README.md
│       ├── newara-fastlane.json
│       ├── report.xml
│       └── upload-keystore.jks
├── assets
│   ├── fonts
│   │   └── ...
│   ├── icons
│   │   └── ...
│   ├── images
│   │   └── ...
│   └── translations
│       ├── en.json
│       └── ko.json
└── lib
    ├── constants
    │   ├── colors_info.dart
    │   └── ...
    ├── main.dart
    ├── models
    │   ├── article_list_action_model.dart
    │   └── ...
    ├── pages
    │   ├── board_list_page.dart
    │   └── ...
    ├── providers
    │   ├── user_provider.dart
    │   └── ...
    ├── translations
    │   ├── codegen_loader.g.dart
    │   └── locale_keys.g.dart
    ├── utils
    │   ├── cache_function.dart
    │   └── ...
    └── widgets
        ├── border_boxes.dart
        └── ...
  • CI/CD is managed by GitHub Actions and fastlane. Deployment keys, such as .jks files, are securely stored in GitHub Secrets.
  • assets/: It consists of images, fonts, and translations.
  • lib/models/: It defines the schemas for API requests and responses.
  • lib/pages/: It contains the structure of pages displayed in the app.
  • lib/providers/: Provider is an official state management libraries. Each dart file manages an entity’s state.
  • lib/widgets/: It contains reusable, common widgets.

3.2 Provider

I used the Provider library to share the same instance across the entire app. It seems like a singleton pattern, but it can also use dependency injection.

Sample

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'cart_provider.dart';
import 'cart_screen.dart';

void main() {
    runApp(MyApp());
}

class MyApp extends StatelessWidget {
    @override
    Widget build(BuildContext context) {
        return MaterialApp(
            home: MultiProvider(
                providers: [
                    ChangeNotifierProvider(create: (_) => CartProvider()),
                ],
                child: CartScreen(), // CartProvider is only available for CartScreen and its widget tree
            ),
        );
    }
}

3.3 Role

We divided roles by features.

  • Sang-oh Kim: Responsible for implementing the comment writing system, personal profile editing, scrap system, and CI/CD.
  • Me: Responsible for implementing the post and board view system, login system, and post writing system.

4. How I implemented login

The login page is composed of a WebView. To use the Ara app, users must log in through the SPARCS SSO web page, which serves as a club-based SSO system.

SPARCS SSO authenticates by storing cookies in the web browser, making a WebView essential. This includes cookies like csrftoken and sessionId for user authentication and session maintenance.

Interestingly, Flutter allows access to WebView cookies, which could pose a security risk. I’ll explain the importance of cookie permissions in a later section.

5. How We Implemented Post Creation, Editing, and Viewing

I used Flutter Quill to implement the HTML editor, and Sang-oh Kim used WebView to implement the HTML viewer.

We prioritized publisher reliability, points, and latest published date when choosing the libraries.

Pub.dev (Dart Package Manager) pub.dev

6. Troubleshooting with Play Store

6.1. Play Store Reviewer’s Spam Comments

We gave the Play Store reviewer a test account to review our app. However, for some reason, the reviewer repeatedly posted meaningless comments, such as email addresses, on the app during the review process. I contacted the Play Store regarding this behavior and received a response stating that “the developer will need to take appropriate action.” As a result, I’ve added a special message for reviewers: “Please refrain from spamming comments on this app.”

Pull Request

Reviewer's Spam Comments Review’s Comments

Received Email Received Email

6.2 Rejections

Rejection Email 1
Rejection Email 2

7. Troubleshooting with Apple App Store

7.1. Rejections

It was rejected because there was no user account deletion feature.

Hello,

Thank you for your efforts to follow our guidelines. There are still some issues that need your attention.

If you have any questions, we are here to help. Reply to this message in App Store Connect and let us know.

Guidelines 2.3.7 - Performance - Accurate Metadata

We noticed that your app subtitle includes the following trademarked term or popular app name, which may confuse customers: KAIST

App Store metadata should only include relevant content that accurately reflects the app’s core experience. Leveraging trademarked terms or the popularity of other apps is not appropriate.

Next Steps

To resolve this issue, it would be appropriate to revise your app name and subtitle so they do not include trademarked terms or popular app names.

Resources

  • Review best practices for creating App Store product pages.
  • Learn more about requirements for app names and subtitles in App Store Review Guideline 2.3.7.

Guideline 5.1.1(v) - Data Collection and Storage

We noticed that your app supports account creation but does not include an option to initiate account deletion that meets all the requirements. Specifically:

  • Your app requires users to send an email to complete account deletion.

The process for initiating account deletion must provide a consistent, transparent experience for App Store users.

Next Steps

It would be appropriate to revise your app to address the issues identified above and resubmit your app once the account deletion option meets all the requirements.

If you believe your current account deletion option meets all the requirements, either because your app operates in a highly-regulated industry or for some other reason, reply to this message and provide additional information or documentation.

Resources

  • Review frequently asked questions and learn more about the account deletion requirements.
  • Apps that offer Sign in with Apple should use the Sign in with Apple REST API to revoke user tokens.

Please see attached screenshots for details.

It was rejected because there was no user blocking feature.

Hello,

Thank you for your efforts to follow our guidelines. There are still some issues that need your attention.

If you have any questions, we are here to help. Reply to this message in App Store Connect and let us know.

Guideline 1.2 - Safety - User Generated Content

Your app enables users to post content anonymously but still does not have the proper precautions in place.

Next Steps

To resolve this issue, please revise your app to implement all of the following precautions:

  • A mechanism for users to block abusive users

Please see attached screenshots for details.

7.2 Solution

The backend team couldn’t create the function using API communication, so I implemented it using app cache as a workaround. However, this solution has limitations: if you delete and reinstall the app or install it on another phone, the blocked users won’t be blocked and your membership status won’t be properly reflected.

8. CI/CD

We implemented Android and iOS CI/CD with Fastlane and Github Actions

9. Points to improve

After finishing the project, I identified several missing points and learned the following development considerations.

9.1. MVC

If I were to implement the code again, I would solidify the MVC (Model-View-Controller) pattern. However, current code is mixed within dart files in a page folder. I would restructure it as shown below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
lib/
├── main.dart
├── models/
│   ├── user_model.dart
│   └── product_model.dart
├── views/
│   ├── home_view.dart
│   ├── login_view.dart
│   └── widgets/
│       ├── custom_button.dart
│       └── product_card.dart
├── controllers/
│   ├── user_controller.dart
│   └── product_controller.dart
├── api_service/
│   ├─  schemas.dart
│   └── dio.dart
└── utils/
    ├── constants.dart
    └── helpers.dart

  • models/: It stores the data and state of the app.
  • views/: It is directly related with UI of the app.
  • controllers/: It extends ChangeNotifier and controls data of models. When the model changes, controllers call notifyListeners() and update the views.
  • api_service/: It defines the schemas of API and API functions.

9.2. Code Push

Flutter doesn’t support Code Push, which means updating the app takes too long due to app store review processes. This is a key reason that new team members are migrating it to React Native, as Code Push would allow immediate updates without a cumbersome review.

9.3. Native App Development for Apple Watch

If I want to implement an app for Apple Watch, I would eventually need to implement Swift code. I noticed that sophisticated apps require native code.

9.4. TDD (Test-Driven Development)

Waterfall

It’s challenging to find bugs in the app. Our app follows a development cycle like the graph below:

graph TD
    A[Gather requirements] --> B[Analysis and design]
    B[Analysis and design] --> C[Code]
    C[Code] --> D[Test]
    D[Test] --> C[Code]
    D[Test] --> E[Deployment]

TDD

While TDD (Test-Driven Development) isn’t absolutely essential, it could be beneficial for effectively catching bugs and organizing features more clearly.

graph TD
    A[Gather requirements] --> B[Analysis and design]
    B[Analysis and design] --> C[Code]
    C[Write test code] --> D[Write feature code]
    D --> E[Test]
    E --> D
    E --> F[Deployment]

This post is licensed under CC BY 4.0 by the author.