It works by augmenting UINavigationController's traditional interactions to add a gesture to view each of the view controller's that have been pushed on to it. We can use it just like a regular navigation controller:
The library will handle showing the history scroll view when the user long presses the back button. If the user's device supports 3D Touch, the history scroll view activate with that gesture instead.
We can also trigger the history view to be shown at anytime like this:
navigationController?.showHistory()
We can customize the background color shown behind all the “zoomed out” view controller easily:
Today we'll check out a library called Shoyu that aims to make setting up UITableViews a breeze. Let's dive in:
Shoyu works as a sort of DSL. It adds a source property on to UITableView, then gives as a few functions like createSection and createRow to fill the table view with content. Let's see what it takes to add a section and a row:
In Bite #162, we took our first look at RxSwift, and how it can transform how we write code. We learned how Observables can simplify our code, and mentioned creating our own Observables briefly, but today we'll dive a bit deeper. Let's begin:
Creating our own Observables is how we can bring code that wasn't built with reactive principals in mind, into the RxSwift world. It's helpful to think of Observables as merely a slightly different way to organize code we were already writing.
Let's look at a simple asynchronous function call:
One last note: We've been talking about all of this in terms of RxSwift, but other reactive or functional libraries might have different names for their streams of values. Even if we're using something other than RxSwift, the underlying concepts will usually remain largely the same. We can still use the techniques here to lift the code out of the conventional world and into the reactive one, regardless of which library we're using.
We first learned about reactive-style programming in Bite #127, with ReactiveCocoa. (Give that Bite a quick look first if you're new to the topic).
Today we'll check out an alternative solution called RxSwift. It just hit 2.0, so now is a great time to take a look. Let's dive in.
Before we begin it's important to note that there are tons of greatlibraries available for writing this style of code. We can enjoy the benefits of functional and reactive programming with any of them.
RxSwift is another take on reactive/functional programming. Similar to ReactiveCocoa, RxSwift works with streams of values called Observables. We can subscribe to them, transform them, bind user interface elements to them, and create them.
Additionally, RxSwift ships with RxCocoa, a framework that provides extensions for UIKit that bring it into RxSwift's reactive world.
RxSwift can transform how we write code in all the layers of our apps. With tight UIKit integration from RxCocoa, we can now forget about delegates, notifications, KVO, etc and instead use one simple interface to think about how data flows through our app.
It's quite common to need to display short status messages or notifications inside our apps. Today we'll look at a library called Whisper that is absolutely packed with features that make displaying these types of messages both simple and beautiful. Let's dive in.
Whisper provides 3 different types of display styles. All allow for extensive customization including colors, fonts, etc.
First up is "Whispering" a Message:
letmessage=Message(title:"🚨 Imperial Troops Have Entered the Base!",color:.redColor())Whisper(message,to:navigationController,action:.Present)Silent(navigationController,after:3)
We call the Silentfunction right after we present the Whisper, this will dismiss the message after 3 seconds.
Next, let's try "Shouting" an Announcement.
This one's great for longer text, and looks great with an image. We can also pass in an closure to run when the user taps the banner.
letannouncement=Announcement(title:"Han Solo Says:",subtitle:"Who's scruffy lookin'?!",image:UIImage(named:"avatar"))Shout(announcement,to:self)
Lastly, we can "Whistle" a Murmur to display a subtle, classy message in the status bar:
letmurmur=Murmur(title:"The last transport... is away! 🚀",backgroundColor:murmurColor)Whistle(murmur)
Today we're looking at another great library from Hyper called Pages. It's essentially a convenience wrapper on top of UIPageViewController (previously covered in Bite #55). It makes working with paged view controllers a bit more friendly. Let's begin:
We'll start by composing a PagesController. This will be the view controller that contains the other view controllers:
We use a few properties to configure the look and feel and behavior we want, then we can use the view controller like normal.
Pages has some awesome convenience functions for programmatically scrolling to pages:
pagesVC.goTo(1)// go to page at index 1pagesVC.next()// go forward 1 pagepagesVC.previous()// go back 1 page
We can also easily add view controller pages on the fly:
pagesVC.add([resistanceBasesVC,jediTemplesVC])
Last but not least, Pages is quite configurable offering properties for customizing not only the look and feel, but also behaviors such as whether or not changing view controllers affects the title shown in navigation bar of the navigation controller containing the pages.
More info about Pages can be found at git.io/pages
If we're not setting an expiration time, we can use this shorthand:
cache["a-unique-cache-key"]="Hello"
We can retrieve values from the cache in a similar way:
letcachedResponse=cache["photos.all"]
One of the handiest features of AwesomeCache is how it can help cache API responses (or any async code really). Here's how it works:
cache.setObjectForKey("photos.all",cacheBlock:{success,failureinself.requestLatestPhotos({responseinsuccess(response,.Seconds(300))},failure:{errorinfailure(error)})},completion:{object,isLoadedFromCache,errorinifletobject=object{// object is now cached, and ready to use}})
If a value already exists in the cache for the given key, the completion** closure** is called right away. Otherwise, the cacheBlockclosure is called. Inside the cacheBlock we call our custom own API code, then tell AwesomeCache if we succeeded or failed.
Way back in Bite #93 we talked about creating a "router" for Alamofire that would generate URL requests from enum cases.
In most apps we'll likely end up with some version of that code to help communicate with the API for that particular app. It'd be great if there was some sort of "standard" way to write this code each time.
Today we'll look at a library called Moya that aims to provide this. Let's dive in. 🏊
Moya is a network abstraction layer that encapsulates calling Alamofire functions. It helps us avoid creating messy custom network abstractions in every project.
To use Moya in an app, we'll need an enum with a case representing each API endpoint we want to hit:
enumFirstOrder{caseAllTroops;caseTroop(String)}
Then we'll extend it to conform to Moya'sTargetTypeprotocol:
This is just a taste, Moya has a ton to offer including some fantastic enforcement of good testing practices.
In conforming to the TargetTypeprotocol, we're actually required to provde sampleData. We can also customize things further by providing an endpointClosure when creating our provider.
Style is important when writing code. Following conventions and guidelines helps tell the story of our code to our team and our future-selves. Today we'll learn about SwiftLint, a tool from Realm that can help us enforce Swift conventions and best practices. Let's get started.
SwiftLint provides a downloadable install package on its Github page, but here we'll install using Homebrew. We'll run:
brew install swiftlint
Now, we can run swiftlint rules at the command line to see all the different convetions and standards that will be enforced. We can also run swiftlint lint in the root directory of our project to see what rules we're breaking right in the terminal.
Our next step is to add SwiftLint as a build phase to our project.
We'll head over to our project's settings, then to the Build Phases tab. We'll click the + button to add a** new “Run Script” phase**. We'll name it “SwiftLint” and give it the follow script content:
if which swiftlint > /dev/null; then
swiftlint
else
echo “Download SwiftLint: https://github.com/realm/SwiftLint"
fi
Now when we build our project, SwiftLint will let us know via regular Errors and Warnings in Xcode when there's something to fix.
We can configure how SwiftLint behaves in complete detail by creating a new file called .swiftlint.yml and putting it in the root directory of our project. We can fill out this file to customize (for example) which conventions are enforced:
disabled_rules:-colon-control_statement
We can disable rules “in-line” in our code with special comments:
// swiftlint:disable colonletnoWarning:String=""// No warning about colon placement// swiftlint:enable colonletyesWarning:String=""// Warning generated
Finally, SwiftLint can correct some violations (trailing_newline, trailing_semicolon, etc.). Just run swiftlint autocorrect.