Skip to content
Samuli edited this page Feb 9, 2016 · 4 revisions

achso_ios file overview

This is an overview over the files in achso_ios.

Note: This is automatically generated from directories.md so changes may get overwritten.

app/

AppDelegate.swift

AppDelegate is the main entry point of the application. Other than some initialization stuff it handles the low level connection to the Core Data.

auth/

Things related to the setup

LoginWebViewController.swift

LoginWebViewController is a simple view controller containing one web view. It is used to display the OIDC logging in or registering page. The view controller tries to "trap" the login redirect and return the control to the app.

Session.swift

Manages a connection to a Layers Box or a public set of servers. Session consists of the current authenticated HTTP client and possible user.

The session is stored in disk with NSCoding using SessionData.

entities/

These are data structures used in the code.

ActiveVideo.swift

ActiveVideo is an video that is currently played or edited, in a better data structure.

Modifications to the video can be solidified with toVideo()

Annotations are separated to batches by time, so that ones that are displayed at once are contained in one batch. This is the data structure that is used with playing and editing.

ActiveVideoState.swift

ActiveVideoState is a snapshot of the state of an ActiveVideo (ActiveVideo.swift). It is stored in a more compact way (contiguous arrays) than the video itself to make it less memory intensive to store many states.

Used to implament undo in the editing.

Annotation.swift

A single annotation object is defined here.

AnnotationBase is a memory-efficent storage of an Annotation, because it's a struct instead of a class. However it's more convenient to work with classes in Swift as struct references don't really exist so Annotation is used in general, while AnnotationBase is used only when required (ActiveVideoState.swift).

AnnotationBatch.swift

AnnotationBatch is a batch of Annotations in a single time point. See ActiveVideo.swift and Annotation.swift.

Genre.swift

Genres exist in two forms, a culture-invariant ID such as "good_work" or "problem" and a localized string such as "Good work" or "Ongelma".

This mapping is often required so the localizations are defined here.

Group.swift

A group in achrails. Contains some info and a list of video IDs.

GroupList.swift

GroupList is a list of Group objects that supports serialization and deserialization for caching the groups locally.

See Group.swift.

User.swift

User maps to the user objects used in the manifest JSON format.

Video.swift

Video maps to the video manifest JSON format and exposes all the data. It has some extra data such as if the video is modified or which user downloaded it.

This is not used for playback or editing purposes, see ActiveVideo.swift.

VideoInfo.swift

VideoInfo is a lightweight video structure. It has enough data to be used in the browsing screen, but not enough for usage.

When an user selects a video for some purpose a full Video object is fetched by the id, see Video.swift.

Serializable to Core Data for quick loading and storing.

player/

This is the player activity of the app. It is a single view with many custom view and layer types.

AnnotationImages.swift

Manages the annotation ring graphics.

Request an image by parameters with getAnnotationImage. It renders a new image using gradients or returns a cached copy.

AnnotationWaitBarView.swift

AnnotationWaitBarView is the progress bar in the bottom of the screen when annotations are displayed while playing.

AVPlayerView.swift

AVPlayerView is just a view wrapping an AVPlayer for playback.

PlayButtonLayer.swift

PlayButtonLayer draws the morphing play/pause button. See PlayButtonView.swift.

PlayButtonView.swift

PlayButtonView is the play/pause button in the player view. Most of the functionality is handled by the base class UIControl, so this handles mostly the state changing.

Uses PlayButtonLayer.swift for display.

PlayerController.swift

This implements the logic for the player.

This is separated to 4 states: Playing, ManualPause, AnnotationPause, AnnotationEdit. The states implement PlayerHandler which has a response to 5 different events start, timeUpdate, userPlay, userSeek, annotationEdit. This defines a matrix of responses to events which is very predictable.

If some state has no response for an event it may switch into another and delegate the event to that.

This file also contains the logic for editing the annotations in the AnnotationEditHandler.annotationEdit function.

PlayerViewController.swift

PlayerViewController is the view controller for the video player activity.

It mostly just loads the video into an AVPlayerView (see AVPlayerView.swift) and bridges the UI to the PlayerController (see PlayerController.swift).

The UI is mostly updated in a single function refreshView() which moves the data from the PlayerController to the interface components.

SeekAnnotationLayer.swift

SeekAnnotationLayer is a CALayer responsible for drawing the annotation markers on the seek bar.

See SeekBarView.swift.

SeekBarLayer.swift

SeekAnnotationLayer is a CALayer responsible for drawing the seek bar itself, that is the background, the filled area and the ball.

See SeekBarView.swift.

SeekBarView.swift

SeekBarView is the seek bar in the player view. Most of the functionality is handled by the base class UIControl, so this handles mostly the state changing.

Uses SeekBarLayer.swift and SeekAnnotationLayer.swift for display.

VideoPlayer.swift

VideoPlayer wraps an AVPlayer and hides most of the complexity providing a simple VideoPlayerDelegate API.

VideoView.swift

VideoView is the view in the player that actually contains the video with the annotations.

The annotations are done as separate CALayers to reduce overdraw.

Uses AVPlayerView.swift and AnnotationImages.swift for display.

broswer/

This is the main activity of the app. It is based on a split view, but mostly only the VideosViewController is visible.

BrowserViewController.swift

BrowserViewController is the parent view controller that contains both CategoriesViewController.swift and VideosViewController.swift .

CategoriesViewController.swift

CategoriesViewController is the left-hand side view in the browsing activity. It manages a list view of the groups and changes the data source of VideosViewController.swift .

Collection.swift

Collection is a filtered collection of videos by some criteria.

GradientLayer.swift

GradientLayer is just a simple CALayer that just fills the area with a gradient. It is used in VideoCellView.swift to make the dark gradient over the thumbnail.

QRScanViewController.swift

QRScanViewController handles the scanning of QR codes for both tagging and searching videos.

It uses AVCaptureMetadataOutput internally for recognizing the QR code from a video feed.

SharesViewController.swift

SharesViewController wraps an WKWebView and displays the achrails web UI for some group management related functions.

Authentication is done with OAuth2 by passing Bearer token manually to the requests with a custom X-Refresh-Token header in case the token expires.

It has some Javascript bridging to modify the look of the site a little bit.

VideoCellView.swift

VideoCellView represents one cell in the grid.

Uses SDWebImage to fetch and display the image. Uses GradientLayer.swift for the gradient.

VideoDetailsViewController.swift

VideoDetailsViewController is used to display the info of one or multiple videos. Currently it's a pretty bare bones form view.

Uses XLForm internally.

VideosViewController.swift

VideosViewController is the right-hand side view of the browsing activity that contains the video thumbnail grid.

This is a complicated view and has a lot of logic.

It handles these:

  • Delegating to different view controllers
  • Displaying the video thumbnails using VideoCellView.swift
  • Filtering the videos based on genre and search query using Search.swift
  • Changing the UI depending on the state (selecting or not)
  • Creates the video objects from recorded videos

backend/

Here is code that communicates with external servers. VideoRepository contains most of the logic. Other files are mostly wrappers for service APIs.

AchMinUpUploader.swift

API wrapper for achminup, see https://github.com/bqqbarbhg/achminup

Achminup or "acsho minimal uploader" is just a simple PHP script that receives files, so it does not generate thumbnails. Uploading is done using simple HTTP post.

Note: This has no authentication.

AchRails.swift

API wrapper for achrails, see https://github.com/learning-layers/achrails

Note: This is only for wrapping the API. All the real logic relating to the syncing of the video and group data is handled in VideoRepository.swift.

achrails is used as the general backend for Ach so!. Authentication is done with OIDC as is with to the other Layers backends.

The server does not store actual video data, but only references to data uploaded to other services. Groups and video sharing are fully stored in achrails.

All communication to the Social Semantic Server is done through achrails.

Uploader.swift

Defines interfaces for uploading video and thumbnail data.

Video upload can potentially result in also a thumbnail if the service supports it, otherwise a separate thumbnail uploading service might be used.

VideoRepository.swift

VideoRepository manages the uploading and downloading of videos and groups.

refresh() performs a local update that is quite fast and simple. It just loads the entities from core data.

refreshOnline() does a full online sync which is complicated and slow (asynchronously). It uses Tasks.swift to break the operation into smaller chunks. The operations are roughly as follows:

  • Download a list of all groups
  • Donwload a list of all videos and compare their versions to the local ones
  • Upload videos which are modified locally (server handles merging conflicts)
  • Download videos that are more recent on the server

uploadVideo(...) uploads the video and thumbnail data in addition to the manifest data. The asynchronous HTTP methods make this function more complicated than it should be and it ended up as a delicate dance between background threads, callbacks and semaphores.

Uses AchRails.swift and Uploader.swift for connecting to the servers.

lib/net/

Defines functionality for connecting to remote servers securely.

HTTP requests are done with Alamofire and the types it defines are used in the API for convenience.

Alamotypes.swift

Aliases for common Alamofire types.

AuthenticatedHTTP.swift

AuthenticatedHTTP is a HTTP client that can make requests using OAuth2 Bearer authentication. It retrieves the authentication tokens and tries to refresh them if they have expired.

Uses OAuth2.swift internally.

AuthUser.swift

AuthUser is an object describing an OIDC user session. It owns the TokenSet used to make authenticated HTTP requests.

It also defines serialization and deserialization methods for storing user state when closing the app.

HTTPRequest.swift

HTTPRequest is an object that specifies an HTTP request. This does not directly do any request but can be executed by some HTTP client supporting this basic structure.

This file also defines extension methods for creating requests from NSURLs.

let request = url.request(.GET, "/relative")
client.execute(request)

OAuth2.swift

A simple OAuth2 utility for creating API calls.

NOTE: This does not actually do any HTTP requests! Use an external client for that.

Initialization:

let provider = OAuth2Provider(authorizeUrl: ..., tokenUrl: ...)
let client = OAuth2Client(provider: provider, clientId: ..., clientSecret: ..., callbackUrl: ...)

Auhtorization flow:

let authUrl = client.createAuthorizationUrlFor(.AuthorizationCode, scopes: [...])

let redirectedUrl: NSURL = `openUrlInWebViewOrBrowserAndLookForRedirect`(authUrl)
let code = OAuth2Client.parseCodeFromCallbackUrl(redirectedUrl)

let tokensRequest = client.requestForTokensFromAuthorizationCode(code)
let responseJson = `executePostRequest`(tokensRequest.url, tokensRequest.body)

let tokens = OAuth2Tokens(responseJson)

Refreshing tokens:

let oldTokens: OAuth2Tokens

let refreshRequest = client.requestForTokensFromRefreshToken(oldTokens.refreshToken!)
let responseJson = `executePostRequest`(refreshRequest.url, refreshRequest.body)

let newTokens = OAuth2Tokens(responseJson)

lib/misc/

Miscellaneous classes and utilities that are mostly contained in a single source file.

Errors.swift

This file defines error types and makes a framework for presenting users with helpful errors.

Any errors that implement PrintableError can be displayed to the user. User facing errors should be localized and if possible provided with fix actions. Errors originating from failing code logic or servers should use internal error types which don't need to be localized.

throw UserError.failedToSaveVideo.withDebugError("Could not connect to the server")

Also defines a Rust-like Try<T> that contains either a result or an error.

LocationRetriever.swift

LocationRetriever manages an CLLocationManager instance. Handles prompting the user for permission transparently.

LocationRetriever.instance.startRetrievingLocation(doStuffWhileLocating)

if let location = LocationRetriever.instance.finishRetrievingLocation() {
    LocationRetriever.instance.reverseGeocodeLocation(location) { placemark in
        if placemark {
            print(placemark.thoroughfare)
        }
    }
}

Search.swift

Simple keyword search implementation.

SearchIndex manages an database of keywords that map to SearchObjects that is potentially slow to build but fast to query.

let searchIndex = SearchIndex()

for document in documents {
    let searchObject = SearchObject(tag: document.id)
    searchObject.feed(document.title)
    searchObject.feed(document.author.name)
    searchObject.feed(document.bodyText)
    searchIndex.add(searchObject)
}

let results = searchIndex.search("simple query")

Secrets.swift

Manages Secrets.plist that contains sensitive data that is kept out of version control.

let apiKey: String = Secrets.get("API_KEY")
let apiUrl = Secrets.getUrl("API_URL")

Tasks.swift

Simple task graph implementaiton. Tasks can dynamically spawn subtasks and defer to them when completed.

Tasks need to be completed or failed explcitly, which allows using asynchronous APIs inside the task processing.

class GetAllThingsTask: Task {
    func run() {
        getAllThingsAsync() { tryThings in
            guard let things = tryThings else { self.fail(tryThings.error) }

            for thing in things {
                self.addSubtask(GetOneThingTask(thing))
            }

            self.done()
        }
    }
}

lib/helpers/

These are helper files, that mostly wrap verbose or otherwise lousy APIs with simpler ones. These should not define any big concepts or behaviour.

AVHelper.swift

Audiovisual helper functions.

Contains functions for creating and saving thumbnails from videos specified by an URL.

DateHelper.swift

Miscellanious date and time helper functions.

Defines iso8601DateFormatter for parsing and writing to the Ach so! manifest format.

GradientHelper.swift

Wraps CGCreateGradientWithColors with a simpler and more natural Swift API.

HexColor.swift

Functions for creating colors from hex values. Handy for inline declarations.

let color = hexCgColor(0x6495ED)

ImageLoader.swift

Implements SDWebImageManagerDelegate that crops downloaded images into 4:3 aspect ratio.

JsonHelper.swift

Defines JSONObject and JSONArray and wrappers for parsing and stringifying JSON data.

Also contains an extension castGet for naturally handling JSON with thrown errors.

let json = parseJson(jsonString)
let intValue: Int = json.castGet("someInt")

MathHelper.swift

Miscellanious math helper functions.

Vector2.swift

Vector2 some common vector math operations that are more cumbersome to do with CGPoint or CGSize objects.

lib/extensions/

Miscellaneous helper extension methods for existing objects.

ArrayExtension.swift

CGColorExtension.swift

CGRectExtension.swift

CGSizeExtension.swift

NSDateExtension.swift

NSURLExtension.swift

NSUUIDExtension.swift

OptionalExtension.swift

UIViewControllerExtension.swift

Clone this wiki locally