Skip to content

Commit

Permalink
address internal comments: shorten the docs.
Browse files Browse the repository at this point in the history
  • Loading branch information
ItzNotABug committed Feb 20, 2025
1 parent e7c9cca commit 1135da3
Showing 1 changed file with 43 additions and 186 deletions.
229 changes: 43 additions & 186 deletions src/routes/docs/products/messaging/send-push-notifications/+page.markdoc
Original file line number Diff line number Diff line change
Expand Up @@ -39,74 +39,45 @@ First, register for remote notifications in your app delegate's `application(_:d
```swift
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {
// Register for remote notifications
UNUserNotificationCenter.current().delegate = self

let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(
options: authOptions,
completionHandler: { granted, error in
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, _ in
if granted {
DispatchQueue.main.async {
if granted {
application.registerForRemoteNotifications()
}
application.registerForRemoteNotifications()
}
}
)

}
return true
}
```


Next, create a handler for when the app receives the push notification device token.
```swift
func application(
_ application: UIApplication,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
) {
// register device
var token = deviceToken.map { String(format: "%.2hhx", $0) }.joined()

// Save the token to be used later
UserDefaults.standard.set(token, forKey: "apnsToken")
/* store this `token` */
let token = deviceToken.map { String(format: "%.2hhx", $0) }.joined()
}

```

Since the token is saved in `UserDefaults`, you can access it from anywhere in your app.
With this saved `apnsToken`, you can create a push target with Appwrite when the user logs in.
Each push target is associated with an account, heres an example with an email password login.
The same logic applies to all types of login methods.
```swift
func login() async {
do {
let session = try await account.createEmailPasswordSession(
email: username,
password: password
)
let session = try await account.createEmailPasswordSession(email: username, password: password)

guard let token = UserDefaults.standard.string(forKey: "apnsToken") else {
return
}

guard let target = try? await account.createPushTarget(
targetId: ID.unique(),
identifier: token
) else {
return
}
let token = /* Retrieve the stored push token */

UserDefaults.standard.set(target.id, forKey: "targetId")

DispatchQueue.main.async {
self.response = String(describing: session.toMap())
}
try await account.createPushTarget(targetId: ID.unique(), identifier: token)
} catch {
DispatchQueue.main.async {
self.response = error.localizedDescription
}
print("Login failed: \(error.localizedDescription)")
}
}
```
Expand All @@ -122,23 +93,15 @@ initialize Firebase in your main activity and fetch the FCM registration token.
```kotlin
class MainActivity : AppCompatActivity() {
override fun onCreate() {
// ... other logic

// Initialize Firebase
FirebaseApp.initializeApp(this)

// Set the FCM token
FirebaseMessaging.getInstance().token.addOnCompleteListener(OnCompleteListener { task ->
if (!task.isSuccessful) {
Log.w(TAG, "Fetching FCM registration token failed", task.exception)
return@OnCompleteListener
if (task.isSuccessful) {
/* store this `token` */
val token = task.result
}

// Get new FCM registration token and save it in prefs to be used later.
val token = task.result
val prefs = getSharedPreferences("example", MODE_PRIVATE)

prefs.edit().putString("fcmToken", token).apply()
})
}
}
Expand All @@ -151,64 +114,22 @@ For example, when the user logs in with email and password, your app
can register itself as a target after handling the login.

```kotlin
class AccountsViewModel(application: Application) : AndroidViewModel(application) {

private val prefs = getApplication<Application>().getSharedPreferences("example", MODE_PRIVATE)

private val _error = MutableLiveData<Event<Exception>>().apply { value = null }
val error: LiveData<Event<Exception>> = _error

private val _response = MutableLiveData<Event<String>>().apply { value = null }
val response: LiveData<Event<String>> = _response

private val _target = MutableLiveData<Event<Target>>().apply { value = null }
val target: LiveData<Event<Target>> = _target
fun login(email: String, password: String) {
viewModelScope.launch {
try {
val session = account.createEmailPasswordSession(email, password)

fun onLogin(
email: String,
password: String,
token: String?,
) {
viewModelScope.launch {
try {
// Log in the user
val session = account.createEmailPasswordSession(
email,
password
)
let token = /* Retrieve the stored push token */

// If a token exists, register a push target with Appwrite.
if (token != null) {
val target = account.createPushTarget(ID.unique(), token)

prefs.edit().putString("targetId", target.id).apply()

_target.postValue(Event(target))
}

_response.postValue(Event(session.toJson()))
} catch (e: AppwriteException) {
_error.postValue(Event(e))
}
/* store the `target.id` */
val target = account.createPushTarget(ID.unique(), token)
} catch (e: AppwriteException) {
Log.e("Login", "Failed: ${e.message}")
}
}
}
```

The FCM token that we defined in `sharedPreferences` will be passed into the `onLogin` handler
to create the push target.
```kotlin
binding.login.setOnClickListener{
viewModel.onLogin(
binding.email.text.toString(),
binding.password.text.toString(),
context
?.getSharedPreferences("example", Context.MODE_PRIVATE)
?.getString("fcmToken", null) ?: null
)
}
```

Lastly, because FCM push tokens can change, we need to add a service to handle FCM token
refreshes and update the target with Appwrite Messaging.

Expand All @@ -217,25 +138,13 @@ the FCM token is updated.

```kotlin
class MessagingService : FirebaseMessagingService() {

private val prefs = getSharedPreferences("example", MODE_PRIVATE)

// This is called when FCM token is updated.
override fun onNewToken(token: String) {
super.onNewToken(token)

prefs.edit().putString("fcmToken", token).apply()

val targetId = prefs.getString("targetId", null)

/* store the `token` */
/* If the user is logged in, update the push target */
runBlocking {
if (targetId == null) {
val target = account?.createPushTarget(ID.unique(), token)

prefs.edit().putString("targetId", target.id).apply()
} else {
account?.updatePushTarget(targetId, token)
}
account?.updatePushTarget(/* retrieve saved `target.id` */, token)
}
}
}
Expand Down Expand Up @@ -271,108 +180,56 @@ implement the messaging delegate protocol, and register for remote notifications
```swift
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
) -> Bool {
// initialize Firebase
FirebaseApp.configure()

// Set the messaging delegate
Messaging.messaging().delegate = self

// Register for remote notifications
UNUserNotificationCenter.current().delegate = self

let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(
options: authOptions,
completionHandler: { granted, error in
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, _ in
if granted {
DispatchQueue.main.async {
if granted {
application.registerForRemoteNotifications()
}
application.registerForRemoteNotifications()
}
}
)

}
return true
}
```
Your APNS token can change, so you need to handle the token refresh event and update the target with Appwrite Messaging.
Implement `didReceiveRegistrationToken`, which is called when the FCM token is updated.
```swift
func messaging(
_ messaging: FirebaseMessaging.Messaging,
didReceiveRegistrationToken fcmToken: String?
) {
guard let fcmToken = fcmToken else {
return
}

// Save the token to be used later
UserDefaults.standard.set(fcmToken , forKey: "fcmToken")

// Get the current Appwrite targetId from UserDefaults
let targetId = UserDefaults.standard.string(forKey: "targetId")
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
/* store the fcmToken */
guard let fcmToken = fcmToken else { return }

Task {
do {
_ = try await account.get()
try await account.createPushTarget(targetId: ID.unique(), identifier: fcmToken)
} catch {
return // if not logged in, don't update the target
}

// if targetId is nil, create a new target
if targetId == nil {
let target = try? await account.createPushTarget(
targetId: ID.unique(),
identifier: fcmToken
)

UserDefaults.standard.set(target?.id , forKey: "targetId")
} else {
// if targetId exists for the Appwrite Account, update the target
_ = try? await account.updatePushTarget(
targetId: targetId!,
identifier: fcmToken
)
print("Failed to create push target: \(error.localizedDescription)")
}
}
}
```
Since the token is saved in `UserDefaults`, you can access it from anywhere in your app.
With this saved `fcmToken`, you can create a push target with Appwrite when the user logs in.
Each push target is associated with an account, heres an example with an email password login.
Each push target is associated with an account, here's an example with an email password login.
The same logic applies to all types of login methods.
```swift
func login() async {
do {
let session = try await account.createEmailPasswordSession(
email: username,
password: password
)
let session = try await account.createEmailPasswordSession(email: username, password: password)

guard let token = UserDefaults.standard.string(forKey: "fcmToken") else {
return
}
let token = /* Retrieve stored push token */

guard let target = try? await account.createPushTarget(
targetId: ID.unique(),
identifier: token
) else {
return
}

UserDefaults.standard.set(target.id, forKey: "targetId")

DispatchQueue.main.async {
self.response = String(describing: session.toMap())
}
let target = try await account.createPushTarget(targetId: ID.unique(), identifier: token)
} catch {
DispatchQueue.main.async {
self.response = error.localizedDescription
}
print("Login failed: \(error.localizedDescription)")
}
}

```

If you have disabled method swizzling, or you are building a SwiftUI app,
Expand Down

0 comments on commit 1135da3

Please sign in to comment.