-
Notifications
You must be signed in to change notification settings - Fork 0
File documentation
This is an overview over the files in achso_ios.
Note: This is automatically generated from directories.md so changes may get overwritten.
AppDelegate is the main entry point of the application. Other than some initialization stuff it handles the low level connection to the Core Data.
Things related to the setup
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.
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.
These are data structures used in the code.
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 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.
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 is a batch of Annotations in a single time point.
See ActiveVideo.swift and Annotation.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.
A group in achrails. Contains some info and a list of video IDs.
GroupList is a list of Group objects that supports serialization and deserialization for caching the groups locally.
See Group.swift.
User maps to the user objects used in the manifest JSON format.
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 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.
This is the player activity of the app. It is a single view with many custom view and layer types.
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 is the progress bar in the bottom of the screen when annotations are displayed while playing.
AVPlayerView is just a view wrapping an AVPlayer for playback.
PlayButtonLayer draws the morphing play/pause button. See 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.
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 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 is a CALayer responsible for drawing the annotation markers on the seek bar.
See SeekBarView.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 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 wraps an AVPlayer and hides most of the complexity providing a simple VideoPlayerDelegate API.
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.
This is the main activity of the app. It is based on a split view, but mostly only the VideosViewController is visible.
BrowserViewController is the parent view controller that contains both CategoriesViewController.swift and VideosViewController.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 is a filtered collection of videos by some criteria.
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 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 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 represents one cell in the grid.
Uses SDWebImage to fetch and display the image. Uses GradientLayer.swift for the gradient.
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 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
Here is code that communicates with external servers. VideoRepository contains most of the logic. Other files are mostly wrappers for service APIs.
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.
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.
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 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.
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.
Aliases for common Alamofire types.
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 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 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)
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)
Miscellaneous classes and utilities that are mostly contained in a single source file.
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 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)
}
}
}
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")
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")
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()
}
}
}
These are helper files, that mostly wrap verbose or otherwise lousy APIs with simpler ones. These should not define any big concepts or behaviour.
Audiovisual helper functions.
Contains functions for creating and saving thumbnails from videos specified by an URL.
Miscellanious date and time helper functions.
Defines iso8601DateFormatter for parsing and writing to the Ach so! manifest format.
Wraps CGCreateGradientWithColors with a simpler and more natural Swift API.
Functions for creating colors from hex values. Handy for inline declarations.
let color = hexCgColor(0x6495ED)
Implements SDWebImageManagerDelegate that crops downloaded images into 4:3 aspect ratio.
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")
Miscellanious math helper functions.
Vector2 some common vector math operations that are more cumbersome to do with CGPoint or CGSize objects.
Miscellaneous helper extension methods for existing objects.