diff --git a/Package.swift b/Package.swift index 1fc313f..080b24d 100644 --- a/Package.swift +++ b/Package.swift @@ -32,5 +32,7 @@ let package = Package( .testTarget( name: "LazyContainersTests", dependencies: ["LazyContainers"]), - ] + ], + + swiftLanguageVersions: [.v5] ) diff --git a/Sources/LazyContainers/LazyContainers.swift b/Sources/LazyContainers/LazyContainers.swift index e2acb82..408b913 100644 --- a/Sources/LazyContainers/LazyContainers.swift +++ b/Sources/LazyContainers/LazyContainers.swift @@ -1,9 +1,9 @@ // // LazyContainers.swift -// Lazy in Swift 5.1 +// Lazy in Swift 5.2 // // Created by Ben Leggiero on 2018-05-04. -// Version 3.1.0 (last edited 2019-08-30) +// Version 4.0.0 (last edited 2020-08-01) // Copyright Ben Leggiero © 2020 // https://github.com/RougeWare/Swift-Lazy-Patterns/blob/master/LICENSE.txt // @@ -217,6 +217,13 @@ public struct Lazy: LazyContainer { /// Same as `init(initializer:)`, but allows you to use property wrapper andor autoclosure semantics /// /// - Parameter initializer: The closure that will be called the very first time a value is needed + @available(swift, // https://github.com/RougeWare/Swift-Lazy-Patterns/issues/20 + introduced: 5.3, + message: """ + Due to changes introduced in Swift 5.3, property wrappers can now be passed their initial value lazily, through the language assignment syntax. This initializer requires that behavior to work properly. + For Swift 5.2.x and earlier, I recommend you don't use `Lazy` as a property wrapper, since Swift 5.2.x property wrappers can't guarantee lazy evaluation. + This is a real downer for me, but at least it appears to have been a fixable bug, rather than a problem with the core feature itself. + """) public init(wrappedValue initializer: @autoclosure @escaping Initializer) { self.init(initializer: initializer) } diff --git a/Tests/LazyContainersTests/GitHubIssue20Tests.swift b/Tests/LazyContainersTests/GitHubIssue20Tests.swift new file mode 100644 index 0000000..07830d8 --- /dev/null +++ b/Tests/LazyContainersTests/GitHubIssue20Tests.swift @@ -0,0 +1,39 @@ +// +// GitHubIssue20Tests.swift +// LazyContainersTests +// +// Created by Gabe Shahbazian on 2020-07-29. +// + +import XCTest +@testable import LazyContainers + + + +#if swift(>=5.3) +var shouldNotRun = false + +class ShouldNotInit { + init() { + shouldNotRun = true + } +} + + + +/// Guards against issue #20 +/// https://github.com/RougeWare/Swift-Lazy-Patterns/issues/20 +final class GitHubIssue20Tests: XCTestCase { + + @Lazy + var lazyShouldNotRun = ShouldNotInit() + + func testLazyInitWithPropertyWrapper() { + XCTAssertFalse(shouldNotRun) + } + + static var allTests = [ + ("testLazyInitWithPropertyWrapper", testLazyInitWithPropertyWrapper) + ] +} +#endif diff --git a/Tests/LazyContainersTests/LazyContainersTests.swift b/Tests/LazyContainersTests/LazyContainersTests.swift index 1fd722c..252aae7 100644 --- a/Tests/LazyContainersTests/LazyContainersTests.swift +++ b/Tests/LazyContainersTests/LazyContainersTests.swift @@ -1,8 +1,10 @@ // // LazyContainersTests.swift +// LazyContainersTests // // Created by Ben Leggiero on 2019-08-19 -// Copyright BH-0-PD © 2019 - https://github.com/BlueHuskyStudios/Licenses/blob/master/Licenses/BH-0-PD.txt +// Copyright Ben Leggiero © 2020 +// https://github.com/RougeWare/Swift-Lazy-Patterns/blob/master/LICENSE.txt // @@ -12,22 +14,29 @@ import XCTest -var sideEffect: String? - +var sideEffectA: String? func makeLazyA() -> String { - sideEffect = "Side effect A" + sideEffectA = "Side effect A1" return "lAzy" } +var sideEffectB: String? +func makeLazyB() -> String { + sideEffectB = "Side effect B" + return "Lazy B (this time with side-effects)" +} + final class LazyContainersTests: XCTestCase { + #if swift(>=5.3) @Lazy(initializer: makeLazyA) - var lazyInitWithPropertyWrapper: String + var lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect: String + #endif var lazyInitTraditionally = Lazy() { - sideEffect = "Side effect B" + sideEffectA = "Side effect A2" return "lazy B" } @@ -43,7 +52,8 @@ final class LazyContainersTests: XCTestCase { override func setUp() { - sideEffect = nil + sideEffectA = nil + sideEffectB = nil } @@ -55,43 +65,60 @@ final class LazyContainersTests: XCTestCase { // MARK: - `Lazy` - func testLazyInitWithPropertyWrapper() { - XCTAssertEqual(sideEffect, nil) - XCTAssertFalse(_lazyInitWithPropertyWrapper.isInitialized) - XCTAssertEqual(sideEffect, nil) - XCTAssertEqual("lAzy", lazyInitWithPropertyWrapper) - XCTAssertEqual(sideEffect, "Side effect A") - XCTAssertTrue(_lazyInitWithPropertyWrapper.isInitialized) - XCTAssertEqual(sideEffect, "Side effect A") - XCTAssertEqual("lAzy", lazyInitWithPropertyWrapper) - XCTAssertEqual(sideEffect, "Side effect A") - XCTAssertTrue(_lazyInitWithPropertyWrapper.isInitialized) - XCTAssertEqual(sideEffect, "Side effect A") - XCTAssertEqual("lAzy", lazyInitWithPropertyWrapper) - XCTAssertEqual(sideEffect, "Side effect A") - XCTAssertTrue(_lazyInitWithPropertyWrapper.isInitialized) - XCTAssertEqual(sideEffect, "Side effect A") + #if swift(>=5.3) + func testLazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect() { + XCTAssertEqual(sideEffectA, nil) + XCTAssertFalse(_lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect.isInitialized) + XCTAssertEqual(sideEffectA, nil) + XCTAssertEqual("lAzy", lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect) + XCTAssertEqual(sideEffectA, "Side effect A1") + XCTAssertTrue(_lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect.isInitialized) + XCTAssertEqual(sideEffectA, "Side effect A1") + XCTAssertEqual("lAzy", lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect) + XCTAssertEqual(sideEffectA, "Side effect A1") + XCTAssertTrue(_lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect.isInitialized) + XCTAssertEqual(sideEffectA, "Side effect A1") + XCTAssertEqual("lAzy", lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect) + XCTAssertEqual(sideEffectA, "Side effect A1") + XCTAssertTrue(_lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect.isInitialized) + XCTAssertEqual(sideEffectA, "Side effect A1") - lazyInitWithPropertyWrapper = "MAnual" + lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect = "MAnual" - XCTAssertEqual(sideEffect, "Side effect A") - XCTAssertTrue(_lazyInitWithPropertyWrapper.isInitialized) - XCTAssertEqual(sideEffect, "Side effect A") - XCTAssertEqual("MAnual", lazyInitWithPropertyWrapper) - XCTAssertEqual(sideEffect, "Side effect A") - XCTAssertTrue(_lazyInitWithPropertyWrapper.isInitialized) - XCTAssertEqual(sideEffect, "Side effect A") - XCTAssertEqual("MAnual", lazyInitWithPropertyWrapper) - XCTAssertEqual(sideEffect, "Side effect A") - XCTAssertTrue(_lazyInitWithPropertyWrapper.isInitialized) - XCTAssertEqual(sideEffect, "Side effect A") - XCTAssertEqual("MAnual", lazyInitWithPropertyWrapper) - XCTAssertEqual(sideEffect, "Side effect A") - XCTAssertTrue(_lazyInitWithPropertyWrapper.isInitialized) - XCTAssertEqual(sideEffect, "Side effect A") + XCTAssertEqual(sideEffectA, "Side effect A1") + XCTAssertTrue(_lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect.isInitialized) + XCTAssertEqual(sideEffectA, "Side effect A1") + XCTAssertEqual("MAnual", lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect) + XCTAssertEqual(sideEffectA, "Side effect A1") + XCTAssertTrue(_lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect.isInitialized) + XCTAssertEqual(sideEffectA, "Side effect A1") + XCTAssertEqual("MAnual", lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect) + XCTAssertEqual(sideEffectA, "Side effect A1") + XCTAssertTrue(_lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect.isInitialized) + XCTAssertEqual(sideEffectA, "Side effect A1") + XCTAssertEqual("MAnual", lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect) + XCTAssertEqual(sideEffectA, "Side effect A1") + XCTAssertTrue(_lazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect.isInitialized) + XCTAssertEqual(sideEffectA, "Side effect A1") } + func testLazyInitWithPropertyWrapperAndSideEffect() { + + struct Test { + @Lazy + var lazyInitWithPropertyWrapperAndSideEffect = makeLazyB() + } + + + let test = Test() + + XCTAssertNil(sideEffectB, "@Lazy eagerly evaluated its initial value") + XCTAssertEqual(test.lazyInitWithPropertyWrapperAndSideEffect, "Lazy B (this time with side-effects)") + } + #endif + + func testLazyInitTraditionally() { XCTAssertFalse(lazyInitTraditionally.isInitialized) XCTAssertEqual("lazy B", lazyInitTraditionally.wrappedValue) @@ -249,9 +276,16 @@ final class LazyContainersTests: XCTestCase { XCTAssertEqual("Manual F", functionalLazyInitTraditionally.wrappedValue) XCTAssertTrue(functionalLazyInitTraditionally.isInitialized) } - - static var allTests = [ - ("testLazyInitWithPropertyWrapper", testLazyInitWithPropertyWrapper), + + #if swift(>=5.3) + static let testsWhichRequireSwift5_3 = [ + ("testLazyInitWithPropertyWrapperWithCustomInitializerAndSideEffect", testLazyInitWithPropertyWrapperAndCustomInitializerWithSideEffect), + ("testLazyInitWithPropertyWrapperAndSideEffect", testLazyInitWithPropertyWrapperAndSideEffect), + ] + #endif + + + static let testsWhichWorkBeforeSwift5_3 = [ ("testLazyInitTraditionally", testLazyInitTraditionally), ("testResettableLazyInitWithPropertyWrapper", testResettableLazyInitWithPropertyWrapper), @@ -260,4 +294,12 @@ final class LazyContainersTests: XCTestCase { ("testFunctionalLazyInitWithPropertyWrapper", testFunctionalLazyInitWithPropertyWrapper), ("testFunctionalLazyInitTraditionally", testFunctionalLazyInitTraditionally), ] + + + #if swift(>=5.3) + static let allTests = testsWhichRequireSwift5_3 + testsWhichWorkBeforeSwift5_3 + #else + @inline(__always) + static let allTests = testsWhichWorkBeforeSwift5_3 + #endif } diff --git a/Tests/LazyContainersTests/XCTestManifests.swift b/Tests/LazyContainersTests/XCTestManifests.swift index e5fcf95..b0edac9 100644 --- a/Tests/LazyContainersTests/XCTestManifests.swift +++ b/Tests/LazyContainersTests/XCTestManifests.swift @@ -4,6 +4,7 @@ import XCTest public func allTests() -> [XCTestCaseEntry] { return [ testCase(LazyContainersTests.allTests), + testCase(GitHubIssue20Tests.allTests), ] } #endif diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift index 82f8567..fa64405 100644 --- a/Tests/LinuxMain.swift +++ b/Tests/LinuxMain.swift @@ -2,6 +2,9 @@ import XCTest import LazyContainersTests -var tests = [XCTestCaseEntry]() -tests += LazyContainersTests.allTests() +let tests: [XCTestCaseEntry] = [ + LazyContainersTests.allTests, + GitHubIssue20Tests.allTests, +] + XCTMain(tests)