Apple Push Notifications with Go language


Step 1. Prerequisites



Step 2. The Code.



package main

import (

func main() {

   // load certificates and setup config
   if err != nil {
   conf := &tls.Config {
        Certificates: []tls.Certificate{cert},

   // connect to the APNS and wrap socket to tls client
   if err != nil {
   tlsconn := tls.Client(conn, conf)

   // Force handshake to verify successful authorization.
   // Handshake is handled otherwise automatically on first
   // Read/Write attempt
   err = tlsconn.Handshake()
   if err != nil {
   // informational debugging stuff
   state := tlsconn.ConnectionState()

   // prepare binary payload from JSON structure
   bpayload, err := json.Marshal(payload)

   // decode hexadecimal push device token to binary byte array
   btoken, _ := hex.DecodeString("6b4628de9317c80edd1c791640b58fdfc46d21d0d2d1351687239c44d8e30ab1") 

   // build the actual pdu
   buffer := bytes.NewBuffer([]byte{})
   // command
   binary.Write(buffer, binary.BigEndian, uint8(1))

   // transaction id, optional
   binary.Write(buffer, binary.BigEndian, uint32(1))

   // expiration time, 1 hour

   // push device token
   binary.Write(buffer, binary.BigEndian, btoken)

   // push payload
   binary.Write(buffer, binary.BigEndian, bpayload)
   pdu := buffer.Bytes()

   // write pdu
   _, err = tlsconn.Write(pdu)
   if err != nil {

   // wait for 5 seconds error pdu from the socket

   readb := [6]byte{}
   n, err := tlsconn.Read(readb[:])
   if n > 0 {


Step 3. Compile and Run


$ 6g apn.go
$ 6l apn.6
$ ./6.out
conn state {true 47}


12 Responses to Apple Push Notifications with Go language

  1. How would you compare Go with Node.js / JavaScript and Erlang? In this use case and in general.

    • tikonen says:

      I would do this with Go for real life app, but much more important than the language are available libraries and stable tools.

  2. Kai Backman says:

    Looks good, especially for your first go program. A few comments, you might already know these but the sample is so short it’s hard to tell..

    – A more canonical way than printing the error and calling os.Exit() would be to just call panic. However, in this case I think you did the right thing, the output would be messier if you’d used panic. I would probably wrap the error reporting into a helper function and call that instead:

    func checkError(err os.Error, format string, v …interface{}) {
    if err != nil {
    fmt.Printf(format, v)

    and then call checkError(err, “LoadX509KeyPair: %v\n”, err)

    – You don’t need to explicitly call err.String(), the fmt library uses reflection and can dig the information out for you if you use the generic %v formatting parameter. All os.Errors satisfy the interface { String() string }.

    – The way you build the data structure passed to json.Marshal works (and is pretty short) but would probably get tedious for a more complex input. Usually you just define structs directly and let the json library use reflection to dig the data out:

    type jsonNotification struct {
    Aps map[string] string “aps” // tag required to force lover casing of the json output

    and then your marshaling code would become:
    notification := jsonNotification{
    Aps: map[string] string {“alert”: “Hello Notification”},
    payload, err := json.Marshal(notification)

    – You really should be checking the error from binary.Write.. In this case you know it probably wont fail, but it’s pretty bad form. That said checking every error is quite tedious and the calls to binary.Write are repetitive so when I recently did similar code I wrote this little helper to make the code more readable:

    func bwrite(w io.Writer, values …interface{}) os.Error {
    for _, v := range values {
    err := binary.Write(w, binary.BigEndian, v)
    if err != nil {
    return err
    return nil

    you can then replace the mid section with this:
    err := bwrite(buffer, uint8(1). uint32(1), uint32(time.Seconds() + 60*60), uint16(len(btoken)), btoken, uint16(len(bpayload)), bpayload)
    checkError(err, “binary write %v\n”, err)

    or split if up into several writes if you want to.

    I can see who has been influencing Petteri to tinker around with Go.. :-)

    • tikonen says:

      Thanks for feedback! I didn’t realize the binary Write could fail, unless you mean Out of memory condition?

      • Kai Backman says:

        binary.Write will fail if the underlying stream fails. In this case you are writing to a bytes.Buffer so it wont fail except with OOM. However, memory depletion doesn’t really count as there is usually not much you can do if you detect it.

        I’m just a stickler with checking error values, you never know when something in the code changes subtly and the person doing the change misses a hidden assumption like the one above. It’s more a question of canonical form than an actual error in this particular code.

  3. Kai Backman says:

    Duh, just noticed the existing binary.Write already supports passing multiple values. The benefit of the shorthand is mostly to hide the encoding which probably isn’t useful. So you could just say this:

    err := binary.Write(buffer, binary.BigEndian, uint8(1). uint32(1), uint32(time.Seconds() + 60*60), uint16(len(btoken)), btoken, uint16(len(bpayload)), bpayload)

  4. Nice feedback!

    Kai, I actually pointed Teemu towards Go. I’m a specialist in finding new languages and platforms and then having neither time nor skills to really test them.

  5. sj2004 says:

    Yeah, for first program this is impressive. It looks clean, compact and easy to read.
    Great write up.

  6. Ryan says:

    HI, thanks for the post. I’m using this as a starting point for writing a push notification service and it’s been very useful so far. I have a question about the .pem files.

    How do you generate them? I have the certificate from Apple as well as the private key. What is meant to be in each of the .pem files and how would I generate them from the keychain?


  7. Pingback: Apple Push Notifications with Haskell « Brave New Method

  8. Pingback: Simple test server for Apple push notification and feedback integration | Brave New Method

Leave a Reply


You are commenting using your account. Log Out /  Change )

Google photo


Twitter picture


Facebook photo


Connecting to %s

%d bloggers like this: