Skip to content

Commit 1f98a95

Browse files
committed
Migrate to Vapor 4 🚀
1 parent c8eed24 commit 1f98a95

14 files changed

+350
-274
lines changed

.gitignore

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
Packages
2+
.build
13
.DS_Store
2-
/.build
3-
/Packages
4-
/*.xcodeproj
4+
*.xcodeproj
5+
DerivedData/
56
Package.resolved
7+
.swiftpm

Package.swift

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
1-
// swift-tools-version:4.0
1+
// swift-tools-version:5.1
22

33
import PackageDescription
44

55
let package = Package(
66
name: "FCM",
7+
platforms: [
8+
.macOS(.v10_14)
9+
],
710
products: [
811
//Vapor client for Firebase Cloud Messaging
912
.library(name: "FCM", targets: ["FCM"]),
1013
],
1114
dependencies: [
1215
// 💧 A server-side Swift web framework.
13-
.package(url: "https://github.com/vapor/vapor.git", from: "3.0.0"),
14-
.package(url: "https://github.com/vapor/jwt.git", from: "3.0.0"),
16+
.package(url: "https://github.com/vapor/vapor.git", from: "4.0.0-beta.2"),
17+
.package(url: "https://github.com/vapor/jwt.git", from: "4.0.0-beta.2"),
1518
],
1619
targets: [
1720
.target(name: "FCM", dependencies: ["Vapor", "JWT"]),

README.md

+42-140
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<img src="https://img.shields.io/badge/license-MIT-brightgreen.svg" alt="MIT License">
66
</a>
77
<a href="https://swift.org">
8-
<img src="https://img.shields.io/badge/swift-4.1-brightgreen.svg" alt="Swift 4.1">
8+
<img src="https://img.shields.io/badge/swift-4.1-brightgreen.svg" alt="Swift 5.1">
99
</a>
1010
<a href="https://twitter.com/VaporRussia">
1111
<img src="https://img.shields.io/badge/twitter-VaporRussia-5AA9E7.svg" alt="Twitter">
@@ -19,9 +19,9 @@
1919

2020
It's a swift lib that gives ability to send push notifications through Firebase Cloud Messaging.
2121

22-
Built for Vapor3 and depends on `JWT` Vapor lib.
22+
Built for Vapor4 and depends on `JWT` Vapor lib.
2323

24-
Note: the project is in active development state and it may cause huge syntax changes before v1.0.0
24+
Vapor3 version available in `vapor3` branch and from `1.0.0` tag.
2525

2626
If you have great ideas of how to improve this package write me (@iMike) in [Vapor's discord chat](http://vapor.team) or just send pull request.
2727

@@ -33,7 +33,7 @@ Edit your `Package.swift`
3333

3434
```swift
3535
//add this repo to dependencies
36-
.package(url: "https://github.com/MihaelIsaev/FCM.git", from: "1.0.0")
36+
.package(url: "https://github.com/MihaelIsaev/FCM.git", from: "2.0.0")
3737
//and don't forget about targets
3838
//"FCM"
3939
```
@@ -45,65 +45,42 @@ First of all you should configure FCM in `configure.swift`
4545
```swift
4646
import FCM
4747

48-
/// Called before your application initializes.
49-
public func configure(_ config: inout Config, _ env: inout Environment, _ services: inout Services) throws {
50-
//here you should initialize FCM
48+
// Called before your application initializes.
49+
func configure(_ app: Application) throws {
50+
/// case 1
51+
/// with service account json file
52+
/// put into your environment variables the following key:
53+
/// FCM_SERVICE_ACCOUNT_KEY_PATH=path/to/serviceAccountKey.json
54+
app.fcm.configuration = .envServiceAccountKey
55+
56+
/// case 2
57+
/// put into your environment variables the following keys:
58+
/// FCM_EMAIL=...
59+
/// FCM_PROJECT_ID=...
60+
/// FCM_KEY_PATH=path/to/key.pem
61+
app.fcm.configuration = .envCredentials
62+
63+
/// case 3
64+
/// manually
65+
app.fcm.configuration = .init(email: "...", projectId: "...", key: "...")
5166
}
5267
```
5368

54-
#### There are two ways
55-
56-
##### 1. Using environment variables 👍
57-
```swift
58-
let fcm = FCM()
59-
services.register(fcm, as: FCM.self)
60-
```
61-
and don't forget to pass the following environment variables
62-
```swift
63-
fcmServiceAccountKeyPath // /tmp/serviceAccountKey.json
64-
```
65-
OR
66-
```swift
67-
fcmEmail // [email protected]
68-
fcmKeyPath // /tmp/fcm.pem
69-
fcmProjectId // example-3ab5c
70-
```
71-
72-
##### 2. Manually 🤖
73-
```swift
74-
let fcm = FCM(pathToServiceAccountKey: "/tmp/serviceAccountKey.json")
75-
services.register(fcm, as: FCM.self)
76-
```
77-
OR
78-
```swift
79-
let fcm = FCM(email: "[email protected]",
80-
projectId: "example-3ab5c",
81-
pathToKey: "/tmp/fcm.pem")
82-
services.register(fcm, as: FCM.self)
83-
```
84-
OR
85-
```swift
86-
let fcm = FCM(email: "[email protected]",
87-
projectId: "example-3ab5c",
88-
key: "<YOUR PRIVATE KEY>")
89-
services.register(fcm, as: FCM.self)
90-
```
91-
9269
> ⚠️ **TIP:** `serviceAccountKey.json` you could get from [Firebase Console](https://console.firebase.google.com)
9370
>
9471
> 🔑 Just go to Settings -> Service Accounts tab and press **Create Private Key** button in e.g. NodeJS tab
9572
9673
#### OPTIONAL: Set default configurations, e.g. to enable notification sound
97-
Add the following code to your `configure.swift`
74+
Add the following code to your `configure.swift` after `app.fcm.configuration = ...`
9875
```swift
99-
fcm.apnsDefaultConfig = FCMApnsConfig(headers: [:],
100-
aps: FCMApnsApsObject(sound: "default"))
101-
fcm.androidDefaultConfig = FCMAndroidConfig(ttl: "86400s",
102-
restricted_package_name: "com.example.myapp",
103-
notification: FCMAndroidNotification(sound: "default"))
104-
fcm.webpushDefaultConfig = FCMWebpushConfig(headers: [:],
105-
data: [:],
106-
notification: [:])
76+
app.fcm.apnsDefaultConfig = FCMApnsConfig(headers: [:],
77+
aps: FCMApnsApsObject(sound: "default"))
78+
app.fcm.androidDefaultConfig = FCMAndroidConfig(ttl: "86400s",
79+
restricted_package_name: "com.example.myapp",
80+
notification: FCMAndroidNotification(sound: "default"))
81+
app.fcm.webpushDefaultConfig = FCMWebpushConfig(headers: [:],
82+
data: [:],
83+
notification: [:])
10784
```
10885
#### Let's send first push notification! 🚀
10986

@@ -112,98 +89,23 @@ Then you could send push notifications using token, topic or condition.
11289
Here's an example route handler with push notification sending using token
11390

11491
```swift
115-
router.get("testfcm") { req -> Future<String> in
116-
let fcm = try req.make(FCM.self)
117-
let token = "<YOUR FIREBASE DEVICE TOKEN>"
118-
let notification = FCMNotification(title: "Vapor is awesome!", body: "Swift one love! ❤️")
119-
let message = FCMMessage(token: token, notification: notification)
120-
return try fcm.sendMessage(req.client(), message: message)
121-
}
122-
```
123-
124-
`fcm.sendMessage` returns message name like `projects/example-3ab5c/messages/1531222329648135`
125-
126-
`FCMMessage` struct is absolutely the same as `Message` struct in Firebase docs https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages
127-
So you could take a look on its source code to build proper message.
128-
129-
## Bonus 🍾
130-
131-
In my Vapor projects I'm using this extension for sending push notifications
132-
133-
```swift
134-
import Vapor
135-
import Fluent
13692
import FCM
13793

138-
protocol Firebaseable: Model {
139-
var firebaseToken: String? { get set }
140-
}
141-
142-
extension Firebaseable {
143-
func sendPush(title: String, message: String, on req: Container) throws -> Future<Void> {
144-
guard let token = firebaseToken else {
145-
return req.eventLoop.newSucceededFuture(result: ())
94+
func routes(_ app: Application) throws {
95+
app.get("testfcm") { req -> EventLoopFuture<String> in
96+
let token = "<YOUR FIREBASE DEVICE TOKEN>" // get it from iOS/Android SDK
97+
let notification = FCMNotification(title: "Vapor is awesome!", body: "Swift one love! ❤️")
98+
let message = FCMMessage(token: token, notification: notification)
99+
return try req.fcm.send(message).map { name in
100+
return "Just sent: \(name)"
146101
}
147-
return try Self.sendPush(title: title, message: message, token: token, on: req)
148-
}
149-
150-
static func sendPush(title: String, message: String, token: String, on container: Container) throws -> Future<Void> {
151-
let fcm = try container.make(FCM.self)
152-
let message = FCMMessage(token: token, notification: FCMNotification(title: title, body: message))
153-
return try fcm.sendMessage(container.make(Client.self), message: message).transform(to: ())
154-
}
155-
}
156-
157-
extension Array where Element: Firebaseable {
158-
func sendPush(title: String, message: String, on container: Container) throws -> Future<Void> {
159-
return try map { try $0.sendPush(title: title, message: message, on: container) }.flatten(on: container)
160-
}
161-
}
162-
```
163-
Optionally you can handle `sendMessage` error through defining `catchFlatMap` after it, e.g. for removing broken tokens or anything else
164-
```swift
165-
return try fcm.sendMessage(container.make(Client.self), message: message).transform(to: ()).catchFlatMap { error in
166-
guard let googleError = error as? GoogleError, let fcmError = googleError.fcmError else {
167-
return container.eventLoop.newSucceededFuture(result: ())
168-
}
169-
switch fcmError.errorCode {
170-
case .unregistered: // drop token only if unregistered
171-
return container.requestPooledConnection(to: .psql).flatMap { conn in
172-
return Self.query(on: conn).filter(\.firebaseToken == token).first().flatMap { model in
173-
defer { try? container.releasePooledConnection(conn, to: .psql) }
174-
guard var model = model else { return container.eventLoop.newSucceededFuture(result: ()) }
175-
model.firebaseToken = nil
176-
return model.save(on: conn).transform(to: ())
177-
}.always {
178-
try? container.releasePooledConnection(conn, to: .psql)
179-
}
180-
}
181-
default:
182-
return container.eventLoop.newSucceededFuture(result: ())
183102
}
184103
}
185104
```
186105

187-
> Special thanks to @grahamburgsma for `GoogleError` and `FCMError` #10
188-
189-
Then e.g. I'm conforming my `Token` model to `Firebaseable`
106+
`fcm.send` returns message name like `projects/example-3ab5c/messages/1531222329648135`
190107

191-
```swift
192-
final class Token: Content {
193-
var id: UUID?
194-
var token: String
195-
var userId: User.ID
196-
var firebaseToken: String?
197-
}
198-
extension Token: Firebaseable {}
199-
```
108+
`FCMMessage` struct is absolutely the same as `Message` struct in Firebase docs https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages
109+
So you could take a look on its source code to build proper message.
200110

201-
So then you'll be able to send pushes by querying tokens like this
202-
```swift
203-
Token.query(on: req)
204-
.join(\User.id, to: \Token.userId)
205-
.filter(\User.email == "[email protected]")
206-
.all().map { tokens in
207-
try tokens.sendPush(title: "Test push", message: "Hello world!", on: req)
208-
}
209-
```
111+
> Special thanks to @grahamburgsma for `GoogleError` and `FCMError` #10

Sources/FCM/FCM+Application.swift

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import Vapor
2+
3+
extension Application {
4+
public var fcm: FCM {
5+
.init(application: self)
6+
}
7+
}

Sources/FCM/FCM+Request.swift

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import Vapor
2+
3+
extension Request {
4+
public var fcm: FCM {
5+
.init(application: application)
6+
}
7+
}

0 commit comments

Comments
 (0)