Salzbaby.live Updater

Years ago when my daughter was born I put together a website/app thingy to help us keep people updated on the progress of her coming into the world. The idea was that it was a single page site that would display a single message at any one time. If you were curious how it was going, you could check the site and not bother us.

I wrote a quick iOS app to help me update it from the hospital. Lots of people liked it and followed along for the entire 40 hours or so it took for my daughter to enter the world. It cut way down on the number of “what’s happening?!” texts we got, although we still got a bunch wondering if the site was broken since it was taking so long.

I consider this an example of an app that is a, in Robin Sloan’s words, a “home-cooked meal”. An app that doesn’t need to be spun off into a SaaS or be open-sourced, or grow an audience. It’s just for the people it is for.

We knew for our son we wanted to do this again. And, because nothing can be easy, it turned out I needed to rewrite the app. Who knew that Swift would change in the intervening years?!

Components

iOS App

After being frustrated that the code I’d hacked together 5 years ago didn’t magically work with no bugs I ended up starting over from scratch using SwiftUI. Swift is better than it was 5 years ago, but still a frustrating language to jump into to “get something done real quick”. The documentation is bad.

Anyway, here’s the code for the view:

@State private var message: String = ""

var body: some View {
    VStack {
        Form {
            Text("Salzbaby.live Updater")
            TextField("Enter Update", text: $message)
                .frame(height: 100.0)
                .textFieldStyle(RoundedBorderTextFieldStyle()
            )
            Button(action: {
                self.POSTfunction(message: self.message)
            }) {
                Text("Submit")
                    .multilineTextAlignment(.center)
            }
        }
    }
}

And that ends up looking like this in the app:

Type in the box, tap “submit” and it fires off a POST request to a file on the server. The passcode is a hardcoded string that I have replaced below so you don’t “hack” me. The server checks for it over on its end.

Here’s the main function for the post request:

func POSTfunction(message: String) { 
        //Create dict and then convert to JSON
        var dict = Dictionary<String, String>()
        dict["passcode"] = "lolno"
        dict["message"] = message
        let data = try! JSONSerialization.data(withJSONObject: dict, options: []) 
        
        // https://stackoverflow.com/questions/32201926/post-json-request-in-swift
        HTTPPostJSON(url: "https://salzbaby.live/SECRETPHPFILE.php", data: data) { (err, result) in
            if(err != nil) {
                print(err!.localizedDescription)
                return
            }
            print(result ?? "")
        }
    }

The app is then sideloaded onto the devices I physically plug into my computer. This does not scale, but it does not need to.

Server

Aside from general NGINX and SSL setup, over on the server I have a PHP file with one job to process the POST request:

<?php
	# Get JSON as a string
	$json_str = file_get_contents('php://input');

	# Get as an object
	$json_obj = json_decode($json_str);

	if($json_obj->passcode == "lolno") {
		$fn = "SECRETTEXTFILE.txt"; 
		$file = fopen($fn, "w+"); 
		$size = filesize($fn); 

		fwrite($file, $json_obj->message); 

		fclose($file); 
	}
?>

PHP’s motto should be: “your server is already running it so why not abuse it?”

Shouldn’t this be saving to a database instead of a txt file? Yes. Absolutely.

Web Site

The site is a bespoke templating library that does server side rendering and delivers an HTML file to your browser that displays the text in the txt file in the middle of your screen:

<!DOCTYPE  HTML>
<html>
	<head>
		<title>Is the Salz Baby Here Yet?</title>
		<style>
			body {
				height:100%;
			}
			#answer {
				text-align:center;
				font-size:4em;
				position: fixed;
				top: 50%;
				left: 50%;
				transform: translate(-50%, -50%);
				-webkit-transform: translate(-50%, -50%);
			}

		</style>
	</head>
	<body>
		<!-- 
			Hi there, nerds! Here's what you're after:

			The text of the page update is being read from a plaintext file. I have a bespoke iOS app on my phone and am sending a POST request to the server to overwrite the file whenever I make a request. It's simple, it works, and, there's no history by design.

			I'll show you the code sometime! Just not now!
		//-->
		<script type="text/javascript">
		</script>


		<div id="answer">
			<?php
				$fn = "answer.txt"; 
				$file = fopen($fn, "r"); 

				$contents = fread($file, filesize($fn)); 

				fclose($file); 

				print $contents;
			?>
		</div>

	</body>
</html>

This file was more or less the same as it was before just with a more direct comment to the many nerds in my social network. This is me making good on the promise to show you how it all worked.

Conclusion

Programmers overcomplicate everything all the time. The hardest part of this for me was limiting the feature set to only the basics. Since this is never going to be used by anyone else I could afford to cut every corner there is. Heck, there’s no notification on whether or not the update went through. You go to the site to see it there. That’s extremely poor UX!

However, despite the length of the list of feature requests requests…it did its job admirably. Folks from all of our disparate social circles got to check in on us when they were thinking of us and get a glimpse into what was going on. And as people woke up on the 27th and saw the update we started to get trickled in congratulations from all over. The Workantile slack even started a thread to notify people whenever there was an update, which was heartwarming to watch.

I’m glad we did this and the site will show this very important message until the registration on the domain name lapses in about a year:

Caspian Orion Salzman

Our son was born on May 27th 2020 at 7:17am. He was 21.25” long and 8lbs 12oz.

I told him this when he was born. Perhaps this is too much to lay at the feet of a baby, born into a country on fire, but I think he can do it:

Promise you’ll be kind.
Promise you’ll be good.
Promise to fight for justice.

He is named after Caspian from the Chronicles of Narnia, although I’m learning to like the Phish song as well. Orion is after the constellation. It’s vast and encompasses many stories. Later he can decide which one he likes best.