APNsCredentialValidator.swift 1.4 KB

123456789101112131415161718192021222324252627282930313233
  1. // LoopFollow
  2. // APNsCredentialValidator.swift
  3. import Foundation
  4. /// Validation rules for the APNs credentials the user pastes in
  5. /// `APNSettingsView`. Used both by the settings UI to surface inline
  6. /// errors and by `LiveActivitySettingsView` to warn when push-based
  7. /// updates won't work.
  8. enum APNsCredentialValidator {
  9. /// Apple Key IDs are exactly 10 uppercase alphanumeric characters.
  10. static func isValidKeyId(_ keyId: String) -> Bool {
  11. guard keyId.count == 10 else { return false }
  12. return keyId.allSatisfy { $0.isASCII && ($0.isUppercase || $0.isNumber) }
  13. }
  14. /// A pasted .p8 must contain both PEM markers. We don't try to verify
  15. /// the inner base64 here — `LoopAPNSService.validateAndFixAPNSKey`
  16. /// already normalizes whitespace and logs deeper warnings, and we
  17. /// don't want to reject keys that JWTManager would happily sign.
  18. static func isValidApnsKey(_ key: String) -> Bool {
  19. let trimmed = key.trimmingCharacters(in: .whitespacesAndNewlines)
  20. guard !trimmed.isEmpty else { return false }
  21. return trimmed.contains("-----BEGIN PRIVATE KEY-----")
  22. && trimmed.contains("-----END PRIVATE KEY-----")
  23. }
  24. /// Convenience for "is the user fully set up to send APNs pushes?"
  25. static func isFullyConfigured(keyId: String, apnsKey: String) -> Bool {
  26. isValidKeyId(keyId) && isValidApnsKey(apnsKey)
  27. }
  28. }