Skip to content

Category: Swift

Building a Caching Custom Combine Operator #2: The .cache() Method

In part 1, we started building our custom combine operator by starting with the foundation: building the Combine chain that implemented our caching operator. In this part, we want to make this something that is reusable, and functions like a built-in Combine operator, by adding it to the Publisher type via an extension.

As a refresher, let’s review how we wanted our .cache() method to work for somebody using it. The upstream chain would provide input for the caching operator, which would either calculate the output from a given operation, or if it’s already been calculated previously, simply pass on the previous value. So the signature would look something like this:

upstream.cache(operation: ...)

Now we just need to convert that into a method declaration in a Publisher extension. Easy, right?

Now we recall the image from part 1, flatMap()’s method declaration:

How do we put together this big bundle of Swift generics? We’ll break that down in this part, and adapt our code to fit the new declaration. By the end, we will have a .cache() method that will cache the results of our operation called on the upstream inputs, whatever the input and output types are. Put on your generics caps, and let’s start.

Keep your debug toolkit handy but hidden using conditional compilation

When you’ve been working on an app for a while, especially as it gets large, you often start to include snippets of code in the app that have been useful for debugging your app in the past. Such debugging code may be as simple as log statements, to as complex as adding full screens to your interface, activated by controls that you add to your interface for debugging only.

Such tools become an integral part of your debugging environment; you don’t want to throw them out. But do you really want to ship those in your app? It makes your app bigger and slower. You could comment them out before you release, but after a marathon debug session, do you trust that you’ll comment out again all of the debugging tools you enabled?

How can I keep my debugging addons, but ensure they aren’t in the version I deploy to the App Store?

There is a way: you can use a feature of Swift called conditional compilation to enable debugging code on your debug build. If you still have Objective-C code, you’re still in luck: a similar feature called preprocessor macros can provide the same functionality.

Porting to Swift? Do it Gradually Using Extensions: Part 3

As a refresher from Part 1 and Part 2, we’re trying to make the process of porting Objective-C to Swift code more gradual by porting classes method by method using Swift extensions. This is very helpful in keeping with the idea of “When it comes to porting, smaller steps are better.” In this part, we’ll cover something I discovered when porting my custom UIKit code to Swift.

Porting UIViews and UIViewControllers is relatively straightforward using the techniques we’ve already discussed. But delegates, your most common extension point for UIViews and UIViewControllers, can be a different story.

You do the easy thing: take that first method from the delegate protocol and move it to your Swift extension. You will probably get away with this for a while, until you move a method, and Xcode suddenly expresses its unhappiness:

Method 'tableView(_:numberOfRowsInSection:)' with Objective-C selector 'tableView:numberOfRowsInSection:'
conflicts with previous declaration with the same Objective-C selector

In this case, long ago, Apple decided to “help” you by providing a default implementation for certain methods of UIKit delegates. Whether this was a good idea in the first place is a topic for another article, but when porting the method to Swift, this causes nothing but problems.

Porting to Swift? Do it Gradually Using Extensions: Part 2

As a refresher from Part 1, we’re trying to make the process of porting Objective-C to Swift code more gradual by porting classes method by method using Swift extensions. This is very helpful in keeping with the idea of “When it comes to porting, smaller steps are better.”

This only gets us so far, though.

Some of the classes you want to port are going to contain business logic for your apps. Model data, calculations on that model data, transformations, etc.

Unfortunately, you cannot move those properties to your extension; extensions don’t support adding stored properties to a class.

What does that mean? You can’t use Swift-only types for your properties, such as structs, enums, Swift arrays, optionals, etc., because they need to be accessible from both Objective-C and Swift.

Does that mean I’m stuck with ugly Objective-C types like NSArray, NSDictionary, etc. in our Swift code, until the entire class is ported?

Thankfully not. While you can’t add stored properties in an extension, you can add computed properties in an extension. By creating computed properties that bridge between the Objective-C properties and a Swift representation of that property, you can still port your class method by method, while still writing your new Swift methods using all of the power of the Swift type system.

Porting to Swift? Do it Gradually Using Extensions: Part 1

Despite the fact that Swift has been available to iOS developers for over 4 years now, it’s likely that many long-lived apps still have Objective-C code in them. To keep things up to date, and to take advantage of the more modern Swift language, you will need to port those at least some of those classes to Swift.

You are instantly drawn to your small, simple classes, and you think, “Sure, those won’t be a problem at all. I could knock these out in a couple hours a piece.”

But then you open up one of your large classes. And you freeze.

You know which one. Every codebase has (at least) one. It is one of the core classes of the app: hundreds (or thousands…) of lines of code, many dependencies on other classes in your codebase. It’s a core part of the app; porting this class and testing that the porting doesn’t completely break everything else could takes weeks. Nobody has time for “weeks” anymore.

So you surrender, and those big, hairy Objective-C classes stay big, hairy Objective-C.

It saves unnecessary rework to only refactor code that you are actually changing for another reason. But if you’re only changing one method, you don’t want to port the rest of the class you aren’t touching, because it means extra coding work, extra testing work, extra review work…

Is there a way to port Swift like this, method by method, property by property? There is, by writing Swift extensions for your Objective-C classes.