How the WebServer Serves the Preferences UI at localhost:62718 in HallelujahIM
The WebServer class wraps GCDWebServer to bind a local HTTP server to port 62718, serves static Vue.js files from the app bundle's web directory, and exposes a JSON API at /preference for reading and writing NSUserDefaults.
The dongyuwei/hallelujahim repository implements a lightweight preferences interface using an embedded web server. By navigating to http://localhost:62718/, users interact with a Vue.js application that communicates with the backend via a RESTful API. This architecture allows the input method editor to expose configuration options through a browser-based UI without external dependencies.
Starting the Local HTTP Server on Port 62718
The server initialization happens in src/WebServer.m using the GCDWebServer framework. The implementation hardcodes port 62718 and restricts binding to localhost only, preventing external network access.
static int port = 62718; // src/WebServer.m L20
options[GCDWebServerOption_Port] = @(port); // src/WebServer.m L71
options[GCDWebServerOption_BindToLocalhost] = @YES; // src/WebServer.m L72
[webServer startWithOptions:options error:nil];
This configuration ensures the preferences UI is accessible only from the local machine at http://localhost:62718/.
Serving the Static Preferences UI
Once the server starts, it registers a handler to serve static files from the web directory inside the app bundle. This directory contains index.html, index.js, vue.js, and index.css.
[webServer addGETHandlerForBasePath:@"/"
directoryPath:[NSString stringWithFormat:@"%@/%@", [NSBundle mainBundle].resourcePath, @"web"]
indexFilename:nil
cacheAge:3600
allowRangeRequests:YES]; // src/WebServer.m L36-L42
A request to http://localhost:62718/ returns index.html, which bootstraps the Vue.js single-page application.
Exposing the /preference API Endpoint
The server exposes a JSON API at /preference for reading and updating user settings stored in NSUserDefaults. The implementation handles both GET and POST methods in src/WebServer.m.
Reading Preferences (GET)
The GET handler returns the current boolean values for TRANSLATION_KEY and COMMIT_WORD_WITH_SPACE_KEY:
[webServer addHandlerForMethod:@"GET"
path:@"/preference"
requestClass:[GCDWebServerRequest class]
processBlock:^GCDWebServerResponse *(GCDWebServerRequest *request) {
return [GCDWebServerDataResponse responseWithJSONObject:@{
TRANSLATION_KEY : @([preference boolForKey:TRANSLATION_KEY]),
COMMIT_WORD_WITH_SPACE_KEY : @([preference boolForKey:COMMIT_WORD_WITH_SPACE_KEY])
}];
}]; // src/WebServer.m L43-L53
Updating Preferences (POST)
The POST handler receives a JSON body, writes the new values to NSUserDefaults, and echoes the payload:
[webServer addHandlerForMethod:@"POST"
path:@"/preference"
requestClass:[GCDWebServerURLEncodedFormRequest class]
processBlock:^GCDWebServerResponse *(GCDWebServerRequest *request) {
NSDictionary *data = ((GCDWebServerDataRequest *)request).jsonObject;
bool showTranslation = [data[TRANSLATION_KEY] boolValue];
[preference setBool:showTranslation forKey:TRANSLATION_KEY];
bool commitWordWithSpace = [data[COMMIT_WORD_WITH_SPACE_KEY] boolValue];
[preference setBool:commitWordWithSpace forKey:COMMIT_WORD_WITH_SPACE_KEY];
return [GCDWebServerDataResponse responseWithJSONObject:data];
}]; // src/WebServer.m L55-L68
Frontend Integration with Vue.js
The Vue.js frontend in web/index.html and web/index.js communicates with the /preference endpoint. On page load, it fetches current settings:
fetch("http://localhost:62718/preference") // get current settings
.then(r => r.json())
.then(pref => { this.preference = pref; });
When the user submits the form, the app POSTs the updated values back to the server:
fetch("http://localhost:62718/preference", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(this.preference)
})
.then(r => r.json())
.then(updated => { console.log("Saved:", updated); });
This creates a seamless loop where the browser-based UI reads from and writes to the application's NSUserDefaults via the local HTTP server.
Summary
- GCDWebServer powers the
WebServersingleton, binding tolocalhost:62718exclusively to prevent external access. - Static file serving from the
web/directory delivers the Vue.js application at the root path/. - RESTful API at
/preferencehandles GET requests to readNSUserDefaultsand POST requests to update boolean settings for translation and commit-word-with-space features. - Vue.js frontend in
web/index.jsconsumes the API, providing a reactive interface for configuring the input method editor.
Frequently Asked Questions
Why does the WebServer bind to localhost only?
The server sets GCDWebServerOption_BindToLocalhost to YES in src/WebServer.m line 72. This security measure ensures the preferences UI is accessible only from the local machine, preventing remote devices from accessing or modifying application settings through the HTTP interface.
What happens if port 62718 is already in use?
The analysis does not show explicit error handling for port conflicts in the provided code. Since the port is hardcoded to 62718 in src/WebServer.m line 20, the server would fail to start if another process occupies that port, likely returning an error through the startWithOptions:error: method's error pointer.
How does the frontend know when preferences are saved?
The POST handler in src/WebServer.m lines 55-68 returns the updated JSON payload immediately after writing to NSUserDefaults. The Vue.js frontend in web/index.js receives this response and logs the confirmation, providing immediate feedback that the settings have been persisted to the application's user defaults.
Can I access the preferences UI from another device on the network?
No. The server explicitly binds to localhost only via GCDWebServerOption_BindToLocalhost set to YES in src/WebServer.m line 72. This restriction prevents network access, ensuring only the local user can view and modify preferences through the browser interface at http://localhost:62718/.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →