Main Technologies

As shown in our research, we have used Swift as our main language and Xcode and its toolchains as our IDE in our project.


alternative
alternative

Dependencies & Tools

In the meantime, we also imported some dependencies in our project to help us build, test and validate our project.


alternative

SwiftSoup

SwiftSoup is a pure Swift library, cross-platform (macOS, iOS, tvOS, watchOS and Linux!), for working with real-world HTML. It provides a very convenient API for extracting and manipulating data, using the best of DOM, CSS, and jQuery-like methods (Chatbi, 2020).

We are embedding SwiftSoup as a core part of the project to parse HTML sent from the X5Learn backend. It empowers us to understand the API changes and adapt to it quickly.

JGProgressHUD

JGProgressHUD is an elegant and simple progress HUD for iOS and tvOS (Gessner, 2020).

We adopt JGProgressHUD heavily in our user-friendly design and refreshing aspect of our application. We utilise this library in all main refresher() to clearly show the loading progress of our application and keep user informed of what the application is doing on the backend.

alternative
alternative

Jazzy

Instead of building documentation ourselves, we deployed jazzy, which hooks into Clang and SourceKit to use the AST representation of the code and actual code comments for generating documentation (realm/jazzy, 2020).

Moreover, with the touch of Jazzy templates, the documentation matches the look and feel of Apple’s official reference documentation.

XCTest

Since Xcode 5, XCTest framework was introduced to perform unit test and UI Tests for Xcode projects (XCTest, 2020).

Our team took advantage of the built-in fast framework and wrote all of our test logic including unit tests, integration tests and performance tests on this framework, and archived 90%+ coverage. This enables us to deliver our product with confidence to our clients.

alternative
alternative

SwiftFormat

SwiftFormat is a code library and command-line tool for reformatting swift code on macOS or Linux (Lockwood, 2020).

Our team was faced with the problem that coding styles are different between the 2 of us. Therefore, we decided to use code linter and formatter to make sure our code compilers and adheres to a good tidy standard in general.

Travis CI

Travis CI is a hosted continuous integration service used to build and test software projects (Travis-CI, 2020).

In this project, we followed better CI/CD practices and pipelined our application's building and testing without human intervention. Travis CI consistently delivers build results and enables us to merge Pull Requests with reliable backup.

alternative

Multi-threading and Task Queue Implementation

Our project at its core is a mobile content application with relatively heavy network load as fetching videos, pdfs, wikichunk data from numerous APIs and sources is embedded in almost every user interaction.

alternative


As demonstrated on the left, the loading process typically involves async-ly conducting network requests, retrieving data and then presenting them on the UI interface with a reloadData() called for a TableView or a CollectionView.

This poses a significant issue when multiple tasks are dispatched to another Thread and not cancellable afterwards. For example, user opens up Video A, and the application dispatches fetchVideo(), fetchSuggestedContents() and fetchWikiChunk() to another thread for async execution, and of course, a reloadData() on UI for the main thread.

To gain a general understanding of how this would typically work, see the refresher below for reference.

alternative

Following up on the previous example, a user would possibly quit Video A before all contents finish loading and quickly open up Video B, or he might repeat it several times to open up couple of other contents.

The traditional async loading strategy would cause a problem here not only as the fetch...() function stacking up slows the overall network connection, more so as the many awaiting reloadData() calls would block the main thread from doing its UI related work and causing significant lags.

Our team then came up with a solution to possibly cancel() some not so useful tasks, e.g. Video A's task when user is actually looking at another video.

alternative

Our first attempt is to build a custom OperationQueue() that takes in tasks through the MainController.Queue and manage them from there. However, this is a right step towards our final solution, but not good enough.

The backend from our client is responding to queries with an average of more than 10 seconds, and during that time you would not have access to the dataTask as it is on a daemon thread. This would still cause resource leaks and uncontrollable tasks if we only stop here, and experiment does show an alleviation of the problem above but not solving it straight-up. Therefore, as a follow up, we rewrote part of the dataTask logic and use a FetchSwitch and a DispatchSemaphore to capture the "out-of-control" tasks.

alternative alternative

With a lambda or Closure injection shown above, we are able to regain control of the dataTask and rank, update or cancel them accordingly. More so, we internally rank tasks with a max-heap to make sure we are always dealing with the most crucial task at hand in the application.

Now, ViewControllers would be able to notify the MainController of the changes and make OperationTask queueing much more efficient. We followed the Open Closed Principle and exposed easy-to-use Queue APIs (see below for example) across the application.

alternative

With some comparison in the profiling tool below, we can easily see the reduction of time hangs caused on the Main Thread, as well as a reduction of dispatch-tasks conducted. This is only tested with loading 3 videos concurrently, greater performance increase can be seen if more videos are loaded. Click on the images below for a clearer comparison.

Before
alternative
After
alternative

With some effort, it finally runs as smooth as silk without worrying anything that could possibly block the main UI Thread.

Draggable PlayerView with flexibility


alternative


One thing we noticed during our HCI design phase was that users are generally prefer the a video application that does not let video occupy the whole screen for the majority of time, especially for a learning application like our project.

Therefore, we attempted to implement a draggable PlayerView with intuitive gestures to minimise/maximise it.

The main challenge here is that you would not be able to use a standard approach like a UIViewController to handle this type of view, simply because presenting a UIViewController inside another one would cause not only significant performance issues, but also not being able to implement dragging UIViewController around.

We decided to think out of the box and used a floating NavView based UIView to archive this, as you can see below in the Storyboard. It is not a fully functional UIViewController, but instead a UIView subsequent in the AppScene of the RootViewController.

alternative

This brought up the issue that we have to control its floating coordinates by ourselves on the screen, yet to consider so many variations of screen sizes on the iOS and iPadOS platforms.

To tackle this difficulty, we started with the origin(centre point) of the View. From the image below, we utilised known constants of UIScreen.main.bounds, i.e the size of the displaying screen to figure out the origin of playerView in its 3 states.

alternative

The animation and gesture recognition of the UIView is a subsequent problem to solve. Otherwise the UIView would just jump around and very likely uncontrollable.

Likewise, we would have to figure out the exact coordinates to make it look and feel smooth when dragging around. We put forward a fast CGPoint function positionDuringSwipe() to calculate its behaviour live with no pre-made animations. See below for implementation details.

alternative

Rather than rendering pre-made animations, or fixing the coordinates to a certain point, we took the most difficult approach which is calculate everything by hand. Yet retrospectively, we think its definitely worth it as it yields the best results as well as the smoothest user experience.

Other Implementation Highlights

Apart from the two detailed main implementation highlights, we provide several other highlights that are worth noting in our product.

alternative

Pre-fetching and Generating Thumbnail on-the-fly

The X5Learn backend only provides users with video's URL. However, it doesn't provide any thumbnails, preview methods or actual video content. To tackle this issue, we created a separate operation with Grand Central Dispatch(Apple, 2009) that aims to generate thumbnails by fetching the video image at 1 minute mark.

This process is automatically activated when the ViewController determines that the content is visible by the user, which is selective yet efficient.

Whilst generating the thumbnail, we figured it would be no difference to also preload the first minute of the video, as we are using that part to generate thumbnail already.

Click on the code below to see the code of this implementation.

alternative

Extendable UIKit for Dark-mode Support

Dark-mode, as a newly introduced feature in iOS 13 and iPadOS 13, is greatly welcomed by many users.

We decided at our polishing stage that we will be providing this feature in our application. It seemed we need to re-work most of our icons and images to support the dark-mode as they might be invisible with the dark background.

However, we used the CIFilter toolkit to invert UIImages in-code, while taking precautions to preserve the original scale and size of the image.

Click on the code below to see the implementation in code.

alternative
alternative
alternative

Mock-up Backend for Pre-development

As we were developing our project, our client was developing the backend of the project. Hence, certain API endpoints like bookmark/ or notes/ are unavailable.

To mitigate their development's effect on our progress, we forked their code and kept on making mocked-up endpoints and APIs such that we can develop, test and deliver our application on time.



Custom-Built Recommendation System

At our development stage, we are facing the lack of a recommendation system on the backend to show relevant contents for the content on display.

On that note, to provide some placeholder while looking believable, we used the search function on X5Learn with the content title as input, and archived valid results. This approach has proven to be a great alternative to an actual recommendation system.

See code below for more details.

alternative
alternative

Implementation Showcase

Since our project is a mobile application, our implementation is heavily View based. Our team decide to present each view in our application for reference.



Light Mode Implementation Showcase




Dark Mode Implementation Showcase




Responsive and Light / Dark Implementation Showcase


alternative alternative

Technical Video Demo

Point-by-Point demonstration by MoSCoW List of all implemented features.

References

  • Apple (2009). Apple Technical Brief on Grand Central Dispatch" (PDF). Archived from the original on September 20, 2009. Retrieved September 12, 2009.

  • Chatbi, N., 2020. Scinfu/Swiftsoup. [online] GitHub. Available at: https://github.com/scinfu/SwiftSoup [Accessed 3 April 2020].

  • Gessner, J., 2020. Jonasgessner/Jgprogresshud. [online] GitHub. Available at: https://github.com/JonasGessner/JGProgressHUD [Accessed 3 April 2020].

  • Realm. 2020. Realm/Jazzy. [online] Available at: https://github.com/realm/jazzy [Accessed 3 April 2020].

  • Developer.apple.com. 2020. Xctest | Apple Developer Documentation. [online] Available at: https://developer.apple.com/documentation/xctest [Accessed 3 April 2020].

  • Lockwood, N., 2020. Nicklockwood/Swiftformat. [online] Available at: https://github.com/nicklockwood/SwiftFormat [Accessed 3 April 2020].

  • Travis-ci.com. 2020. Travis CI - Test And Deploy With Confidence. [online] Available at: https://travis-ci.com/ [Accessed 3 April 2020].