5
5
<img src="https://img.shields.io/badge/license-MIT-brightgreen.svg" alt="MIT License">
6
6
</a>
7
7
<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">
9
9
</a>
10
10
<a href="https://twitter.com/VaporRussia">
11
11
<img src="https://img.shields.io/badge/twitter-VaporRussia-5AA9E7.svg" alt="Twitter">
19
19
20
20
It's a swift lib that gives ability to send push notifications through Firebase Cloud Messaging.
21
21
22
- Built for Vapor3 and depends on ` JWT ` Vapor lib.
22
+ Built for Vapor4 and depends on ` JWT ` Vapor lib.
23
23
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.
25
25
26
26
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.
27
27
@@ -33,7 +33,7 @@ Edit your `Package.swift`
33
33
34
34
``` swift
35
35
// 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" )
37
37
// and don't forget about targets
38
38
// "FCM"
39
39
```
@@ -45,65 +45,42 @@ First of all you should configure FCM in `configure.swift`
45
45
``` swift
46
46
import FCM
47
47
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 : " ..." )
51
66
}
52
67
```
53
68
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
-
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
-
92
69
> ⚠️ ** TIP:** ` serviceAccountKey.json ` you could get from [ Firebase Console] ( https://console.firebase.google.com )
93
70
>
94
71
> 🔑 Just go to Settings -> Service Accounts tab and press ** Create Private Key** button in e.g. NodeJS tab
95
72
96
73
#### 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 = ... `
98
75
``` 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 : [: ])
107
84
```
108
85
#### Let's send first push notification! 🚀
109
86
@@ -112,98 +89,23 @@ Then you could send push notifications using token, topic or condition.
112
89
Here's an example route handler with push notification sending using token
113
90
114
91
``` 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
136
92
import FCM
137
93
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 ) "
146
101
}
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 : ())
183
102
}
184
103
}
185
104
```
186
105
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 `
190
107
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.
200
110
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
0 commit comments