Weekly Sponsor: Buddybuild 🤖🛠

A huge thanks to Buddybuild for returning to sponsor 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, deploying and gathering 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.

Buddybuild will now build and run our tests each time we push to Github, and we still haven't configured a thing by hand, neat!

Buddybuild's email and Slack based deployment service can deploy instantly on every build, every night, or anytime we'd like at the push of a button!

We won't need to wait for Apple to process or review anything before deploying to testers, either.

We can even connect our iTunes Connect accounts to Buddybuuild 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!

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.

Buddybuild has fantastic crash reporting as well! It can highlight the exact line of code that caused each crash, tell us which users were affected, and much more!

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!

Topics

#230: More Fun with fastlane Actions 🤖🎉

Topics

It's another fastlane Friday! In Bite #140 we began looking at fastlane's actions.

We learned how to add actions to our Fastfile and how they can help truly unlock the full power of fastlane. Today we'll check out a few more actions and look at why we might want to use them. Let's get started!

First up is clear_derived_data. This action does what it says on the tin. (We learned about the DerivedData directory and why clearing it can workaround issues in Xcode in Bite #208). Super handy!

Next, it's ensure_xcode_version. This is great when working on a team, (or for our own sanity) to make sure everyone is on the same, known version of Xcode. We can use it like this:

ensure_xcode_version(version: "7.2")

One action that can be quite versatile is update_info_plist. We can specify a .plist path, (perhaps our app's "main" Info.plist), and modify just about anything inside, here's an example for changing the name depending on how we're deploying:

update_info_plist(
  plist_path: "path/to/Info.plist",
  display_name: "Spaceships-Beta"
)

Finally, let's look at prompt. This action lets us collect data from the user, and use it within our lanes.

Let's try using it to allow us to enter a changelog each time we upload a build to TestFlight with Pilot:

changelog = prompt(
  text: "Changelog: ",
  multi_line_end_keyword: "END"
)

pilot(changelog: changelog)

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.

Last week we in Bite #223 and #224 we covered an example "Standard Setup" for new apps. Since those Bites were published, readers have been asking many questions, requesting more information about how all these things fit together.

Today we'll answer one of those questions in more depth: Authentication. Let's dive in.

Who? Again it's me, Jake Marsh! I write this thing.

Like we covered before, the heart of our HTTP "stack" is built around the Moya framework (Bite #150). One of the neat parts about Moya is how it lets us customize how it works. One way is through an "request closure". As the name implies, this closure's job is to create the actual NSURLRequest that will be sent.

Here's an example implementation. Inside we can set HTTP headers, and modify the URL request in any way we want. This makes adding authentication super simple.
Here we set a "Bearer" token as the Authorization header.

let requestClosure = { (endpoint: RxMoya.Endpoint<StewardAPI>, done: NSURLRequest -> Void) in
  let request = endpoint.urlRequest.mutableCopy() as! NSMutableURLRequest

  if let token = SharedKeycard.token {
    request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
  }

  done(request)
}

The SharedKeycard is simply a singleton that holds the auth token:

func lookForTokenInKeychain() -> String? { /* not important */ }

public class Keycard : NSObject {
  public var token: String? = nil

  public override init() {
    super.init()    
    token = lookForTokenInKeychain()
  }
}

public let SharedKeycard = Keycard()

Finally, we simply pass in our custom request closure when creating our Moya "provider":

let StewardAPI = RxMoyaProvider<StewardAPI>(
  requestClosure: requestClosure
)

A quick shout out to reader Justin Williams who wrote in asking for more information about how authentication fits into this puzzle.

Have any questions about how all this fits together? Want to see some other developer's example setup? Don't be shy! hello@littlebitesofcocoa.com. Any and all comments/questions are welcome!

Markdown is awesome. For those not familiar, read up on why Markdown is awesome in this fantastic post by Brett Terpstra.

When it comes to using Markdown on iOS and OS X, there's tons of options. Today we'll look at one popular solution from Simon Fairbairn called SwiftyMarkdown. It helps us translate Swift Strings containing Markdown into NSAttributedStrings. Let's take a look.

let md = SwiftyMarkdown(string: "# SwiftyMarkdown\nConverts *Markdown* files and strings into NSAttributedString")
md.attributedString()

It's that simple! SwiftyMarkdown will use some defaults for fonts, colors, etc. Customizing those is quite simple:

md.body.fontName = "AvenirNextCondensed-Medium"

md.h1.color = UIColor.redColor()
md.h1.fontName = "AvenirNextCondensed-Bold"

md.italic.color = UIColor.blueColor()

We can supply custom colors and fonts for each different tag, SwiftyMarkdown even supports inline links, neat!

More info about SwiftyMarkdown can be found at git.io/swiftymarkdown

Topics

#226: BetterSegmentedControl 🛠

Topics

UISegmentedControl is great, but it's not animated, and has limits to how much you can customize it. UISwitch is great too, it is animated, but it's limited to a yes/no choice, and customization again can be quite tricky. Today we'll look at a library from George Marmaridis called BetterSegmentedControl that aims to helps us here.

Let's try it out in a navigation bar:

let c = BetterSegmentedControl(titles: ["Lights On", "Lights Off"])

c.frame = CGRect(x: 35.0, y: 40.0, width: 200.0, height: 30.0)
c.cornerRadius = 3.0
c.titleFont = UIFont(name: "Avenir", size: 13.0)
c.backgroundColor = .darkGrayColor()
c.titleColor = .lightGrayColor()
c.selectedTitleColor = .whiteColor()
c.bouncesOnChange = false

navigationItem.titleView = c

Awesome! More info about BetterSegmentedControl can be found at git.io/bsc

Weekly Sponsor: Hired 🏢

A continued huge thanks to the folks at Hired.com for sponsoring this week's bites. Finding a good job can be a daunting task. Today we'll take a look at how we can use Hired.com to save our sanity, and find a great job in the process. Let's dive in.

We'll start by entering our email and answering some basic questions:

That's it. Hired will then work with over 3,000 pre-screened companies (both big and small) in 13 major metro areas (North America & Europe) to try find us a great job. Software engineers and designers on Hired can get 5+ job offers in a single week. Each offer will include salary and equity details upfront.

We can view interview requests and accept or reject them before talking to any company. If we get a job through Hired, they'll give us a $1,000 "thank you" bonus!

Even better, if we sign up through this link: littlebitesofcocoa.com/hired, they'll double our bonus to $2,000!

More Reasons to Use Hired

  • Full time or contract opportunities are available
  • View offers and accept or reject them without talking to anyone.
  • Completely FREE + no obligations ever.

Hired is the real deal. They have solved some very real problems and fixed this historically messy process. Looking for a job? Don't waste time, sign up for Hired today so you can get back to work!

It's another fastlane Friday!

Bitcode is awesome. It lets our users download much smaller versions of our apps, containing only the bits they need to run it on their devices.

Unfortunately, it creates a few challenges for using third-party crash-reporting services.

Not to worry, fastlane has our backs. Today we'll look at a new built-in fastlane action (Bite #140) that can automate all of this.

The issue is that Crashlytics (or any service analyzing our crash logs) needs our dSYM files to convert raw crash logs to ones that containing line numbers and function names and all the other useful information we'll need to actually fix the bug causing the crash.

We'll begin by making sure we have the latest version of fastlane. (A quick gem update fastlane will do the trick).

Then, we'll add a new lane:

lane :refresh_dsyms do
  download_dsyms                  # Download dSYM files from iTunes Connect
  upload_symbols_to_crashlytics   # Upload them to Crashlytics
  clean_build_artifacts           # Delete the local dSYM files
end

Now we can just run fastlane refresh_dsyms at anytime and everything will be synced up to Crashlytics. Apple seems to recompile our apps at will, and whenever their systems need.

For these reasons, it's probably best to toss this command into a CI server or some other system that will run it once or twice a day.

Support is already in for Crashlytics, Sentry and HockeyApp.

Everything is open source, so we can add support for our own service if needed.

More info about all of this can be found in fastlane's docs, found here.

Topics

#224: Standard Architecture 🏣

Topics

After Bite #223's "Standard Setup", many readers asked for more detail about the actual architecture involved. Today we'll take a look at an example request, model, and a bit of how data "flows" from the web in to views in the app.

Who? Again it's me, Jake Marsh! I write this thing. Writing in the first person feels quite strange at this point.

Let's start with a Moya setup for one endpoint. I love this convention. It standardizes "what goes where" for HTTP verbs, params, etc. I'm never trying invent, just define it, and move on.

public enum StewardAPI : RxMoya.TargetType {
  case Checkins(start: Int)
}

extension StewardAPI {
  public var method: RxMoya.Method { return .GET }
}

extension StewardAPI {
  public var path: String {
    switch self {
      case .Checkins: return "/checkins"
    }
  }
}

extension StewardAPI {
  public var parameters: [String: AnyObject]? {
    switch self {
    case .Checkins(let start):
      return ["start": start, "maxResults" : 50]

Next, to actually use this API, I have an small extension I add with a global function called fetch. This returns an Observable<Decodable>.

This "calls" the HTTP endpoint, and expects some JSON that can be transformed into one of my defined Decodable types:

fetch(.Checkins(start: start), type: [Checkin].self).subscribe { event in
  switch event {
    case .Next(let checkins): print(checkins)
    case .Error(let error): print(error)
    default: break
  }
}.addDisposableTo(rx_disposeBag)

Normally, I wouldn't switch here, I'd map this into a type I use called a "Presenter" (people also call it a "Decorator" or a "View Model").

It is immutable and contains boring business logic. This is what actually gets handed to my view controllers and views:

fetch(.Checkins(start: start), type: [Checkin].self)
  .map { ListItemPresenter(item: $0) }

Please send any questions about any of this to hello@littlebitesofcocoa.com. Happy to go in to further detail if there is interest here.

Topics

#223: Standard Setup 🛠💭

Topics

Everyone does their iOS development a little bit differently. Today we'll take a look at one developer's example "default" depdency setup and describe the choices within. Let's get started.

Who? It's me, Jake Marsh! I write this thing.

I use Moya for all of my network requests. It's a fantastic tool that allows you to abstract your requests away using Swift enum cases. Associated values are used to pass in params. Love the convention this offers, feels like Swift on Rails. Moya was covered in Bite #150.

For my JSON decoding I like Decodable at the moment, it again offers a nice convention of "define a type, define its deserialization below". I also like how it uses native Swift errors combined with Optionals to gracefully fallback when deserializing. Decodable was covered in Bite #86.

I use HanekeSwift for image downloading/caching, it's fast and lightweight. I use RxSwift (Bite #162) for reactive things. I prefer its API over Reactive Cocoa (Bite #127). Both are great. Use what you like.

Who's standard setup do you want to see? Send it in: hello@littlebitesofcocoa.com

Page 13 of 38