Skip to content
2 changes: 1 addition & 1 deletion Sources/FCM/FCM+Application.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Vapor

extension Application {
public var fcm: FCM {
public var fcm: FCM.Storage {
.init(application: self)
}
}
32 changes: 32 additions & 0 deletions Sources/FCM/FCM+ID.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
//
// FCM+ID.swift
//
//
// Created by Alessandro Di Maio on 08/09/23.
//

import Foundation

extension FCM {
public struct ID: Hashable, ExpressibleByStringLiteral {
public typealias StringLiteralType = String

public let rawValue: String

public init(stringLiteral value: String) {
rawValue = value
}

public init(_ value: String) {
rawValue = value
}

public init(_ id: FCM.ID) {
rawValue = id.rawValue
}
}
}

extension FCM.ID {
public static let `default`: Self = "default"
}
6 changes: 5 additions & 1 deletion Sources/FCM/FCM+Request.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import Vapor

extension Request {
public var fcm: FCM {
.init(request: self)
fcm(.default)
}

public func fcm(_ id: FCM.ID) -> FCM {
application.fcm.client(id)
}
}
48 changes: 48 additions & 0 deletions Sources/FCM/FCM+Storage.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//
// FCM+Storage.swift
//
//
// Created by Alessandro Di Maio on 07/09/23.
//

import Foundation
import NIOConcurrencyHelpers
import Vapor

extension FCM {
public struct Storage {
private let application: Application

private var container: FCMClientContainer {
guard let existingContainer = application.storage[ContainerKey.self] else {
let lock = application.locks.lock(for: ContainerKey.self)
lock.lock()
defer { lock.unlock() }

let new = FCMClientContainer(application: application)
application.storage.set(ContainerKey.self, to: new)
return new
}

return existingContainer
}

init(application: Application) {
self.application = application
}

public func client(_ id: FCM.ID) -> FCM {
container.client(id)
}

public func use(_ id: FCM.ID, configuration: FCMConfiguration) throws {
try container.use(id, configuration: configuration)
}
}
}

extension FCM.Storage {
private struct ContainerKey: StorageKey, LockKey {
typealias Value = FCMClientContainer
}
}
100 changes: 19 additions & 81 deletions Sources/FCM/FCM.swift
Original file line number Diff line number Diff line change
@@ -1,72 +1,49 @@
import Vapor
import Foundation
import JWT
import Vapor

// MARK: Engine

public struct FCM {
let application: Application

public class FCM {
let client: Client

let configuration: FCMConfiguration

var gAuth: GAuthPayload?
var accessToken: String?
var jwt: String?

let scope = "https://www.googleapis.com/auth/cloud-platform"
let audience = "https://www.googleapis.com/oauth2/v4/token"
let actionsBaseURL = "https://fcm.googleapis.com/v1/projects/"
let iidURL = "https://iid.googleapis.com/iid/v1:"
let batchURL = "https://fcm.googleapis.com/batch"

// MARK: Default configurations

public var apnsDefaultConfig: FCMApnsConfig<FCMApnsPayload>? {
get { configuration?.apnsDefaultConfig }
set { configuration?.apnsDefaultConfig = newValue }
configuration.apnsDefaultConfig
}

public var androidDefaultConfig: FCMAndroidConfig? {
get { configuration?.androidDefaultConfig }
set { configuration?.androidDefaultConfig = newValue }
configuration.androidDefaultConfig
}

public var webpushDefaultConfig: FCMWebpushConfig? {
get { configuration?.webpushDefaultConfig }
set { configuration?.webpushDefaultConfig = newValue }
configuration.webpushDefaultConfig
}

// MARK: Initialization

init(application: Application, client: Client) {
self.application = application
public init(client: Client, configuration: FCMConfiguration) {
self.client = client
}

public init(application: Application) {
self.init(application: application, client: application.client)
}

public init(request: Request) {
self.init(application: request.application, client: request.client)
self.configuration = configuration
warmUpCache(with: configuration.email)
}
}

// MARK: Cache

extension FCM {
struct ConfigurationKey: StorageKey {
typealias Value = FCMConfiguration
}

public var configuration: FCMConfiguration? {
get {
application.storage[ConfigurationKey.self]
}
nonmutating set {
application.storage[ConfigurationKey.self] = newValue
if let newValue = newValue {
warmUpCache(with: newValue.email)
}
}
}

private func warmUpCache(with email: String) {
if gAuth == nil {
gAuth = GAuthPayload(iss: email, sub: email, scope: scope, aud: audience)
Expand All @@ -79,43 +56,4 @@ extension FCM {
}
}
}

struct JWTKey: StorageKey {
typealias Value = String
}

var jwt: String? {
get {
application.storage[JWTKey.self]
}
nonmutating set {
application.storage[JWTKey.self] = newValue
}
}

struct AccessTokenKey: StorageKey {
typealias Value = String
}

var accessToken: String? {
get {
application.storage[AccessTokenKey.self]
}
nonmutating set {
application.storage[AccessTokenKey.self] = newValue
}
}

struct GAuthKey: StorageKey {
typealias Value = GAuthPayload
}

var gAuth: GAuthPayload? {
get {
application.storage[GAuthKey.self]
}
nonmutating set {
application.storage[GAuthKey.self] = newValue
}
}
}
47 changes: 47 additions & 0 deletions Sources/FCM/FCMClientContainer.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//
// FCMClientContainer.swift
//
//
// Created by Alessandro Di Maio on 07/09/23.
//

import Foundation
import NIOConcurrencyHelpers
import Vapor


class FCMClientContainer {
private let application: Application
private var clients: [FCM.ID: FCM]
private let lock: NIOLock

init(application: Application) {
self.application = application
clients = [:]
lock = .init()
}

func client(_ id: FCM.ID) -> FCM {
guard let client = clients[id] else {
fatalError("No clients configured for \(id)")
}

return client
}

func use(_ id: FCM.ID, configuration: FCMConfiguration) throws {
lock.lock()
defer { lock.unlock() }

guard !clients.keys.contains(id) else {
fatalError("Cannot change fcm client config of \(id) while running.")
}

let client = FCM(
client: application.client,
configuration: configuration
)

clients[id] = client
}
}
Loading