This episode covers Swift application security: secure coding practices and data validation, storing credentials in the Keychain, basic encryption and hashing with CryptoKit, plus secure storage, sandboxing, and privacy protection on Apple platforms.

Security is not a feature bolted on at the end — it is a design decision that permeates every layer of an application. Episode 11 covers secure coding and data protection in Swift: input validation practices, storing secrets in the Keychain, encryption and hashing with CryptoKit, and how Apple platforms protect data through sandboxing and privacy.
In a world where user data is the attacker's primary target, a careless application can leak sensitive data even while "working" perfectly. This episode gives you the foundation to build an application that is secure from the first line.
Four principles govern all security practices:
Validation happens as close to the input source as possible, using whitelists rather than blacklists:
struct Validator {
static func emailValid(_ email: String) -> Bool {
let pola = "^[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}$"
return email.range(of: pola, options: .regularExpression) != nil
}
static func rentangUmur(_ nilai: Int) -> Bool {
return (13...120).contains(nilai)
}
}
print(Validator.emailValid("arman@example.com"))
print(Validator.rentangUmur(200))email.range(of:options:.regularExpression) checks the email against a whitelist pattern. Validating with explicit ranges like (13...120).contains(nilai) rejects out-of-bounds values from the start — far safer than cleaning up bad values afterward.
The Keychain is a system-level encrypted storage for secrets — tokens, passwords, and other sensitive data. Unlike UserDefaults and files, Keychain data is encrypted and tied to the device and user:
import Security
func simpanKeychain(_ data: Data, untuk key: String) -> OSStatus {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key,
kSecValueData as String: data
]
return SecItemAdd(query as CFDictionary, nil)
}SecItemAdd(query, nil) stores data in the Keychain; the kSecClassGenericPassword attribute marks the entry as a generic password. This function returns an OSStatus that must be checked — errSecSuccess means success.
Reading it back is done with a search query:
func ambilKeychain(key: String) -> Data? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key,
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne
]
var item: CFTypeRef?
let status = SecItemCopyMatching(query as CFDictionary, &item)
return status == errSecSuccess ? item as? Data : nil
}SecItemCopyMatching(query, &item) returns the stored Data if found. All application secrets — API tokens, credentials — must go through this path, not global variables or plist files that are easy to extract.
To verify integrity or create a fingerprint of data, use hashing — a one-way function that cannot be reversed:
import CryptoKit
let pesan = Data("pesan rahasia".utf8)
let digest = SHA256.hash(data: pesan)
print(digest.map { String(format: "%02x", $0) }.joined())SHA256.hash(data: pesan) produces a 32-byte digest. CryptoKit provides a hardware-optimized, audited implementation — never write your own cryptographic algorithms; always use a trusted library.
To store sensitive data you need to read back, use symmetric encryption with AES-GCM:
let key = SymmetricKey(size: .bits256)
let plainData = Data("isi rahasia".utf8)
let sealed = try AES.GCM.seal(plainData, using: key)
let dibuka = try AES.GCM.open(sealed, using: key)
print(String(data: dibuka, encoding: .utf8)!)AES.GCM.seal(plainData, using: key) encrypts the data and includes an authentication tag that detects tampering. Store the key safely in the Keychain — combining CryptoKit encryption with the Keychain is the most common pattern for protected data storage.
iOS apps run in a sandbox: access to the system is limited to your own container. Data inside the container can be protected further with Data Protection — files are encrypted automatically and can only be accessed while the device is unlocked:
let data = Data("catatan pribadi".utf8)
try data.write(to: fileURL,
options: [.completeFileProtection]).completeFileProtection makes a file accessible only while the device is unlocked. Other protection levels — .completeFileProtectionUnlessOpen and .noFileProtection — balance security against availability when notifications arrive.
iOS requires you to declare the reason for every access to personal data (camera, location, photos) in Info.plist through keys like NSCameraUsageDescription. Users also see an indicator when that data is accessed. Beyond compliance, good privacy practice means:
Warning
Secrets written to logs are one of the most common leaks. Enforce a strict rule: credentials and tokens never enter print, NSLog, or crash reports.
Key takeaways:
In the next episode, episode 12, we'll cover authentication and authorization — implementing OAuth 2.0 and OpenID Connect in Swift apps, integrating Sign in with Apple, managing tokens and refresh tokens, and consuming APIs securely with HTTP security headers. Your security extends to the server side!