Published on

Indie Dev Weekly #22

Authors

2023, 0710-0716

This week I suddenly had a momentary thought to make this weekly journal paid reading, publishing it on platforms like Xiaobao Tong or Zhubai or enabling paid reading for this public WeChat account. But then I thought of how many top experts and masters have been continuously outputting high quality articles for free, like fatbobman, if I charged for these ramblings it would be too outrageous, so I quickly dismissed that idea.

Minimal Diary App Store

This week I did paired programming with Claude 2 to implement the drafts feature decided on last week. I thought it would only take a day or two but this feature ended up taking the entire week of writing and rewriting, on Saturday I even decided to scrap everything and start over.

git_log

All the coding work this week was related to Core Data, learning a lot of new things about Core Data.

objectID

It was only through Zhouzi Ge's blog that I learned every managed object in Core Data has an objectID property, and you can easily get a managed object through this property and the existingObject(with:) method. Previously when I needed to get a specific managed object, I would build a predicate using a custom id property on the model, query, and just take the first result.

However, this property seemingly can't be used in predicates, I tried several ways but all got errors, the answers from Claude 2 also got errors.

Relationship Properties

My initial thought was: when editing a diary entry, first convert the Entry to a Draft, then bind all editable variables to the Draft using Combine for realtime saving. But I discovered a problem doing this - after generating the Draft from the Entry, the original relational properties of the Entry disappeared. Below is the reason explained by Claude 2:

claude_relationship

CRUD Through Different Contexts

For Entry objects gotten through @FetchRequest in the view, if you edit and save them directly through performBackgroundTask(_:) there's no issue changing properties on the Entry itself, but changing relational properties causes a crash. After consulting Claude 2, unifying the contexts solved this problem.

claude_context

Optional Properties

By default all Core Data properties are Optional, meaning a property can be empty when saved to Core Data. But I didn't know this previously - the autogenerated NSManagedObject code from Core Data has all properties as Optional too, so I thought it was the same as Swift Optionals.

coredata_optional

To avoid checking Optionals, I changed some Draft entity properties to non-Optional and set default values, but the autogenerated code still had the properties as Optional... Asking Claude 2 also resulted in nonsense answers. So in the end I still couldn't remove the ? from each property.

claude_optional

Additionally, always respect every Optional variable, this was something I learned the hard way this week. Diary list data comes from converting Entry to EntryViewModel. Because Entry's id property was Optional, in the conversion process I assigned a default value to the corresponding EntryViewModel id property - I thought this was just an operation to resolve warnings, but it created a hidden danger that took half a day to discover the cause. Because the default UUID, it caused the id for a EntryViewModel generated from an Entry without id to be different each time, then leading to being unable to find the original Entry when updating the generated Draft...

optional_uuid

Despite the various issues, the drafts feature finally got completed. On Sunday afternoon I sent TestFlight build 11, testers immediately discovered all kinds of bugs, so Sunday night I sent 3 more builds to fix those bugs. In any case, I still want to release 1.7.6 next week, then I can focus on adapting to iOS 17 and developing a new app.