Angelo Stavrow [dot] Blog

Missives and musings on a variety of topics.

This post is part of a series about rewriting my iOS app, Per. Per is a price per unit comparison app with a bunch of neat convenience figures, but it hasn't been updated in years, so I'm rewriting it from scratch to eliminate a bunch of technical debt. Just because it's not an open-source app doesn't mean I can't share what I learn as I go!

See the rest of the series here.

Two Steps Forward, One Step Back

Papercuts are the little scratches you get as you plow headlong through the prickly underbrush of implementing features.

One such papercut that I sorted out this morning was a weird UIToolbar iOS 13 bug requiring me to set a size on the input accessory view's frame lest I get that annoying “unable simultaneously satisfy constraints” warning. Previously, you could instantiate the toolbar with a simple

let inputAccessoryView = UIToolbar()

but now you'll need to do something like:

let inputAccessoryView = UIToolbar(frame: CGRect(x: 0, y: 0, width: 100, height: 100)).

I'm running into a weird bug, however, where the input view changes size on some devices between my custom calculator keyboard and the picker view:

"Screenshots from an iPhone 8 and iPhone 11 showing relative differences in input view height"

Notice how those input views are the same size on an iPhone 8 (on the left), for example, but very different on an iPhone 11 (on the right).

I'll dig into this some more tomorrow.

#per #perRewriteDiary #ios

Discuss...

This post is part of a series about rewriting my iOS app, Per. Per is a price per unit comparison app with a bunch of neat convenience figures, but it hasn't been updated in years, so I'm rewriting it from scratch to eliminate a bunch of technical debt. Just because it's not an open-source app doesn't mean I can't share what I learn as I go!

See the rest of the series here.

Onwards and Upwards

Just a short update today: as planned, I added the input accessory view to the detail form today. This only adds a simple previous/next toolbar button to skip between text fields in the form. It was nice to use SF Symbols for the left and right carets!

That gets us to feature parity — but now we've got the papercuts to deal with, with the hope that I'll have this installed on my phone for testing by the end of the week.

Tomorrow I take care of planning what to tackle first!

#per #perRewriteDiary #ios

Discuss...

This post is part of a series about rewriting my iOS app, Per. Per is a price per unit comparison app with a bunch of neat convenience figures, but it hasn't been updated in years, so I'm rewriting it from scratch to eliminate a bunch of technical debt. Just because it's not an open-source app doesn't mean I can't share what I learn as I go!

See the rest of the series here.

One Step Closer

A couple of little calculator-related papercuts were sorted out today.

  1. The math operator keys (“+”, “–”, “⨉”, “÷”) now enable themselves when there's a left-hand-side operand for the equation, and disable themselves when both operands are present (or nothing is in the text field at all).
  2. Tapping into the next text field will solve for any equation entered into the current text field (or, if you've got something like “12+” entered, it'll clear the “+” symbol from the text field).
  3. Deleting through an equation no longer crashes the app when you delete the operator symbol.

The only thing I want to finish up before installing going back to clean up any other papercuts is to add an input accessory view —the toolbar that sits just on top of the iOS keyboard, like a hat— that includes a previous and next button to skip between fields, which I'll add tomorrow.

#per #perRewriteDiary #ios

Discuss...

This post is part of a series about rewriting my iOS app, Per. Per is a price per unit comparison app with a bunch of neat convenience figures, but it hasn't been updated in years, so I'm rewriting it from scratch to eliminate a bunch of technical debt. Just because it's not an open-source app doesn't mean I can't share what I learn as I go!

See the rest of the series here.

I 💖 Property Observers

Slowly unravelling work on Per's calculator feature has been interesting. I had worked on getting the UX/UI of the calculator working before getting the actual arithmetic sorted out, such that certain keys would be enabled or disabled based on state, and then tried to add the math functionality on top of that, which made for a very messy pile of code.

I went through and removed anything that enables or disables keys, and cleared out the big, ugly updateResult() method that handled the actual (attempt at) arithmetic. I'm pushing a lot of the work into a property observer, and that's probably not going to be the final solution for this class, but it's really handy for simplifying how a thing should react to a change.

At any rate, here's how it's working now.

Every time a key in the keyboard is tapped, a character is appended to a currentString property — either a digit, a decimal, or an arithmetic operator. The main use for this string is to track what's in the relevant UITextField.

I'm using the didSet: observer on that property to parse that string every time it's updated, such that I can set a pair of Doubles for the left- and right-hand-side operands. This is done by checking a flag that tells me if an operator button was tapped. If that's false, we're setting the LHS operand; if it's true, we're setting the RHS operand.

When you tap an operator button, the class also makes a note of what that symbol was. Then, when you tap the equals button, it calls a much-simplified updateResult() method that looks at the value of the LHS and RHS operands, and performs the arithmetic based on the last operator button tapped (so, if you had tapped the + key, it'll add the operands) before resetting a few properties. The solution to that calculation is set as the new value of currentString (which sets this as a new LHS operand), and is also returned as a string (which the caller uses to update the target text field).

Oh, if you're creating a custom input view for your app and need a way to clear the text field, here's a handy recursive function you can use:

func clearTextField(_ sender: UIButton) {
    guard let isNotEmpty = target?.hasText else { return }
    if (isNotEmpty) {
        target?.deleteBackward()
        clearTextField(sender)
    }
}

Tomorrow, I'm going to add back automatic enabling and disabling of various keys based on the state of the keyboard.

#per #perRewriteDiary #ios

Discuss...

This post is part of a series about rewriting my iOS app, Per. Per is a price per unit comparison app with a bunch of neat convenience figures, but it hasn't been updated in years, so I'm rewriting it from scratch to eliminate a bunch of technical debt. Just because it's not an open-source app doesn't mean I can't share what I learn as I go!

See the rest of the series here.

Re-visiting The Calculator

On Wednesday, I laid out how the calculator feature would work. Then, yesterday, I added an equals button to the calculator keyboard, which changed how this all works. Here's how it should go:

  1. The user taps on a text field:
    • if it's empty, proceed to step 2.
    • if it's already got a first number (LHS operand) in it, proceed to step 3.
  2. Enter the LHS operand via the keypad; it shows up in the text field.
  3. Tap an operator symbol on the keypad; it shows up in the text field.
  4. If the user then:
    • switches to a different field, remove the operator symbol from the text field and go back to step 1.
    • enters a second number (RHS operand), proceed to step 5.
  5. As the user enters the RHS operand, it shows up in the text field.
  6. If the user then:
    • switches to a different field, perform the arithmetic and replace the text field contents with the result.
    • taps the equals button on the keypad, perform the arihtmetic and replace the text field contents with the result as a new LHS operand, then append the new operator symbol to the text field.

This is proving a little bit more complicated to untangle than I'd expected. The first equation entered calculates correctly, but then I've got too many flags and temporary variables to keep track of, and inevitably things get mixed up.

Tomorrow, I'm going to try to sketch out a better approach for this.

#per #perRewriteDiary #ios

Discuss...

Friday, March 6th marked three years of working remotely out of my home office. In prior years, I've answered a series of questions, but I've been struggling to write this year's post.

Read more...

This post is part of a series about rewriting my iOS app, Per. Per is a price per unit comparison app with a bunch of neat convenience figures, but it hasn't been updated in years, so I'm rewriting it from scratch to eliminate a bunch of technical debt. Just because it's not an open-source app doesn't mean I can't share what I learn as I go!

See the rest of the series here.

Untangling Stack Views

One thing I always have struggled with is visually reasoning about nested arrays — which, if you kinda squint and look at from the right angle, is very similar to what you've got with nested stack views.

The actual code to set up a stack view? Fairly straightforward, really. Apple did a nice job with this API. But untangling my own set up of the various horizontal and vertical stack views to create the keyboard was a bit... gross. I should have probably started by thinking about the top-level stack view in terms of columns rather than rows — blindly copy-pasting a solution from Stack Overflow often gets you to a solution while creating other problems, I guess.

At any rate, that's cleaned up now. I ended up exploring three options, and I'm not in love with any of them:

"Three options of calculator keyboard layouts"

For now I'm going with the one on the far right, with the equals button in its own column.

(Yes, I'm testing with the iPhone 8 Simulator right now. I always tend to test with an older device's screen size, though I don't have a good reason for why — but I will have to take care to ensure the layout makes sense with the home bar indicator thing on the iPhone X/Xs/11.)

Tomorrow, I get back to the actual calculator functionality!

#per #perRewriteDiary #ios

Discuss...

This post is part of a series about rewriting my iOS app, Per. Per is a price per unit comparison app with a bunch of neat convenience figures, but it hasn't been updated in years, so I'm rewriting it from scratch to eliminate a bunch of technical debt. Just because it's not an open-source app doesn't mean I can't share what I learn as I go!

See the rest of the series here.

What A Difference A Dream Makes

I got a pretty good night's sleep last night, so I'm feeling much better today as far as being able to think clearly. Before I just start writing code, here's the plan for how the calculator should work from a user's point of view:

  1. The user taps on a text field:
    • if it's empty, proceed to step 2.
    • if it's already got a first number (LHS operand) in it, proceed to step 3.
  2. Enter the LHS operand via the keypad; it shows up in the text field.
  3. Tap an operator symbol on the keypad; it shows up in the text field.
  4. If the user then:
    • switches to a different field, remove the operator symbol from the text field and go back to step 1.
    • enters a second number (RHS operand), proceed to step 5.
  5. As the user enters the RHS operand, it shows up in the text field.
  6. If the user then:
    • switches to a different field, perform the arithmetic and replace the text field contents with the result.
    • taps an operator symbol on the keypad, perform the arihtmetic and replace the text field contents with the result as a new LHS operand, then append the new operator symbol to the text field.

This works out for the current state of the calculator keyboard, but I'm not thrilled about that last step: it feels like there should be an equals button (“=”) that the user can press at any time to perform the arithmetic and update the text field to the result.

Another thing that the current shipping version of Per does is only allow one operator symbol to show at any given time. In other words, you have to solve the two-operand equation before you can continue, and this is enforced by disabling the operator symbols. Operator precedence is tricky to reason about (ever second-guessed yourself answering a “skill-testing question” on a contest?) so Per removes that option entirely.

Combining these approaches, I could add an equals button to complete arithmetic, and also disable other operator symbols (except for the equals key) whenever the user is entering a RHS operand. The only ugly part here is how to add a single button to the currently well-balanced 16-key grid. Here's what I'm thinking, using one of my terrible ASCII layouts:

 ----------------------
|                      | + |
|                       ---
|                      | - |
|                       ---
|                      | ⨉ |
|     NUMERIC KEYS      ---
|                      | ÷ |
|                       ---
|                      |   |
|                      | = |
 ---------------------- ---

So, a separate set of stack views for the numeric keys, and then another set of stack views over to the right for the operator keys and a double-tall equals key.

It's alwayhs better to work from a plan. Looking forward to kicking off the implementation tomorrow!

#per #perRewriteDiary #ios

Discuss...

This post is part of a series about rewriting my iOS app, Per. Per is a price per unit comparison app with a bunch of neat convenience figures, but it hasn't been updated in years, so I'm rewriting it from scratch to eliminate a bunch of technical debt. Just because it's not an open-source app doesn't mean I can't share what I learn as I go!

See the rest of the series here.

Back To Work, Sorta

Another night of terrible sleep has me struggling to reason with the best way to get the calculator keyboard to actually, y'know, calculate.

So I'm taking baby steps: first, figure out when the user is entering a LHS (left-hand side) vs RHS (right-hand side) operand for the equation. I can do that with a Boolean flag (isSettingLHSOperand). Okay, that's working as expected.

Then, I need to keep track of each of those operands (two Doubles, lhsOperand and rhsOperand), as well as the last operator symbol (“+”, “–”, “⨉”, “÷”) entered (a String called lastOperatorSymbol). From there, I can get my solution based on the operator passed in and move ahead with my work.

Yup, it took me an hour to struggle through this, and I attribute that squarely on having had only about 4 hours of sleep in the last two days.

I'm going to keep harping on this because it's important: if you have difficulty sleeping, do what you can to remedy it. It's easier said than done, I know — but it's important.

#per #perRewriteDiary #ios

Discuss...

This post is part of a series about rewriting my iOS app, Per. Per is a price per unit comparison app with a bunch of neat convenience figures, but it hasn't been updated in years, so I'm rewriting it from scratch to eliminate a bunch of technical debt. Just because it's not an open-source app doesn't mean I can't share what I learn as I go!

See the rest of the series here.

It's Okay To Take A Break

Nothing to report here today. I'm exhausted after a rough night and when my alarm went off at 5AM, I chose “roll over and go back to sleep” over “get up and hustle” because really, I don't hustle so good when I'm exhausted.

Build cool things. Create learning opportunities. And make sure to listen to your body, too.

#per #perRewriteDiary #ios

Discuss...

Enter your email to subscribe to updates.