/Snrlax

Primary LanguageSwiftMIT LicenseMIT

SNRLAX

Snrlax: Simple, Fluff-less, Swift Networking

Twitter

Swift-Native REST-compliant Library for Asynchronous Transactions

Snrlax is the leanest HTTP(S) networking library for iOS, making it wildly easy to securely exchange information with any remote API.

Developed for both ease of use and familiarity, Snrlax uses concise syntax and method delegation to fit in swimmingly alongside the Swift 3 standard library. This allows new iOS developers to adapt future-leaning habits while still allowing experienced developers to follow patterns they are already comfortable with.

Features

  • Written originally and entirely in Swift
    • Removes need for Obj-C Bridging
    • Leaves behind outdated patterns found in libraries based on pre-Swift practices
    • Much smaller than Alamofire, AFNetworking, and Firebase
  • HTTP Secure by Default
  • Network Activity Indicator Management by Default
    • Dynamically display when requests are in progress and hide when all requests are complete or suspended
    • Optional delegate for additional custom 'loading' widgets (UI elements)
  • HTTP Response Code Validation by Default
  • Easily customizable request header Key-Value Pairs
  • Protocols for variable request results
  • Authentication with URLCredential
  • Enforced asynchronious transactions
    • Much like Snorlax multitasks while RESTing, your application must be multi-thread compliant. This pushes developers to write UX-considerate code.
  • Dynamic response management, through both polymorphism and/or delegation

v1.0 Release Promises

  • Download File using Request or Resume Data
    • Request
    • Resume Data
  • Upload File / Data / Stream / MultipartFormData
  • Chainable Request / Response Methods
    • Network request -> New Task
    • File download -> New Task
    • File upload -> New Task
  • Dynamically Adapt and Retry Requests
  • TLS Certificate and Public Key Pinning
  • Network Reachability
  • User Interface Bindings
    • UIImageView
    • UITableView
    • SnrlaxVideoView
  • Comprehensive Unit and Integration Test Coverage
  • Complete Documentation

Requirements

  • iOS 8.0+
  • Xcode 8.1+
  • Swift 3.0+

Installation

CocoaPods

CocoaPods is a centralized dependency manager for Cocoa projects. Assuming you have Ruby, you can install it with the following command:

$ gem install cocoapods

CocoaPods 1.1.0+ is required to build release version of Snrlax 1.0.0+.

To integrate Snrlax into your Xcode project using CocoaPods, specify it in your Podfile:

source 'https://github.com/CocoaPods/Specs.git'
use_frameworks!

target '<Your Target Name>' do
    pod 'Snrlax', '~> 0.1'
end

Then, run the following command:

$ pod install

Carthage

Carthage is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks.

You can install Carthage with Homebrew using the following command:

$ brew update
$ brew install carthage

To integrate Snrlax into your Xcode project using Carthage, specify it in your Cartfile:

github "rwolande/Snrlax" ~> 0.1

Run carthage update to build the framework and drag the built Snrlax.framework into your Xcode project.

Swift Package Manager

The Swift Package Manager is a tool for automating the distribution of Swift code and is integrated into the swift compiler. Be aware that the Swift Package Manager is in early development; this said, Snrlax does support its use on supported platforms.

Once you have your Swift package set up, adding Snrlax as a dependency is as easy as adding it to the dependencies value of your Package.swift.

dependencies: [
    .Package(url: "https://github.com/rwolande/Snrlax.git", majorVersion: 0)
]

Embedded Framework

  • Due to the nature of a Snrlax being a quick and nimble networking library, we anticipate many developers using Snrlax as they learn Swift. Due to the nuances and bloat of embedded frameworks, we strongly discourage their use in general and ask only professionals consider their use with Snrlax.

Usage

Snrlax is a great library for API networking as well as media transactions.

API Networking

To use Snrlax, there are a few easy steps you'll want to follow:

1x per project

  1. Install Snrlax
  2. Import Snrlax in relevant Swift scripts
import Snrlax
  1. Configure your instance to reflect your API.

0-1x per request

  1. Make Request
  2. Handle Response

Configuration

A singleton for Snrlax will be your primary instance

Snrlax.shared

It can be configured by assigning an instance of SnrlaxServiceConfiguration

let YOUR_HOST_DOMAIN = "api.snrlax.com/"
Snrlax.shared.configuration = SnrlaxServiceConfiguration(host: YOUR_HOST_DOMAIN)

HTTPS is assumed by default. We encourage you to use SSL (HTTPS) for all data transfers. In the event you must use an unecrypted connection, you can turn SSL off in your configuration

Snrlax.shared.configuration!.ssl = false //HTTP://\(YOUR_HOST) Requests

If you need to include custom header Key-Value pairs, simply include them in your Snrlax configuration initializer.

Snrlax.shared.configuration = SnrlaxServiceConfiguration(host: YOUR_HOST_DOMAIN, header_keys: ["user":"abc"])

You can also override the current header keys whenever you like.

Snrlax.shared.configuration!.header_keys = ["user":"def","extra":"param"]

Making a Request

Passing Body Data: QueryDataSource

Oftentimes, you'll want to include custom paramaters with your request. This is where Snrlax shines, both with it's Swifty signatures and it's dynamic overloading ability. A class which conforms to QueryDataSource can optionally implement a body() function to return custom parameters.

QueryDataSource Methods
func body(query: SnrlaxQuery) -> [String:Any]
class ViewController: UIViewController, QueryDataSource
{
	func body(query: SnrlaxQuery) -> [String:Any]
	{
		return [
		"universities": [
			1,2,3],
    	"coordinate": [
    		"latitude": "42.24", "longitude" : "-83.1"],
        "inputted_text": "Hello Kanto!"]
	}
}

Including the data_source parameter with request():

class ViewController: UIViewController, QueryDataSource
{

        override func viewDidLoad()
        {
                super.viewDidLoad()
                
                let endpoint = SnrlaxEndpoint(literal: "user/2") //Domain-specific route
                Snrlax.shared.request(endpoint: endpoint, parser_delegate: nil, data_source: self) //data_source -> QueryDataSource
        }
        
        //Will now be called back when forming request
        func body(query: SnrlaxQuery) -> [String:Any]
	{
		return [
		"universities": [
			1,2,3],
    	"coordinate": [
    		"latitude": "42.24", "longitude" : "-83.1"],
        "inputted_text": "Hello Kanto!"]
	}
}

Handle a Response

Response Management: QueryDelegate

One of the many advantages of Snrlax is it's rather Swifty response management. Following Apple's lead, Snrlax opts to use delegatation (protocol inheritance) over closures for response management. Any class which will process and parse a JSON result must conform to the QueryDelegate protocol.

QueryDelegate Methods
func successful_query(query: SnrlaxQuery, body: [String : AnyObject])
func failed_query(query: SnrlaxQuery, error: NSError?)

A slightly more practical implementation:

class ViewController: UIViewController, QueryDelegate
{

        override func viewDidLoad()
        {
                super.viewDidLoad()
                
                let endpoint = SnrlaxEndpoint(literal: "user/2") //Domain-specific route
                Snrlax.shared.request(endpoint: endpoint, parser_delegate: self) //parser_delegate -> QueryDelegate
        }
        
        //Optional
        func successful_query(query: SnrlaxQuery, body: [String : AnyObject])
        {
                print(body)
        }
        
        //Optional
        func failed_query(query: SnrlaxQuery, error: NSError?)
        {
                
        }
}

Handling the Response of a Request made in Snrlax is straight forward. All keys from your API will be root-keys of the body Dictionary passed in successful_query().

  • In the event a JSONArray is returned from your API at the root level, the body will have 1-root key: "data", which will map to your array values.

  • At least one key will always be included in Underlying data will be found at the root level. This minimizes much of the 'Swift Optional Dance' while also keeping your data concise and most easily processed.

Finally, an actually practical implementation of successful_query()

let API_MESSAGES_KEY = "messages"
let API_META_KEY = "meta"

func successful_query(query: SnrlaxQuery, body: [String : Any])
{

	guard let raw_messages = body[API_MESSAGES_KEY] as? [Any]
	else
	{
		//No messages
		return
	}

	for message in raw_messages
	{
		//Process each message as you like: local memory, core data, etc.
	}

	DispatchQueue.main.async (execute: {
		//Optionally cease displaying any 'loading' widgets or update heuristics
                                self.tableView.reloadData()
                })
}

Inheriting QueryDataSource & QueryDelegate

We encourage Snrlax users to create a pair of custom classes to conform to QueryDelegate and QueryDataSource.

Both QueryDelegate functions are optional so all conforming classes will compile.

class ViewController: UIViewController, QueryDelegate, QueryDataSource {
}
Default Body Data

In the case you consistently provide default body parameters, Snrlax offers a boiler-plate-minimizing solution by always including a body assuming: Snrlax.shared.global_data_source != nil.

  • In the event you pass a data_source to the request() method, both bodies will be included as one combined body. If there is a key collision, the passed QueryDataSource will take precedence over the global QueryDataSource.
public class DefaultQueryDataSource: QueryDataSource
{
        static let shared = DefaultQueryDataSource()
        public func body(query: SnrlaxQuery) -> [String:Any]
        {
                return [
                        "coordinate": [
                                "latitude": "42.24", "longitude" : "-83.1"]]
        }
}

Snrlax.shared.global_data_source = DefaultQueryDataSource.shared
Default Response Delegate

It is highly encouraged that you also have a global response delegate. To provide a 2017-quality user experience, users expected heuristics indicating different states like success, pending, and failed. This is particularly useful with the modern UITableView "Scroll Down to Refresh" experience. You might find the following as a good template:

public class DefaultQueryDelegate: QueryDelegate
{
        static let shared = DefaultQueryDelegate()
        func successful_query(query: SnrlaxQuery, body: [String : AnyObject])
        {
                //Parse application-common parameters
                //Display any Application-Window level heuristics
        }
        
        func failed_query(query: SnrlaxQuery, error: NSError?)
        {
                //Display UIAlertViewController or custom alert heuristic
        }
}

Snrlax.shared.global_parser_delegate = DefaultQueryDelegate.shared

-Snrlax.shared.global_parser_delegate will process the query result before a query-specific delegate is called. Notably, this is done on a background thread, so you can safely assume all data modified in your global class. This cleanly seperates your data management to allow you to update the interface on the main thread within your custom handler method.

Networking with Snrlax is done asynchronously. Asynchronous data request management is an integral aspect of modern application development and, in agreement with Apple, Snrlax was developed with this in mind.

If no encoding is specified, Snrlax will use the text encoding specified in the HTTPURLResponse from the server. If the text encoding cannot be determined by the server response, it defaults to .isoLatin1.

All JSON serialization is handled by the JSONSerialization API in the Foundation framework. Unlike the Alamofire underpinnings, multiple response handlers will still only require server data to be serialized a single time. With large body loads, this can make Snrlax O(n)-times faster than Alamofire.

Response Validation

By default, Snrlax treats any completed request to be successful, regardless of the content of the response. Calling validate before a response handler causes an error to be generated if the response had an unacceptable status code or MIME type.

Response Code Validation

Response handling is not configured by default. To perform validation of the HTTPURLResponse from your API, you can assign maps to your configuration class, one for 'accepted codes' and one for 'problematic codes'.

For example, response status codes in the 400..<500 and 500..<600 ranges do NOT automatically trigger an Error.

Response Caching

Response Caching is handled on the system framework level by URLCache. It provides a composite in-memory and on-disk cache and lets you manipulate the sizes of both the in-memory and on-disk portions.

By default, Snrlax leverages the shared URLCache. In order to customize it, see the Session Manager Configurations section.

HTTP Methods

The RESTmethod enumeration lists many the HTTP methods defined in RFC 7231 §4.3:

public enum RESTmethod
        {
                case POST
                case GET
                case PUT
                case DELETE
                
                var includes_body: Bool
                {
                        return self == .POST || self == .PUT
                }
                
                var string: String
                {
                        switch self
                        {
                        case .POST:
                                return "POST"
                        case .GET:
                                return "GET"
                        case .PUT:
                                return "PUT"
                        case .DELETE:
                                return "DELETE"
                        }
                }
        }

These values can be passed as the method argument to the SnrlaxEndpoint API:

let USER_ROUTE = "user"

//Case 1
let endpoint = SnrlaxEndpoint(literal: USER_ROUTE + "/2") //Method defauls to get
Snrlax.shared.request(endpoint: endpoint)

//Case 2
let endpoint = SnrlaxEndpoint(literal: USER_ROUTE + "/2", method: .GET) //Same as above
Snrlax.shared.request(endpoint: endpoint)

//Case 3
let endpoint = SnrlaxEndpoint(literal: USER_ROUTE, method: .POST) //Post method will be used
Snrlax.shared.request(endpoint: endpoint)

This allows common RESTful practices to be mirrored in Swift. Routes can be recycled with different appendments (as seen above) and also with different methods.

The SnrlaxEndpoint.rest_method parameter defaults to .get.

Media Transactions

Snrlax provides it's own caches, SnrlaxMediaCache. SnrlaxMediaManager has an array of SnrlaxMediaCaches. By default, there is one for thumbnails, one for higher-quality images, and one for video data. When you make a request for an image that you have not yet cached, if a corresponding thumbnail has been cached it will be used until the higher-quality image is fully received.

Downloading Media

To download an image, call the pull method of your shared instance: Snrlax.shared.media_manager.pull().

The first argument is a required string and the second is an enum SnrlaxMediaManager.MediaType. This is specified so the correct cache is used, and so thumbnails can be used if available.

Finally, optionally pass a UIImageView, which will be updated asynchronously. Unlike alternative libraries, when an image download is complete, Snrlax ensures the ImageView still wants the same image, making it UITableViewCell-recycling-safe. Accordingly, Snrlax is a perfect library for image displaying within UITableViews. Again, this forces developers to write UX-considerate code.

let IMAGE_URL = "https://static1.squarespace.com/static/5563f674e4b024c2e33e24e5/576fb637b3db2bd35f665512/576fb64bff7c50a6aeabf726/1466938964385/Globs.jpg?format=1500w"
                
Snrlax.shared.media_manager.pull(endpoint: IMAGE_URL, media_type: .image, image_view: image_view)

Extension Libraries

While Snrlax will unequivocally remain a "no fluff" library for Swift Networking, additional components have been created to compliment the Snrlax ecosystem. These can additionally be included in your project, with specific instructions within each repository.

  • SnrlaxS3 - An AWS S3-focused library that extends media management to support S3 buckets.
  • SnrlaxUI - A User Interface library that extends UIKit staples to optionally bind with data, while also providing elegant widgets for image, gif, & video media.

Communication

  • If you found a bug, open an issue.
  • If you have a feature request, open an issue.
  • If you want to contribute, submit a pull request.

Open Radars

There are currently no open radars for Snrlax! Good job guys!

FAQ

Where does the name come from?

SNRLAX is an acronym for Swift-Native REST-compliant Library for Asynchronous Transactions. We also couldn't resist the play on words with REST.

Credits

Snrlax is created and maintained by Ryan Wolande & Friends. You can follow the development cycle on Twitter at @SnrlaxSwift for project updates and releases. Feel free to tweet at us for feedback, requests, or to bring attention to anything else involving the Snrlax library.

Security Disclosure

If you believe you have identified a security vulnerability with Snrlax, you should report it as soon as possible via email to security@snrlax.com. Please do not post it to a public issue tracker.

License

Snrlax is released under the MIT license. See LICENSE for details.