Direct answer
If your certificates rotate more often than users update the app, TrustKit is the more sustainable option because its configuration-driven backup pins let you accept the next key before the current one expires. The native URLSessionDelegate approach removes external dependencies, but it makes rotation harder and moves the security burden onto your code.
What is confirmed versus what is likely
Confirmed behavior: urlSession(_:didReceive:completionHandler:) hands you a URLAuthenticationChallenge containing server trust. On recent iOS versions, you can evaluate that trust with SecTrustEvaluateWithError and decide whether to continue. TrustKit wraps the same Security framework, letting you declare public-key hashes in a dictionary rather than writing imperative chain checks.
Likely risk: Manual pinning implementations often fail-open because they compare only the leaf certificate or ignore intermediate-chain errors. A native implementation that validates the full chain and pins the Subject Public Key Info (SPKI) hash can be secure, but the margin for error is higher than with a maintained, widely used abstraction.
Steps for each path
Native URLSessionDelegate
- Pin the SPKI hash, not the raw certificate or public-key bits.
- Call
SecTrustEvaluateWithError on the full chain before comparing the server's public key. - Hardcode at least one backup pin for the next rotation.
- Test with an expired or mismatched certificate to confirm the handler rejects the connection.
TrustKit
- Configure multiple SPKI hashes in the pinning policy, including a backup.
- Verify the library's current maintenance status and iOS compatibility before adopting it; third-party wrappers can lag behind platform changes.
Verification
Extract the server's SPKI hash with OpenSSL and compare it against your configuration:
openssl x509 -in server.crt -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64
Run a local test with a self-signed or proxy certificate to ensure your challenge handler calls the completion handler with .cancelAuthenticationChallenge rather than allowing the connection.
Open question
Do your certificates rotate on a schedule shorter than your typical app-store update cycle? If yes, backup pins are mandatory regardless of which approach you choose.