Data Persistence Dilemma

Sometimes, in order to solve a problem, I have to think through it out loud (or in this case, in writing).

Here’s the sitch: I’m making an app for knitters and crocheters. They need to be able to manage projects (i.e. “Baby blanket,” “Socks for mom,” etc.). In addition to a bunch of metadata about each project, users should be able to add photos and designate one or more images or PDF files as the project’s pattern. A PDF or image of the pattern isn’t required, but including one will allow users to enter a split view where they can view the pattern and operate a row counter at the same time.

A project shouldn’t necessarily “own” its pattern. In other words, a pattern can have multiple projects associated with it (say you want to make the same baby blanket for multiple babies), so as to avoid the needless duplication of the pattern file. A pattern can exist without a project and a project can exist without a pattern, but when linked, the project becomes the child of the pattern.

My user base includes people who may not always have an internet connection. Therefore, all data needs to be stored locally. However, those who do have an internet connection are going to want iCloud sync between devices.

I like Core Data. If I were to set this up in Core Data, without any consideration of iCloud syncing, I’d create Project and Pattern entities, store images and PDFs in the file system, and call it a day.

iCloud syncing is where things get murky for me. Core Data + iCloud is deprecated, and I don’t want to use it. Not only that, I don’t know what to do with the PDFs and images. Storing them as BLOBs in Core Data seems like a bad idea. I understand how to save them to the file system but don’t understand how to sync them via iCloud and also have a reference to them in Core Data. Do I use iCloud Document Storage for them? Do I zip them up somehow (NSFileWrapper??) and use UIDocument? How do I store a reference to them in Core Data (just the file name of the UIDocument, since the file URL is variable?). If users will be adding photos and PDFs at different times, do I use one UIDocument subclass for photos and one for PDFs or do I use a single document and update it with the added information? You can tell I obviously have no idea how this works, and a multitude of Google searches has yet to clear it up for me.

As for the rest of the information in Core Data, I’m thinking of trying to sync it  with CloudKit using something like the open source version of Ensembles or Seam3.

I guess I’m not sure if I’m on the right track and would welcome any advice/feedback. I’d really like to stay away from non-Apple services (like Realm) for the time being. Comments are open!

Waiting for Review Day 2: WatchKit & Core Data

Note: This is the second entry in a series of posts about writing my first iOS app. The app is currently in review, and until it is rejected or approved, I plan to write something every day about what I’ve learned.

In my first post, I talked about how I decided to use Core Data to store the verses in my Bible verse app and how I went about seeding that database. The next thing I had to figure out was how in the world my Watch app and iOS app could access the same Core Data model. I learned from a Make & Build tutorial that I needed to create both an App Group and a custom framework that could be shared between the two apps.

After creating a framework, I added two files to it: my Verse class, and a singleton DataManager class that handles all of the Core Data methods. Singletons are still a little confusing to me, so I pretty much just copied the code from this excellent tutorial video. At first I couldn’t figure out why my apps couldn’t seem to “see” anything in the framework, even after linking to it. Then I realized that I forgot to mark everything in the framework as “public.” /facepalm

My DataManager class has three simple methods: a method to fetch all of the verses in the store and place them in an array, a method to fetch only verses marked as a favorite, and a method to select a random verse from the array. Using one of the methods in my WatchKit app was as easy as saying:

Tomorrow, I’ll either talk about my app’s visual style (colors, icon, name, etc.), or about Glances and using Handoff.

Waiting for Review Day 1: Creating a pre-populated Core Data database

Guess what? I finally did it. I submitted my first app to the iOS App Store around 2am this morning. It feels so unbelievably great to have actually finished something (well, as much as any 1.0 can be “finished”). As it’s now waiting to be reviewed by someone from the App Store team, I thought I’d write one post every day during the waiting period, describing the app and what I learned from making it. A quick note before I go any further though: it is a Bible verse app, so if you don’t want to hear about that, this would be a good time to stop reading.

The Idea

My first idea for an app was a sort of non-traditional To-Do list app. It seemed like a neat concept and well-suited for a beginner, but after messing around with it for a few months I realized it wasn’t quite coming out the way I’d hoped. I felt slightly discouraged—that is, until I got my Apple Watch on April 24. Because I am a Christian and my faith is very important to me, one of the first things I did upon receiving my watch was look for an app that would allow me to view Bible verses on my wrist.

As of this writing, there are about a dozen watch apps that show up when you search for “bible verses” in the App Store. They fall into roughly two camps: apps that display daily verses, and apps that allow you to read the entire Bible. I wasn’t really looking for either one of those. What I wanted was an app that would allow me to hit “refresh” and see a new verse whenever I wanted, without having to wait for the next day. At first, I thought I could create the whole thing using just a Glance; however, when I learned that Glances must be static and can’t have any buttons, I set out to build a full-fledged app. According to Xcode, I started the project on April 30.

First Steps: Creating a database

I knew I would need a database of verses to pull from, and that I would need to decide what translation(s) to use. Because I didn’t feel like messing with copyright issues, I decided to build my own database using translations that are in the public domain: the World English Bible and King James Version. I pasted the verses into a Google spreadsheet with columns for the verse text, reference, and translation.

Next I needed to figure out how to get that data into my app. I stumbled upon a Ray Wenderlich tutorial that described how to create a command line utility app for OS X that would basically spit out a pre-filled SQLite database for use with Core Data. I liked the idea of shipping my app with the database already populated, so even though the tutorial was old (iOS 5!), I decided to give it a try. Translating it into Swift was surprisingly easy, but then I hit a problem: the tutorial used a JSON file, and all I had was a spreadsheet.

I feel the need to reiterate that I have no computer science/programming background, and know next to nothing about databases. For all I know, it’s probably ridiculously easy to parse a CSV file. However, my tutorial used JSON, and so I did what any self-respecting woman with over 20 years of Microsoft Office experience would do: I did a mail merge. Yes, I literally mail merged my spreadsheet into a Word document so that it matched proper JSON syntax, and then converted it to plaintext and threw a “.json” at the end. If you’re an experienced programmer and that’s the most ridiculous thing you’ve ever read: you’re welcome. ;-)

Anyway, in case any of you want to use that Ray Wenderlich tutorial with Swift, here is what I did:

  • I pasted the boilerplate Core Data stack into a new Swift class called “DataManager.”
  • After starting a new Xcode project for my iOS app and setting up Core Data, I copied my Core Data class called “Verse” and my .xcdatamodeld file from that project into the command line utility app project.
  • I copied my newly created “Verses.json” file into the utility app project.
  • Note: there are a couple changes you have to make to the Core Data stack code. You can find those changes in the tutorial.

Here is the code for my main.swift:

Tomorrow, I’ll talk about how I set up Core Data so that it could be accessed by my iOS app, Watch extension, and Today View Widget.