Wishlist for Swift 3.0

Here’s my shortlist of the features I most want to see added to Swift:

One-to-Many Data Flow

There are only three ways to propagate changes from your data model in off-the-shelf Swift: delegation, NSNotifications, and KVO. Only delegation can be accomplished in pure Swift, but it only works for one-to-one relationships. NSNotifications and KVO are Objective-C hacks that are made available to Swift via bridging, but there are numerous shortcomings with both APIs.

Until there’s a proper first-party alternative, I’ve been using KVO, but that requires making all my essential model classes inherit from NSObject. This feels icky.

I know there are third-party alternatives like ReactiveCocoa, but I don’t trust something that important to a third-party library (e.g. none of the libraries I’ve seen include quality-of-service parameters in their API designs, nor do I trust them to get multi-threading right when tying together changes from a background process to main-thread-only UI code).

Whether or not Apple does a Swift-native “port” of KVO or an Apple-esque rendition of ReactiveCocoa, there’s one thing I’d like them to do regardless: weak collections. Something like NSMapTable, only it would actually work as advertised. Then you could do something like:

var delegates: WeakCollection<MyDelegateProtocol>

i.e. a one-to-many version of the delegation pattern.

Improved Debugger

Maybe I’m missing some key knowledge here, but it seems like the debugger has a lot of room for improvement compared to what it was like with Objective-C. Examples abound. Entering po foo.property.anotherProperty should not choke on optionals as hard as it does currently. po someSwiftDictionary should print something closer to what [NSDictionary description] outputs. Using the expandable-caret view in the debugger for something like a Dictionary is pretty painful, too.

More Flexible Collection Constraints

As long as we’re dependent upon subclassing first-party classes like UIView and UIViewController to build our apps, there’s going to be a need to constrain collections to objects that both inherit from a given class and conform to a given protocol (or protocols). For example, if I have a custom view controller container that queries its children for UI details (the way UINavigationController does), then I want my array of child view controllers to be something like:

public var viewControllers: [MyProtocol<UIViewController>]

Currently, the only alternative is something nasty like:

public var viewControllerItems: [MyProtocol]

// ...

protocol MyProtocol { func viewController() -> UIViewController func someOtherMethod() -> UIColor }

In the nasty alternative my view controller subclasses would return self for viewController().

More Flexible Extension Constraints for Collections

It’s currently not possible to extend Dictionary with an arbitrary constraint on the key type. In other words, it’s not possible to do this:

extension Dictionary where Key:String, Value:JSONEncodable {
    // blah....
}

This would be helpful when, like the code example suggests, extending Dictionary to aid in JSON encoding/decoding.

Typed Throws

Twitter-friend Benjamin Mayo reminded me of another: we want typed throws. Unless Apple scraps the throws API altogether in favor of something less NSError-bound, it should at least indicate to consumers of a public API what values can be thrown by a function that throws. Currently, if you don’t have access to the source code for such a function, you have to rely on guess-and-check or documentation to discover what might be thrown. I’m sure Apple is aware of this drawback and have plans for it, but hey — this is my wishlist.

|  28 Sep 2015




Swift – Versus – Storyboards and State Restoration

I don’t use Storyboards or State Restoration in my own projects, but I have looked into them in detail at one time or another. From what I understand about both of these APIs, the life cycle for view controllers looks something like this:

1. Something happens in the app.
2. UIKit triggers the initialization of a view controller.
3. The initialized view controller is prepared just prior to being presented.
4. The view controller is presented.

It’s usually inside of Step 3 that depedencies are passed into the new view controller. In the case of State Restoration and a primary Storyboard, for example, the new view controller is given an opportunity to resume its previous state inside:

func decodeRestorableStateWithCoder(_ coder: NSCoder)

Or in the case of a Storyboard segue, the following method is called:

func prepareForSegue(_ segue: UIStoryboardSegue, sender sender: AnyObject?)

In either case, the new view controller is initialized before it receives its dependencies. But this order of operations is the opposite of what Swift encourages, particularly with regard to optionals.

Swift’s optionals make my code safer. They make it nearly impossible, with good coding practices, to make a mistake when reasoning about whether or not an element will be non-nil at run time. But optionals can be really annoying, too, for the same reason that they’re safer. As if let and guard checks build up like snowdrifts at the edges of my code, I find myself gravitating towards API designs that eliminate unnecessary optionals. I look for opportunities to “convert” an optional property to a non-optional one by providing a guaranteed non-nil value for it in the view controller’s initializer.

Thus, my Swift code tends towards view controller life cycles like this:

1. Something happens in my app.
2. I initialize my view controller programmatically, passing in dependencies.
3. My view controller is presented.

Clearly this pattern in Swift is mismatched with the requirements imposed by Storyboards and State Restoration. Swift pushes dependencies towards the init() method, while Storyboards and State Restoration push them into later stages in the life cycle. I have been able to handle this so far by avoiding Storyboards and State Restoration in favor of custom, programmatic alternatives. But I worry that Apple’s winds are blowing in the other direction and someday I’ll have to change up for a new design pattern.

What do you do in your code? Do you avoid Storyboards and State Restoration like I do? Do you have cleverer ways of dealing with the mismatch?

|  9 Sep 2015




Pushing on the Pull Door

Over at our company engineering blog, I wrote about some really dumb mistakes I made while working on Bloglovin’s iOS 9 compatibility:

Lessons learned while debugging URL scheme usage on iOS 9, most importantly: that bug isn’t unreproducible, you’ve just already fixed it without realizing it.

Hope this spares somebody from repeating my errors.

|  4 Sep 2015




Suggestions to Improve Using CocoaPods with Private Specs

Speaking of CocoaPods, I’ve been running into some irritations when using CocoaPods with a private specs repo. I jotted down some suggestions in a GitHub issue. I’ll repost them here for good measure. I appreciate any advice you have if you’ve used private specs repos extensively.

Podspec linting for private specs really falls short of an ideal, streamlined experience. There are several areas for improvement:

1) Don’t Require Irrelevant Metadata.

Private specs should not be obligated to provide an open-source license. They should not have to provide both descriptions and summaries (and ideally neither one should be required). Nor should a homepage should be required. These fields should only be required for public specs as they exist solely to aid in discovery by the general public.

2) Remove the HTTPS Requirement for Source URLs.

Private specs should be allowed to use SSH source URLs. As the linting warning reminds us, the enforcement of HTTPS is a matter of hospitality to the general public who might be behind firewalls that disallow SSH. It doesn’t make sense to apply this requirement to private specs. The HTTPS requirement is especially irritating for anyone who has TFA enabled on their GitHub account. To skirt the SSH warning, you have to disable all warnings via the --allow-warnings flag. I should be able to leave all unrelated warnings untouched. Ignoring every warning as a means of ignoring a single warning is suboptimal.

3) Provide Explicit Metadata Options to Indicate that a Podspec is Private

I’d like to be able to workaround issue #1 and #2 (and any other issues that I’ve neglected to mention) by an option like:

s.private = true

When private is present and equals true, this would indicate to the spec linter to skip linting steps that are only appropriate for public specs.

4) Disallow Pushing a Private Spec to the Public Trunk

It is extremely easy to accidentally publish a private spec to the public trunk, and difficult to fix the mistake after it occurs. Any Podpec with s.private = true should not be allowed to be pushed to the public trunk.

5) Allow Podspec to Indicate a Private Specs Repo in Which to Search For a Dependency

In some cases a private specs repo contains a spec with the same name as a spec in the public trunk. If a Podfile has a pod dependency that seems to appear in multiple repositories, running pod install will result in error logs about the duplicate specs. The Podfile of the host app bears the responsibility for making sure that custom specs repos are listed in the correct search order.

It would be preferable for a Podspec to be able to declare that a dependency can only be searched for within a given private spec repo url, e.g:

s.dependency 'AFOAuth2Manager', 'repo: git@github.com:/jaredsinclair/private-specs.git`

This would address cases where a public repo has to be forked into a private spec to fix incompatibilities with other third-party dependencies (long story).

|  22 Aug 2015




Code-Signing and CocoaPods

I’ve appended a short update to my article on best practices for code signing on iOS. If you use CocoaPods, you’ll need the following post_install script in your host project’s Podfile, which applies the correct code signing identity for the current scheme/config to each dependency framework:

post_install do |installer_representation|
    installer_representation.pods_project.targets.each do |target|
            target.build_configurations.each do |config|
                if config.to_s == 'Beta'
                    config.build_settings['CODE_SIGN_IDENTITY[sdk=iphoneos*]'] = 'iPhone Distribution'
                elsif config.to_s == 'Release'
                    config.build_settings['CODE_SIGN_IDENTITY[sdk=iphoneos*]'] = 'iPhone Distribution'
                end
            end
    end
end

My script assumes that you are using a distribution identity for configs named Beta and/or Release, but these can edited to match your own build configs. The CocoaPods default for the Debug code-signing identity (as well as the default Automatic selection for provisioning profiles) I have left untouched since they’re best practice (in my opinion).

One unfortunate drawback of using CocoaPods is that it is not possible for the post_install script to select a specific code-signing identity by name. For example, if your project uses an enterprise code signing identity for beta builds and a standard developer identity for release builds, you would ideally want to indicate those specific identities (per my recommendations). Alas, the best you can do via the script is to choose the automatic iOS Distribution selection and pray that Xcode will choose the appropriate identity. From what I have observed so far, Xcode is doing the right thing. Let me know if your experience differs.

This script is available as a gist. Hat-tip to GitHub user @TravisCrist for how to construct the outer portions of this post_install script.

|  22 Aug 2015