Swift Protocols are great. They allow us to express the idea of expecting a set of functionality, without needing to expect a specific concrete type. Today we'll look at creating a Protocol to make working with colors in our apps a bit more flexible. Let's dive in. 🏊
We'll start by creating a new Protocol, giving it a conventional name, and require just one read-only property from our conforming types. This property will always return a UIColor.
lethv=SpaceshipNameHeaderView()// works!hv.nameTextColor=UIColor.black// works just as well!hv.nameTextColor=SpaceshipsColors.aluminum
Swift Protocols can help us write code that is both expressive and quite flexible. They can take a while to get a handle on, but understanding them is a key step towards unlocking the full power of Swift.
Swift Protocols are awesome. Understanding how they can (or should) fit into our code can be tricky. Today we'll take a baby step into the world of protocols with a simple, but "real" example and try to illustrate the upsides. Let's get started.
We're going to be fancy and abstract away some of our layout code. So we'll create a little struct to hold some layout settings like this:
See? Fancy. This is great if we want to specify each individual combination each time, but it'd be nice if we could define some sort of "pre-canned" layouts that we could use by name. Sounds like a great job for a Swift Enum.
Dependencies are as old as the art of engineering itself. In terms of iOS and OS X development, they are often thought of in the form of Cocoapods or Carthage libraries, or really just any bit of code from elsewhere that we bring in and use in our app.
There's also implicit dependencies we work with such as Xcode itself, or the language we write in. Today we'll look at some of the questions involved with using dependencies and try to answer them. Let's begin.
First up: How do we know what bits to write ourselves, and what bits are good candidates for third-party solutions?
Typically, it's good practice to try to limit the number of dependencies in general. Rules always have exceptions, but the thinking goes "the less moving parts, the less that can break." A decent way of thinking, but surely we don't want to build a full computing unit from raw silicone every time we have an idea for a new feature, right?
Any of these are fine choices, it's really depends on the use case. Cocoapods is great, but requires use of an .xcworkspace file, and clean builds will rebuild all pods from scratch. Carthage doesn't have those issues, but can sometimes be at odds with the latest Xcode/Swift compiler changes.
The bottom line is: Carefully consider each option, and make the best choices you can.
Life needs limits. Whether its for usability reasons or maybe we're building the next Twitter, eventually we'll need to enforce a limit on how much content a user can enter into a UITextView. Let's take a look at how to do this.
The real world doesn't have straight edges. Things are rounded. Our UIViews can be too! Most of us know about the cornerRadiusproperty on CALayer. We can set a value, then see all the corners round themselves to that value:
Nice, but what if we only want to round some of the corners though?
For this, we'll need to get a bit more creative. We can use an often-overlooked type called UIRectCorner.
It lets us describe which corners we'd like to round. Then we'll use a UIBezierPath to create a "mask" layer that will only allow some of the content to "show through".
We pass in an option set for the byRoundingCornersparameter, listing out the corners we'd like rounded.
Success! Happy rounding!
Update: Shout-out to Reddit-reader /u/cuomo456 who reminds us to update/replace our layer masks anytime the view/layer we're masking's frame or bounds changes. If we don't, we could find ourselves scratching our heads why our rounded corners aren't working in a UITableViewCell, for example.
Swift Enums are super handy. With associated values, they can be used in some quite interesting ways. Today we'll check out a couple. These aren't working examples, but might spark some ideas. Let's take a look.
Enums with Extensions
Keeping colors in one place is a great way to ensure consistency in a project.
This one is a little crazy (but fun!). Imagine we had our own custom system for laying out views in our app. We could not only describe that system perfectly with enums, we could even add a little more dynamic spice to the mix by making one of the associated values of our enum a closure. Neat!
Those who have been an iOS, OS X, tvOS, or watchOS developer for any significant amount of time know that Xcode is an incredible tool for creating awesome apps. Until it isn't. Sometimes Xcode gets confused and needs our help. Today we'll look at one way we can clean up our environment and possibly get back to work.
There's a myriad of reasons why the following technique resolves many common Xcode issues. The important part is that it works. Sometimes. Maybe.
Anyways, there's this folder full of "invariants" and temporary files that Xcode shuffles things around in as we use it to build our app. It's called DerivedData and contains caches, compiler waste, build products, etc.
If we ever find ourselves wondering if a certain compiler error or unexpected Xcode behavior really is our faultor a bug in Xcode, we can try clearing out this folder before jumping on the Google train.
The "manual" quick/dirty way is to quit Xcode then run:
rm -rf ~/Library/Developer/Xcode/DerivedData
Then re-launch Xcode. Also keep those fingers crossed.
Alternatively, we could use an app to do all this for us. Watchdog is an app that lives in our menu bar and cleans up stale cache files in Xcode. Very cool!
One easy trick to make our app look a little nicer is to animate changes to the heights of the UITableViewCells in our UITableView. All we need to do is make whatever changes to our models we want to cause our cells to be a different height. (For example we might let the user tap a button to "expand" all comments in an app). Then we just call:
UIKit will interpolate & animate the changes, neat!
Here we're ensuring that we have a fuelCell using optional unwrapping, then using pattern matching to make sure that our (now unwrapped optional) fuel cell has enough fuel in it for us to launch. Right away we can start to see how much clarity this adds.
If either part of our condition fails, we use the else block to run some code then return from the function.
Also as a bonus, the variables we unwrapped in the guardstatement are now available, unwrapped, and in-scope for the rest of our function (anything below the guardstatement). Neat!
This allows the rest of our function to focus on the actual work being done, rather than checking for validity throughout. We can stack these up too, it's quite common to see multiple guardstatements at the top of a functions in Swift.
It's important to note that we aren't required to return at the end of a guard's else block. Imagine using guard in loops to continue, or in a function that throws to throw an error. Guard helps keep our code clean and readable. Happy guarding!
Today we'll continue looking at initialization in Swift with Designated Initializers. Let's get started.
Swift's initialization system is made up of what are essentially a set of rules. We'll step through them here. Let's start with a class.
The first rule of Designated Initializers is that we need one. All classes must define (or inherit) at least one.
Ok, we've added one designated and one convenience initializer. Not too shabby. Now, let's use this base class.
Uh oh, what's going on here? Turns out it's the next rule. We're writing the designated initializer for Peakachew. Designated initializers must call a designated initializer from their immediate superclass, not a convenience one like we tried to do here.
Well, we've fixed our first problem but we've hit the next rule. Designated initializers have to initialize all stored properties on their class before calling super.
Lastly, designated initializers must call their parent class's initializer before assigning a value to any properties they've inherited from it.
With that, we're done. We're now following all the rules: