-
Notifications
You must be signed in to change notification settings - Fork 84
Description
✍️ Issue Description
IterableLogUtil.formatDate() allocates a new DateFormatter instance on every invocation. DateFormatter (NSDateFormatter) is well-documented as expensive to create on iOS. When multiple SDK log calls fire in quick succession on the main thread (e.g. during foreground transitions), this causes fully-blocked app hangs of 2-3+ seconds.
Additionally, the expensive formatting happens before the log level is checked in the delegate, so even apps that set DefaultLogDelegate(minLogLevel: .error) pay the full cost for every ITBInfo() and ITBDebug() call internally.
Affected code
swift-sdk/Internal/Utilities/IterableLogUtil.swift, lines 52-56:
private static func formatDate(date: Date) -> String {
let formatter = DateFormatter() // ← new allocation every call
formatter.dateFormat = "HH:mm:ss.SSSS"
return formatter.string(from: date)
}
And the call site in log() at line 19-20:
func log(level: LogLevel, message: String?, file: String, method: String, line: Int) {
let logMessage = IterableLogUtil.formatLogMessage(...) // ← formats BEFORE delegate check
logDelegate.log(level: level, message: logMessage) // ← level filtered here, too late
}
Stack trace (from Sentry crash reporting)
-[NSDateFormatter stringForObjectValue:]
IterableLogUtil.formatDate (IterableLogUtil.swift)
IterableLogUtil.formatLogMessage (IterableLogUtil.swift)
(×6 similar frames)
UIApplicationMain
main (main.swift)
App Hang Fully Blocked: 2.4–3.2 seconds. Occurs during foreground transition after returning from an external login flow.
Suggested fix
Two changes would resolve this:
- Cache the DateFormatter as a static property:
private static let dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "HH:mm:ss.SSSS"
return formatter
}()
private static func formatDate(date: Date) -> String {
dateFormatter.string(from: date)
}
- Check the log level before formatting (optional but recommended):
Ideally the delegate protocol could be extended with a shouldLog(level:) method, or the level filtering could move into IterableLogUtil.log() itself before formatting. This would avoid the cost of formatLogMessage() entirely for filtered-out messages.
This issue was investigated with the help of Claude (Anthropic).
👤 Iterable orgId: Wolt
📦 Iterable SDK version: 6.6.5 (also verified the code is unchanged in 6.6.7)
📲 iOS SDK version: From iOS 16 to iOS 26.x
⚠️ Beta Software Notice
Important: Our team does not provide support for issues encountered on beta or pre-release versions of operating systems, development tools, or other software. Please verify that the issue occurs on stable, officially released software before submitting this report. Thank you for your understanding.