feat: Add iOS 26 Foundation Models framework support for Apple Intelligence LLM#42
Conversation
…igence LLM Updates Apple Intelligence capability detection to use the new Foundation Models framework introduced in iOS 26. This fixes the issue where Apple Intelligence was not appearing as an LLM provider option on compatible devices. Changes: - Update device identifiers to include iPhone 17 series (iPhone18,x) - Add Foundation Models framework integration for iOS 26+ - Create AppleIntelligenceLLMService with streaming support - Add detailed availability status with specific unavailability reasons - Update tests for new functionality Device compatibility updated: - iPhone 15 Pro/Pro Max (iPhone16,1/16,2) - iPhone 16 series (iPhone17,1-5) - iPhone 17 series (iPhone18,1-3) - iPads with M1+ chips (iPad13,4+, iPad14,x, iPad16,x) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Pull Request ReviewOverall AssessmentThis is a well-structured PR that adds iOS 26 Foundation Models framework support for Apple Intelligence. The code quality is high, with good error handling, comprehensive testing, and thoughtful API design. I've identified a few areas for improvement below. Code Quality & Best Practices ✅Strengths:
Suggested Improvements:1. Session Management Could Cause Issues private func getOrCreateSession(instructions: String?) async throws -> LanguageModelSession {
if let session = currentSession, instructions == cachedInstructions {
return session
}
// Creates new session...
}Issue: If the same instructions are used but the previous session encountered an error or was invalidated, you'd be reusing a potentially broken session. Recommendation: Consider adding session health checks or error recovery logic: private func getOrCreateSession(instructions: String?) async throws -> LanguageModelSession {
// Check if session is still valid
if let session = currentSession,
instructions == cachedInstructions,
await isSessionHealthy(session) {
return session
}
// Create new session...
}2. Memory Management for Long-Running Sessions 💭 Recommendation: Consider adding a method to explicitly clear the session: public func clearSession() async {
currentSession = nil
cachedInstructions = nil
}Potential Bugs 🐛1. Missing Error Propagation in Stream return AsyncThrowingStream { continuation in
Task {
do {
let stream = session.streamResponse(to: prompt, options: genOptions)
for try await partialResponse in stream {
continuation.yield(partialResponse.content)
}
continuation.finish()
} catch {
continuation.finish(throwing: AppleIntelligenceError.generationFailed(error.localizedDescription))
}
}
}Issue: The Recommendation: Consider using a weak reference or task cancellation: return AsyncThrowingStream { continuation in
let task = Task {
do {
// ... stream logic
} catch is CancellationError {
continuation.finish(throwing: CancellationError())
} catch {
continuation.finish(throwing: AppleIntelligenceError.generationFailed(error.localizedDescription))
}
}
continuation.onTermination = { @Sendable _ in
task.cancel()
}
}2. Device Identifier Detection Edge Case 🔍 "iPad13,4", "iPad13,5", "iPad13,6", "iPad13,7",
"iPad13,8", "iPad13,9", "iPad13,10", "iPad13,11",
"iPad14,", // Prefix matchQuestion: Why are iPad13 models explicitly listed but iPad14+ use prefix matching? This inconsistency could cause confusion. Recommendation: Either be consistent (all prefixes or all explicit), or add a comment explaining why iPad13 needs specific model numbers. Performance Considerations ⚡1. Excellent Performance Characteristics
2. Minor Optimization Opportunity private static let cachedDeviceIdentifier: String = {
var systemInfo = utsname()
uname(&systemInfo)
let machine = systemInfo.machine
var identifier = ""
let mirror = Mirror(reflecting: machine)
for child in mirror.children {
guard let value = child.value as? Int8, value != 0 else { break }
identifier += String(UnicodeScalar(UInt8(value)))
}
return identifier
}()
private func isCompatibleDevice() -> Bool {
let identifier = Self.cachedDeviceIdentifier
// ... rest of logic
}Security Concerns 🔒1. No Security Issues Identified ✅
2. Privacy Consideration 💭
Recommendation: Add documentation about privacy implications: /// Apple Intelligence LLM service using Foundation Models framework
///
/// Privacy: All processing happens on-device. No data is sent to Apple's servers.
/// The session maintains conversation context until cleared via clearSession().
public protocol AppleIntelligenceLLMService: Sendable {
// ...
}Test Coverage 🧪Strengths:
Gaps:1. Missing Integration Tests 2. Missing Tests for Session Reuse @Test("Session reuse with same instructions")
@available(iOS 26.0, *)
func sessionReuseWithSameInstructions() async throws {
// Test that session is reused when instructions match
}
@Test("New session created when instructions change")
@available(iOS 26.0, *)
func newSessionWhenInstructionsChange() async throws {
// Test that new session is created when instructions differ
}3. Missing Stream Cancellation Test @Test("Stream cancellation cleans up properly")
func streamCancellation() async throws {
// Test cancellation behavior
}Additional Recommendations 📝1. Documentation /// Reasons why Apple Intelligence might be unavailable
public enum AppleIntelligenceUnavailableReason: Sendable, Equatable {
/// Device doesn't have required hardware (A17+ or M1+ chip)
case deviceNotEligible
/// User has not enabled Apple Intelligence in Settings
case appleIntelligenceNotEnabled
/// Model is still downloading or not fully initialized
case modelNotReady
/// iOS version is below 26.0
case unsupportedOS
/// Unknown reason (future-proofing for new cases)
case unknownReason
}2. Usage Example /// Example:
/// ```swift
/// let service = AppleIntelligenceLLMServiceFactory.create()
///
/// // Check availability
/// switch service.checkAvailability() {
/// case .available:
/// let response = try await service.generate(
/// prompt: "Summarize this text",
/// instructions: "Be concise",
/// options: AppleIntelligenceGenerationOptions(temperature: 0.7)
/// )
/// print(response.content)
/// case .unavailable(let reason):
/// print("Unavailable: \(reason)")
/// }
/// ```SummaryApprove with minor suggestions ✅ This is high-quality code that follows Swift best practices and provides a clean API for Apple Intelligence integration. The identified issues are minor and mostly relate to edge cases and documentation. The core implementation is solid. Priority fixes before merge:
Recommended for follow-up:
Great work on this implementation! |
Summary
Changes
getAppleIntelligenceAvailability()method with detailed statusDevice Compatibility Updated
Test Plan
References
🤖 Generated with Claude Code