Files
Pulse/Sources/PulseUI/Helpers/FileViewModelContext.swift
Bartosz Dolewski 07f1008abe Fix SIGTRAP in NetworkTaskEntity.state(in:) when session UUID is NULL
NetworkTaskEntity.session is declared @NSManaged var session: UUID
(non-optional), but the matching attribute in LoggerStore+Model.swift
is created without explicitly setting isOptional = false:

    Attribute("session", .UUIDAttributeType),

NSAttributeDescription.isOptional defaults to true, so Core Data
permits NULL for session. Rows that end up with a NULL session (e.g.
data persisted under an older schema and carried through lightweight
migration) trap on read: the implicit ObjC->Swift UUID bridge calls
UUID._unconditionallyBridgeFromObjectiveC, which fires SIGTRAP on nil.

The only PulseUI read site is FileViewModelContext.swift:52, hit
transitively from ConsoleFormatter.status(for:store:) while rendering
ConsoleTaskCell.

Switch that read to value(forKey:) so a NULL stored value is surfaced
as Optional.none instead of trapping the bridge. Semantics are preserved:
- For valid rows, the comparison behaves exactly as before.
- For NULL-session rows, the cast yields nil, the comparison nil != sessionID
  is true, and the function returns nil — matching the existing doc
  comment ("returns nil if the task is an unknown state ... from the
  previous app run").
2026-05-15 12:24:35 +02:00

64 lines
2.3 KiB
Swift

// The MIT License (MIT)
//
// Copyright (c) 2020-2026 Alexander Grebenyuk (github.com/kean).
import Pulse
import Foundation
package struct FileViewerViewModelContext {
package var contentType: NetworkLogger.ContentType?
package var originalSize: Int64
package var metadata: [String: String]?
package var isResponse = true
package var error: NetworkLogger.DecodingError?
package var sourceURL: URL?
package init(contentType: NetworkLogger.ContentType? = nil, originalSize: Int64, metadata: [String : String]? = nil, isResponse: Bool = true, error: NetworkLogger.DecodingError? = nil, sourceURL: URL? = nil) {
self.contentType = contentType
self.originalSize = originalSize
self.metadata = metadata
self.isResponse = isResponse
self.error = error
self.sourceURL = sourceURL
}
}
extension NetworkTaskEntity {
package var requestFileViewerContext: FileViewerViewModelContext {
FileViewerViewModelContext(
contentType: originalRequest?.contentType,
originalSize: requestBodySize,
metadata: metadata,
isResponse: false,
error: nil
)
}
package var responseFileViewerContext: FileViewerViewModelContext {
FileViewerViewModelContext(
contentType: response?.contentType,
originalSize: responseBodySize,
metadata: metadata,
isResponse: true,
error: decodingError,
sourceURL: currentRequest?.url.flatMap(URL.init)
)
}
/// - returns `nil` if the task is an unknown state. It may happen if the
/// task is pending, but it's from the previous app run.
package func state(in store: LoggerStoreProtocol?) -> NetworkTaskEntity.State? {
let state = self.state
if state == .pending, let sessionID = store?.currentSessionID {
// Read via KVC so a NULL stored value (allowed by Core Data because
// `session` is implicitly optional in the model) is surfaced as
// `nil` instead of trapping the ObjC->Swift UUID bridge.
let taskSession = value(forKey: "session") as? UUID
if taskSession != sessionID {
return nil
}
}
return state
}
}