Security Fundamentals
Prerequisites: Networking with URLSession, Files and FileManager
Apps hold personal data and credentials, and they run on devices you don't control. A few practices cover most of the risk.
Store secrets in the Keychain
Tokens, passwords, and keys go in the Keychain — the system's
encrypted credential store — never in UserDefaults or plain files,
which are stored unencrypted in the app's container.
import Foundation
import Security
func saveToken(_ token: String, account: String) -> Bool {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account,
kSecValueData as String: Data(token.utf8),
kSecAttrAccessible as String: kSecAttrAccessibleAfterFirstUnlock,
]
SecItemDelete(query as CFDictionary) // replace any existing item
return SecItemAdd(query as CFDictionary, nil) == errSecSuccess
}
The raw Keychain API is verbose, so most projects wrap it in a small helper type or use a well-maintained package.
Don't ship secrets in the app
Anything inside an app bundle — API keys, passwords, private URLs — can be extracted by anyone who downloads it. Keep secrets on your server and have the app authenticate to it with per-user tokens.
Network security
- HTTPS only. App Transport Security blocks plain
http://by default. Don't add blanket exceptions toInfo.plist. - Validate on the server. Anyone can modify requests an app sends; treat all client input as untrusted on the back end.
- For high-risk apps (banking, health), consider certificate pinning, which rejects connections to servers presenting unexpected certificates.
Cryptography: use the platform, not your own
CryptoKit provides modern, safe primitives on Apple platforms (and
the open-source swift-crypto package offers the same API on Linux,
imported as Crypto instead of CryptoKit):
import CryptoKit
import Foundation
let digest = SHA256.hash(data: Data("hello".utf8))
print(digest.map { String(format: "%02x", $0) }.joined())
let key = SymmetricKey(size: .bits256)
let sealed = try AES.GCM.seal(Data("secret".utf8), using: key)
let opened = try AES.GCM.open(sealed, using: key)
print(String(decoding: opened, as: UTF8.self))
2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824
secret
Never invent your own encryption scheme, and don't hash passwords with a fast hash like SHA-256 on a server — use a dedicated password-hashing algorithm such as bcrypt or Argon2.
Privacy
- Request permissions (camera, location, contacts) only when the feature needs them, with a clear usage description string.
- Declare the data you collect in your app's privacy manifest, which App Store review requires.
- Don't log personal data or tokens — device logs can be read by diagnostic tools.
Common mistakes
- Storing an auth token in
UserDefaults. - Trusting data because it came from your own app. The server must check permissions on every request.