Active Filters: Techniques

Topics

#317: Crafting Great Reverse-DNS Identifiers 🆔

Topics

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. Bundle IDs 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

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 keep user data in it's own group.

That's all for now, have a tip or some advice about these identifiers that to share? Send it to hello@littlebitesofcocoa.com.

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.

public protocol ColorConvertible {
  var colorValue: UIColor { get }
}

Now, let's try this new Protocol out by creating an enum that represents the different colors we use in our app:

public enum SpaceshipsColors : String {
  case white = "FFFFFF"
  case red = "E2574C"
  case gold = "EFC75E"
  case darkTeal = "314D5B"
  case lightTeal = "3CB39E"
  case spaceGray = "233640"
  case aluminum = "6A838D"
  case black = "000000"
}

Looking pretty good, now let's extend our enum to conform to ColorConvertible.

We're using Hue here (Bite #195) to convert our hex color code Strings into UIColors.

extension SpaceshipsColors : ColorConvertible {
  public var colorValue: UIColor {
    return UIColor.hex(rawValue)
  }
}

Now we can reference colors in our code by name, and get full autocompletion when typing them. Using our new Protocol is super simple, let's make a quick UIView subclass to show it off:

class SpaceshipNameHeaderView : UIView {
  var nameLabel: UILabel
  var nameTextColor: ColorConvertible? {
    didSet {
      nameLabel.textColor = nameTextColor?.colorValue
    }
  }
}

It'd be nice if we could still supply "one-off" UIColors as well. No problem, let's simply conform UIColor itself to be ColorConvertible. We can just return self:

extension UIColor : ColorConvertible {
  public var colorValue: UIColor {
    return self
  }
}

Finally, we can use it like this:

let hv = 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.

Topics

#232: Practical Protocols 🔩

Topics

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:

struct LayoutSettings {
  let direction: FlexDirection
  let justification: Justification
  let alignmentSelf: Alignment
  let alignmentChildren: Alignment

  /// ...etc
}

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.

enum CannedLayout {
  case FillParent
  case SizeToFit
  case Absolute(point: CGPoint)
  case Relative(closure: (parentFrame: CGRect) -> CGSize)
}

Lovely, this will be handy. How are we going to wire all this together though? Simple, we'll make a Protocol that's only responsibility is to convert itself to a LayoutSettings.

protocol LayoutSettingsConvertible {
  func layoutSettings() -> LayoutSettings
}

LayoutSettings can adopt this extremely simply:

extension LayoutSettings : LayoutSettingsConvertible {
  func layoutSettings() -> LayoutSettings { return self }
}

Whew! That was tough.

Making our CannedLayout Enum adopt our new Protocol is a bit more involved, but really just means switch-ing over self and return the proper combination of settings for each case.

extension CannedLayout : LayoutSettingsConvertible {
  func layoutSettings() -> LayoutSettings {
    switch self {
      case .FillParent: return LayoutSettings(direction: .Vertical, justification: .Start, alignmentSelf: .Stretch, alignmentChildren: .Start)
      /// ...etc
    }
  }
}

All that's left is to use this new protocol somewhere. Let's wire this up to UIView to make it useful:

extension UIView {
  func layout(settings: LayoutSettingsConvertible) {
    /// configure the view for the new settings here
  }
}

Neat! Now, we can use configure views with one of our canned layouts:

let v = UIView(frame: .zero)
v.layout(CannedLayout.FillParent)

But we can also easily configure them the "long way" using a full LayoutSettings object directly:

let v = UIView(frame: .zero)
v.layout(LayoutSettings(direction: .Vertical, justification: .Start, alignmentSelf: .Stretch, alignmentChildren: .Start))

Now that we have this simple protocol, we can make other helper types like this:

struct Row : LayoutSettingsConvertible {
  func layoutSettings() -> LayoutSettings {
    return LayoutSettings(direction: .Horizontal, justification: .Start, alignmentSelf: .Stretch, alignmentChildren: .Start)
  }
}

That's just the basics when it comes to Protocols. They have much more to offer. More on this topic soon!

Topics

#229: Dependency or Not to Be? 👑

Topics

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?

As the always-great John Siracusa puts it:

"It's all about finding where to draw that line."

Another reasonable guideline might be to "use libraries or frameworks that are used by lots of other people."

A networking library such as  Alamofire (Bite #93, Bite #94) is a good example of a third-party solution that might be called ubiquitous.

This is great. We want code that's been used on the App Store, and on millions of customer's devices. Code in which most of the bugs have been discovered and squashed long ago.

Once we've decided to integrate a piece of third-party code, we have to think about how.

We've covered Cocoapods (#60) and Carthage (#11), or we could just add the git repo as a submodule, or maybe drag in a static library, whew!

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.

let CharacterLimit = 182

class ViewController: UIViewController, UITextViewDelegate {
  let textView = UITextView(frame: CGRect.zero)

  override func viewDidLoad() {
    super.viewDidLoad()
    textView.delegate = self
  }

  func textView(textView: UITextView, shouldChangeTextInRange range: NSRange, replacementText text: String) -> Bool {
    let currentText = textView.text as NSString
    let updatedText = currentText.stringByReplacingCharactersInRange(range, withString: text)

    return updatedText.characters.count <= CharacterLimit
  }
}

We'll start by defining our limit as a global variable, making it easy to change later.

Next, we'll add a new UITextView to our view controller, and set ourselves as its delegate. (The text view's layout is handled off camera).

Finally, in the shouldChangeTextInRange delegate function, we'll perform our checks.

We'll apply the proposed changes, and check if the resulting length is within our limits. The Bool we return describes if the change should be allowed.

And, we're done!

Update: March 28th, 6:15 PM

A previous version of this Bite contained an inferior implementation of the shouldChangeTextInRange: function. The one shown here comes from reader Filip Radelic. Thanks Filip!

The real world doesn't have straight edges. Things are rounded. Our UIViews can be too! Most of us know about the cornerRadius property on CALayer. We can set a value, then see all the corners round themselves to that value:

contentView.layer.cornerRadius = 8

Nice, but what if we only want to round some of the corners though?

let cornerRadius: CGFloat = 8
let maskLayer = CAShapeLayer()

maskLayer.path = UIBezierPath(
  roundedRect: view.bounds,
  byRoundingCorners: [.BottomLeft, .BottomRight],
  cornerRadii: CGSize(width: cornerRadius, height: cornerRadius)
).CGPath

view.layer.mask = maskLayer

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 byRoundingCorners parameter, 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.

Topics

#209: Swift Enums Cookbook 🍳📖

Topics

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.

public enum Color : String {
  case Green = "28A860"
  case Blue = "3B87D6"
}

extension Color : UIColorConvertible {
  public func asColor() -> UIColor {
    return UIColor(hexString: rawValue)
  }
}

This same technique is great for images too!

Enums with Closures

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!

public enum LayoutStrategy {
  case SizeToFit
  case Fixed(width: CGFloat, height: CGFloat)
  case Relative(closure: ((parentSize: CGSize) -> CGSize))
}

Enums can describe so many types of state and data. Know of a cool use of them? Send it to hello@littlebitesofcocoa.com and it might be featured here!

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 fault or 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!

Today we'll check out a few different tips and tricks for working with UITableView. Let's get started.

Adding a Refresh Control

We can add pull-to-refresh capabilities to any UITableView easily:

let refreshControl = UIRefreshControl()
refreshControl.addTarget(
  self, 
  action: Selector("refreshed:"), 
  forControlEvents: .ValueChanged
)

tableView.addSubview(refreshControl)

Then we just implement that refreshed: function like this:

func refreshed(sender: UIRefreshControl) {
  refreshTheThing {
    sender.endRefreshing()
  }
}

Reload Visible Cells

Here's a quick (almost) one-liner to reload only the visible cells in the table view:

tableView.reloadRowsAtIndexPaths(
  tableView.indexPathsForVisibleRows ?? [],
  withRowAnimation: .Automatic
)

Accessing Rects

It's often handy to grab a reference to the actual frame of a cell, section, header or footer. We can do this easily with:

tableView.rectForRowAtIndexPath(
  selectedIndexPath
)

Animate Height Changes

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:

tableView.beginUpdates()
tableView.endUpdates()

UIKit will interpolate & animate the changes, neat!

Topics

#205: Swift guard Basics ⚔

Topics

The guard statement was added to Swift in its 2.0 release. It can dramatically increase the readability and clarity of the conditional checks in our code.

It supports Swift's inline optional unwrapping as well as pattern matching. Let's try it out!

We can think of the guard statement like a sort of inverted if statement. We essentially say: "this thing must be true otherwise we're bailing out!". Here's a fairly full-featured example:

func launch() {
  guard let fc = fuelCell where fc.level > 0.5 else {
    showLowFuelWarning()
    return
  }

  startEngines()
  // ...etc
}

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 guard statement are now available, unwrapped, and in-scope for the rest of our function (anything below the guard statement). 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 guard statements 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!

Page 1 of 4