PJFDataSource is a small library that provides a simple, clean architecture for your app to manage its data sources while providing a consistent user interface for common content states (i.e. loading, loaded, empty, and error).

Overview

PJFDataSource

CI Status Version License Platform

PJFDataSource is a small library that provides a simple, clean architecture for your app to manage its data sources while providing a consistent user interface for common content states (i.e. loading, loaded, empty, and error).

Inspiration

PJFDataSource was built as a simpler and more focused alternative to Apple's AdvancedCollectionView sample code which was originally introduced in the 2014 WWDC presentation Advanced User Interfaces with Collection Views.

We keep the basic patterns that we like, but discard most of the more complex features. For example, PJFDataSource doesn't have any built-in support for "aggregate data sources". Nor does it do any handling of your actual view's content (e.g. configuring and displaying UICollectionViewCells). It doesn't even require you to use a UICollectionView. Your app remains responsible for providing and configuring your user interface's content view, giving you the flexibility to choose a table view, collection view, stack view, etc.

Installation

CocoaPods

To install PJFDataSource in your iOS project, install with CocoaPods:

platform :ios, '11.0'
pod 'PJFDataSource'

Demo

To see the featureset of PJFDataSource in a more concrete way, check out the simplistic demo app included in this repository.

  1. Clone this repository to your machine
  2. Open PJFDataSource.xcodeproj in Xcode
  3. Switch your Target to PJFDataSourceDemoApp and Run

The demo app will open in the iOS Simulator, presenting you with a table view showing a bunch of colored rows. This is our simplistic content view, a UITableView. The three buttons at the top of the view, labeled "Success", "Empty" and "Error" simply tell the demo app to reload it's content, and simulate either a successful response, an empty response, or an error response. Give each of these a try and notice:

  • Tapping any of the buttons triggers a reload of the content. Notice that the UI transitions to show a loading indicator while we wait for the asynchronous data load. The loading indicator image can be customized using the UIAppearance protocol on PJFLoadingView.
  • Tapping the "Empty" button will simulate the data source loading without error, but determining it has no content to show (see -[PJFDataSource hasContent]). The UI provides a way of displaying an image/title/message and action, configured via your PJFContentWrapperViewDelegate. The appearance of this placeholder view can be customized using the UIAppearance protocol on PJFImageTitleMessageView.
  • Tapping the "Error" button will simulate an error loading content and provide a similar placeholder UI. Your PJFContentWrapperViewDelegate is provided the underlying NSError object, so you can customize the UI based on the specific error.
  • Try tapping one of the buttons several times in a row, then quickly tapping a different button. You'll see that the final UI will reflect your final tap, ignoring responses from the earlier requests. See PJFLoadingCoordinator and the single-boolean "state machine" PJFLoadingState.

That's really all there is to it!

Usage

There are two primary interaction points between your app and PJFDataSource are:

  • PJFContentWrapperView, the wrapper view you'll insert into your view hierarchy. PJFDataSource components will work with the content wrapper view to switch between your app-provided content view and the PJFDataSource-provided views for loading/empty/error states.
  • PJFDataSource, an abstract class which you'll subclass for each of your data sources. The data source is responsible for loading content, updating its internal model to reflect the loaded content, and notifying of success/failure via its loadingCoordinator. Often, your data source will also serve as the data source for your content view (e.g. as the UITableViewDataSource for a UITableView).

Basic integration into your app looks like this:

  1. Create the view controller that will own the content view you want to show.
  2. In the view controller, instantiate a PJFContentWrapperView using initWithFrame:contentView:, passing in your content view of choice (e.g. a UITableView). Insert the wrapper view into the appropriate place in your view hierarchy—likely as a full-size subview of your root view. Although not required, you will likely want to set your view controller as the PJFContentWrapperViewDelegate of the wrapper view, and implement at least willShowNoContentView: and willShowErrorView: so that you can customize the content of these placeholder views.
  3. In the view controller, instantiate your PJFDataSource subclass (see below), with your view controller as the delegate. Implement contentWrapperViewForDataSource:, the single required method on the PJFDataSourceDelegate protocol, having it return the content wrapper view created above.
  4. In the view controller's viewWillAppear: method, call loadContent on your data source.

You've now got your own PJFDataSource subclass instance. You're telling it to load when your view controller appears. The data source does its work and updates the content wrapper view appropriately. Ta-da!

Example -[PJFDataSource loadContent]

Implementing your loadContent method correctly is important and not entirely obvious from the API. The key is that the PJFDataSource instance uses its provided PJFLoadingCoordinator to actually kick off the load, as well as to notify of success/failure. See this example, taken directly from the demo app:

- (void)loadContent;
{    
    [self.loadingCoordinator loadContentWithBlock:^(PJFLoadingState *loadingState) {
        [self.colorsLoader asyncLoadColorsWithSuccess:^(NSArray *colors) {
            if (!loadingState.valid) {
                return;
            }
            
            self.colors = colors;
            
            [self.loadingCoordinator loadContentDidFinishWithError:nil];
            
        } error:^(NSError *error) {
            if (!loadingState.valid) {
                return;
            }
            
            [self.loadingCoordinator loadContentDidFinishWithError:error];            
        }];
    }];
}

The self.loadingCoordinator object is provided by the PJFDataSource base class. When you start loading your content, do so via the loadContentWithBlock: method. This allows the loading coordinator to know when you start loading and allows it to provide a PJFLoadingState object so we can ignore incoming responses from obsolete requests (i.e. invalidated by a more recent request).

Within the loadContentWithBlock: block, you'll kick off your async loading task with callbacks for completion. In this case we're using another object (self.colorsLoader) to do the heavy lifting. In our completion blocks, we check the loadingState to determine if it is still current. If it isn't, we ignore this response by returning immediately. If it's still current, then we'll update our internal model (e.g. self.colors = colors;) and then notify our loadingCoordinator of success or failure via loadContentDidFinishWithError:.

Appearance Styling

PJFDataSource provides for limited appearance styling via the UIAppearance mechanism. See the properties marked with UI_APPEARANCE_SELECTOR in PJFLoadingView and PJFContentWrapperView. Also see the demo app's AppAppearance class, whose sole purpose is to set up the appearance styling.

Missing Features

We want to keep PJFDataSource simple, but there are some things we know we'd like to add:

  • Increased customizability of PJFImageTitleMessageView via the UIAppearance protocol
  • Customization of the animation (currently a rotation) applied to the PJFLoadingView's loading image

If you'd like to help, see "Contributing" below.

Requirements

  • iOS 8 or later.

Contributing

We’re glad you’re interested in PJFDataSource, and we’d love to see where you take it. Please read our contributing guidelines prior to submitting a Pull Request.

Fry Meme wondering about PJFDataSource's name prefix

You might also like...
Conv smart represent UICollectionView data structure more than UIKit.
Conv smart represent UICollectionView data structure more than UIKit.

Conv Conv smart represent UICollectionView data structure more than UIKit. Easy definition for UICollectionView DataSource and Delegate methods. And C

Conv smart represent UICollectionView data structure more than UIKit.
Conv smart represent UICollectionView data structure more than UIKit.

Conv Conv smart represent UICollectionView data structure more than UIKit. Easy definition for UICollectionView DataSource and Delegate methods. And C

It's simple IOS Study Case - Movie App

IOS Deployment Info: IOS 15.0 ve üzeri Kullanılan Teknolojiler ve Yapılar Kingfisher AVFoundation URLSession Generics CollectionView VIPER Swipe Gestu

💾 A library for backporting UITableView/UICollectionViewDiffableDataSource.

DiffableDataSources 💾 A library for backporting UITableView/UICollectionViewDiffableDataSource powered by DifferenceKit. Made with ❤️ by Ryo Aoyama I

Netflix and App Store like UITableView with UICollectionView, written in pure Swift 4.2
Netflix and App Store like UITableView with UICollectionView, written in pure Swift 4.2

GLTableCollectionView Branch Status master develop What it is GLTableCollectionView is a ready to use UITableViewController with a UICollectionView fo

Easy and type-safe iOS table and collection views in Swift.
Easy and type-safe iOS table and collection views in Swift.

Quick Start TL;DR? SimpleSource is a library that lets you populate and update table views and collection views with ease. It gives you fully typed cl

A SwiftUI collection view with support for custom layouts, preloading, and more.
A SwiftUI collection view with support for custom layouts, preloading, and more.

ASCollectionView A SwiftUI implementation of UICollectionView & UITableView. Here's some of its useful features: supports preloading and onAppear/onDi

An iOS drop-in UITableView, UICollectionView and UIScrollView superclass category for showing a customizable floating button on top of it.
An iOS drop-in UITableView, UICollectionView and UIScrollView superclass category for showing a customizable floating button on top of it.

MEVFloatingButton An iOS drop-in UITableView, UICollectionView, UIScrollView superclass category for showing a customizable floating button on top of

A Swift mixin for reusing views easily and in a type-safe way (UITableViewCells, UICollectionViewCells, custom UIViews, ViewControllers, Storyboards…)
A Swift mixin for reusing views easily and in a type-safe way (UITableViewCells, UICollectionViewCells, custom UIViews, ViewControllers, Storyboards…)

Reusable A Swift mixin to use UITableViewCells, UICollectionViewCells and UIViewControllers in a type-safe way, without the need to manipulate their S

Comments
  • Composed Datasources

    Composed Datasources

    This looks like a great tool, but I am wondering if there is a way to handle composed datasources (i.e. a dataSource made up of other datasSources, each representing, perhaps, a section)?

    opened by inPhilly 4
  • Add basic support for refreshing of content without removing the content view

    Add basic support for refreshing of content without removing the content view

    This is primarily intended to support 'pull to refresh' type of behavior, where you trigger a reload of the data source, but leave the currently-loaded content view in place.

    Also added PJFContentWrapperViewDelegate support for will/didShowLoadingView.

    opened by mthole-old 0
Releases(1.0.6)
Owner
Square
Square
TLIndexPathTools is a small set of classes that can greatly simplify your table and collection views.

TLIndexPathTools TLIndexPathTools is a small set of classes that can greatly simplify your table and collection views. Here are some of the awesome th

SwiftKick Mobile 347 Sep 21, 2022
Dwifft is a small Swift library that tells you what the "diff" is between two collections

Dwifft! In 10 seconds Dwifft is a small Swift library that tells you what the "diff" is between two collections, namely, the series of "edit operation

Jack Flintermann 1.8k Dec 12, 2022
A guy that helps you manage collections and placeholders in easy way.

Why? As mobile developers we all have to handle displaying collections of data. But is it always as simple as it sounds? Looks like spaghetti? It is a

AppUnite Sp. z o.o. Spk. 47 Nov 19, 2021
Automates prefetching of content in UITableView and UICollectionView

Automates preheating (prefetching) of content in UITableView and UICollectionView. Deprecated on iOS 10. This library is similar to UITableViewDataSou

Alexander Grebenyuk 633 Sep 16, 2022
🚴 A declarative library for building component-based user interfaces in UITableView and UICollectionView.

A declarative library for building component-based user interfaces in UITableView and UICollectionView. Declarative Component-Based Non-Destructive Pr

Ryo Aoyama 1.2k Jan 5, 2023
Infinite paging, Smart auto layout, Interface of similar to UIKit.

Infinite paging, Smart auto layout, Interface of similar to UIKit. Appetize's Demo Requirements Swift 4.2 iOS 8.0 or later How to Install PagingView C

Kyohei Ito 314 Dec 6, 2022
A data-driven UICollectionView framework for building fast and flexible lists.

A data-driven UICollectionView framework for building fast and flexible lists. Main Features ?? Never call performBatchUpdates(_:, completion:) or rel

Instagram 12.5k Jan 1, 2023
This component allows for the transfer of data items between collection views through drag and drop

Drag and Drop Collection Views Written for Swift 4.0, it is an implementation of Dragging and Dropping data across multiple UICollectionViews. Try it

Michael Michailidis 508 Dec 19, 2022
Generic collection view controller with external data processing

FlexibleCollectionViewController Swift library of generic collection view controller with external data processing of functionality, like determine ce

Dmytro Pylypenko 3 Jul 16, 2018
💾 🔜📱 Type-safe data-driven CollectionView, TableView Framework. (We can also use ASCollectionNode)

⚠️ The latest updates is this PR. It changes the difference algorithm to DifferenceKit. DataSources ?? ?? ?? Type-safe data-driven List-UI Framework.

Muukii 563 Dec 16, 2022