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 book Combine: Asynchronous Programing with Swift by the raywenderlich.com tutorial team (writers Scott Gardner, Shai Mishali, Florent Pillet, & Marin Todorov on this book, and designer Vicki Wenderlich). It’s a good book on Combine and this code is from the project in Chapter 8.

The problem with this pattern in this particular case, and other similar situations, is that the AnyCancellable is never removed from the subscriptions Set and so it’s a memory leak. 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 this sample project, the user can do the action invoking this code (adding photos in this case) repeatedly and each time the .store(...) call adds another AnyCancellable to the Set.


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!

“Help getting into the tech scene in Portland”

I get this question a lot, “I’m having trouble getting into the tech industry in Portland, any suggestions?” Usually from recent college graduates or people who are coming into tech from other than a CS degree path.  I just wrote up this response to one and decided I was tired of typing similar stuff in over and over so extractedit and am putting it here:

—-

Jobs resources:

There are more for sure.

While you search (since one can only spend so many hours a day searching), build something that demonstrates your skills and helps you learn more skills. Picking something you see lots of job ads for (if it’s interesting to you) is a decent way to start. Lot of demand for machine learning these days. Taking online courses can help accelerate this learning but won’t get you a job; building something that works and that demonstrates what you can do is much more likely to.

If you find a place you want to work, get to know people there and let them get to know you.(1)  If they like you then they’ll help you know about positions that open up. Learn from them what they need and go teach yourself those things. Ask smart questions (find answers to easy ones online and in documentation).

Finally, big companies like eBay, Apple, Google, Amazon etc open up there internships for current students and recent graduates in (Oct?)/Nov/Dec so inquiring about them can be a good route into larger companies.


1) don’t be creepy! See if you have any friends in common.  See if there’s a meetup or other public social gathering they go to where you can meet them.  Follow on twitter to get to know them over time.  Look for their office having an open office gathering, etc.

Tax Time: Retirement plan for Indies

Tax Time here in the U S of A.   This post is for self-employeed and small business owners in the USA but does not constitute tax advice – you should consult a qualified licensed/certified tax professional – but is just some information to help you ask them some leading questions and pointers to documentation on the IRS.gov website for educational purposes.

π

As I’m calculating the basis in my SEP-IRA due to some conversions of IRA to ROTH IRA, I’m reminded that I lost out on about 10 years of pre-tax retirement savings due to a bad CPA who said I wasn’t eligible to do so since it was an S-Corp.  Got a new CPA and found out the first one was wrong (or the law changed and he was wrong at first but didn’t inform me that it had changed).

I suggest you educate yourself by starting at IRS.gov SEP FAQs and learn enough to ask the questions I should have :-/

The great thing about a SEP-IRA, for an S-Corp anyway (not sure about LLCs or sole-proprietors), is that the business makes the contributions for the employee and these contributions are deductible business expenses.

The limits are also a LOT higher than you as an individual have.  From an FAQ sub-page linked to from the one above:

How much can I contribute to my SEP?

The contributions you make to each employee’s SEP-IRA each year cannot exceed the lesser of:

  1. 25% of compensation, or
  2. $53,000 (for 2015 and 2016 and subject to annual cost-of-living adjustments for later years).

These limits apply to contributions you make for your employees to all defined contribution plans, which includes SEPs. Compensation up to $265,000 in 2015 and 2016 and subject to cost-of-living adjustments for later years) of an employee’s compensation may be considered. If you’re self-employed, use a special calculation to determine contributions for yourself.

So if you’re making more than $72,000 a year you can start to exceed the $18,000 personal contribution limit to a SEP-IRA or 401K.

This makes it sound like you don’t have to be a Corporation (but read Publication 560 & ask your CPA):

How much can I contribute if I’m self-employed?

The same limits on contributions made to employees’ SEP-IRAs also apply to contributions if you are self-employed. However, special rules apply when figuring the maximum deductible contribution. See Publication 560 for details on determining the contribution amount.

The only real down-side that I’m aware of (again, consult your hopefully better-than-my-old-CPA CPA) is that you make this contribution for all employees who meet the criterion you set in the SEP Plan Document.  The IRS has a “model SEP plan document”, form 5305-SEP that I used and that might be enough for you.  See the details/restrictions here.

My company mostly used contractors and I set the requirement to full-time employment for 3 years which was likely to exclude all but the most dedicated and valuable employees (and which I already qualified for :)).

On the other hand, having a good retirement plan that is competitive with with a big company’s 401K plan might help you attract employees that you otherwise couldn’t, so you might want eligibility that let’s employees participate as soon as they’ve passed some introductory trial period (90 or 180 days or whatever).  Notice that the limits for the SEP-IRA are actually higher than those for a 401K plan once compensation crosses the $72,000/year threshold.  So you can actually provide a more aggressive retirement plan with a SEP-IRA plan than with that big company 401K plan (unless I’m missing something).

And the employee can also make a ROTH IRA contribution as well (subject to the usual ROTH IRA contribution limits).

A SEP-IRA is cheaper to operate than a 401K plan (which is complicated enough that you have to pay a service to operate it for you – and they aren’t cheap).  Your employees can open their own SEP-IRA account and your business simply deposits money into it.  (See the IRS site for details).

Over the 10 years I was operating with the bad CPA I could have saved a lot more for retirement than the ~$4000-$5000 ROTH IRA limit, if only I’d known….   Now you know.

Go forth and save those pre-tax dollars for retirement!

Your 50-year-old self will thank you.  😀

 

This is not tax advice. All readers should consult a qualified tax professional about any tax decisions they make for themselves or their business.

iWork ’09 and .docx files

If you happen to rebuild a mac and put 10.9.5 on it and you install iWork ’09 from an older CD/DVD so you get iWork 9.0, you will find that you cannot open .docx files.

Going to the AppStore is fruitless – just wants to get you to update to the current versions of Pages (the rewrite) and then tells you that you have an incompatible system (requires OS X 10.10 or later).  It doesn’t offer to update you to the updates for iWork ’09 that you may have been offered back when you first installed iWork and before the new iWork apps came out.

The solution is to get the updater from Apple’s Support area (search for “iWork 09 update”).  Here’s a link to the newest we found (9.3).