A new Apple device! I’m required to have opinions. Here they are in a contrived fashion!

The Good

Great UI and aesthetics.

It’s faster.

Scrubbing through video is significantly improved. I almost don’t believe how much better it is.

Siri works as advertised. Accuracy, speed, and results are all great.

All the potential!

The Bad

Siri doesn’t index content stored in iTunes libraries on the network. So to get to all the DVDs and Blu-rays I’ve ripped I have to use the same UI as the old Apple TV.1

Siri doesn’t work with Music. Not for music libraries on the network, iTunes Match, or Apple Music. I really don’t understand this one, I’m hoping it comes soon.

Only one remote can be paired with it. This makes gaming tricky. Hoping this changes soon too.

The Ugly

Passwords. This has already been discussed at length everywhere. One thought I had would be that it’d be cool to enter passwords via Siri. Y’know, like that one scene in TNG:

(A Touch ID powered remote would probably be better)

App discoverability. It’s really bad, and I don’t know how they’re going to fix it. Top Charts was just added, which is better than nothing, but if someone wants to market their app anywhere online they have no way to provide a link to it! “Go to the app store, search for s-o-m-e-t-h-i…” is all kinds of awful.

Overall I like it. I have almost no reason to use the Roku now2.

  1. I do not have Plex, so I don’t know if that circumvents it. If it does, I may grab it asap.

  2. Other than the rare times there’s something on Amazon Video that isn’t anywhere else.

Getting the last photo taken is a convenient feature to have in your app when dealing with photo picking. Prior to iOS 8, you could get it by using the Assets Library Framework and then looping through the various groups to get the right photo.

In iOS 8, the Asset Library is still available, but Apple introduced a new framework to go along with Photos.app. It’s creatively called the Photos Framework and it makes certain things, such as querying for the recent image much easier. To further entice you to use it, the Assets Library is deprecated in iOS 9.

Here’s a function that’ll get the most recent photo with the Photos Framework:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import UIKit
import Photos

// I like to typealias my blocks, makes for easier reading
typealias ImageCallback = (UIImage? -> Void)

func fetchLastPhoto(resizeTo size: CGSize?, imageCallback: ImageCallback) {
    let fetchOptions = PHFetchOptions()
    fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]

//        fetchOptions.fetchLimit = 1 // Available in iOS 9

    if let fetchResult = PHAsset.fetchAssetsWithMediaType(.Image, options: fetchOptions) {
        if let asset = fetchResult.firstObject as? PHAsset {
            let manager = PHImageManager.defaultManager()
            let targetSize = size == nil ? CGSize(width: asset.pixelWidth, height: asset.pixelHeight) : size!
            manager.requestImageForAsset(asset,
                targetSize: targetSize,
                contentMode: .AspectFit,
                options: nil,
                resultHandler: { image, info in
                imageCallback(image)
            })
        } else {
            imageCallback(nil)
        }
    }
}

There’s a fair amount going on in there. First, we create a PHFetchOptions object which we can use to pass in additional information to filter the query. In this case we only need to sort by creationDate descending. iOS 9 introduces fetchLimit which could reduce a bit of overhead since we know we only need 1 image.

PHFetchOptions also has a predicate property that has a bunch of interesting capabilities, such as restricting the search to specific mediaSubtypes like PhotoHDR or PhotoPanorama. If you want all photos just ignore it; that’s determined later. Do note that “Photos does not support predicates created with the predicateWithBlock: method.”.

Once we have our options we are ready to query. The Photos Framework gives you access to 3 types of things that are stored in Photos.app: PHAsset, PHAssetCollection, and PHCollectionList. We only care about PHAsset right now. A PHAsset is a representation of the media stored on the device (photo or video). The other two are ways to group those assets in general.

Querying happens through class-level methods on the PHAsset class. There are a few to choose from, but we want fetchAssetsWithMediaType:options:. This returns a PHFetchResult which is kinda like an NSArray, but not exactly. In Swift it’s filled with optional AnyObjects. The first one should be the most recently created file so we cast it to a PHAsset.

Now comes the time to convert that PHAsset into what we really want: a UIImage. This is the responsibility of PHImageManager.

Most of the time when querying for images, we just want to resize the photo to fit into a UIImageView so Photos does most of the heavy lifting for you. Give it the size you want and a couple other options and off it will go. This happens asynchronously, so hand off the results to your callback block when it’s done. This’d be especially handy for generating a bunch of thumbnails.

Photos gives you quite a bit of flexability. We didn’t touch on how it helps you to edit assets, manage/query video, or observe changes to photos. I wasn’t aware of this framework until today, but it looks like a powerful one worth getting to know if you do anything with the images on your device.

Great Customer Service is not dead! When I was doing freelance/independent work I used Harvest’s Solo plan to track my time and invoice clients. It was affordable for a single person and wasn’t over-engineered. Since it was cheaper in the long run I paid for a yearly plan and renewed each time it came up. I would’ve recommended them to anyone in a similar position. Now I’ll recommend them even more heavily.

A few months ago I took a full-time position and just didn’t need the paid account any more. I couldn’t figure out how to downgrade though so I sent a request to their customer service. The page told me to expect a response within an hour. Within 5 minutes I got a response from Jennifer telling me that my plan had been downgraded to the free one, AND they refunded the prorated amount for the remainder of the year back to me.

Getting a refund wasn’t even on my radar. I was perfectly happy with keeping the paid plan and just not renewing it when the time came. What I love about this is that Harvest didn’t waffle around the issue for a long time only to end up doing the bare minimum to get the customer to go away. They were fast, proactive, and exceeded expectations.

That’s how you do customer service.

I’ve been noodling with an idea for a watchOS 2 app. It involves Connectivity – transferring files from the Watch to its paired iOS device. The WWDC sessions are very good, but I ran into a couple of snags with the framework. I had tweeted something that I thought was a bug, but on closer inspection of the docs it actually wasn’t. I tweeted another message stating as much, but it hasn’t gotten the notice that the original did.1 Anyways, here’s what I learned.

[ed: This is with iOS 9, watchOS 2, and Xcode 7, all in beta 2. Stuff might change.]

You can run both simulators at once

But it’s not obvious. First, run the watchOS simulator. In the scheme control, it’s the “[App Name] WatchKit App” scheme, which has both the iPhone and Apple Watch available (for example, “iPhone 6 + Apple Watch – 38mm”), and run it.

Xcode Scheme for iPhone6 and Watch

When it’s running, change the scheme (without stopping the simulator) to the normal iPhone scheme. In this case “[App Name] > iPhone 6”.

Xcode Scheme for iPhone6 and Watch

Now, run this scheme without compiling: hold down ⌘ and click the Run button. The iOS app will now be running in the simulator. Sometimes the watch app will close, but you can open it back up again.

The console and breakpoints may get a little squirrelly, so you probably won’t be able to depend on them so well.

WCSessionDelegate

Connectivity has one main delegate to implement: WCSessionDelegate. This can be implemented on both the iOS and watchOS apps, and different behavior will happen. For example, when you transfer a file, the receiver will need to implement session:didReceiveFile:. If you want to receive some kind of notification that the file was sent the sender needs to implement session:didFinishFileTransfer:error:.

This seemed counter-intuitive to me at first, but on further reflection, it makes sense. I do wish the docs more clearly illustrated this though.

You’re in the background

Don’t forget that your iOS app is not guaranteed to be running when these notifications come in. And they will come in on a background queue too, so don’t immediately try to update the UI without getting over to the main queue first.

Demo

I created a simple demo app to illustrate all of this. It’s up on GitHub. Take a look at the AppDelegate for the iOS app, the ExtensionDelegate and InterfaceController in the watchOS extension target. This demo was thrown together quickly without regard for best practices, blah blah blah.

  1. Such is the nature of Twitter, alas.

When you’re a solo developer, you can use Git in nearly any darn way you choose. No branching? Ok. Branch on everything? Sure. Want all your commit messages to consist of “stuff”? Knock yourself out. You might regret some of that in the long run, but it’s not hassling anyone else. But, as soon as you add another person into the mix, things will have to change.

One gigantic benefit from collaboration is having a second set of eyes look at your code. GitHub makes this easy if you follow a few steps.

[ed: This might be old hat for some of you, but I don’t know if I’ve ever read an entire guide for this, so I’m writing it all down. Please send me feedback, nicely, if there’s a problem.]

Create an Organization

This one is optional, but does help in a few ways. The Organization becomes the face of any projects underneath it. It also makes a few things a little easier with regard to deployments, issue tracking, and documentation.

Everyone Forks from the Organization

If the repository is called some-org/project-x, then each developer forks that to create swilliams/project-x, sally-developer/project-x, and so on. If a repository is private on the Organization, your forks will be private too, and won’t count against your own private project count.

Clone Your Fork

Now get your local copy.

git clone https://github.com/swilliams/project-x.git

Set up Remotes

Your fork on GitHub will automatically be your origin remote. Add a remote for the Organizations repository. By convention this is typically called upstream.

git remote add upstream https://github.com/some-org/project-x.git

Work in Branches

Working on a feature? Create a branch feature-abc. Fixing a bug? Create a branch issue-254-login-done-broke. Keep master clean.

git checkout -b feature-abc

Push Branches to Origin

Done with a feature or an issue? Push it back up to origin (your fork).

git push origin feature-abc (you can add a -u flag too to track the remote branch too)

Create a Pull Request

Why do we go to the hassle of creating all those branches? Because with branches, you can create multiple outstanding Pull Requests at once. If you did all your development in master, any additional commits you push up will be added to an open Pull Request, which can cause issues.

Multiple small Pull Requests are much easier to review. Would you rather review 3 files over 5 commits or 50 files and 75 commits?

Someone Else Reviews the Pull Request

Perhaps my favorite piece of functionality in GitHub is the Pull Request review process. Use it to annotate code and discuss. Merge it in if everything is good.

Rules for the Road

  1. Keep the master branch clean. That should be ready to go live if necessary. This means tests should be passing, everything compiles, nothing important should be broken, etc.
  2. Never commit directly to upstream. Upstream should only be updated through Pull Requests. Exception: pushing tags.
  3. Pull from upstream regularly. The more codebases diverge, the more likely a nasty merge problem will occur.
  4. Keep branches small. Just reiterating it again.
  5. There are exceptions to every rule. Use them intelligently.

At some point in your career in technology, you’re going to have to make some kind of presentation. This could be connecting your laptop to a projector, sharing your screen over some video conference, or just have people huddled around your desk. Since I care about you, I’m gonna share some advice about avoiding common pitfalls that might occur.

Turn off notifications. Say you’re right in the middle of explaining how beautifully designed your database is to an important client, and one of your friends sends you an email.

oh dear
Oh dear.

Your only options are to make an awkward joke, or ignore it completely. Hopefully Jane doesn’t send any follow-ups.

On OS X you can turn off notifications by clicking the button in the top right of the menu bar, scrolling down, and then turning “Do not disturb” on.

do not disturb

Hide everything you aren’t showing. Again, say you’re presenting something amazing to the top brass at Giant Health Conglomerate Inc. You need to switch from Keynote to a spreadsheet. You minimize Keynote and your Inbox is now in full view. What if there are some strategy emails from your CEO sitting in front of everyone? What if Jane from above has graphically described other things found in her vomit?

On OS X I like to move my presentation and ancillary windows to a separate space. One with a very plain wallpaper.

Use Chrome? Double check the default sites when you open a new tab. Nothing instills confidence with a prospective client when you open a new tab and the top 8 sites you visit are right there in the open for everyone to see. “Oh, I guess you really like Harry Potter/Star Trek slash fiction?”

An Incognito window is a quick way to hide all that.

Similarly, consider clearing your browser history. Maybe you need to open up a webpage you weren’t anticipating. Maybe you’re checking the redis documentation, but when you type red into the address bar, all the subreddits you’ve ever visited are immediately available. Maybe you just browse /r/aww and everyone is happy, or maybe you visit more… unconventional… ones.

Ultimately it’s up to you how many awkward situations you want to have with your co-workers and/or clients. Try to just think about what would happen if your mother/rabbi/priest/psychologist were watching.

What do you do with a codebase for a client project that ended years ago? Since it’s client work it shouldn’t be made public, but if you keep it “live” in GitHub your private repository count will creep up. Deleting the repository outright seems wrong; it’s not that unusual to have an old client cold call you with an update, and having that old codebase handy can save some headaches. The lazy way to fix this would be to just give GitHub more money to increase the limit. But I felt the itch to solve the problem with code.

Git itself is flexible. It’s trivial to clone a repository, put it in a safe place (or alternate service) and call it a day. But with GitHub, that doesn’t include Issues. There could be some solid ideas (or bugs) stored in Open Issues that should be preserved. GitHub has a great API to retrieve those, and I decided to create a simple Ruby script to make it a smooth process.

Take a look at GitHub Issue Exporter. It’s pretty basic right now — just downloads Issues into a bunch of JSON and will also let you import them back into a new project. The idea is that you clone the repository you want to archive, then export all the open issues, store it all in a safe place, then you can safely delete the repository and free up some space.

Sometimes ideas pop into my head, and sometimes I think they’re awesome. Most times though, they’d require way more time and effort to implement properly than I have available. Since I’ll never get around to making this myself, I’ll just share it here.

I was playing some Team Fortress 2 a little while ago and enjoying myself when I had the epiphany, you could do this in real life. Or at least something similar1.

Modern smartphones have very accurate GPS chips inside of them, such that things like geofencing are possible2. Why not use that technology to add an additional layer of gameplay on top of things like paintball or airsoft? What if you could turn what is typically an unstructured free-for-all into a real-life tactical Capture the Flag game? Here’s how I’d do it.

Setup

The Creator creates a new game and divvies up players into teams, and assigns a Commander to each team. The Commander has special privileges (defined below).

The Map

The Map
Some desert

Take the strip of desert that you play in. The Creator defines the boundaries for the game. If a Player strays beyond those boundaries for more than, say 10 seconds, they’re penalized.

Boundaries
Drag to rearrange

Next up is defining team based boundaries.

Team Boundaries

Each team’s Commander can setup their own map dependent points of interest: a base, the Flag, etc.

Gameplay

Red Team

The Commander can also see where each of their team members are currently located, and what directions they are going. The Commander can call out orders or instructions, set a rally point and draw directions on the map that will show up on Players devices.

For Capture the Flag, the Flag could be another kind of device like an iBeacon tied to an actual flag. When the Flag is moved it sets off alarms for its team. Moving the other team’s Flag back to your base scores points for your team.

Other forms of gameplay can be defined here. If you want to have “multiple lives” the Commander can set the place you return to “regenerate”. Once the game starts, these places cannot be changed.

Communication

All communication would have to be done by some kind of bluetooth headset and microphone combination. All cues would have to be audio based to avoid having to stare at your screen all the time. Through this you’d have:

  • Open communication with your team
  • Alerts from the game itself (“You have left the playing field, you have 10 seconds to get back in”, “Now entering Blue Space”, “Your Flag has been taken”, etc)
  • Broadcast something to all Players (“Game ends in 10 minutes”)

After-game

When the game is over, players can view a replay and see all the movements and events from the game. This is similar to the Commander’s view, but now everyone can see everything. This will let players review tactics and strategies for the next go-around.

Considerations and Potential Issues

Phones are fragile. They won’t hold up very well to paintpalls or airsoft pellets. You can route around this by keeping them in a protective case and inside a deep pocket. The headset interaction becomes key since actually pulling out a phone and looking at it would be a good way to lose focus on the action and become a nice target for the other team. Now that smart watches are becoming a thing, these could also provide something useful, but again fragility would have to be considered.

The Commander may have to use their device more often. I would assume they would be in more of a “base” or out of harm’s way to allow this to happen. But hey, if General Patton was up close to enemy lines, then you can too I suppose.

Battery is also an issue. Constant GPS and Network usage will suck a battery down in no time. I think a 100% charge on a modern phone would last long enough for a couple hours, but something like a Mophie would be recommended.

Lastly, networking could be tricky, especially in remote locations. Having something covered in wifi would ease this restriction (and help battery life) but those logistics could be a bear.

In Conclusion

As it says on the box, this is half-baked. I don’t even think you’d be able to create much of a business out of it, but goshdarnit it would be fun to play. If someone actually did make something like this, I’d be all over it, and demand only a modest stake in the business.

  1. If you know how to double jump in real life, I’m all ears.

  2. Geofencing means that something happens when your phone enters or exits a pre-defined boundary. For example, you could have your lights automatically turn on when you get home.

Let’s take a completely hypothetical situation where you’re developing an app that uses Core Data for the local storage and have a bunch of beta testers eagerly awaiting the next version before your product launch. Your previous attitude towards the data model was something along the lines of “It’s still pre-1.0, I’m not bothering with migrations yet, just delete and reinstall, c’mon.” However, you forgot that requiring the beta testers to deal with that isn’t exactly a friendly experience for them, and you made at least three changes to the data model since your last Beta release. Now if they run the app it’ll crash immediately because the database isn’t in sync with the data model.

Oops.

Usually when you make changes to the data model, you do so by creating a new version and telling the NSPersistentStoreCoordinator to perform lightweight migrations, then make your changes. Adding a new version of the data model after changes were made accomplishes nothing. Fortunately, you’re not screwed. We’re going to jump back in time, grab the old data model then pretend it was there all along.

Your MyProject.xcdatamodeld file is actually a directory. If you browse it in the Finder or Terminal, you’ll see more folders inside it, one for each version of your model. Inside those folders is a file simply called contents. This is an XML representation of the editor you see in Xcode.

Step 1 — Find the data model from the last beta version you released

Look through the history the xcdatamodeld file in your source control system 1. Hopefully you’ve been tagging all of your releases and can just checkout that specific one.

1
> git checkout 1.0-beta4

If not, you can mess around with git log to figure out where to go. This snippet can help you see the commits for a single file:

1
> git log --pretty=format:'%h : %s' --graph -n 45 FILENAME

Then, checkout the particular commit with the right version.

Step 2 — Copy the contents file

Find the contents file within your .xcdatamodeld file. Copy all that XML somewhere safe.

Go back to your HEAD or wherever you were.

Step 3 — Create the new version of the data model

If you didn’t know, the process is:

  • Open the .xcdatamodeld file in Xcode
  • In the Editor menu, click “Add Model Version”. Follow the instructions.
  • Open the File Inspector for your .xcdatamodeld. There is a Model Version segment in the inspector, make sure it’s on the version you just created.

Now you have two data models that are identical. Let’s change the history on the original one.

Step 4 — Change history

Close Xcode. That’s not mandatory but I’ve had it crash when mucking about with these files, and it’s just not worth the hassle.

Open the contents file for the original .xcdatamodeld in a text editor.

Paste in the version you created in Step 2.

Open Xcode. If you haven’t set up the NSPersistentStoreCoordinator to run migrations, do so now. This tutorial is pretty good.

Now when the app runs, the migrations update the users’ data and keep things from crashing.

Note: This is for lightweight migrations. Custom migrations are more complicated. objc.io has a great article on these. I don’t know if you can play fast and loose with the data model file like you can here though.

  1. You ARE using Source Control, right? Sometimes new developers will ask me why they need Source Control. I usually parrot the usual answers - branching is good, undo mistakes, tool integration, etc - but situations like this are where it really shines. Without source control here, you’d be hosed. You’d have to manually fix the XML in the contents file, which would be monumentally hard or altogether impossible depending on what changed and how good your memory is.

In the interest of security I’ve started to turn on Two Factor Authentication (aka 2FA) for some of the services I use. I tried it out with GitHub about a year ago, but turned it off shortly thereafter because I encountered a bunch of problems and didn’t have the time to figure them all out. That and Google’s Authenticator app having data loss issues after an update was a big red flag too.

Today it’s a little easier to manage. 1Password has 2FA support built in now, and there’s also Duo Mobile’s app. Turning it on was pretty easy: Go to the security page, click a few buttons, and follow instructions. Once it was enabled I decided to push some changes for a project, and then this happened:

1
2
3
> git push origin master
remote: Invalid username or password.
fatal: Authentication failed for 'https://github.com/swilliams/my-repo.git/'

Umm, ok. I mean, I guess the most secure repository is one that nobody can access.

The solution isn’t immediately obvious. I looked at GitHub’s setup docs again, but they didn’t mention anything about 2FA. When in doubt, try it again right? This time I got a username/password prompt. I had assumed I would get some sort of additional prompt to enter a single use code for the 2FA, so I pasted in my GitHub password.

1
2
3
4
5
> git push origin master
Username for 'https://github.com': swilliams
Password for 'https://swilliams@github.com':
remote: Invalid username or password.
fatal: Authentication failed for 'https://github.com/swilliams/my-repo.git/'

No dice.

Googling around a bit finally brought me to this page, “Creating an access token for command-line use”. When you enable 2FA you need to use a token as your password for the Terminal. I created this with the default scopes provided1, then copy/pasted the resulting token into the password prompt in my Terminal window.

1
2
3
4
5
6
7
8
9
10
± git push origin master
Username for 'https://github.com': swilliams
Password for 'https://swilliams@github.com':
Counting objects: 80, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (78/78), done.
Writing objects: 100% (80/80), 9.02 KiB | 0 bytes/s, done.
Total 80 (delta 58), reused 0 (delta 0)
To https://github.com/swilliams/my-repo.git
   01efb2c..445a0b6  master -> master

Yay!

I think that should handle all the headaches for 2FA with GitHub. I like the warm security feeling it brings, and it seems like the user experience has been cleared up too.

  1. For standard git operations, I don’t think you would need any of the other scopes available for apps, and you could probably remove

    gist
    from it too.

Copyright © 2018 - Scott Williams - Powered by Octopress