Skip to content

hsuanchih/CodingChallenge-500px

Repository files navigation

CodingChallenge-500px

The objective of this coding challenge is to create a simple photo-browsing app using 500px's APIs. The deliverable should fulfill a minimal set of requirements as outlined below:

  • Photo Showcase

    The app should showcase Popular photos from 500px dynamically obtained from the 500px API.
    Futhermore, the showcase should allow app users to browse through multiple pages of content.

  • Photo Details

    When a user clicks on showcased photo, a full screen version of the photo should be displayed along with detailed information about the photo.

The engineering team at 500px works with IGListKit, RocketData and Alamofire on a day-to-day basis to deliver performant & reliable products. My attempt at this challenge will be to recreate a simplified version of 500px using their native counterparts - UICollectionView, CoreData, and URLSession.

The Demo

Demo

TODO

  • Justified Layout - Showcasing Photos with Preserved Aspect Ratios

    A classic approach to justifying layout of a photo grid to display photos in their aspect ratios finds its roots in the Text Justification problem. The idea is to have photo dimensions resized to fit fixed row heights while preserving aspect ratios. Photo failing to stack to the end of the row is moved to the next row.

    The drawback with this approach is that it introduces either uneven inter-item spacings or inconsistent margins at the end of each row, depending on the fitting policy. With somewhat flexible row heights, inter-item spacing can be strictly enforced to give a more consistent look.

    500px's own Greedo Layout is an elegant take on the idea. Nevertheless, the tough grind with justified layout implementations is adapting to device orientation transitions.

Instruction

Create file "consumer_key.txt" with 500px's consumer key in the Xcode project.

Design

Design

Data Source


Fetching Remote Data

Request is a lightweight web service client - a simple wrapper around URLRequest & URLComponents coupled with its own builder designed to fullfil a subset of 500px's HTTP GET requests. It supports query parameter injection through the ResourceParameter abstraction.

Handling Fetched Data

Response from the request are de-serialized into CoreData entities using JSONDecoder. Data models are managed objects subclasses conforming to the Decodable protocol. Entities are created and added to the background managed object context attached to the JSONDecoder via its userInfo dictionary.

CoreData

  • Why CoreData?

    Or more specifically, why use CoreData with SQLLite as its persistent store type as opposed to using CoreData simply for its in-memory object graph? Keeping managed objects and their relationships in memory is bound to overrun the heap at some point as the number of objects grow. So the idea is to have objects backed in the persistent store and faulted into memory as needed.

  • Managed Object Context Relationships

    The decision is to forgo the typical parent-child relationship between managed object contexts. The reason being that the managed object model is setup to use the id property of each Photo/User entity as the primary key (and the merge policy on relevant managed object contexts set to NSMergeByPropertyObjectTrumpMergePolicy) so to avoid duplicates.

    Uniqueness constraint validation in CoreData relies on NSPersistentStoreCoordinator working along-side the relational database to do the heavy-lifting, so changes propagated between managed object contexts through parent-child relationships will also bring along unresolved duplications. Thus the decision is to have each managed object context wired to the persistent store coordinator as its parent store, and propagate changes with saves.

ImageCache

Loading images from remote is expensive, naturally a local copy of images fetched should be kept. Databases are not ideal for storing BLOBs, thus ImageCache exists to fulfill this purpose - to fetch images through remote urls as required and store them locally in the application's caches directory.

PersistentStore is used by the ImageCache to persist & retrieve images from the local store. It implements two-level caching, using NSCache as an intermediate and resorting to disk only on cache miss. Operations on the PersistentStore are controlled through an designated dispatch queue, allowing for concurrent reads while enforcing exclusive writes.

Presentation


The PhotoGrid

ShowcaseViewController pulls remote data through its DataProvider, and uses NSFetchedResultsController together with UICollectionView and UICollectionViewDataSource to bridge fetched data with the user interface. Conforming to NSFetchedResultsControllerDelegate brings data source changes to the user interface.

Infinite Scroll

Infinite scroll is accomplished through UICollectionViewDataSourcePrefetching.

Presenting Photo Details

Photo details are presented by DetailViewController. This view controller is presented modally by ShowcaseViewController inside a UIPageViewController. Navigation between detail views is possible by having ShowcaseViewController conform to UIPageViewControllerDataSource.

Author

Hsuan-Chih Chuang, hsuanchih.chuang@gmail.com

Releases

No releases published

Packages

No packages published

Languages