apns2

repository·master·Indexed 25 days ago

https://github.com/sideshow/apns2

A Go package for sending Apple Push Notifications (iOS, OSX, Safari) using the HTTP/2 protocol. It supports certificate-based (.p12, .pem) and JWT token-based (.p8) authentication, providing a ClientManager for connection pooling and a payload builder for structured notifications. The library supports various push types including alert, background, location, VoIP, and Live Activities.

Tokens
4.4K
Snippets
8
Records
26
Agent score
84%

What's inside apns2

  1. Optimize APNS performance

    master

    To achieve high throughput:

    1. Reuse Clients: Do not create a new apns2.Client for every push. The TLS handshake is expensive. Hold onto a single client instance to leverage the underlying connection pool.
    2. Scale Instances: A single client can handle 4,000+ pushes per second. If you need more, use one instance per CPU core.
    3. Server Location: Place your server close to Apple's servers (e.g., AWS US regions) to minimize network latency.
  2. Initialize an APNs Client

    master

    You can create an APNs client using two different authentication methods:

    1. Certificate-based authentication: Use NewClient(certificate tls.Certificate) when you have a .p12 or .pem certificate.
    2. Token-based authentication (JWT): Use NewTokenClient(token *token.Token) when using a JWT token.

    Important: As per Apple's API guidelines, you should keep a single handle on the Client and reuse it across multiple notifications. Do not repeatedly open and close connections, as APNs may treat rapid connection/disconnection as a DoS attack.

  3. Send push notifications using certificate-based authentication

    master

    You can authenticate with Apple using a .p12 or .pem certificate. Use certificate.FromP12File to load your certificate and apns2.NewClient(cert) to create a client. You must specify whether to use .Development() or .Production() environments.

    package main
    
    import (
      "log"
      "fmt"
    
      "github.com/sideshow/apns2"
      "github.com/sideshow/apns2/certificate"
    )
    
    func main() {
    
      cert, err := certificate.FromP12File("../cert.p12", "")
      if err != nil {
        log.Fatal("Cert Error:", err)
      }
    
      notification := &apns2.Notification{}
      notification.DeviceToken = "11aa01229f15f0f0c52029d8cf8cd0aeaf2365fe4cebc4af26cd6d76b7919ef7"
      notification.Topic = "com.sideshow.Apns2"
      notification.Payload = []byte(`{"aps":{"alert":"Hello!"}}`)
    
      // Use .Development() for Xcode builds, .Production() for App Store/Ad-hoc
      client := apns2.NewClient(cert).Production()
      res, err := client.Push(notification)
    
      if err != nil {
        log.Fatal("Error:", err)
      }
    
      fmt.Printf("%v %v %v\n", res.StatusCode, res.ApnsID, res.Reason)
    }
  4. Send push notifications using JWT Token Authentication

    master

    Instead of certificates, you can use Apple's Token Based Authentication (JWT). This requires a .p8 signing key, a KeyID, and a TeamID from your Apple developer account. Use apns2.NewTokenClient(token) to create the client. JWT keys work for both development and production environments and do not expire.

    authKey, err := token.AuthKeyFromFile("../AuthKey_XXX.p8")
    if err != nil {
      log.Fatal("token error:", err)
    }
    
    token := &token.Token{
      AuthKey: authKey,
      // KeyID from developer account
      KeyID:   "ABC123DEFG",
      // TeamID from developer account
      TeamID:  "DEF123GHIJ",
    }
    
    client := apns2.NewTokenClient(token)
    res, err := client.Push(notification)
  5. Construct payloads with the payload builder

    master

    While you can use raw bytes for notification.Payload, you can also use the payload package to build structured APNs payloads easily.

    // Result: {"aps":{"alert":"hello","badge":1},"key":"val"}
    
    payload := payload.NewPayload().Alert("hello").Badge(1).Custom("key", "val")
    
    notification.Payload = payload
    client.Push(notification)
  6. Handle APNS responses and errors

    master

    The Push method returns both a Response and an error.

    • error: Returned for unrecoverable issues (e.g., connection problems, certificate errors, or if the payload was never sent).
    • Response: Returned if the payload was successfully sent to Apple. Use res.Sent() to check if the notification was accepted by Apple.
    res, err := client.Push(notification)
    if err != nil {
      log.Println("There was an error", err)
      return
    }
    
    if res.Sent() {
      log.Println("Sent:", res.ApnsID)
    } else {
      fmt.Printf("Not Sent: %v %v %v\n", res.StatusCode, res.ApnsID, res.Reason)
    }
  7. Configure an apns2.Notification

    master

    A Notification requires three minimum fields: DeviceToken, Topic, and Payload.

    Optional fields include:

    • ApnsID: A unique identifier.
    • Expiration: A time.Time value.
    • Priority: Set using apns2.PriorityLow or other priority constants.
    notification := &apns2.Notification{
      DeviceToken: "11aa01229f15f0f0c52029d8cf8cd0aeaf2365fe4cebc4af26cd6d76b7919ef7",
      Topic: "com.sideshow.Apns2",
      Payload: []byte(`{"aps":{"alert":"Hello!"}}`),
    }
    
    notification.ApnsID = "40636A2C-C093-493E-936A-2A4333C06DEA"
    notification.Expiration = time.Now()
    notification.Priority = apns2.PriorityLow
  8. Use Context for timeouts and cancellations

    master

    To control request cancellations and timeouts, use PushWithContext(ctx, notification) instead of Push. This allows you to cancel all pushes when a parent process is cancelled or to set fine-grained timeouts for individual pushes.

    ctx, cancel = context.WithTimeout(context.Background(), 10 * time.Second)
    res, err := client.PushWithContext(ctx, notification)
    defer cancel()
  9. Configure ClientManager settings

    master

    You can customize the behavior of the ClientManager by setting the following fields:

    • MaxSize: The maximum number of clients allowed. When this limit is reached, the least recently used client is evicted. Set to 0 for no limit.
    • MaxAge: The maximum duration a client can remain unused in the manager. If a client is retrieved after this duration, it is evicted and a new one is created via the Factory. Set to 0 to disable age-based eviction.
    • Factory: A function used to construct clients if they are not found in the manager. It must accept a tls.Certificate and return a *Client.
  10. Use the apns2 command line tool

    master

    The apns2 CLI tool allows you to send notifications via STDIN.

    Installation:

    go get github.com/sideshow/apns2/apns2

    Usage: apns2 --certificate-path=CERTIFICATE-PATH --topic=TOPIC [<flags>]

    Input Format: <DeviceToken> <APNS Payload>

    Example: aff0c63d9eaa63ad161bafee732d5bc2c31f66d552054718ff19ce314371e5d0 {"aps": {"alert": "hi"}}

    Flags:

    • -c, --certificate-path=CERTIFICATE-PATH: Path to certificate file.
    • -t, --topic=TOPIC: The topic (usually the app bundle ID).
    • -m, --mode="production": APNS server (production or development). Defaults to production.
    • --help: Show help.
  11. Send a Notification with Push or PushWithContext

    master

    To send a notification to a device, use the Push or PushWithContext methods.

    • Push(n *Notification): Sends a notification using context.Background().
    • PushWithContext(ctx Context, n *Notification): Sends a notification with a specific context, allowing for custom timeouts or cancellation signals.

    Both methods return a *Response indicating if the notification was accepted or rejected, or an error if the request failed.