Skip to content

Commit e996500

Browse files
stephencelisp4checo
authored andcommitted
Loosen platform requirements for Dependencies library (#1466)
* Loosen platform requirements for Dependencies Dependencies depends on runtime warning functionality, which is brought in via the "os" module. If we check for availability we can allow the Dependencies module to be used from multiplatform libraries, as we have in isowords. * More agnostic * wip * Update RuntimeWarnings.swift * Update RuntimeWarnings.swift (cherry picked from commit 2d943dadae3b378f083f9b234d34df1ee62043dd) # Conflicts: # Sources/ComposableArchitecture/Internal/RuntimeWarnings.swift # Sources/ComposableArchitecture/SwiftUI/SwitchStore.swift # Tests/ComposableArchitectureTests/EffectRunTests.swift # Tests/ComposableArchitectureTests/EffectTaskTests.swift
1 parent 5d0492f commit e996500

21 files changed

+297
-351
lines changed

Sources/ComposableArchitecture/Effect.swift

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -137,20 +137,15 @@ extension Effect where Failure == Never {
137137
#if DEBUG
138138
var errorDump = ""
139139
customDump(error, to: &errorDump, indent: 4)
140-
runtimeWarning(
140+
runtimeWarn(
141141
"""
142-
An 'Effect.task' returned from "%@:%d" threw an unhandled error. …
142+
An "Effect.task" returned from "\(fileID):\(line)" threw an unhandled error. …
143143
144-
%@
144+
\(errorDump)
145145
146-
All non-cancellation errors must be explicitly handled via the 'catch' parameter \
147-
on 'Effect.task', or via a 'do' block.
146+
All non-cancellation errors must be explicitly handled via the "catch" parameter \
147+
on "Effect.task", or via a "do" block.
148148
""",
149-
[
150-
"\(fileID)",
151-
line,
152-
errorDump,
153-
],
154149
file: file,
155150
line: line
156151
)
@@ -224,20 +219,15 @@ extension Effect where Failure == Never {
224219
#if DEBUG
225220
var errorDump = ""
226221
customDump(error, to: &errorDump, indent: 4)
227-
runtimeWarning(
222+
runtimeWarn(
228223
"""
229-
An 'Effect.run' returned from "%@:%d" threw an unhandled error. …
224+
An "Effect.run" returned from "\(fileID):\(line)" threw an unhandled error. …
230225
231-
%@
226+
\(errorDump)
232227
233-
All non-cancellation errors must be explicitly handled via the 'catch' parameter \
234-
on 'Effect.run', or via a 'do' block.
228+
All non-cancellation errors must be explicitly handled via the "catch" parameter \
229+
on "Effect.run", or via a "do" block.
235230
""",
236-
[
237-
"\(fileID)",
238-
line,
239-
errorDump,
240-
],
241231
file: file,
242232
line: line
243233
)

Sources/ComposableArchitecture/Effects/TaskResult.swift

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -214,21 +214,20 @@ extension TaskResult: Equatable where Success: Equatable {
214214
case let (.failure(lhs), .failure(rhs)):
215215
return _isEqual(lhs, rhs) ?? {
216216
#if DEBUG
217-
if TaskResultDebugging.emitRuntimeWarnings, type(of: lhs) == type(of: rhs) {
218-
runtimeWarning(
217+
let lhsType = type(of: lhs)
218+
if TaskResultDebugging.emitRuntimeWarnings, lhsType == type(of: rhs) {
219+
let lhsTypeName = typeName(lhsType)
220+
runtimeWarn(
219221
"""
220-
'%1$@' is not equatable. …
222+
"\(lhsTypeName)" is not equatable. …
221223
222-
To test two values of this type, it must conform to the 'Equatable' protocol. For \
224+
To test two values of this type, it must conform to the "Equatable" protocol. For \
223225
example:
224226
225-
extension %1$@: Equatable {}
227+
extension \(lhsTypeName): Equatable {}
226228
227-
See the documentation of 'TaskResult' for more information.
228-
""",
229-
[
230-
"\(type(of: lhs))",
231-
]
229+
See the documentation of "TaskResult" for more information.
230+
"""
232231
)
233232
}
234233
#endif
@@ -253,19 +252,17 @@ extension TaskResult: Hashable where Success: Hashable {
253252
} else {
254253
#if DEBUG
255254
if TaskResultDebugging.emitRuntimeWarnings {
256-
runtimeWarning(
255+
let errorType = typeName(type(of: error))
256+
runtimeWarn(
257257
"""
258-
'%1$@' is not hashable. …
258+
"\(errorType)" is not hashable. …
259259
260-
To hash a value of this type, it must conform to the 'Hashable' protocol. For example:
260+
To hash a value of this type, it must conform to the "Hashable" protocol. For example:
261261
262-
extension %1$@: Hashable {}
262+
extension \(errorType): Hashable {}
263263
264-
See the documentation of 'TaskResult' for more information.
265-
""",
266-
[
267-
"\(type(of: error))",
268-
]
264+
See the documentation of "TaskResult" for more information.
265+
"""
269266
)
270267
}
271268
#endif

Sources/ComposableArchitecture/Internal/Deprecations.swift

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1086,15 +1086,15 @@ extension TestStore where ScopedState: Equatable, Action: Equatable {
10861086
return .none
10871087
}
10881088
if index >= parentState[keyPath: toElementsState].endIndex {
1089-
runtimeWarning(
1089+
runtimeWarn(
10901090
"""
1091-
A "forEach" reducer at "%@:%d" received an action when state contained no element at \
1092-
that index. …
1091+
A "forEach" reducer at "\(fileID):\(line)" received an action when state contained no \
1092+
element at that index. …
10931093
10941094
Action:
1095-
%@
1095+
\(debugCaseOutput(action))
10961096
Index:
1097-
%d
1097+
\(index)
10981098
10991099
This is generally considered an application logic error, and can happen for a few \
11001100
reasons:
@@ -1116,12 +1116,6 @@ extension TestStore where ScopedState: Equatable, Action: Equatable {
11161116
when its state contains an element at this index. In SwiftUI applications, use \
11171117
"ForEachStore".
11181118
""",
1119-
[
1120-
"\(fileID)",
1121-
line,
1122-
debugCaseOutput(action),
1123-
index,
1124-
],
11251119
file: file,
11261120
line: line
11271121
)
Lines changed: 54 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,71 @@
1-
#if DEBUG
2-
import XCTestDynamicOverlay
3-
#if canImport(os)
4-
import os
5-
6-
// NB: Xcode runtime warnings offer a much better experience than traditional assertions and
7-
// breakpoints, but Apple provides no means of creating custom runtime warnings ourselves.
8-
// To work around this, we hook into SwiftUI's runtime issue delivery mechanism, instead.
9-
//
10-
// Feedback filed: https://gist.github.com/stephencelis/a8d06383ed6ccde3e5ef5d1b3ad52bbc
11-
@usableFromInline
12-
let rw = (
13-
dso: { () -> UnsafeMutableRawPointer in
14-
let count = _dyld_image_count()
15-
for i in 0..<count {
16-
if let name = _dyld_get_image_name(i) {
17-
let swiftString = String(cString: name)
18-
if swiftString.hasSuffix("/SwiftUI") {
19-
if let header = _dyld_get_image_header(i) {
20-
return UnsafeMutableRawPointer(mutating: UnsafeRawPointer(header))
21-
}
22-
}
23-
}
24-
}
25-
return UnsafeMutableRawPointer(mutating: #dsohandle)
26-
}(),
27-
log: OSLog(subsystem: "com.apple.runtime-issues", category: "ComposableArchitecture")
28-
)
29-
#endif
30-
#endif
31-
1+
@_transparent
322
@usableFromInline
333
@inline(__always)
34-
func runtimeWarning(
35-
_ message: @autoclosure () -> StaticString,
36-
_ args: @autoclosure () -> [CVarArg] = [],
4+
func runtimeWarn(
5+
_ message: @autoclosure () -> String,
6+
category: String? = "ComposableArchitecture",
377
file: StaticString? = nil,
388
line: UInt? = nil
399
) {
4010
#if DEBUG
4111
let message = message()
12+
let category = category ?? "Runtime Warning"
4213
if _XCTIsTesting {
4314
if let file = file, let line = line {
44-
XCTFail(String(format: "\(message)", arguments: args()), file: file, line: line)
15+
XCTFail(message, file: file, line: line)
4516
} else {
46-
XCTFail(String(format: "\(message)", arguments: args()))
17+
XCTFail(message)
4718
}
4819
} else {
4920
#if canImport(os)
50-
unsafeBitCast(
51-
os_log as (OSLogType, UnsafeRawPointer, OSLog, StaticString, CVarArg...) -> Void,
52-
to: ((OSLogType, UnsafeRawPointer, OSLog, StaticString, [CVarArg]) -> Void).self
53-
)(.fault, rw.dso, rw.log, message, args())
21+
os_log(
22+
.fault,
23+
dso: dso,
24+
log: OSLog(subsystem: "com.apple.runtime-issues", category: category),
25+
"%@",
26+
message
27+
)
28+
#else
29+
fputs("\(formatter.string(from: Date())) [\(category)] \(message)\n", stderr)
5430
#endif
5531
}
5632
#endif
5733
}
34+
35+
#if DEBUG
36+
import XCTestDynamicOverlay
37+
38+
#if canImport(os)
39+
import os
40+
41+
// NB: Xcode runtime warnings offer a much better experience than traditional assertions and
42+
// breakpoints, but Apple provides no means of creating custom runtime warnings ourselves.
43+
// To work around this, we hook into SwiftUI's runtime issue delivery mechanism, instead.
44+
//
45+
// Feedback filed: https://gist.github.com/stephencelis/a8d06383ed6ccde3e5ef5d1b3ad52bbc
46+
@usableFromInline
47+
let dso = { () -> UnsafeMutableRawPointer in
48+
let count = _dyld_image_count()
49+
for i in 0..<count {
50+
if let name = _dyld_get_image_name(i) {
51+
let swiftString = String(cString: name)
52+
if swiftString.hasSuffix("/SwiftUI") {
53+
if let header = _dyld_get_image_header(i) {
54+
return UnsafeMutableRawPointer(mutating: UnsafeRawPointer(header))
55+
}
56+
}
57+
}
58+
}
59+
return UnsafeMutableRawPointer(mutating: #dsohandle)
60+
}()
61+
#else
62+
import Foundation
63+
64+
@usableFromInline
65+
let formatter: DateFormatter = {
66+
let formatter = DateFormatter()
67+
formatter.dateFormat = "yyyy-MM-dd HH:MM:SS.sssZ"
68+
return formatter
69+
}()
70+
#endif
71+
#endif

0 commit comments

Comments
 (0)