Perhaps very few people still remember GPA.ZJU. This app was officially launched on the AppStore on October 24, 2013, and after two long years, it has survived to this day. During this period, it went through two major version updates (the latest 3.0 version is currently under review for release), and it also experienced several major crash incidents. The longest-lasting crash has continued until now; because of my own personal reasons, I have never managed to update it, and I feel very sorry about that.
At first, the main functional goal of GPA.ZJU was to implement push notifications for new grades. After every exam week ends, there are always some people refreshing their grades every moment (regardless of whether they are top students, poor students, or somewhere in between). This is a very painful thing. As a developer who firmly believes that “anything that can be solved with code should never be done manually,” I chose to write a program to ease this pain. At that time, I had just switched to an iPhone, so I resolutely set foot on the road of iOS development—a road of no return.
Two months later, an initial and very rough version was born. I had originally planned to just entertain myself with it, but later, in the spirit of helping others, I paid the annual developer fee out of my own pocket and put it on the AppStore. During that time, I received a lot of encouragement, as well as some doubts. Two years of development experience turned me from a boy on a pleasure tower into a grown man on a traveler’s boat. It was full of setbacks, but it also gave me some experience and insights, and I hope to take this opportunity to share them with everyone. The content of this article is fairly basic and not very well organized; I’ll just talk about whatever comes to mind.
Before you start reading this article, I hope you can download the iOS version of GPA.ZJU so that you know what I’m talking about...
In the following text, I will start with three questions—
- How to build a GPA.ZJU
- How to get started with iOS development
- Let me tell you a story
1. How to build a GPA.ZJU
GPA.ZJU is divided into two parts: the iOS side and the server side. The iOS side handles data presentation and user interaction; the server side is responsible for data exchange and storage, as well as communication with the academic affairs website.
1. Basic idea
First of all, the academic affairs website definitely will not provide an interface for you to call. The only thing you can do is construct HTTP Post requests yourself to request data from the academic affairs website, and then parse that data.
This is a very simple process. You only need to open the network panel in the browser inspector when logging into the academic affairs website, click login, and you will see a Post request similar to the one below.

Post request
Construct a similar request in code, and after sending it successfully, you can obtain the cookie as well as the data returned by the server (HTML code). The verification code on Zhejiang University’s academic affairs website has a rather serious bug: if you remove this field in the request you construct, you no longer need the verification code and can log in directly, which greatly reduces the difficulty of development.
After logging in successfully, you can use the same method to obtain grades and exam information. You can then use regular expressions or an existing open-source Parse library online to parse the data and generate the data you need.
One thing to note here is the _VIEWSTATE field. The academic affairs website uses this parameter to track changes in the values of various fields in forms as pages switch. You need to parse out the _VIEWSTATE value from the current page in your POST request, then place it into the new form data (Form Data) and send the request. The official explanation of _VIEWSTATE can be found here.
For Mac users, I recommend a very useful piece of software: Paw. Paw makes it very convenient to construct requests and is very easy to use for API debugging.
2. Framework structure design
Across the three major version updates, I used the structure in Figure 1 for versions 1.0 and 2.0, and the structure in Figure 2 for version 3.0.

Framework 1

Framework 2
Obviously, for the academic affairs website (which often changes inexplicably and is not standardized), the structure in Figure 2 is more suitable. If an error occurs, developers only need to modify the code on the server and redeploy it; there is no need to modify the App code anymore. After all, modifying an App, submitting it for review, and having the review approved is a very long process. If everything goes smoothly, it takes a little more than a week; if the submission is rejected, the time required can multiply. Of course, there are also some other methods that can be used in emergencies, taking advantage of Objective-C runtime features to modify the code of an iOS app to achieve the purpose of Hot Patch. I will briefly introduce that later.
3. Technology selection
After 3.0, my goal was to turn GPA.ZJU into a permanently usable product (after all, I’m already a senior now). Even if one day I no longer spend a lot of time maintaining the code, if a serious Bug appears, I can still fix it in a very short time. Besides that, since this is a non-profit project, the choice of server is also very important.
Here I want to strongly recommend a platform—LeanCloud. “LeanCloud is a one-stop solution for accelerating app development, focused on providing first-class tools, platforms, and services for app developers.” It mainly provides services for data storage, real-time messaging and push notifications, and analytics. In terms of functionality, LeanCloud is very similar to the overseas Parse, but the cloud engine feature released recently (supporting Python and Node.js) can satisfy most of your basic server needs. See the official website for details.
That is to say, you can get a server in a sandbox for free or at a very low price. If you want, you can interact with the server without writing any server code at all, or even modify the database contents directly (not recommended). Given so many excellent features of LeanCloud (I won’t say it’s because it’s free), I chose LeanCloud as the backend server for GPA.ZJU and wrote the interfaces using node.js with the express framework.
The reason I do not recommend directly modifying the database, as mentioned above, is this: if you do that, the coupling between the App and LeanCloud becomes too high. If LeanCloud goes out of business in the future (isn’t Parse shutting down?), and you want your App to migrate away from LeanCloud, the cost will be enormous.
On the App side, you only need to wrap the interface calls. If you ever need to switch servers, you only need to modify the server address.
4. iOS-side solution
4.0 The design concept of the latest 3.0 version of the App is a newspaper style, using black and white as the two color types to fill all content. This concept was proposed by Messizon, the design director of my company (Catch Photography), while the design inside was mainly done by me. After all, I’m a programmer, so if there is anything you feel looks bad or feels awkward, please be tolerant, or go complain about it in the App.
4.1 The entire App follows the MVC architecture. Don’t know MVC? The MVC framework divides an application into three parts: Model, View, and Controller. Model is mainly responsible for providing data to the ViewController and providing interfaces for ViewController to store data; View is mainly responsible for responding to events unrelated to business logic (animations, tap feedback) and presenting interface elements; Controller mainly manages the lifecycle of the ViewContainer, is responsible for generating all View instances and placing them into the ViewContainer, listens for business-related events from the View, and works with the Model to complete the corresponding business logic.
4.2 Use CocoaPods to manage third-party libraries. CocoaPods is a very popular dependency management tool that allows you to integrate and update third-party libraries very easily. See the official website for details.
4.3 The project directory structure is as follows:

Directory
Macro contains files similar to define; Category contains class Categories; Vender contains some third-party libraries not included in CocoaPods; Helper contains animations or other miscellaneous files; UI contains Storyboard; Layout contains some layout files, such as the layouts used by UICollectionView; Support contains images and other files.
4.4 Write the UI using a combination of Storyboard and code. “Which of these three methods—Storyboard, Xib, or code—should be used to write View” is a question that is always debated and never answered. It must be said that Storyboard, as a method recommended by Apple, really did reduce a lot of my workload. However, Storyboard has one drawback: in team collaboration, if multiple people modify the Storyboard at the same time, conflicts are easily generated. My suggestion is to divide the application UI into several different Storyboards according to functional modules, with each Storyboard performing its own duty. If there are some shared Views across several Storyboards, you can consider placing them in the View directory in the form of xib files. In addition, I personally recommend lightweight Storyboards—that is, implement complex transitions between ViewControllers in code rather than dragging segues in Storyboard. This can make the Storyboard much clearer (without lines flying everywhere), and at the same time make the logic for page transitions much simpler. Some even simpler Views, I implemented directly in code.
4.5 The entire App is centered around a single NavigationController throughout. I modified the animations of push and pop transitions to produce the current effect. The small square in the upper-right corner of the home page is placed above the NavigationController’s view. The implementation of that small square uses UIKitDynamics, newly introduced in iOS 7. You can read this blog post for an introduction.
4.6 Hot deployment solution
GPA.ZJU uses JSPatch. “JSPatch is an open-source project. You only need to include a very small engine file in your project, and then you can use JavaScript to call any native Objective-C interface and replace any native Objective-C method.” In other words, if the App online happens to have some Bug, I can inject a piece of code to replace a native method and achieve the purpose of fixing the bug. Version 3.0 went online yesterday, and because the pre-release testing was insufficient, a situation occurred where the general education data was missing one category. I successfully fixed this Bug through JSPatch.
5. Server-side solution
I used the Express framework of Node.js to write the backend of GPA.ZJU and deployed it in LeanCloud’s cloud engine. For some small applications, LeanCloud really has provided me with a lot of convenience (they really should pay me advertising fees here). From then on, server maintenance became very simple.
The server implements the following interfaces:
/auth: login authentication
/exam: get exam information
/grade: get grade information
/mix: get information combining exam and grade data (mainly for the App)
/push: view and modify push status
The principles for obtaining login, exam, and grade information were already mentioned in the introduction at the beginning, so I won’t repeat them here. After obtaining the information, the server parses the data and generates a JSON-formatted string to return to the App. The App can then convert it into modules as needed and call them.
2. How to get started with iOS development
The steps I personally recommend are:
- Read the documentation for Objective-C (or Swift) and understand the basic syntax.
- Learn about the relevant Cocoa frameworks. I recommend the book Pro iOS Development (7th Edition) and the video course Stanford Open Course: iOS 7 Application Development (NetEase Open Course).
- Choose a project you want to build and start development from that project. Google will tell you the answers to all your questions. My first project was GPA.ZJU. At that time, I spent an entire summer vacation building it, and Bugs often appeared (the code was written like shit), but this process from 0 to 1 still gave me a great sense of achievement.
- Read through the implementation of a complete project. There are many complete open-source projects on Github, and you can pick one you like by looking from top to bottom according to the number of Stars.
I think there are some Tips that are important:
- Don’t reinvent the wheel. Or rather, before reinventing the wheel, you must first browse the code of some open-source libraries with similar functionality and understand how they are implemented. Otherwise, what you write may have no practical value at all.
- Read some iOS-related blogs. Their understanding of a problem is often very deep, and you can learn a lot.
- Follow some developers’ Weibo accounts. Every day they share fun, useful, and practical things.
- Once you reach a certain level, don’t take on too many projects or outsourcing jobs. They will consume a huge amount of your time, to the point that you won’t have the energy to study new technologies, and that will hinder your progress. Remember, you are a programmer, not a skilled laborer.
Some resources:
Github collection: https://github.com/Aufree/trip-to-iOS
Some Weibo accounts: @iOS程序犭袁@hangcom2010 @我就叫Sunny怎么了@移动开发小冉 @叶孤城_ @唐巧_boy @onevcat @周楷雯Kevin@雷纯锋2011@汤圣罡 @KITTEN-YANG @程序媛念茜
Some blogs: 唐巧, OneV's Den, casatwy, beyondvincent
By the way, the iOS code of GPA.ZJU has already been open-sourced on my Github. I removed some LeanCloud-related information. If you have any questions, please send me an email or open an issue.
3. Let me tell you a story
Once there was a boy who loved computers very much from a very young age.
He did many things. For example, during junior high school computer class, in order to prevent others from competing with him for network speed, he caused other people’s computers to blue-screen. Of course, the vulnerability he used was also very old and simple.
He drove an excavator to dig for website vulnerabilities, but because his skills were not good enough, he did not get the key to open the door.
He also wrote a VB program for a girl he liked, added a password to it, and sent it to her.
The girl was very touched, but in the end, she did not choose him.
And so, after all that messing around, in the summer of 2012, he entered university, feeling very lost.
Because he had focused on physics competitions in high school, he could not compete in algorithms with classmates who had done OI. In high school, he had been called a genius, yet he had not mastered one or two technologies in any real depth, and this troubled him greatly.
His daily life was: the whole dorm playing LOL together, messing around with code, messing around with homework, and staying up all night before exams.
He had experienced taking calculus exams without reviewing, and he had also experienced teaching himself an entire linear algebra book in one week.
Well, the result was of course disastrous. It wasn’t so bad that he failed courses, but his grades were ugly.
In this miserable way, he got into a CS-related major as he had wished and began his journey as a coder.
That coder journey went fairly smoothly, perhaps because he was a little bit smart.
During the summer after freshman year, he started learning iOS development because of a certain need. He felt that being able to make something he wrote run on a phone was a very happy thing.
The facts proved that it really was very happy.
If life contains many regrets, then learning iOS development has never belonged to that set.
After that, he did many projects, took some outsourcing jobs, participated in some competitions, and received some honors and awards.
He joined a startup company and grew together with it; the company has now reached Series A.
The founder is very reliable. Through communication with him, the boy’s playful personality improved somewhat, and he also came to understand what responsibility means.
When he was little, like everyone else, he would always ask himself, “In the future, should I go to Tsinghua or Peking University?”
But regarding one thing, he never changed—“go abroad.”
A friend asked:
“What are you planning to do after graduation?”
“Go abroad.”
“Have you taken the TOEFL?”
“...”
“What about the GRE?”
“...”
“How’s your GPA?”
“Why do you talk so much!”
This kind of conversation continued all the way until September 2015...
At that time, he finally realized how difficult English was.
If you ask which period in his four college years made him break down the most, it was without question the three months spent preparing for English.
Every morning he got up memorizing English, every night he went to sleep memorizing English; more than ten hours a day were related to English.
Somehow, someone also gave him the pressure of “not being able to go abroad.”
So he was in great pain—more painful than looking at a 1000-line function with no comments, more painful than not being able to find a bug.
He took the Toefl many, many times, and the GRE many, many times; before every exam, he was so sleepless that he stayed awake until dawn.
Perhaps heaven was merciful: in December, his GRE finally broke 320, and his Toefl finally broke 100, though his speaking score was 18...
He was very happy and began applying, preparing essays, filling out forms, and submitting scores.
He had an average GPA, a TOEFL score above 100, a GRE score above 320, some rather mediocre small-scale research, some competition awards, a startup experience, and one useful skill.
He thought he was about to reach the pinnacle of life...
However, reality was still very cruel. Up to now (March 5, 2016), he still had not received an Admission.
Yes, he was still in a state of having nowhere to study.
He began reflecting on his applications, reflecting on his grades, and reflecting on his personal statements.
Why couldn’t the GPA have been a little higher!
Why didn’t he practice speaking more before!
Why did he so confidently want to switch directions in the personal statement!
Why didn’t he do some research before!
Well, he had already thought it through: if he ended up with nowhere to study, he would give himself a break and go out to see the world.
Perhaps one day, his footprints will be found in many corners of this world...
The main point is to tell everyone that if you want to get into a good school, start preparing your English early (speaking at least 22), make sure your grades are good, have some research experience, and have some strengths. (In order of priority.)
Once you achieve those, the Offer will naturally come.