Skip to main content

Playing with Swift and SpriteKit

I've always wanted to learn how to write native apps for iOS and the Mac as long as either has existed. However, the barrier of entry has always been too high, given that I only ever had time to play with them as a hobby. The Objective-C programming language is a bit complicated to learn, especially alongside all the memory management that you have to do (and it doesn't help that Apple has gone through several memory management schemes through Mac OS X versions). To add on to that, the Cocoa framework is huge, and it's quite daunting to even know where to get started with it.

With Apple's announcement of the Swift programming language in June, it was clear to me that the language would significantly lower the barrier of entry. The XCode 6 beta is now public (i.e., you do not need to have a paid Apple Developer account to access it), so anyone can play with Swift.

Note that I am still very new to both Swift and iOS development in general, so it's quite likely that some of the things I mention here are actually bad ideas. If you know more than I do and spot a bad thing that I am doing, please mention it in the comments.

It's also possible that some of the assumptions I've made about the Swift language or the SpriteKit framework are actually wrong. Please remember that I am still a beginner and take what I say with a grain of salt.

The Swift Language

If you don't know how to program at all, I don't know how well this will work for you. I already know several language, especially Python, so my experience derives from that.

First, read through the Swift language guide. If you have XCode 6, you can read it interactively as a Playground. I only have read through the first part so far, which gives a high-level overview of the language.

The Swift language is actually quite easy to learn, especially if you already know a high-level language like Python. A few important things:

  • var and let seem a bit confusing. The difference is actually quite simple: var denotes a variable that can change and let denotes a variable that cannot. You could in theory just use var for everything, but let lets the compiler spot mistakes for you, and it also probably lets it make your code faster. If you intend to never change the value of a variable, use let.

  • Swift uses type inference, meaning that you usually don't need to specify types. But when you do, you do so by putting a : after the variable name, like var a: Int = 2 or func f(a: Int). The exception is the return type of a function, which uses the arrow -> (if you are familiar with Python 3 type annotations, the syntax is exactly the same), func f(a: Int) -> Int.

  • Swift uses ? after a type name to indicate that a variable could be its given type, or nil. If you are familiar with Haskell, this is like the Maybe monad. I know very little Haskell, so I don't know if Swift's implementation of ? is truly a Monad.

    Roughly speaking, in many circumstances, you don't know if a variable will actually be the given type or not. A good example of this is with dictionaries. var a: [String: Int] creates a dictionary that maps strings to integers. If you manipulate this dictionary, and then access a key from it, like a[b], there is no way for the compiler to know if that key will really be in the dictionary. If the key is in the dictionary, you will get the value of that key. Otherwise, you will get nil. Hence, the type of a[b] is Int?.

    Swift uses ! to indicate that the value is not nil, which tells the compiler to compile code that doesn't check for that case.

    For the most part, you can ignore this as well, at least when you start. Just write code as you would, let XCode add in the types for you, and only worry about types if the compiler tells you something is wrong.

  • Swift functions often require the parameters be named, for instance, you have to write CGSize(width: 1, height: 2) instead of just CGSize(1, 2). This is both for clarity (the former is much easier to read if you aren't familiar with the API for CGSize), and because Swift allows polymorphism, i.e., you can define different initializers for the same class with different type signatures. For example, CGRect can be initialized as CGRect(origin: CGPoint, size: CGSize) or CGRect(x: Int, y: Int, width: Int, height: Int). This can lead to ambiguities in some cases unless you explicitly tell the compiler which version to use.

I've found Swift to be a very strict language. I don't mean this in the sense described by this Wikipedia article. What I mean is that Swift typically only lets you do things one way. This is similar to Python's "one way to do it," except Swift enforces this at the language level.

A nice example of this is that I rarely get a warning from the Swift compiler. Just about every message I've gotten from the compiler has been an error. The difference is that the program will still compile and run with a warning. This is different from C, C++, and Objective-C, which have many warnings that the compiler will still compile with. These warnings usually are for things like an incorrect pointer type. Since there is really only one type in C, the integer (because all data in memory is just integers), the program can still run even if you mix your types up a bit.

There are also many cases where Swift seems maybe too strict about things, although it's clear that it is doing it to try to stray people away from common mistakes and antipatterns. For example, the condition of an if statement in Swift must always be of type Bool. Unlike languages like Python, things do not have implicit boolean values. if 1 is a syntax error. So is if a unless a is type Bool.

This ends up not being a big problem. The things Swift forces you to do feel like good programming practices. This is not unlike how Python "forces" you to keep your code indented correctly. It feels very different from a language like Java, where the things that you are forced to do all feel like they are there simply to make the compiler writer's life easier. And indeed, unlike Java and Objective-C and much like Python, Swift code requires very little boilerplate. There are no header files for instance.

So all said and done, I like Swift. I don't like it as much as Python (I also don't have my head wrapped around it as much as Python). But it's far better than Objective-C, and that's what matters. Frankly, my biggest gripe with it is the ubiquitous use of CamelCasing and two letter prefixing (NS, CG, SK; I don't know if there's a name for this) in the API. I adamantly refuse to do this with my own variables, because I believe CamelCase reduces readability over underscore_casing. I like the Python convention to use underscore_casing for variables, functions, and methods, and CamelCase for classes (because classes are kind of like proper nouns, and CamelCase is as close to Capitalization as possible in programming language conventions).

Learn to read Objective-C

While it is not necessary to write Objective-C any more, it is a good idea to know how to read it. The reason is that a lot of good resources on the internet are still in Objective-C (also a lot of Apple's example documentation). The API names are the same, so this mainly boils down to understanding how to convert the basic syntax to Swift. Reading the section of the Wikipedia article on the syntax of Objective-C should be enough.

For instance

[touch locationInNode:self]

would be translated to

touch.locationInNode(self)

Use XCode

If you are comfortable with the Swift language itself, you should get started with a project.

First off, you should use XCode to edit your code, at least to begin with, even if you are accustomed to using another editor. The reason is that XCode is going to do a lot of things for you which will make your life easier and reduce the complexity significantly as you get started. Once you are comfortable, you can move to another editor.

Some things that XCode will do for you:

  • Autocompletion: The best way to figure out the Cocoa APIs is to use the autocompletion. This pops up when you want to override a method in a subclass, create an object from an existing class, access an attribute of a class, or instantiate a class or call a function (remember that Swift is polymorphic, so it's useful to know all the possible ways to instantiate a class or call a function).

  • Compiler errors and warnings: Swift, being a strictly typed language, will give you a lot of useful compiler errors. It's pretty hard to write a program incorrectly from a type point of view, and have it even compile. XCode integrates this nicely, and it even offers suggestions on how to fix things every once in a while (so that you can just click the button and have it fixed inline).

  • Direct interaction with the iOS Simulator: One button will compile your code and start the simulator. If your target is Mac OS X, it will open the application.

  • Debugger: Clicking to the left of a line will set a breakpoint in the debugger. The Swift debugger seems pretty limited right now. I wasn't able to get any useful information out of the variables view when I used it. But in my experience using XCode in the past to debug C, its graphical debugger is one of the best.

  • Configuration settings: If you click on the XCode project in the files view (the root node of all the files), you get a view with all the different settings for your project. Most of these you probably won't want to change, but a few are important, like what devices and orientations you want to allow, what operating system versions you want to support, and the name and version of your project. Editing these outside of XCode requires editing an XML file, which is no fun.

Of course, any editor can potentially do these things, and I'm really looking forward to the point where I can just use Emacs to edit Swift code, as the XCode editor as an editor is quite annoying. XCode was the editor that I used before I switched to using Emacs, and it's not gotten much better. There are still several visual glitches in the editor environment, especially with the scope coloring and syntax highlighting. You can edit the keyboard shortcuts in the XCode setting to get some things the way you like them (although I found that trying to set TAB to autoindent did not work). You can also use a tool like Karabiner (previously KeyRemap4MacBook) to enable Vim or Emacs editor shortcuts everywhere (including XCode). It doesn't help that XCode 6 is still in beta (at some point the editor backend died and all syntax highlighting and tab completion stopped working; I managed to fix it by removing a spurious ! in the code)

The iOS Simulator

One disappointing thing that I learned is that you cannot run any iOS program you write on an iOS device unless you are in the paid developer program (or if you Jailbreak and are willing to go through some hoops). The iOS developer program costs $100 a year, and since I'm not sure yet how far I am going to go with this, I am holding off on it.

The only other option then is to run on the simulator. The simulator is fine, the only issue is that there are limits to how you can simulate a touch screen on a computer with a mouse.

A few things to note about the simulator:

  • There are several things you can do with the "hardware" from the hardware menu, such as rotating the device or pressing the home button.

  • It's worth remembering the keyboard shortcut for pressing the home button, ⇧⌘H, as you can press it twice in quick succession just like on a real device to open the task manger. You can then drag your app up to completely reset it, without having to restart the simulator.

  • The retina iPad is taller than your display, even if you have a 15" retina display. So be aware that you will need to scroll up and down to see it all. Alternately, you can use a smaller device, like an iPhone, or rotate it to landscape, where it all fits.

  • The only way to do multitouch is to hold down the Option key. This will create two fingers. However, it's quite limited as the two fingers are always centered around the center of the screen. Therefore if you want to test multitouching two objects, you'll have to position them symmetrically so that you can grab them both.

Getting started with a project

The best way to start is to start a template project with XCode. I personally started with a SpriteKit game for iOS. This created a basic template "Hello World" Swift file with the basic SKScene subclass. Go ahead and compile and run this in the simulator to see what it does.

There are four important methods of SKScene which you will want to override, didMoveToView, touchesBegan, touchesEnded, and update. didMoveToView is the initializer for the scene. Anything that should be set up and appear from the very beginning should go there. touchesBegan and touchesEnded are called when a finger touches the screen and when it leaves the screen, respectively. Remember always that iOS devices are multitouch devices, so these events can happen concurrently, and there can be multiple touches happening at once. The first argument to these methods is a set of touches, which you should iterate over to perform actions (the "Hello World" example shows how to do this). Finally, the update method is called every time the scene is updated, at each "frame" essentially.

There are other methods, for instance, touchesMoved. However, I discovered that you don't actually want to use touchesMoved to do what you would think you'd use it for, namely, to move stuff around. The reason is that there is no easy way to sync up multitouches between touchesBegan (where you know what thing the finger started on) and touchesMoved to move it around. It works well for a single touch, but if you want to be able to move multiple things around at once (which I highly recommend, as it leads to a much nicer user experience), you have to do things a little differently, as I'll explain below.

Adding some objects to your environment

There are a lot of classes to create various objects of various shapes. I started with SKSpriteNode, which creates a simple square, because I wanted to play around with touch events.

I started out with four sprites (yes, it would be better to put these in an array, and probably abstract them to a method):

let sprite1 = SKSpriteNode(color: UIColor(red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0), size: CGSize(width: 30, height: 30))
let sprite2 = SKSpriteNode(color: UIColor(red: 0.0, green: 1.0, blue: 0.0, alpha: 1.0), size: CGSize(width: 30, height: 30))
let sprite3 = SKSpriteNode(color: UIColor(red: 0.0, green: 0.0, blue: 1.0, alpha: 1.0), size: CGSize(width: 30, height: 30))
let sprite4 = SKSpriteNode(color: UIColor(red: 1.0, green: 1.0, blue: 0.0, alpha: 1.0), size: CGSize(width: 30, height: 30))

These lines go at the class level. This lets them be accessed from within any method of the class.

One thing I could not figure out how to do was how to access class variables from within other class variables. In Python, you can do

class Test:
    a = 1
    b = a + 1

But in Swift, if you do

class Test {
    let a = 1
    let b = a + 1
}

it tells you that Test.Type does not have a member named 'a' on the let b = a + 1 line.

You may have to use properties with getters and setters in this case, which I didn't feel like fooling with. The result is that I did not abstract out the CGSize(width: 30, height: 30) into a common variable.

The didMoveToView method then becomes

override func didMoveToView(view: SKView) {
    let center = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame))

    for s in [sprite1, sprite2, sprite3, sprite4] {
        s.position = center

        self.addChild(s)
    }
}

The self.addChild is the most important method here, as it actually puts the sprite in the main view. If you forget this line, none of the sprites will show up.

If you run this, you will only see the yellow box, because you put them all on top of one another in the center of the view.

Adding Basic Physics

We could change the positions so that they do not overlap, but the option I went with was to play around with the physics a little. SpriteKit has a nice 2D physics engine built in, and it's quite easy to use.

So my final didMoveToView was

override func didMoveToView(view: SKView) {
    /* Setup your scene here */
    let center = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame))

    for s in [sprite1, sprite2, sprite3, sprite4] {
        s.position = center

        var physics_body = SKPhysicsBody(rectangleOfSize: CGSize(width: 30, height: 30))

        physics_body.affectedByGravity = false
        physics_body.allowsRotation = false

        s.physicsBody = physics_body
        self.addChild(s)
    }

}

For each sprite, I create an SKPhysicsBody with the exact same size as the SKSpriteNodes (there's probably a more direct way to do this), and attach it to that node. The affectedByGravity property is important. If you don't set it to false, all the objects will fall off the bottom of the screen. I disabled allowsRotation because I wanted my squares to stay upright. Otherwise when when the squares hit one another they will rotate in space.

Now SceneKit will prevent the squares from overlapping with one another, even if we put them on top of each other as we have done.

So now when we start the simulator, we see

Making the squares movable

Now, let's make it so that we can move these squares around. The correct way to do this took me some time to figure out. I finally got some hints from this site.

The key thing here is that the UITouch objects remain the same objects for the duration of the touch. Their position is updated when the touch moves. Hence, you just need to associate each touch with the node that was touched when the touch began, and move the node to the position of that touch with each update.

To start, we will create a dictionary on the class mapping touches to nodes

var selected: [UITouch: SKNode] = [:]

Then, in the touchesBegan method, map every touch to the node that it touches.

override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
    /* Called when a touch begins */

    selected = [:]
    for touch: AnyObject in touches {
        let location = touch.locationInNode(self)

        selected[touch as UITouch] = nodeAtPoint(location)

    }
}

The as UITouch part is needed because the compiler only knows that touch is AnyObject. This was one of the things that was helpfully suggested by the compiler, so I did not really need to know what I was doing to get it right.

Note that even if you touch the background behind the squares, you are still touching a node, namely, the GameScene node itself (the node for the class you are working on). This is a very important observation, as it will tell us how to get the right position for the node when we update it. It also means that we should keep track of which nodes we actually want to be moved by the touch. Trying to move the GameScene node is ignored, at leads to a lot of console logs, so we should avoid it.

Next, let's write the touchesEnded method. This method is simple. If a touch ends (the finger is removed from the screen), we should remove it from the selected dictionary.

override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
    for touch: AnyObject in touches {
    selected[touch as UITouch] = nil
    }
}

To delete an item from a dictionary in Swift, just set it to nil.

Now, finally, we need to write the update method to move the node to the current position of the touch.

The simplest way to do this is

override func update(currentTime: CFTimeInterval) {
    /* Called before each frame is rendered */
    for (touch, node) in selected {
        if !contains([sprite1, sprite2, sprite3, sprite4], node) {
            continue
        }
        let location = touch.locationInNode(self)
        node.position = location
    }
}

Note that we only modify the position for the four sprite nodes.

The touch.locationInNode(self) part took me a long time to figure out. There are other methods, like touch.locationInView(nil), but this does something very strange where the the horizontal axis was doubled (moving the touch one inch moved the object two inches), and the vertical axis was inverted. If someone knows what was going on there, please let me know.

Modifying the position directly is nice, but it's nice to play around a little bit with a third thing from SpriteKit, actions.

What we will do instead of setting the position of the node is to tell SpriteKit to move the node there in a certain amount of time. If we make this time small enough, like 0.01 seconds, it will appear to act exactly the same. If we up this time, there will be a smooth "lag" where the node catches up to the touch. Because the movement always happens in the same amount of time, it will move faster if the finger is farther away. This gives the squares a nice "frictioney" effect with some springiness to it, which is quite nice.

override func update(currentTime: CFTimeInterval) {
    /* Called before each frame is rendered */
    for (touch, node) in selected {
        if !contains([sprite1, sprite2, sprite3, sprite4], node) {
            continue
        }
        let location = touch.locationInNode(self)
        let action = SKAction.moveTo(location, duration: 0.1)
        node.runAction(SKAction.repeatAction(action, count: 1))
    }
}

There are many other actions we can perform, like rotations and color changes.

Conclusion

Here is an example of the movement. You can see it works even with multitouch. You can also see the collision physics cause the other squares to move out of the way when another square hits them.

Here you can see the movement lag caused by using SKAction.moveTo with duration: 0.1 (note that the mouse itself jumps a bit at the beginning, but this is due to lag in the recording).

I have uploaded the full code to GitHub.

This isn't exactly a "game", but it does lay down the foundations for what you would need to write a game in Swift using SpriteKit. At least for me, it shows me the technical basics of how to write some games that I had thought about, which mostly involve making shapes and moving them around the screen.

SciPy 2014

I just finished SciPy 2014, a week-long conference in Austin, TX for scientific computing with Python.

This is my third SciPy (I have been to 2011 and 2013). This year, the conference was noticeably larger. Last year there were ~350 people, this year, there were ~450 people. Aside from there being a lot more people, and the main keynotes taking place in a larger room, the most noticeable consequence of this is that there were three days of talks this year, and three concurrent tracks of talks all three days (last year there were two of each). The conference consisted of two days of tutorials, three days of talks, and two days of sprints, running from July 5 to July 12.

Tutorials

The conference started on Sunday with tutorials. I gave a tutorial on SymPy with Matthew Rocklin and Jason Moore. The videos are on YouTube (parts one, two, three, and four). I gave tutorials for SymPy the previous two times I was at SciPy, although with different people (with Mateusz Paprocki in 2011 and Ondřej Čertík in 2013). I really enjoy seeing new people learn about SymPy, and working with Matthew Rocklin, who is a very good speaker and teacher.

I also attended the tutorial on PyDy by Jason Moore, Gilbert Gede, and Obinna Nwanna (parts one and two). This tutorial was also well done, and I highly recommend it if you are interested in Newtonian mechanics.

I unfortunately was unable to attend any of the other tutorials, but I heard good things about them, especially the Julia tutorial.

Talks

From Tuesday to Thursday were talks. The quality of talks this year was very high. The SciPy talks have always been high quality talks, but this year I felt that they were particularly good. I don't think I saw a bad talk.

Thus, I can't really recommend the good talks that I saw without recommending all of them. You should go to YouTube and the SciPy schedule and watch any talk that looks interesting.

I therefore am going to recommend here the very best talks. Two talks in particular stood out to me as the best.

First is Greg Wilson's Thursday keynote, which is among the best talks I've ever seen from any conference.

Greg mentions a lot of ideas, quite a few of which are controversial, which I think always makes for an interesting talk (it also means that I don't agree with everything he said, although I do agree with most of it). Most of the talk is about pedagogy, especially regarding his experiences at Software Carpentry. Some things he posited:

  • There is actually good research about what methods work and don't work in teaching. He referenced this presentation, which lists just about every possible pedagogical method, and the net difference that it has on students, based on over 50,000 studies. For example, individualized instruction has a very small positive effect, whereas teacher feedback has a very large positive effect. Since each takes time and resources, we should focus on those effects that have the highest impact. Greg pointed out that web-based learning has very little positive effect, and hence is a waste of time and money. The most effective change is removing disruptive students.

    In particular, I liked the quote, "if you want more computing in high school, you have to tell me what to take out." People like to go on that schools need to teach more of this or more of that, and computing and programming tends to be high on that list these days, but anyone who does not discuss what things should be removed from the curriculum, which is already quite full, is not being honest about the discussion.

  • The other big point Greg made is that we need more incremental massive collaboration in teaching. This is the same model that has built open source and Wikipedia, but is mostly absent from teaching. Incremental change is important here, as well. It is more useful for someone to contribute fixes to existing lesson plans, so that they become better for the students, but in his experience, people are much more willing to add new lessons. Greg calls for a "culture of patching". If incremental change could be adopted in teaching, teachers could aggregate methods and lesson plans, removing the massive duplication, and most importantly, making teaching materials that actually work for students to learn. Greg Wilson asks why open source and Wikipedia are able to thrive on massive incremental change, but teaching is not, a question he hasn't found the answer to.

    My thought on the matter is that unlike writing software or collecting and presenting facts, pedagogy is very difficult. If I contribute a patch to an open source project that fixes a bug, I can run the tests to see if my fix is "correct". If I fix an incorrect fact on Wikipedia, it is less easy, but I can still cite and check references to make sure it is correct. But for teaching, it is very difficult to know what methods work and what don't, and as Greg pointed out at the beginning of his talk, the effects of different methods can be very counterintuitive.

The second talk that I recommend is Jake VanderPlas's talk about Frequentism and Bayesianism.

I won't summarize this talk, as Jake has done a much better job in his blog (parts one, two, three, and four). The best thing is to just watch the talk. I will just point out that before the talk, I did not really understand the difference, not being a statistician or someone who works with statistics regularly, and having seen the talk, I now feel that I do. It's a controversial topic, and if you care about the matter, you should know that Jake is a strong Bayesian, although I felt that he gave both sides a fair exposition.

Again, all talks I saw at the conference were good. But those two I felt were the best. I should also mention here that I myself gave a talk on Conda (more on that later).

The Conference

Of course, the talks are only a part of any conference. The best part of SciPy is the gathering of the community. Each year I meet more new people, as well as talk with people I already know, but don't get to see outside of SciPy.

For me, the biggest part of the interactions this year were on Conda and packaging. The background is that I have been working full time for Continuum since January, and I had interned last summer, working primarily on the Conda package manager and Anaconda, the Python distribution. This year, some of the biggest buzz at the conference was about Conda. I'm obviously selection biased, because people came to me specifically to talk about Conda, but I also overheard it in other people's conversations, in several of the presentations, and frankly, the people who did talk to me about Conda were very excited about it. Just like everyone was talking about the IPython Notebook last year and how it has solved the fundamental problems of sharing and presenting data analysis, this year, everyone thanked me for my work on Conda and how it has basically solved the packaging problem, the ubiquitous problem in Python since people started using it.

Conda: The Packaging Problem Solved

Here is the talk I gave on Conda:

I made the claim in my talk that Conda has solved the packaging problem, and the general feel from people I talked to who are using Conda is that it has.

I think this slide from my presentation summarizes why Conda solves the packaging problem.

One of the most amazing things about the scientific Python community, and one of the things that I think really sets it apart from other Python communities, is the use of Python alongside other languages, such as C, C++, Fortran, R, or Julia. No one language is enough to get the job done for serious scientific work. The fundamental brokenness of Python packaging has been that it has focused too much on Python specific tools and processes. The distutils/setuptools/pip/virtualenv stack works great if your code begins and ends with Python. Where it falls over is when you want to link against a C library, compile some Fortran or Cython code, and communicate with other languages like R and Julia. By being a system level package manager, which is fundamentally Python agnostic, Conda is able to deal with all packages equally, whether that package be a Python package, a C extension which other packages link against, or Python itself.

By being truly cross-platform and user installable, Conda is able to reach the maximal number of users, especially those who have historically been hit by the packaging problem the hardest: those who are on Windows or those who do not have admin rights to install necessary tools to install the packages they need.

Finally, Conda installs binaries, not source packages, and its metadata is entirely static (you do not need to execute arbitrary Python code to capture the metadata of a package). These two things remove two of the largest sources of issues with the existing Python packaging tools, such as compiler errors, and nonuniformity in metadata standards (there seem to be as many different ways of writing setup.py as there are packages on PyPI), by removing arbitrary code execution from package installation.

Conda opens up its ecosystem to anybody by making it easy for people to build their own Conda packages using reproducible Conda recipes. And Binstar makes it easy to share those packages. I'm very excited about Binstar, as I think it does for packaging what GitHub has done for open source, i.e., distributes and democratizes it. There are challenges on how to deal with this, of course. As with any distributed democratized system, Binstar can be a wild west of packages. Continuum is thinking about ways to manage this complexity, while still reaping the benefits it provides. If you have any thoughts on things that can be done, let me know in the comments section below.

Of course, solving the packaging problem and removing it are different things. Conda does not make it easier to compile difficult packages. It only makes it so that fewer people have to do it. And there is still work to be done before Conda really takes over the world.

Sprints

The conference ended with two days of sprints. I mainly helped people with Conda packaging. One key thing that happened is that I worked with Aron Ahmadia so that HashDist can generate Conda packages. HashDist is a package compiling framework that makes it easy to have completely reproducible builds by hashing all the information that was used to compile a package, and recompiling when any of that information changes. You can learn more about HashDist by watching Aron's talk from the conference:

I am now convinced that HashDist is a good solution for people who still want the control of compiling their own packages. Once HashDist is able to produce Conda packages, then you can gain the benefits of both worlds: Conda's powerful package management and environment notion, with HashDist's modular and reproducible package building framework.

Other thoughts

The organizers of SciPy did an excellent job this year. The video crew did something which I have not seen before, which is that they uploaded the videos of the talks on the same day that the talks were held. My talk, which was held right before lunch, was uploaded before the last talk of the day. Something that I saw come out of this is that people not attending the conference were able to watch the talks and take part of the conversation with the conference attendees, via Twitter and other social media, or by joining the sprints after the conference.

The extended three days of talks really took their toll on me. The conference starts early enough in the morning and the social events after go so late in the evening that each day of the conference I become a little more sleep deprived. Usually by two days of tutorials and two days of talks I have hit my limit, and this year, I really had a hard time making it through that fifth day. Fortunately for the sprints I was able sleep in a little bit, as it's not a big deal if you miss the beginning.

This year the conference organizers made a push for diversity, and it shows. There were noticeably more women at the conference this year, and not just insomuch as there were more people at all.

Finally, I leave you with the greatest lightening talk. Ever.

Moving to GitHub pages with Nikola

So I've finally decided to move my blog from Wordpress to GitHub pages. I highly recommend it if you are technically skilled enough to do it. I was getting pretty annoyed at Wordpress. It forces you to write your posts in html (or else using their WYSIWYG editor), the wordpress.com is locked down, so you can't add any Javascript, their math is stuck in the past rendering png instead of using MathJax. The list goes on.

With GitHub pages, I can write my posts in Markdown, and I have full control over everything. And there is no lock in. If I decide I don't like the software that is generating the posts, I can easily move to something else, since the post content itself is all Markdown (or the occasional rst or IPython notebook if I want to do something that Markdown doesn't support). I can use MathJax for math (like $ e^{i\pi} + 1 = 0 $). Wordpress.com doesn't let you install abtirary Javascript on your blog, so you can't do things like install MathJax or enable some cool sidebar thing (like a Twitter feed).

Setting up GitHub Pages

First, you need to set up GitHub pages. This is a bit confusing, because there are actually two kinds of GitHub pages, user pages and project pages. User pages are if you have a repo named username.github.io (or .com). The pages are served from the master branch.

For project pages, you add a gh-pages branch to any one of your projects, and GitHub hosts the content automatically at username.github.io/projectname. I originally had my blog content at asmeurer.github.io, but I didn't like that I had to do everything in master, both the generated and original content. So instead I created a repo called blog. I have my content in the master branch and the generated pages in the gh-pages branch (more on this later). At my asmeurer.github.com repo, I just have for now a basic redirect to the blog. In the future, I may want to put additional, non-blog content on the website, and it would go there (or in a separate project repo with its own gh-pages branch).

Nikola

I had initially planned on using Pelican. However, I got stalled on the Wordpress import. I like that Pelican is written in Python, but I was not too keen on their abrasive license. Frankly, I shouldn't say too many bad things about Pelican because I never really tried that hard with it.

I have decided to try Nikola instead. It's also written in Python. It has a very nice license. I like the philosophy of the manual:

DON'T READ THIS MANUAL. IF YOU NEED TO READ IT I FAILED, JUST USE THE THING.

I've also discovered that the Nikola community is very nice. And of course, even if Nikola ends up not being for me, it will be easy to switch, because my actual content is just some Markdown files that I own.

Getting started

Getting started with Nikola is pretty easy. First, you need to install it. It has a ton of dependencies (fortunately all Python, so it won't be that hard). In addition to the ones in the requirements.txt, you should also install markdown and webassets. While using nikola, it will tell you if you don't have something installed that you should, so if you see that, just install what it tells you to. If you use conda and Mac OS X, I have uploaded all the dependencies to my Binstar, so you can just conda install -c asmeurer nikola. Oh and don't worry, Nikola and its dependencies fully support Python 3 (I wouldn't be using it if they didn't).

Then you just run the commands from http://getnikola.com/handbook.html#all-you-need-to-know.

One thing that doesn't tell you is that after you init the site, you should walk through conf.py and change the settings to your liking.

Another trick not there is that you can add

eval "`nikola tabcompletion`"

to your Bash profile to get tab completion.

Tricks

Here are some useful tricks:

  • To enable MathJax, you have to type mathjax in a line by itself in the metadata file. There are some bugs right now, but ideally you could do inline math with $math$ and display math with $$math$$. $math$ doesn't work currently, but you can do \\(math\\) (both \s are required, although this is likely a bug). You can do \\[math\\] for display math. Here are some examples. Inline: $ \sin ^2{x} + \cos^2{x} = 1$. Display: $$ e^{i\pi} + 1 = 0 .$$

  • Your one-stop command when blogging is nikola auto. This requires livereload. This will serve the blog on localhost, and automatically rebuild it when any change is made (and I really mean any change: it can even detect when you change Nikola itself).

  • I have the following in my conf.py to deploy:

DEPLOY_COMMANDS = [
    "git checkout gh-pages",
    "rsync -rPv --delete-after --exclude old_blog --exclude .git --exclude .gitignore --exclude cache/ --exclude .doit.db.db output/ .",
    "git add -A",
    "git commit -a -m 'Updating blog content'",
    "git push",
    "git checkout master",
]

WARNING: These commands are dangerous. If you don't properly exclude things like .git, you will wipe your entire git history. I highly recommend committing everything and pushing to GitHub before deploying.

  • Use
_site/
*.pyc
.DS_Store
.doit.db.db
cache/
output/

for your .gitignore.

  • Despite what it says on the Nikola page, be sure to read the docs, because there are a lot of cool features you won't know about unless you read about them. Also be sure to read through conf.py.

Wordpress import

This is something that I am still figuring out. You can see the progress at http://asmeurer.github.io/blog/old_blog

Importing from Wordpress is pretty easy actually (at least in theory). First you need to go to the Wordpress site dashboard and go to "Export" from the "Tools" menu. From here you can download an XML file with all your content. Then just do

nikola import_wordpress export_file.xml

Note that the current version of Nikola as of this writing (6.3.0) doesn't do this right, so you'll need to use the git master. There are some issues with the import, since Wordpress has its own markup that it doesn't know everything about, so you may need to go in and fix things. Or report them as bugs to Nikola and reimport when they are fixed.

You'll need to go through the posts and make sure that they are rendered correctly (this is one reason I haven't finished doing it yet).

For comments, you first need to create a Disqus account, and enable it in your conf.py. You should then upload the xml file that you exported from Wordpress to Disqus. At this point, the comments should just work, because Nikola sets the Disqus url for the imported comments to the old Wordpress url (look at the Disqus section of one of the built pages).

I don't know how to automatically backlink from Wordpress back to Nikola. Maybe I should just automatically generate some links and paste them in manually.