iOS设备Alamofire网络请求出现NSPOSIXErrorDomain Code=28错误的原因分析及预防方案咨询
Great question—this error is tricky because it’s not actually tied to your device’s total available disk or memory. Let’s break down what’s causing it and how to fix it.
Why Does Error Code 28 Happen (Even With Free Space)?
POSIX error code 28 maps to ENOSPC, which translates to "no space left on device"—but in iOS, this can trigger from sandboxed storage limits or resource exhaustion that has nothing to do with your device’s overall free space. Here are the most likely culprits for your scenario:
App-Specific Cache/Temporary Directory Quota
iOS enforces per-app storage quotas for cache and temporary directories, even if the device has plenty of free space. If Alamofire’s default URLSession cache or your app’s own temporary files fill up this quota, you’ll hit this error. This is especially common with frequent requests (like your 5-second location check) that generate cached data over time.File Descriptor Exhaustion
Every network request (via Alamofire’sDataTask) uses a file descriptor. If your app spawns too many concurrent requests, or fails to properly clean up finished/canceled tasks, you can hit the iOS per-app file descriptor limit. This is easy to miss because it doesn’t show up in standard disk/memory stats.iOS 14+ Sandbox Storage Changes
iOS 14 tightened sandbox storage rules, including how apps use cache directories. If your cached files aren’t marked as eligible for system cleanup (e.g., missing theNSURLIsExcludedFromBackupKeyattribute), the system may restrict your app’s cache usage even if space is available.Misconfigured Alamofire/URLSession Cache
If you haven’t customized Alamofire’s cache settings, the default URLSession cache might be set to an overly large size, or it might not prune old entries automatically. Over time, this can eat into your app’s storage quota.
How to Prevent This Error
Here are actionable steps to fix and avoid the issue:
1. Limit Concurrent Requests and Clean Up Tasks
Since your app sends a request every 5 seconds (when the user moves), make sure you’re not spawning more requests than necessary:
- Set a maximum concurrent request limit in your Alamofire
Session:let sessionConfiguration = URLSessionConfiguration.default let session = Session( configuration: sessionConfiguration, maximumConcurrentRequests: 2 // Adjust based on your app's needs ) - Always cancel unused requests (e.g., when a view controller is dismissed or the user stops moving):
// Store a reference to your pending request var pendingLocationRequest: DataRequest? // Cancel when no longer needed pendingLocationRequest?.cancel()
2. Customize and Prune Alamofire’s Cache
Tweak the URLSession cache to stay within your app’s quota:
- Set explicit memory and disk cache limits when initializing your Alamofire
Session:let cache = URLCache( memoryCapacity: 10 * 1024 * 1024, // 10MB in-memory cache diskCapacity: 50 * 1024 * 1024, // 50MB disk cache diskPath: nil ) let sessionConfiguration = URLSessionConfiguration.default sessionConfiguration.urlCache = cache let session = Session(configuration: sessionConfiguration) - Periodically clear old cache entries to free up space:
// Clear all cached responses URLCache.shared.removeAllCachedResponses() // Or clear cache for specific request URLs URLCache.shared.removeCachedResponse(for: URLRequest(url: yourLocationAPIURL))
3. Monitor and Clean Up Temporary/Cache Directories
Keep tabs on your app’s storage usage and clean up when needed:
- Use this helper function to calculate the size of your cache directory:
func calculateDirectorySize(at url: URL) -> UInt64 { guard let enumerator = FileManager.default.enumerator( at: url, includingPropertiesForKeys: [.fileSizeKey], options: .skipsHiddenFiles ) else { return 0 } var totalSize: UInt64 = 0 for case let fileURL as URL in enumerator { do { let attributes = try fileURL.resourceValues(forKeys: [.fileSizeKey]) if let size = attributes.fileSize { totalSize += UInt64(size) } } catch { continue } } return totalSize } - When the cache size exceeds a threshold (e.g., 40MB), delete old files or clear the entire cache.
4. Mark Cache Files as Eligible for System Cleanup
Ensure your app’s cache files are marked so iOS can automatically clean them when low on space:
func markFileAsCleanable(at url: URL) { do { try url.setResourceValue(true, forKey: .isExcludedFromBackupKey) } catch { print("Failed to mark file as cleanable: \(error)") } }
Apply this to any cached files your app generates (not just Alamofire’s default cache).
5. Update Alamofire to the Latest Version
Older versions of Alamofire had occasional resource leaks with URLSession tasks. Updating to the latest stable release can fix hidden issues that lead to file descriptor or storage exhaustion.
内容的提问来源于stack exchange,提问作者J. van 't Veld

