Apple platforms make heavy use of "reverse-DNS" identifiers.
They appear everywhere from our Application's Identifier all the way down to the Dispatch Queues we create in our code.
These identifiers are somewhat opaque in a conceptual sense, and have been around in programming for many years.
We create or set them once, and sometimes reference them in our code, but the main idea is that they're a unique, human-readable way to identify some unique "thing". Their nature also offers the benefit of being unlikely to "collide" with one another.
The first thing we'll want to define is some kind of top level prefix to put on all our identifiers.
Fun Fact:Historically these identifiers were literally reversed-DNS. That is to say, to form one in code we might take our website's domain name, reverse it, then append our app's name (for example). These days, on Apple platforms, this isn't really a "thing" anymore. Now, we're usually given a string property or text field where we can supply just about any value we want.
This means that the common TLD's on the internet are also commonly used here. A company may begin all of their identifiers with com., while a non-profit may start theirs with org..
Next, we'll want to think of some way to group all of our identifiers, across all of our apps.
Often this is a domain-style version of our company or organization's name, or perhaps simply our own name, all lowercased, with no punctuation.
Pro Tip:Identifiers can contain numbers, letters, hyphens (-), and dot (.) characters. Hi! Bundle ID can have hyphens, not underscores. They're also case-sensitive. (Thanks to reader @pointum for the tips here!)
This gives us something like com.littlebites.
Great. Now we can apply this wherever we need to identify things.
Here are a few examples:
Applications and Extensions
com.littlebites.reader - An identifier for an imaginary app to read Bites. We might be tempted to just use com.littlebites.app or something generic, but this way we leave ourselves open to the possibility of making other apps in the future.
In-App Purchase Identifiers
With these, we want to think about the complete set of In-App Purchases we plan on offering and create groups within them if possible.
com.littlebites.reader.subscriptions.monthly, com.littlebites.reader.subscriptions.yearly - Here we have an imaginary subscription plan In-App Purchase.
We know we want to offer other types of In-App Purchases in the future, so we've "grouped" all subscription purchases under a subscriptions. segment. Then we're able to add a shorter phrase denoting the type as the final segment.
App Groups help us share data between our apps or our app's extensions.
group.com.littlebites.cache, group.com.littlebites.user - Apple encourages to begin these with the group.. This way we know right away what we're dealing with. Here we've created two app groups, one to share less-important cache data (images, etc.) between apps while we sort of "wall-off" user data into it's own group.
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!