Handwritting Processing for OCR
Runs a Core Image pipeline to desaturate, boost contrast, and sharpen a captured image before passing it to Vision for OCR, significantly improving handwriting recognition accuracy.
static func preprocessForHandwriting(_ image: UIImage) -> CGImage? {
guard let cgImage = image.cgImage else { return nil }
let ciImage = CIImage(cgImage: cgImage)
let controls = ciImage.applyingFilter(
"CIColorControls",
parameters: [
kCIInputSaturationKey: 0.0, // Grayscale
kCIInputContrastKey: 1.45, // Boost contrast
kCIInputBrightnessKey: 0.05
]
)
let sharpened = controls.applyingFilter(
"CIUnsharpMask",
parameters: [kCIInputRadiusKey: 2.0, kCIInputIntensityKey: 0.85]
)
let context = CIContext(options: nil)
return context.createCGImage(sharpened, from: sharpened.extent)
}
Quiz Generation Protocol
Defines a shared interface for card generation so the app can swap between on-device and external AI without changing any calling code.
protocol CardGenerating {
func generateCards(from text: String) async throws -> [AIFlashcard]
func generateDistractors(
question: String, correctAnswer: String,
otherAnswers: [String], sourceText: String
) async throws -> [String]
func generateQuiz(
cards: [(question: String, answer: String)],
sourceText: String
) async throws -> [AIQuizQuestionModel]
}
Different API Requests
Handles both OpenAI-compatible and Anthropic APIs through a single request path, switching auth headers and body format based on the selected provider.
switch apiFormat {
case .openAI:
request.setValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")
let body = OpenAIChatRequest(
model: model,
messages: [
.init(role: "system", content: systemMessage),
.init(role: "user", content: prompt)
],
temperature: 0.3
)
request.httpBody = try JSONEncoder().encode(body)
case .anthropic:
request.setValue(apiKey, forHTTPHeaderField: "x-api-key")
request.setValue("2023-06-01", forHTTPHeaderField: "anthropic-version")
let body = AnthropicMessagesRequest(
model: model,
max_tokens: 4096,
system: systemMessage,
messages: [.init(role: "user", content: prompt)],
temperature: 0.3
)
request.httpBody = try JSONEncoder().encode(body)
}
Secure API Key Storage
Stores API keys as encrypted generic passwords using iOS Keychain Services, keeping credentials out of plaintext storage.
enum KeychainManager {
private static let service = "com.jaidenhenley.quickstudy"
private static let account = "external-api-key"
static func saveAPIKey(_ key: String) throws {
let data = Data(key.utf8)
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: account
]
SecItemDelete(query as CFDictionary)
let attributes = query.merging([kSecValueData as String: data]) { _, new in new }
let status = SecItemAdd(attributes as CFDictionary, nil)
guard status == errSecSuccess else {
throw CardGenerationError.keychainError(status)
}
}
}