Quiver: Code and Commands

In Part 1 of my series on leaving Evernote I took a look at Google Keep. Part 2 is an app that most people will have no need for, but it ended up being a very useful tool in my day to day work. It is Quiver, a note taking app designed specifically for code.

I actually started using Quiver while I was still using Evernote. While Evernote was an okay place to store code snippets, it wasn’t ideal. Notes are rich text by default, and if you wanted any sort of syntax highlighting, you had to do it by hand. Evernote was not designed with this task in mind.

Quiver is different. Code is its purpose. Yes, it can be made to function as a very nice plaintext note app, but that isn’t the primary purpose. Like Evernote you can create various notebooks, each storing a collection of individual notes. The notes can contain a mix of “cells” that are either rich text, code, markdown, latex, and diagram.

The code cells are the big one for me. Quiver isn’t an IDE, nor is it meant to be. I don’t use it to write any complex scripts or programs. But my job does require me to use a lot of commands, whether it be managing Macs, configuring switches, or setting up servers. I can’t keep it all in my head. Quiver has been extremely valuable for recording and finding these commands. In particular those ones that are used infrequently. I can’t commit them to memory, but I can easily find them in my Quiver library. It provides the answer to those “how did I solve that last time” questions. I also created a few notebooks for my “standard setups”. If I need to quickly spin up a web server, I can open that notebook, follow the commands in order, and end up with a system configured exactly how I want, with all the security settings that are important to not overlook. Yes, I know how to do this in my head, for the most part, but being able to follow a checklist pretty much guarantees I am not accidentally skipping a step.

The only real downside to Quiver right now is that it does not have an iOS component. A beta was announced a while ago, but nothing has come of that yet. Quiver does support sync via Dropbox, iCloud, or Google Drive, so I am able to keep a copy on both my home and work MacBook Pro. I use Dropbox for sync and has been completely reliable. The library can be stored anywhere, so in theory you could set up your own server and sync through it in the event you were syncing sensitive data and would rather not trust a public cloud service.

Given that I rarely do this kind of work on iOS, it isn’t a big deal right now that the iOS version has yet to materialize. It is something to keep in mind if you are regularly using an iPad for this kind of work.

Most people don’t need Quiver, and you could easily use another tool to store code notes. But I like having a tool dedicated to this task. I can very quickly find the commands I am looking for without returning a whole bunch of other unrelated results. If you are a programer, sysadmin, or any job that requires regular use of the command line and / or programming languages, this is a great tool to have in your arsenal.

An Open Letter to RunKeeper: Quality over Brand

Dear RunKeeper,

I wasn’t really a runner until I met you. In 2009 I could barely make two miles. My early runs tracked with your software are laughable compared to what I can do now. Twelve marathons and nearly 5000 miles later I caught the bug, and you helped me get there.

Back when the app was $10 I bought it gladly. When you introduced a subscription I joined immediately, and have been a member ever since. You have been on my home screen for nearly the entire time I have owned the iPhone.

But lately things have not been great. I feel your quality slipping, and even more concerning to me is that your focus, at least from an outside perspective, seems to have strayed from what is most important. Your own marketing of your brand has become all consuming, far more important that the product itself.

For a month it seemed that all you were focused on was the logo. There was an entire release in the App Store whose release notes talked about branding and nothing else. This is a very worrying sign from a tech company. Like with another favorite of mine falling on hard times, Evernote, it signals a move away from caring about the core product. See also Twitter.

Meanwhile the app has gotten extremely buggy. I have lost runs due to the app crashing on upload, taking my data with it. My husband has given up using it entirely as the GPS data has been wildly inaccurate lately. But worst of all the Apple Watch app took my day one move streak and deleted it on the day of the New York City Marathon. Your user base is made up of fitness fanatics. This kind of data destruction is going to be unacceptable to them. Even if this was an iOS issue and out of your control, you have the email addresses of all your users. We should have been warned. But it seems the brand was more important than the users.

I am not leaving yet but I will be exporting all my data into preparation to do so. I am hoping you can turn this around.
1. Stop rebranding. You are good. We are happy with the look. Get back to the product.
2. Communicate with your users. If there are bugs, especially ones that can result in data loss, we need to know.
3. Focus on reliability. No one wants to use an app that fails as often as it succeeds.
4. Get back to being about running and the runners who love it. The bright lights of Silicon Valley and its destructive culture of funding are the sirens that lead so many innovators to their deaths upon the rocks.
5. You don’t need to be the biggest run tracking app in the world, you need to be the best.

I want to stay with you RunKeeper I really do. I want to go back to the days when you were a small startup with a little app that just worked. No getting caught up in side projects (Breeze). No talk of the brand. I hate that word. That word is where you go when you are out of ideas. It is the word you use when the marketers and investors are running the show. Do not let his happen. Get back on course.

DST Strikes Again: Loss of Marathon Apple Watch Activity

UPDATE: The cause of the data loss is RunKeeper. The Apple Watch currently subtracts from your move goal rather than add to it. Do Not use RunKeeper for Apple Watch.

Daylight Saving Time sucks. No matter what BS reason you believe for it existing (conserving energy, farming, school buses), twice a year we all get to stress out, miss appointments, have our biological clocks messed with, and increasingly see software issues due to programming not able to handle the switch.

On November 1st I ran the New York City Marathon. It was my third time running the NYC race, and my first with the Apple Watch. But you would not know it from looking at my Activity data. A bug in the app is causing it to crash when attempting to view data from that day. That would be bad enough, but to make it worse, the app no longer recognizes that I met my goal that day, while in reality I met it five times over.

Activity ring not closed despite marathon.
That ring should have been closed 5 times over.

Apple has had some notorious DST related bugs in the past. But one would have thought that their device whose main purpose is to tell time would have done better. Numerous posts on Apple’s discussion page says that this is widespread, and definitely a DST problem.

I know it seems trivial, and ultimately it is, but this bug has really had a dramatic effect on how I feel about the watch. My enthusiasm has been completely zapped. I was really proud of the fact that I had an unbroken activity streak that went back to the very first day. And now, through no fault of my own and despite literally my best effort, it’s all gone. I am back to the beginning.

If Apple is going to make fitness tracking a headline feature of this device then it has to work and it has to work perfectly. To Tim Cook, I know you really care about CSAT.[1] Consider me unSAT today.

Note: I filed a bug report with Apple on this one. rdar://23374091

  1. Customer Satisfaction for those who don’t speak corporatese.  ↩

Learning to Code: Siri, meet Todoist

So I have not exactly been good about checking in here with my New Year’s Resolution, and now the year is nearly a quarter over. But this does not mean I have been ignoring my promise to learn to code this year. In fact, I have my first functional piece of software, an AppleScript based app that takes reminders – like the ones you get when you use Siri – and transfers them into Todoist, my to do app of choice these days.

I started out with the basics using my Lynda.com subscription. I took three courses:

  1. Foundations of Programming: Fundamentals

  2. Foundations of Programming: Object-Oriented Design

  3. Up and Running with AppleScript

I have had limited experience with all of these concepts in the past, but I figured this was a good time to sit down and start over with the basics. I have always been able to copy code from others and make minor adjustments, but the goal now is to know how all of it works, not just the small section I am tweaking. The most difficult thing with these courses was not getting caught up in the specifics. They are broad overview courses. It’s about learning the mechanics, not specific syntax.

For my first usable app I decided to go with something that fills an actual need. I have been using Todoist for a while now and have really liked it, except for one thing. It does not work with Siri. No third party app works with Siri, but some other to do apps have bridged the divide by pulling in information from Reminders. Todoist does not do this. Yes, I could use IFTTT, but the problem with that is it merely duplicates the data rather than transfer it.

This was a great use case for a simple AppleScript app. It does not need a UI, and Reminders is AppleScript friendly. Todoist is not, but they do have a very open API.

With all that information in hand, here is the code I wrote, in AppleScript, to take Reminders, add them to Todoist with all data intact, and then delete them from Reminders.

-- Copy todos from Reminders to Todoist
-- Set the script to stay open.
on idle
    tell application "Reminders"
        -- Set Reminders to loop through until the count is 0
        repeat while ((count of reminders) in list "Reminders") is greater than 0
            -- Get the name of the reminder
            set eventName to name of ((first reminder) in list "Reminders")
            -- Get the due date if one is present
            if due date of ((first reminder) in list "Reminders") is not missing value then
                (* Break apart the due date and set the month and day as integers.
                Format as MM/DD. This is required syntax for Todoist *)
                set dueDate to due date of first reminder
                set dueMonth to month of dueDate
                set dueMonthInt to dueMonth as integer
                set dueDay to day of dueDate
                set dueDayInt to dueDay as integer
                -- Set time in the hh:mm format
                set dueHour to hours of dueDate as string
                if minutes of dueDate is less than 10 then
                    set dueMin to "0" & minutes of dueDate as string
                    set dueMin to minutes of dueDate as string
                end if
                -- Set the variable for the due date we will actually pass to Todoist
                set dueOn to dueMonthInt & "/" & dueDayInt & " @ " & dueHour & ":" & dueMin as string
            end if
            -- Execute shell script to Todoist API with no date
            if due date of ((first reminder) in list "Reminders") is missing value then
                do shell script "curl -X POST -d 'content=" & {eventName} & "' -d priority=1 -d 'token=YOURTOKENHERE' https://todoist.com/API/additem"
                -- Execute shell script to Todoist with date
                do shell script "curl -X POST -d 'content=" & {eventName} & "' -d priority=1 -d 'date_string=" & {dueOn} & "' -d 'token=YOURTOKENHERE' https://todoist.com/API/additem"
            end if
            -- Delete the reminder. The loop ends when all reminders are processed.
            delete ((first reminder) in list "Reminders")
        end repeat
    end tell
    -- Set the script to run again every 30 seconds.
    return 30
end idle

I am posting this code here and making it freely available. Feel free to use it yourself and alter it as you see fit (I would love attribution for anything you derive from it though).

The app is designed to run continuously in the background. It gets the reminders in a list I have called “Reminders”. I only want to pull from this list as I do have others that I do not want to script to touch. I get the name and due date if there is one. The due date proved the be the trickiest part of this. Todoist expects a very specifically formatted date string and the format AppleScript was returning was not compatible.

I know there is likely a better way to get the date into the format I need (MM/DD hh:mm) but this was how I was able to get it to work for me. Not the most elegant code I know, but it worked, and given this is my first functional program, I wanted to show it here as it was when it first ran successfully.

If there is no due date, the script simply passes the event name to Todoist’s API. Otherwise name and date are included. Note that if you want to use this you need to replace YOURTOKENHERE with your API token that you can get from the Todoist settings. Otherwise the script won’t know which Todoist account to add to.

The script will delete each todo from Reminders after processing it. Once there are no reminders left, it ends, and waits 30 seconds before running again when you export it as an application using the “stay open after run handler” option.

In my case I loaded the app onto my OS X server, enabled iCloud reminders sync to that machine, and that was that. Everything now works. Within 30 seconds of me using Siri to add a new reminder, it will be transferred from Reminders into Todoist. If you don’t have an OS X server, it will still work on any OS X machine so long as the computer is running.

Yes this is a very simple app using a scripting language. It won’t be in the App Store any time soon. But it feels extremely good to have a real, functional piece of software that exists and fills a need. For the first time rather than relying on someone else solving this problem, I did it myself. I have done a few other AppleScript apps since this one, and now feel much more comfortable with at least the basics of programming. So now it is time to move on and dip my toe into the Objective-C pool. That will be the next post on this topic.

New Year’s Resolution – Learning to Code

I have never been big on New Year’s resolutions as most people abandon them by the end of January. I only set things I intend to actually follow through on. In 2009 my resolution was to work out more often and lose weight. I managed to lose 30 pounds that year and started running (and look how that has gone since). I have avoided a resolution until this year, but it is time to try again.

I have had little bits and pieces of programming experience here and there, but nothing formal and never amounting to anything actually useful. So for 2015 my resolution is to finally learn to code for real. By the end of the year my goal is to have a simple, but functional iOS app available in the App Store. I don’t even know what it will be yet, but something I can point to and declare this resolution successful.

I figure it would also be fun to document the process here. Not to mention the side benefit of publicly forcing myself to stick to it. I am going to start at the very beginning, no previous experience. So time to fire up Lynda.com and get to work.