‘grocc’ v1.0.2

This term I had the opportunity to work on a personal project for college credit through the independent study CS 406 “Projects” course. I chose to work on my grocery and recipe utility iOS app ‘grocc‘ which allows users to quickly translate a meal plan into a grocery list.


My goals for this term were to:

  1. Complete development of recipe ingredient keeping functionality.
  2. Integrate the grocery list export functionality with one or more to-do list apps.
  3. Successfully publish the app to either the Google Play Store and Apple App Store.
  4. Complete a write-up on the completed app for my tech blog showcasing my development skills.

What I accomplished this term was:

  • Complete development of core feature set, including recipe keeping and list checking.
  • Itegrated the grocery list export functionality with Todoist’s REST v2 API (based on manual API token entry).
  • Successfully published the app to the Apple iOS App Store.
  • Posting this write-up on the work I completed and the finished product!

Finished Product

‘grocc’ can be viewed in the App Store.

‘grocc’ is a recipe and grocery utility app for iOS which allows users to quickly translate a meal plan into a grocery list.

Users can enter their recipes’ ingredient lists into the app, and modify or delete previously entered ingredients and recipes.

Users can select recipes to generate a grocery list, including multiple selections of a given recipe to ensure the correct quantities of ingredients are purchased.

Once users have selected their desired recipes, they are presented with a checklist and are invited to check their current pantry to ensure they actually need to purchase ingredients as groceries.

After checking their list, users can export their grocery list to Todoist.

When attempting to export their grocery list to Todoist, if the user has not yet entered their Todoist API token a form is displayed which accepts this token. After entering their Todoist API token, users can select the project or project section within their Todoist library which they want to export their grocery list to. Both of these features can also be accessed from the main menu of the app.

Features Implemented

The features I implemented during the course include:

  • Main Menu for app
  • Multiple selection of recipes when generating grocery list
  • Help dialog/FAQ for ‘Generate Grocery List’ page
  • Integration Test Suite
  • “Checklist Hopping”
  • Quantity+Measure form as dialog (not page)
  • Show Q+M form on new ingredient completion
  • Overload back button to minimize data loss
  • Edit/Delete ingredients in new and previously entered recipes
  • Delete recipe
  • Manage Todoist API Token
  • Select Todoist Export Location

Tech Stack

The primary technology that I utilized for the development of “grocc” was Flutter. Flutter is a cross-platform mobile application framework which is written in the Dart programming language. Both Dart and Flutter are created by Google, and are open-source.

I used several Flutter packages to offload development tasks. Packages I used include:

  • “google_fonts” to load fonts for the app
  • “sqflite” to implement two SQLite databases on the host device to store recipe and ingredient data
  • “shared_preferences” to gain access to persistent key:value data storage on the host device, which I used to persist the Todoist API token and export location after entered by the user
  • “uuid” to generate uuid’s fro Todoist API requests.
  • “integration_test” to enable the running of integration tests of the app

To deploy the app, I utilized Xcode and the Apple Developer program to upload an archive of the app, and have published the iOS version of the app to the Apple iOS App Store.


I accomplished all of the tasks in my initial plan, as well as several more (including development of a test suite and development of the “modify ingredient list of recipe” feature) which were added to subsequent versions of the plan.

In this project I succeeded at:

  • adapting my plan to accommodate development delays,
  • discovering critical features which were overlooked during initial planning,
  • learning to create and run integration tests for Flutter apps,
  • discovering and documenting potential features,
  • not letting said discoveries derail active development according to the development plan,
  • making regular status updates about project development, and
  • completing the development of a tool I now use on a weekly basis.


In this project I faced the following challenges:

Missing a Plan for Testing

In my initial project plan I failed to plan for testing of my app. Thanks to feedback on my initial submission, I created a plan to develop integration tests of the main features of my app and revised my project plan to include the associated tasks. The current version of my app is now well tested with integration tests.

Discovering Potential Features, and Sticking to Plan

Throughout development, I faced uncertainty in regards to whether or not I should pursue the development of potential features which I discovered while developing previously planned features. In most cases, I opted to instead only take careful notes about the newly discovered features.

However, I did choose to pursue the development of the “edit/delete ingredient list in recipe” feature, which I rediscovered after beginning development this term. I felt that as opposed to many of the other potential features that I discovered such as “hydrate state of ingredients checked when adding recipes after checking some ingredients”, “exporting quantities and measures needed for an ingredient as ‘secondary item text’ to Todoist”, and “add ad-hoc grocery item in checklist”, these ingredient modification features seemed essential to an initial public release.

I believe I made the correct decision to pursue the development of those features, but to defer the development of other potential features in order to stay on track with my plan.

Bug Fix: Exporting Unwanted Items

During development, I discovered the following bug: When checking an ingredient list to generate a grocery list, if an ingredient was initially tagged by a user as “need” (and thus added to the grocery list) but was later retagged as “have” (which should remove it from the grocery list), this item would still be exported to Todoist as an item on the grocery list. I tracked down this bug, and the cause turned out to be that while my code was removing the ingredient from the list which informs the display of the ingredient on the checklist, my code was not removing the ingredient from the “grocery list” list. To remedy this, I rewrote the “addToList()” function to be a “setCheckListItem()” function which accepts a bool named “adding” to indicate whether the ingredient in question is being added to or removed from (if applicable) the grocery list.

Bug Fix: Multiple Appearance of Single Checklist Item

During development, I discovered the following bug: When generating the checklist of ingredients, if an ingredient was in multiple recipes it would sometimes appear more than once on the checklist. This was a bug because I believed my code was sorting all of the like ingredients together and parsing grouped like ingredients into a single checklist item. As it turns out, my sorting algorithm was correctly grouping the ingredients by “name”, but a subsequent sort by “type” was behaving differently than I had anticipated and breaking up some “name” groups of ingredients. To remedy this, I changed my sorting procedure to first create separate lists of the ingredients based on their “type”, then sorting each list of “type”d ingredients by “name”, and finally returning the concatenation of all the sorted “type”d lists of ingredients as a single list.

Bug Fix: Recipe Selection State Corrupted

During development, I discovered the following bug: When returning from a checklist to add or remove a recipe and regenerate the checklist, the state of which recipes had been selected was corrupted and or missing. The problem turned out to be that the function I had written to generate the checklist from the selected recipes was consuming the data which indicated the user’s recipe selections. The solution was to first copy this data then consume only the copied data in the function, leaving the original selection data unchanged in the state of the app.

App Store Submission Hangups

When I first submitted the app for review to the iOS App Store, my submission was rejected. This was frustrating as the app had been previously approved for distribution via TestFlight, and I was uncertain as to the differences between the two approval processes.

The problems with my submission turned out to be fairly simple — the screenshots I had submitted included the Flutter “debug” banner. This goes against an App Store guideline which prohibits unnecessary references to the development process. Additionally the reviewer wanted answers to a few basic questions about the purpose and target audience of the app (as this was my first submission to the App Store for this app).

To resolve these issues I answered the questions, used an image editor to cover up the “debug” banner in my app screenshots, and resubmitted for review. This second submission was approved, and now my app is available on the App Store.

Feature Discovery: Informing User of Token Validity

When I was planning to work on the integration test for the “manage Todoist credentials” feature/menu, I realized that users were not receiving sufficient feedback to determine whether the API token they had entered would allow them to export their grocery lists. This led to a feature discovery — “token trial dialogs” which inform the user of the validity of the API token that they entered.


This term I completed an independent study course in which I developed features and tests for a grocery list app in preparation for its initial public release. I adapted to various challenges that cropped up during development, regularly reported on my progress, and ultimately published the app to the iOS App Store.

Print Friendly, PDF & Email

Leave a comment

Your email address will not be published. Required fields are marked *