diff --git a/Sources/Nimble/Adapters/NMBExpectation.swift b/Sources/Nimble/Adapters/NMBExpectation.swift index 4279c7780..6ca25fb7b 100644 --- a/Sources/Nimble/Adapters/NMBExpectation.swift +++ b/Sources/Nimble/Adapters/NMBExpectation.swift @@ -14,7 +14,7 @@ private func from(objcMatcher: NMBMatcher) -> Matcher { // Equivalent to Expectation, but for Nimble's Objective-C interface public final class NMBExpectation: NSObject, Sendable { - internal let _actualBlock: @Sendable () -> NSObject? + internal let _actualBlock: @Sendable () -> sending NSObject? internal let _negative: Bool internal let _file: FileString internal let _line: UInt diff --git a/Sources/Nimble/Matchers/AllPass.swift b/Sources/Nimble/Matchers/AllPass.swift index f1f6e2b5d..489f2e4bb 100644 --- a/Sources/Nimble/Matchers/AllPass.swift +++ b/Sources/Nimble/Matchers/AllPass.swift @@ -100,7 +100,9 @@ extension NMBMatcher { ) } - let expr = Expression(expression: ({ nsObjects }), location: location) + let immutableCollection = nsObjects + + let expr = Expression(expression: ({ immutableCollection }), location: location) let pred: Matcher<[NSObject]> = createMatcher(Matcher { expr in return matcher.satisfies(({ try expr.evaluate() }), location: expr.location).toSwift() }) diff --git a/Sources/Nimble/Matchers/BeCloseTo.swift b/Sources/Nimble/Matchers/BeCloseTo.swift index 36dc0e00f..0031d8f3e 100644 --- a/Sources/Nimble/Matchers/BeCloseTo.swift +++ b/Sources/Nimble/Matchers/BeCloseTo.swift @@ -67,7 +67,7 @@ private func beCloseTo( } #if canImport(Darwin) -public class NMBObjCBeCloseToMatcher: NMBMatcher { +public final class NMBObjCBeCloseToMatcher: NMBMatcher, @unchecked Sendable { private let _expected: NSNumber fileprivate init(expected: NSNumber, within: CDouble) { diff --git a/Sources/Nimble/Matchers/BeEmpty.swift b/Sources/Nimble/Matchers/BeEmpty.swift index 43e1ca791..cdec3e606 100644 --- a/Sources/Nimble/Matchers/BeEmpty.swift +++ b/Sources/Nimble/Matchers/BeEmpty.swift @@ -88,7 +88,8 @@ extension NMBMatcher { let expr = Expression(expression: { value }, location: location) return try beEmpty().satisfies(expr).toObjectiveC() } else if let value = actualValue as? NSString { - let expr = Expression(expression: { value }, location: location) + let stringValue = String(value) + let expr = Expression(expression: { stringValue }, location: location) return try beEmpty().satisfies(expr).toObjectiveC() } else if let actualValue = actualValue { let badTypeErrorMsg = "be empty (only works for NSArrays, NSSets, NSIndexSets, NSDictionaries, NSHashTables, and NSStrings)" diff --git a/Sources/Nimble/Matchers/Contain.swift b/Sources/Nimble/Matchers/Contain.swift index 555d4d1ff..93e5364f9 100644 --- a/Sources/Nimble/Matchers/Contain.swift +++ b/Sources/Nimble/Matchers/Contain.swift @@ -77,10 +77,14 @@ public func contain(_ substrings: NSString...) -> Matcher { } public func contain(_ substrings: [NSString]) -> Matcher { + let stringSubstrings = substrings.map { String($0) } return Matcher.simple("contain <\(arrayAsString(substrings))>") { actualExpression in guard let actual = try actualExpression.evaluate() else { return .fail } + let actualString = String(actual) - let matches = substrings.allSatisfy { actual.range(of: $0.description).length != 0 } + let matches = stringSubstrings.allSatisfy { string in + actualString.contains(string) + } return MatcherStatus(bool: matches) } } @@ -115,7 +119,8 @@ extension NMBMatcher { let expectedOptionals: [Any?] = expected.map({ $0 as Any? }) return try contain(expectedOptionals).satisfies(expr).toObjectiveC() } else if let value = actualValue as? NSString { - let expr = Expression(expression: ({ value as String }), location: location) + let stringValue = String(value) + let expr = Expression(expression: ({ stringValue }), location: location) // swiftlint:disable:next force_cast return try contain(expected as! [String]).satisfies(expr).toObjectiveC() } diff --git a/Sources/Nimble/Matchers/Matcher.swift b/Sources/Nimble/Matchers/Matcher.swift index ffb5798b5..470985f60 100644 --- a/Sources/Nimble/Matchers/Matcher.swift +++ b/Sources/Nimble/Matchers/Matcher.swift @@ -225,7 +225,7 @@ public class NMBMatcher: NSObject, @unchecked Sendable { self.init(matcher: predicate) } - func satisfies(_ expression: @escaping @Sendable () throws -> NSObject?, location: SourceLocation) -> NMBMatcherResult { + func satisfies(_ expression: @escaping @Sendable () throws -> sending NSObject?, location: SourceLocation) -> NMBMatcherResult { let expr = Expression(expression: expression, location: location) do { return try self.matcher(expr) diff --git a/Tests/NimbleTests/Matchers/BeAKindOfTest.swift b/Tests/NimbleTests/Matchers/BeAKindOfTest.swift index 93bf7b9fa..1160eb57a 100644 --- a/Tests/NimbleTests/Matchers/BeAKindOfTest.swift +++ b/Tests/NimbleTests/Matchers/BeAKindOfTest.swift @@ -5,9 +5,9 @@ import Nimble import NimbleSharedTestHelpers #endif -private class TestNull: NSNull {} +private final class TestNull: NSNull, @unchecked Sendable {} private protocol TestProtocol {} -private class TestClassConformingToProtocol: TestProtocol {} +private final class TestClassConformingToProtocol: TestProtocol {} private struct TestStructConformingToProtocol: TestProtocol {} final class BeAKindOfSwiftTest: XCTestCase {