Wednesday, October 29, 2025

My iOS App: Tap to Capture the Moments

Background Story

It all started with a conversation in a social group long time ago.

We were discussing a tragic event — a moment that led us to reflect deeply on the meaning of life.

I said something that surprised even myself:

“I’ve written an iOS app. Maybe it could help people — maybe even save a life.”

Someone asked, “Tea Director, what kind of app is it? Could you tell us more?”

I smiled and replied, “Someday, when the time is right.”

That night, the topic was too heavy to shift away from.

Now, after quite some time, deeply believing in my iOS app, I’ve finally decided to write this post — to share with my friends what I consider one of my finest creations:

TapMyNotes — Tap to Capture the Moments.

The Journey Begins: A Hike in the Rockies

Let me start with a story — a mountain hike that inspired this reflection.

On October 7, 2025, a Tuesday, I went hiking in the Canadian Rockies with my wife and a couple of our best frieds, longing to see the golden larch needles of autumn.

As usual, once we parked near the trailhead, I opened my iPhone, launched my own app which I have being loved and trusted for many years, and tapped on a button in a view — recording the very start of our hike.

Now, days later, sitting at home, I opened that same record.

A map view of the coordinates, a visual feature of the app, exactly where we began.

It felt simple yet powerful — a small mark of time and place preserved, quietly, inside my app.

About the App

The app is called TapMyNotes (Chinese: 点击美好记录).

It supports both English and Chinese (Simplified and Traditional).

The idea is simple yet meaningful: with just one tap, you can instantly capture

  1. When,
  2. Where, and
  3. What

the three essentials I call the 3W Record.

The app’s data model is natural, open and flexible.

You can define your own structure in two levels:

  • Tap Entities – broad categories like Life, Work, Health, Outdoor, Travel, etc.
  • Tap Items – specific topics within each, like Running, Hiking, Swimming, Skiing, etc.

To record something, just select a tap item, open its info button, and tap the hand icon in a popup view.

The app automatically captures time and location, which cannot be changed.

That’s intentional — I want each record to reflect a true moment.

If you forget to tap, that moment is gone forever — because life only happens once.

Every tap becomes a conscious choice, a moment you decided was worth keeping.

Adding the Words

After the tap, the app reminds you to write — a note, a thought, a few simple words.

You can type, speak, or scan text; you can always edit or add more later.

I encourage users to write something immediately after the tap.

Those few words often capture your most genuine feelings.

Later, when you revisit and expand them, they’ll form the truest memories — written proof of what you felt in that moment.

Words breathe life into data; they give warmth to time and place.

Another reason I emphasize on the importance of a note is that words might be the key to find out your past taps by the search feature of the app, when you have hundres, thousands of tap records.

My Mountain Records

Nearly ten years ago, I started using my own app to record meaningful moments in my life.

My very first record was on May 27, 2017, a Saturday.

Since then, I’ve used it to document everything — from ideas to travels, from daily reflections to outdoor activities, including mountain hikes.

For hiking, I first tried naming each mountain as a separate Tap Item.

But soon, I realized that created too many entries.

So I created one item called “Mountain Hiking”, and used keywords within notes to distinguish each hike.

For total of 363 taps, if each hiking records 20 taps on average, this tap item contains about 18 hiking activities.

At the parking lot, start point, I added a key word of Vista Park 25/10, and a brief description about our hike.

Every time I hike, I record:

  • The starting point
  • Key scenic, event, or turning points or directions along the way
  • The summit or destination
  • And finally, the return to the starting point

Each record includes timestamp, GPS coordinates, and a short description.

These points connect on the map, forming a clear trail line. Mapview is one of key features in the app.

The app also calculates duration, distance, pace, and elevation gain — all essential for mountain activities, maybe usesful for other events or moments.

During hikes, I announced data to my companions from time to time:

“We’ve been walking for eighteen minutes — straight line distance 85 meters from the last stop, altitude 1,879 meters, up 29 meters!”

We walked deep in the woods and mountants. With the convinience of mobile phone and my app, we felt not only more confidence, but also a lots of fun.

Reflections on Data and Meaning

Every piece of data belongs to the user.

All information is stored locally, allowing full control and privacy.

Users can view details of each GPS location point associated to a tap — coordinates, intervals, elevation changes — or calculate the total ascent and duration between waypoints.

These records, precise and personal, aren’t just statistics.

They become stories, memories, and traces of a life well-lived.

Even years later, when place names change, the GPS coordinates remain — timeless reminders of where we once stood.

That’s why I treasure every tap record created with TapMyNotes.

It’s more than an app; it’s a way to keep your footprints visible across both time and space.

The following is the mapview of our last mountain hiking activity: Vista Lake and Arnica Lake

Vista Lake

and Arnica Lake

Afterthought

Since releasing the app, I’ve continued to refine it — adding new features, improving performance, and learning from users and friends who generously test and share feedback.

Many of them call it “a truly valuable app,” and I’m deeply grateful for their trust and encouragement.

Together, we’ve turned a personal idea into a shared journey of creativity and reflection.


I’ve been consulting ChatGPT for months on various design ideas and coding challenges. Many times, ChatGPT confirmed my thoughts and inspired me with ideas I hadn’t considered before for my requests many times.

With ChatGPT’s help, I’m now close to releasing my next update version — and through this experience, I believe that ChatGPT already knows my app quite well.

After finishing this draft, I asked ChatGPT to review it and craft a poetic ending, which I could not write.

I was deeply impressed by how well ChatGPT understood my intention and how beautifully it wrote this conclusion piece.

By the way, I didn’t use ChatGPT’s version word for word. No one understands TapMyNotes better than I do, and some things can only be said from the heart. So I made a few revisions in my own voice to bring out what this app truly means to me.

Conclusion by ChatGPT

A Moment in the Mountain —
Light in the Shadow

Deep in the mountains, the wind fell silent.
Golden needles were scattered along the trail.
Up and down the slopes, the mountain’s shadow
slowly pushed the sun away.

For a moment, I was not sure —
where I was, how far from the parking lot,
and how far to my destination.
Then, I took out my phone and tapped.

A signal appeared, faint yet steady.
The map glowed softly — a blank base of color,
but thin lines of blue and red
connected where I stood
to the previous point,
and to where I had begun.

That line was more than direction —
it was reassurance.
And the numbers, more than data —
they told a story of progress and endurance,
a quiet warmth in the cold air.

Those numbers showed where I was
and how my energy had been spent.
Though small compared to the mountains,
I felt great — accomplished, so far and so high.

Every tap, I realized,
is a promise to remember.
Each one says:
“I was here — in this place, at this moment.”

The shadow faded,
the mountain fell back into silence,
and I followed those colorful lines
back into the sunlight.

Epilogue

TapMyNotes is not just a note-taking app.
It is a way to mark your present existence —
to remind yourself that
  every moment in time axis,
  every breath in the universe,
  every step along your path,
  and every effort you’ve made,
are worth recording.

Other Blogs

Read More...

Sunday, May 25, 2025

Accessing iOS App Files

iOS apps are built on a sandbox infrastructure, which normally makes it difficult to access the files within an app.

However, as an iOS developer with an app on the App Store, I’ve found a way to view my app’s contents using the Xcode application.

In Xcode, go to Window → Devices and Simulators to open the relevant window. You should see your connected iPhone there. In the Installed APPS panel, click the + button to add your app if it isn’t already listed. Once added, your app will appear in the list.

To view the contents of the app, you need to download it first. Click the ... button next to the app name and select Download from the options.

Once downloaded, you can view the package contents from Finder. The file I’m most interested in is the core database of my app. From here, I can easily examine all supporting files, including the database itself. Finder allows me to open the files I need and verify their contents.

This is an extremely valuable and convenient tool for iOS developers. It allows me to verify, back up, and review the app’s contents on my iPhone before making further changes. I can also modify and replace content for testing purposes, and then incorporate those changes into my Xcode project after thorough testing.

For example, I’ve recently been working on an update to migrate from a single database store to multiple database stores. This change lays the foundation for future features and improvements in data access efficiency. However, it involves a major overhaul of the project, affecting many parts of the codebase. I was concerned about testing these changes on my iPhone, especially because I didn’t want to risk losing 18 years of valuable collected data. With this tool, I can now safely back up my app by selecting Download container... before running any tests. If anything goes wrong, I can easily restore the app using the Replace container... option.

References

  • Try searching for "access iPhone app files on Mac from Xcode". As of May 25, 2025, the top results (possibly generated by AI) provided exactly the guidance I needed to begin my research.

Other Blogs

Read More...

Tuesday, April 15, 2025

Common Issue for Updating iOS App

I have an iOS app listed on the Apple Store. For various reasons, I need to update my app regularly. For example, I have to ensure it keeps up with the constant release of new iOS frameworks, fix bugs discovered by users, and, most importantly, add new features that bring more value to users.

In some of my past updates, I’ve had to make changes to the Core Data database, which involves schema updates. Fortunately, Apple provides a solid Core Data foundation, so in many cases, I’ve had to do little or no extra work. However, some updates require modifying managed Core Data entities. In those cases, I need to write code that ensures a smooth migration from the old version to the new schema, while maintaining correct structures and property values.

Consulting Help from ChatGPT

Recently, I made another change to my Core Data schema. Before implementing my strategy, I decided to consult ChatGPT for advice. Here’s what I asked:

One question about the update. This change involves a Core Data database update: adding a new property typeTag (Int16) to the TapEntity managed object.

For the existing users, their app's database has to be upgraded to the new one when they download this update. All the user defined tap entity objects be updated with the new property typeTag set to 100.

For existing users, their app's database needs to be upgraded when they download this update. All user-defined tap entity objects should have the new typeTag property set to 100.

For both old and new users, an app-defined tap entity object, with the typeTag property set to 10. should also be added to the database.

Here's my strategy: a function like updateDB() is called during app launch or preparation. Within the function, the app checks the saved database version. Fortunately, all previous versions of my app save the database version in user defaults. If the version is 2.0 (or older), I can safely assume that the existing tap entities do not have a typeTag value. So I will set it to 100.

If the database version is not set (for new users) or is less than 2.1 (the current update), the app will create the new app-defined tap entity object with typeTag set to 10. After this update, the app will update the user defaults to save the new database version as 2.1.

I believe this approach will minimize the amount of work needed to update or prepare the database—especially in cases where the dataset is very large. What do you think of this strategy? Did I miss anything? Do you have any suggestions for improvement?

I received a response from ChatGPT almost instantly.

Your strategy is well thought out, and it shows a strong balance between performance, reliability, and maintainability—especially when handling large user databases. Here's a breakdown of what you're doing right and a few small suggestions to make it even better:


✅ Why Your Strategy Is Solid

  1. Lightweight & Targeted Update:
    • Only update entities where typeTag is missing (older data).
    • No full migration or sweeping changes—minimal impact on large datasets.
  2. Explicit Versioning via UserDefaults:
    • Keeps control in your hands instead of relying entirely on Core Data’s inferred migrations.
    • Makes it easy to track what version of data the app is working with.
  3. App-defined Entities Only Added When Needed:
    • Ensures fresh installs or updated apps get what they need without redundancy.
  4. Post-update Versioning:
    • Clearly bumping the version to 2.2 ensures that this logic won’t be run again unnecessarily.

✍️ Example Pseudocode Outline

  1. func updateDatabaseIfNeeded() {
  2.  let currentVersion = UserDefaults.standard.string(forKey: "dbVersion") ?? "0.0"
  3.  if currentVersion.compare("2.1", options: .numeric) == .orderedAscending {
  4.    // This includes new users (no version set) or users upgrading from < 2.1
  5.    let context = persistentContainer.viewContext
  6.    let fetchRequest: NSFetchRequest<TapEntity> = TapEntity.fetchRequest()
  7.    do {
  8.      let entities = try context.fetch(fetchRequest)
  9.      for entity in entities where entity.typeTag == 0 {
  10.        entity.typeTag = 100
  11.      }
  12.      // Insert app-defined entity if it doesn't exist yet
  13.      if !hasAppDefinedEntity(context: context) {
  14.        createAppDefinedEntities(in: context)
  15.      }
  16.      try context.save()
  17.      UserDefaults.standard.set("2.2", forKey: "dbVersion")
  18.    } catch {
  19.      print("Database update failed: \(error)")
  20.    }
  21.  }
  22. }

🧠 Minor Suggestions for Improvement

  1. Make Your Checks Idempotent
    • Ensure createAppDefinedEntities() checks if each entity already exists to avoid duplication (e.g., by name or uuid).
  2. Batching for Performance (Optional)
    • If the dataset is truly massive, consider using a NSBatchUpdateRequest (iOS 8+ only) to update typeTag in bulk. But your loop is just fine for moderate-sized data.
  3. Add Logging in Debug Builds
    • Helpful for QA or debugging issues in migration.

✅ Conclusion

Your strategy is already solid and efficient. With minor improvements (e.g. using numeric versions, making the patch idempotent), it’ll scale well and provide great control for you as the developer. It’s especially wise because it:

  • Avoids unnecessary operations
  • Keeps logic in Swift code (not buried in Core Data migrations)
  • Future-proofs your app structure for versioned upgrades

I was excited to see ChatGPT's very positive and encouraging comments on my strategy. One suggestion it made—regarding idempotency—was particularly valuable. It reminded me to ensure that app-defined entities are not created more than once, even though my current strategy is already quite robust. Indeed, since the update process should only run once, I can't imagine how the database version in user defaults would be reset. That setting is read-only during the update process.

I had much more back-and-forth conversation with ChatGPT on this update strategy and related issues, but I’ll have to keep it brief here and share only a small portion of our discussion. In short, I found this consulting experience extremely helpful and productive.

With ChatGPT’s confirmation and insights, I’m ready to roll up my sleeves and implement the update code for the next release.

By the way, my iOS app is called TapMyNotes. If you're using the app, you'll see this update once it's released. The new version will go live after development and thorough testing are complete. I hope you'll enjoy the app and be pleased with the exciting new feature enhancements!

Other Blogs

Read More...

Wednesday, April 09, 2025

A Conversation with ChatGPT

Recently, I had a very good conversation with ChatGPT about a topic that often goes unnoticed: how to name a variable in a Swift class in my iOS app. This isn't the kind of question you typically search for on Google or post on StackOverflow (where you might have to wait a day or longer for a response). Yet, I was stuck trying to find a meaningful name—one that clearly conveys its purpose and is easy to recognize later during maintenance.

My Initial Question

ChatGPT came to mind. I explained why I needed this variable, and with some context, I hoped it would suggest a good name. Here's my original question:

I have a main view with some tab bars, such as Edit, Tap, and Report. The main view is the starting point. I would like to define an enum type to indicate where the starting point is, for example, TabBarStatus. I also like to define a variable for the status. What names should I use for the enum and variable? The variable will be used in subsequent views to find out where the start view is.

Less than a second later, ChatGPT responded with a thoughtful explanation and code examples.

For your use case, where you want to track which tab was the starting point (Edit, Tap, Report, etc.), defining an enum and a variable to hold that state is a great approach. Here's a naming suggestion that is clear, consistent with Swift/iOS conventions, and scalable:

🧩 Enum Name

Use a descriptive enum name that reflects the context:

  1. enum MainTab {
  2.  case edit
  3.  case tap
  4.  case report
  5. }

Alternative names:

  • StartTab
  • MainTabType
  • TabBarStatus (your suggestion, but slightly less Swift-like)

Swift style prefers concise, noun-based enum names like MainTab or StartTab.

🧩 Variable Name

For a variable that stores the starting tab, here are a few clean suggestions:

  • startingTab: MainTab
  • originTab: MainTab
  • entryTab: MainTab
  • launchTab: MainTab
  • startFromTab: MainTab (if used in routing context)
✅ Example Usage
  1. enum MainTab {
  2.  case edit
  3.  case tap
  4.  case report
  5. }
  6. struct ViewModel {
  7.  var startingTab: MainTab
  8. }

My Follow-Up

Just like talking to a senior developer, I continued the conversation and explained more about the context and how this variable would be used:

Good. Another question I need your advice on. All main view controllers and subsequent view controllers (VCs) are based on a base view controller, BaseViewController: UIViewController. All the subsequent VCs are presented as detail views, modal views, etc. I would like to define a public variable in BaseViewController, and its value is set in the main view controllers. I think this var should be static, instead of an instance variable. The reason is that there is only one starting tab. Right?

I was hoping ChatGPT could offer a better design based on my explanation. To my surprise, ChatGPT not only understood my design but also laid out a solid analysis of how the design could be improved.

ChatGPT's instant reply

Great question—and you're thinking in the right direction! Let's break it down and figure out the best approach.


🧠 Your Goal

You want all view controllers (which inherit from BaseViewController) to know where the user started (i.e., which main tab). There's only one starting tab per app session, and this info is set by the main view controller.


✅ Static vs Instance Variable
  • Static variable (static var or class var) is shared across all instances of BaseViewController. Perfect for global, app-wide state.
  • Instance variable (var) is unique to each instance, so you'd need to pass it manually to every view controller—probably not what you want here.

👉 Since there's only one starting tab, and you want all view controllers to access the same value, a static variable is the right choice.


✅ Suggested Implementation
  1. class BaseViewController: UIViewController {
  2.  // Default value, set from main controller
  3.  static var startingTab: MainTab = .tap
  4. }

Then, in your main tab view controllers (e.g. EditViewController, TapViewController, etc.), you set it like this:

  1. override func viewDidLoad() {
  2.  super.viewDidLoad()
  3.  BaseViewController.startingTab = .edit  // or .tap or .report
  4. }

And any other view controller can read it like:

  1. let origin = BaseViewController.startingTab


✅ Optional: Make It a Computed Property (if you want read-only from others)
  1. class BaseViewController: UIViewController {
  2.  private static var _startingTab: MainTab = .tap
  3.  public class var startingTab: MainTab {
  4.    get { return _startingTab }
  5.    set { _startingTab = newValue }
  6.  }
  7. }

Let me know if you also want to persist this value (e.g. between launches) or just keep it in-memory for one app session.

A Better Design

During this very natural and productive conversation, three concepts caught my attention: state, persistence, and computed property. They suddenly lit up my thinking like a flash of inspiration. A new design came to mind, and I shared it with ChatGPT:

There may be some cases where a subsequent view is not inherited from BaseViewController. I think it may be better to define a static class, such as MainTabStatus (or maybe a better name?). Within the class, a private var _startingTab and a public computed property startingTab can be used to manage the starting tab status. This class can be extended to include mechanisms to store the status in memory or user settings (persisting across app launches). This helper class can then be accessed from any view controller, view, or class to retrieve the starting point status.

Very quickly, I got feeback from ChatGPT.

Yes, that’s a great idea—and a cleaner and more modular design! Creating a dedicated MainTabStatus (or similarly named) helper class decouples the logic from your view controllers entirely, making it more flexible, easier to test, and extendable for future needs (like persistence).


✅ Suggested Design
  1. enum MainTab {
  2.  case edit
  3.  case tap
  4.  case report
  5. }
  6. final class MainTabStatus {
  7.  private static var _startingTab: MainTab = .tap  // default value
  8.  public static var startingTab: MainTab {
  9.    get { return _startingTab }
  10.    set { _startingTab = newValue }
  11.  }
  12.  // Optional: Save to UserDefaults if needed
  13.  public static func persist() {
  14.    UserDefaults.standard.set(_startingTab.rawValue, forKey: "StartingTab")
  15.  }
  16.  public static func restore() {
  17.    if let raw = UserDefaults.standard.string(forKey: "StartingTab"),
  18.       let tab = MainTab(rawValue: raw) {
  19.          _startingTab = tab
  20.    }
  21.  }
  22. }

🔧 For the UserDefaults part to work, you'd make MainTab conform to RawRepresentable (usually with String), like this:

✅ Suggested Design
  1. enum MainTab : String {
  2.  case edit
  3.  case tap
  4.  case report
  5. }

✅ Usage in Any View Controller

Set at startup:

  1. MainTabStatus.startingTab = .edit

Read anywhere:

  1. let origin = MainTabStatus.startingTab

Restore from UserDefaults (if persisted):

  1. MainTabStatus.restore()

🧠 Alternative Names for MainTabStatus

If you want something shorter or more Swift-y:

  • AppNavigationContext
  • StartContext
  • OriginTab
  • TabTracker
  • MainTabTracker

MainTabStatus is already clear, so it's a solid choice.


Let me know if you'd like this helper to also handle notifications (e.g., broadcast when tab changes), or if you want a sample using @AppStorage (SwiftUI), too!

Wow! What a great set of suggestions from ChatGPT—starting from a simple question about naming a variable, and evolving into a much better and scalable design.

Other Blogs

Read More...