The process of me making an app

Category

Welcome Back!

Hello everyone, long time no see! It's finally summer vacation, and I have two major goals during this break.

The first is to achieve a great score on the TOEFL exam — a real challenge for me. The second is to kick off a project that I'll be working on in the next semester.

About My Project

In this post, I'd like to share something about my project and what I've accomplished so far.

My project is to develop an app designed to help people with anaphylactoid purpura track their diet, symptoms, and share personal experiences or medical knowledge about the condition. Beyond simple logging, the app uses AI to provide intelligent suggestions and analyses based on user-submitted data.

I'm working on this project with one of my classmates, who also suffers from purpura. She hopes this app can be a meaningful tool for others facing the same condition.

Public Testing & Source Code

I'm excited to announce that the app is now available for public testing on Apple's TestFlight. You can try it out using the following link:

https://testflight.apple.com/join/dZQy4fKN

If you have any suggestions or find any bugs, feel free to contact me — your feedback is truly appreciated.

Cross-Platform Challenges

Before developing the app, I struggled with the question of how to efficiently support multiple platforms. iOS uses Swift, while Android uses Kotlin — learning and maintaining both would be quite difficult. While exploring solutions, I discovered Google's Flutter framework. However, I found the learning curve a bit steep. Then, I had an idea: since I'm already familiar with HTML, why not create a web app and embed it into a native app? This would drastically reduce my workload and development time. As a result, the current version of my app follows this hybrid structure. The core code in Xcode is very simple, I generated it directly using artificial intelligence.

The code I wrote on the Apple side is very simple, I can just put it out:

import UIKit
            import WebKit
            
            class WebViewController: UIViewController {
              private var webView: WKWebView!
            
              override func loadView() {
                let webConfiguration = WKWebViewConfiguration()
                webView = WKWebView(frame: .zero, configuration: webConfiguration)
                view = webView
              }
            
              override func viewDidLoad() {
                super.viewDidLoad()
            
                // 设置导航代理以处理加载失败
                webView.navigationDelegate = self
            
                // 加载线上地址
                if let url = URL(string: "https://app.zdelf.cn") {
                  let request = URLRequest(url: url)
                  webView.load(request)
                }
              }
            }
            
            // MARK: - WKNavigationDelegate
            extension WebViewController: WKNavigationDelegate {
              func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
                showErrorAlert(error: error)
              }
              
              func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
                showErrorAlert(error: error)
              }
              
              private func showErrorAlert(error: Error) {
                let alert = UIAlertController(title: "加载失败", message: error.localizedDescription, preferredStyle: .alert)
                alert.addAction(UIAlertAction(title: "确定", style: .default))
                present(alert, animated: true)
              }
            }

Project Begins: Creating the UI

In the early days, I deployed my project directly using Cloudflare Pages, as I only needed to write simple UI elements and didn't need to run code. Just like this website, when writing the UI, I simply wrote some HTML, and the CSS code was completely generated by AI. I have to say, AI is really useful these days. During this process, I would repeatedly let the AI optimize the UI to make the page look good. In my opinion, UI is more important than functionality, as a good UI makes my website pleasing to the eye. During this process, since I didn't have a ChatGPT membership, I registered several email addresses to use.

Making the backend: user registration and login

After completing the UI, the first thing I needed to do was to implement user login functionality. I previously built a WebUI for a project on a Raspberry Pi using the Python Flask library, so this time, I planned to use Flask as my backend. I used a MySQL database to store user data. The overall process was as follows: I ran the Flask code on a server. The Flask code opened a port and contained multiple routes. I used JavaScript to access the corresponding routes on this port to manipulate the database.

Switching from http to https

Originally, I planned to use the app to access the server via its public IP address to retrieve data. However, I later read that Apple requires all network requests made by apps to use the HTTPS protocol; using HTTP is highly likely to result in rejection during the app review process. As a result, I decided to register a domain name. My primary domain is hosted on Cloudflare and hasn't undergone ICP registration in China, which means it cannot be linked to a public server hosted in the country. Registering a new domain and completing the ICP approval process would have taken several weeks. Then I remembered that I had previously registered a domain name for a website I was planning to build for people with disabilities. Unfortunately, the project never moved forward because my friend, who was supposed to collaborate with me, backed out. So I repurposed that domain name and pointed it to my server. I then installed an SSL certificate using Let's Encrypt, which enabled access to the site over HTTPS. However, my backend—built using the Flask library—was still configured to serve over HTTP. I switched it to HTTPS, but ran into a series of problems, such as CORS-related issues, which took a considerable amount of time to debug and resolve, with help from AI tools.

The biggest difficulty so far

After deploying the backend services and setting up HTTPS encryption, I planned to display something like "Username + Greeting" at the top of the homepage. Sounds simple, right? All I had to do was use the frontend JavaScript to request a request from the backend server, which would query the database and return the result. But honestly, this was the most challenging part of the project, and it took me a significant amount of time. Initially, the entire page was stuck loading. Even adding timeout logic to the JavaScript file had no effect. I began to suspect my JavaScript wasn't running at all. I opened my browser's developer tools and, sure enough, found that the browser wasn't loading daily.js. After consulting AI, I realized that my daily.html page was dynamically loaded from index.html, so I had to manually load daily.js in index.js. In theory, this should have fixed the problem. However, strangely, even though daily.js was loading, the code wasn't executing. I added debugging information to the code and realized the problem lay in event binding: the AI-generated code relied on the DOMContentLoaded event. Since daily.js was dynamically loaded, the event had already been triggered by the time it loaded, so the code wasn't executed at all. I ultimately changed the logic to run the initialization code directly whenever the event was triggered, and the problem was resolved. The AI's help during this process was limited. It could only statically analyze a few files and had no understanding of the dynamic loading structure of my code, nor could it determine the specific timing of event triggering. Ultimately, it was through my own analysis and debugging that I finally identified the true cause of the problem.

Integrating DeepSeek: Building an AI Assistant

After that, I planned to introduce some AI features to my website. First, I wanted to integrate an AI assistant that users could use to ask questions. DeepSeek has been very popular in China recently, offering powerful features and a relatively low price, so I chose to use its API. I wrote a Python script that uses Flask to provide a routing interface. When the front-end user enters and submits content, the JavaScript sends the input to my back-end server. Upon receiving the request, the back-end uses DeepSeek's API to retrieve the corresponding response, returning the result to the front-end for display on the page. The entire process was very simple, and DeepSeek also provides detailed API documentation, so the development process was relatively straightforward.

Designing the Core: User Data Collection and AI Analysis

Okay, now that the preliminary preparations are complete, the AI has been invoked, and all the pages have been set up, I can move on to the core content of the website. The core of our software is to analyze user-recorded data, so the first step in building the website is, of course, to record that data. In this version of the concept, we need three types of data: time, diet, and physical condition. My idea is to have a date selection window on the top of the page. The default date is the current date, which the user can change if they wish. Below is an input box where the user can enter anything, and the AI will analyze "which are diet and which are physical conditions." I also added a route using the Flask library to request DeepSeek to process this data. DeepSeek returns data in a JSON format encapsulated in Markdown. After removing the ``` markdown encapsulation in the Flask library, I return it to the front-end for display.

Make my project safer

Recently, I came across a video about SQL injection online, and it immediately made me realize that my SQL database might have security vulnerabilities. Previously, I was accessing the database by concatenating strings on the frontend—a very risky approach. So, I quickly updated the backend logic to use %s placeholders to prevent injection attacks. After making that change, another thought struck me—my DeepSeek API key and database password were written in plain text directly in my Python code! To make matters worse, the entire project was open-sourced on GitHub. That sent a chill down my spine, and I took immediate action: I moved all sensitive information into a .env file and added it to .gitignore. Finally, I regenerated both the API key and the database password to completely close this security loophole. Although I believe that, at this stage, no one would bother attacking my database—after all, the project is still small and the DeepSeek API account only has about 10 yuan in it—I firmly believe that cybersecurity awareness must always be maintained. Waiting until something goes wrong to take action is simply too late.

A Complete UI Overhaul

1:00 AM, August 8th, Beijing time. OpenAI had just released GPT-5, and I couldn't wait to give it a try. The result? Its capabilities went far beyond my expectations. Naturally, I handed it the task of revamping my project's UI. Just a few hours later, a brand-new interface was born: clean layouts, well-defined hierarchy, and animations flowing as smoothly as water. Although all my daily devices are made by Apple, I've never been particularly fond of Apple's design language—especially the liquid-glass effect introduced in iOS 26. It's flashy, but not quite my style. In contrast, I've always preferred Google's flat design: simple, restrained, and timeless. That's why this UI overhaul embraces Google's visual language entirely—and the outcome turned out even better than I imagined.

Next Steps: User Information Management

Next, I plan to implement user information management. Since all data generated by the app needs to be written to the database—and each user must be precisely distinguished—this step is crucial.

I used an advanced GPT‑5 model to generate a beautiful UI and added several buttons. Some of these buttons don't have functionality yet, but I expect they'll be useful in future iterations.

Implemented Features

  • Log out: Simply remove the userid and redirect to the login page's HTML.
  • Edit age & password: First, I read the current data via readdata and display the user's age in the UI. The user can then modify their age and password, and I compare the new input with the data from readdata to prevent setting an identical password. Finally, I submit the changes to the server through my new editdata route.

Security Note

I identified a security issue: the server currently returns data—including passwords—in plaintext. I can't fully fix this immediately because the frontend relies on that data for some logic. However, I'll address this by moving all password-related checks to the cloud so that plaintext passwords never appear on the frontend.

Implementing SMS-Based Registration & Login with Alibaba Cloud

One thing I really wanted to improve was making the registration and login process more convenient and secure for users. I thought about how much easier it would be if users could just log in with their phone number, without having to remember a password. SMS verification not only simplifies the login flow but also helps confirm that the user actually owns the phone number they're registering with.

After researching several options, I decided to use Alibaba Cloud's SMS service. Their documentation was clear and the pricing was reasonable, which made the decision pretty straightforward.

In China, using SMS services for things like login requires going through three main steps: submitting your qualifications, applying for a signature, and creating a template for the messages. At first, I thought this process would be complicated, but it turned out to be much smoother than expected.

For the qualification step, I used my father's company. I uploaded my ID and the company's business license as required. To my surprise, the approval came through in just two hours.

The next step was applying for a "signature"—which is basically the company name that appears as the sender in the SMS messages. This required an authorization letter stamped with the company's official seal. Alibaba Cloud reviewed this very quickly as well, but then there was a five-day period for the mobile carriers to integrate the new signature. During this time, I periodically sent test messages to check on the progress, which made the wait feel a bit less long.

After that, I created the SMS message template. This step was even faster, and the approval came almost immediately.

With everything set up, I wrote a new sms route in my Flask backend. I split it into two subroutes: send for sending the verification code to the user's phone, and verify for checking the code the user enters. I also updated my users table in the database to include a phone_number column, so users could be uniquely identified by their phone.

Honestly, the whole process was much less daunting than I had imagined. Alibaba Cloud's documentation was thorough, and their APIs were fast and reliable. Adding SMS-based registration and login made the app feel more professional, and it was satisfying to see it all come together so smoothly.

The process of making the app is not over yet, I will continue to update this passage, thank you, Because this post is already quite long, I will open a separate page for the next post. You can click here to view the next article.

我做App的过程

目录

欢迎回来!

大家好,好久不见!终于放暑假了,这个假期我有两个主要目标。

第一个是在托福考试中取得好成绩——这对我来说是个真正的挑战。第二个是启动一个我将在下学期继续推进的项目。

关于我的项目

在这篇文章中,我想分享一些关于我的项目以及我目前取得的进展。

我的项目是开发一款App,旨在帮助过敏性紫癜患者记录饮食、症状,并分享个人经验或关于该疾病的医学知识。除了简单的记录功能,这款App还利用AI根据用户提交的数据提供智能建议和分析。

我和一位同学一起做这个项目,她也患有紫癜。她希望这款App能成为帮助其他同样面临这种疾病的人的有意义的工具。

公开测试与源代码

我很高兴地宣布,这款App现在已经可以通过Apple的TestFlight进行公开测试。你可以通过以下链接来试用:

https://testflight.apple.com/join/dZQy4fKN

如果你有任何建议或发现了Bug,欢迎联系我——非常感谢你的反馈。

跨平台挑战

在开发App之前,我一直在纠结如何高效地支持多个平台。iOS使用Swift,而Android使用Kotlin——同时学习和维护两者确实很困难。在探索解决方案的过程中,我发现了Google的Flutter框架。但我觉得学习曲线有点陡峭。然后,我有了一个想法:既然我已经熟悉HTML,为什么不创建一个Web应用然后嵌入到原生App中呢?这将大幅减少我的工作量和开发时间。因此,当前版本的App采用了这种混合结构。Xcode中的核心代码非常简单,我直接用人工智能生成的。

我在Apple端写的代码非常简单,我可以直接放出来:

import UIKit
            import WebKit
            
            class WebViewController: UIViewController {
              private var webView: WKWebView!
            
              override func loadView() {
                let webConfiguration = WKWebViewConfiguration()
                webView = WKWebView(frame: .zero, configuration: webConfiguration)
                view = webView
              }
            
              override func viewDidLoad() {
                super.viewDidLoad()
            
                // 设置导航代理以处理加载失败
                webView.navigationDelegate = self
            
                // 加载线上地址
                if let url = URL(string: "https://app.zdelf.cn") {
                  let request = URLRequest(url: url)
                  webView.load(request)
                }
              }
            }
            
            // MARK: - WKNavigationDelegate
            extension WebViewController: WKNavigationDelegate {
              func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
                showErrorAlert(error: error)
              }
              
              func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
                showErrorAlert(error: error)
              }
              
              private func showErrorAlert(error: Error) {
                let alert = UIAlertController(title: "加载失败", message: error.localizedDescription, preferredStyle: .alert)
                alert.addAction(UIAlertAction(title: "确定", style: .default))
                present(alert, animated: true)
              }
            }

项目开始:创建UI

在早期,我直接使用Cloudflare Pages来部署项目,因为我只需要写一些简单的UI元素,不需要运行代码。就像这个网站一样,写UI时我只需要写一些HTML,CSS代码完全由AI生成。不得不说,现在的AI真的很好用。在这个过程中,我会反复让AI优化UI,使页面看起来更美观。在我看来,UI比功能更重要,因为好的UI让我的网站赏心悦目。在这个过程中,由于我没有ChatGPT的会员,我注册了好几个邮箱来使用。

搭建后端:用户注册与登录

完成UI之后,我需要做的第一件事就是实现用户登录功能。我之前使用Python的Flask库在树莓派上为一个项目搭建过WebUI,所以这次我也计划使用Flask作为后端。我用MySQL数据库来存储用户数据。整体流程如下:我在服务器上运行Flask代码。Flask代码打开了一个端口,包含多个路由。我使用JavaScript访问这个端口上对应的路由来操作数据库。

从http切换到https

最初,我打算让App通过服务器的公网IP地址来获取数据。但后来我读到Apple要求App发出的所有网络请求都必须使用HTTPS协议;使用HTTP很可能会在审核过程中被拒绝。因此,我决定注册一个域名。我的主域名托管在Cloudflare上,没有在中国进行ICP备案,这意味着它无法绑定到国内的公共服务器。注册新域名并完成ICP审批过程需要好几周。后来我想起来,我之前为了给残障人士建一个网站注册过一个域名。不幸的是,那个项目因为本来要和我合作的朋友退出了而没有推进。所以我把那个域名拿来用了,指向了我的服务器。然后我使用Let's Encrypt安装了SSL证书,这样就可以通过HTTPS访问网站了。但是,我使用Flask库搭建的后端仍然配置为HTTP。我把它切换到了HTTPS,但遇到了一系列问题,比如CORS相关的问题,花了相当多的时间来调试和解决,当然也借助了AI工具的帮助。

目前最大的困难

在部署好后端服务并设置好HTTPS加密之后,我计划在主页顶部显示类似"用户名 + 问候语"的内容。听起来很简单,对吧?我只需要用前端JavaScript向后端服务器发送请求,查询数据库并返回结果。但说实话,这是整个项目中最具挑战性的部分,花了我很多时间。最初,整个页面卡在加载状态。即使在JavaScript文件中添加超时逻辑也没有效果。我开始怀疑我的JavaScript根本没有运行。我打开浏览器的开发者工具,果然发现浏览器没有加载daily.js。咨询AI后,我意识到我的daily.html页面是从index.html动态加载的,所以我必须在index.js中手动加载daily.js。理论上,这应该能解决问题。然而,奇怪的是,尽管daily.js已经在加载,代码却没有执行。我在代码中添加了调试信息,发现问题出在事件绑定上:AI生成的代码依赖DOMContentLoaded事件。由于daily.js是动态加载的,加载时该事件已经触发过了,所以代码根本没有执行。我最终修改了逻辑,在事件触发时直接运行初始化代码,问题就解决了。在这个过程中,AI的帮助很有限。它只能静态分析几个文件,无法理解我代码的动态加载结构,也无法判定事件触发的具体时机。最终,是通过我自己的分析和调试,才找到了问题的真正原因。

集成DeepSeek:构建AI助手

之后,我计划为网站引入一些AI功能。首先,我想集成一个AI助手供用户提问。DeepSeek最近在中国非常火爆,功能强大且价格相对低廉,所以我选择使用它的API。我写了一个Python脚本,使用Flask提供路由接口。当前端用户输入并提交内容时,JavaScript将输入发送到我的后端服务器。收到请求后,后端使用DeepSeek的API获取相应的回复,然后将结果返回给前端在页面上展示。整个过程非常简单,DeepSeek也提供了详细的API文档,所以开发过程相当顺利。

设计核心:用户数据收集与AI分析

好的,前期准备工作都完成了,AI已经接入,所有页面也都搭建好了,我可以开始做网站的核心内容了。我们软件的核心是分析用户记录的数据,所以搭建网站的第一步当然是记录这些数据。在这个版本的构想中,我们需要三类数据:时间、饮食和身体状况。我的想法是在页面顶部放一个日期选择窗口。默认日期为当天,用户可以根据需要更改。下面是一个输入框,用户可以输入任何内容,AI会分析"哪些是饮食,哪些是身体状况"。我还使用Flask库添加了一个路由来请求DeepSeek处理这些数据。DeepSeek返回的数据是封装在Markdown中的JSON格式。在Flask库中去除```Markdown封装后,我将其返回给前端展示。

让我的项目更安全

最近,我在网上看到了一个关于SQL注入的视频,这立即让我意识到我的SQL数据库可能存在安全漏洞。之前,我通过前端拼接字符串来访问数据库——这是一种非常危险的做法。所以,我迅速更新了后端逻辑,使用%s占位符来防止注入攻击。做完这个改动后,另一个想法又涌上心头——我的DeepSeek API密钥和数据库密码竟然以明文直接写在Python代码里!更糟糕的是,整个项目还在GitHub上开源了。这让我不寒而栗,我立刻采取行动:将所有敏感信息移到了.env文件中,并将其添加到.gitignore。最后,我重新生成了API密钥和数据库密码,彻底关闭了这个安全漏洞。虽然我相信在现阶段,没有人会费心攻击我的数据库——毕竟项目还很小,DeepSeek API账户里也只有大约10块钱——但我坚信网络安全意识必须时刻保持。等到出了问题才采取行动,那就太晚了。

全面UI改版

北京时间8月8日凌晨1点。OpenAI刚刚发布了GPT-5,我迫不及待地想试一试。结果?它的能力远远超出了我的预期。自然而然地,我把改造项目UI的任务交给了它。仅仅几个小时后,一个全新的界面就诞生了:布局整洁、层次分明、动画流畅如水。虽然我日常使用的设备全是苹果的,但我从未特别喜欢Apple的设计语言——尤其是iOS 26中引入的液态玻璃效果。它很炫酷,但不太是我的风格。相比之下,我一直更喜欢Google的扁平化设计:简洁、克制、永不过时。这就是为什么这次UI改版完全拥抱了Google的视觉语言——效果甚至比我想象的还要好。

下一步:用户信息管理

接下来,我计划实现用户信息管理。由于App生成的所有数据都需要写入数据库——而且必须精确区分每个用户——这一步至关重要。

我使用了先进的GPT-5模型来生成漂亮的UI,并添加了几个按钮。其中一些按钮目前还没有功能,但我预计它们在未来的迭代中会派上用场。

已实现的功能

  • 退出登录:简单地移除userid并重定向到登录页面的HTML。
  • 编辑年龄和密码:首先,通过readdata读取当前数据并在UI中显示用户的年龄。然后用户可以修改年龄和密码,我将新输入与readdata的数据进行比较,以防设置相同的密码。最后,通过新的editdata路由将更改提交到服务器。

安全说明

我发现了一个安全问题:服务器目前以明文返回数据——包括密码。由于前端的某些逻辑依赖这些数据,我无法立即完全修复。不过,我会通过将所有密码相关的检查移到云端来解决这个问题,这样明文密码就永远不会出现在前端。

使用阿里云实现短信注册与登录

我真正想改进的一件事是让注册和登录过程对用户来说更方便、更安全。我想到,如果用户只需用手机号就能登录,不用记密码,那该多方便啊。短信验证不仅简化了登录流程,还能确认用户确实拥有他们注册的手机号。

在研究了几种方案后,我决定使用阿里云的短信服务。他们的文档清晰明了,价格也合理,这让决策变得很简单。

在中国,将短信服务用于登录等功能需要经过三个主要步骤:提交资质、申请签名和创建消息模板。一开始我以为这个过程会很复杂,但结果比预期顺利得多。

在资质提交这一步,我借用了我父亲的公司。我按要求上传了身份证和公司的营业执照。令我惊讶的是,审批只用了两个小时就通过了。

下一步是申请"签名"——基本上就是短信中显示的发送方公司名称。这需要一封盖有公司公章的授权书。阿里云的审核也非常快,但之后有一个为期五天的运营商接入新签名的时间。在这段时间里,我会定期发送测试短信来检查进度,这让等待感觉没那么漫长。

之后,我创建了短信消息模板。这一步更快,审批几乎是立刻通过的。

一切就绪后,我在Flask后端写了一个新的sms路由。我把它分成了两个子路由:send用于向用户手机发送验证码,verify用于验证用户输入的验证码。我还更新了数据库中的users表,添加了phone_number列,这样用户就可以通过手机号来唯一标识。

说实话,整个过程比我想象的要轻松得多。阿里云的文档很详尽,API也快速可靠。添加基于短信的注册和登录功能让App感觉更加专业,看到一切如此顺利地整合在一起也很有成就感。

做App的过程还没有结束,我会继续更新这篇文章,谢谢。由于这篇文章已经很长了,下一篇我会另开一页。你可以点击这里查看下一篇文章。