Instance Method
use(_:eventLoopGroupProvider:responseDecoder:requestEncoder:byteBufferAllocator:as:isDefault:)
func use(_ config: APNSClientConfiguration, eventLoopGroupProvider: NIOEventLoopGroupProvider, responseDecoder: JSONDecoder, requestEncoder: JSONEncoder, byteBufferAllocator: ByteBufferAllocator = .init(), as id: APNSContainers.ID, isDefault: Bool? = nil)
Parameters
configThe APNs configuration.
eventLoopGroupProviderSpecify how the
NIOCore/EventLoopGroupwill be created. Example:.shared(app.eventLoopGroup)responseDecoderA decoder to use when decoding responses from the APNs server. Example:
JSONDecoder()requestEncoderAn encoder to use when encoding notifications. Example:
JSONEncoder()byteBufferAllocatorThe allocator to use.
idThe container ID to access the configuration under.
isDefaultA flag to specify the configuration as the default when
clientis called. The first configuration that doesn’t specifyfalseis automatically configured as the default.
Discussion
You must configure at lease one client in order to send notifications to devices. If you plan on supporting both development builds (ie. run from Xcode) and release builds (ie. TestFlight/App Store), you must configure at least two configurations:
/// The .p8 file as a string.
let apnsKey = Environment.get("APNS_KEY_P8")
/// The identifier of the key in the developer portal.
let keyIdentifier = Environment.get("APNS_KEY_ID")
/// The team identifier of the app in the developer portal.
let teamIdentifier = Environment.get("APNS_TEAM_ID")
let productionConfig = APNSClientConfiguration(
authenticationMethod: .jwt(
privateKey: try .loadFrom(string: apnsKey),
keyIdentifier: keyIdentifier,
teamIdentifier: teamIdentifier
),
environment: .production
)
app.apns.containers.use(
productionConfig,
eventLoopGroupProvider: .shared(app.eventLoopGroup),
responseDecoder: JSONDecoder(),
requestEncoder: JSONEncoder(),
as: .production
)
var developmentConfig = productionConfig
developmentConfig.environment = .sandbox
app.apns.containers.use(
developmentConfig,
eventLoopGroupProvider: .shared(app.eventLoopGroup),
responseDecoder: JSONDecoder(),
requestEncoder: JSONEncoder(),
as: .development
)
As shown above, the same key can be used for both the development and production environments.
Important
Make sure not to store your APNs key within your code or repo directly, and opt to store it via a secure store specific to your deployment, such as in a .env supplied at deploy time.
You can determine which environment is being used in your app by checking its entitlements, and including the information along with the device token when sending it to your server:
enum APNSDeviceTokenEnvironment: String {
case production
case development
}
/// Get the APNs environment from the embedded
/// provisioning profile, or nil if it can't
/// be determined.
///
/// Note that both TestFlight and the App Store
/// don't have provisioning profiles, and always
/// run in the production environment.
var pushEnvironment: APNSDeviceTokenEnvironment? {
#if canImport(AppKit)
let provisioningProfileURL = Bundle.main.bundleURL
.appending(path: "Contents", directoryHint: .isDirectory)
.appending(path: "embedded.provisionprofile", directoryHint: .notDirectory)
guard let data = try? Data(contentsOf: provisioningProfileURL)
else { return nil }
#else
guard
let provisioningProfileURL = Bundle.main
.url(forResource: "embedded", withExtension: "mobileprovision"),
let data = try? Data(contentsOf: provisioningProfileURL)
else {
#if targetEnvironment(simulator)
return .development
#else
return nil
#endif
}
#endif
let string = String(decoding: data, as: UTF8.self)
guard
let start = string.firstRange(of: "<plist"),
let end = string.firstRange(of: "</plist>")
else { return nil }
let propertylist = string[start.lowerBound..<end.upperBound]
guard
let provisioningProfile = try? PropertyListSerialization
.propertyList(from: Data(propertylist.utf8), format: nil) as? [String : Any],
let entitlements = provisioningProfile["Entitlements"] as? [String: Any],
let environment = (
entitlements["aps-environment"]
?? entitlements["com.apple.developer.aps-environment"]
) as? String
else { return nil }
return APNSDeviceTokenEnvironment(rawValue: environment)
}
Note that the simulator doesn’t have a provisioning profile, and will always register under the development environment.