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.
Today we'll tackle a common question from those beginning to learn iOS development:
"UICollectionView and UITableView seem to overlap in terms of functionality in many ways. When should I use one or the other?"
The practical answer is actually a common idiom when building software: What's the simplest possible solution?
Let's look at the basics of table and collection views, then we'll circle back and learn how we apply that answer to our question.
UITableViews are all about vertically scrolling lists of content. They expect us to use them to display rows of content inside sections. We get a ton of conventional "iOS-native" feeling functionality for free such as group-style table views that look like iOS's settings app or system-standard accessory views.
In short: UITableViews give us a lot for free, but they expect us to think in their terms.
UICollectionViews are infinitely more customizable than Table Views. We can completely customize a collection view'slayout, even animate between layouts.
UICollectionViews don't provide anywhere near as much functionality out of the box, but they offer an incredible amount of power and capability.
In many ways, Collection Views are the more-capable big sister to Table Views. They aren't constrained by "rows" or "accessory views". They are great for simple grids, all the way up to incrediblycomplexlayouts.
Back to our original answer: What's the simplest possible solution?
We can now answer our question by rephrasing it: Given what I'd like to build, will a UITableView or a UICollectionView help me get there faster, with less (or at least simpler) code?
Building a Settings screen or vertical list of social media posts? A UITableView is probably the way to go.
Building a photo gallery, or perhaps a screen that needs to scroll content horizontally? We'll need a UICollectionView for those.
In either case, if we ever start to feel like we're fighting against UIKit, that's a good time to step back and re-evaluate.
A huge continued thanks Buddybuild for sponsoring this week's Bites!
Buddybuild is the simplest and fastest way to automate our iOS continuous integration and continuous deployment workflows (and much, much more). Let's take a look:
Buddybuild ties together and automates: building, testing, deployingandgathering feedback for our apps. Let's try it.
We'll sign in with Github, and Buddybuild will ask us to choose the repository that contains the app we want to use with Buddybuild.
Next, Buddybuild will pull down our code and look for apps and schemes in our repo, and begin to build our app. We don't have to configure anything or write any scripts, Buddybuild takes care of everything automatically. 🤖
Buddybuildwill now build and run our tests each time we push to Github, and we still haven't configured a thing by hand, neat!
Let's continue by importing our certificates and profiles. Buddybuild provides a simple way to do this. We just need to copy/paste and run the command they provide, and all the right files will be securely uploaded. 🔒
Next, we can invite testers, and they can register on their devices. We can even connect our iTunes Connect accounts so we never have to manage new devices, UDIDs, profiles or signing identities again. New testers and their devices will be automatically provisioned on our behalf!
Now, we can now instantly deploy to testers on every build, every night, or on demand.
Testers can provide feedback by taking a screenshot. Buddybuild will kick in and show them a slick custom UI for graphically annotating and sending feedback notes. We'll also get reports of any crashes with the exact line they occurred on.
Last but not least, Buddybuild integrates with tons of services like GitHub, BitBucket, GitLab, Slack, JIRA, Pivotal Tracker, Slack, HipChat, and more.
Buddybuild is an incredible tool that's absolutely packed with great features, try it right now with your own apps at buddybuild.com!
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'll begin checking out some syntactic niceties available in Swift. These are tricks that can help make our code more readable, and can often make it easier to reason about. Let's get started:
Omitting Types
The Swift type system is pretty great. As long as the compiler can infer the type, we can simply leave it out when calling static functions:
This trick also works just as well for static properties:
letcell=ThemeTableViewCell(theme:.currentTheme)
Shorthand Argument Names
We can use a $x syntax to reference closure arguments:
spaceships.sort{$0.name>$1.name}
Trailing Closures
Many functions accept a closure as their last parameter:
funcchangeTheme(theme:Theme,completion:()->())
In these cases, we can call them with this shorter syntax:
Theme.changeTheme(dayMode){/* */}
Nil coalescing
The ??operator offers us a way to express a sort of "fallback" relationship between two statements. When the first statement is nil, it "falls back" to the second.
letcellTitle=trooper.nickname??trooper.troopID
At a technical level this returns either an unwrapped optional, or the value on the right, which can't be an optional.
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.
A huge continued thanks Buddybuild for sponsoring this week's Bites!
Buddybuild is the simplest and fastest way to automate our iOS continuous integration and continuous deployment workflows (and much, much more). Let's take a look:
Buddybuild ties together and automates: building, testing, deployingandgathering feedback for our apps. Let's try it.
We'll sign in with Github, and Buddybuild will ask us to choose the repository that contains the app we want to use with Buddybuild.
Next, Buddybuild will pull down our code and look for apps and schemes in our repo, and begin to build our app. We don't have to configure anything or write any scripts, Buddybuild takes care of everything automatically. 🤖
Buddybuildwill now build and run our tests each time we push to Github, and we still haven't configured a thing by hand, neat!
Let's continue by importing our certificates and profiles. Buddybuild provides a simple way to do this. We just need to copy/paste and run the command they provide, and all the right files will be securely uploaded. 🔒
Next, we can invite testers, and they can register on their devices. We can even connect our iTunes Connect accounts so we never have to manage new devices, UDIDs, profiles or signing identities again. New testers and their devices will be automatically provisioned on our behalf!
Now, we can now instantly deploy to testers on every build, every night, or on demand.
Testers can provide feedback by taking a screenshot. Buddybuild will kick in and show them a slick custom UI for graphically annotating and sending feedback notes. We'll also get reports of any crashes with the exact line they occurred on.
Last but not least, Buddybuild integrates with tons of services like GitHub, BitBucket, GitLab, Slack, JIRA, Pivotal Tracker, Slack, HipChat, and more.
Buddybuild is an incredible tool that's absolutely packed with great features, try it right now with your own apps at buddybuild.com!