I read books I have scanned myself. I wanted a more comfortable way to read those PDFs on my iPad.
My main frustration was how PDF readers handled right-to-left reading and cover pages. I wanted the cover to appear on its own, followed by two-page spreads. When a PDF contained reading-direction and page-layout settings, I wanted the reader to use them automatically.
Some free viewers I tried offered related options, but I could not find one that opened my PDFs the way I expected. So I started building a reader for my own needs.
The app is called MihirakiPDFViewer in English and 見開きPDFビューア in Japanese. Mihiraki is a Japanese word for the two facing pages of an open book.
This is a look at what happened after I began asking AI to write the code: repeated layout fixes, problems discovered through daily use, tests with predictable results, and checks on the version prepared for App Store submission.
Figure 1. An earlier build displaying an English sample PDF in two-page view.
Returning to iOS development after almost ten years
I had developed and published an iOS app independently in 2016. However, almost ten years had passed. This time, I was working with SwiftUI for the interface and PDFKit for PDF handling.
I began development in July 2026. My initial workflow involved two computers:
- On Windows, I used Cline with a local Gemma model running through LM Studio to generate and modify code.
- On a Mac, I built the app in Xcode and checked it in the Simulator and on a physical iPad.
From August 15, I used Gemma for code changes and ChatGPT for bug fixes. On August 25, I moved the development work entirely to ChatGPT, continuing with Codex inside Xcode.
In my setup, dealing with syntax errors in generated code made it difficult to see when I would finish. That was my reason for changing tools, rather than the result of a controlled comparison between models.
The tools changed, but the cycle remained much the same: describe the behavior I wanted, change the code, build the app, use it, and decide what to ask next.
“Fixed” was the beginning of another check
Two-page layout required repeated corrections.
In an early version, the pages overlapped. I reported the problem and received a response saying it had been fixed. When I ran the app again, the pages were still misaligned, but in a different way.
Other attempts left an unwanted gap between the pages or produced awkward margins. “Display two pages side by side” turned out to leave several questions unanswered.
Where should their inner edges meet? How should pages of different sizes fit into the available space? Which dimensions should determine the scale?
I added debugging information showing dimensions and scale values. My requests became more specific: preserve the aspect ratio, fit the pages within the available area, and place their inner edges together at the center.
Figure 2. The same-sized pages with an unwanted gap (top) and with their inner edges meeting at the center (bottom). This schematic illustrates the layout requirement; it is not a reconstruction of a historical screenshot.
Right-to-left reading added another distinction. The left and right positions within a spread, the sequence of spreads, and the initial reading position all needed checking. Swapping two pages did not settle the whole problem.
I learned to describe a bug through its conditions and expected result. An AI completion message was useful, but I still needed to open the document and inspect the output.
Daily use changed requirements I had written myself
Once the basic features worked, I continued reading with the app on my iPad almost every day.
One issue was rendering quality. PDFs looked less sharp than they did in other apps. I requested high-quality rendering and sharpening options. After that change, even the normal rendering mode looked better.
The code changes showed that the rendering path used by normal mode had also changed. However, I had not preserved before-and-after images under identical conditions. I could identify the implementation change and describe my observation, but I could not establish the exact cause of the improvement.
Another issue came directly from my own instructions.
Initially, I had asked for the PDF to shrink when the reading controls appeared. The app did that, but while reading, I found the changing text size distracting. I reconsidered the interface layout.
Testing was therefore about more than finding implementation mistakes. It also helped me discover when a requirement I had chosen did not feel good in practice.
Turning “it works” into something repeatable
At first, I tested with PDFs of books I had scanned. I could not distribute those files with the source code, so I needed test documents that other people could use too.
I prepared shareable PDFs with known properties:
- Reading-direction and page-layout settings.
- Search terms with known occurrence counts.
- Large file sizes and high page counts.
A document containing a search term 600 times gives a test a concrete expected result. A right-to-left document with a cover gives it an expected sequence of page arrangements.
Instead of asking only whether a feature worked, I could specify a file, an operation, and an expected outcome.
Reading-session restoration provided another useful example. Switching to other apps and returning preserved my page. Ending the app’s process and relaunching it returned me to the file picker.
Figure 3. A schematic based on historical Simulator test records. The process was terminated for the test; an actual operating-system termination caused by low memory was not reproduced.
The state could remain available while the process was alive, but saving and restoring it across launches required additional work. That distinction led to changes for restoring the document and reading position.
I also discovered that the rendering options reverted to their defaults after relaunching. Being able to turn a setting on was only one part of its behavior.
These automated tests were performed in the Simulator at the time. Their scope mattered. Matching search counts did not prove that highlights appeared at the correct coordinates. Successfully importing a local file did not establish that the complete iCloud Drive interaction had been tested.
Keeping those boundaries in the records helped me understand what still needed checking.
Submission preparation expanded the work beyond code
Preparing for App Store submission made me think about someone opening the app without having my documents or development context.
I created shareable sample PDFs. In the development version, I also added a way to download and open them from inside the app.
The sample needed to match the task. A PDF made entirely from images could demonstrate two-page viewing, but it could not demonstrate text search. A useful-looking example was not automatically useful test data.
I also kept the submitted version separate from ongoing development. Changes to the working code did not update the binary already submitted for review.
While waiting for review, I expanded my checks to a saved distribution file and the corresponding source. That inspection revealed additional work involving an in-app privacy-policy link and a privacy manifest.
Apple did not identify those findings as the reason for the extended review. The lesson I took from the inspection was to check the contents of the packaged app prepared for submission, as well as the submission status.
Review status checked on November 3, 2026 (Japan Standard Time): App Store Connect still showed version 1.0.4 (build 10) as “In Review.” The three supporter-icon in-app purchases submitted alongside it were also “In Review.”
This status update is based on a direct check of App Store Connect. The development experiences described above cover the earlier work documented through October 27, 2026.
What I would carry into my next project
AI helped me create and modify code, add tests, and consider improvements. Throughout the process, I continued using the app, describing unexpected behavior, and checking the results of changes.
For my next small app, I would establish three habits earlier:
- Record problems with reproduction steps. Include the document, actions, observed behavior, and expected result.
- Prepare shareable test inputs. Known inputs and outcomes make repeated checks more useful.
- Record the version and environment. Distinguish development builds from submitted artifacts, and Simulator results from physical-device checks.
I did not have all of this in place at the start. My development process improved alongside the app, as each problem gave me a reason to make the next check more precise.
What problems have you discovered only after using your own app in everyday life?
This article was adapted and reorganized from my Japanese Qiita development series. The source repository includes changes made after some of the development stages described here.
I used generative AI to help structure, draft, and translate this article. It is based on my development experience and a review of historical code and work logs.



Top comments (0)