diff --git a/go.mod b/go.mod
index cbd3b4f84c2ecda77836cd87f631d34326485e8e..750797553056cdcff5b0b42a02dda39b51ee945b 100644
--- a/go.mod
+++ b/go.mod
@@ -9,6 +9,7 @@ require (
 	firebase.google.com/go v3.12.0+incompatible
 	github.com/jonahh-yeti/apns v0.0.1
 	github.com/pkg/errors v0.9.1
+	github.com/sideshow/apns2 v0.20.0
 	github.com/spf13/cobra v1.0.0
 	github.com/spf13/jwalterweatherman v1.1.0
 	github.com/spf13/viper v1.7.0
diff --git a/go.sum b/go.sum
index 811e5c87ec25d3606cc5c1717155751d99d76db7..f9d445a30cf8a5acb448cae35f5aac20bb159091 100644
--- a/go.sum
+++ b/go.sum
@@ -327,6 +327,8 @@ github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9Nz
 github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc h1:jUIKcSPO9MoMJBbEoyE/RJoE8vz7Mb8AjvifMMwSyvY=
 github.com/shopspring/decimal v0.0.0-20200227202807-02e2044944cc/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
 github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
+github.com/sideshow/apns2 v0.20.0 h1:5Lzk4DUq+waVc6/BkKzpDTpQjtk/BZOP0YsayBpY1NE=
+github.com/sideshow/apns2 v0.20.0/go.mod h1:f7dArLPLbiZ3qPdzzrZXdCSlMp8FD0p6z7tHssDOLvk=
 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
 github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
diff --git a/notifications/apns/apns.go b/notifications/apns/apns.go
new file mode 100644
index 0000000000000000000000000000000000000000..2e3f8e15bfba4ef4ca0815040a1f865247922be4
--- /dev/null
+++ b/notifications/apns/apns.go
@@ -0,0 +1,19 @@
+package apns
+
+import "github.com/sideshow/apns2"
+
+type ApnsComm struct {
+	*apns2.Client
+	topic string
+}
+
+func NewApnsComm(cl *apns2.Client, topic string) *ApnsComm {
+	return &ApnsComm{
+		Client: cl,
+		topic:  topic,
+	}
+}
+
+func (c *ApnsComm) GetTopic() string {
+	return c.topic
+}
diff --git a/firebase/fcm.go b/notifications/firebase/fcm.go
similarity index 96%
rename from firebase/fcm.go
rename to notifications/firebase/fcm.go
index cbe26d45b50b1c02c0accdcd4bce7e31ea832f2f..94bbfb4d0eb0a0af00c0cd7f7badc82bee0ad183 100644
--- a/firebase/fcm.go
+++ b/notifications/firebase/fcm.go
@@ -21,13 +21,13 @@ import (
 )
 
 // function types for use in notificationsbot struct
-type SetupFunc func(string) (*messaging.Client, context.Context, error)
 type SendFunc func(FBSender, string, *mixmessages.NotificationData) (string, error)
 
 // FirebaseComm is a struct which holds the functions to setup the messaging app and sending notifications
 // Using a struct in this manner allows us to properly unit test the NotifyUser function
 type FirebaseComm struct {
 	SendNotification SendFunc
+	*messaging.Client
 }
 
 // FBSender is an interface which matches the send function in the messaging app, allowing us to unit test sendNotification
@@ -36,9 +36,10 @@ type FBSender interface {
 }
 
 // NewFirebaseComm create a *FirebaseComm object with the proper setup and send functions
-func NewFirebaseComm() *FirebaseComm {
+func NewFirebaseComm(cl *messaging.Client) *FirebaseComm {
 	return &FirebaseComm{
 		SendNotification: sendNotification,
+		Client:           cl,
 	}
 }
 
diff --git a/firebase/fcm_test.go b/notifications/firebase/fcm_test.go
similarity index 98%
rename from firebase/fcm_test.go
rename to notifications/firebase/fcm_test.go
index 70741aec18f7f8077661c80ced17cd17af636fd2..08650bbdc49e3a9401a13a0debe9c0383695235a 100644
--- a/firebase/fcm_test.go
+++ b/notifications/firebase/fcm_test.go
@@ -37,7 +37,7 @@ func TestSendNotification(t *testing.T) {
 
 // Unit test the NewFirebaseComm method
 func TestNewFirebaseComm(t *testing.T) {
-	comm := NewFirebaseComm()
+	comm := NewFirebaseComm(nil)
 	if comm.SendNotification == nil {
 		t.Error("Failed to set functions in comm")
 	}
diff --git a/notifications/notifications.go b/notifications/notifications.go
index c7a0aa2fe307a2ccd17cb4540d399e04a5ee6726..f500602d2104a653fbef6857860d24ba5a2e3a77 100644
--- a/notifications/notifications.go
+++ b/notifications/notifications.go
@@ -10,16 +10,20 @@ package notifications
 
 import (
 	"encoding/base64"
-	"firebase.google.com/go/messaging"
-	"github.com/jonahh-yeti/apns"
+	"gitlab.com/elixxir/notifications-bot/notifications/apns"
+
+	// "github.com/jonahh-yeti/apns"
 	"github.com/pkg/errors"
+	"github.com/sideshow/apns2"
+	"github.com/sideshow/apns2/payload"
+	apnstoken "github.com/sideshow/apns2/token"
 	jww "github.com/spf13/jwalterweatherman"
 	pb "gitlab.com/elixxir/comms/mixmessages"
 	"gitlab.com/elixxir/comms/network"
 	"gitlab.com/elixxir/comms/notificationBot"
 	"gitlab.com/elixxir/crypto/hash"
 	"gitlab.com/elixxir/crypto/registration"
-	"gitlab.com/elixxir/notifications-bot/firebase"
+	"gitlab.com/elixxir/notifications-bot/notifications/firebase"
 	"gitlab.com/elixxir/notifications-bot/storage"
 	"gitlab.com/xx_network/comms/connect"
 	"gitlab.com/xx_network/crypto/signature/rsa"
@@ -34,10 +38,7 @@ import (
 )
 
 // Function type definitions for the main operations (poll and notify)
-type NotifyFunc func(*pb.NotificationData, ApnsSender, *messaging.Client, *firebase.FirebaseComm, *storage.Storage) error
-type ApnsSender interface {
-	Send(token string, p apns.Payload, opts ...apns.SendOption) (*apns.Response, error)
-}
+type NotifyFunc func(*pb.NotificationData, *apns.ApnsComm, *firebase.FirebaseComm, *storage.Storage) error
 
 // Params struct holds info passed in for configuration
 type Params struct {
@@ -61,8 +62,8 @@ type Impl struct {
 	Storage     *storage.Storage
 	inst        *network.Instance
 	notifyFunc  NotifyFunc
-	fcm         *messaging.Client
-	apnsClient  *apns.Client
+	fcm         *firebase.FirebaseComm
+	apnsClient  *apns.ApnsComm
 	receivedNdf *uint32
 
 	ndfStopper Stopper
@@ -92,18 +93,19 @@ func StartNotifications(params Params, noTLS, noFirebase bool) (*Impl, error) {
 	}
 
 	// Set up firebase messaging client
-	var app *messaging.Client
+	var fbComm *firebase.FirebaseComm
 	if !noFirebase {
-		app, err = firebase.SetupMessagingApp(params.FBCreds)
+		app, err := firebase.SetupMessagingApp(params.FBCreds)
 		if err != nil {
 			return nil, errors.Wrap(err, "Failed to setup firebase messaging app")
 		}
+		fbComm = firebase.NewFirebaseComm(app)
 	}
 	receivedNdf := uint32(0)
 
 	impl := &Impl{
 		notifyFunc:  notifyUser,
-		fcm:         app,
+		fcm:         fbComm,
 		receivedNdf: &receivedNdf,
 	}
 
@@ -113,27 +115,27 @@ func StartNotifications(params Params, noTLS, noFirebase bool) (*Impl, error) {
 		if params.APNS.KeyID == "" || params.APNS.Issuer == "" || params.APNS.BundleID == "" {
 			return nil, errors.WithMessagef(err, "APNS not properly configured: %+v", params.APNS)
 		}
-		apnsKey, err := utils.ReadFile(params.APNS.KeyPath)
+
+		authKey, err := apnstoken.AuthKeyFromFile(params.APNS.KeyPath)
 		if err != nil {
-			return nil, errors.WithMessage(err, "Failed to read APNS key")
+			return nil, errors.WithMessage(err, "Failed to load auth key from file")
+		}
+		token := &apnstoken.Token{
+			AuthKey: authKey,
+			// KeyID from developer account (Certificates, Identifiers & Profiles -> Keys)
+			KeyID: params.APNS.KeyID,
+			// TeamID from developer account (View Account -> Membership)
+			TeamID: params.APNS.Issuer,
 		}
-		var endpoint apns.ClientOption
+		apnsClient := apns2.NewTokenClient(token)
 		if params.APNS.Dev {
-			jww.INFO.Println("")
-			endpoint = apns.WithEndpoint(apns.DevelopmentGateway)
+			jww.INFO.Printf("Running with dev apns gateway")
+			apnsClient.Development()
 		} else {
-			endpoint = apns.WithEndpoint(apns.ProductionGateway)
+			apnsClient.Production()
 		}
-		apnsClient, err := apns.NewClient(
-			apns.WithJWT(apnsKey, params.APNS.KeyID, params.APNS.Issuer),
-			apns.WithBundleID(params.APNS.BundleID),
-			apns.WithMaxIdleConnections(100),
-			apns.WithTimeout(5*time.Second),
-			endpoint)
-		if err != nil {
-			return nil, errors.WithMessage(err, "Failed to setup apns client")
-		}
-		impl.apnsClient = apnsClient
+
+		impl.apnsClient = apns.NewApnsComm(apnsClient, params.APNS.BundleID)
 	}
 
 	// Start notification comms server
@@ -171,7 +173,7 @@ func NewImplementation(instance *Impl) *notificationBot.Implementation {
 
 // NotifyUser accepts a UID and service key file path.
 // It handles the logic involved in retrieving a user's token and sending the notification
-func notifyUser(data *pb.NotificationData, apnsClient ApnsSender, fcm *messaging.Client, fc *firebase.FirebaseComm, db *storage.Storage) error {
+func notifyUser(data *pb.NotificationData, apnsClient *apns.ApnsComm, fc *firebase.FirebaseComm, db *storage.Storage) error {
 	elist, err := db.GetEphemeral(data.EphemeralID)
 	if err != nil {
 		if errors.Is(err, gorm.ErrRecordNotFound) {
@@ -188,25 +190,23 @@ func notifyUser(data *pb.NotificationData, apnsClient ApnsSender, fcm *messaging
 		}
 
 		isAPNS := !strings.Contains(u.Token, ":")
-		mutableContent := 1
+		// mutableContent := 1
 		if isAPNS {
 			jww.INFO.Printf("Notifying ephemeral ID %+v via APNS to token %+v", data.EphemeralID, u.Token)
-			resp, err := apnsClient.Send(u.Token, apns.Payload{
-				APS: apns.APS{
-					Alert: apns.Alert{
-						Title: "Privacy: protected!",
-						Body:  "Some notifications are not for you to ensure privacy; we hope to remove this notification soon",
-					},
-					MutableContent: &mutableContent,
-				},
-				CustomValues: map[string]interface{}{
-					"messagehash":         base64.StdEncoding.EncodeToString(data.MessageHash),
-					"identityfingerprint": base64.StdEncoding.EncodeToString(data.IdentityFP),
-				},
-			}, apns.WithExpiration(604800), // 1 week
-				apns.WithPriority(10),
-				apns.WithCollapseID(base64.StdEncoding.EncodeToString(u.TransmissionRSAHash)),
-				apns.WithPushType("alert"))
+			notifPayload := payload.NewPayload().AlertTitle("Privacy: protected!").AlertBody(
+				"Some notifications are not for you to ensure privacy; we hope to remove this notification soon").MutableContent().Custom(
+				"messagehash", base64.StdEncoding.EncodeToString(data.MessageHash)).Custom(
+				"identityfingerprint", base64.StdEncoding.EncodeToString(data.IdentityFP))
+			notif := &apns2.Notification{
+				CollapseID:  base64.StdEncoding.EncodeToString(u.TransmissionRSAHash),
+				DeviceToken: u.Token,
+				Expiration:  time.Now().Add(time.Hour * 24 * 7),
+				Priority:    apns2.PriorityHigh,
+				Payload:     notifPayload,
+				PushType:    apns2.PushTypeAlert,
+				Topic:       apnsClient.GetTopic(),
+			}
+			resp, err := apnsClient.Push(notif)
 			if err != nil {
 				jww.ERROR.Printf("Failed to send notification via APNS: %+v: %+v", resp, err)
 				// TODO : Should be re-enabled for specific error cases? deep dive on apns docs may be helpful
@@ -218,7 +218,7 @@ func notifyUser(data *pb.NotificationData, apnsClient ApnsSender, fcm *messaging
 				jww.INFO.Printf("Notified ephemeral ID %+v [%+v] and received response %+v", data.EphemeralID, u.Token, resp)
 			}
 		} else {
-			resp, err := fc.SendNotification(fcm, u.Token, data)
+			resp, err := fc.SendNotification(fc.Client, u.Token, data)
 			if err != nil {
 				// Catch two firebase errors that we don't want to crash on
 				// 400 and 404 indicate that the token stored is incorrect
@@ -335,9 +335,8 @@ func (nb *Impl) ReceiveNotificationBatch(notifBatch *pb.NotificationBatch, auth
 
 	jww.INFO.Printf("Received notification batch for round %+v", notifBatch.RoundID)
 
-	fbComm := firebase.NewFirebaseComm()
 	for _, notifData := range notifBatch.GetNotifications() {
-		err := nb.notifyFunc(notifData, nb.apnsClient, nb.fcm, fbComm, nb.Storage)
+		err := nb.notifyFunc(notifData, nb.apnsClient, nb.fcm, nb.Storage)
 		if err != nil {
 			return err
 		}
diff --git a/notifications/notifications_test.go b/notifications/notifications_test.go
index c7fc27518ebf6f4b080a69b9a9bd3c086a3ed0d0..b2d9de848ced32e0be43f6cad0ee17d3c09bf80e 100644
--- a/notifications/notifications_test.go
+++ b/notifications/notifications_test.go
@@ -6,14 +6,14 @@
 package notifications
 
 import (
-	"firebase.google.com/go/messaging"
 	"fmt"
-	"github.com/jonahh-yeti/apns"
 	"github.com/pkg/errors"
+	"github.com/sideshow/apns2"
 	pb "gitlab.com/elixxir/comms/mixmessages"
 	"gitlab.com/elixxir/crypto/hash"
 	"gitlab.com/elixxir/crypto/registration"
-	"gitlab.com/elixxir/notifications-bot/firebase"
+	"gitlab.com/elixxir/notifications-bot/notifications/apns"
+	"gitlab.com/elixxir/notifications-bot/notifications/firebase"
 	"gitlab.com/elixxir/notifications-bot/storage"
 	"gitlab.com/xx_network/comms/connect"
 	"gitlab.com/xx_network/crypto/csprng"
@@ -29,12 +29,6 @@ import (
 
 var port = 4200
 
-type MockApns struct{}
-
-func (m *MockApns) Send(token string, p apns.Payload, opts ...apns.SendOption) (*apns.Response, error) {
-	return nil, nil
-}
-
 // Test notificationbot's notifyuser function
 // this mocks the setup and send functions, and only tests the core logic of this function
 func TestNotifyUser(t *testing.T) {
@@ -68,11 +62,13 @@ func TestNotifyUser(t *testing.T) {
 	if err != nil {
 		t.Errorf("Failed to add latest ephemeral: %+v", err)
 	}
+
+	ac := apns.NewApnsComm(apns2.NewTokenClient(nil), "")
 	err = notifyUser(&pb.NotificationData{
 		EphemeralID: eph.EphemeralId,
 		IdentityFP:  nil,
 		MessageHash: nil,
-	}, &MockApns{}, nil, fcBadSend, s)
+	}, ac, fcBadSend, s)
 	if err == nil {
 		t.Errorf("Should have returned an error")
 	}
@@ -81,7 +77,7 @@ func TestNotifyUser(t *testing.T) {
 		EphemeralID: eph.EphemeralId,
 		IdentityFP:  nil,
 		MessageHash: nil,
-	}, &MockApns{}, nil, fc, s)
+	}, ac, fc, s)
 	if err != nil {
 		t.Errorf("Failed to notify user properly")
 	}
@@ -292,7 +288,7 @@ func TestImpl_UnregisterForNotifications(t *testing.T) {
 func TestImpl_ReceiveNotificationBatch(t *testing.T) {
 	impl := getNewImpl()
 	dataChan := make(chan *pb.NotificationData)
-	impl.notifyFunc = func(data *pb.NotificationData, apns ApnsSender, f *messaging.Client, fc *firebase.FirebaseComm, s *storage.Storage) error {
+	impl.notifyFunc = func(data *pb.NotificationData, apns *apns.ApnsComm, fc *firebase.FirebaseComm, s *storage.Storage) error {
 		go func() { dataChan <- data }()
 		return nil
 	}