Improving on the common AnyCancellable .store(…) pattern.

If you’ve been reading any Combine documentation or code, you’ve probably seen code like this before:

let newPhotos = photos.selectedPhotos
newPhotos
  .map { [unowned self] newImage in
    return self.images.value + [newImage]
  }
  .assign(to: \.value, on: images)
  .store(in: &subscriptions)

Pretty standard: call something that returns a publisher, map the results to whatever type you need, assign that value to an instance variable, and finally, store the resulting AnyCancellable into a Set named subscriptions which is an instance variable or property in the enclosing scope. In this case we’re in a UIViewController subclass where it’s defined as:

private var subscriptions = Set<AnyCancellable>()

This code is from the popular book on Combine.

The problem with this pattern in this particular case, and other similar situations, is that the AnyCancellable is never removed from the subscriptions Set. Sometimes this doesn’t matter – when the enclosing scope is going to go away and the code is only called once relatively soon before exiting scope cleans up memory. But sometimes, as in the sample project this code is from, the user can do the action invoking this code (adding photos in this case) repeatedly. Each time this code is invoked the .store(...) call adds another AnyCancellable to the Set. Since this view controller is the root view controller of the app it is never closed and this memory is never cleaned up during the lifetime of the app.


An easy solution is to add an instance variable to your class (a UIViewController in this case):

private var newPhotosSubscription: AnyCancellable?

and then change the code above to something like this so the AnyCancellable is assigned to the instance variable and the .store(..) part is removed:

self.newPhotosSubscription = newPhotos
  .map { [unowned self] newImage in
    self.images.value + [newImage]
  }
  .assign(to: \.value, on: images)

When this assignment happens it frees the previous value, if any, and thus there will be at most a single AnyCancellable kept around.

Ok, that works. But I’m a perfectionist and that single AnyCancellable hanging around bugged me. That’s fixable, but as I went and fixed this issue in more places in the project there was a proliferation of instance variables as I had to add one for each place this came up.

So here’s a solution I came up with to avoid that.

First, add an extension on AnyCancellable:

extension AnyCancellable {
  func store(in dictionary: inout [UInt64: AnyCancellable], 
             for key: UInt64) {
    dictionary[key] = self
  }
}

Then change the subscriptions instance variable to be a matching Dictionary (and remove the individual instance variables you added for each case previously, if you did that already):

private var subscriptions = Dictionary<UInt64, AnyCancellable>()

In our original function which created the newPhotosSubscription and let the user choose photos, change the code to:

let key = DispatchTime.now().uptimeNanoseconds
 
newPhotos
  .handleEvents(receiveCompletion: { [unowned self] _ in
    self.subscriptions.removeValue(forKey: key)
  })
  .map { [unowned self] newImage in
    self.images.value + [newImage]
  }
  .assign(to: \.value, on: images)
  .store(in: &subscriptions, for: key)

So above we created a Dictionary instead of a Set for our subscriptions instance variable that holds on to the AnyCancellable so it remains allocated while needed. The dictionary allows us to store the AnyCancellable under a unique key, which in this case is a UInt64. On the first line above we create that key and assign it the number of nanoseconds since the device rebooted.

Then we add a .handleEvents operator to the subscription pipeline. Once the publisher has sent the .finished or .failure completion event we no longer need to keep the AnyCancellable around. Our receiveCompletion closure code removes the AnyCancellable stored under the captured key from our subscriptions instance variable.

(Note: if you preferred, you could replace UInt64 with UUID in the dictionary declaration and the AnyCancellable extension. Then instead of setting the key value to DispatchTime.now().uptimeNanoseconds you could generate a new UUID with UUID(). All that matters is that the is a unique value conforming to Hashable and doesn’t cost too much to generate).


I’m relatively new to Combine, so if you know a better way to do this, I’d love to hear about it!


Addendum

1) Be aware that Swift closures capture variables by reference by default (unlike for Objective C). So don’t re-use the key variable like I did in my project 

2) I also ran into a case where the pattern above did odd things for a Combine pipeline subscribed to a Future publisher – the store seems to happen after the pipeline has finished executing (huh?!) and thus the clean up in the completion handler is called before the store. I haven’t dug into that to understand why, but thought I’d mention it in case you are seeing things not get cleaned up when you expect like I was.

-> I later learned that this is because in Combine Future is “eager” and will complete immediately. One option is to wrap it in a Deferred publisher and another is to capture the AnyCancellable in a local variable and only save it to the dictionary if the completion block hasn’t been called yet.

Swift 5 Encodable nested and hoisted JSON

Ran into an Encodable situation that puzzled me for a bit today.

For those who don’t want to read much, the key observation is that try foo.encode(to: container.superEncoder(forKey: key)) will DELETE any existing key node in the container and the encode foo into a new node with that key. However, nestedContainer(keyedBy: CodingKeys.self, forKey: key) will add encoded items to an existing key if it already exists.

Here’s the blow-by-blow of me figuring that out 🙂

(I have a lot of Swift yet to learn, so it’s likely there is a better way to do all this, but my searching wasn’t finding anything and this works and isn’t entirely horrible, so I thought I’d post it in case it helps someone else).

I have the following model which is already Codable (only Encodable shown for brevity):

public enum UserIdentifier: Encodable {
  case email(String)
  case phone(String)
  case guestID(String)
}

extension UserIdentifier {
  enum CodingKeys: String, CodingKey {
    case address, phone, gid
  }
    
  public func encode(to encoder: Encoder) throws {
    var container = encoder.container(keyedBy: CodingKeys.self)
    switch self {
    case let .email(value):
      try container.encode(value, forKey: CodingKeys.address)
    case let .phone(value): 
      try container.encode(value, forKey: CodingKeys.phone)
    case let .guestID(value): 
      try container.encode(value, forKey: CodingKeys.gid)
    }
  }
}

This then needed to be a field in a struct representing the body of a POST request to an http server:

struct PostBody: Encodable {
  let userID: User.UserIdentifier
  let name: String
  let password: String
}

and what the server expected in the body is JSON of the form:

{
  "address" : "bob@email.com",
  "username" : "Robert Tree",
  "password" : "MyBadPassword!"
}

To see what the output of the default Encodable support will output I created a little test case (I often do this kind of thing in a playground, but this was already embedded in an iOS app so it was easiest to just make a test case):

class Tests: XCTestCase {
  func testCreateUserPostBodyToJSON() throws {
    let encoder = JSONEncoder()
    encoder.outputFormatting = .prettyPrinted

    let body = UserService.CreateUserPostBody(userID: User.UserIdentifier.email("bob@email.com"), 
        name: "Robert Tree", 
        password: "MyBadPassword!")

    let jsonData = try encoder.encode(body)
    print(String(data: jsonData, encoding: .utf8)!)
  }
}

and, of course we expect this won’t be right, but it’s our starting point.  Here’s what it outputs:

{
  "userID" : {
     "address" : "bob@email.com"
  },
  "name" : "Robert Tree",
  "password" : "MyBadPassword!"
}

So clearly we need a custom encode function to pull the userID enum contents up to the top level. Let’s try this:

func encode(to encoder: Encoder) throws {
  enum CodingKeys: String, CodingKey {
    case username, password
  }
  var container = encoder.container(keyedBy: CodingKeys.self)
  try container.encode(name, forKey: .username)
  try container.encode(password, forKey: .password)
  try userID.encode(to: encoder)
}

The key is that last line: try userID.encode(to: encoder). Here’s the new output:

{
  "address" : "bob@email.com",
  "username" : "Robert Tree",
  "password" : "MyBadPassword!"
}

Bingo! Just what the doctor ordered!

Except then I try the request to the server and it turns out the api documents for the server are out of date (🤦🏼‍♂️) and what the server really wants is:

{
  "user" : {
    "address" : "bob@email.com",
    "username" : "Robert Tree",
    "password" : "MyBadPassword!"
  }
}

Hmmm. Ok, so this is a little more challenging, but it’s what nestedContainer is for, so we’ll try using that:

func encode(to encoder: Encoder) throws {
  enum ParentCodingKeys: String, CodingKey {
    case user
  }
  enum CodingKeys: String, CodingKey {
    case username, password
  }

  var container = encoder.container(keyedBy: ParentCodingKeys.self)
  var nestedContainer = container.nestedContainer(keyedBy: CodingKeys.self, forKey: .user)
  try nestedContainer.encode(name, forKey: .username)
  try nestedContainer.encode(password, forKey: .password)
  try userID.encode(to: container.superEncoder(forKey: .user))
}

Looks fancy. Let’s try it:

{
  "user" : {
    "address" : "bob@email.com"
  }
}

OH NO!!! what happened?!!

Hmmm… That looks like just the output of the last line, so comment out
try userID.encode(to: container.superEncoder(forKey: .user))
and see if the first part is working at least:

{
  "user" : {
    "username" : "Robert Tree",
    "password" : "MyBadPassword!"
  }
}

Ok. So the first part is creating things exactly how we want it to, it’s just that last line isn’t adding itself to the already existing "user" : { } node. Rats! Documentation isn’t shedding any light (it *does* in fact shed light, but it’s too subtle for me to catch on a too-quick read; as we’ll see below). Tried this on a whim:

func encode(to encoder: Encoder) throws {
  enum ParentCodingKeys: String, CodingKey {
    case user
  }
  enum CodingKeys: String, CodingKey {
    case username, password
  }

  var container = encoder.container(keyedBy: ParentCodingKeys.self)
  try userID.encode(to: container.superEncoder(forKey: .user))
  var nestedContainer = container.nestedContainer(keyedBy: CodingKeys.self, forKey: .user)
  try nestedContainer.encode(name, forKey: .username)
  try nestedContainer.encode(password, forKey: .password)
}

And the output is:

{
  "user" : {
    "address" : "bob@email.com",
    "username" : "Robert Tree",
    "password" : "MyBadPassword!"
  }
}

Success!

So, apparently, superEncoder(forKey: key) (Docs, emphasis mine: “Stores a new nested container for the given key and returns A new encoder instance for encoding super into that container.”) always makes the node for key and overwrites whatever happened to be there before (how rude!). Luckily, nestedContainer(keyedBy: Keys, forKey: key) (Docs: “Stores a keyed encoding container for the given key and returns it.”) doesn’t do that and will just add items to the node if it already exists. Sweet!

Follow up:

It occurs to me that the solution above only works because there’s only a single enum like UserIdentifier to include in the nested structure. They can’t both be first and so the second one would overwrite the first one.

Hopefully there’s another better way to do this that someone will point out in a comment which can handle multiple objects.

Making iOS 12 projects in Xcode 11

Some Xcode 11 iOS project templates aren’t iOS 12 compatible out of the box, but it’s relatively easy to make them work, here’s how:

  1. Select File > New… > Project…
  2. Select iOS and “Single View App” template, for example.
  3. Give the app a name, Swift, and be sure to select “Storyboard”, not “SwiftUI” (which needs some View and some requires iOS 13 according to the error message from Xcode), then select the “Next” to create the project.
  4. Select “AppDelegate.swift” and add @available(iOS 13, *) on the line above each of the func definitions in the “UISceneSession Lifecycle”
  5. Add an implementation of the window property inside the AppDelegate class like so: var window: UIWindow?
  6. Open “SceneDelegate.swift” and add @available(iOS 13, *) on the line above class SceneDelegate
  7. Select the project file in the Project Navigator to edit project settings and select the Project in the left pane of the editor
  8. In the “Info” tab, change the “iOS Deployment Target” at the top of the right pane to the appropriate 12.x deployment target for your project.
  9. Build and Run.  Good to go!

I’ve posted the two edited source files in a gist here

Objective C Builder Pattern play

So I read two posts on the Builder Pattern from Java today that got linked to off of Twitter.

This by Klaas Pieter which referenced this one by Uli Kusterer.  Both good articles.

I haven’t done Java much at all for the last 10 years and so am not used to this pattern, but I thought about how I might do something similar and while I’m not sure my first thought is any better, it seems to meet the requirements and/or benefits noted by the two blog posts in question and the tweet discussion referencing them.

Instead of:

 Pizza pizza = new Pizza.Builder()
     .size(12)
     .pepperoni(true)
     .mushrooms(true)
     .build();

(from Uli’s post), or

  Image* theImage =
    (new Image.Builder)->SetWidth(100)
    ->SetHeight(80)->SetDepth(8)->Build();

(from Klaas’ post)

I tried something like:

Foo * aFoo = [Foo fooWithData: @{
     @"width" : @21, @"height" : @22 }];

Here’s one way to implement that – there are multiple, clearly
(pardon the formatting, trying to fit into our narrow blog them is annoying):

// .h file
#import <Foundation/Foundation.h>

@interface Foo : NSObject

+ (instancetype) fooWithData: (NSDictionary*) initParams;

@end

// .m file
@interface Foo ()
@property (nonatomic, assign) long width;
@property (nonatomic, assign) long height;
@property (nonatomic, assign) long depth;
@end

@implementation Foo

+ (instancetype) fooWithData: (NSDictionary*) initParams
{
  Foo *result = [[Foo alloc] init];

  // If one uses setValue:forKey: in a loop as
  // as Uli notes then we can't support integral
  // properties like ints.
  // Also doing it explicitly as below
  // means we don't have to have the same name
  // for our private internal properties as we
  // document for our public parameters because
  // we can map them here.
  // e.g.,
  //  result.imageWidth = [initParams[@"width"] longValue];

  if ( initParams[@"width"] != nil )
    result.width = [initParams[@"width"] longValue];

  if ( initParams[@"height"] != nil )
    result.height = [initParams[@"height"] longValue];

  if ( initParams[@"depth"] != nil )
    result.depth = [initParams[@"depth"] longValue];

  return result;
}

- (instancetype) init
{
  self = [super init];
  if ( self )
  {
    // init with defaults
    _width = 10;
    _height = 10;
    _depth = 1;
  }
  return self;
}

- (NSString*) description
{
  return [NSString stringWithFormat:
           @"Foo: (%p), width: %ld, height: %ld, depth: %ld",
           self, self.width, self.height, self.depth];
}
@end

// main.m
#import <Foundation/Foundation.h>

int main(int argc, char *argv[]) {
  @autoreleasepool {
      // note no depth specified, taking default of 1
    Foo * aFoo =
      [Foo fooWithData: @{ @"width" : @21, @"height" : @22 }];
    NSLog(@"aFoo: %@", aFoo );
  }
}

Paste the above into CodeRunner and run it and you get:

2014-04-12 16:22:38.918 Untitled 7[71144:707]
aFoo: Foo: (0x7f8842c0af00), width: 21, height: 22, depth: 1

In the above I only typed in the class method as taking the full parameter list but normally I would have made the init method take the same parameter and do the initialization & mapping there. Something I’d likely add if I was going to actually use this, which I’m not likely to. Why not? Because to use the construct above you’d have to document the parameter keys available to use in the dictionary and what type of value each takes. This is where ObjectiveC’s named parameters comes in handy: they are self documenting.

Anyway, an interesting procrastination from what I was supposed to be doing this Saturday afternoon… 🙂

7DRL 2014 – Day 7: In which everything is complete

Trinkets – the roguelike game I’ve spent the past week creating for the 7DRL competition – is finished, about 20 hours before the deadline! It’s a game about wandering an extra dimensional vault, acquiring trinkets and trying to get back home.

A build for mac is available here. Unfortunately, there’s no windows/linux build, but if you’re on one of those operating systems and want to play, the source is available here.

Screen Shot 2014-03-15 at 2.38.31 PM

… which turns out to be entirely justified.

My high score is 59 – can you beat it? If so, let me know in the comments below!

7DRL 2014 – Day 6: In which victory approaches at high velocity

One day remains, and I’m actually almost done. It would be nice to redo the map generator, and game balance can always be tweaked some more, but the plot and endgame are implemented, the boss-fights are done with basic AI, and there’s even a score feature. Shown below is the second of four boss fights:

You have a bad feeling about this level...
You have a bad feeling about this level…

… which turns out to be entirely justified.
… which turns out to be entirely justified.

7DRL 2014 – Day 5: In which parkour is performed in fancy shoes

Today was very productive; I finished the procedural generation of trinkets (for now, at least), improved the magic system, procedurally generated strangely worded descriptions of said trinkets, added new effects including knock-back and circular slide, and added all of the non-boss enemies, including the Abberant visible below:

Image
Parkour shoes acquired!

Shown above is the non-procedurally-generated description of one of the coolest shoe types in the game (thus far, at least). These shoes allow you to run along walls, moving faster than enemies or even jumping over them.

I still have two more days left to go, and quite a bit still to do, including:

  • Plot
  • Victory conditions
  • Boss fights + Boss AI
  • Game balance
  • Prettier maps

7DRL 2014 – Days 3 and 4: In which trinkets are created

So, I’m about halfway through the week, but days 2-4 were all mostly occupied by other things, so the real productivity is just beginning. I’ve reworked the combat system to use the same generalized targeting/effect model as the spells. I’ve also changed trinket abilities to work on that model, divided trinkets into six types (hats, orbs, wands, cloaks, weapons, and shoes), which can be equipped and used for various nefarious purposes. I’ve started work on the procedural generation of trinkets, but there’s still a lot to do on that front, which will occupy the next day and a half, leaving me with a day and a half for enemies, plot, game balance, and a dungeon generator that isn’t stolen from the libtcod tutorial. That’s plenty of time, right?

Screen Shot 2014-03-12 at 11.36.07 PM
Weapons can now replace your bump attack with one or two spell casts. This one replaces your attack with two fire bolts, but they can also sunder defenses or daze and freeze enemies, among other things.

Screen Shot 2014-03-11 at 9.38.44 PM
Death Star? Death Star! DEATH STAR!
This is what happens when you procedurally generate names by picking two words from a list and concatenating them…

 

7DRL Competition 2014 – Day 2: In which the art of sorcery is practiced

Screenshot of Trinkets
Magic system: Activate!

There are now six spells, and status effects, and some of the ‘everything goes horribly wrong’.

The spells have all been divided into ‘targeting’ and ‘effect’ components – except for teleportation, which still consists of duct tape and hacks and probably will remain that way.

There’s also a bit more user interface than before, and the trinkets are actually worth using sometimes (even if they are all still exactly identical).

Melee combat has also been re-written to allow for spells like the Ethereal Axe, which replaces your melee attack with a high-damage Arcane attack.

Tomorrow, I’ll be working on equipment. I won’t have much time though – classes and whatnot – so there probably won’t be a blog post.

The day after, procedural trinkets, more enemies, and everything going even more horribly wrong!

7DRL Competition 2014 – Day 1: In which a roguelike is begun

This year I’m participating in the Seven Day Roguelike Competition, a contest to make a game in the roguelike genre in a week. (For those not familiar with it, roguelikes tend to be dungeon-crawlers with procedurally generated words, permanent character death, and often old-school graphics. There are frequently some variations from this rough description.)

I’m making a game known as ‘Trinkets’ until I think of a better name. It is a game in which a wizard strapped for cash loots a pocket dimension, and everything goes horribly wrong. It will experiment with a nontraditional power progression; you become weaker, then strong again, rather than a constant power level or steady increase. There will be a plot and randomly generated Trinkets.

My time will be somewhat limited, as I am currently a college student, but after spending most of today programming I produced the following:

Screenshot of Trinkets
Taste the power, troll scum!

I’ve used some code from some past projects, specifically most of the data structures and UI code. I’m using python, with the libtcod library.

Current features:

  • Move around…
  • …on a procedurally generated map…
  • …and hit things…
  • …which hit you back, and can kill you if you’re not careful.
  • Look for trinkets…
  • …which have magic powers…
  • …which are simply not as powerful (at least for now) as your spells.
  • Speaking of spells, you have five (and will have six), which in combination make you nearly unstoppable…
  • …at least until I implement the ‘everything goes horribly wrong’ part.