Thursday, Apr 2, 2020
This post follows where my server-side Swift Kitura adventures left off.
Some sad news came about when it was announced that Kitura was winding down, and I realized that, since I built my blog using Kitura, I should probably find an alternative with a more stable future.
I peeked around and Vapor caught my eye.
Quoting their GitHub:
"Vapor is a web framework for Swift. It provides a beautifully expressive and easy to use foundation for your next website, API, or cloud project."
It's one of the most popular server-side Swift frameworks nowadays, powered by Swift NIO, and powers some pretty cool apps (like Scribble: an app that let's you share whiteboard space remotely!).
It even has some pretty nice integrations with things like SQLite
and has some pretty nice ORM stuff with their Fluent library. For my blog it's pretty basic, and doesn't really use any of this though.
The first thing I had to do was setup a new vapor app using their guide.
Note: As of writing this, Vapor 4 is in the RC stages so it may very well be available by the time you're reading this.
The rest of it was relatively painless and just required me to restructure some of my routing.
What once looked like this in Kitura:
app.router.get("/") { request, response, next in
guard let recentPosts = getAllPosts(atPath: blogFileServer.absoluteRootPath, limit: 6) else { return }
do {
try response.render("blog-home.stencil", context: [ "posts": recentPosts.map { $0.dictionaryRepresentation } ])
response.status(.OK)
} catch let error {
response.send(json: ["Error": error.localizedDescription])
}
next()
}
Became this in Vapor:
router.get("/") { request -> Future<View> in
guard let recentPosts = getAllPosts(atPath: rootBlogPath, limit: 6) else { throw Abort(.notFound, reason: "") }
return try request.view().render("blog-home", ["posts": recentPosts.map { $0.dictionaryRepresentation }])
}
What's nice is the API changes slimmed things down to a significantly more simple way of defining the root page for my blog.
You'll notice that requests return Future
s of View
s, which will resolve at some point in the future (reminds me a lot of Hack!).
Also, this gets rid of the next()
invocations littered about when using Kitura, which I never personally really understood that well.
The next thing I had to port over were my stencil definitions. Kitura uses Leaf, which is another way of defining HTML templates that Vapor can populate.
They were basically the same, except for a few syntactic changes, namely the use of #(something)
instead of {{ something }}
.
You can check out the documentation for Leaf here.
Kitura:
<!DOCTYPE html>
<html>
<body>
<ul>
{% for post in posts %}
<li>
{{ post.title }}
</li>
{% endfor %}
</ul>
</body>
</html>
Vapor:
<!DOCTYPE html>
<html>
<body>
<ul>
#for(post in posts) {
<li>
#(post.title)
</li>
}
</ul>
</body>
</html>
For my case, switching over was really simple and felt like a relatively painless API migration. What's kinda neat too is, at least in my case, Vapor is noticeably more stable when running my blog. It hasn't gone down once since porting over (which would happen every month or so with Kitura).
In addition, compile times seem to have been halved, although I'm not sure if this is just tangential to Swift improvements or not.
Vapor seems great to me! I'm sad to see Kitura go, but hopefully this one stays around!
Give it a go if you're interested!
Wednesday, Jul 24, 2019
A couple days ago I noticed a really weird bug, images that had been used as templates would occasionally draw darker or lighter (rather than the color originally chosen). I thought it was just some weird duplicate layer ontop of another layer that would lead to the icon being darker / lighter.
I tried using Xcode's View Debugger, but it just wouldn't work at all and would error out with:
Assertion failed: ([layer isKindOfClass:[self class]]), function -[CALayer _initWithReference:], file /BuildRoot/Library/Caches/com.apple.xbs/Sources/QuartzCore/QuartzCore-697.24.4.2/LayerKit/api/CALayer.mm, line 1947.
Somehow we had a CALayer
which… wasn't a CALayer
? Seeing as though Mojave has been having some super broken CALayer
stuff recently, I assumed it might've been a similar issue. Perhaps having too many layers was also corrupting the rendering of images?
It turns out, the original bug in question is actually an AppKit bug, but this bug lead me down a path of understanding how Xcode's View Debugger works.
I figured I could just put a breakpoint on -[CALayer initWithReference:]
, but sadly that breakpoint was never hit. There were potentially a few reasons from this:
-[CALayer initWithReference:]
is being invoked inside Xcode's process.Cutting this writeup short, there's no XPC process we need to worry about (thankfully), and manually attaching LLDB to Xcode and trying to break on that function didn't work either. Turns out, LLDB doesn't work when you're trying to set a breakpoint during Xcode's loading of the View Debugger, so we need to get a bit more creative…
After starting the View Debugger, we can use image list
to print out all the currently loaded libraries into the app being debugged:
(lldb) image list
...
[ 1] F217F7F8-A795-3109-B77F-B1E2277F3E3B 0x0000000103fea000 /usr/lib/dyld
...
[298] BD43F13C-7F88-301D-A860-77EE5F6CBBE4 0x00000001042bc000 /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Debugger/libViewDebuggerSupport.dylib
...
libViewDebuggerSupport.dylib
looks interesting.
It turns out, Xcode will dynamically inject that dylib into the running application and call some special methods on it in order to get info on how the application's view hierarchy works. This is very similar to how Reveal works (albeit sadly that app doesn't work for debugging AppKit apps…).
Now that we know what binary is being loaded, we need to find out how it's actually starting this view debugging code. We can use static analysis and decompilation to figure this out.
Hopper is a fantastic tool for taking apart binaries, and it's something I always find myself coming back to. The way it works? Simply hand it an executable, and it will disassemble the executable, showing the innerworkings of what it does.
Opening that dylib up into Hopper, and searching for "viewdebugger" will give us lots of results for methods on the class DBGViewDebuggerSupport
. Trawling through those results we'll eventually find [DBGViewDebuggerSupport fetchViewHierarchy]
. Now this seems to be the main-ish method that's called to kickstart things, so we'll see what it does. We're trying to find where the layers are referenced so we'll see what happens with the sublayer hierarchies. Eventually we'll see something like
var_170 = [r14 _collectSubviewInfoForView:r13 encodeLayers:rcx];
This is shown in the decompiled code, $r14
and $r13
are registers in the CPU, which hold references to objects (kinda). Navigating inside that method, we'll eventually find the magic function that we seem to be breaking: CAEncodeLayerTree(CALayer *layer)
which is inside QuartzCore.framework
.
Now, whilst it'd be easy to just invoke that function from LLDB, our breakpoint still won't be hit (since you can't break on things being invoked from LLDB), so we need to hardcode a way of invoking it. CAEncodeLayerTree
also is private API, and has no header definition, so we'll need to use something else… something like dlopen
and dlsym
should help! dlopen
will open / load a library into the current process and return a pointer to the address where it starts in memory, and dlsym
will lookup the pointer to the function itself so we can manually call it:
#import <dlfcn.h>
void *quartzCore = dlopen("/System/Library/Frameworks/QuartzCore.framework/QuartzCore", RTLD_NOW|RTLD_GLOBAL);
void(* CAEncodeLayerTree)(CALayer *) = (void (*)(CALayer *))dlsym(quartzCore, "CAEncodeLayerTree");
CAEncodeLayerTree(view.layer);
I assigned this to a double click, but you can invoke it however you like.
Finally, to figure out the weird layer that's breaking things, we can use LLDB's conditional breakpoints:
(lldb) b -[CALayer _initWithReference:]
Breakpoint 5: where = QuartzCore`-[CALayer _initWithReference:], address = 0x00007fff496ba037
(lldb) breakpoint set --name "-[CALayer _initWithReference:]" --condition '([$arg3 isKindOfClass:[$arg1 class]] == 0)'
Here we're setting a conditional breakpoint on -[CALayer _initWithReference:]
emulating the assert that was failing previously. Here, $arg3
is the argument passed to -initWithReference:
and $arg1
is the object the method is being invoked on (aka self
when you've hit the breakpoint). This is using aliases for registers that'd be used when calling Objective-C methods. For more details on how these "pseudo" registers work, check out @natashatherobot's post, and if you're super interested, this post goes into lots of details.
Finally, invoking this will allow us to finally find our view that's breaking things. Once the breakpoint is hit, we can just invoke po $arg3
to see what object is breaking things.
In my case, I was incorrectly configuring a CAGradientLayer
's delegate incorrectly. Fixing that resolved the issue.
Sometimes the best approach to fixing bugs is to, well, debug a debugger. It's pretty dense, and reverse engineering opaque technologies isn't talked about that much nowadays, so I figured I'd post more broadly about how I go about researching / investigating the internals of various things.
Hope you found this post helpful and If you've got any questions, feel free to message me!
PS: @indragie made an amazing open source project that aims to replicate Xcode's View Debugger / Reveal and it's pretty awesome. Check it out here!
Wednesday, May 29, 2019
This post follows where the first part left off.
Now that you've written a blog, or whatever you'd like as part of server-side Swift, the next step is to get it hosted somewhere and running all the time. Personally, I use DigitalOcean for my hosting running an Ubuntu VPS. Most of the instructions here are fairly generic, but depending on the distribution of Linux (or Unix) you're running, you may need to switch some things up.
The first thing you'll want to do after getting your VPS is getting Swift 5 (the latest version as of writing this post) setup and running on it.
As a pre-requisite you'll need clang installed.
sudo apt-get install clang
You'll then want to download the latest version from swift.org.
Note: As of this writing, packages are only pre-built for Ubuntu, so if you're using something else, you'll need to compile your own version.
You'll want to unpack the downloaded Swift package, and then add it to your $PATH
:
tar xzf swift-<VERSION>-<PLATFORM>.tar.gz
echo 'export PATH=/path/to/swift-<VERSION>-<PLATFORM>/usr/bin:"${PATH}"' >> ~/.bashrc
After that, you should be able to run Swift from the commandline.
> swift
Welcome to Swift version 5.0 (swift-5.0-RELEASE).
Type :help for assistance.
1> var a = 1 + 1
a: Int = 2
A really bad thing™ to do would be to run your Swift server as root… if someone manages to hack your server, they'll be able to wreck havoc as root. You'll want to configure your server to run as an unprivileged user.
sudo adduser swiftserver
sudo usermod -aG sudo swiftserver
This'll create a new user called swiftserver
that can run privileged commands with sudo if needed (ps: don't start your server with sudo
).
You'll want to logout and then log back in as the swiftserver
user.
To serve the server, you can actually just run it directly via swift run
and it'll be served (by default) on port 8080. However, this doesn't support SSL or any of the nice things that come with web servers (security, disabling access to folders, etc.). Whilst I'm sure you could do all of this by hand, I wanted to focus mostly on serving blog content and not the intricacies of web servers. To serve the Swift app, I setup NGINX and setup a reverse-proxy to my Swift app running. This way, the Swift app would run on a different port and NGINX would communicate with it (instead of it being the whole webserver).
It looks something like this:
[browser: https://blog.mysite.com/blog/entry] -> [NGINX] -> [swift-server] -> [NGINX] -> [browser]
Setting up NGINX is pretty easy:
sudo apt-get install nginx
systemctl status nginx
# DigitalOcean needs to have the firewall configured via ufw
# This opens both port 80 and 443, but you may want to use just Nginx HTTPS to be more secure after you've setup HTTPS
sudo ufw allow 'Nginx Full'
Next you'll want to configure NGINX.
Create a file at /etc/nginx/sites-available/swift-blog
(replace swift-blog
with whatever your binary is called) and fill it out with something like this:
server {
server_name blog.mysite.com;
location / {
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://127.0.0.1:8888;
proxy_read_timeout 90;
proxy_redirect http://127.0.0.1:8888 http://mysite.com;
# Required for new HTTP-based CLI
proxy_http_version 1.1;
proxy_request_buffering off;
}
}
Lastly, you'll want to activate this configuration by symlinking it:
sudo ln -s /etc/nginx/sites-available/swift-blog /etc/nginx/sites-enabled/swift-blog
# Reload NGINX
sudo systemctl restart nginx
You'll want to set the server_name
to use the subdomain of where you're hosting this server, and proxy_pass
and proxy_redirect
accordingly (this is where you supply the port your server will be running on). Now whenever you visit blog.mysite.com
you'll be sending web requests to your Swift app via NGINX.
There's a lot of configuration options available here, and if you'd like to setup a separate webserver alongside this app, you can do so in this file as well. For more information on that check out DigitalOcean's tutorial here.
The next thing you'll want to do is serve your content securely using SSL. Whilst this is technically optional, I think it's much more future proof and safer to setup for people consuming your content. There's countless ways to set this up, but the easiest way I found was done by setting up Cloudflare (thanks @_inside!). When you setup your domain and verify it through Cloudflare you'll be given a certificate and a private key. You'll want to put those on your server somewhere (I used /etc/cloudflare/
). You'll also want to download the Origin Pull certificate here and save that into the same directory as your Cloudflare certificates.
The last thing you'll need is to generate a dhparam.pem
and save that into the same directory as well. You can do that via this command:
sudo openssl dhparam -out /etc/cloudflare/dhparam.pem 4096
After you've got all those files, you'll want to edit the top of the server
area of your swift-blog
config with the following:
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
ssl on;
ssl_certificate /etc/cloudflare/cloudflare_origin_cert.pem;
ssl_certificate_key /etc/cloudflare/cloudflare_origin_privatekey.pem;
ssl_dhparam /etc/cloudflare/dhparam.pem;
ssl_client_certificate /etc/cloudflare/origin-pull-ca.pem;
ssl_verify_client on;
...
This change will enable serving content with NGINX using HTTPS / SSL.
After making these changes you'll also want to change your firewall settings and reload NGINX.
sudo ufw allow 'Nginx HTTPS'
sudo systemctl restart nginx
Sometimes apps crash or servers fail, so you need to ensure that your Swift app can restart itself in case it fails in any way; that way it can still communicate with NGINX and the web. Setting this up with Ubuntu is pretty easy, you just need to create and register a service for it.
Simply create a file at /etc/systemd/system/swift-blog.service
with the following contents:
[Unit]
Description=swift-blog
After=network.target
[Service]
Type=simple
Environment="PORT=8888"
WorkingDirectory=/path/to/your/swift-blog-folder
ExecStart=/path/to/your/swift-blog-folder/swift-blog
Restart=always
StandardOutput=file:/var/log/swift-blog.log
[Install]
WantedBy=multi-user.target
You'll then want to activate your server service using the following:
sudo systemctl start swift-blog
sudo systemctl enable swift-blog
After that, you should be able to visit https://blog.mysite.com/ and your Swift server will be running!
Writing my whole blog in Swift and then getting it running as a fully fledged web app was a really neat endeavour. It was fun starting from square one and getting everything running from the ground up; I definitely learned a bunch and I hope in reading all of this you. It's still wild as a concept to me, being able to use all of my Cocoa knowledge I've built up over the years to use for a Linux server box. I'm still learning how to make it better, caching things, etc. so I'm always open to suggestions on how to improve.
Let me know how your own server-side Swift projects go and if you have any questions feel free to ask!
Wednesday, Apr 17, 2019
Recently I attended try! Swift in Tokyo, Japan (which was an absolutely fantastic conference), and there I heard about a lot of folks that were using (or beginning to use) Swift on the Server. Now that Swift has reached version 5, and its ABI is stable, I thought it'd be a great time to try it out for myself. I didn't really know what to write, but I saw that @stroughtonsmith had recently rewritten his blog as a static one being served by PHP and I thought that could be a pretty cool thing to make in Swift! My previous blog was hosted on Tumblr, and it ended up being a lot of maintenance to do simple things, which was pretty discouraging from wanting to write things. I wanted to write something that would make publishing a blog with media and code samples trivial without importing thousands of dependencies.
The end result however, well, you're reading it. As of this post, my entire personal website and blog runs on a server I wrote in Swift using IBM's Kitura, which, in-turn, is running on some Linux box in the cloud. I can easily write new posts, upload media and code samples, and the backing for it is all type-safe and native (thanks to Swift). Updating it is as easy as compiling an Xcode project, and there's no more need to trawl through arcane linux directories to find error logs.
This post will go over the basics of how I wrote my blog, and how you can get started writing your own. I wrote it in a way where you can get started with little server-side code experience, so hopefully this post gets you on your feet and helps you get started in the world of Server-Side Swift.
In all honesty, my 15+ years of experience with server-side code has been:
So I went into this completely blind not knowing anything at all of what I'm supposed to be doing. ¯\_(ツ)_/¯
I normally write apps, not distributed servers. This ended up being way outside my comfort zone, so I'm still learning, and if there's things I'm missing or could do better, please let me know!
Pro-tip: Don't run your server as root. It's a very bad idea.
Starting out, I didn't really know what I should be doing or how I should start. How does one get Swift working on Linux? How does the app I write in Swift produce HTML for browsers to read? In the past, if I wanted to write scripts for my website I'd just open a new PHP file and it'd work.
I started searching and was slammed with a wall of information:
All of that was a lot of dense reading and background info on a lot of technolgies I've only heard in passing. Half the time I was thinking to myself wtf is all of this?. I decided to start with the basics and just see what I could get working so I ended up picking up Kitura because their support team were super nice and it seems as though they've been contributing a fair bit to Apple's open source projects too. I have nothing against any competing open source projects (i.e. Vapor), I just personally haven't used any of them.
Anyways, setting up Kitura is pretty simple:
brew install kitura
mkdir blog-test
cd blog-test
kitura init
Note: The name of the folder you run kitura init
inside is the name of the target that will be created. Here we're using blog-test
.
This'll give you a barebones project setup with the Swift Package Manager. I wanted to write things in Xcode, so I did the following:
swift package generate-xcodeproj
And that created an Xcode project I could build and run with.
Note: This also adds a bunch of other stuff that I didn't end up using but could be helpful (i.e. Health, Analytics, etc.). They all are optional, so if you don't want them you can remove them.
⌘+R
and you'll get a web server running at http://127.0.0.1:8080
or http://localhost:8080
Application.swift
is the main entry point for things and here is where you'll setup routes. Routes are a way of parsing incoming network requests, doing some logic with them, and then giving back a response.
For example:
router.add("/neat") { (response, something, something) in
response.write("neat! 📸")
}
Will output nothing but "neat! 📸" when you navigate to http://localhost:8080/neat
. While that's cool and all, that doesn't really allow the dynamism of Swift to show, and it isn't that helpful for building sites easily. Swift + Stencils is something I adopted to make this far more easy.
So Kitura provides out of the box support for Stencil. Stencil is a template language for Swift and is a way of adding preprocessor functionality to HTML. You can take a context defined in Swift and use that to generate or fill-in placeholders in the HTML. I needed to write a listing of all my blog entries, so I could write a stencil file like this:
<!DOCTYPE html>
<html>
<body>
<ul>
{% for post in posts %}
<li>
{{ post.title }}
</li>
{% endfor %}
</ul>
</body>
</html>
And after using a JSON data structure like this:
{
"posts": [
{
"title": "My first blog post"
},
{
"title": "My second blog post"
}
]
}
I could create something like this:
<!DOCTYPE html>
<html>
<body>
<ul>
<li>
My first blog post
</li>
<li>
My second blog post
</li>
</ul>
</body>
</html>
Neat huh? It's similar to writing inline PHP code for generating missing parts of HTML, or filling in fields as a page is being loaded. The difference, however, is Swift is supplying a single context object to the HTML stencil, which is what the stencil will be populated with.
To setup stenciling, we need to opt-in for it. You'll want to edit Package.swift
to look something like this:
// swift-tools-version:5.0
import PackageDescription
let package = Package(
name: "blog-test",
dependencies: [
.package(url: "https://github.com/IBM-Swift/Kitura.git", .upToNextMinor(from: "2.6.0")),
.package(url: "https://github.com/IBM-Swift/HeliumLogger.git", from: "1.7.1"),
.package(url: "https://github.com/IBM-Swift/CloudEnvironment.git", from: "9.0.0"),
.package(url: "https://github.com/RuntimeTools/SwiftMetrics.git", from: "2.0.0"),
.package(url: "https://github.com/IBM-Swift/Health.git", from: "1.0.0"),
.package(url: "https://github.com/IBM-Swift/Kitura-StencilTemplateEngine.git", from: "1.8.0"),
],
targets: [
.target(name: "blog-test", dependencies: [ .target(name: "Application"), "Kitura", "HeliumLogger"]),
.target(name: "Application", dependencies: [ "Kitura", "CloudEnvironment", "SwiftMetrics", "Health", "KituraStencil" ]),
.testTarget(name: "ApplicationTests" , dependencies: [.target(name: "Application"), "Kitura", "HeliumLogger" ])
]
)
Note: At time of writing I'm using Swift 5 for this, so you may need to update .swift-version
and ensure your Xcode version is at least 10.2. Also there's a strong chance these packages have updated versions now, so you'll also want to update those accordingly.
Then run the following to regenerate / update your project:
swift package update
swift package build-xcodeproj
Setting up stenciling templates isn't all that difficult. By default, Kitura will render template files provided in Views/
. So let's create a folder named Views
next to the Sources
folder and inside Views/
create a stencil file called blog-posts.stencil
.
Fill it with something like this:
<!DOCTYPE html>
<html>
<body>
<ul>
{% for post in posts %}
<li>
{{ post.title }}
</li>
{% endfor %}
</ul>
</body>
</html>
Next, we need to have our server render the specified template file when requested, so we need to setup a route for anytime someone visits posts
to populate a data structure of all the posts for filling in the template.
In Application.swift
, import KituraStencil
add the following just before Kitura.addHTTPServer(onPort: cloudEnv.port, with: router)
:
// Enable the stencil engine and add it to the router.
router.add(templateEngine: StencilTemplateEngine())
// Create a data structure to be shown by the stencil.
let posts: [String: Any] = [
"posts": [
[
"title": "My First Post"
],
[
"title": "My Second Post"
],
]
]
// Anytime someone navigates to http://mysite.com/posts run this closure.
router.get("/posts") { request, response, next in
do {
// If it's successful in rendering, mark the response as OK.
try response.render("blog-posts.stencil", context: posts)
response.status(.OK)
} catch let error {
// Otherwise, log out an error.
response.send(json: ["Error": error.localizedDescription])
}
// Calling next will allow other parts of your server to respond to similar requests (rather than just halting here).
next()
}
Build and run, and if you did everything correctly, open Safari and navigate to http://localhost:8080/posts
.
You should see something like this:
Next steps here would be to write something to accept an arbitrary URL and use that to serve content dynamically. This would allow for linking back to specific posts via something like this:
http://localhost:8080/myPost
Kitura supports queries and variable parameters too, and it's pretty simple to declare:
router.get("/:postName") { request, response, next in
// postName would be myPost in this case
guard let postName = request.parameters["postName"] else {
response.status(.notFound)
return
}
// TODO: do something with the post name.
next()
}
One thing that you could do with that postName
variable is use it to render a markdown file with a specific filename. Kitura supports it out of the box as well!
Add KituraMarkdown
to your Package.swift
:
.package(url: "https://github.com/IBM-Swift/Kitura-Markdown", from: "1.1.0"),
And also add it as a dependency to the Application
target:
.target(name: "Application", dependencies: [ "Kitura", "CloudEnvironment", "SwiftMetrics", "Health", "KituraStencil", "KituraMarkdown" ]),
Then run and update things:
swift package update
swift package generate-xcodeproj
In Application.swift
you'll want to import KituraMarkdown
and add the rendering engine for it (just like the StencilTemplateEngine
):
let markdownRenderer = KituraMarkdown()
markdownRenderer.setRootPaths(rootPaths: [ "Views/blog" ])
router.add(templateEngine: markdownRenderer)
let blogFileServer = StaticFileServer(path: "Views/blog")
router.get("/", middleware: blogFileServer)
Now anytime the server tries to render a markdown file inside Views/blog
it'll convert the markdown file and render it as HTML. You'll also notice that we added a static file server. This is so that anytime a resource is referenced inside the markdown file (i.e. 
), the server will know to transform that path myimage.png
into something like Views/blog/myimage.png
.
Create a markdown file called markdown-test.md
inside Views/blog/
and fill it with something like this:
This is a markdown file!
# It Supports Big Headers
## Smaller Ones
### And Even Smaller Ones

Note: Don't forget about the image, feel free to swap it with whatever you like, but it's a good way to test. Just put it right next to the markdown-test.md
file.
A lot of blog sites allow linking to specific blog articles usually in the form of website.com/your-blog-entry-here
so we need to have a way of mapping everything after the /
to a particular blog entry to render. To make things super simple, we can just map the filename. In this case, navigating to http://localhost:8080/markdown-test
should render our markdown file.
To do this, we need to setup a route to handle that parameter. Kitura, again, makes this pretty easy to setup. Add the following after the blogFileServer
setup code to allow the server to render arbitrarily specified markdown files:
router.get("/:postName") { request, response, next in
// Gets the argument passed after the "/"
guard let postName = request.parameters["postName"] else {
response.status(.notFound)
return
}
// Tries to render the markdown file named {postName}.md stored in the blog folder (contained in the Views folder)
try response.render("blog/\(postName).md", context: [String: Any]())
response.status(.OK)
next()
}
Assuming everything worked, you'll have something that looks like this:
This blog post is already gigantic, so I'll be writing more in Part 2 which will be coming soon! We'll be covering how to take all of this swift server code and putting it on a server. We'll also be talking about how to setup SSL so it's nice and secure for people to consume it.
Learning all of this and building it over time was a lot of fun. It's inspired me to write a lot more, especially since a lot of the pains of blogging before have been fully removed in my own workflow, written by me. Any minute changes I want I can easily change, and it's built atop a pseudo-familiar platform to me. I hope you found this guide useful, and maybe it'll get you excited about writing Swift on the server too!
If you have any questions, feel free to give me a shout on twitter.
I can't wait to write more, definitely have some things planned to talk about (synths, sounds, etc.) but until then, I'm just praying that this server doesn't get taken down :P
Sunday, Sep 13, 2015
Just wanted to add a bit of commentary to Steven’s awesome writeup on the UIKit hax we did with Apple Watch… :D
WatchKit apps are handcuffed, as it were, only able to display UI through the elements WatchKit provides. They cannot get the position of touches onscreen, or use swipe gestures, or multitouch.
Seriously, it’s horrible. Being able to build proper apps would be so great, instead of simple ways to display JSON files with Interface Builder (yes, that’s basically what WatchKit currently is). Especially watch faces, I’d love to be able to build my own (even powered by Sprite Kit, I know the stock ones use it!), I’ve got so many ideas for things to build.
I roped @b3ll into a torturous day & night of testing things, he in a cafe across from WWDC (with @saurik for much-needed moral support!), me across the Atlantic.
10/10, can confirm, working as a remote, human, lldb / gdb session over iMessage was really weird. I’m pretty sure I have 30+ zipped up attempted builds, all of which had more and more aggressive naming schemes as time progressed :P (fku.zip). Sometimes the apps would install, sometimes they wouldn’t. Sometimes they’d insta-crash, sometimes they wouldn’t. Sometimes they’d just insta-crash Carousel.
¯\_(ツ)_/¯
PS: Here’s a visual representation of how debugging went.
:/
:(
:D
After getting a basic UIKit app going, we started trying out various other iOS projects to see how they’d scale (scale… hehe… the watch is tiny… geddit?).
PepperUICore classes are prefixed PUIC and are mostly subclasses of UIKit classes. Your root UIApplication subclass should be PUICApplication.
While messing about with PepperCore, we whipped up an app to report the specs although whenever we tried to query the CPU speed it blew up. It’s got a really weird CPU(s?). Still… that’s a lot of RAM for a tiny watch!
Next I tried porting Doom to the Apple Watch, since that’d be well, totally really super awesome. Doom is the most ported game of all time and it runs on calculators, so surely it must work on the Apple Watch, right?
watchOS can be so incredibly fickle when installing binaries. I’ve seen this with non-hacked regular WatchKit apps too, but you can get into a state where the OS refuses to install a binary and gives you random error messages. Sometimes rebooting fixes this, sometimes removing the app from your phone helps. Sometimes the exact same binary won’t install one minute, and works the next.
I actually had it compiling and running at one point, but the fact that the builds were like 30mb made it excruciatingly painful to install and debug (transferring 30mb wirelessly took like 30 minutes). It also didn’t get very far, sadly. It was crashing on some weird and random OpenGL bugs, which I just blamed on the buggy 2.0 beta 1 software. What was even more “fun” was when I’d actually try and use the watch normally by raising my wrist, you know to check the time, and then seeing as it would just deadlock itself.
Yes… yes… I know… “Friends don’t let friends install iOS betas.” -@mxweas
What didn’t help was I had no debugging session to attach to, nor did I have any crashlogs (Steven says they take an hour… um no, more like a day ;P). To make things easier I migrated to a slightly smaller and more manageable codebase, Canabalt! Cramming everything to fit onscreen and make it playable took some messing about, but eventually it worked pretty great. Sound, controls, 60fps, the whole deal (the soundtrack is fantastic btw). Not quite sure how quickly it would drain the battery, but it seemed okay!
Working on this and keeping it somewhat secret was super fun, and a challenge at times, but now that it’s out in the open, make some cool stuff! If you do make anything cool, feel free to give me or @stroughtonsmith a shout! :D
watchOS is iOS; most of the frameworks you expect to be there are actually on-disk. However, the SDK will not include them, or have complete headers for them.
It’s so true. Everything “just works”, well, within reason. Once you’ve got ramped up and things working, do give it a shot at some point!
I know my Click Wheel Keyboard is just begging for an Apple Watch port… Digital Click Wheel Crown anyone? :D
Tuesday, May 6, 2014
Ever since Apple first announced Passbook two years ago, I've always liked the idea of using it to store business cards.
However, it'd be far more useful if it could store Pokemon cards... someone should file a radar on that...
Every year at WWDC (and any conference really) it's super fun to play the "trading card game" and collect various business cards from people. Some are really cool, some are so fantastic that the people that gave you their card ... will ask for it back -_-
The first day Passbook was announced was when I made my first card and hopefully this'll stir up some creative Passbook business cards come WWDC 2014!
tl;dr: they're cool, you should make one.
Making a Passbook business card is, well, easier than writing HTML! For the most part you're just filling in blanks in a JSON file. You create a folder, and inside that folder you put the main JSON file, throw in a few images, and BAM Passbook card (waves hands)!
Sharing them is just as easy, especially with iOS 7. With iOS 7 came AirDrop (a somewhat crippled version, fwiw) and with that you can share Passbook cards over AirDrop, or anywhere else from the standard iOS share sheet.
Let's look at mine as an example:
{
"formatVersion" : 1,
"passTypeIdentifier" : "pass.adambell.businesscard",
"organizationName" : "diffractive",
"serialNumber" : "0x1337",
"teamIdentifier" : "sekrit",
"description" : "Adam Bell",
"generic" : {
"primaryFields" : [
{
"key" : "name",
"label" : "",
"value" : "Mobile Developer"
}
],
"secondaryFields" : [
{
"key" : "website",
"label" : "Web",
"value" : "www.adambell.ca",
"textAlignment" : "PKTextAlignmentLeft"
},
{
"key" : "twitter",
"label" : "Twitter",
"value" : "@b3ll"
}
],
"auxiliaryFields" : [
{
"key" : "email",
"label" : "Mail",
"value" : "[email protected]"
}
]
},
"barcode" : {
"format" : "PKBarcodeFormatPDF417",
"message" : "https URL here",
"messageEncoding" : "iso-8859-1"
},
"backgroundColor" : "rgb(221, 18, 29)",
"foregroundColor" : "rgb(255, 255, 255)",
"labelColor" : "rgb(255, 255, 255)",
"logoText" : "Adam Bell",
"suppressStripShine" : true
}
The meat of the pass is under the generic section. Depending on the type of pass you're making, you can specify a multitude of fields (like departure / arrival if you were making a flight ticket), but for our sake we're just re-purposing a generic pass. Your teamIdentifier
is used to specify your signing identity if you want to sign your pass but we'll get to that.
Here's how I tailored my pass as a business card:
I don't really know if this used on iOS, but on Windows Phone it shows up in the live tile... more on this later.
Set the value of name
to a title for yourself, I kept Mobile Developer to keep things nice and simple. If you want to be fancy, might I recommend something like "iOS Connoisseur" (complete with air quotes)?
Here you get up to two fields to specify information (after that it looks kinda stupid with text all smushed together). I chose my twitter handle and website, since they're the easiest way to show off what I've done as well as get quickly in contact.
You get 1-2 more fields for info. I chose my email, since I'd rather people use it as a last resort (for most things) ;)
This is the fun part. With iOS 7 came the ability to scan barcodes to download Passbook passes. It uses the same wicked fast iTunes gift card scanning / redeeming tech, but comes with some minor annoyances.
First off, the link specified here must be HTTPS. ...unless of course you break PKAllowHTTP() ;)
You get 3 types to pick from:
If you choose Aztec or QR, the rest of the card just ends up being a massive zebra.
Also, you might want to enable suppressStripShine
, last thing you want is your pass looking like a shiny zebra (unless you like shiny pokémon!)
You can invoke it like so:
pkpass -p <some folder> -o <outputfile.pkpass>
pkpass
as the file extension.application/vnd.apple.pkpass
specified as the MIME type.What's even more awesome is Microsoft decided to be nice (or lazy :D) and support Apple Passbook cards in Windows Phone 8.1! They work out of the box too! You can simply email a pass to your Windows Phone and add it that way.
What's nice is you literally have to change nothing, it just works.
That's a first for Microsoft. :P
As I mentioned earlier, description
is the field that defines what will show on the Windows Phone Start Screen when you add your pass as a live tile (super quick access to contact details!). Best practice for this is to keep it short and simple unless you have an arbitrarily long name that just overflows everything...
If you haven't used Passbook yet, this might be a fun way to start. Might put you on the "bleeding edge" of business cards! :D
PS: Pokémon Passbook cards really should be a thing.
I wish I could have a larger strip image, a gold border, and a purple background, but the current Passbook spec doesn't allow it. :(