From 9b617409c4dbe585410728cbc92347e2d12be352 Mon Sep 17 00:00:00 2001 From: Stephen Kac Date: Tue, 14 Jul 2020 13:44:33 -0600 Subject: [PATCH 01/26] Cleaned up tests --- SAKBase.xcodeproj/project.pbxproj | 56 +++++-- .../xcshareddata/xcschemes/SAKBase.xcscheme | 9 +- Sources/SAKBase/BaseTypes/Stack.swift | 2 + .../BaseTypes/BindingCommandTests.swift | 38 +++++ .../SAKBaseTests/BaseTypes/CommandTests.swift | 78 +++++++++ Tests/SAKBaseTests/BaseTypes/EventTests.swift | 32 ++++ Tests/SAKBaseTests/BaseTypes/StackTests.swift | 32 ++++ .../BaseTypes/UniversalIDTests.swift | 33 ++++ .../{ => Concurrency}/Concurrency.swift | 0 Tests/SAKBaseTests/EventTests.swift | 54 ------ .../Comparison.swift} | 23 --- Tests/SAKBaseTests/SAKBaseTests.swift | 156 ------------------ 12 files changed, 266 insertions(+), 247 deletions(-) create mode 100644 Tests/SAKBaseTests/BaseTypes/BindingCommandTests.swift create mode 100644 Tests/SAKBaseTests/BaseTypes/CommandTests.swift create mode 100644 Tests/SAKBaseTests/BaseTypes/EventTests.swift create mode 100644 Tests/SAKBaseTests/BaseTypes/StackTests.swift create mode 100644 Tests/SAKBaseTests/BaseTypes/UniversalIDTests.swift rename Tests/SAKBaseTests/{ => Concurrency}/Concurrency.swift (100%) delete mode 100644 Tests/SAKBaseTests/EventTests.swift rename Tests/SAKBaseTests/{MathTests.swift => Math/Comparison.swift} (67%) delete mode 100644 Tests/SAKBaseTests/SAKBaseTests.swift diff --git a/SAKBase.xcodeproj/project.pbxproj b/SAKBase.xcodeproj/project.pbxproj index 12612aa..02069df 100644 --- a/SAKBase.xcodeproj/project.pbxproj +++ b/SAKBase.xcodeproj/project.pbxproj @@ -8,10 +8,9 @@ /* Begin PBXBuildFile section */ CC90BC1224AA7CD2002CB53F /* SAKBase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CC90BC0924AA7CD1002CB53F /* SAKBase.framework */; }; - CC90BC2024AA7CE2002CB53F /* MathTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0D223D6B3860027CD2B /* MathTests.swift */; }; + CC90BC2024AA7CE2002CB53F /* Comparison.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0D223D6B3860027CD2B /* Comparison.swift */; }; CC90BC2124AA7CE2002CB53F /* EventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0D423D6B3860027CD2B /* EventTests.swift */; }; CC90BC2224AA7CE2002CB53F /* Concurrency.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0EE23D6CB3F0027CD2B /* Concurrency.swift */; }; - CC90BC2324AA7CE2002CB53F /* SAKBaseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0D323D6B3860027CD2B /* SAKBaseTests.swift */; }; CC90BC2424AA7CE7002CB53F /* Point3.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0AE23D6B3550027CD2B /* Point3.swift */; }; CC90BC2524AA7CE7002CB53F /* Point.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0B023D6B3550027CD2B /* Point.swift */; }; CC90BC2624AA7CE7002CB53F /* UniversalID.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0AC23D6B3550027CD2B /* UniversalID.swift */; }; @@ -28,6 +27,10 @@ CC90BC3124AA7CF7002CB53F /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0BC23D6B3550027CD2B /* String.swift */; }; CC90BC3224AA7CF7002CB53F /* Array.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0BB23D6B3550027CD2B /* Array.swift */; }; CC90BC3324AA7CF7002CB53F /* Optional.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0BD23D6B3550027CD2B /* Optional.swift */; }; + CCA0A4F524BE2BE1007258BA /* UniversalIDTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCA0A4F424BE2BE1007258BA /* UniversalIDTests.swift */; }; + CCA0A4F724BE2C0F007258BA /* StackTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCA0A4F624BE2C0F007258BA /* StackTests.swift */; }; + CCA0A4F924BE2C40007258BA /* BindingCommandTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCA0A4F824BE2C40007258BA /* BindingCommandTests.swift */; }; + CCA0A4FB24BE2C6B007258BA /* CommandTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCA0A4FA24BE2C6B007258BA /* CommandTests.swift */; }; CCCE144924AA801900022FFC /* SAKBase.h in Headers */ = {isa = PBXBuildFile; fileRef = CC90BC3824AA7D65002CB53F /* SAKBase.h */; settings = {ATTRIBUTES = (Public, ); }; }; /* End PBXBuildFile section */ @@ -46,6 +49,10 @@ CC90BC1124AA7CD2002CB53F /* SAKBaseTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SAKBaseTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; CC90BC3824AA7D65002CB53F /* SAKBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SAKBase.h; sourceTree = ""; }; CC90BC3A24AA7D6B002CB53F /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + CCA0A4F424BE2BE1007258BA /* UniversalIDTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UniversalIDTests.swift; sourceTree = ""; }; + CCA0A4F624BE2C0F007258BA /* StackTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StackTests.swift; sourceTree = ""; }; + CCA0A4F824BE2C40007258BA /* BindingCommandTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BindingCommandTests.swift; sourceTree = ""; }; + CCA0A4FA24BE2C6B007258BA /* CommandTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommandTests.swift; sourceTree = ""; }; CCC1C0AA23D6B3550027CD2B /* BindingCommand.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BindingCommand.swift; sourceTree = ""; }; CCC1C0AB23D6B3550027CD2B /* Stack.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Stack.swift; sourceTree = ""; }; CCC1C0AC23D6B3550027CD2B /* UniversalID.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UniversalID.swift; sourceTree = ""; }; @@ -62,8 +69,7 @@ CCC1C0BB23D6B3550027CD2B /* Array.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Array.swift; sourceTree = ""; }; CCC1C0BC23D6B3550027CD2B /* String.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = ""; }; CCC1C0BD23D6B3550027CD2B /* Optional.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Optional.swift; sourceTree = ""; }; - CCC1C0D223D6B3860027CD2B /* MathTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MathTests.swift; sourceTree = ""; }; - CCC1C0D323D6B3860027CD2B /* SAKBaseTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SAKBaseTests.swift; sourceTree = ""; }; + CCC1C0D223D6B3860027CD2B /* Comparison.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Comparison.swift; sourceTree = ""; }; CCC1C0D423D6B3860027CD2B /* EventTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventTests.swift; sourceTree = ""; }; CCC1C0E323D6B45E0027CD2B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; CCC1C0EE23D6CB3F0027CD2B /* Concurrency.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Concurrency.swift; sourceTree = ""; }; @@ -88,6 +94,34 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + CCA0A4F124BE2B68007258BA /* Math */ = { + isa = PBXGroup; + children = ( + CCC1C0D223D6B3860027CD2B /* Comparison.swift */, + ); + path = Math; + sourceTree = ""; + }; + CCA0A4F224BE2B9A007258BA /* BaseTypes */ = { + isa = PBXGroup; + children = ( + CCC1C0D423D6B3860027CD2B /* EventTests.swift */, + CCA0A4FA24BE2C6B007258BA /* CommandTests.swift */, + CCA0A4F424BE2BE1007258BA /* UniversalIDTests.swift */, + CCA0A4F624BE2C0F007258BA /* StackTests.swift */, + CCA0A4F824BE2C40007258BA /* BindingCommandTests.swift */, + ); + path = BaseTypes; + sourceTree = ""; + }; + CCA0A4F324BE2BA5007258BA /* Concurrency */ = { + isa = PBXGroup; + children = ( + CCC1C0EE23D6CB3F0027CD2B /* Concurrency.swift */, + ); + path = Concurrency; + sourceTree = ""; + }; CCC1C09323D6B3320027CD2B = { isa = PBXGroup; children = ( @@ -169,10 +203,9 @@ CCC1C0D123D6B3860027CD2B /* SAKBaseTests */ = { isa = PBXGroup; children = ( - CCC1C0D223D6B3860027CD2B /* MathTests.swift */, - CCC1C0D323D6B3860027CD2B /* SAKBaseTests.swift */, - CCC1C0EE23D6CB3F0027CD2B /* Concurrency.swift */, - CCC1C0D423D6B3860027CD2B /* EventTests.swift */, + CCA0A4F124BE2B68007258BA /* Math */, + CCA0A4F224BE2B9A007258BA /* BaseTypes */, + CCA0A4F324BE2BA5007258BA /* Concurrency */, ); path = SAKBaseTests; sourceTree = ""; @@ -333,8 +366,11 @@ files = ( CC90BC2124AA7CE2002CB53F /* EventTests.swift in Sources */, CC90BC2224AA7CE2002CB53F /* Concurrency.swift in Sources */, - CC90BC2324AA7CE2002CB53F /* SAKBaseTests.swift in Sources */, - CC90BC2024AA7CE2002CB53F /* MathTests.swift in Sources */, + CCA0A4F524BE2BE1007258BA /* UniversalIDTests.swift in Sources */, + CC90BC2024AA7CE2002CB53F /* Comparison.swift in Sources */, + CCA0A4F724BE2C0F007258BA /* StackTests.swift in Sources */, + CCA0A4F924BE2C40007258BA /* BindingCommandTests.swift in Sources */, + CCA0A4FB24BE2C6B007258BA /* CommandTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/SAKBase.xcodeproj/xcshareddata/xcschemes/SAKBase.xcscheme b/SAKBase.xcodeproj/xcshareddata/xcschemes/SAKBase.xcscheme index aa336f3..ad17c30 100644 --- a/SAKBase.xcodeproj/xcshareddata/xcschemes/SAKBase.xcscheme +++ b/SAKBase.xcodeproj/xcshareddata/xcschemes/SAKBase.xcscheme @@ -14,7 +14,7 @@ buildForAnalyzing = "YES"> @@ -26,13 +26,14 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - shouldUseLaunchSchemeArgsEnv = "YES"> + shouldUseLaunchSchemeArgsEnv = "YES" + codeCoverageEnabled = "YES"> @@ -60,7 +61,7 @@ diff --git a/Sources/SAKBase/BaseTypes/Stack.swift b/Sources/SAKBase/BaseTypes/Stack.swift index 115f95e..1568b96 100755 --- a/Sources/SAKBase/BaseTypes/Stack.swift +++ b/Sources/SAKBase/BaseTypes/Stack.swift @@ -6,6 +6,8 @@ public struct Stack { fileprivate var array = [T]() + public init(){} + public var isEmpty: Bool { return array.isEmpty } diff --git a/Tests/SAKBaseTests/BaseTypes/BindingCommandTests.swift b/Tests/SAKBaseTests/BaseTypes/BindingCommandTests.swift new file mode 100644 index 0000000..c90ba0f --- /dev/null +++ b/Tests/SAKBaseTests/BaseTypes/BindingCommandTests.swift @@ -0,0 +1,38 @@ +// +// BindingCommandTests.swift +// SAKBaseTests +// +// Created by Stephen Kac on 7/14/20. +// Copyright © 2020 Stephen Kac. All rights reserved. +// + +import XCTest +import SAKBase + +class BindingCommandTests: XCTestCase { + + func testBindingCommand() { + /* + Tests the BindingCommands ability to undo and execute a closure. + */ + command: do { + let bindingCommand = BindingCommand(exec: { + return true + }) + XCTAssert(bindingCommand.execute()) // Should return true + } + + undoCommand: do { + let bindingCommand = BindingCommand(exec: { + return true + }, undo: { + return true + }) + + bindingCommand.execute() + XCTAssert(bindingCommand.undo()) // Should return true + } + + } + +} diff --git a/Tests/SAKBaseTests/BaseTypes/CommandTests.swift b/Tests/SAKBaseTests/BaseTypes/CommandTests.swift new file mode 100644 index 0000000..bcbe9ba --- /dev/null +++ b/Tests/SAKBaseTests/BaseTypes/CommandTests.swift @@ -0,0 +1,78 @@ +// +// CommandTests.swift +// SAKBaseTests +// +// Created by Stephen Kac on 7/14/20. +// Copyright © 2020 Stephen Kac. All rights reserved. +// + +import XCTest +import SAKBase + +class CommandTests: XCTestCase { + + func testCommand() { + + let testClass = TestClass() + + // TEST: Testing basic operation of command to execute + + let executeExclusiveCommand = Command(actor: testClass, value: true) { + guard let value = $1 else { return false } + $0.bool = value + return true + } + + + XCTAssert(executeExclusiveCommand.execute()) + XCTAssert(testClass.bool) + testClass.bool = false + XCTAssert(executeExclusiveCommand.getUndo() == nil) + + // TEST: Testing exec operation and then undoing of that operation + + let executeUndoCommand = Command(actor: testClass, value: true, exec: { + guard let value = $1 else { return false } + $0.bool = value + return true + }, undo: { + guard let value = $1 else { return false } + $0.bool = !value + return true + }) + + XCTAssert(executeUndoCommand.execute()) // Should successdully execute + XCTAssert(testClass.bool) // The Boolean should have changed to true + XCTAssert(executeUndoCommand.getUndo() != nil) // Should be able to get the undo command + XCTAssert(executeUndoCommand.undo()) // Should be able to successfully execute the Undo command + XCTAssert(!testClass.bool) // The boolean should be back to false + + + // TEST: Testing the undo functionality after the command has already been uninitialized + var uninitialized: Command? = Command(actor: testClass, value: true, exec: { + guard let value = $1 else { return false } + $0.bool = value + return true + }, undo: { + guard let value = $1 else { return false } + $0.bool = !value + return true + }) + + XCTAssert(uninitialized?.execute() ?? false) + XCTAssert(testClass.bool == true) + let undoCommand = uninitialized?.getUndo() + uninitialized = nil + XCTAssert(undoCommand?() ?? false) + XCTAssert(!(testClass.bool)) + + } + +} + +// MARK: - Helpers +extension CommandTests { + class TestClass { + var bool = false + } +} diff --git a/Tests/SAKBaseTests/BaseTypes/EventTests.swift b/Tests/SAKBaseTests/BaseTypes/EventTests.swift new file mode 100644 index 0000000..694c4ae --- /dev/null +++ b/Tests/SAKBaseTests/BaseTypes/EventTests.swift @@ -0,0 +1,32 @@ +// +// EventTests.swift +// SAKBase +// +// Created by Stephen Kac on 7/14/18. +// Copyright © 2018 Stephen Kac. All rights reserved. +// + +import XCTest +import SAKBase + +class EventTests: XCTestCase { + + var eventHandler: Disposable? = nil + + func test() { + + let event = Event() + + let string: NSString? = NSString("Hello") + + eventHandler = event.addHandler(string!) { + print($0, $1) + return true + } + + event.raise(5) + + event.raise(3) + } + +} diff --git a/Tests/SAKBaseTests/BaseTypes/StackTests.swift b/Tests/SAKBaseTests/BaseTypes/StackTests.swift new file mode 100644 index 0000000..5069929 --- /dev/null +++ b/Tests/SAKBaseTests/BaseTypes/StackTests.swift @@ -0,0 +1,32 @@ +// +// StackTests.swift +// SAKBaseTests +// +// Created by Stephen Kac on 7/14/20. +// Copyright © 2020 Stephen Kac. All rights reserved. +// + +import XCTest +import SAKBase + +class StackTests: XCTestCase { + + func testStack() { + var stack = Stack() + + XCTAssert(stack.count == 0) + XCTAssert(stack.isEmpty) + XCTAssert(stack.pop() == nil) + XCTAssert(stack.top == nil) + + stack.push(1) + stack.push(3) + + XCTAssert(stack.count == 2) + XCTAssert(!stack.isEmpty) + XCTAssert(stack.top == 3) + XCTAssert(stack.pop() == 3) + XCTAssert(stack.top == 1) + } + +} diff --git a/Tests/SAKBaseTests/BaseTypes/UniversalIDTests.swift b/Tests/SAKBaseTests/BaseTypes/UniversalIDTests.swift new file mode 100644 index 0000000..9484b55 --- /dev/null +++ b/Tests/SAKBaseTests/BaseTypes/UniversalIDTests.swift @@ -0,0 +1,33 @@ +// +// UniversalIDTests.swift +// SAKBaseTests +// +// Created by Stephen Kac on 7/14/20. +// Copyright © 2020 Stephen Kac. All rights reserved. +// + +import XCTest +import SAKBase + +class UniversalIDTests: XCTestCase { + + func testUniversalID() { + let uniqueIDs = UniversalID() + var uniqueSet = Set() + + // Generate 100000 ids + for _ in 0..<100000 { + uniqueSet.insert(uniqueIDs.nextID()) + } + + // If the count is 10000 and both sets are equal than 10000 unique ids were created + XCTAssert(uniqueIDs.ids == uniqueSet) + XCTAssert(uniqueIDs.ids.count == 100000) + + // Make sure id is removed properly + let id = uniqueSet.popFirst()! + uniqueIDs.remove(id: id) + XCTAssert(!uniqueIDs.ids.contains(id)) + } + +} diff --git a/Tests/SAKBaseTests/Concurrency.swift b/Tests/SAKBaseTests/Concurrency/Concurrency.swift similarity index 100% rename from Tests/SAKBaseTests/Concurrency.swift rename to Tests/SAKBaseTests/Concurrency/Concurrency.swift diff --git a/Tests/SAKBaseTests/EventTests.swift b/Tests/SAKBaseTests/EventTests.swift deleted file mode 100644 index 1a5ba8b..0000000 --- a/Tests/SAKBaseTests/EventTests.swift +++ /dev/null @@ -1,54 +0,0 @@ -// -// EventTests.swift -// SAKBase -// -// Created by Stephen Kac on 7/14/18. -// Copyright © 2018 Stephen Kac. All rights reserved. -// - -import XCTest -import SAKBase - -class EventTests: XCTestCase { - - var eventHandler: Disposable? = nil - - override func setUp() { - // Put setup code here. This method is called before the invocation of each test method in the class. - } - - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - - func test() { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. - - // TODO: Add Test expectations to test proper handler calling and disposing. - - - let event = Event() - - let string: NSString? = NSString("Hello") - - eventHandler = event.addHandler(string!) { - print($0, $1) - return true - } - - event.raise(5) - -// XCTAssert(event.raise(5)) - - event.raise(3) - } - - func testPerformanceExample() { - // This is an example of a performance test case. - self.measure { - // Put the code you want to measure the time of here. - } - } - -} diff --git a/Tests/SAKBaseTests/MathTests.swift b/Tests/SAKBaseTests/Math/Comparison.swift similarity index 67% rename from Tests/SAKBaseTests/MathTests.swift rename to Tests/SAKBaseTests/Math/Comparison.swift index b680c40..ce7915d 100644 --- a/Tests/SAKBaseTests/MathTests.swift +++ b/Tests/SAKBaseTests/Math/Comparison.swift @@ -9,16 +9,6 @@ import XCTest @testable import SAKBase class MathTests: XCTestCase { - - override func setUp() { - super.setUp() - // Put setup code here. This method is called before the invocation of each test method in the class. - } - - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - super.tearDown() - } // MARK: Comparison Tests @@ -42,17 +32,4 @@ class MathTests: XCTestCase { XCTAssert(areFloats(Double.leastNonzeroMagnitude, Double.zero, equalWithin: Double.leastNonzeroMagnitude.nextUp)) } - // func testFloor() { - // XCTAssert(!(2.0).fl ~= 1) - // - // XCTAssert((1.03).floor ~= 1) - // } - - func testPerformanceExample() { - // This is an example of a performance test case. - self.measure { - // Put the code you want to measure the time of here. - } - } - } diff --git a/Tests/SAKBaseTests/SAKBaseTests.swift b/Tests/SAKBaseTests/SAKBaseTests.swift deleted file mode 100644 index 60b51a1..0000000 --- a/Tests/SAKBaseTests/SAKBaseTests.swift +++ /dev/null @@ -1,156 +0,0 @@ -// -// SAKBase_macOSTests.swift -// SAKBase-macOSTests -// -// Created by Stephen Kac on 2/25/18. -// - -import XCTest -@testable import SAKBase - -class SAKBaseTests: XCTestCase { - - - override func setUp() { - super.setUp() - // Put setup code here. This method is called before the invocation of each test method in the class. - } - - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - super.tearDown() - } - - - - func testUniversalID() { - let uniqueIDs = UniversalID() - var uniqueSet = Set() - - // Generate 100000 ids - for _ in 0..<100000 { - uniqueSet.insert(uniqueIDs.nextID()) - } - - // If the count is 10000 and both sets are equal than 10000 unique ids were created - XCTAssert(uniqueIDs.ids == uniqueSet) - XCTAssert(uniqueIDs.ids.count == 100000) - - // Make sure id is removed properly - let id = uniqueSet.popFirst()! - uniqueIDs.remove(id: id) - XCTAssert(!uniqueIDs.ids.contains(id)) - } - - func testStack() { - var stack = Stack() - - - XCTAssert(stack.count == 0) - XCTAssert(stack.isEmpty) - XCTAssert(stack.pop() == nil) - XCTAssert(stack.top == nil) - - stack.push(1) - stack.push(3) - - XCTAssert(stack.count == 2) - XCTAssert(!stack.isEmpty) - XCTAssert(stack.top == 3) - XCTAssert(stack.pop() == 3) - XCTAssert(stack.top == 1) - } - - func testBindingCommand() { - /* - Tests the BindingCommands ability to undo and execute a closure. - */ - command: do { - let bindingCommand = BindingCommand(exec: { - return true - }) - XCTAssert(bindingCommand.execute()) // Should return true - } - - undoCommand: do { - let bindingCommand = BindingCommand(exec: { - return true - }, undo: { - return true - }) - - bindingCommand.execute() - XCTAssert(bindingCommand.undo()) // Should return true - } - - } - - func testCommand() { - - class TestClass { - var bool = false - } - - let testClass = TestClass() - - // TEST: Testing basic operation of command to execute - - let executeExclusiveCommand = Command(actor: testClass, value: true) { - guard let value = $1 else { return false } - $0.bool = value - return true - } - - - XCTAssert(executeExclusiveCommand.execute()) - XCTAssert(testClass.bool) - testClass.bool = false - XCTAssert(executeExclusiveCommand.getUndo() == nil) - - // TEST: Testing exec operation and then undoing of that operation - - let executeUndoCommand = Command(actor: testClass, value: true, exec: { - guard let value = $1 else { return false } - $0.bool = value - return true - }, undo: { - guard let value = $1 else { return false } - $0.bool = !value - return true - }) - - XCTAssert(executeUndoCommand.execute()) // Should successdully execute - XCTAssert(testClass.bool) // The Boolean should have changed to true - XCTAssert(executeUndoCommand.getUndo() != nil) // Should be able to get the undo command - XCTAssert(executeUndoCommand.undo()) // Should be able to successfully execute the Undo command - XCTAssert(!testClass.bool) // The boolean should be back to false - - - // TEST: Testing the undo functionality after the command has already been uninitialized - var uninitialized: Command? = Command(actor: testClass, value: true, exec: { - guard let value = $1 else { return false } - $0.bool = value - return true - }, undo: { - guard let value = $1 else { return false } - $0.bool = !value - return true - }) - - XCTAssert(uninitialized?.execute() ?? false) - XCTAssert(testClass.bool == true) - let undoCommand = uninitialized?.getUndo() - uninitialized = nil - XCTAssert(undoCommand?() ?? false) - XCTAssert(!(testClass.bool)) - - } - - func testPerformanceExample() { - // This is an example of a performance test case. - self.measure { - // Put the code you want to measure the time of here. - } - - } -} From 88ff6e85f701cbe344caaa4725a711d5845e0b55 Mon Sep 17 00:00:00 2001 From: Stephen Kac Date: Wed, 15 Jul 2020 12:03:32 -0600 Subject: [PATCH 02/26] Added new Float comparison functions --- SAKBase.xcodeproj/project.pbxproj | 63 +++++++--- .../contents.xcworkspacedata | 2 +- Sources/SAKBase/BaseTypes/Point.swift | 67 +++++++---- Sources/SAKBase/BaseTypes/Point3.swift | 79 ++++++++----- Sources/SAKBase/Math/Comparison.swift | 17 --- Sources/SAKBase/Math/RealComparison.swift | 110 ++++++++++++++++++ Sources/SAKBase/SAKBase.h | 19 --- Tests/SAKBaseTests/BaseTypes/PointTests.swift | 41 +++++++ Tests/SAKBaseTests/Math/Comparison.swift | 35 ------ Tests/SAKBaseTests/Math/RealComparison.swift | 44 +++++++ 10 files changed, 340 insertions(+), 137 deletions(-) delete mode 100644 Sources/SAKBase/Math/Comparison.swift create mode 100644 Sources/SAKBase/Math/RealComparison.swift delete mode 100644 Sources/SAKBase/SAKBase.h create mode 100644 Tests/SAKBaseTests/BaseTypes/PointTests.swift delete mode 100644 Tests/SAKBaseTests/Math/Comparison.swift create mode 100644 Tests/SAKBaseTests/Math/RealComparison.swift diff --git a/SAKBase.xcodeproj/project.pbxproj b/SAKBase.xcodeproj/project.pbxproj index 02069df..494f02f 100644 --- a/SAKBase.xcodeproj/project.pbxproj +++ b/SAKBase.xcodeproj/project.pbxproj @@ -3,15 +3,14 @@ archiveVersion = 1; classes = { }; - objectVersion = 50; + objectVersion = 52; objects = { /* Begin PBXBuildFile section */ CC90BC1224AA7CD2002CB53F /* SAKBase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CC90BC0924AA7CD1002CB53F /* SAKBase.framework */; }; - CC90BC2024AA7CE2002CB53F /* Comparison.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0D223D6B3860027CD2B /* Comparison.swift */; }; + CC90BC2024AA7CE2002CB53F /* RealComparison.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0D223D6B3860027CD2B /* RealComparison.swift */; }; CC90BC2124AA7CE2002CB53F /* EventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0D423D6B3860027CD2B /* EventTests.swift */; }; CC90BC2224AA7CE2002CB53F /* Concurrency.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0EE23D6CB3F0027CD2B /* Concurrency.swift */; }; - CC90BC2424AA7CE7002CB53F /* Point3.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0AE23D6B3550027CD2B /* Point3.swift */; }; CC90BC2524AA7CE7002CB53F /* Point.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0B023D6B3550027CD2B /* Point.swift */; }; CC90BC2624AA7CE7002CB53F /* UniversalID.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0AC23D6B3550027CD2B /* UniversalID.swift */; }; CC90BC2724AA7CE7002CB53F /* Stack.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0AB23D6B3550027CD2B /* Stack.swift */; }; @@ -19,7 +18,7 @@ CC90BC2924AA7CE7002CB53F /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0AD23D6B3550027CD2B /* Event.swift */; }; CC90BC2A24AA7CE7002CB53F /* BindingCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0AA23D6B3550027CD2B /* BindingCommand.swift */; }; CC90BC2B24AA7CEC002CB53F /* ProgressReporting.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0B223D6B3550027CD2B /* ProgressReporting.swift */; }; - CC90BC2C24AA7CEF002CB53F /* Comparison.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0B423D6B3550027CD2B /* Comparison.swift */; }; + CC90BC2C24AA7CEF002CB53F /* RealComparison.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0B423D6B3550027CD2B /* RealComparison.swift */; }; CC90BC2D24AA7CF4002CB53F /* GroupOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0B723D6B3550027CD2B /* GroupOperation.swift */; }; CC90BC2E24AA7CF4002CB53F /* PThread.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0B923D6B3550027CD2B /* PThread.swift */; }; CC90BC2F24AA7CF4002CB53F /* AsyncOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0B823D6B3550027CD2B /* AsyncOperation.swift */; }; @@ -31,7 +30,9 @@ CCA0A4F724BE2C0F007258BA /* StackTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCA0A4F624BE2C0F007258BA /* StackTests.swift */; }; CCA0A4F924BE2C40007258BA /* BindingCommandTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCA0A4F824BE2C40007258BA /* BindingCommandTests.swift */; }; CCA0A4FB24BE2C6B007258BA /* CommandTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCA0A4FA24BE2C6B007258BA /* CommandTests.swift */; }; - CCCE144924AA801900022FFC /* SAKBase.h in Headers */ = {isa = PBXBuildFile; fileRef = CC90BC3824AA7D65002CB53F /* SAKBase.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CCA0A4FD24BE438F007258BA /* PointTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCA0A4FC24BE438F007258BA /* PointTests.swift */; }; + CCA0A50024BE5856007258BA /* RealModule in Frameworks */ = {isa = PBXBuildFile; productRef = CCA0A4FF24BE5856007258BA /* RealModule */; }; + CCA0A50324BE6330007258BA /* Point3.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0AE23D6B3550027CD2B /* Point3.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -47,12 +48,12 @@ /* Begin PBXFileReference section */ CC90BC0924AA7CD1002CB53F /* SAKBase.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SAKBase.framework; sourceTree = BUILT_PRODUCTS_DIR; }; CC90BC1124AA7CD2002CB53F /* SAKBaseTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SAKBaseTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - CC90BC3824AA7D65002CB53F /* SAKBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SAKBase.h; sourceTree = ""; }; CC90BC3A24AA7D6B002CB53F /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; CCA0A4F424BE2BE1007258BA /* UniversalIDTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UniversalIDTests.swift; sourceTree = ""; }; CCA0A4F624BE2C0F007258BA /* StackTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StackTests.swift; sourceTree = ""; }; CCA0A4F824BE2C40007258BA /* BindingCommandTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BindingCommandTests.swift; sourceTree = ""; }; CCA0A4FA24BE2C6B007258BA /* CommandTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommandTests.swift; sourceTree = ""; }; + CCA0A4FC24BE438F007258BA /* PointTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointTests.swift; sourceTree = ""; }; CCC1C0AA23D6B3550027CD2B /* BindingCommand.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BindingCommand.swift; sourceTree = ""; }; CCC1C0AB23D6B3550027CD2B /* Stack.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Stack.swift; sourceTree = ""; }; CCC1C0AC23D6B3550027CD2B /* UniversalID.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UniversalID.swift; sourceTree = ""; }; @@ -61,7 +62,7 @@ CCC1C0AF23D6B3550027CD2B /* Command.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Command.swift; sourceTree = ""; }; CCC1C0B023D6B3550027CD2B /* Point.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Point.swift; sourceTree = ""; }; CCC1C0B223D6B3550027CD2B /* ProgressReporting.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProgressReporting.swift; sourceTree = ""; }; - CCC1C0B423D6B3550027CD2B /* Comparison.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Comparison.swift; sourceTree = ""; }; + CCC1C0B423D6B3550027CD2B /* RealComparison.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealComparison.swift; sourceTree = ""; }; CCC1C0B623D6B3550027CD2B /* Lock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Lock.swift; sourceTree = ""; }; CCC1C0B723D6B3550027CD2B /* GroupOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GroupOperation.swift; sourceTree = ""; }; CCC1C0B823D6B3550027CD2B /* AsyncOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AsyncOperation.swift; sourceTree = ""; }; @@ -69,7 +70,7 @@ CCC1C0BB23D6B3550027CD2B /* Array.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Array.swift; sourceTree = ""; }; CCC1C0BC23D6B3550027CD2B /* String.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = ""; }; CCC1C0BD23D6B3550027CD2B /* Optional.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Optional.swift; sourceTree = ""; }; - CCC1C0D223D6B3860027CD2B /* Comparison.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Comparison.swift; sourceTree = ""; }; + CCC1C0D223D6B3860027CD2B /* RealComparison.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealComparison.swift; sourceTree = ""; }; CCC1C0D423D6B3860027CD2B /* EventTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventTests.swift; sourceTree = ""; }; CCC1C0E323D6B45E0027CD2B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; CCC1C0EE23D6CB3F0027CD2B /* Concurrency.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Concurrency.swift; sourceTree = ""; }; @@ -80,6 +81,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + CCA0A50024BE5856007258BA /* RealModule in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -97,7 +99,7 @@ CCA0A4F124BE2B68007258BA /* Math */ = { isa = PBXGroup; children = ( - CCC1C0D223D6B3860027CD2B /* Comparison.swift */, + CCC1C0D223D6B3860027CD2B /* RealComparison.swift */, ); path = Math; sourceTree = ""; @@ -110,6 +112,7 @@ CCA0A4F424BE2BE1007258BA /* UniversalIDTests.swift */, CCA0A4F624BE2C0F007258BA /* StackTests.swift */, CCA0A4F824BE2C40007258BA /* BindingCommandTests.swift */, + CCA0A4FC24BE438F007258BA /* PointTests.swift */, ); path = BaseTypes; sourceTree = ""; @@ -174,7 +177,7 @@ CCC1C0B323D6B3550027CD2B /* Math */ = { isa = PBXGroup; children = ( - CCC1C0B423D6B3550027CD2B /* Comparison.swift */, + CCC1C0B423D6B3550027CD2B /* RealComparison.swift */, ); path = Math; sourceTree = ""; @@ -213,7 +216,6 @@ CCE7642224AA77C7002F2863 /* SAKBase */ = { isa = PBXGroup; children = ( - CC90BC3824AA7D65002CB53F /* SAKBase.h */, CCC1C0A923D6B3550027CD2B /* BaseTypes */, CCC1C0B123D6B3550027CD2B /* NSSwift */, CCC1C0B323D6B3550027CD2B /* Math */, @@ -239,7 +241,6 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( - CCCE144924AA801900022FFC /* SAKBase.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -260,6 +261,9 @@ dependencies = ( ); name = SAKBase; + packageProductDependencies = ( + CCA0A4FF24BE5856007258BA /* RealModule */, + ); productName = SAKBase; productReference = CC90BC0924AA7CD1002CB53F /* SAKBase.framework */; productType = "com.apple.product-type.framework"; @@ -309,6 +313,9 @@ Base, ); mainGroup = CCC1C09323D6B3320027CD2B; + packageReferences = ( + CCA0A4FE24BE5856007258BA /* XCRemoteSwiftPackageReference "swift-numerics" */, + ); productRefGroup = CCC1C09E23D6B3320027CD2B /* Products */; projectDirPath = ""; projectRoot = ""; @@ -345,6 +352,7 @@ CC90BC2B24AA7CEC002CB53F /* ProgressReporting.swift in Sources */, CC90BC2624AA7CE7002CB53F /* UniversalID.swift in Sources */, CC90BC2524AA7CE7002CB53F /* Point.swift in Sources */, + CCA0A50324BE6330007258BA /* Point3.swift in Sources */, CC90BC3124AA7CF7002CB53F /* String.swift in Sources */, CC90BC2E24AA7CF4002CB53F /* PThread.swift in Sources */, CC90BC3224AA7CF7002CB53F /* Array.swift in Sources */, @@ -352,8 +360,7 @@ CC90BC2824AA7CE7002CB53F /* Command.swift in Sources */, CC90BC3024AA7CF4002CB53F /* Lock.swift in Sources */, CC90BC2924AA7CE7002CB53F /* Event.swift in Sources */, - CC90BC2C24AA7CEF002CB53F /* Comparison.swift in Sources */, - CC90BC2424AA7CE7002CB53F /* Point3.swift in Sources */, + CC90BC2C24AA7CEF002CB53F /* RealComparison.swift in Sources */, CC90BC2F24AA7CF4002CB53F /* AsyncOperation.swift in Sources */, CC90BC2D24AA7CF4002CB53F /* GroupOperation.swift in Sources */, CC90BC3324AA7CF7002CB53F /* Optional.swift in Sources */, @@ -364,10 +371,11 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + CCA0A4FD24BE438F007258BA /* PointTests.swift in Sources */, CC90BC2124AA7CE2002CB53F /* EventTests.swift in Sources */, CC90BC2224AA7CE2002CB53F /* Concurrency.swift in Sources */, CCA0A4F524BE2BE1007258BA /* UniversalIDTests.swift in Sources */, - CC90BC2024AA7CE2002CB53F /* Comparison.swift in Sources */, + CC90BC2024AA7CE2002CB53F /* RealComparison.swift in Sources */, CCA0A4F724BE2C0F007258BA /* StackTests.swift in Sources */, CCA0A4F924BE2C40007258BA /* BindingCommandTests.swift in Sources */, CCA0A4FB24BE2C6B007258BA /* CommandTests.swift in Sources */, @@ -451,7 +459,7 @@ CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = N3S44ULQD4; INFOPLIST_FILE = Tests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.5; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -462,6 +470,7 @@ SDKROOT = iphoneos; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; + TVOS_DEPLOYMENT_TARGET = 13.3; }; name = Debug; }; @@ -472,7 +481,7 @@ CODE_SIGN_STYLE = Automatic; DEVELOPMENT_TEAM = N3S44ULQD4; INFOPLIST_FILE = Tests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.5; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -483,6 +492,7 @@ SDKROOT = iphoneos; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; + TVOS_DEPLOYMENT_TARGET = 13.3; VALIDATE_PRODUCT = YES; }; name = Release; @@ -643,6 +653,25 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + CCA0A4FE24BE5856007258BA /* XCRemoteSwiftPackageReference "swift-numerics" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "git@github.com:apple/swift-numerics.git"; + requirement = { + kind = exactVersion; + version = 0.0.6; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + CCA0A4FF24BE5856007258BA /* RealModule */ = { + isa = XCSwiftPackageProductDependency; + package = CCA0A4FE24BE5856007258BA /* XCRemoteSwiftPackageReference "swift-numerics" */; + productName = RealModule; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = CCC1C09423D6B3320027CD2B /* Project object */; } diff --git a/SAKBase.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/SAKBase.xcodeproj/project.xcworkspace/contents.xcworkspacedata index 3048a3a..919434a 100644 --- a/SAKBase.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ b/SAKBase.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -2,6 +2,6 @@ + location = "self:"> diff --git a/Sources/SAKBase/BaseTypes/Point.swift b/Sources/SAKBase/BaseTypes/Point.swift index e86cd2c..33fd5a4 100644 --- a/Sources/SAKBase/BaseTypes/Point.swift +++ b/Sources/SAKBase/BaseTypes/Point.swift @@ -6,51 +6,74 @@ // Copyright © 2016 StephenKac. All rights reserved. // +import RealModule import Foundation -// A Swift protocol for a point in some 2 dimensional container +/// A Swift protocol for a point in some 2 dimensional container public protocol Point: Equatable { - var x: Double { get set } - var y: Double { get set } - - init(x: Float, y: Float) - - init(x: Int, y: Int) - - init(x: Double, y: Double) + associatedtype PointValue: Real + + var xCord: PointValue { get set} + var yCord: PointValue { get set} + + init(xCord: PointValue, yCord: PointValue) } public extension Point { - static func +=(left: inout Self, right: Self) { - left = left + right - } + init(xCord: Int, yCord: Int) { + self.init(xCord: PointValue(xCord), yCord: PointValue(yCord)) + } } -public func ==(lhs: PointType, rhs: PointType) -> Bool { - return lhs.x == rhs.x && lhs.y == rhs.y +public extension Point { + static func +=(left: inout Self, right: Self) { + left = left + right + } +} + +public extension Point { + /// Returns wether or not the value is within the given episolon of the other value + /// - Parameters: + /// - epsilon: The max allowed difference between two values + /// - point: The value to compare against + /// - Returns: True if within epsilon of point, false otherwise. + func within(_ epsilon: Self, of point: Self) -> Bool { + return self.xCord.within(epsilon.xCord, of: point.xCord) && + self.yCord.within(epsilon.yCord, of: point.yCord) + } + + /// Returns wether or not the value is within the given episolon of the other value + /// - Parameters: + /// - epsilon: The max allowed difference between two values + /// - point: The value to compare against + /// - Returns: True if within epsilon of point, false otherwise. + func cordsWithin(_ epsilon: Self.PointValue, of point: Self) -> Bool { + return within(Self.init(xCord: epsilon, yCord: epsilon), + of: point) + } } public func +(lhs: PointType, rhs: PointType) -> PointType { - return PointType(x: lhs.x + rhs.x, y: lhs.y + rhs.y) + return PointType(xCord: lhs.xCord + rhs.xCord, yCord: lhs.yCord + rhs.yCord) } public func -(lhs: PointType, rhs: PointType) -> PointType { - return PointType(x: lhs.x - rhs.x, y: lhs.y - rhs.y) + return PointType(xCord: lhs.xCord - rhs.xCord, yCord: lhs.yCord - rhs.yCord) } public func *(lhs: PointType, rhs: PointType) -> PointType { - return PointType(x: lhs.x * rhs.x, y: lhs.y * rhs.y) + return PointType(xCord: lhs.xCord * rhs.xCord, yCord: lhs.yCord * rhs.yCord) } public func /(lhs: PointType, rhs: PointType) -> PointType { - return PointType(x: lhs.x / rhs.x, y: lhs.y / rhs.y) + return PointType(xCord: lhs.xCord / rhs.xCord, yCord: lhs.yCord / rhs.yCord) } public extension Point { - func floored() -> Self { - let value = Self.init(x: floor(self.x), y: floor(self.y)) - return value - } + func floored() -> Self { + let value = Self.init(xCord: floor(xCord), yCord: floor(yCord)) + return value + } } diff --git a/Sources/SAKBase/BaseTypes/Point3.swift b/Sources/SAKBase/BaseTypes/Point3.swift index 8032cb1..1ecc131 100644 --- a/Sources/SAKBase/BaseTypes/Point3.swift +++ b/Sources/SAKBase/BaseTypes/Point3.swift @@ -5,46 +5,73 @@ // Created by Stephen Kac on 12/29/16. // Copyright © 2016 StephenKac. All rights reserved. // -import GameKit +import RealModule +import Foundation +/// A Swift protocol for a point in some 2 dimensional container public protocol Point3: Equatable { - var x: Double { get set } - var y: Double { get set } - var z: Double { get set } - - init(x: Double, y: Double, z: Double) - - init(x: Int, y: Int, z: Int) - - init(x: Float, y: Float, z: Float) + associatedtype PointValue: Real + + var xCord: PointValue { get set} + var yCord: PointValue { get set} + var zCord: PointValue { get set} + + init(xCord: PointValue, yCord: PointValue, zCord: PointValue) } -extension Point3 { - static func +=(left: inout Self, right: Self) { - left = left + right - } +public extension Point3 { + init(xCord: Int, yCord: Int, zCord: Int) { + self.init(xCord: PointValue(xCord), yCord: PointValue(yCord), zCord: PointValue(zCord)) + } } -public func ==(lhs: Point3Type, rhs: Point3Type) -> Bool { - return lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z +public extension Point3 { + static func +=(left: inout Self, right: Self) { + left = left + right + } +} + +public extension Point3 { + /// Returns wether or not the value is within the given episolon of the other value + /// - Parameters: + /// - epsilon: The max allowed difference between two values + /// - point: The value to compare against + /// - Returns: True if within epsilon of point, false otherwise. + func within(_ epsilon: Self, of point: Self) -> Bool { + xCord.within(epsilon.xCord, of: point.xCord) && + yCord.within(epsilon.yCord, of: point.yCord) && + zCord.within(epsilon.zCord, of: point.zCord) + } + + /// Returns wether or not the value is within the given episolon of the other value + /// - Parameters: + /// - epsilon: The max allowed difference between two values + /// - point: The value to compare against + /// - Returns: True if within epsilon of point, false otherwise. + func cordsWithin(_ epsilon: Self.PointValue, of point: Self) -> Bool { + return within(Self.init(xCord: epsilon, yCord: epsilon, zCord: epsilon), + of: point) + } } -public func +(lhs: Point3Type, rhs: Point3Type) -> Point3Type { - return Point3Type.init(x: lhs.x + rhs.x, y: lhs.y + rhs.y, z: lhs.z + rhs.z) +public func +(lhs: PointType, rhs: PointType) -> PointType { + PointType(xCord: lhs.xCord + rhs.xCord, yCord: lhs.yCord + rhs.yCord, zCord: lhs.zCord + rhs.zCord) } -public func -(lhs: Point3Type, rhs: Point3Type) -> Point3Type { - return Point3Type.init(x: lhs.x - rhs.x, y: lhs.y - rhs.y, z: lhs.z - rhs.z) +public func -(lhs: PointType, rhs: PointType) -> PointType { + PointType(xCord: lhs.xCord - rhs.xCord, yCord: lhs.yCord - rhs.yCord, zCord: lhs.zCord - rhs.zCord) } -public func *(lhs: Point3Type, rhs: Point3Type) -> Point3Type { - return Point3Type.init(x: lhs.x * rhs.x, y: lhs.y * rhs.y, z: lhs.z * rhs.z) +public func *(lhs: PointType, rhs: PointType) -> PointType { + PointType(xCord: lhs.xCord * rhs.xCord, yCord: lhs.yCord * rhs.yCord, zCord: lhs.zCord * rhs.zCord) } -public func /(lhs: Point3Type, rhs: Point3Type) -> Point3Type { - return Point3Type.init(x: lhs.x / rhs.x, y: lhs.y / rhs.y, z: lhs.z / rhs.z) +public func /(lhs: PointType, rhs: PointType) -> PointType { + PointType(xCord: lhs.xCord / rhs.xCord, yCord: lhs.yCord / rhs.yCord, zCord: lhs.yCord / rhs.yCord) } -public func floor(Point3: Point3Type) -> Point3Type { - return Point3Type.init(x: floor(Point3.x), y: floor(Point3.x), z: floor(Point3.z)) +public extension Point3 { + func floored() -> Self { + Self.init(xCord: floor(xCord), yCord: floor(yCord), zCord: floor(zCord)) + } } diff --git a/Sources/SAKBase/Math/Comparison.swift b/Sources/SAKBase/Math/Comparison.swift deleted file mode 100644 index 0da6766..0000000 --- a/Sources/SAKBase/Math/Comparison.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// File.swift -// SAKBase -// -// Created by Stephen Kac on 2/27/18. -// - - -/// Returns wether the two types are equal within the given epsilon -/// - Parameters: -/// - left -/// - right -/// - epislon: Check difference is within this value -public func areFloats(_ left: FloatingType, _ right: FloatingType, equalWithin epislon: FloatingType) -> Bool { - return abs(left - right) < epislon -} - diff --git a/Sources/SAKBase/Math/RealComparison.swift b/Sources/SAKBase/Math/RealComparison.swift new file mode 100644 index 0000000..d1c42b2 --- /dev/null +++ b/Sources/SAKBase/Math/RealComparison.swift @@ -0,0 +1,110 @@ +// +// File.swift +// SAKBase +// +// Created by Stephen Kac on 2/27/18. +// + +import RealModule + + +/// - Parameters: + + +public extension Real { + + /// Returns wether or not the value is within the given episolon of the other value + /// - Parameters: + /// - epsilon: The max allowed difference between two values + /// - realNumber: The value to compare against + /// - Returns: True if within epsilon of realNumber, false otherwise. + func within(_ epsilon: Self, of realNumber: Self) -> Bool { + + guard (self.isNaN || realNumber.isNaN || epsilon.isNaN) == false else { + return false + } + + guard self != realNumber else { + return true + } + + guard self.sign == realNumber.sign else { + return false + } + + guard (self.isInfinite || realNumber.isInfinite) == false else { + return false + } + + guard epsilon.isInfinite == false else { + return true + } + + let range = (realNumber - epsilon)...(realNumber + epsilon) + + return range.contains(self) + } +} + +public protocol EquivalentIntegerSize { + associatedtype IntegerSize: SignedNumeric & FixedWidthInteger +} + +public extension EquivalentIntegerSize where Self: BinaryFloatingPoint { + + /// Returns wether or not the value is within the given ulps epsilon of the other value + /// - Warning: Ulps is not a good way to compare small numbers. 0.1 and 0 has 1000's of ulps of difference + /// - Parameters: + /// - ulps: The max allowed difference in ulps between two values + /// - realNumber: The value to compare against + /// - Returns: True if within epsilon of realNumber, false otherwise. + func within(ulps: UInt, of realNumber: Self) -> Bool { + + let withinUlps = self.distanceInUlps(to: realNumber) + + return withinUlps <= ulps + } + + + /// Gets the distance between floating points in ulps. + /// - Warning: Ulps is not a good way to compare small numbers. 0.1 and 0 has 1000's of ulps of difference + /// - Parameter realNumber: The number to compare with + /// - Returns: the distance in ulps + func distanceInUlps(to realNumber: Self) -> IntegerSize { + guard (self.isNaN || realNumber.isNaN) == false else { + return IntegerSize.max + } + + guard self != realNumber else { + return 0 + } + + guard (self.isInfinite || realNumber.isInfinite) == false else { + return IntegerSize.max + } + + guard self.sign == realNumber.sign else { + return IntegerSize.max + } + + let bitcastedFloat = unsafeBitCast(self, to: IntegerSize.self) + let bitcastedFloatCompared = unsafeBitCast(realNumber, to: IntegerSize.self) + + return abs(bitcastedFloat - bitcastedFloatCompared) + } +} + +// MARK: - Conformances + +extension Float32: EquivalentIntegerSize { + public typealias IntegerSize = Int32 +} + +extension Float64: EquivalentIntegerSize { + public typealias IntegerSize = Int64 +} + +@available(iOS 14.0, *) +extension Float16: EquivalentIntegerSize { + public typealias IntegerSize = Int16 +} diff --git a/Sources/SAKBase/SAKBase.h b/Sources/SAKBase/SAKBase.h deleted file mode 100644 index ed4db9b..0000000 --- a/Sources/SAKBase/SAKBase.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// SAKBase.h -// SAKBase -// -// Created by Stephen Kac on 6/29/20. -// Copyright © 2020 Stephen Kac. All rights reserved. -// - -#import - -//! Project version number for SAKBase. -FOUNDATION_EXPORT double SAKBaseVersionNumber; - -//! Project version string for SAKBase. -FOUNDATION_EXPORT const unsigned char SAKBaseVersionString[]; - -// In this header, you should import all the public headers of your framework using statements like #import - - diff --git a/Tests/SAKBaseTests/BaseTypes/PointTests.swift b/Tests/SAKBaseTests/BaseTypes/PointTests.swift new file mode 100644 index 0000000..b950db9 --- /dev/null +++ b/Tests/SAKBaseTests/BaseTypes/PointTests.swift @@ -0,0 +1,41 @@ +// +// PointTests.swift +// SAKBaseTests +// +// Created by Stephen Kac on 7/14/20. +// Copyright © 2020 Stephen Kac. All rights reserved. +// + +import XCTest +import SAKBase + +class PointTests: XCTestCase { + + func test_points_equality() { + let point1 = PointTest(xCord: 1, yCord: 1) + let point2 = PointTest(xCord: 1.0, yCord: 1.0) + let point3 = PointTest(xCord: 1.1, yCord: 1.0) + + XCTAssertTrue(point1 ~= point2) + XCTAssertFalse(point1 ~= point3) + } + + func test_points_arithmetic() { + let point1 = PointTest(xCord: 1, yCord: 1) + + XCTAssertTrue((point1 + point1).cordsWithin(0.00000000001, + of: PointTest(xCord: 2, yCord: 2))) + + XCTAssertTrue((point1 - point1).cordsWithin(0.00000000001, + of: PointTest(xCord: 0.0, yCord: 0.0))) + } +} + +private extension PointTests { + struct PointTest: Point { + typealias PointValue = Double + + var xCord: PointValue + var yCord: PointValue + } +} diff --git a/Tests/SAKBaseTests/Math/Comparison.swift b/Tests/SAKBaseTests/Math/Comparison.swift deleted file mode 100644 index ce7915d..0000000 --- a/Tests/SAKBaseTests/Math/Comparison.swift +++ /dev/null @@ -1,35 +0,0 @@ -// -// MathTests.swift -// SAKBase -// -// Created by Stephen Kac on 2/27/18. -// - -import XCTest -@testable import SAKBase - -class MathTests: XCTestCase { - - - // MARK: Comparison Tests - func testNearEqualOperator() { - // Canhandle floats that should be equal and epislons great than the biggest compared float - XCTAssert(areFloats(1.0, 1.0, equalWithin: 1.0)) - - XCTAssert(areFloats(1.0, 1.0, equalWithin: 2.0)) - - // Must treat Infinity as always equal - XCTAssert(areFloats(Double.greatestFiniteMagnitude, Double.leastNonzeroMagnitude, equalWithin: Double.infinity)) - - // Must traet nan epislon as never equal - XCTAssert(!areFloats(1.0, 1.0, equalWithin: Double.nan)) - XCTAssert(!areFloats(Double.zero, Double.zero, equalWithin: Double.nan)) - XCTAssert(!areFloats(Double.leastNonzeroMagnitude, Double.leastNonzeroMagnitude, equalWithin: Double.nan)) - XCTAssert(!areFloats(Double.greatestFiniteMagnitude, Double.greatestFiniteMagnitude, equalWithin: Double.nan)) - - // Can handle small and big values - XCTAssert(areFloats(Double.greatestFiniteMagnitude, Double.greatestFiniteMagnitude, equalWithin: 1)) - XCTAssert(areFloats(Double.leastNonzeroMagnitude, Double.zero, equalWithin: Double.leastNonzeroMagnitude.nextUp)) - } - -} diff --git a/Tests/SAKBaseTests/Math/RealComparison.swift b/Tests/SAKBaseTests/Math/RealComparison.swift new file mode 100644 index 0000000..31fec27 --- /dev/null +++ b/Tests/SAKBaseTests/Math/RealComparison.swift @@ -0,0 +1,44 @@ +// +// MathTests.swift +// SAKBase +// +// Created by Stephen Kac on 2/27/18. +// + +import XCTest +@testable import SAKBase + +class MathTests: XCTestCase { + + + // MARK: Comparison Tests + func testNearEqualOperator() { + // Can handle floats that should be equal and epislons great than the biggest compared float + XCTAssertTrue(1.0.within(0.00000000000001, of: 1.0)) + + XCTAssertTrue(1.0.within(2.0, of: 0.1)) + + // Must treat Infinity as always equal + XCTAssertTrue(Double.greatestFiniteMagnitude.within(.infinity, of: .leastNonzeroMagnitude)) + + // Must treet nan epislon as never equal + XCTAssertFalse(1.0.within(.nan, of: 1.0)) + XCTAssertFalse(Double.zero.within(.nan, of: .zero)) + XCTAssertFalse(Double.leastNonzeroMagnitude.within(.greatestFiniteMagnitude, of: .nan)) + XCTAssertFalse(Double.greatestFiniteMagnitude.within(Double.nan, of: Double.greatestFiniteMagnitude)) + + // Can handle small and big values + XCTAssertTrue(Double.greatestFiniteMagnitude.within(1, of: Double.greatestFiniteMagnitude)) + XCTAssertTrue(Double.leastNonzeroMagnitude.within(Double.leastNonzeroMagnitude.nextUp, of: Double.zero)) + } + + func testUlps() { + XCTAssertEqual(Float32(1.0).distanceInUlps(to: Float(1.0).nextUp), 1) + + XCTAssertTrue(1.0.within(ulps: 1, of: 1.0.nextUp)) + XCTAssertEqual(1.0.distanceInUlps(to: 1.0.nextUp), 1) + + XCTAssertTrue(1.0.within(ulps: 2, of: 1.0.nextUp.nextUp)) + XCTAssertTrue(1.0.within(ulps: 2, of: 1.0.nextDown)) + } +} From 3e1e1cf778203e80936b0049344a80cab8948649 Mon Sep 17 00:00:00 2001 From: Stephen Kac Date: Wed, 15 Jul 2020 12:26:46 -0600 Subject: [PATCH 03/26] Fixed a few edge cases. for the real comparisons --- SAKBase.xcodeproj/project.pbxproj | 8 +++---- .../xcshareddata/xcschemes/SAKBase.xcscheme | 12 ++++++++++- Sources/SAKBase/Math/RealComparison.swift | 5 +---- ...arison.swift => RealComparisonTests.swift} | 21 ++++++++++++++++--- 4 files changed, 34 insertions(+), 12 deletions(-) rename Tests/SAKBaseTests/Math/{RealComparison.swift => RealComparisonTests.swift} (62%) diff --git a/SAKBase.xcodeproj/project.pbxproj b/SAKBase.xcodeproj/project.pbxproj index 494f02f..a14d11e 100644 --- a/SAKBase.xcodeproj/project.pbxproj +++ b/SAKBase.xcodeproj/project.pbxproj @@ -8,7 +8,7 @@ /* Begin PBXBuildFile section */ CC90BC1224AA7CD2002CB53F /* SAKBase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CC90BC0924AA7CD1002CB53F /* SAKBase.framework */; }; - CC90BC2024AA7CE2002CB53F /* RealComparison.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0D223D6B3860027CD2B /* RealComparison.swift */; }; + CC90BC2024AA7CE2002CB53F /* RealComparisonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0D223D6B3860027CD2B /* RealComparisonTests.swift */; }; CC90BC2124AA7CE2002CB53F /* EventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0D423D6B3860027CD2B /* EventTests.swift */; }; CC90BC2224AA7CE2002CB53F /* Concurrency.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0EE23D6CB3F0027CD2B /* Concurrency.swift */; }; CC90BC2524AA7CE7002CB53F /* Point.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0B023D6B3550027CD2B /* Point.swift */; }; @@ -70,7 +70,7 @@ CCC1C0BB23D6B3550027CD2B /* Array.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Array.swift; sourceTree = ""; }; CCC1C0BC23D6B3550027CD2B /* String.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = ""; }; CCC1C0BD23D6B3550027CD2B /* Optional.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Optional.swift; sourceTree = ""; }; - CCC1C0D223D6B3860027CD2B /* RealComparison.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealComparison.swift; sourceTree = ""; }; + CCC1C0D223D6B3860027CD2B /* RealComparisonTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealComparisonTests.swift; sourceTree = ""; }; CCC1C0D423D6B3860027CD2B /* EventTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EventTests.swift; sourceTree = ""; }; CCC1C0E323D6B45E0027CD2B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; CCC1C0EE23D6CB3F0027CD2B /* Concurrency.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Concurrency.swift; sourceTree = ""; }; @@ -99,7 +99,7 @@ CCA0A4F124BE2B68007258BA /* Math */ = { isa = PBXGroup; children = ( - CCC1C0D223D6B3860027CD2B /* RealComparison.swift */, + CCC1C0D223D6B3860027CD2B /* RealComparisonTests.swift */, ); path = Math; sourceTree = ""; @@ -375,7 +375,7 @@ CC90BC2124AA7CE2002CB53F /* EventTests.swift in Sources */, CC90BC2224AA7CE2002CB53F /* Concurrency.swift in Sources */, CCA0A4F524BE2BE1007258BA /* UniversalIDTests.swift in Sources */, - CC90BC2024AA7CE2002CB53F /* RealComparison.swift in Sources */, + CC90BC2024AA7CE2002CB53F /* RealComparisonTests.swift in Sources */, CCA0A4F724BE2C0F007258BA /* StackTests.swift in Sources */, CCA0A4F924BE2C40007258BA /* BindingCommandTests.swift in Sources */, CCA0A4FB24BE2C6B007258BA /* CommandTests.swift in Sources */, diff --git a/SAKBase.xcodeproj/xcshareddata/xcschemes/SAKBase.xcscheme b/SAKBase.xcodeproj/xcshareddata/xcschemes/SAKBase.xcscheme index ad17c30..5a88937 100644 --- a/SAKBase.xcodeproj/xcshareddata/xcschemes/SAKBase.xcscheme +++ b/SAKBase.xcodeproj/xcshareddata/xcschemes/SAKBase.xcscheme @@ -27,7 +27,17 @@ selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" shouldUseLaunchSchemeArgsEnv = "YES" - codeCoverageEnabled = "YES"> + codeCoverageEnabled = "YES" + onlyGenerateCoverageForSpecifiedTargets = "YES"> + + + + diff --git a/Sources/SAKBase/Math/RealComparison.swift b/Sources/SAKBase/Math/RealComparison.swift index d1c42b2..ecd95af 100644 --- a/Sources/SAKBase/Math/RealComparison.swift +++ b/Sources/SAKBase/Math/RealComparison.swift @@ -28,10 +28,6 @@ public extension Real { return true } - guard self.sign == realNumber.sign else { - return false - } - guard (self.isInfinite || realNumber.isInfinite) == false else { return false } @@ -59,6 +55,7 @@ public extension EquivalentIntegerSize where Self: BinaryFloatingPoint { /// - realNumber: The value to compare against /// - Returns: True if within epsilon of realNumber, false otherwise. func within(ulps: UInt, of realNumber: Self) -> Bool { + precondition(ulps != IntegerSize.max) let withinUlps = self.distanceInUlps(to: realNumber) diff --git a/Tests/SAKBaseTests/Math/RealComparison.swift b/Tests/SAKBaseTests/Math/RealComparisonTests.swift similarity index 62% rename from Tests/SAKBaseTests/Math/RealComparison.swift rename to Tests/SAKBaseTests/Math/RealComparisonTests.swift index 31fec27..1e46237 100644 --- a/Tests/SAKBaseTests/Math/RealComparison.swift +++ b/Tests/SAKBaseTests/Math/RealComparisonTests.swift @@ -1,5 +1,5 @@ // -// MathTests.swift +// RealComparisonTests.swift // SAKBase // // Created by Stephen Kac on 2/27/18. @@ -8,7 +8,7 @@ import XCTest @testable import SAKBase -class MathTests: XCTestCase { +class RealComparisonTests: XCTestCase { // MARK: Comparison Tests @@ -18,7 +18,7 @@ class MathTests: XCTestCase { XCTAssertTrue(1.0.within(2.0, of: 0.1)) - // Must treat Infinity as always equal + // Must treat Infinity as always equal XCTAssertTrue(Double.greatestFiniteMagnitude.within(.infinity, of: .leastNonzeroMagnitude)) // Must treet nan epislon as never equal @@ -30,6 +30,12 @@ class MathTests: XCTestCase { // Can handle small and big values XCTAssertTrue(Double.greatestFiniteMagnitude.within(1, of: Double.greatestFiniteMagnitude)) XCTAssertTrue(Double.leastNonzeroMagnitude.within(Double.leastNonzeroMagnitude.nextUp, of: Double.zero)) + + // Can handle differently signed values + XCTAssertTrue(Double.zero.nextDown.within(0.1, of: Double.zero)) + XCTAssertTrue(Double.zero.nextDown.within(0.1, of: Double.zero.nextUp)) + + XCTAssertFalse(Double.infinity.within(Double.greatestFiniteMagnitude, of: 0.0)) } func testUlps() { @@ -40,5 +46,14 @@ class MathTests: XCTestCase { XCTAssertTrue(1.0.within(ulps: 2, of: 1.0.nextUp.nextUp)) XCTAssertTrue(1.0.within(ulps: 2, of: 1.0.nextDown)) + + XCTAssertFalse(Double.nan.within(ulps: 2, of: 1.0.nextUp.nextUp)) + XCTAssertFalse(Double.infinity.within(ulps: 2, of: 1.0.nextDown)) + XCTAssertTrue(Double.zero.within(ulps: 0, of: Double.zero)) + + XCTAssertTrue(Double.zero.within(ulps: 1, of: -Double.zero)) + + // Differently signed values are always false no matter what + XCTAssertFalse((-Double.zero.nextUp).within(ulps: UInt(Int.max) - 1, of: Double.zero.nextUp)) } } From e54bbcb763fdc4d2a6276610b00f91b786d3e67f Mon Sep 17 00:00:00 2001 From: Stephen Kac Date: Wed, 15 Jul 2020 16:02:36 -0600 Subject: [PATCH 04/26] Removed unneeded testable import and updated the Package.swift file for the new dependency --- Package.resolved | 16 ++++++++++++++++ Package.swift | 3 ++- .../SAKBaseTests/Math/RealComparisonTests.swift | 6 +++--- 3 files changed, 21 insertions(+), 4 deletions(-) create mode 100644 Package.resolved diff --git a/Package.resolved b/Package.resolved new file mode 100644 index 0000000..32dd46a --- /dev/null +++ b/Package.resolved @@ -0,0 +1,16 @@ +{ + "object": { + "pins": [ + { + "package": "swift-numerics", + "repositoryURL": "git@github.com:apple/swift-numerics.git", + "state": { + "branch": null, + "revision": "110beefe0b9a4af05b09f2d33faf52740d9229a7", + "version": "0.0.6" + } + } + ] + }, + "version": 1 +} diff --git a/Package.swift b/Package.swift index 6bd1128..a0ddd42 100644 --- a/Package.swift +++ b/Package.swift @@ -17,6 +17,7 @@ let package = Package( targets: ["SAKBase"]), ], dependencies: [ + .package(url: "git@github.com:apple/swift-numerics.git", from: "0.0.6") // Dependencies declare other packages that this package depends on. // .package(url: /* package url */, from: "1.0.0"), ], @@ -25,7 +26,7 @@ let package = Package( // Targets can depend on other targets in this package, and on products in packages which this package depends on. .target( name: "SAKBase", - dependencies: []), + dependencies: [.product(name: "RealModule", package: "swift-numerics")]), .testTarget( name: "SAKBaseTests", dependencies: ["SAKBase"]), diff --git a/Tests/SAKBaseTests/Math/RealComparisonTests.swift b/Tests/SAKBaseTests/Math/RealComparisonTests.swift index 1e46237..fdd4a2e 100644 --- a/Tests/SAKBaseTests/Math/RealComparisonTests.swift +++ b/Tests/SAKBaseTests/Math/RealComparisonTests.swift @@ -6,7 +6,7 @@ // import XCTest -@testable import SAKBase +import SAKBase class RealComparisonTests: XCTestCase { @@ -49,9 +49,9 @@ class RealComparisonTests: XCTestCase { XCTAssertFalse(Double.nan.within(ulps: 2, of: 1.0.nextUp.nextUp)) XCTAssertFalse(Double.infinity.within(ulps: 2, of: 1.0.nextDown)) - XCTAssertTrue(Double.zero.within(ulps: 0, of: Double.zero)) + XCTAssertTrue(Double.zero.within(ulps: 0, of: .zero)) - XCTAssertTrue(Double.zero.within(ulps: 1, of: -Double.zero)) + XCTAssertTrue(Double.zero.within(ulps: 1, of: -.zero)) // Differently signed values are always false no matter what XCTAssertFalse((-Double.zero.nextUp).within(ulps: UInt(Int.max) - 1, of: Double.zero.nextUp)) From 4d56c324f739a6831df141f7b233b6eed1cf68d7 Mon Sep 17 00:00:00 2001 From: Stephen Kac Date: Wed, 15 Jul 2020 16:15:37 -0600 Subject: [PATCH 05/26] Added fix for iOS only extension --- Sources/SAKBase/Math/RealComparison.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Sources/SAKBase/Math/RealComparison.swift b/Sources/SAKBase/Math/RealComparison.swift index ecd95af..f360fa6 100644 --- a/Sources/SAKBase/Math/RealComparison.swift +++ b/Sources/SAKBase/Math/RealComparison.swift @@ -101,7 +101,9 @@ extension Float64: EquivalentIntegerSize { public typealias IntegerSize = Int64 } +#if os(iOS) @available(iOS 14.0, *) extension Float16: EquivalentIntegerSize { public typealias IntegerSize = Int16 } +#endif From 662a967726b67aca4584b3a4b3a12ec5edec8d54 Mon Sep 17 00:00:00 2001 From: Stephen Kac Date: Wed, 15 Jul 2020 18:25:54 -0600 Subject: [PATCH 06/26] Event and Disposable have been updated to modern styles. --- SAKBase.xcodeproj/project.pbxproj | 18 ++- Sources/SAKBase/BaseTypes/Disposable.swift | 38 +++++ Sources/SAKBase/BaseTypes/Event.swift | 147 +++++++++--------- .../BaseTypes/DisposableTests.swift | 60 +++++++ Tests/SAKBaseTests/BaseTypes/EventTests.swift | 67 ++++++-- .../BaseTypes/UniversalIDTests.swift | 33 ---- 6 files changed, 232 insertions(+), 131 deletions(-) create mode 100644 Sources/SAKBase/BaseTypes/Disposable.swift create mode 100644 Tests/SAKBaseTests/BaseTypes/DisposableTests.swift delete mode 100644 Tests/SAKBaseTests/BaseTypes/UniversalIDTests.swift diff --git a/SAKBase.xcodeproj/project.pbxproj b/SAKBase.xcodeproj/project.pbxproj index a14d11e..03e6913 100644 --- a/SAKBase.xcodeproj/project.pbxproj +++ b/SAKBase.xcodeproj/project.pbxproj @@ -7,12 +7,13 @@ objects = { /* Begin PBXBuildFile section */ + CC1F5E4024BFBDD60062B978 /* Disposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC1F5E3F24BFBDD60062B978 /* Disposable.swift */; }; + CC1F5E4324BFD2640062B978 /* DisposableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC1F5E4124BFD25D0062B978 /* DisposableTests.swift */; }; CC90BC1224AA7CD2002CB53F /* SAKBase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CC90BC0924AA7CD1002CB53F /* SAKBase.framework */; }; CC90BC2024AA7CE2002CB53F /* RealComparisonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0D223D6B3860027CD2B /* RealComparisonTests.swift */; }; CC90BC2124AA7CE2002CB53F /* EventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0D423D6B3860027CD2B /* EventTests.swift */; }; CC90BC2224AA7CE2002CB53F /* Concurrency.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0EE23D6CB3F0027CD2B /* Concurrency.swift */; }; CC90BC2524AA7CE7002CB53F /* Point.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0B023D6B3550027CD2B /* Point.swift */; }; - CC90BC2624AA7CE7002CB53F /* UniversalID.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0AC23D6B3550027CD2B /* UniversalID.swift */; }; CC90BC2724AA7CE7002CB53F /* Stack.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0AB23D6B3550027CD2B /* Stack.swift */; }; CC90BC2824AA7CE7002CB53F /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0AF23D6B3550027CD2B /* Command.swift */; }; CC90BC2924AA7CE7002CB53F /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0AD23D6B3550027CD2B /* Event.swift */; }; @@ -26,7 +27,6 @@ CC90BC3124AA7CF7002CB53F /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0BC23D6B3550027CD2B /* String.swift */; }; CC90BC3224AA7CF7002CB53F /* Array.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0BB23D6B3550027CD2B /* Array.swift */; }; CC90BC3324AA7CF7002CB53F /* Optional.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0BD23D6B3550027CD2B /* Optional.swift */; }; - CCA0A4F524BE2BE1007258BA /* UniversalIDTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCA0A4F424BE2BE1007258BA /* UniversalIDTests.swift */; }; CCA0A4F724BE2C0F007258BA /* StackTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCA0A4F624BE2C0F007258BA /* StackTests.swift */; }; CCA0A4F924BE2C40007258BA /* BindingCommandTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCA0A4F824BE2C40007258BA /* BindingCommandTests.swift */; }; CCA0A4FB24BE2C6B007258BA /* CommandTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCA0A4FA24BE2C6B007258BA /* CommandTests.swift */; }; @@ -46,10 +46,11 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + CC1F5E3F24BFBDD60062B978 /* Disposable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Disposable.swift; sourceTree = ""; }; + CC1F5E4124BFD25D0062B978 /* DisposableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisposableTests.swift; sourceTree = ""; }; CC90BC0924AA7CD1002CB53F /* SAKBase.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SAKBase.framework; sourceTree = BUILT_PRODUCTS_DIR; }; CC90BC1124AA7CD2002CB53F /* SAKBaseTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SAKBaseTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; CC90BC3A24AA7D6B002CB53F /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - CCA0A4F424BE2BE1007258BA /* UniversalIDTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UniversalIDTests.swift; sourceTree = ""; }; CCA0A4F624BE2C0F007258BA /* StackTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StackTests.swift; sourceTree = ""; }; CCA0A4F824BE2C40007258BA /* BindingCommandTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BindingCommandTests.swift; sourceTree = ""; }; CCA0A4FA24BE2C6B007258BA /* CommandTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommandTests.swift; sourceTree = ""; }; @@ -108,8 +109,8 @@ isa = PBXGroup; children = ( CCC1C0D423D6B3860027CD2B /* EventTests.swift */, + CC1F5E4124BFD25D0062B978 /* DisposableTests.swift */, CCA0A4FA24BE2C6B007258BA /* CommandTests.swift */, - CCA0A4F424BE2BE1007258BA /* UniversalIDTests.swift */, CCA0A4F624BE2C0F007258BA /* StackTests.swift */, CCA0A4F824BE2C40007258BA /* BindingCommandTests.swift */, CCA0A4FC24BE438F007258BA /* PointTests.swift */, @@ -162,6 +163,7 @@ CCC1C0AE23D6B3550027CD2B /* Point3.swift */, CCC1C0AF23D6B3550027CD2B /* Command.swift */, CCC1C0B023D6B3550027CD2B /* Point.swift */, + CC1F5E3F24BFBDD60062B978 /* Disposable.swift */, ); path = BaseTypes; sourceTree = ""; @@ -349,8 +351,8 @@ buildActionMask = 2147483647; files = ( CC90BC2724AA7CE7002CB53F /* Stack.swift in Sources */, + CC1F5E4024BFBDD60062B978 /* Disposable.swift in Sources */, CC90BC2B24AA7CEC002CB53F /* ProgressReporting.swift in Sources */, - CC90BC2624AA7CE7002CB53F /* UniversalID.swift in Sources */, CC90BC2524AA7CE7002CB53F /* Point.swift in Sources */, CCA0A50324BE6330007258BA /* Point3.swift in Sources */, CC90BC3124AA7CF7002CB53F /* String.swift in Sources */, @@ -372,9 +374,9 @@ buildActionMask = 2147483647; files = ( CCA0A4FD24BE438F007258BA /* PointTests.swift in Sources */, + CC1F5E4324BFD2640062B978 /* DisposableTests.swift in Sources */, CC90BC2124AA7CE2002CB53F /* EventTests.swift in Sources */, CC90BC2224AA7CE2002CB53F /* Concurrency.swift in Sources */, - CCA0A4F524BE2BE1007258BA /* UniversalIDTests.swift in Sources */, CC90BC2024AA7CE2002CB53F /* RealComparisonTests.swift in Sources */, CCA0A4F724BE2C0F007258BA /* StackTests.swift in Sources */, CCA0A4F924BE2C40007258BA /* BindingCommandTests.swift in Sources */, @@ -412,6 +414,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); + MACOSX_DEPLOYMENT_TARGET = 10.15; MARKETING_VERSION = 0.0.1; PRODUCT_BUNDLE_IDENTIFIER = "com.Stephen-Kac.SAKBase"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; @@ -441,6 +444,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); + MACOSX_DEPLOYMENT_TARGET = 10.15; MARKETING_VERSION = 0.0.1; PRODUCT_BUNDLE_IDENTIFIER = "com.Stephen-Kac.SAKBase"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; @@ -465,6 +469,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); + MACOSX_DEPLOYMENT_TARGET = 10.15; PRODUCT_BUNDLE_IDENTIFIER = "com.Stephen-Kac.SAKBaseTests"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; @@ -487,6 +492,7 @@ "@executable_path/Frameworks", "@loader_path/Frameworks", ); + MACOSX_DEPLOYMENT_TARGET = 10.15; PRODUCT_BUNDLE_IDENTIFIER = "com.Stephen-Kac.SAKBaseTests"; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = iphoneos; diff --git a/Sources/SAKBase/BaseTypes/Disposable.swift b/Sources/SAKBase/BaseTypes/Disposable.swift new file mode 100644 index 0000000..f4103b6 --- /dev/null +++ b/Sources/SAKBase/BaseTypes/Disposable.swift @@ -0,0 +1,38 @@ +// +// Disposable.swift +// SAKBase +// +// Created by Stephen Kac on 7/15/20. +// Copyright © 2020 Stephen Kac. All rights reserved. +// + +/// Automatically disposes of a event upon deinit +public class DisposeContainer: Disposable { + private weak var toBeDisposed: Disposable? + + public init(toBeDisposed: Disposable) { + self.toBeDisposed = toBeDisposed + } + + /// Removes the responsibility of disposing the related class from the container + public func removeDisposableFromContainer() -> Disposable? { + defer { + toBeDisposed = nil + } + return toBeDisposed + } + + /// Manually disposes of the contained disposable + public func dispose() { + toBeDisposed?.dispose() + } + + deinit { + dispose() + } +} + +/// Lets you dispose of a connected type. +public protocol Disposable: AnyObject { + func dispose() +} diff --git a/Sources/SAKBase/BaseTypes/Event.swift b/Sources/SAKBase/BaseTypes/Event.swift index 96de88b..96b2bf9 100644 --- a/Sources/SAKBase/BaseTypes/Event.swift +++ b/Sources/SAKBase/BaseTypes/Event.swift @@ -6,87 +6,84 @@ // Copyright © 2018 Stephen Kac. All rights reserved. // -/// Provides an interface to raise events in a more performant and pure Swift manner. When raising an event all handlers will be called witht data from the event and if a handlers target no longer exists in memory then the handler will be removed automatically. If a handle returns false then it will also be removed. +import Foundation + +/// Provides an interface to raise events in a more performant and pure Swift manner. +/// When raising an event all handlers will be called witht data from the event. +/// If the returned DisposeContainer leaves its scope, it will remove its related event. +/// If a handle throws an error then it will also be removed silently. public class Event { - - fileprivate typealias EventHandler = EventHandlerWrapper - - fileprivate var eventHandlers = [EventHandler]() - - public init() {} - - public func raise(_ data: DataType) { - for handler in self.eventHandlers { - handler.invoke(data) - } - } - - /** Adds a handler that will be called when the event is raised. - - Parameter target: An Object passed to the handler to act on or with, usually the interested class. - - Parameter handler: A block of code to run with the target and data raised. - - returns: - A handler that allows you to manually stop listenning to the event. You do not need to dispose upon deint of the Target. - */ + + fileprivate typealias EventHandler = EventHandlerWrapper + public typealias Handler = (DataType) throws -> Void + + fileprivate var eventHandlers = Set() + + public var handlerCount: Int { + get { + eventHandlers.count + } + } + + public init() {} + + public func raise(_ data: DataType) { + for handler in self.eventHandlers { + handler.invoke(data) + } + } + + /** Adds a handler that will be called when the event is raised. + - Parameter target: An Object passed to the handler to act on or with, usually the interested class. + - Parameter handler: A block of code to run with the target and data raised. + - returns: + A handler that allows you to manually stop listenning to the event. You do not need to dispose upon deint of the Target. + */ @discardableResult - public func addHandler(_ target: AnyObject, - handler: @escaping (AnyObject, DataType) -> (Bool)) -> Disposable { - let wrapper = EventHandlerWrapper(target: target, - handler: handler, event: self) - eventHandlers.append(wrapper) - return wrapper - } - - /** Adds a handler that will be called when the event is raised. - - parameter target: An Object passed to the handler to act on or with, usually the interested class. - - parameter handler: A function pointer that takes in a the events datatype and a handler to run it on. This will be executed when the event is raised - - returns: - A handler that allows you to manually stop listenning to the event. You do not need to dispose upon deint of the Target. - **/ - public func addHandler (_ target: Target, handler: @escaping (Target) -> ((DataType) -> ())) -> Disposable { - let newHandler: (AnyObject, DataType) -> (Bool) = { (target, data) in - guard let target = target as? Target else { return false } - handler(target)(data) - return true - } - - return addHandler(target, handler: newHandler) - } - - deinit { - for handler in eventHandlers { - handler.dispose() - } - } + public func addHandler( + handler: @escaping Handler) -> DisposeContainer { + let wrapper = EventHandlerWrapper(handler: handler, event: self) + eventHandlers.insert(wrapper) + return DisposeContainer(toBeDisposed: wrapper) + } + + deinit { + for handler in eventHandlers { + handler.dispose() + } + } } private class EventHandlerWrapper -: Disposable { - - typealias Target = AnyObject - - weak var target: Target? - let handler: (Target, DataType) -> (Bool) - let event: Event - - init(target: Target?, handler: @escaping (Target, DataType) -> (Bool), event: Event) { - self.target = target - self.handler = handler - self.event = event; - } - - func invoke(_ data: DataType) -> () { - guard let t = target else { dispose(); return } - guard handler(t, data) else { dispose(); return } - } - - public func dispose() { - event.eventHandlers = - event.eventHandlers.filter { $0 !== self } - } +: Disposable, Identifiable { + + typealias Handler = (DataType) throws -> Void + + let id = UUID() + + let handler: Handler + let event: Event + + init(handler: @escaping Handler, event: Event) { + self.handler = handler + self.event = event; + } + + func invoke(_ data: DataType) -> () { + guard (try? handler(data)) != nil else { dispose(); return } + } + + public func dispose() { + event.eventHandlers.remove(self) + } } +extension EventHandlerWrapper: Hashable { + static func == (lhs: EventHandlerWrapper, rhs: EventHandlerWrapper) -> Bool { + lhs.id == rhs.id + } -/// Lets you dispose of a connected type. -public protocol Disposable { - func dispose() + func hash(into hasher: inout Hasher) { + hasher.combine(id) + } } diff --git a/Tests/SAKBaseTests/BaseTypes/DisposableTests.swift b/Tests/SAKBaseTests/BaseTypes/DisposableTests.swift new file mode 100644 index 0000000..a9df02b --- /dev/null +++ b/Tests/SAKBaseTests/BaseTypes/DisposableTests.swift @@ -0,0 +1,60 @@ +// +// DisposableTests.swift +// SAKBase +// +// Created by Stephen Kac on 7/15/20. +// Copyright © 2020 Stephen Kac. All rights reserved. +// + +import Foundation + +import XCTest +import SAKBase + +class DisposableTests: XCTestCase { + + func test_disposableContainer_doesDispose_whenDestroyed() { + weak var spy: DisposableSpy? + + do { + let spyStrong = DisposableSpy() + spy = spyStrong + + _ = DisposeContainer(toBeDisposed: spyStrong) + + XCTAssertEqual(spy?.disposeCalls, 1) + } + + XCTAssertNil(spy) + } + + func test_disposableContainer_doesNotDispose_whenDestroyed() { + var spy: DisposableSpy? + let disposable: Disposable? + + do { + let spyStrong = DisposableSpy() + spy = spyStrong + + disposable = DisposeContainer(toBeDisposed: spyStrong) + .removeDisposableFromContainer() + + XCTAssertEqual(spy?.disposeCalls, 0) + } + + disposable?.dispose() + XCTAssertEqual(spy?.disposeCalls, 1) + } +} + +// MARK: - Helpers +private extension DisposableTests { + class DisposableSpy: Disposable { + + var disposeCalls = 0 + + func dispose() { + disposeCalls += 1 + } + } +} diff --git a/Tests/SAKBaseTests/BaseTypes/EventTests.swift b/Tests/SAKBaseTests/BaseTypes/EventTests.swift index 694c4ae..da588d1 100644 --- a/Tests/SAKBaseTests/BaseTypes/EventTests.swift +++ b/Tests/SAKBaseTests/BaseTypes/EventTests.swift @@ -11,22 +11,55 @@ import SAKBase class EventTests: XCTestCase { - var eventHandler: Disposable? = nil - - func test() { - - let event = Event() - - let string: NSString? = NSString("Hello") - - eventHandler = event.addHandler(string!) { - print($0, $1) - return true - } - - event.raise(5) - - event.raise(3) - } + weak var eventHandler: Disposable? = nil + + func test_event_doesGetDisposed() { + + let event = Event() + + do { + let eventHandler = event.addHandler() { _ in } + XCTAssertEqual(event.handlerCount, 1) + self.eventHandler = eventHandler + } + + XCTAssertNil(eventHandler) + XCTAssertEqual(event.handlerCount, 0) + } + + func test_event_doesRaiseAllHandlers() { + + let event = Event() + + var disposables = Array() + + var calls = 0 + + for _ in 0..<3 { + disposables.append(event.addHandler { _ in + calls += 1 + }) + } + + event.raise(1) + + XCTAssertEqual(calls, 3) + } + + func test_event_sendsRaisedValue() { + let event = Event() + + var givenValue: Int? + + let disposable = event.addHandler { + givenValue = $0 + } + + event.raise(13) + + XCTAssertEqual(givenValue, 13) + + disposable.dispose() + } } diff --git a/Tests/SAKBaseTests/BaseTypes/UniversalIDTests.swift b/Tests/SAKBaseTests/BaseTypes/UniversalIDTests.swift deleted file mode 100644 index 9484b55..0000000 --- a/Tests/SAKBaseTests/BaseTypes/UniversalIDTests.swift +++ /dev/null @@ -1,33 +0,0 @@ -// -// UniversalIDTests.swift -// SAKBaseTests -// -// Created by Stephen Kac on 7/14/20. -// Copyright © 2020 Stephen Kac. All rights reserved. -// - -import XCTest -import SAKBase - -class UniversalIDTests: XCTestCase { - - func testUniversalID() { - let uniqueIDs = UniversalID() - var uniqueSet = Set() - - // Generate 100000 ids - for _ in 0..<100000 { - uniqueSet.insert(uniqueIDs.nextID()) - } - - // If the count is 10000 and both sets are equal than 10000 unique ids were created - XCTAssert(uniqueIDs.ids == uniqueSet) - XCTAssert(uniqueIDs.ids.count == 100000) - - // Make sure id is removed properly - let id = uniqueSet.popFirst()! - uniqueIDs.remove(id: id) - XCTAssert(!uniqueIDs.ids.contains(id)) - } - -} From 43413d93d780c9c54385c94734d2330dae6e9656 Mon Sep 17 00:00:00 2001 From: Stephen Kac Date: Wed, 15 Jul 2020 18:29:06 -0600 Subject: [PATCH 07/26] Removed Universal ID --- SAKBase.xcodeproj/project.pbxproj | 2 -- Sources/SAKBase/BaseTypes/UniversalID.swift | 40 --------------------- 2 files changed, 42 deletions(-) delete mode 100644 Sources/SAKBase/BaseTypes/UniversalID.swift diff --git a/SAKBase.xcodeproj/project.pbxproj b/SAKBase.xcodeproj/project.pbxproj index 03e6913..5e17866 100644 --- a/SAKBase.xcodeproj/project.pbxproj +++ b/SAKBase.xcodeproj/project.pbxproj @@ -57,7 +57,6 @@ CCA0A4FC24BE438F007258BA /* PointTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointTests.swift; sourceTree = ""; }; CCC1C0AA23D6B3550027CD2B /* BindingCommand.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BindingCommand.swift; sourceTree = ""; }; CCC1C0AB23D6B3550027CD2B /* Stack.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Stack.swift; sourceTree = ""; }; - CCC1C0AC23D6B3550027CD2B /* UniversalID.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UniversalID.swift; sourceTree = ""; }; CCC1C0AD23D6B3550027CD2B /* Event.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Event.swift; sourceTree = ""; }; CCC1C0AE23D6B3550027CD2B /* Point3.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Point3.swift; sourceTree = ""; }; CCC1C0AF23D6B3550027CD2B /* Command.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Command.swift; sourceTree = ""; }; @@ -158,7 +157,6 @@ children = ( CCC1C0AA23D6B3550027CD2B /* BindingCommand.swift */, CCC1C0AB23D6B3550027CD2B /* Stack.swift */, - CCC1C0AC23D6B3550027CD2B /* UniversalID.swift */, CCC1C0AD23D6B3550027CD2B /* Event.swift */, CCC1C0AE23D6B3550027CD2B /* Point3.swift */, CCC1C0AF23D6B3550027CD2B /* Command.swift */, diff --git a/Sources/SAKBase/BaseTypes/UniversalID.swift b/Sources/SAKBase/BaseTypes/UniversalID.swift deleted file mode 100644 index 1d5d5d7..0000000 --- a/Sources/SAKBase/BaseTypes/UniversalID.swift +++ /dev/null @@ -1,40 +0,0 @@ -// -// UniversalEntityInfo.swift -// Dark Ember -// -// Created by Stephen Kac on 12/29/16. -// Copyright © 2016 StephenKac. All rights reserved. -// - -import Foundation - -public typealias Identifier = String - -public protocol Identifiable { - var universalID: String {get} -} - -public fileprivate(set) var universalObjectID = UniversalID() - -final public class UniversalID: Codable { - public private(set) var ids: Set - - public func nextID() -> Identifier { - while(true) { - let id = UUID().uuidString - if !ids.contains(id) { - ids.insert(id) - return id - } - } - } - - public func remove(id: Identifier) { - ids.remove(id) - } - - public init(ids: Set = Set()) { - self.ids = ids - } - -} From 16290517d914cfc59ed056c6a190f6c739567e13 Mon Sep 17 00:00:00 2001 From: Stephen Kac Date: Wed, 15 Jul 2020 20:41:11 -0600 Subject: [PATCH 08/26] Added test for stack iterator --- Tests/SAKBaseTests/BaseTypes/StackTests.swift | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/Tests/SAKBaseTests/BaseTypes/StackTests.swift b/Tests/SAKBaseTests/BaseTypes/StackTests.swift index 5069929..d36da5b 100644 --- a/Tests/SAKBaseTests/BaseTypes/StackTests.swift +++ b/Tests/SAKBaseTests/BaseTypes/StackTests.swift @@ -29,4 +29,20 @@ class StackTests: XCTestCase { XCTAssert(stack.top == 1) } + func test_stack_iteratesOverAll() { + var stack = Stack() + + let values = (0...5).map { $0 } + + for index in 0...5 { + stack.push(values[index]) + } + + var stackCount = stack.count + for element in stack { + XCTAssertEqual(values[stackCount - 1], element) + stackCount -= 1 + } + } + } From 3a7843b492dd2fbb6871dd2cd1b74e8f6e1cd6e3 Mon Sep 17 00:00:00 2001 From: Stephen Kac Date: Wed, 15 Jul 2020 20:53:45 -0600 Subject: [PATCH 09/26] Removed the Sequence conformance of Stack and replaced it with forEachPop as it makes more sense for a stack. --- Sources/SAKBase/BaseTypes/Stack.swift | 23 ++++++++----------- Tests/SAKBaseTests/BaseTypes/StackTests.swift | 4 +++- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/Sources/SAKBase/BaseTypes/Stack.swift b/Sources/SAKBase/BaseTypes/Stack.swift index 1568b96..fee77bd 100755 --- a/Sources/SAKBase/BaseTypes/Stack.swift +++ b/Sources/SAKBase/BaseTypes/Stack.swift @@ -3,8 +3,8 @@ Push and pop are O(1) operations. */ -public struct Stack { - fileprivate var array = [T]() +public struct Stack { + fileprivate var array = [Element]() public init(){} @@ -16,24 +16,21 @@ public struct Stack { return array.count } - public mutating func push(_ element: T) { + public mutating func push(_ element: Element) { array.append(element) } - public mutating func pop() -> T? { + public mutating func pop() -> Element? { return array.popLast() } - public var top: T? { + public var top: Element? { return array.last } -} -extension Stack: Sequence { - public func makeIterator() -> AnyIterator { - var curr = self - return AnyIterator { - return curr.pop() - } - } + public mutating func forEachPop(_ body: (Element) throws -> Void) rethrows { + while let element = pop() { + try body(element) + } + } } diff --git a/Tests/SAKBaseTests/BaseTypes/StackTests.swift b/Tests/SAKBaseTests/BaseTypes/StackTests.swift index d36da5b..93022b9 100644 --- a/Tests/SAKBaseTests/BaseTypes/StackTests.swift +++ b/Tests/SAKBaseTests/BaseTypes/StackTests.swift @@ -39,10 +39,12 @@ class StackTests: XCTestCase { } var stackCount = stack.count - for element in stack { + stack.forEachPop { element in XCTAssertEqual(values[stackCount - 1], element) stackCount -= 1 } + + XCTAssertEqual(stack.count, 0) } } From 3b5938ab8a2beff9efc24ddbe3fb70a024209244 Mon Sep 17 00:00:00 2001 From: Stephen Kac Date: Wed, 15 Jul 2020 21:22:03 -0600 Subject: [PATCH 10/26] Cleaned up formatting --- .../SAKBase/BaseTypes/BindingCommand.swift | 80 +++++----- Sources/SAKBase/BaseTypes/Command.swift | 150 +++++++++--------- Sources/SAKBase/BaseTypes/Disposable.swift | 8 +- Sources/SAKBase/BaseTypes/Event.swift | 28 ++-- Sources/SAKBase/BaseTypes/Point.swift | 8 +- Sources/SAKBase/BaseTypes/Point3.swift | 6 +- Sources/SAKBase/BaseTypes/Stack.swift | 50 +++--- .../SAKBase/Concurrency/AsyncOperation.swift | 94 +++++------ .../SAKBase/Concurrency/GroupOperation.swift | 12 +- Sources/SAKBase/Concurrency/Lock.swift | 116 +++++++------- Sources/SAKBase/Concurrency/PThread.swift | 100 ++++++------ Sources/SAKBase/Math/RealComparison.swift | 34 ++-- .../SAKBase/NSSwift/ProgressReporting.swift | 2 +- Sources/SAKBase/Swift/Array.swift | 20 +-- Sources/SAKBase/Swift/Optional.swift | 6 +- Sources/SAKBase/Swift/String.swift | 24 +-- 16 files changed, 369 insertions(+), 369 deletions(-) diff --git a/Sources/SAKBase/BaseTypes/BindingCommand.swift b/Sources/SAKBase/BaseTypes/BindingCommand.swift index f76e454..1e063d9 100644 --- a/Sources/SAKBase/BaseTypes/BindingCommand.swift +++ b/Sources/SAKBase/BaseTypes/BindingCommand.swift @@ -9,49 +9,49 @@ import Foundation /** - Abstract: - Used for a command responder chain in order to allow responsibility to be passed but retain a strong reference to all captured types. - */ +Abstract: +Used for a command responder chain in order to allow responsibility to be passed but retain a strong reference to all captured types. +*/ public protocol BindingCommandResponder { - func recieve(_ command: BindingCommand, sender: Any?) + func recieve(_ command: BindingCommand, sender: Any?) } /** - Abstract: - Is used to give buttons and inputs actions that they will incur. Unlike the Command, this class is static. - */ +Abstract: +Is used to give buttons and inputs actions that they will incur. Unlike the Command, this class is static. +*/ final public class BindingCommand { - - /// The name of the command - public var name: String? - - /// The command to be executed - private let execCommand: () -> Bool - - /// The command that will undo the execCommand - private let undoCommand: (() -> Bool)? - - /// Initializes the Command with an executable closure and a closure to undo it. - public init(exec: @escaping () -> Bool, undo: (() -> Bool)? = nil ) { - execCommand = exec - undoCommand = undo - } - - /** - Executes the command if actor is non-nil. - - Returns: a discardable `Bool` describing if the action was successful - */ - @discardableResult - public func execute() -> Bool { - return execCommand() - } - - /** - Does the undo variant of the command if undo is set with the last value set. - - Returns: a discardable `Bool` describing if the action was successful - */ - @discardableResult - public func undo() -> Bool { - return undoCommand?() ?? false - } + + /// The name of the command + public var name: String? + + /// The command to be executed + private let execCommand: () -> Bool + + /// The command that will undo the execCommand + private let undoCommand: (() -> Bool)? + + /// Initializes the Command with an executable closure and a closure to undo it. + public init(exec: @escaping () -> Bool, undo: (() -> Bool)? = nil ) { + execCommand = exec + undoCommand = undo + } + + /** + Executes the command if actor is non-nil. + - Returns: a discardable `Bool` describing if the action was successful + */ + @discardableResult + public func execute() -> Bool { + return execCommand() + } + + /** + Does the undo variant of the command if undo is set with the last value set. + - Returns: a discardable `Bool` describing if the action was successful + */ + @discardableResult + public func undo() -> Bool { + return undoCommand?() ?? false + } } diff --git a/Sources/SAKBase/BaseTypes/Command.swift b/Sources/SAKBase/BaseTypes/Command.swift index 64e200c..4544b17 100644 --- a/Sources/SAKBase/BaseTypes/Command.swift +++ b/Sources/SAKBase/BaseTypes/Command.swift @@ -10,80 +10,80 @@ import Foundation /** - Abstract: - Is used to give buttons and inputs actions that they will incur. THis is helpful if you dont want to create a new command for every incurrence of the command with a different value - */ +Abstract: +Is used to give buttons and inputs actions that they will incur. This is helpful if you dont want to create a new command for every incurrence of the command with a different value +*/ final public class Command { - - // TODO: Add listenning so that classes can subscribe and listen to this command. - //private let listeners: [ - - /// The command to be executed - private let execCommand: (ActorType, ValueType?) -> Bool - - /// The command that will undo the execCommand - public let undoCommand: ((ActorType, ValueType?) -> Bool)? - - /// This is the value that will be used in during the exec or undo commands - public var value: ValueType? - - /// This is the current actor the comamnds will act upon - public weak var actor: ActorType? - - /// Initializes the Command with an executable closure and the required default values to run the command - public convenience init(actor: ActorType?, value: ValueType?, exec: @escaping (ActorType, ValueType?) -> Bool) { - self.init(actor: actor, value: value, exec: exec, undo: nil) - } - - /// Initializes the Command with an executable closure and an undo closure - public required init(actor: ActorType?, value: ValueType?, exec: @escaping (ActorType, ValueType?) -> Bool, undo: ((ActorType, ValueType?) -> Bool)?) { - self.execCommand = exec - self.undoCommand = undo - self.value = value - self.actor = actor - } - - /** - Executes the command with the new value if actor is non-nil. - - Returns: a discardable `Bool` describing if the action was successful - */ - @discardableResult - public func execute(with newValue: ValueType) -> Bool { - value = newValue - guard let actor = actor else { return false } - return execCommand(actor, value) - } - - /** - Executes the command if actor is non-nil. - - Returns: a discardable `Bool` describing if the action was successful - */ - @discardableResult - public func execute() -> Bool { - guard let actor = actor else { return false } - return execCommand(actor, value) - } - - /** - Does the undo variant of the command if undo is set with the last value set. - - Returns: a discardable `Bool` describing if the action was successful - */ - @discardableResult - public func undo() -> Bool { - guard let actor = actor else { return false } - return undoCommand?(actor, value) ?? false - } - - /** - Gets a closure that can undo the last exec comamnd. This can be helpful in queing up a list of commands that can undo previous actions. - - Returns: a closure that returns bool for wether it was successful or not - */ - @discardableResult - public func getUndo() -> (() -> Bool)? { - guard let undoCommand = undoCommand else { return nil } - return { [weak localActor = self.actor, value = self.value, undoCommand] in - guard let actor = localActor else { return false } - return undoCommand(actor, value) - } - } + + // TODO: Add listenning so that classes can subscribe and listen to this command. + //private let listeners: [ + + /// The command to be executed + private let execCommand: (ActorType, ValueType?) -> Bool + + /// The command that will undo the execCommand + public let undoCommand: ((ActorType, ValueType?) -> Bool)? + + /// This is the value that will be used in during the exec or undo commands + public var value: ValueType? + + /// This is the current actor the comamnds will act upon + public weak var actor: ActorType? + + /// Initializes the Command with an executable closure and the required default values to run the command + public convenience init(actor: ActorType?, value: ValueType?, exec: @escaping (ActorType, ValueType?) -> Bool) { + self.init(actor: actor, value: value, exec: exec, undo: nil) + } + + /// Initializes the Command with an executable closure and an undo closure + public required init(actor: ActorType?, value: ValueType?, exec: @escaping (ActorType, ValueType?) -> Bool, undo: ((ActorType, ValueType?) -> Bool)?) { + self.execCommand = exec + self.undoCommand = undo + self.value = value + self.actor = actor + } + + /** + Executes the command with the new value if actor is non-nil. + - Returns: a discardable `Bool` describing if the action was successful + */ + @discardableResult + public func execute(with newValue: ValueType) -> Bool { + value = newValue + guard let actor = actor else { return false } + return execCommand(actor, value) + } + + /** + Executes the command if actor is non-nil. + - Returns: a discardable `Bool` describing if the action was successful + */ + @discardableResult + public func execute() -> Bool { + guard let actor = actor else { return false } + return execCommand(actor, value) + } + + /** + Does the undo variant of the command if undo is set with the last value set. + - Returns: a discardable `Bool` describing if the action was successful + */ + @discardableResult + public func undo() -> Bool { + guard let actor = actor else { return false } + return undoCommand?(actor, value) ?? false + } + + /** + Gets a closure that can undo the last exec comamnd. This can be helpful in queing up a list of commands that can undo previous actions. + - Returns: a closure that returns bool for wether it was successful or not + */ + @discardableResult + public func getUndo() -> (() -> Bool)? { + guard let undoCommand = undoCommand else { return nil } + return { [weak localActor = self.actor, value = self.value, undoCommand] in + guard let actor = localActor else { return false } + return undoCommand(actor, value) + } + } } diff --git a/Sources/SAKBase/BaseTypes/Disposable.swift b/Sources/SAKBase/BaseTypes/Disposable.swift index f4103b6..c7d7244 100644 --- a/Sources/SAKBase/BaseTypes/Disposable.swift +++ b/Sources/SAKBase/BaseTypes/Disposable.swift @@ -9,11 +9,11 @@ /// Automatically disposes of a event upon deinit public class DisposeContainer: Disposable { private weak var toBeDisposed: Disposable? - + public init(toBeDisposed: Disposable) { self.toBeDisposed = toBeDisposed } - + /// Removes the responsibility of disposing the related class from the container public func removeDisposableFromContainer() -> Disposable? { defer { @@ -21,12 +21,12 @@ public class DisposeContainer: Disposable { } return toBeDisposed } - + /// Manually disposes of the contained disposable public func dispose() { toBeDisposed?.dispose() } - + deinit { dispose() } diff --git a/Sources/SAKBase/BaseTypes/Event.swift b/Sources/SAKBase/BaseTypes/Event.swift index 96b2bf9..aac393a 100644 --- a/Sources/SAKBase/BaseTypes/Event.swift +++ b/Sources/SAKBase/BaseTypes/Event.swift @@ -13,26 +13,26 @@ import Foundation /// If the returned DisposeContainer leaves its scope, it will remove its related event. /// If a handle throws an error then it will also be removed silently. public class Event { - + fileprivate typealias EventHandler = EventHandlerWrapper public typealias Handler = (DataType) throws -> Void - + fileprivate var eventHandlers = Set() - + public var handlerCount: Int { get { eventHandlers.count } } - + public init() {} - + public func raise(_ data: DataType) { for handler in self.eventHandlers { handler.invoke(data) } } - + /** Adds a handler that will be called when the event is raised. - Parameter target: An Object passed to the handler to act on or with, usually the interested class. - Parameter handler: A block of code to run with the target and data raised. @@ -46,7 +46,7 @@ public class Event { eventHandlers.insert(wrapper) return DisposeContainer(toBeDisposed: wrapper) } - + deinit { for handler in eventHandlers { handler.dispose() @@ -56,23 +56,23 @@ public class Event { private class EventHandlerWrapper : Disposable, Identifiable { - + typealias Handler = (DataType) throws -> Void - + let id = UUID() - + let handler: Handler let event: Event - + init(handler: @escaping Handler, event: Event) { self.handler = handler self.event = event; } - + func invoke(_ data: DataType) -> () { guard (try? handler(data)) != nil else { dispose(); return } } - + public func dispose() { event.eventHandlers.remove(self) } @@ -82,7 +82,7 @@ extension EventHandlerWrapper: Hashable { static func == (lhs: EventHandlerWrapper, rhs: EventHandlerWrapper) -> Bool { lhs.id == rhs.id } - + func hash(into hasher: inout Hasher) { hasher.combine(id) } diff --git a/Sources/SAKBase/BaseTypes/Point.swift b/Sources/SAKBase/BaseTypes/Point.swift index 33fd5a4..772d930 100644 --- a/Sources/SAKBase/BaseTypes/Point.swift +++ b/Sources/SAKBase/BaseTypes/Point.swift @@ -12,10 +12,10 @@ import Foundation /// A Swift protocol for a point in some 2 dimensional container public protocol Point: Equatable { associatedtype PointValue: Real - + var xCord: PointValue { get set} var yCord: PointValue { get set} - + init(xCord: PointValue, yCord: PointValue) } @@ -41,7 +41,7 @@ public extension Point { return self.xCord.within(epsilon.xCord, of: point.xCord) && self.yCord.within(epsilon.yCord, of: point.yCord) } - + /// Returns wether or not the value is within the given episolon of the other value /// - Parameters: /// - epsilon: The max allowed difference between two values @@ -49,7 +49,7 @@ public extension Point { /// - Returns: True if within epsilon of point, false otherwise. func cordsWithin(_ epsilon: Self.PointValue, of point: Self) -> Bool { return within(Self.init(xCord: epsilon, yCord: epsilon), - of: point) + of: point) } } diff --git a/Sources/SAKBase/BaseTypes/Point3.swift b/Sources/SAKBase/BaseTypes/Point3.swift index 1ecc131..9a75a1d 100644 --- a/Sources/SAKBase/BaseTypes/Point3.swift +++ b/Sources/SAKBase/BaseTypes/Point3.swift @@ -11,11 +11,11 @@ import Foundation /// A Swift protocol for a point in some 2 dimensional container public protocol Point3: Equatable { associatedtype PointValue: Real - + var xCord: PointValue { get set} var yCord: PointValue { get set} var zCord: PointValue { get set} - + init(xCord: PointValue, yCord: PointValue, zCord: PointValue) } @@ -42,7 +42,7 @@ public extension Point3 { yCord.within(epsilon.yCord, of: point.yCord) && zCord.within(epsilon.zCord, of: point.zCord) } - + /// Returns wether or not the value is within the given episolon of the other value /// - Parameters: /// - epsilon: The max allowed difference between two values diff --git a/Sources/SAKBase/BaseTypes/Stack.swift b/Sources/SAKBase/BaseTypes/Stack.swift index fee77bd..d9af259 100755 --- a/Sources/SAKBase/BaseTypes/Stack.swift +++ b/Sources/SAKBase/BaseTypes/Stack.swift @@ -1,33 +1,33 @@ /* - Last-in first-out stack (LIFO) +Last-in first-out stack (LIFO) - Push and pop are O(1) operations. +Push and pop are O(1) operations. */ public struct Stack { - fileprivate var array = [Element]() - + fileprivate var array = [Element]() + public init(){} - - public var isEmpty: Bool { - return array.isEmpty - } - - public var count: Int { - return array.count - } - - public mutating func push(_ element: Element) { - array.append(element) - } - - public mutating func pop() -> Element? { - return array.popLast() - } - - public var top: Element? { - return array.last - } - + + public var isEmpty: Bool { + return array.isEmpty + } + + public var count: Int { + return array.count + } + + public mutating func push(_ element: Element) { + array.append(element) + } + + public mutating func pop() -> Element? { + return array.popLast() + } + + public var top: Element? { + return array.last + } + public mutating func forEachPop(_ body: (Element) throws -> Void) rethrows { while let element = pop() { try body(element) diff --git a/Sources/SAKBase/Concurrency/AsyncOperation.swift b/Sources/SAKBase/Concurrency/AsyncOperation.swift index 99a98da..ddae183 100644 --- a/Sources/SAKBase/Concurrency/AsyncOperation.swift +++ b/Sources/SAKBase/Concurrency/AsyncOperation.swift @@ -10,51 +10,51 @@ import Foundation /// Code taken from https://medium.com/flawless-app-stories/parallel-programming-with-swift-operations-54cbefaf3cb0 /// Overide the exec method in order to use. open class AsyncOperation: Operation { - - public var isFinishedEvent = Event() - public var isExecutingEvent = Event() - - override open var isAsynchronous: Bool { - return true - } - - var _isFinished: Bool = false - - override open var isFinished: Bool { - set { - willChangeValue(forKey: "isFinished") - _isFinished = newValue - didChangeValue(forKey: "isFinished") - isFinishedEvent.raise(newValue) - } - - get { - return _isFinished - } - } - - var _isExecuting: Bool = false - - override open var isExecuting: Bool { - set { - willChangeValue(forKey: "isExecuting") - _isExecuting = newValue - didChangeValue(forKey: "isExecuting") - isExecutingEvent.raise(newValue) - } - - get { - return _isExecuting - } - } - - open func execute() { - } - - override open func start() { - isExecuting = true - execute() - isExecuting = false - isFinished = true - } + + public var isFinishedEvent = Event() + public var isExecutingEvent = Event() + + override open var isAsynchronous: Bool { + return true + } + + var _isFinished: Bool = false + + override open var isFinished: Bool { + set { + willChangeValue(forKey: "isFinished") + _isFinished = newValue + didChangeValue(forKey: "isFinished") + isFinishedEvent.raise(newValue) + } + + get { + return _isFinished + } + } + + var _isExecuting: Bool = false + + override open var isExecuting: Bool { + set { + willChangeValue(forKey: "isExecuting") + _isExecuting = newValue + didChangeValue(forKey: "isExecuting") + isExecutingEvent.raise(newValue) + } + + get { + return _isExecuting + } + } + + open func execute() { + } + + override open func start() { + isExecuting = true + execute() + isExecuting = false + isFinished = true + } } diff --git a/Sources/SAKBase/Concurrency/GroupOperation.swift b/Sources/SAKBase/Concurrency/GroupOperation.swift index b402e65..d6ba0e6 100644 --- a/Sources/SAKBase/Concurrency/GroupOperation.swift +++ b/Sources/SAKBase/Concurrency/GroupOperation.swift @@ -10,10 +10,10 @@ import Foundation // public class GroupOperation: AsyncOperation { - let queue = OperationQueue() - public var operations: [AsyncOperation] = [] - - public override func execute() { - queue.addOperations(operations, waitUntilFinished: true) - } + let queue = OperationQueue() + public var operations: [AsyncOperation] = [] + + public override func execute() { + queue.addOperations(operations, waitUntilFinished: true) + } } diff --git a/Sources/SAKBase/Concurrency/Lock.swift b/Sources/SAKBase/Concurrency/Lock.swift index 5f1ceec..94a747a 100644 --- a/Sources/SAKBase/Concurrency/Lock.swift +++ b/Sources/SAKBase/Concurrency/Lock.swift @@ -9,65 +9,65 @@ import Foundation /// A C based Lock with Concurrent read access across threads final class Lock { - private var internalValue: T - - private var lock = PThread.ReadWriteLock() - - init(_ value: T) { - internalValue = value - } - - /// Allows use to edit the value contained - /// - Parameter closure:Contains the logic for editing the given variable - /// - Warning: Returned value is not thread safe for reference types - @discardableResult - public func edit(_ closure: (inout T) -> ()) -> T { - lock.readWriteLock() - defer { - lock.unlock() - } - - closure(&internalValue) - - return internalValue - } - - /// Allows use to read the given value - /// - Warning: Not thread safe for reference types - /// - Parameter closure:Contains the logic for editign the given variable - @discardableResult - public func read(_ closure: (T) -> ()) -> T { - lock.readLock() - defer { - lock.unlock() - } - - closure(internalValue) - - return internalValue - } - - /// Returns a copy of the contained value. - /// - Warning: NOT thread safe for reference types - public var value: T { - lock.readLock() - defer { - lock.unlock() - } - - return internalValue - } - - public init(value: T) { - self.internalValue = value - } - - // Tells codable to ignore NSLock as NSLock is dependant on runtime and does not need to be stored - private enum CodingKeys : String, CodingKey { - case internalValue - } + private var internalValue: T + + private var lock = PThread.ReadWriteLock() + + init(_ value: T) { + internalValue = value + } + + /// Allows use to edit the value contained + /// - Parameter closure:Contains the logic for editing the given variable + /// - Warning: Returned value is not thread safe for reference types + @discardableResult + public func edit(_ closure: (inout T) -> ()) -> T { + lock.readWriteLock() + defer { + lock.unlock() + } + + closure(&internalValue) + + return internalValue + } + + /// Allows use to read the given value + /// - Warning: Not thread safe for reference types + /// - Parameter closure:Contains the logic for editign the given variable + @discardableResult + public func read(_ closure: (T) -> ()) -> T { + lock.readLock() + defer { + lock.unlock() + } + + closure(internalValue) + + return internalValue + } + + /// Returns a copy of the contained value. + /// - Warning: NOT thread safe for reference types + public var value: T { + lock.readLock() + defer { + lock.unlock() + } + + return internalValue + } + + public init(value: T) { + self.internalValue = value + } + + // Tells codable to ignore NSLock as NSLock is dependant on runtime and does not need to be stored + private enum CodingKeys : String, CodingKey { + case internalValue + } } extension Lock: Codable where T: Codable { - + } diff --git a/Sources/SAKBase/Concurrency/PThread.swift b/Sources/SAKBase/Concurrency/PThread.swift index 2126ca5..f7ce26f 100644 --- a/Sources/SAKBase/Concurrency/PThread.swift +++ b/Sources/SAKBase/Concurrency/PThread.swift @@ -10,54 +10,54 @@ import Foundation /// Contains wrapper types for C pthread operations public struct PThread { - - private init() {} - - /// Wrapper for pthread_rwlock_t - public class ReadWriteLock { - @usableFromInline - var theLock = pthread_rwlock_t() - - public init() { - pthread_rwlock_init(&theLock, nil) - } - - public func readLock() { - pthread_rwlock_wrlock(&theLock) - } - - public func unlock() { - pthread_rwlock_unlock(&theLock) - } - - public func readWriteLock() { - pthread_rwlock_rdlock(&theLock) - } - - deinit { - pthread_rwlock_destroy(&theLock) - } - } - - /// A wrapper for pthread_mutex_t - class Mutex { - @usableFromInline - var theLock = pthread_mutex_t() - - public init() { - pthread_mutex_init(&theLock, nil) - } - - public func lock() { - pthread_mutex_lock(&theLock) - } - - public func unlock() { - pthread_mutex_unlock(&theLock) - } - - deinit { - pthread_mutex_destroy(&theLock) - } - } + + private init() {} + + /// Wrapper for pthread_rwlock_t + public class ReadWriteLock { + @usableFromInline + var theLock = pthread_rwlock_t() + + public init() { + pthread_rwlock_init(&theLock, nil) + } + + public func readLock() { + pthread_rwlock_wrlock(&theLock) + } + + public func unlock() { + pthread_rwlock_unlock(&theLock) + } + + public func readWriteLock() { + pthread_rwlock_rdlock(&theLock) + } + + deinit { + pthread_rwlock_destroy(&theLock) + } + } + + /// A wrapper for pthread_mutex_t + class Mutex { + @usableFromInline + var theLock = pthread_mutex_t() + + public init() { + pthread_mutex_init(&theLock, nil) + } + + public func lock() { + pthread_mutex_lock(&theLock) + } + + public func unlock() { + pthread_mutex_unlock(&theLock) + } + + deinit { + pthread_mutex_destroy(&theLock) + } + } } diff --git a/Sources/SAKBase/Math/RealComparison.swift b/Sources/SAKBase/Math/RealComparison.swift index f360fa6..65b3cb1 100644 --- a/Sources/SAKBase/Math/RealComparison.swift +++ b/Sources/SAKBase/Math/RealComparison.swift @@ -12,32 +12,32 @@ import RealModule public extension Real { - + /// Returns wether or not the value is within the given episolon of the other value /// - Parameters: /// - epsilon: The max allowed difference between two values /// - realNumber: The value to compare against /// - Returns: True if within epsilon of realNumber, false otherwise. func within(_ epsilon: Self, of realNumber: Self) -> Bool { - + guard (self.isNaN || realNumber.isNaN || epsilon.isNaN) == false else { return false } - + guard self != realNumber else { return true } - + guard (self.isInfinite || realNumber.isInfinite) == false else { return false } - + guard epsilon.isInfinite == false else { return true } - + let range = (realNumber - epsilon)...(realNumber + epsilon) - + return range.contains(self) } } @@ -47,7 +47,7 @@ public protocol EquivalentIntegerSize { } public extension EquivalentIntegerSize where Self: BinaryFloatingPoint { - + /// Returns wether or not the value is within the given ulps epsilon of the other value /// - Warning: Ulps is not a good way to compare small numbers. 0.1 and 0 has 1000's of ulps of difference /// - Parameters: @@ -56,13 +56,13 @@ public extension EquivalentIntegerSize where Self: BinaryFloatingPoint { /// - Returns: True if within epsilon of realNumber, false otherwise. func within(ulps: UInt, of realNumber: Self) -> Bool { precondition(ulps != IntegerSize.max) - + let withinUlps = self.distanceInUlps(to: realNumber) - + return withinUlps <= ulps } - - + + /// Gets the distance between floating points in ulps. /// - Warning: Ulps is not a good way to compare small numbers. 0.1 and 0 has 1000's of ulps of difference /// - Parameter realNumber: The number to compare with @@ -71,22 +71,22 @@ public extension EquivalentIntegerSize where Self: BinaryFloatingPoint { guard (self.isNaN || realNumber.isNaN) == false else { return IntegerSize.max } - + guard self != realNumber else { return 0 } - + guard (self.isInfinite || realNumber.isInfinite) == false else { return IntegerSize.max } - + guard self.sign == realNumber.sign else { return IntegerSize.max } - + let bitcastedFloat = unsafeBitCast(self, to: IntegerSize.self) let bitcastedFloatCompared = unsafeBitCast(realNumber, to: IntegerSize.self) - + return abs(bitcastedFloat - bitcastedFloatCompared) } } diff --git a/Sources/SAKBase/NSSwift/ProgressReporting.swift b/Sources/SAKBase/NSSwift/ProgressReporting.swift index 291041d..f50e7be 100644 --- a/Sources/SAKBase/NSSwift/ProgressReporting.swift +++ b/Sources/SAKBase/NSSwift/ProgressReporting.swift @@ -8,5 +8,5 @@ import Foundation public protocol ProgressReporting { - var progress: Progress { get } + var progress: Progress { get } } diff --git a/Sources/SAKBase/Swift/Array.swift b/Sources/SAKBase/Swift/Array.swift index 85865e6..d9ab658 100644 --- a/Sources/SAKBase/Swift/Array.swift +++ b/Sources/SAKBase/Swift/Array.swift @@ -10,14 +10,14 @@ import Foundation extension Array { - /// Returns the value if it exists at the given index, otherwise nil. - /// - Parameters: - /// - guarded: The index to safely get the value from - public subscript(guarded index: Int) -> Element? { - guard (startIndex.. Element? { + guard (startIndex..(wrapped: T?, failureText: @autoclosure () -> String) -> T { - if let x = wrapped {return x} - fatalError(failureText()) + if let x = wrapped {return x} + fatalError(failureText()) } diff --git a/Sources/SAKBase/Swift/String.swift b/Sources/SAKBase/Swift/String.swift index c1b1b56..ffc7407 100644 --- a/Sources/SAKBase/Swift/String.swift +++ b/Sources/SAKBase/Swift/String.swift @@ -9,16 +9,16 @@ import Foundation extension String { - /// Returns the CustomStringConvertible representation of a non-nil value or the default string if nil - /// - Parameters: - /// - describing: The optional to get String - /// - defaultValue: The string to return if nil - public init(describing optional: T?, defaultValue: @autoclosure () -> String) { - switch optional { - case .some(let value): - self = String(describing: value) - case .none: - self = defaultValue() - } - } + /// Returns the CustomStringConvertible representation of a non-nil value or the default string if nil + /// - Parameters: + /// - describing: The optional to get String + /// - defaultValue: The string to return if nil + public init(describing optional: T?, defaultValue: @autoclosure () -> String) { + switch optional { + case .some(let value): + self = String(describing: value) + case .none: + self = defaultValue() + } + } } From d896428faf0e3c1535110d20804299b5a1257fd4 Mon Sep 17 00:00:00 2001 From: Stephen Kac Date: Thu, 16 Jul 2020 10:33:16 -0600 Subject: [PATCH 11/26] Completed the Point types --- SAKBase.xcodeproj/project.pbxproj | 20 ++-- Sources/SAKBase/BaseTypes/Point.swift | 44 +++++++-- Sources/SAKBase/BaseTypes/Point3.swift | 81 +++++++++++----- Sources/SAKBase/Math/RealComparison.swift | 6 +- .../SAKBaseTests/BaseTypes/Point3Tests.swift | 96 +++++++++++++++++++ Tests/SAKBaseTests/BaseTypes/PointTests.swift | 87 +++++++++++++---- 6 files changed, 275 insertions(+), 59 deletions(-) create mode 100644 Tests/SAKBaseTests/BaseTypes/Point3Tests.swift diff --git a/SAKBase.xcodeproj/project.pbxproj b/SAKBase.xcodeproj/project.pbxproj index 5e17866..3b62ad2 100644 --- a/SAKBase.xcodeproj/project.pbxproj +++ b/SAKBase.xcodeproj/project.pbxproj @@ -9,6 +9,8 @@ /* Begin PBXBuildFile section */ CC1F5E4024BFBDD60062B978 /* Disposable.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC1F5E3F24BFBDD60062B978 /* Disposable.swift */; }; CC1F5E4324BFD2640062B978 /* DisposableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC1F5E4124BFD25D0062B978 /* DisposableTests.swift */; }; + CC1F5E4524C0379B0062B978 /* Point3.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC1F5E4424C0379B0062B978 /* Point3.swift */; }; + CC1F5E4824C0B7FB0062B978 /* Point3Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC1F5E4624C039F50062B978 /* Point3Tests.swift */; }; CC90BC1224AA7CD2002CB53F /* SAKBase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CC90BC0924AA7CD1002CB53F /* SAKBase.framework */; }; CC90BC2024AA7CE2002CB53F /* RealComparisonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0D223D6B3860027CD2B /* RealComparisonTests.swift */; }; CC90BC2124AA7CE2002CB53F /* EventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0D423D6B3860027CD2B /* EventTests.swift */; }; @@ -32,7 +34,6 @@ CCA0A4FB24BE2C6B007258BA /* CommandTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCA0A4FA24BE2C6B007258BA /* CommandTests.swift */; }; CCA0A4FD24BE438F007258BA /* PointTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCA0A4FC24BE438F007258BA /* PointTests.swift */; }; CCA0A50024BE5856007258BA /* RealModule in Frameworks */ = {isa = PBXBuildFile; productRef = CCA0A4FF24BE5856007258BA /* RealModule */; }; - CCA0A50324BE6330007258BA /* Point3.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0AE23D6B3550027CD2B /* Point3.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -48,6 +49,8 @@ /* Begin PBXFileReference section */ CC1F5E3F24BFBDD60062B978 /* Disposable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Disposable.swift; sourceTree = ""; }; CC1F5E4124BFD25D0062B978 /* DisposableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisposableTests.swift; sourceTree = ""; }; + CC1F5E4424C0379B0062B978 /* Point3.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Point3.swift; sourceTree = ""; }; + CC1F5E4624C039F50062B978 /* Point3Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Point3Tests.swift; sourceTree = ""; }; CC90BC0924AA7CD1002CB53F /* SAKBase.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SAKBase.framework; sourceTree = BUILT_PRODUCTS_DIR; }; CC90BC1124AA7CD2002CB53F /* SAKBaseTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SAKBaseTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; CC90BC3A24AA7D6B002CB53F /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -58,7 +61,6 @@ CCC1C0AA23D6B3550027CD2B /* BindingCommand.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BindingCommand.swift; sourceTree = ""; }; CCC1C0AB23D6B3550027CD2B /* Stack.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Stack.swift; sourceTree = ""; }; CCC1C0AD23D6B3550027CD2B /* Event.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Event.swift; sourceTree = ""; }; - CCC1C0AE23D6B3550027CD2B /* Point3.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Point3.swift; sourceTree = ""; }; CCC1C0AF23D6B3550027CD2B /* Command.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Command.swift; sourceTree = ""; }; CCC1C0B023D6B3550027CD2B /* Point.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Point.swift; sourceTree = ""; }; CCC1C0B223D6B3550027CD2B /* ProgressReporting.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProgressReporting.swift; sourceTree = ""; }; @@ -112,6 +114,7 @@ CCA0A4FA24BE2C6B007258BA /* CommandTests.swift */, CCA0A4F624BE2C0F007258BA /* StackTests.swift */, CCA0A4F824BE2C40007258BA /* BindingCommandTests.swift */, + CC1F5E4624C039F50062B978 /* Point3Tests.swift */, CCA0A4FC24BE438F007258BA /* PointTests.swift */, ); path = BaseTypes; @@ -158,9 +161,9 @@ CCC1C0AA23D6B3550027CD2B /* BindingCommand.swift */, CCC1C0AB23D6B3550027CD2B /* Stack.swift */, CCC1C0AD23D6B3550027CD2B /* Event.swift */, - CCC1C0AE23D6B3550027CD2B /* Point3.swift */, CCC1C0AF23D6B3550027CD2B /* Command.swift */, CCC1C0B023D6B3550027CD2B /* Point.swift */, + CC1F5E4424C0379B0062B978 /* Point3.swift */, CC1F5E3F24BFBDD60062B978 /* Disposable.swift */, ); path = BaseTypes; @@ -352,7 +355,7 @@ CC1F5E4024BFBDD60062B978 /* Disposable.swift in Sources */, CC90BC2B24AA7CEC002CB53F /* ProgressReporting.swift in Sources */, CC90BC2524AA7CE7002CB53F /* Point.swift in Sources */, - CCA0A50324BE6330007258BA /* Point3.swift in Sources */, + CC1F5E4524C0379B0062B978 /* Point3.swift in Sources */, CC90BC3124AA7CF7002CB53F /* String.swift in Sources */, CC90BC2E24AA7CF4002CB53F /* PThread.swift in Sources */, CC90BC3224AA7CF7002CB53F /* Array.swift in Sources */, @@ -378,6 +381,7 @@ CC90BC2024AA7CE2002CB53F /* RealComparisonTests.swift in Sources */, CCA0A4F724BE2C0F007258BA /* StackTests.swift in Sources */, CCA0A4F924BE2C40007258BA /* BindingCommandTests.swift in Sources */, + CC1F5E4824C0B7FB0062B978 /* Point3Tests.swift in Sources */, CCA0A4FB24BE2C6B007258BA /* CommandTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -397,7 +401,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = N3S44ULQD4; DYLIB_COMPATIBILITY_VERSION = 1; @@ -413,7 +417,7 @@ "@loader_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 0.0.1; + MARKETING_VERSION = 0.0.2; PRODUCT_BUNDLE_IDENTIFIER = "com.Stephen-Kac.SAKBase"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SDKROOT = iphoneos; @@ -427,7 +431,7 @@ isa = XCBuildConfiguration; buildSettings = { CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 3; + CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = N3S44ULQD4; DYLIB_COMPATIBILITY_VERSION = 1; @@ -443,7 +447,7 @@ "@loader_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 0.0.1; + MARKETING_VERSION = 0.0.2; PRODUCT_BUNDLE_IDENTIFIER = "com.Stephen-Kac.SAKBase"; PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; SDKROOT = iphoneos; diff --git a/Sources/SAKBase/BaseTypes/Point.swift b/Sources/SAKBase/BaseTypes/Point.swift index 772d930..eadb0b0 100644 --- a/Sources/SAKBase/BaseTypes/Point.swift +++ b/Sources/SAKBase/BaseTypes/Point.swift @@ -10,16 +10,16 @@ import RealModule import Foundation /// A Swift protocol for a point in some 2 dimensional container -public protocol Point: Equatable { - associatedtype PointValue: Real +public protocol Point: Hashable { + associatedtype PointValue: SignedNumeric - var xCord: PointValue { get set} - var yCord: PointValue { get set} + var xCord: PointValue { get set } + var yCord: PointValue { get set } init(xCord: PointValue, yCord: PointValue) } -public extension Point { +public extension Point where PointValue: Real { init(xCord: Int, yCord: Int) { self.init(xCord: PointValue(xCord), yCord: PointValue(yCord)) } @@ -31,7 +31,7 @@ public extension Point { } } -public extension Point { +public extension Point where PointValue: Real { /// Returns wether or not the value is within the given episolon of the other value /// - Parameters: /// - epsilon: The max allowed difference between two values @@ -53,6 +53,28 @@ public extension Point { } } +public extension Point where PointValue: Real { + @available(*, deprecated: 0.0.0, message: "Do not use equivalency with Real types as it will not work as expected, please use within comparisons isntead") + static func ==(lhs: Self, rhs: Self) -> Bool { + return lhs.xCord == rhs.xCord && lhs.yCord == rhs.yCord + } +} + +public extension Point where PointValue: EquivalentIntegerSize { + + /// Returns wether or not the value is within the given episolon of the other value + /// - Parameters: + /// - ulps: The max allowed difference between two values + /// - point: The value to compare against + /// - Returns: True if within epsilon of point, false otherwise. + func within(ulps: PointType, of point: Self) -> Bool + where PointType.PointValue == UlpType { + return self.xCord.within(ulps: ulps.xCord, of: point.xCord) && + self.yCord.within(ulps: ulps.yCord, of: point.yCord) + } +} + + public func +(lhs: PointType, rhs: PointType) -> PointType { return PointType(xCord: lhs.xCord + rhs.xCord, yCord: lhs.yCord + rhs.yCord) } @@ -65,11 +87,17 @@ public func *(lhs: PointType, rhs: PointType) -> PointType { return PointType(xCord: lhs.xCord * rhs.xCord, yCord: lhs.yCord * rhs.yCord) } -public func /(lhs: PointType, rhs: PointType) -> PointType { +public func /(lhs: PointType, rhs: PointType) -> PointType +where PointType.PointValue: BinaryInteger { return PointType(xCord: lhs.xCord / rhs.xCord, yCord: lhs.yCord / rhs.yCord) } -public extension Point { +public func /(lhs: PointType, rhs: PointType) -> PointType +where PointType.PointValue: BinaryFloatingPoint { + return PointType(xCord: lhs.xCord / rhs.xCord, yCord: lhs.yCord / rhs.yCord) +} + +public extension Point where PointValue: Real { func floored() -> Self { let value = Self.init(xCord: floor(xCord), yCord: floor(yCord)) return value diff --git a/Sources/SAKBase/BaseTypes/Point3.swift b/Sources/SAKBase/BaseTypes/Point3.swift index 9a75a1d..8c50b5c 100644 --- a/Sources/SAKBase/BaseTypes/Point3.swift +++ b/Sources/SAKBase/BaseTypes/Point3.swift @@ -1,25 +1,26 @@ // // Point3.swift -// Dark Ember +// SAKBase // -// Created by Stephen Kac on 12/29/16. -// Copyright © 2016 StephenKac. All rights reserved. +// Created by Stephen Kac on 7/16/20. +// Copyright © 2020 Stephen Kac. All rights reserved. // + import RealModule import Foundation /// A Swift protocol for a point in some 2 dimensional container -public protocol Point3: Equatable { - associatedtype PointValue: Real - - var xCord: PointValue { get set} - var yCord: PointValue { get set} - var zCord: PointValue { get set} - +public protocol Point3: Hashable { + associatedtype PointValue: SignedNumeric + + var xCord: PointValue { get set } + var yCord: PointValue { get set } + var zCord: PointValue { get set } + init(xCord: PointValue, yCord: PointValue, zCord: PointValue) } -public extension Point3 { +public extension Point3 where PointValue: Real { init(xCord: Int, yCord: Int, zCord: Int) { self.init(xCord: PointValue(xCord), yCord: PointValue(yCord), zCord: PointValue(zCord)) } @@ -31,47 +32,79 @@ public extension Point3 { } } -public extension Point3 { +public extension Point3 where PointValue: Real { /// Returns wether or not the value is within the given episolon of the other value /// - Parameters: /// - epsilon: The max allowed difference between two values /// - point: The value to compare against /// - Returns: True if within epsilon of point, false otherwise. func within(_ epsilon: Self, of point: Self) -> Bool { - xCord.within(epsilon.xCord, of: point.xCord) && - yCord.within(epsilon.yCord, of: point.yCord) && - zCord.within(epsilon.zCord, of: point.zCord) + self.xCord.within(epsilon.xCord, of: point.xCord) && + self.yCord.within(epsilon.yCord, of: point.yCord) } - + /// Returns wether or not the value is within the given episolon of the other value /// - Parameters: /// - epsilon: The max allowed difference between two values /// - point: The value to compare against /// - Returns: True if within epsilon of point, false otherwise. func cordsWithin(_ epsilon: Self.PointValue, of point: Self) -> Bool { - return within(Self.init(xCord: epsilon, yCord: epsilon, zCord: epsilon), + within(Self.init(xCord: epsilon, yCord: epsilon, zCord: epsilon), of: point) } } +public extension Point3 where PointValue: Real { + @available(*, deprecated: 0.0.0, message: "Do not use equivalency with Real types as it will not work as expected, please use within comparisons isntead") + static func ==(lhs: Self, rhs: Self) -> Bool { + lhs.xCord == rhs.xCord + && lhs.yCord == rhs.yCord + && lhs.zCord == rhs.zCord + } +} + +public extension Point3 where PointValue: EquivalentIntegerSize { + + /// Returns wether or not the value is within the given episolon of the other value + /// - Parameters: + /// - ulps: The max allowed difference between two values + /// - point: The value to compare against + /// - Returns: True if within epsilon of point, false otherwise. + func within(ulps: PointType, of point: Self) -> Bool + where PointType.PointValue == UlpType { + return self.xCord.within(ulps: ulps.xCord, of: point.xCord) && + self.yCord.within(ulps: ulps.yCord, of: point.yCord) + } +} + + public func +(lhs: PointType, rhs: PointType) -> PointType { - PointType(xCord: lhs.xCord + rhs.xCord, yCord: lhs.yCord + rhs.yCord, zCord: lhs.zCord + rhs.zCord) + return PointType(xCord: lhs.xCord + rhs.xCord, yCord: lhs.yCord + rhs.yCord, zCord: lhs.zCord + rhs.zCord) } public func -(lhs: PointType, rhs: PointType) -> PointType { - PointType(xCord: lhs.xCord - rhs.xCord, yCord: lhs.yCord - rhs.yCord, zCord: lhs.zCord - rhs.zCord) + return PointType(xCord: lhs.xCord - rhs.xCord, yCord: lhs.yCord - rhs.yCord, zCord: lhs.zCord - rhs.zCord) } public func *(lhs: PointType, rhs: PointType) -> PointType { - PointType(xCord: lhs.xCord * rhs.xCord, yCord: lhs.yCord * rhs.yCord, zCord: lhs.zCord * rhs.zCord) + return PointType(xCord: lhs.xCord * rhs.xCord, yCord: lhs.yCord * rhs.yCord, zCord: lhs.zCord * rhs.zCord) } -public func /(lhs: PointType, rhs: PointType) -> PointType { - PointType(xCord: lhs.xCord / rhs.xCord, yCord: lhs.yCord / rhs.yCord, zCord: lhs.yCord / rhs.yCord) +public func /(lhs: PointType, rhs: PointType) -> PointType +where PointType.PointValue: BinaryInteger { + return PointType(xCord: lhs.xCord / rhs.xCord, yCord: lhs.yCord / rhs.yCord, zCord: lhs.zCord / rhs.zCord) } -public extension Point3 { +public func /(lhs: PointType, rhs: PointType) -> PointType +where PointType.PointValue: BinaryFloatingPoint { + return PointType(xCord: lhs.xCord / rhs.xCord, yCord: lhs.yCord / rhs.yCord, zCord: lhs.zCord / rhs.zCord) +} + +public extension Point3 where PointValue: Real { func floored() -> Self { - Self.init(xCord: floor(xCord), yCord: floor(yCord), zCord: floor(zCord)) + let value = Self.init(xCord: floor(xCord), yCord: floor(yCord), zCord: floor(zCord)) + return value } } + + diff --git a/Sources/SAKBase/Math/RealComparison.swift b/Sources/SAKBase/Math/RealComparison.swift index 65b3cb1..1c6f1f1 100644 --- a/Sources/SAKBase/Math/RealComparison.swift +++ b/Sources/SAKBase/Math/RealComparison.swift @@ -42,11 +42,11 @@ public extension Real { } } -public protocol EquivalentIntegerSize { +public protocol EquivalentIntegerSize: BinaryFloatingPoint { associatedtype IntegerSize: SignedNumeric & FixedWidthInteger } -public extension EquivalentIntegerSize where Self: BinaryFloatingPoint { +public extension EquivalentIntegerSize { /// Returns wether or not the value is within the given ulps epsilon of the other value /// - Warning: Ulps is not a good way to compare small numbers. 0.1 and 0 has 1000's of ulps of difference @@ -54,7 +54,7 @@ public extension EquivalentIntegerSize where Self: BinaryFloatingPoint { /// - ulps: The max allowed difference in ulps between two values /// - realNumber: The value to compare against /// - Returns: True if within epsilon of realNumber, false otherwise. - func within(ulps: UInt, of realNumber: Self) -> Bool { + func within(ulps: Integer, of realNumber: Self) -> Bool { precondition(ulps != IntegerSize.max) let withinUlps = self.distanceInUlps(to: realNumber) diff --git a/Tests/SAKBaseTests/BaseTypes/Point3Tests.swift b/Tests/SAKBaseTests/BaseTypes/Point3Tests.swift new file mode 100644 index 0000000..74d1430 --- /dev/null +++ b/Tests/SAKBaseTests/BaseTypes/Point3Tests.swift @@ -0,0 +1,96 @@ +// +// Point3Tests.swift +// SAKBase +// +// Created by Stephen Kac on 7/16/20. +// Copyright © 2020 Stephen Kac. All rights reserved. +// + +import XCTest +import SAKBase + +class Point3Tests: XCTestCase { + + func test_pointsDouble_equality() { + let point1 = Point3DoubleTest(xCord: 1, yCord: 1, zCord: 1) + let point2 = Point3DoubleTest(xCord: 1.0, yCord: 1.0, zCord: 1) + let point3 = Point3DoubleTest(xCord: 3.9, yCord: 1.0, zCord: 1) + + XCTAssertTrue(point1.cordsWithin(2.0, of: point2)) + XCTAssertFalse(point1.cordsWithin(2.0, of: point3)) + XCTAssertTrue(point1.within(ulps: Point3IntTest(xCord: 0, yCord: 1, zCord: 1), of: point1)) + } + + func test_pointsDouble_arithmetic() { + let point1 = Point3DoubleTest(xCord: 1, yCord: 1, zCord: 1) + + XCTAssertTrue( + (point1 + point1) + .cordsWithin(0.00000000001, + of: Point3DoubleTest(xCord: 2, yCord: 2, zCord: 2))) + + XCTAssertTrue( + (point1 - point1) + .cordsWithin(0.00000000001, + of: Point3DoubleTest(xCord: 0.0, yCord: 0.0, zCord: 0.0))) + + var pointVar = point1 + + pointVar += point1 + XCTAssertTrue(pointVar.cordsWithin(0.000000001, of: point1 + point1)) + + let pointX = pointVar * Point3DoubleTest(xCord: 0, yCord: 1, zCord: 2) + XCTAssertTrue(pointX.cordsWithin(0.000000001, of: Point3DoubleTest(xCord: 0, yCord: pointVar.yCord, zCord: 2))) + + let pointDiv = pointVar / Point3DoubleTest(xCord: 2, yCord: 2, zCord: 2) + XCTAssertTrue(pointDiv.cordsWithin(0.0000001, of: point1)) + + XCTAssertEqual(Point3DoubleTest(xCord: 3.0, yCord: 4.0, zCord: 3.0), Point3DoubleTest(xCord: 3.2, yCord: 4.3, zCord: 3.4).floored()) + } + + func test_pointsInt_equality() { + let point1 = Point3IntTest(xCord: 1, yCord: 1, zCord: 1) + let point2 = Point3IntTest(xCord: 1, yCord: 1, zCord: 1) + let point3 = Point3IntTest(xCord: 2, yCord: 2, zCord: 3) + + XCTAssertTrue(point1 == point2) + XCTAssertFalse(point1 == point3) + } + + func test_pointsInt_arithmetic() { + let point1 = Point3IntTest(xCord: 1, yCord: 1, zCord: 1) + + XCTAssertTrue( + (point1 + point1) + == Point3IntTest(xCord: 2, yCord: 2, zCord: 2)) + + XCTAssertTrue( + (point1 - point1) + == Point3IntTest(xCord: 0, yCord: 0, zCord: 0)) + + var pointVar = point1 + + pointVar += point1 + XCTAssertTrue(pointVar == (point1 + point1)) + + let pointX = pointVar * Point3IntTest(xCord: 0, yCord: 1, zCord: 0) + XCTAssertTrue(pointX == Point3IntTest(xCord: 0, yCord: pointVar.yCord, zCord: 0)) + + let pointDiv = pointVar / Point3IntTest(xCord: 2, yCord: 2, zCord: 2) + XCTAssertTrue(pointDiv == point1) + } +} + +private extension Point3Tests { + struct Point3DoubleTest: Point3 { + var xCord: Double + var yCord: Double + var zCord: Double + } + + struct Point3IntTest: Point3 { + var xCord: Int + var yCord: Int + var zCord: Int + } +} diff --git a/Tests/SAKBaseTests/BaseTypes/PointTests.swift b/Tests/SAKBaseTests/BaseTypes/PointTests.swift index b950db9..da9040c 100644 --- a/Tests/SAKBaseTests/BaseTypes/PointTests.swift +++ b/Tests/SAKBaseTests/BaseTypes/PointTests.swift @@ -11,31 +11,86 @@ import SAKBase class PointTests: XCTestCase { - func test_points_equality() { - let point1 = PointTest(xCord: 1, yCord: 1) - let point2 = PointTest(xCord: 1.0, yCord: 1.0) - let point3 = PointTest(xCord: 1.1, yCord: 1.0) + func test_pointsDouble_equality() { + let point1 = PointDoubleTest(xCord: 1, yCord: 1) + let point2 = PointDoubleTest(xCord: 1.0, yCord: 1.0) + let point3 = PointDoubleTest(xCord: 3.9, yCord: 1.0) - XCTAssertTrue(point1 ~= point2) - XCTAssertFalse(point1 ~= point3) + XCTAssertTrue(point1.cordsWithin(2.0, of: point2)) + XCTAssertFalse(point1.cordsWithin(2.0, of: point3)) + XCTAssertTrue(point1.within(ulps: PointIntTest(xCord: 0, yCord: 1), of: point1)) + + XCTAssertTrue(point1 == point2) + } + + func test_pointsDouble_arithmetic() { + let point1 = PointDoubleTest(xCord: 1, yCord: 1) + + XCTAssertTrue( + (point1 + point1) + .cordsWithin(0.00000000001, + of: PointDoubleTest(xCord: 2, yCord: 2))) + + XCTAssertTrue( + (point1 - point1) + .cordsWithin(0.00000000001, + of: PointDoubleTest(xCord: 0.0, yCord: 0.0))) + + var pointVar = point1 + + pointVar += point1 + XCTAssertTrue(pointVar.cordsWithin(0.000000001, of: point1 + point1)) + + let pointX = pointVar * PointDoubleTest(xCord: 0, yCord: 1) + XCTAssertTrue(pointX.cordsWithin(0.000000001, of: PointDoubleTest(xCord: 0, yCord: pointVar.yCord))) + + let pointDiv = pointVar / PointDoubleTest(xCord: 2, yCord: 2) + XCTAssertTrue(pointDiv.cordsWithin(0.0000001, of: point1)) + + XCTAssertEqual(PointDoubleTest(xCord: 3.0, yCord: 4.0), PointDoubleTest(xCord: 3.2, yCord: 4.3).floored()) } - func test_points_arithmetic() { - let point1 = PointTest(xCord: 1, yCord: 1) + func test_pointsInt_equality() { + let point1 = PointIntTest(xCord: 1, yCord: 1) + let point2 = PointIntTest(xCord: 1, yCord: 1) + let point3 = PointIntTest(xCord: 2, yCord: 2) + + XCTAssertTrue(point1 == point2) + XCTAssertFalse(point1 == point3) + } + + func test_pointsInt_arithmetic() { + let point1 = PointIntTest(xCord: 1, yCord: 1) + + XCTAssertTrue( + (point1 + point1) + == PointIntTest(xCord: 2, yCord: 2)) - XCTAssertTrue((point1 + point1).cordsWithin(0.00000000001, - of: PointTest(xCord: 2, yCord: 2))) + XCTAssertTrue( + (point1 - point1) + == PointIntTest(xCord: 0, yCord: 0)) - XCTAssertTrue((point1 - point1).cordsWithin(0.00000000001, - of: PointTest(xCord: 0.0, yCord: 0.0))) + var pointVar = point1 + + pointVar += point1 + XCTAssertTrue(pointVar == (point1 + point1)) + + let pointX = pointVar * PointIntTest(xCord: 0, yCord: 1) + XCTAssertTrue(pointX == PointIntTest(xCord: 0, yCord: pointVar.yCord)) + + let pointDiv = pointVar / PointIntTest(xCord: 2, yCord: 2) + XCTAssertTrue(pointDiv == point1) } } private extension PointTests { - struct PointTest: Point { - typealias PointValue = Double + struct PointDoubleTest: Point { + var xCord: Double + var yCord: Double + } - var xCord: PointValue - var yCord: PointValue + struct PointIntTest: Point { + var xCord: Int + var yCord: Int } } From f508cbf64b5ea92834ce39ccdea843e000804476 Mon Sep 17 00:00:00 2001 From: Stephen Kac Date: Thu, 16 Jul 2020 11:36:00 -0600 Subject: [PATCH 12/26] Setup Swift Lint and Linted program --- SAKBase.xcodeproj/project.pbxproj | 30 ++++++++-- .../SAKBase/BaseTypes/BindingCommand.swift | 17 +++--- Sources/SAKBase/BaseTypes/Command.swift | 38 +++++++------ Sources/SAKBase/BaseTypes/Disposable.swift | 8 +-- Sources/SAKBase/BaseTypes/Event.swift | 40 +++++++------ Sources/SAKBase/BaseTypes/Point.swift | 57 ++++++++++++++----- Sources/SAKBase/BaseTypes/Point3.swift | 51 +++++++++++++---- Sources/SAKBase/BaseTypes/Stack.swift | 16 +++--- .../SAKBase/Concurrency/AsyncOperation.swift | 32 +++++------ .../SAKBase/Concurrency/GroupOperation.swift | 4 +- Sources/SAKBase/Concurrency/Lock.swift | 32 +++++------ Sources/SAKBase/Concurrency/PThread.swift | 2 +- ...Comparison.swift => Real+Comparison.swift} | 37 ++++++------ Sources/SAKBase/Swift/Array.swift | 2 +- Sources/SAKBase/Swift/Optional.swift | 3 +- .../SAKBaseTests/BaseTypes/CommandTests.swift | 2 - Tests/SAKBaseTests/BaseTypes/EventTests.swift | 6 +- .../SAKBaseTests/BaseTypes/Point3Tests.swift | 3 +- Tests/SAKBaseTests/BaseTypes/StackTests.swift | 25 ++++---- .../Concurrency/Concurrency.swift | 46 +++++++-------- .../Math/RealComparisonTests.swift | 3 +- 21 files changed, 263 insertions(+), 191 deletions(-) rename Sources/SAKBase/Math/{RealComparison.swift => Real+Comparison.swift} (97%) diff --git a/SAKBase.xcodeproj/project.pbxproj b/SAKBase.xcodeproj/project.pbxproj index 3b62ad2..47a2121 100644 --- a/SAKBase.xcodeproj/project.pbxproj +++ b/SAKBase.xcodeproj/project.pbxproj @@ -21,7 +21,7 @@ CC90BC2924AA7CE7002CB53F /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0AD23D6B3550027CD2B /* Event.swift */; }; CC90BC2A24AA7CE7002CB53F /* BindingCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0AA23D6B3550027CD2B /* BindingCommand.swift */; }; CC90BC2B24AA7CEC002CB53F /* ProgressReporting.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0B223D6B3550027CD2B /* ProgressReporting.swift */; }; - CC90BC2C24AA7CEF002CB53F /* RealComparison.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0B423D6B3550027CD2B /* RealComparison.swift */; }; + CC90BC2C24AA7CEF002CB53F /* Real+Comparison.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0B423D6B3550027CD2B /* Real+Comparison.swift */; }; CC90BC2D24AA7CF4002CB53F /* GroupOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0B723D6B3550027CD2B /* GroupOperation.swift */; }; CC90BC2E24AA7CF4002CB53F /* PThread.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0B923D6B3550027CD2B /* PThread.swift */; }; CC90BC2F24AA7CF4002CB53F /* AsyncOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0B823D6B3550027CD2B /* AsyncOperation.swift */; }; @@ -64,7 +64,7 @@ CCC1C0AF23D6B3550027CD2B /* Command.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Command.swift; sourceTree = ""; }; CCC1C0B023D6B3550027CD2B /* Point.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Point.swift; sourceTree = ""; }; CCC1C0B223D6B3550027CD2B /* ProgressReporting.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ProgressReporting.swift; sourceTree = ""; }; - CCC1C0B423D6B3550027CD2B /* RealComparison.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RealComparison.swift; sourceTree = ""; }; + CCC1C0B423D6B3550027CD2B /* Real+Comparison.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Real+Comparison.swift"; sourceTree = ""; }; CCC1C0B623D6B3550027CD2B /* Lock.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Lock.swift; sourceTree = ""; }; CCC1C0B723D6B3550027CD2B /* GroupOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GroupOperation.swift; sourceTree = ""; }; CCC1C0B823D6B3550027CD2B /* AsyncOperation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AsyncOperation.swift; sourceTree = ""; }; @@ -180,7 +180,7 @@ CCC1C0B323D6B3550027CD2B /* Math */ = { isa = PBXGroup; children = ( - CCC1C0B423D6B3550027CD2B /* RealComparison.swift */, + CCC1C0B423D6B3550027CD2B /* Real+Comparison.swift */, ); path = Math; sourceTree = ""; @@ -256,6 +256,7 @@ buildPhases = ( CC90BC0424AA7CD1002CB53F /* Headers */, CC90BC0524AA7CD1002CB53F /* Sources */, + CC1F5E4924C0B90B0062B978 /* Swift-Lint */, CC90BC0624AA7CD1002CB53F /* Frameworks */, CC90BC0724AA7CD1002CB53F /* Resources */, ); @@ -346,6 +347,27 @@ }; /* End PBXResourcesBuildPhase section */ +/* Begin PBXShellScriptBuildPhase section */ + CC1F5E4924C0B90B0062B978 /* Swift-Lint */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Swift-Lint"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n"; + }; +/* End PBXShellScriptBuildPhase section */ + /* Begin PBXSourcesBuildPhase section */ CC90BC0524AA7CD1002CB53F /* Sources */ = { isa = PBXSourcesBuildPhase; @@ -363,7 +385,7 @@ CC90BC2824AA7CE7002CB53F /* Command.swift in Sources */, CC90BC3024AA7CF4002CB53F /* Lock.swift in Sources */, CC90BC2924AA7CE7002CB53F /* Event.swift in Sources */, - CC90BC2C24AA7CEF002CB53F /* RealComparison.swift in Sources */, + CC90BC2C24AA7CEF002CB53F /* Real+Comparison.swift in Sources */, CC90BC2F24AA7CF4002CB53F /* AsyncOperation.swift in Sources */, CC90BC2D24AA7CF4002CB53F /* GroupOperation.swift in Sources */, CC90BC3324AA7CF7002CB53F /* Optional.swift in Sources */, diff --git a/Sources/SAKBase/BaseTypes/BindingCommand.swift b/Sources/SAKBase/BaseTypes/BindingCommand.swift index 1e063d9..da8e4a2 100644 --- a/Sources/SAKBase/BaseTypes/BindingCommand.swift +++ b/Sources/SAKBase/BaseTypes/BindingCommand.swift @@ -10,7 +10,8 @@ import Foundation /** Abstract: -Used for a command responder chain in order to allow responsibility to be passed but retain a strong reference to all captured types. +Used for a command responder chain in order to allow responsibility to be passed +but retain a strong reference to all captured types. */ public protocol BindingCommandResponder { func recieve(_ command: BindingCommand, sender: Any?) @@ -20,23 +21,23 @@ public protocol BindingCommandResponder { Abstract: Is used to give buttons and inputs actions that they will incur. Unlike the Command, this class is static. */ -final public class BindingCommand { - +public final class BindingCommand { + /// The name of the command public var name: String? - + /// The command to be executed private let execCommand: () -> Bool - + /// The command that will undo the execCommand private let undoCommand: (() -> Bool)? - + /// Initializes the Command with an executable closure and a closure to undo it. public init(exec: @escaping () -> Bool, undo: (() -> Bool)? = nil ) { execCommand = exec undoCommand = undo } - + /** Executes the command if actor is non-nil. - Returns: a discardable `Bool` describing if the action was successful @@ -45,7 +46,7 @@ final public class BindingCommand { public func execute() -> Bool { return execCommand() } - + /** Does the undo variant of the command if undo is set with the last value set. - Returns: a discardable `Bool` describing if the action was successful diff --git a/Sources/SAKBase/BaseTypes/Command.swift b/Sources/SAKBase/BaseTypes/Command.swift index 4544b17..bdb02e8 100644 --- a/Sources/SAKBase/BaseTypes/Command.swift +++ b/Sources/SAKBase/BaseTypes/Command.swift @@ -8,41 +8,42 @@ import Foundation - /** Abstract: -Is used to give buttons and inputs actions that they will incur. This is helpful if you dont want to create a new command for every incurrence of the command with a different value +Is used to give buttons and inputs actions that they will incur. +This is helpful if you dont want to create a new command for every incurrence of the command with a different value */ -final public class Command { - - // TODO: Add listenning so that classes can subscribe and listen to this command. - //private let listeners: [ - +public final class Command { + /// The command to be executed private let execCommand: (ActorType, ValueType?) -> Bool - + /// The command that will undo the execCommand public let undoCommand: ((ActorType, ValueType?) -> Bool)? - + /// This is the value that will be used in during the exec or undo commands public var value: ValueType? - + /// This is the current actor the comamnds will act upon public weak var actor: ActorType? - + /// Initializes the Command with an executable closure and the required default values to run the command public convenience init(actor: ActorType?, value: ValueType?, exec: @escaping (ActorType, ValueType?) -> Bool) { self.init(actor: actor, value: value, exec: exec, undo: nil) } - + /// Initializes the Command with an executable closure and an undo closure - public required init(actor: ActorType?, value: ValueType?, exec: @escaping (ActorType, ValueType?) -> Bool, undo: ((ActorType, ValueType?) -> Bool)?) { + public required init( + actor: ActorType?, + value: ValueType?, + exec: @escaping (ActorType, ValueType?) -> Bool, + undo: ((ActorType, ValueType?) -> Bool)?) { self.execCommand = exec self.undoCommand = undo self.value = value self.actor = actor } - + /** Executes the command with the new value if actor is non-nil. - Returns: a discardable `Bool` describing if the action was successful @@ -53,7 +54,7 @@ final public class Command { guard let actor = actor else { return false } return execCommand(actor, value) } - + /** Executes the command if actor is non-nil. - Returns: a discardable `Bool` describing if the action was successful @@ -63,7 +64,7 @@ final public class Command { guard let actor = actor else { return false } return execCommand(actor, value) } - + /** Does the undo variant of the command if undo is set with the last value set. - Returns: a discardable `Bool` describing if the action was successful @@ -73,9 +74,10 @@ final public class Command { guard let actor = actor else { return false } return undoCommand?(actor, value) ?? false } - + /** - Gets a closure that can undo the last exec comamnd. This can be helpful in queing up a list of commands that can undo previous actions. + Gets a closure that can undo the last exec comamnd. + This can be helpful in queing up a list of commands that can undo previous actions. - Returns: a closure that returns bool for wether it was successful or not */ @discardableResult diff --git a/Sources/SAKBase/BaseTypes/Disposable.swift b/Sources/SAKBase/BaseTypes/Disposable.swift index c7d7244..f4103b6 100644 --- a/Sources/SAKBase/BaseTypes/Disposable.swift +++ b/Sources/SAKBase/BaseTypes/Disposable.swift @@ -9,11 +9,11 @@ /// Automatically disposes of a event upon deinit public class DisposeContainer: Disposable { private weak var toBeDisposed: Disposable? - + public init(toBeDisposed: Disposable) { self.toBeDisposed = toBeDisposed } - + /// Removes the responsibility of disposing the related class from the container public func removeDisposableFromContainer() -> Disposable? { defer { @@ -21,12 +21,12 @@ public class DisposeContainer: Disposable { } return toBeDisposed } - + /// Manually disposes of the contained disposable public func dispose() { toBeDisposed?.dispose() } - + deinit { dispose() } diff --git a/Sources/SAKBase/BaseTypes/Event.swift b/Sources/SAKBase/BaseTypes/Event.swift index aac393a..3dadb0b 100644 --- a/Sources/SAKBase/BaseTypes/Event.swift +++ b/Sources/SAKBase/BaseTypes/Event.swift @@ -13,31 +13,30 @@ import Foundation /// If the returned DisposeContainer leaves its scope, it will remove its related event. /// If a handle throws an error then it will also be removed silently. public class Event { - + fileprivate typealias EventHandler = EventHandlerWrapper public typealias Handler = (DataType) throws -> Void - + fileprivate var eventHandlers = Set() - + public var handlerCount: Int { - get { eventHandlers.count - } } - + public init() {} - + public func raise(_ data: DataType) { for handler in self.eventHandlers { handler.invoke(data) } } - + /** Adds a handler that will be called when the event is raised. - Parameter target: An Object passed to the handler to act on or with, usually the interested class. - Parameter handler: A block of code to run with the target and data raised. - returns: - A handler that allows you to manually stop listenning to the event. You do not need to dispose upon deint of the Target. + A handler that allows you to manually stop listenning to the event. + You do not need to dispose upon deint of the Target. */ @discardableResult public func addHandler( @@ -46,7 +45,7 @@ public class Event { eventHandlers.insert(wrapper) return DisposeContainer(toBeDisposed: wrapper) } - + deinit { for handler in eventHandlers { handler.dispose() @@ -54,25 +53,24 @@ public class Event { } } -private class EventHandlerWrapper -: Disposable, Identifiable { - +private class EventHandlerWrapper: Disposable, Identifiable { + typealias Handler = (DataType) throws -> Void - + let id = UUID() - + let handler: Handler let event: Event - + init(handler: @escaping Handler, event: Event) { self.handler = handler - self.event = event; + self.event = event } - - func invoke(_ data: DataType) -> () { + + func invoke(_ data: DataType) { guard (try? handler(data)) != nil else { dispose(); return } } - + public func dispose() { event.eventHandlers.remove(self) } @@ -82,7 +80,7 @@ extension EventHandlerWrapper: Hashable { static func == (lhs: EventHandlerWrapper, rhs: EventHandlerWrapper) -> Bool { lhs.id == rhs.id } - + func hash(into hasher: inout Hasher) { hasher.combine(id) } diff --git a/Sources/SAKBase/BaseTypes/Point.swift b/Sources/SAKBase/BaseTypes/Point.swift index eadb0b0..e46edd0 100644 --- a/Sources/SAKBase/BaseTypes/Point.swift +++ b/Sources/SAKBase/BaseTypes/Point.swift @@ -12,10 +12,10 @@ import Foundation /// A Swift protocol for a point in some 2 dimensional container public protocol Point: Hashable { associatedtype PointValue: SignedNumeric - + var xCord: PointValue { get set } var yCord: PointValue { get set } - + init(xCord: PointValue, yCord: PointValue) } @@ -26,9 +26,19 @@ public extension Point where PointValue: Real { } public extension Point { - static func +=(left: inout Self, right: Self) { + // swiftlint:disable shorthand_operator + static func += (left: inout Self, right: Self) { left = left + right } + + static func -= (left: inout Self, right: Self) { + left = left - right + } + + static func *= (left: inout Self, right: Self) { + left = left * right + } + // swiftlint:enable shorthand_operator } public extension Point where PointValue: Real { @@ -41,7 +51,7 @@ public extension Point where PointValue: Real { return self.xCord.within(epsilon.xCord, of: point.xCord) && self.yCord.within(epsilon.yCord, of: point.yCord) } - + /// Returns wether or not the value is within the given episolon of the other value /// - Parameters: /// - epsilon: The max allowed difference between two values @@ -54,8 +64,14 @@ public extension Point where PointValue: Real { } public extension Point where PointValue: Real { - @available(*, deprecated: 0.0.0, message: "Do not use equivalency with Real types as it will not work as expected, please use within comparisons isntead") - static func ==(lhs: Self, rhs: Self) -> Bool { + @available(*, + deprecated: 0.0.0, + message: + """ + Do not use equivalency with Real types as + "it will not work as expected, please use within comparisons isntead + """) + static func == (lhs: Self, rhs: Self) -> Bool { return lhs.xCord == rhs.xCord && lhs.yCord == rhs.yCord } } @@ -74,7 +90,6 @@ public extension Point where PointValue: EquivalentIntegerSize { } } - public func +(lhs: PointType, rhs: PointType) -> PointType { return PointType(xCord: lhs.xCord + rhs.xCord, yCord: lhs.yCord + rhs.yCord) } @@ -87,14 +102,28 @@ public func *(lhs: PointType, rhs: PointType) -> PointType { return PointType(xCord: lhs.xCord * rhs.xCord, yCord: lhs.yCord * rhs.yCord) } -public func /(lhs: PointType, rhs: PointType) -> PointType -where PointType.PointValue: BinaryInteger { - return PointType(xCord: lhs.xCord / rhs.xCord, yCord: lhs.yCord / rhs.yCord) +public extension Point where PointValue: BinaryInteger { + static func / (lhs: Self, rhs: Self) -> Self { + return Self(xCord: lhs.xCord / rhs.xCord, yCord: lhs.yCord / rhs.yCord) + } + + // swiftlint:disable shorthand_operator + static func /= (left: inout Self, right: Self) { + left = left / right + } + // swiftlint:enable shorthand_operator } -public func /(lhs: PointType, rhs: PointType) -> PointType -where PointType.PointValue: BinaryFloatingPoint { - return PointType(xCord: lhs.xCord / rhs.xCord, yCord: lhs.yCord / rhs.yCord) +public extension Point where PointValue: BinaryFloatingPoint { + static func / (lhs: Self, rhs: Self) -> Self { + return Self(xCord: lhs.xCord / rhs.xCord, yCord: lhs.yCord / rhs.yCord) + } + + // swiftlint:disable shorthand_operator + static func /= (left: inout Self, right: Self) { + left = left / right + } + // swiftlint:enable shorthand_operator } public extension Point where PointValue: Real { @@ -103,5 +132,3 @@ public extension Point where PointValue: Real { return value } } - - diff --git a/Sources/SAKBase/BaseTypes/Point3.swift b/Sources/SAKBase/BaseTypes/Point3.swift index 8c50b5c..3498754 100644 --- a/Sources/SAKBase/BaseTypes/Point3.swift +++ b/Sources/SAKBase/BaseTypes/Point3.swift @@ -27,9 +27,19 @@ public extension Point3 where PointValue: Real { } public extension Point3 { - static func +=(left: inout Self, right: Self) { + // swiftlint:disable shorthand_operator + static func += (left: inout Self, right: Self) { left = left + right } + + static func -= (left: inout Self, right: Self) { + left = left - right + } + + static func *= (left: inout Self, right: Self) { + left = left * right + } + // swiftlint:enable shorthand_operator } public extension Point3 where PointValue: Real { @@ -55,8 +65,14 @@ public extension Point3 where PointValue: Real { } public extension Point3 where PointValue: Real { - @available(*, deprecated: 0.0.0, message: "Do not use equivalency with Real types as it will not work as expected, please use within comparisons isntead") - static func ==(lhs: Self, rhs: Self) -> Bool { + @available(*, + deprecated: 0.0.0, + message: + """ + Do not use equivalency with Real types as + "it will not work as expected, please use within comparisons isntead + """) + static func == (lhs: Self, rhs: Self) -> Bool { lhs.xCord == rhs.xCord && lhs.yCord == rhs.yCord && lhs.zCord == rhs.zCord @@ -77,7 +93,6 @@ public extension Point3 where PointValue: EquivalentIntegerSize { } } - public func +(lhs: PointType, rhs: PointType) -> PointType { return PointType(xCord: lhs.xCord + rhs.xCord, yCord: lhs.yCord + rhs.yCord, zCord: lhs.zCord + rhs.zCord) } @@ -90,14 +105,28 @@ public func *(lhs: PointType, rhs: PointType) -> PointType { return PointType(xCord: lhs.xCord * rhs.xCord, yCord: lhs.yCord * rhs.yCord, zCord: lhs.zCord * rhs.zCord) } -public func /(lhs: PointType, rhs: PointType) -> PointType -where PointType.PointValue: BinaryInteger { - return PointType(xCord: lhs.xCord / rhs.xCord, yCord: lhs.yCord / rhs.yCord, zCord: lhs.zCord / rhs.zCord) +public extension Point3 where PointValue: BinaryInteger { + static func / (lhs: Self, rhs: Self) -> Self { + return Self(xCord: lhs.xCord / rhs.xCord, yCord: lhs.yCord / rhs.yCord, zCord: lhs.zCord / rhs.zCord) + } + + // swiftlint:disable shorthand_operator + static func /= (left: inout Self, right: Self) { + left = left / right + } + // swiftlint:enable shorthand_operator } -public func /(lhs: PointType, rhs: PointType) -> PointType -where PointType.PointValue: BinaryFloatingPoint { - return PointType(xCord: lhs.xCord / rhs.xCord, yCord: lhs.yCord / rhs.yCord, zCord: lhs.zCord / rhs.zCord) +public extension Point3 where PointValue: BinaryFloatingPoint { + static func / (lhs: Self, rhs: Self) -> Self { + return Self(xCord: lhs.xCord / rhs.xCord, yCord: lhs.yCord / rhs.yCord, zCord: lhs.zCord / rhs.zCord) + } + + // swiftlint:disable shorthand_operator + static func /= (left: inout Self, right: Self) { + left = left / right + } + // swiftlint:enable shorthand_operator } public extension Point3 where PointValue: Real { @@ -106,5 +135,3 @@ public extension Point3 where PointValue: Real { return value } } - - diff --git a/Sources/SAKBase/BaseTypes/Stack.swift b/Sources/SAKBase/BaseTypes/Stack.swift index d9af259..1e46dd0 100755 --- a/Sources/SAKBase/BaseTypes/Stack.swift +++ b/Sources/SAKBase/BaseTypes/Stack.swift @@ -5,29 +5,29 @@ Push and pop are O(1) operations. */ public struct Stack { fileprivate var array = [Element]() - - public init(){} - + + public init() {} + public var isEmpty: Bool { return array.isEmpty } - + public var count: Int { return array.count } - + public mutating func push(_ element: Element) { array.append(element) } - + public mutating func pop() -> Element? { return array.popLast() } - + public var top: Element? { return array.last } - + public mutating func forEachPop(_ body: (Element) throws -> Void) rethrows { while let element = pop() { try body(element) diff --git a/Sources/SAKBase/Concurrency/AsyncOperation.swift b/Sources/SAKBase/Concurrency/AsyncOperation.swift index ddae183..d618f22 100644 --- a/Sources/SAKBase/Concurrency/AsyncOperation.swift +++ b/Sources/SAKBase/Concurrency/AsyncOperation.swift @@ -10,47 +10,47 @@ import Foundation /// Code taken from https://medium.com/flawless-app-stories/parallel-programming-with-swift-operations-54cbefaf3cb0 /// Overide the exec method in order to use. open class AsyncOperation: Operation { - + public var isFinishedEvent = Event() public var isExecutingEvent = Event() - + override open var isAsynchronous: Bool { return true } - - var _isFinished: Bool = false - + + var isFinishedInternal: Bool = false + override open var isFinished: Bool { set { willChangeValue(forKey: "isFinished") - _isFinished = newValue + isFinishedInternal = newValue didChangeValue(forKey: "isFinished") isFinishedEvent.raise(newValue) } - + get { - return _isFinished + return isFinishedInternal } } - - var _isExecuting: Bool = false - + + var isExecutingInternal: Bool = false + override open var isExecuting: Bool { set { willChangeValue(forKey: "isExecuting") - _isExecuting = newValue + isExecutingInternal = newValue didChangeValue(forKey: "isExecuting") isExecutingEvent.raise(newValue) } - + get { - return _isExecuting + return isExecutingInternal } } - + open func execute() { } - + override open func start() { isExecuting = true execute() diff --git a/Sources/SAKBase/Concurrency/GroupOperation.swift b/Sources/SAKBase/Concurrency/GroupOperation.swift index d6ba0e6..74b1d96 100644 --- a/Sources/SAKBase/Concurrency/GroupOperation.swift +++ b/Sources/SAKBase/Concurrency/GroupOperation.swift @@ -12,8 +12,8 @@ import Foundation public class GroupOperation: AsyncOperation { let queue = OperationQueue() public var operations: [AsyncOperation] = [] - - public override func execute() { + + override public func execute() { queue.addOperations(operations, waitUntilFinished: true) } } diff --git a/Sources/SAKBase/Concurrency/Lock.swift b/Sources/SAKBase/Concurrency/Lock.swift index 94a747a..0415efa 100644 --- a/Sources/SAKBase/Concurrency/Lock.swift +++ b/Sources/SAKBase/Concurrency/Lock.swift @@ -10,43 +10,43 @@ import Foundation /// A C based Lock with Concurrent read access across threads final class Lock { private var internalValue: T - + private var lock = PThread.ReadWriteLock() - + init(_ value: T) { internalValue = value } - + /// Allows use to edit the value contained /// - Parameter closure:Contains the logic for editing the given variable /// - Warning: Returned value is not thread safe for reference types @discardableResult - public func edit(_ closure: (inout T) -> ()) -> T { + public func edit(_ closure: (inout T) -> Void) -> T { lock.readWriteLock() defer { lock.unlock() } - + closure(&internalValue) - + return internalValue } - + /// Allows use to read the given value /// - Warning: Not thread safe for reference types /// - Parameter closure:Contains the logic for editign the given variable @discardableResult - public func read(_ closure: (T) -> ()) -> T { + public func read(_ closure: (T) -> Void) -> T { lock.readLock() defer { lock.unlock() } - + closure(internalValue) - + return internalValue } - + /// Returns a copy of the contained value. /// - Warning: NOT thread safe for reference types public var value: T { @@ -54,20 +54,20 @@ final class Lock { defer { lock.unlock() } - + return internalValue } - + public init(value: T) { self.internalValue = value } - + // Tells codable to ignore NSLock as NSLock is dependant on runtime and does not need to be stored - private enum CodingKeys : String, CodingKey { + private enum CodingKeys: String, CodingKey { case internalValue } } extension Lock: Codable where T: Codable { - + } diff --git a/Sources/SAKBase/Concurrency/PThread.swift b/Sources/SAKBase/Concurrency/PThread.swift index f7ce26f..aa38c58 100644 --- a/Sources/SAKBase/Concurrency/PThread.swift +++ b/Sources/SAKBase/Concurrency/PThread.swift @@ -55,7 +55,7 @@ public struct PThread { public func unlock() { pthread_mutex_unlock(&theLock) } - + deinit { pthread_mutex_destroy(&theLock) } diff --git a/Sources/SAKBase/Math/RealComparison.swift b/Sources/SAKBase/Math/Real+Comparison.swift similarity index 97% rename from Sources/SAKBase/Math/RealComparison.swift rename to Sources/SAKBase/Math/Real+Comparison.swift index 1c6f1f1..687ca4e 100644 --- a/Sources/SAKBase/Math/RealComparison.swift +++ b/Sources/SAKBase/Math/Real+Comparison.swift @@ -1,5 +1,5 @@ // -// File.swift +// Real+Comparison.swift // SAKBase // // Created by Stephen Kac on 2/27/18. @@ -7,37 +7,35 @@ import RealModule - /// - Parameters: - public extension Real { - + /// Returns wether or not the value is within the given episolon of the other value /// - Parameters: /// - epsilon: The max allowed difference between two values /// - realNumber: The value to compare against /// - Returns: True if within epsilon of realNumber, false otherwise. func within(_ epsilon: Self, of realNumber: Self) -> Bool { - + guard (self.isNaN || realNumber.isNaN || epsilon.isNaN) == false else { return false } - + guard self != realNumber else { return true } - + guard (self.isInfinite || realNumber.isInfinite) == false else { return false } - + guard epsilon.isInfinite == false else { return true } - + let range = (realNumber - epsilon)...(realNumber + epsilon) - + return range.contains(self) } } @@ -47,7 +45,7 @@ public protocol EquivalentIntegerSize: BinaryFloatingPoint { } public extension EquivalentIntegerSize { - + /// Returns wether or not the value is within the given ulps epsilon of the other value /// - Warning: Ulps is not a good way to compare small numbers. 0.1 and 0 has 1000's of ulps of difference /// - Parameters: @@ -56,13 +54,12 @@ public extension EquivalentIntegerSize { /// - Returns: True if within epsilon of realNumber, false otherwise. func within(ulps: Integer, of realNumber: Self) -> Bool { precondition(ulps != IntegerSize.max) - + let withinUlps = self.distanceInUlps(to: realNumber) - + return withinUlps <= ulps } - - + /// Gets the distance between floating points in ulps. /// - Warning: Ulps is not a good way to compare small numbers. 0.1 and 0 has 1000's of ulps of difference /// - Parameter realNumber: The number to compare with @@ -71,22 +68,22 @@ public extension EquivalentIntegerSize { guard (self.isNaN || realNumber.isNaN) == false else { return IntegerSize.max } - + guard self != realNumber else { return 0 } - + guard (self.isInfinite || realNumber.isInfinite) == false else { return IntegerSize.max } - + guard self.sign == realNumber.sign else { return IntegerSize.max } - + let bitcastedFloat = unsafeBitCast(self, to: IntegerSize.self) let bitcastedFloatCompared = unsafeBitCast(realNumber, to: IntegerSize.self) - + return abs(bitcastedFloat - bitcastedFloatCompared) } } diff --git a/Sources/SAKBase/Swift/Array.swift b/Sources/SAKBase/Swift/Array.swift index d9ab658..3b68170 100644 --- a/Sources/SAKBase/Swift/Array.swift +++ b/Sources/SAKBase/Swift/Array.swift @@ -17,7 +17,7 @@ extension Array { guard (startIndex..(wrapped: T?, failureText: @autoclosure () -> String) -> T { - if let x = wrapped {return x} + if let unwrapped = wrapped { return unwrapped } fatalError(failureText()) } diff --git a/Tests/SAKBaseTests/BaseTypes/CommandTests.swift b/Tests/SAKBaseTests/BaseTypes/CommandTests.swift index bcbe9ba..9825e64 100644 --- a/Tests/SAKBaseTests/BaseTypes/CommandTests.swift +++ b/Tests/SAKBaseTests/BaseTypes/CommandTests.swift @@ -23,7 +23,6 @@ class CommandTests: XCTestCase { return true } - XCTAssert(executeExclusiveCommand.execute()) XCTAssert(testClass.bool) testClass.bool = false @@ -47,7 +46,6 @@ class CommandTests: XCTestCase { XCTAssert(executeUndoCommand.undo()) // Should be able to successfully execute the Undo command XCTAssert(!testClass.bool) // The boolean should be back to false - // TEST: Testing the undo functionality after the command has already been uninitialized var uninitialized: Command? = Command(actor: testClass, value: true, exec: { guard let value = $1 else { return false } diff --git a/Tests/SAKBaseTests/BaseTypes/EventTests.swift b/Tests/SAKBaseTests/BaseTypes/EventTests.swift index da588d1..cf368eb 100644 --- a/Tests/SAKBaseTests/BaseTypes/EventTests.swift +++ b/Tests/SAKBaseTests/BaseTypes/EventTests.swift @@ -11,14 +11,14 @@ import SAKBase class EventTests: XCTestCase { - weak var eventHandler: Disposable? = nil + weak var eventHandler: Disposable? func test_event_doesGetDisposed() { let event = Event() do { - let eventHandler = event.addHandler() { _ in } + let eventHandler = event.addHandler { _ in } XCTAssertEqual(event.handlerCount, 1) self.eventHandler = eventHandler } @@ -31,7 +31,7 @@ class EventTests: XCTestCase { let event = Event() - var disposables = Array() + var disposables = [Disposable]() var calls = 0 diff --git a/Tests/SAKBaseTests/BaseTypes/Point3Tests.swift b/Tests/SAKBaseTests/BaseTypes/Point3Tests.swift index 74d1430..d074f37 100644 --- a/Tests/SAKBaseTests/BaseTypes/Point3Tests.swift +++ b/Tests/SAKBaseTests/BaseTypes/Point3Tests.swift @@ -45,7 +45,8 @@ class Point3Tests: XCTestCase { let pointDiv = pointVar / Point3DoubleTest(xCord: 2, yCord: 2, zCord: 2) XCTAssertTrue(pointDiv.cordsWithin(0.0000001, of: point1)) - XCTAssertEqual(Point3DoubleTest(xCord: 3.0, yCord: 4.0, zCord: 3.0), Point3DoubleTest(xCord: 3.2, yCord: 4.3, zCord: 3.4).floored()) + XCTAssertEqual(Point3DoubleTest(xCord: 3.0, yCord: 4.0, zCord: 3.0), + Point3DoubleTest(xCord: 3.2, yCord: 4.3, zCord: 3.4).floored()) } func test_pointsInt_equality() { diff --git a/Tests/SAKBaseTests/BaseTypes/StackTests.swift b/Tests/SAKBaseTests/BaseTypes/StackTests.swift index 93022b9..1f52234 100644 --- a/Tests/SAKBaseTests/BaseTypes/StackTests.swift +++ b/Tests/SAKBaseTests/BaseTypes/StackTests.swift @@ -11,28 +11,31 @@ import SAKBase class StackTests: XCTestCase { - func testStack() { + func test_stack() { var stack = Stack() - XCTAssert(stack.count == 0) - XCTAssert(stack.isEmpty) - XCTAssert(stack.pop() == nil) - XCTAssert(stack.top == nil) + XCTAssertTrue(stack.isEmpty) + XCTAssertNil(stack.pop()) + XCTAssertNil(stack.top) stack.push(1) stack.push(3) - XCTAssert(stack.count == 2) - XCTAssert(!stack.isEmpty) - XCTAssert(stack.top == 3) - XCTAssert(stack.pop() == 3) - XCTAssert(stack.top == 1) + XCTAssertEqual(stack.count, 2) + XCTAssertFalse(stack.isEmpty) + XCTAssertEqual(stack.top, 3) + XCTAssertEqual(stack.pop(), 3) + XCTAssertEqual(stack.top, 1) + + XCTAssertTrue(stack.isEmpty) + XCTAssertNil(stack.pop()) + XCTAssertNil(stack.top) } func test_stack_iteratesOverAll() { var stack = Stack() - let values = (0...5).map { $0 } + let values = Array(0...5) for index in 0...5 { stack.push(values[index]) diff --git a/Tests/SAKBaseTests/Concurrency/Concurrency.swift b/Tests/SAKBaseTests/Concurrency/Concurrency.swift index 25734dd..0e3b9ff 100644 --- a/Tests/SAKBaseTests/Concurrency/Concurrency.swift +++ b/Tests/SAKBaseTests/Concurrency/Concurrency.swift @@ -10,80 +10,78 @@ import XCTest @testable import SAKBase class Concurrency: XCTestCase { - + func testParrallelWrite() { - + let firstAccess = XCTestExpectation(description: "Dual Access 1") let secondAccess = XCTestExpectation(description: "Dual Access 1") let rangeToLoop = 0...100000 - - // TODO: Implement a concurent test. + let lockedValue = Lock(value: [Int]()) - + DispatchQueue.main.async(group: nil, qos: .userInteractive, flags: []) { [lockedValue] in for num in rangeToLoop.reversed() { - lockedValue.edit() { + lockedValue.edit { $0.append(num) } } firstAccess.fulfill() } - + DispatchQueue.main.async(group: nil, qos: .userInteractive, flags: []) { [lockedValue] in - for num in rangeToLoop { - lockedValue.edit() { + for num in rangeToLoop { + lockedValue.edit { $0.append(num) } } secondAccess.fulfill() } - + wait(for: [firstAccess, secondAccess], timeout: 10.0) - + let newMap = lockedValue.value.reduce(into: [:]) { frequency, value in frequency[value, default: 0] += 1 } - + XCTAssert(newMap.count == rangeToLoop.count) - + // All values must appear exactly twice - XCTAssert(newMap.contains() { + XCTAssert(newMap.contains { $1 != 2 } == false) - + } - + func testParrallelRead() { - // TODO: Implement a concurent test. let rangeToLoop = 0...100000 let lockedValue = Lock(value: Array(rangeToLoop)) - + self.measure { let firstAccess = XCTestExpectation(description: "Dual Access 1") let secondAccess = XCTestExpectation(description: "Dual Access 1") - + //Operation - + DispatchQueue.main.async(group: nil, qos: .userInteractive, flags: []) { [lockedValue] in for num in rangeToLoop { XCTAssert(num == lockedValue.value[num]) } firstAccess.fulfill() } - + DispatchQueue.main.async(group: nil, qos: .userInteractive, flags: []) {[lockedValue] in for num in (rangeToLoop).reversed() { XCTAssert(num == lockedValue.value[num]) } secondAccess.fulfill() } - + for num in (rangeToLoop).reversed() { XCTAssert(num == lockedValue.value[num]) } - + wait(for: [firstAccess, secondAccess], timeout: 10.0) - + } } } diff --git a/Tests/SAKBaseTests/Math/RealComparisonTests.swift b/Tests/SAKBaseTests/Math/RealComparisonTests.swift index fdd4a2e..1e0afe8 100644 --- a/Tests/SAKBaseTests/Math/RealComparisonTests.swift +++ b/Tests/SAKBaseTests/Math/RealComparisonTests.swift @@ -10,12 +10,11 @@ import SAKBase class RealComparisonTests: XCTestCase { - // MARK: Comparison Tests func testNearEqualOperator() { // Can handle floats that should be equal and epislons great than the biggest compared float XCTAssertTrue(1.0.within(0.00000000000001, of: 1.0)) - + XCTAssertTrue(1.0.within(2.0, of: 0.1)) // Must treat Infinity as always equal From 13f2786c8d5ef756326ca23e6ad82edec2c1f321 Mon Sep 17 00:00:00 2001 From: Stephen Kac Date: Thu, 16 Jul 2020 11:36:51 -0600 Subject: [PATCH 13/26] Swift Lint rules --- .swiftlint.yml | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) create mode 100644 .swiftlint.yml diff --git a/.swiftlint.yml b/.swiftlint.yml new file mode 100644 index 0000000..41d5782 --- /dev/null +++ b/.swiftlint.yml @@ -0,0 +1,44 @@ +disabled_rules: # rule identifiers to exclude from running + - class_delegate_protocol +opt_in_rules: # some rules are only opt-in + - empty_count + - convenience_type + - empty_string + - fatal_error_message + - file_name + - first_where + - modifier_order + - private_action + - private_outlet + - toggle_bool + - unused_private_declaration + - array_init + - block_based_kvo + - closure_end_indentation + - closure_spacing + - collection_alignment + - contains_over_filter_count + - contains_over_filter_is_empty + - contains_over_first_not_nil + - contains_over_range_nil_comparison + - discouraged_optional_boolean + - discouraged_optional_collection + - empty_collection_literal +included: # paths to include during linting. `--path` is ignored if present. + - Sources + - Tests +excluded: # paths to ignore during linting. Takes precedence over `included`. + - Carthage + - Pods +analyzer_rules: # Rules run by `swiftlint analyze` (experimental) + - explicit_self +type_name: + min_length: 4 # only warning + max_length: # warning and error + warning: 40 + error: 50 +identifier_name: + excluded: # excluded via string array + - id + - URL +reporter: "xcode" From edb53e89be165c30ef98a8e11232445558787142 Mon Sep 17 00:00:00 2001 From: Stephen Kac Date: Thu, 16 Jul 2020 11:59:05 -0600 Subject: [PATCH 14/26] Updated addHandler to represent the no longer discardable result --- Sources/SAKBase/BaseTypes/Event.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Sources/SAKBase/BaseTypes/Event.swift b/Sources/SAKBase/BaseTypes/Event.swift index 3dadb0b..62328ec 100644 --- a/Sources/SAKBase/BaseTypes/Event.swift +++ b/Sources/SAKBase/BaseTypes/Event.swift @@ -38,7 +38,6 @@ public class Event { A handler that allows you to manually stop listenning to the event. You do not need to dispose upon deint of the Target. */ - @discardableResult public func addHandler( handler: @escaping Handler) -> DisposeContainer { let wrapper = EventHandlerWrapper(handler: handler, event: self) From d8232dd65c6cdacb6270e22bc472a8121026f5ad Mon Sep 17 00:00:00 2001 From: Stephen Kac Date: Thu, 16 Jul 2020 17:16:46 -0600 Subject: [PATCH 15/26] Added Weakify, Strongify, UnsafeWeakify --- SAKBase.xcodeproj/project.pbxproj | 40 ++++ Sources/SAKBase/Adapters/Strongify.swift | 81 +++++++ Sources/SAKBase/Adapters/UnsafeWeakify.swift | 69 ++++++ Sources/SAKBase/Adapters/Weakify.swift | 165 +++++++++++++++ .../SAKBaseTests/Adapters/StongifyTests.swift | 198 +++++++++++++++++ .../Adapters/UnsafeWeakifyTests.swift | 184 ++++++++++++++++ .../SAKBaseTests/Adapters/WeakifyTests.swift | 200 ++++++++++++++++++ 7 files changed, 937 insertions(+) create mode 100644 Sources/SAKBase/Adapters/Strongify.swift create mode 100644 Sources/SAKBase/Adapters/UnsafeWeakify.swift create mode 100644 Sources/SAKBase/Adapters/Weakify.swift create mode 100644 Tests/SAKBaseTests/Adapters/StongifyTests.swift create mode 100644 Tests/SAKBaseTests/Adapters/UnsafeWeakifyTests.swift create mode 100644 Tests/SAKBaseTests/Adapters/WeakifyTests.swift diff --git a/SAKBase.xcodeproj/project.pbxproj b/SAKBase.xcodeproj/project.pbxproj index 47a2121..d9a503e 100644 --- a/SAKBase.xcodeproj/project.pbxproj +++ b/SAKBase.xcodeproj/project.pbxproj @@ -11,6 +11,12 @@ CC1F5E4324BFD2640062B978 /* DisposableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC1F5E4124BFD25D0062B978 /* DisposableTests.swift */; }; CC1F5E4524C0379B0062B978 /* Point3.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC1F5E4424C0379B0062B978 /* Point3.swift */; }; CC1F5E4824C0B7FB0062B978 /* Point3Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC1F5E4624C039F50062B978 /* Point3Tests.swift */; }; + CC1F5E4C24C0D2800062B978 /* Weakify.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC1F5E4B24C0D2800062B978 /* Weakify.swift */; }; + CC2EC25824C0F0E1005FA6E8 /* WeakifyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC2EC25724C0F0E1005FA6E8 /* WeakifyTests.swift */; }; + CC2EC25B24C0FA46005FA6E8 /* Strongify.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC2EC25A24C0FA46005FA6E8 /* Strongify.swift */; }; + CC2EC25D24C10134005FA6E8 /* UnsafeWeakify.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC2EC25C24C10134005FA6E8 /* UnsafeWeakify.swift */; }; + CC2EC25F24C1025B005FA6E8 /* StongifyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC2EC25E24C1025B005FA6E8 /* StongifyTests.swift */; }; + CC2EC26124C10341005FA6E8 /* UnsafeWeakifyTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CC2EC26024C10341005FA6E8 /* UnsafeWeakifyTests.swift */; }; CC90BC1224AA7CD2002CB53F /* SAKBase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CC90BC0924AA7CD1002CB53F /* SAKBase.framework */; }; CC90BC2024AA7CE2002CB53F /* RealComparisonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0D223D6B3860027CD2B /* RealComparisonTests.swift */; }; CC90BC2124AA7CE2002CB53F /* EventTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CCC1C0D423D6B3860027CD2B /* EventTests.swift */; }; @@ -51,6 +57,12 @@ CC1F5E4124BFD25D0062B978 /* DisposableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DisposableTests.swift; sourceTree = ""; }; CC1F5E4424C0379B0062B978 /* Point3.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Point3.swift; sourceTree = ""; }; CC1F5E4624C039F50062B978 /* Point3Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Point3Tests.swift; sourceTree = ""; }; + CC1F5E4B24C0D2800062B978 /* Weakify.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Weakify.swift; sourceTree = ""; }; + CC2EC25724C0F0E1005FA6E8 /* WeakifyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeakifyTests.swift; sourceTree = ""; }; + CC2EC25A24C0FA46005FA6E8 /* Strongify.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Strongify.swift; sourceTree = ""; }; + CC2EC25C24C10134005FA6E8 /* UnsafeWeakify.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnsafeWeakify.swift; sourceTree = ""; }; + CC2EC25E24C1025B005FA6E8 /* StongifyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StongifyTests.swift; sourceTree = ""; }; + CC2EC26024C10341005FA6E8 /* UnsafeWeakifyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnsafeWeakifyTests.swift; sourceTree = ""; }; CC90BC0924AA7CD1002CB53F /* SAKBase.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SAKBase.framework; sourceTree = BUILT_PRODUCTS_DIR; }; CC90BC1124AA7CD2002CB53F /* SAKBaseTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SAKBaseTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; CC90BC3A24AA7D6B002CB53F /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -98,6 +110,26 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + CC1F5E4A24C0D1BB0062B978 /* Adapters */ = { + isa = PBXGroup; + children = ( + CC1F5E4B24C0D2800062B978 /* Weakify.swift */, + CC2EC25A24C0FA46005FA6E8 /* Strongify.swift */, + CC2EC25C24C10134005FA6E8 /* UnsafeWeakify.swift */, + ); + path = Adapters; + sourceTree = ""; + }; + CC2EC25924C0F0E6005FA6E8 /* Adapters */ = { + isa = PBXGroup; + children = ( + CC2EC25724C0F0E1005FA6E8 /* WeakifyTests.swift */, + CC2EC25E24C1025B005FA6E8 /* StongifyTests.swift */, + CC2EC26024C10341005FA6E8 /* UnsafeWeakifyTests.swift */, + ); + path = Adapters; + sourceTree = ""; + }; CCA0A4F124BE2B68007258BA /* Math */ = { isa = PBXGroup; children = ( @@ -209,6 +241,7 @@ CCC1C0D123D6B3860027CD2B /* SAKBaseTests */ = { isa = PBXGroup; children = ( + CC2EC25924C0F0E6005FA6E8 /* Adapters */, CCA0A4F124BE2B68007258BA /* Math */, CCA0A4F224BE2B9A007258BA /* BaseTypes */, CCA0A4F324BE2BA5007258BA /* Concurrency */, @@ -219,6 +252,7 @@ CCE7642224AA77C7002F2863 /* SAKBase */ = { isa = PBXGroup; children = ( + CC1F5E4A24C0D1BB0062B978 /* Adapters */, CCC1C0A923D6B3550027CD2B /* BaseTypes */, CCC1C0B123D6B3550027CD2B /* NSSwift */, CCC1C0B323D6B3550027CD2B /* Math */, @@ -374,9 +408,12 @@ buildActionMask = 2147483647; files = ( CC90BC2724AA7CE7002CB53F /* Stack.swift in Sources */, + CC1F5E4C24C0D2800062B978 /* Weakify.swift in Sources */, CC1F5E4024BFBDD60062B978 /* Disposable.swift in Sources */, CC90BC2B24AA7CEC002CB53F /* ProgressReporting.swift in Sources */, + CC2EC25D24C10134005FA6E8 /* UnsafeWeakify.swift in Sources */, CC90BC2524AA7CE7002CB53F /* Point.swift in Sources */, + CC2EC25B24C0FA46005FA6E8 /* Strongify.swift in Sources */, CC1F5E4524C0379B0062B978 /* Point3.swift in Sources */, CC90BC3124AA7CF7002CB53F /* String.swift in Sources */, CC90BC2E24AA7CF4002CB53F /* PThread.swift in Sources */, @@ -396,6 +433,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + CC2EC25F24C1025B005FA6E8 /* StongifyTests.swift in Sources */, CCA0A4FD24BE438F007258BA /* PointTests.swift in Sources */, CC1F5E4324BFD2640062B978 /* DisposableTests.swift in Sources */, CC90BC2124AA7CE2002CB53F /* EventTests.swift in Sources */, @@ -403,7 +441,9 @@ CC90BC2024AA7CE2002CB53F /* RealComparisonTests.swift in Sources */, CCA0A4F724BE2C0F007258BA /* StackTests.swift in Sources */, CCA0A4F924BE2C40007258BA /* BindingCommandTests.swift in Sources */, + CC2EC26124C10341005FA6E8 /* UnsafeWeakifyTests.swift in Sources */, CC1F5E4824C0B7FB0062B978 /* Point3Tests.swift in Sources */, + CC2EC25824C0F0E1005FA6E8 /* WeakifyTests.swift in Sources */, CCA0A4FB24BE2C6B007258BA /* CommandTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Sources/SAKBase/Adapters/Strongify.swift b/Sources/SAKBase/Adapters/Strongify.swift new file mode 100644 index 0000000..09d2216 --- /dev/null +++ b/Sources/SAKBase/Adapters/Strongify.swift @@ -0,0 +1,81 @@ +// +// Strongify.swift +// SAKBase +// +// Created by Stephen Kac on 7/16/20. +// Copyright © 2020 Stephen Kac. All rights reserved. +// + +import Foundation + +/** Takes a target and strongly captures it and runs the given closure body against it. + +Equivalent to: +``` +body(target) +``` +*/ +/// - Parameters: +/// - target: The target to apply the body too +/// - body: T +/// - Returns: The body to execute +@inlinable +public func strongify( + _ target: Target, + _ body: @escaping (Target) -> (Value) -> ReturnType) -> (Value) -> ReturnType { + return body(target) +} + +/** Takes a target and strongly captures it and runs the given closure body against it. + +Equivalent to: +``` +body(target) +``` +*/ +/// - Parameters: +/// - target: The target to apply the body too +/// - body: T +/// - Returns: The body to execute +@inlinable +public func strongify( + _ target: Target, + _ body: @escaping (Target) -> () -> Void) -> () -> Void { + return body(target) +} + +/** Takes a target and strongly captures it and runs the given closure body against it. + +Equivalent to: +``` +body(target) +``` +*/ +/// - Parameters: +/// - target: The target to apply the body too +/// - body: T +/// - Returns: The body to execute +@inlinable +public func strongify( + _ target: Target, + _ body: @escaping (Target) -> () -> ReturnType) -> () -> ReturnType { + return body(target) +} + +/** Takes a target and strongly captures it and runs the given closure body against it. + +Equivalent to: +``` +body(target) +``` +*/ +/// - Parameters: +/// - target: The target to apply the body too +/// - body: T +/// - Returns: The body to execute +@inlinable +public func strongify( + _ target: Target, + _ body: @escaping (Target) -> (Value) -> Void) -> (Value) -> Void { + return body(target) +} diff --git a/Sources/SAKBase/Adapters/UnsafeWeakify.swift b/Sources/SAKBase/Adapters/UnsafeWeakify.swift new file mode 100644 index 0000000..e26f88b --- /dev/null +++ b/Sources/SAKBase/Adapters/UnsafeWeakify.swift @@ -0,0 +1,69 @@ +// +// UnsafeWeakify.swift +// SAKBase +// +// Created by Stephen Kac on 7/16/20. +// Copyright © 2020 Stephen Kac. All rights reserved. +// + +import Foundation + +/** Takes a target and unsafely runs the given closure against it as if the target is unowned. + +Equivalent to: +``` +body(target) +``` +*/ +/// - Parameters: +/// - target: The target to apply the body too +/// - body: T +/// - Returns: The body to execute +@inlinable +public func unsafeWeakify( + _ target: Target, + _ body: @escaping (Target) -> (Value) -> ReturnType) -> (Value) -> ReturnType { + return { [unowned target] (value: Value) in + return body(target)(value) + } +} + +/** Takes a target and unsafely runs the given closure against it as if the target is unowned. + +Equivalent to: +``` +body(target) +``` +*/ +/// - Parameters: +/// - target: The target to apply the body too +/// - body: T +/// - Returns: The body to execute +@inlinable +public func unsafeWeakify( + _ target: Target, + _ body: @escaping (Target) -> (Value) -> Void) -> (Value) -> Void { + return { [unowned target] (value: Value) in + return body(target)(value) + } +} + +/** Takes a target and unsafely runs the given closure against it as if the target is unowned. + +Equivalent to: +``` +body(target) +``` +*/ +/// - Parameters: +/// - target: The target to apply the body too +/// - body: T +/// - Returns: The body to execute +@inlinable +public func unsafeWeakify( + _ target: Target, + _ body: @escaping (Target) -> () -> ReturnType) -> () -> ReturnType { + return { [unowned target] in + return body(target)() + } +} diff --git a/Sources/SAKBase/Adapters/Weakify.swift b/Sources/SAKBase/Adapters/Weakify.swift new file mode 100644 index 0000000..453e929 --- /dev/null +++ b/Sources/SAKBase/Adapters/Weakify.swift @@ -0,0 +1,165 @@ +// +// Weakify.swift +// SAKBase +// +// Created by Stephen Kac on 7/16/20. +// Copyright © 2020 Stephen Kac. All rights reserved. +// + +import Foundation + +/** Takes a target and weakly runs the given closure against it +Return the default value if nil. + +Equivalent to: +``` +[weak target] (value: Value) in + guard let target = target else { return defaultReturn } +return body(target)(value) +} +``` +*/ +/// - Parameters: +/// - target: The target to apply the body too +/// - defaultReturn: The value to return if target is nil +/// - body: T +/// - Returns: The body to execute +@inlinable +public func weakify( + _ target: Target?, + default defaultReturn: ReturnType, + _ body: @escaping (Target) -> ((Value) -> ReturnType)) -> (Value) -> ReturnType { + return { [weak target] (value: Value) in + guard let target = target else { return defaultReturn } + return body(target)(value) + } +} + +/** Takes a target and weakly runs the given closure against it. +If nil, does nothing. + +Equivalent to: +``` +[weak target] (value: Value) in +guard let target = target else { return } +return body(target)(value) +} +``` +*/ +/// - Parameters: +/// - target: The target to apply the body too +/// - body: T +/// - Returns: The body to execute +@inlinable +public func weakify( + _ target: Target?, + _ body: @escaping (Target) -> ((Value) -> Void)) -> (Value) -> Void { + return { [weak target] (value: Value) in + guard let target = target else { return } + return body(target)(value) + } +} + +/** Takes a target and weakly runs the given closure against it. +If nil, does nothing. + +Equivalent to: +``` +[weak target] in +guard let target = target else { return } +return body()(value) +} +``` +*/ +/// - Parameters: +/// - target: The target to apply the body too +/// - body: T +/// - Returns: The body to execute +@inlinable +public func weakify( + _ target: Target?, + _ body: @escaping (Target) -> (() -> Void)) -> () -> Void { + return { [weak target] in + guard let target = target else { return } + return body(target)() + } +} + +/** Takes a target and weakly runs the given closure against it. +If nil, does nothing. + +Equivalent to: +``` +[weak target] in +guard let target = target else { return } +return body()(value) +} +``` +*/ +/// - Parameters: +/// - target: The target to apply the body too +/// - body: T +/// - Returns: The body to execute +@inlinable +public func weakify( + _ target: Target?, + default defaultReturn: ReturnType, + _ body: @escaping (Target) -> (() -> ReturnType)) -> () -> ReturnType { + return { [weak target] in + guard let target = target else { return defaultReturn } + return body(target)() + } +} + +/** Takes a target and weakly runs the given closure against it. +If nil, does nothing. + +Equivalent to: +``` +[weak target] in +guard let target = target else { return } +return body()(value) +} +``` +*/ +/// - Parameters: +/// - target: The target to apply the body too +/// - body: T +/// - Returns: The body to execute +@inlinable +public func weakify( + _ target: Target?, + default defaultReturn: ReturnType? = nil, + _ body: @escaping (Target) -> (() -> ReturnType?)) -> () -> ReturnType? { + return { [weak target] in + guard let target = target else { return defaultReturn } + return body(target)() + } +} + +/** Takes a target and weakly runs the given closure against it +Return the default value if nil. If default value is not given, nil will be returned isntead. + +Equivalent to: +``` +[weak target] (value: Value) in +guard let target = target else { return defaultReturn } +return body(target)(value) +} +``` +*/ +/// - Parameters: +/// - target: The target to apply the body too +/// - defaultReturn: The value to return if target is nil +/// - body: T +/// - Returns: The body to execute +@inlinable +public func weakify( + _ target: Target?, + default defaultReturn: ReturnType? = nil, + _ body: @escaping (Target) -> ((Value) -> ReturnType?)) -> (Value) -> ReturnType? { + return { [weak target] (value: Value) in + guard let target = target else { return defaultReturn } + return body(target)(value) + } +} diff --git a/Tests/SAKBaseTests/Adapters/StongifyTests.swift b/Tests/SAKBaseTests/Adapters/StongifyTests.swift new file mode 100644 index 0000000..7101ece --- /dev/null +++ b/Tests/SAKBaseTests/Adapters/StongifyTests.swift @@ -0,0 +1,198 @@ +// +// StongifyTests.swift +// SAKBaseTests +// +// Created by Stephen Kac on 7/16/20. +// Copyright © 2020 Stephen Kac. All rights reserved. +// + +import XCTest +import SAKBase + +// swiftlint:disable discouraged_optional_boolean +class StongifyTests: XCTestCase { + + func test_strongify_void() { + + let closure: () -> Void + weak var spyWeak: StongifySpy? + + do { + let spy = StongifySpy() + spyWeak = spy + + closure = strongify(spy, StongifySpy.void) + closure() + XCTAssertEqual(spy.calls, 1) + } + + XCTAssertNotNil(spyWeak) + closure() + XCTAssertNotNil(spyWeak) + } + + func test_strongify_takesNoValueReturns() { + + let closure: () -> Bool + weak var spyWeak: StongifySpy? + + do { + let spy = StongifySpy() + spyWeak = spy + + closure = strongify(spy, + StongifySpy.takesNoValueReturns) + + XCTAssertNotNil(spyWeak) + XCTAssertTrue(closure()) + XCTAssertEqual(spyWeak?.calls, 1) + } + + XCTAssertNotNil(spyWeak) + XCTAssertTrue(closure()) + } + + func test_strongify_takesNoValueReturnsOptional() { + + let closure: () -> Bool? + weak var spyWeak: StongifySpy? + + do { + let spy = StongifySpy() + spyWeak = spy + + closure = strongify(spy, + StongifySpy.takesNoValueReturns) + + XCTAssertNotNil(spyWeak) + XCTAssertNotNil(closure()) + XCTAssertEqual(spyWeak?.calls, 1) + } + + XCTAssertNotNil(spyWeak) + XCTAssertNotNil(closure()) + } + + func test_strongify_takesValue() { + + let closure: (Bool) -> Void + weak var spyWeak: StongifySpy? + + do { + let spy = StongifySpy() + spyWeak = spy + + closure = strongify(spy, + StongifySpy.takesValue) + + XCTAssertNotNil(spyWeak) + closure(true) + XCTAssertEqual(spyWeak?.calls, 1) + } + + XCTAssertNotNil(spyWeak) + closure(true) + } + + func test_strongify_takesOptionalValue() { + + let closure: (Bool?) -> Void + weak var spyWeak: StongifySpy? + + do { + let spy = StongifySpy() + spyWeak = spy + + closure = strongify(spy, + StongifySpy.takesOptionalValue) + + XCTAssertNotNil(spyWeak) + closure(nil) + XCTAssertEqual(spyWeak?.calls, 1) + } + + XCTAssertNotNil(spyWeak) + closure(nil) + } + + func test_strongify_takesValueReturns() { + + let closure: (Bool) -> Bool + weak var spyWeak: StongifySpy? + + do { + let spy = StongifySpy() + spyWeak = spy + + closure = strongify(spy, + StongifySpy.takesValueReturns) + + XCTAssertNotNil(spyWeak) + XCTAssertEqual(closure(true), true) + XCTAssertEqual(spyWeak?.calls, 1) + } + + XCTAssertNotNil(spyWeak) + XCTAssertEqual(closure(false), true) + } + + func test_strongify_takesValueReturnsOptional() { + + let closure: (Bool) -> Bool? + weak var spyWeak: StongifySpy? + + do { + let spy = StongifySpy() + spyWeak = spy + + closure = strongify(spy, + StongifySpy.takesValueReturnsOptional) + + XCTAssertNotNil(spyWeak) + XCTAssertEqual(closure(true), true) + XCTAssertEqual(spyWeak?.calls, 1) + } + + XCTAssertNotNil(spyWeak) + XCTAssertNotNil(closure(true)) + } + +} + +private extension StongifyTests { + class StongifySpy { + var calls = 0 + + func void() { + calls += 1 + } + + func takesNoValueReturns() -> Bool { + calls += 1 + return true + } + + func takesNoValueReturnsOptional() -> Bool? { + calls += 1 + return true + } + + func takesValue(_: Bool) { + calls += 1 + } + + func takesOptionalValue(_: Bool?) { + calls += 1 + } + + func takesValueReturns(_: Bool) -> Bool { + calls += 1 + return true + } + + func takesValueReturnsOptional(_: Bool) -> Bool? { + calls += 1 + return true + } + } +} diff --git a/Tests/SAKBaseTests/Adapters/UnsafeWeakifyTests.swift b/Tests/SAKBaseTests/Adapters/UnsafeWeakifyTests.swift new file mode 100644 index 0000000..da95d94 --- /dev/null +++ b/Tests/SAKBaseTests/Adapters/UnsafeWeakifyTests.swift @@ -0,0 +1,184 @@ +// +// UnsafeWeakifyTests.swift +// SAKBaseTests +// +// Created by Stephen Kac on 7/16/20. +// Copyright © 2020 Stephen Kac. All rights reserved. +// + +import XCTest +import SAKBase + +// swiftlint:disable discouraged_optional_boolean +class UnsafeWeakifyTests: XCTestCase { + + func test_unsafeWeakify_void() { + + let closure: () -> Void + weak var spyWeak: UnsafeWeakifySpy? + + do { + let spy = UnsafeWeakifySpy() + spyWeak = spy + + closure = unsafeWeakify(spy, UnsafeWeakifySpy.void) + closure() + XCTAssertEqual(spy.calls, 1) + } + + XCTAssertNil(spyWeak) + } + + func test_unsafeWeakify_takesNoValueReturns() { + + let closure: () -> Bool + weak var spyWeak: UnsafeWeakifySpy? + + do { + let spy = UnsafeWeakifySpy() + spyWeak = spy + + closure = unsafeWeakify(spy, + UnsafeWeakifySpy.takesNoValueReturns) + + XCTAssertEqual(closure(), true) + XCTAssertEqual(spy.calls, 1) + } + + XCTAssertNil(spyWeak) + } + + func test_unsafeWeakify_takesNoValueReturnsOptional() { + + let closure: () -> Bool? + weak var spyWeak: UnsafeWeakifySpy? + + do { + let spy = UnsafeWeakifySpy() + spyWeak = spy + + closure = unsafeWeakify(spy, + UnsafeWeakifySpy.takesNoValueReturnsOptional) + + XCTAssertEqual(closure(), true) + XCTAssertEqual(spy.calls, 1) + } + + XCTAssertNil(spyWeak) + } + + func test_unsafeWeakify_takesValue() { + + let closure: (Bool) -> Void + weak var spyWeak: UnsafeWeakifySpy? + + do { + let spy = UnsafeWeakifySpy() + spyWeak = spy + + closure = unsafeWeakify(spy, + UnsafeWeakifySpy.takesValue) + + closure(true) + XCTAssertEqual(spy.calls, 1) + } + + XCTAssertNil(spyWeak) + } + + func test_unsafeWeakify_takesOptionalValue() { + + let closure: (Bool?) -> Void + weak var spyWeak: UnsafeWeakifySpy? + + do { + let spy = UnsafeWeakifySpy() + spyWeak = spy + + closure = unsafeWeakify(spy, + UnsafeWeakifySpy.takesOptionalValue) + + closure(nil) + XCTAssertEqual(spy.calls, 1) + } + + XCTAssertNil(spyWeak) + } + + func test_unsafeWeakify_takesValueReturns() { + + let closure: (Bool) -> Bool + weak var spyWeak: UnsafeWeakifySpy? + + do { + let spy = UnsafeWeakifySpy() + spyWeak = spy + + closure = unsafeWeakify(spy, + UnsafeWeakifySpy.takesValueReturns) + + XCTAssertEqual(closure(true), true) + XCTAssertEqual(spy.calls, 1) + } + + XCTAssertNil(spyWeak) + } + + func test_unsafeWeakify_takesValueReturnsOptional() { + + let closure: (Bool) -> Bool? + weak var spyWeak: UnsafeWeakifySpy? + + do { + let spy = UnsafeWeakifySpy() + spyWeak = spy + + closure = unsafeWeakify(spy, + UnsafeWeakifySpy.takesValueReturnsOptional) + + XCTAssertNotNil(closure(true)) + XCTAssertEqual(spy.calls, 1) + } + + XCTAssertNil(spyWeak) + } + +} + +private extension UnsafeWeakifyTests { + class UnsafeWeakifySpy { + var calls = 0 + + func void() { + calls += 1 + } + + func takesNoValueReturns() -> Bool { + calls += 1 + return true + } + + func takesNoValueReturnsOptional() -> Bool? { + calls += 1 + return true + } + + func takesValue(_: Bool) { + calls += 1 + } + + func takesOptionalValue(_: Bool?) { + calls += 1 + } + + func takesValueReturns(_: Bool) -> Bool { + calls += 1 + return true + } + + func takesValueReturnsOptional(_: Bool) -> Bool? { + calls += 1 + return true + } +} +} diff --git a/Tests/SAKBaseTests/Adapters/WeakifyTests.swift b/Tests/SAKBaseTests/Adapters/WeakifyTests.swift new file mode 100644 index 0000000..62dff76 --- /dev/null +++ b/Tests/SAKBaseTests/Adapters/WeakifyTests.swift @@ -0,0 +1,200 @@ +// +// WeakifyTests.swift +// SAKBaseTests +// +// Created by Stephen Kac on 7/16/20. +// Copyright © 2020 Stephen Kac. All rights reserved. +// + +import XCTest +import SAKBase + +// swiftlint:disable discouraged_optional_boolean +class WeakifyTests: XCTestCase { + + func test_weakify_void() { + + let closure: () -> Void + weak var spyWeak: WeakifySpy? + + do { + let spy = WeakifySpy() + spyWeak = spy + + closure = weakify(spy, WeakifySpy.void) + closure() + XCTAssertEqual(spy.calls, 1) + } + + XCTAssertNil(spyWeak) + closure() + XCTAssertNil(spyWeak) + } + + func test_weakify_takesNoValueReturns() { + + let closure: () -> Bool + weak var spyWeak: WeakifySpy? + + do { + let spy = WeakifySpy() + spyWeak = spy + + closure = weakify(spy, + default: false, + WeakifySpy.takesNoValueReturns) + + XCTAssertNotNil(spyWeak) + XCTAssertTrue(closure()) + XCTAssertEqual(spyWeak?.calls, 1) + } + + XCTAssertNil(spyWeak) + XCTAssertFalse(closure()) + } + + func test_weakify_takesNoValueReturnsOptional() { + + let closure: () -> Bool? + weak var spyWeak: WeakifySpy? + + do { + let spy = WeakifySpy() + spyWeak = spy + + closure = weakify(spy, + WeakifySpy.takesNoValueReturnsOptional) + + XCTAssertNotNil(spyWeak) + XCTAssertNotNil(closure()) + XCTAssertEqual(spyWeak?.calls, 1) + } + + XCTAssertNil(spyWeak) + XCTAssertNil(closure()) + } + + func test_weakify_takesValue() { + + let closure: (Bool) -> Void + weak var spyWeak: WeakifySpy? + + do { + let spy = WeakifySpy() + spyWeak = spy + + closure = weakify(spy, + WeakifySpy.takesValue) + + XCTAssertNotNil(spyWeak) + closure(true) + XCTAssertEqual(spyWeak?.calls, 1) + } + + XCTAssertNil(spyWeak) + closure(true) + } + + func test_weakify_takesOptionalValue() { + + let closure: (Bool?) -> Void + weak var spyWeak: WeakifySpy? + + do { + let spy = WeakifySpy() + spyWeak = spy + + closure = weakify(spy, + WeakifySpy.takesOptionalValue) + + XCTAssertNotNil(spyWeak) + closure(nil) + XCTAssertEqual(spyWeak?.calls, 1) + } + + XCTAssertNil(spyWeak) + closure(nil) + } + + func test_weakify_takesValueReturns() { + + let closure: (Bool) -> Bool + weak var spyWeak: WeakifySpy? + + do { + let spy = WeakifySpy() + spyWeak = spy + + closure = weakify(spy, + default: false, + WeakifySpy.takesValueReturns) + + XCTAssertNotNil(spyWeak) + XCTAssertEqual(closure(true), true) + XCTAssertEqual(spyWeak?.calls, 1) + } + + XCTAssertNil(spyWeak) + XCTAssertEqual(closure(true), false) + } + + func test_weakify_takesValueReturnsOptional() { + + let closure: (Bool) -> Bool? + weak var spyWeak: WeakifySpy? + + do { + let spy = WeakifySpy() + spyWeak = spy + + closure = weakify(spy, + WeakifySpy.takesValueReturnsOptional) + + XCTAssertNotNil(spyWeak) + XCTAssertEqual(closure(true), true) + XCTAssertEqual(spyWeak?.calls, 1) + } + + XCTAssertNil(spyWeak) + XCTAssertNil(closure(true)) + } + +} + +private extension WeakifyTests { + class WeakifySpy { + var calls = 0 + + func void() { + calls += 1 + } + + func takesNoValueReturns() -> Bool { + calls += 1 + return true + } + + func takesNoValueReturnsOptional() -> Bool? { + calls += 1 + return true + } + + func takesValue(_: Bool) { + calls += 1 + } + + func takesOptionalValue(_: Bool?) { + calls += 1 + } + + func takesValueReturns(_: Bool) -> Bool { + calls += 1 + return true + } + + func takesValueReturnsOptional(_: Bool) -> Bool? { + calls += 1 + return true + } + } +} From daad7256d300cac82f1eba0c967c49a53c66d4e1 Mon Sep 17 00:00:00 2001 From: Stephen Kac Date: Thu, 16 Jul 2020 17:46:32 -0600 Subject: [PATCH 16/26] Fixed stack unit test --- Tests/SAKBaseTests/BaseTypes/StackTests.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Tests/SAKBaseTests/BaseTypes/StackTests.swift b/Tests/SAKBaseTests/BaseTypes/StackTests.swift index 1f52234..18c98c4 100644 --- a/Tests/SAKBaseTests/BaseTypes/StackTests.swift +++ b/Tests/SAKBaseTests/BaseTypes/StackTests.swift @@ -26,6 +26,7 @@ class StackTests: XCTestCase { XCTAssertEqual(stack.top, 3) XCTAssertEqual(stack.pop(), 3) XCTAssertEqual(stack.top, 1) + XCTAssertEqual(stack.pop(), 1) XCTAssertTrue(stack.isEmpty) XCTAssertNil(stack.pop()) From 7a21fd6cb581bdf1670ed11756795297a67ebdbd Mon Sep 17 00:00:00 2001 From: Stephen Kac Date: Fri, 1 Jan 2021 11:23:34 -0600 Subject: [PATCH 17/26] Swift min version is now 5.3 --- Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index a0ddd42..c49079e 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.1 +// swift-tools-version:5.3 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription From d997e137b35da956891d4764c34e6d5fba9731f9 Mon Sep 17 00:00:00 2001 From: Stephen Kac Date: Fri, 1 Jan 2021 12:34:58 -0600 Subject: [PATCH 18/26] Fixed strong reference cycle and added test regression --- Sources/SAKBase/BaseTypes/Event.swift | 2 +- Tests/SAKBaseTests/BaseTypes/EventTests.swift | 23 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/Sources/SAKBase/BaseTypes/Event.swift b/Sources/SAKBase/BaseTypes/Event.swift index 62328ec..656b3ee 100644 --- a/Sources/SAKBase/BaseTypes/Event.swift +++ b/Sources/SAKBase/BaseTypes/Event.swift @@ -59,7 +59,7 @@ private class EventHandlerWrapper: Disposable, Identifiable { let id = UUID() let handler: Handler - let event: Event + weak var event: Event? init(handler: @escaping Handler, event: Event) { self.handler = handler diff --git a/Tests/SAKBaseTests/BaseTypes/EventTests.swift b/Tests/SAKBaseTests/BaseTypes/EventTests.swift index cf368eb..0fc62e9 100644 --- a/Tests/SAKBaseTests/BaseTypes/EventTests.swift +++ b/Tests/SAKBaseTests/BaseTypes/EventTests.swift @@ -12,6 +12,29 @@ import SAKBase class EventTests: XCTestCase { weak var eventHandler: Disposable? + + func test_event_doesdeallocate() { + class HandlerSpy { + func handle(_ value: Int) {} + } + + weak var weakEvent: Event? + weak var weakHandler: HandlerSpy? + + do { + let event = Event() + weakEvent = event + + let handler = HandlerSpy() + weakHandler = handler + + _ = event.addHandler(handler: { handler.handle($0) }) + .removeDisposableFromContainer() + } + + XCTAssertNil(weakEvent) + XCTAssertNil(weakHandler) + } func test_event_doesGetDisposed() { From c34600727663364cb2bf3fdf453a8f97fb12cf5d Mon Sep 17 00:00:00 2001 From: Stephen Kac Date: Fri, 1 Jan 2021 12:51:42 -0600 Subject: [PATCH 19/26] Documented Event --- Sources/SAKBase/BaseTypes/Event.swift | 47 ++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/Sources/SAKBase/BaseTypes/Event.swift b/Sources/SAKBase/BaseTypes/Event.swift index 656b3ee..0d23d94 100644 --- a/Sources/SAKBase/BaseTypes/Event.swift +++ b/Sources/SAKBase/BaseTypes/Event.swift @@ -14,20 +14,28 @@ import Foundation /// If a handle throws an error then it will also be removed silently. public class Event { - fileprivate typealias EventHandler = EventHandlerWrapper + /// The type of the event handlers + private typealias EventHandler = EventHandlerWrapper + + /// This closure is responseible for handling the raised events data when invoked. public typealias Handler = (DataType) throws -> Void + + /// The handlers to be invoked once the event has been raised with a value + private var eventHandlers = Set() - fileprivate var eventHandlers = Set() - + /// The count of the current handlers public var handlerCount: Int { eventHandlers.count } + /// init public init() {} - - public func raise(_ data: DataType) { + + /// Raises the event causing all events to be called with the data from data. data is called to generate the data once per handler/listener for the event. + /// - Parameter data: The data to pass to all events, it has the unique attribute of only being called once per handler. + public func raise(_ data: @autoclosure () -> DataType) { for handler in self.eventHandlers { - handler.invoke(data) + handler.invoke(data()) } } @@ -45,6 +53,7 @@ public class Event { return DisposeContainer(toBeDisposed: wrapper) } + /// deinit deinit { for handler in eventHandlers { handler.dispose() @@ -52,34 +61,56 @@ public class Event { } } +/// An internal type used to wrap a handler and the event type. private class EventHandlerWrapper: Disposable, Identifiable { + /// This closure is responseible for handling the events data when invoked. typealias Handler = (DataType) throws -> Void + // The id of the EventHandlerWrapper used to remove itself from its event let id = UUID() + /// The handler for any events that happen let handler: Handler + + /// The event the wrapper belongs too weak var event: Event? - + + /// Creates an EventHandlerWrapper with the handler to handle the invocation and the event the wrapper belongs too + /// - Parameters: + /// - handler: The handler to call when invoked + /// - event: The event the EventHandlerWrapper belongs too init(handler: @escaping Handler, event: Event) { self.handler = handler self.event = event } + /// Invokes the handler for stored event type, disposes itself if the handler throws + /// - Parameter data: The data to invoke the handler with func invoke(_ data: DataType) { guard (try? handler(data)) != nil else { dispose(); return } } + + /// Removes the eventhandler from its event which will allow it to deallocate public func dispose() { - event.eventHandlers.remove(self) + event?.eventHandlers.remove(self) } } extension EventHandlerWrapper: Hashable { + + /// - Parameters: + /// - lhs: The left object of the comparator + /// - rhs: The object on the right of the comparator + /// - Returns: Wether or not the ids of the two objects match static func == (lhs: EventHandlerWrapper, rhs: EventHandlerWrapper) -> Bool { lhs.id == rhs.id } + + /// Creates a hash using the id + /// - Parameter hasher: The haser to hash into func hash(into hasher: inout Hasher) { hasher.combine(id) } From fc20dd49a3a344acf46ab26d6b23621baad00b31 Mon Sep 17 00:00:00 2001 From: Stephen Kac Date: Fri, 1 Jan 2021 12:51:49 -0600 Subject: [PATCH 20/26] Documented Disposable --- Sources/SAKBase/BaseTypes/Disposable.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Sources/SAKBase/BaseTypes/Disposable.swift b/Sources/SAKBase/BaseTypes/Disposable.swift index f4103b6..2414e20 100644 --- a/Sources/SAKBase/BaseTypes/Disposable.swift +++ b/Sources/SAKBase/BaseTypes/Disposable.swift @@ -8,8 +8,10 @@ /// Automatically disposes of a event upon deinit public class DisposeContainer: Disposable { + /// The object that is to have its dispose called upon deinit private weak var toBeDisposed: Disposable? + /// - Parameter toBeDisposed: The object that is to have its dispose called upon deinit public init(toBeDisposed: Disposable) { self.toBeDisposed = toBeDisposed } @@ -34,5 +36,7 @@ public class DisposeContainer: Disposable { /// Lets you dispose of a connected type. public protocol Disposable: AnyObject { + + /// Tells the object to dispose itself func dispose() } From 46efe9f9dc8c86fcbdab31fde9a3514cd976c834 Mon Sep 17 00:00:00 2001 From: Stephen Kac Date: Fri, 1 Jan 2021 12:51:57 -0600 Subject: [PATCH 21/26] Documented Binding Command --- Sources/SAKBase/BaseTypes/BindingCommand.swift | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Sources/SAKBase/BaseTypes/BindingCommand.swift b/Sources/SAKBase/BaseTypes/BindingCommand.swift index da8e4a2..9c93690 100644 --- a/Sources/SAKBase/BaseTypes/BindingCommand.swift +++ b/Sources/SAKBase/BaseTypes/BindingCommand.swift @@ -14,6 +14,11 @@ Used for a command responder chain in order to allow responsibility to be passed but retain a strong reference to all captured types. */ public protocol BindingCommandResponder { + + /// Called on the next responder in the chain. + /// - Parameters: + /// - command: The command to be run by the responder or to be passed to another responder in the chain + /// - sender: The sender of the command func recieve(_ command: BindingCommand, sender: Any?) } @@ -31,8 +36,11 @@ public final class BindingCommand { /// The command that will undo the execCommand private let undoCommand: (() -> Bool)? - + /// Initializes the Command with an executable closure and a closure to undo it. + /// - Parameters: + /// - exec: The closure to be used for execution + /// - undo: The closue to be used to undo the state changes caused by the exec closure public init(exec: @escaping () -> Bool, undo: (() -> Bool)? = nil ) { execCommand = exec undoCommand = undo From cd709a7988a96fb5f6d0687973abb30aa21a1823 Mon Sep 17 00:00:00 2001 From: Stephen Kac Date: Fri, 1 Jan 2021 12:52:18 -0600 Subject: [PATCH 22/26] Minimum macOS target is now 10.15 --- Package.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Package.swift b/Package.swift index c49079e..5b8d080 100644 --- a/Package.swift +++ b/Package.swift @@ -5,7 +5,7 @@ import PackageDescription let package = Package( name: "SAKBase", - platforms: [.macOS(.v10_14), + platforms: [.macOS(.v10_15), .iOS(.v13), .tvOS(.v13), .watchOS(.v6) From 15b8c7f56083a8bfcd3548822af8357b687af9e7 Mon Sep 17 00:00:00 2001 From: Stephen Kac Date: Fri, 1 Jan 2021 13:05:27 -0600 Subject: [PATCH 23/26] Adjusted access specifiers for events --- Sources/SAKBase/BaseTypes/Event.swift | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Sources/SAKBase/BaseTypes/Event.swift b/Sources/SAKBase/BaseTypes/Event.swift index 0d23d94..35c804d 100644 --- a/Sources/SAKBase/BaseTypes/Event.swift +++ b/Sources/SAKBase/BaseTypes/Event.swift @@ -15,7 +15,7 @@ import Foundation public class Event { /// The type of the event handlers - private typealias EventHandler = EventHandlerWrapper + fileprivate typealias EventHandler = EventHandlerWrapper /// This closure is responseible for handling the raised events data when invoked. public typealias Handler = (DataType) throws -> Void @@ -47,11 +47,16 @@ public class Event { You do not need to dispose upon deint of the Target. */ public func addHandler( - handler: @escaping Handler) -> DisposeContainer { + handler: @escaping Handler) -> some DisposeContainer { let wrapper = EventHandlerWrapper(handler: handler, event: self) eventHandlers.insert(wrapper) return DisposeContainer(toBeDisposed: wrapper) } + + /// Removes the given handler + fileprivate func remove(handler: EventHandler) { + eventHandlers.remove(handler) + } /// deinit deinit { @@ -94,7 +99,7 @@ private class EventHandlerWrapper: Disposable, Identifiable { /// Removes the eventhandler from its event which will allow it to deallocate public func dispose() { - event?.eventHandlers.remove(self) + event?.remove(handler: self) } } From 120e7e765880548c110ee6eab69b9991e9a07e59 Mon Sep 17 00:00:00 2001 From: Stephen Kac Date: Fri, 1 Jan 2021 13:17:50 -0600 Subject: [PATCH 24/26] Swift-doc --- Documentation/AsyncOperation.md | 58 ++++++++++++ Documentation/BindingCommand.md | 59 ++++++++++++ Documentation/BindingCommandResponder.md | 24 +++++ Documentation/Command.md | 104 +++++++++++++++++++++ Documentation/Disposable.md | 21 +++++ Documentation/DisposeContainer.md | 41 +++++++++ Documentation/EquivalentIntegerSize.md | 9 ++ Documentation/Event.md | 71 +++++++++++++++ Documentation/GroupOperation.md | 25 ++++++ Documentation/Home.md | 86 ++++++++++++++++++ Documentation/PThread.md | 7 ++ Documentation/PThread_ReadWriteLock.md | 35 ++++++++ Documentation/Point.md | 29 ++++++ Documentation/Point3.md | 31 +++++++ Documentation/ProgressReporting.md | 13 +++ Documentation/Stack.md | 53 +++++++++++ Documentation/_Footer.md | 1 + Documentation/_Sidebar.md | 46 ++++++++++ Documentation/strongify(_:_:).md | 91 +++++++++++++++++++ Documentation/unsafeWeakify(_:_:).md | 68 ++++++++++++++ Documentation/weakify(_:_:).md | 53 +++++++++++ Documentation/weakify(_:default:_:).md | 109 +++++++++++++++++++++++ 22 files changed, 1034 insertions(+) create mode 100644 Documentation/AsyncOperation.md create mode 100644 Documentation/BindingCommand.md create mode 100644 Documentation/BindingCommandResponder.md create mode 100644 Documentation/Command.md create mode 100644 Documentation/Disposable.md create mode 100644 Documentation/DisposeContainer.md create mode 100644 Documentation/EquivalentIntegerSize.md create mode 100644 Documentation/Event.md create mode 100644 Documentation/GroupOperation.md create mode 100644 Documentation/Home.md create mode 100644 Documentation/PThread.md create mode 100644 Documentation/PThread_ReadWriteLock.md create mode 100644 Documentation/Point.md create mode 100644 Documentation/Point3.md create mode 100644 Documentation/ProgressReporting.md create mode 100644 Documentation/Stack.md create mode 100644 Documentation/_Footer.md create mode 100644 Documentation/_Sidebar.md create mode 100644 Documentation/strongify(_:_:).md create mode 100644 Documentation/unsafeWeakify(_:_:).md create mode 100644 Documentation/weakify(_:_:).md create mode 100644 Documentation/weakify(_:default:_:).md diff --git a/Documentation/AsyncOperation.md b/Documentation/AsyncOperation.md new file mode 100644 index 0000000..b67595d --- /dev/null +++ b/Documentation/AsyncOperation.md @@ -0,0 +1,58 @@ +# AsyncOperation + +Code taken from https:​//medium.com/flawless-app-stories/parallel-programming-with-swift-operations-54cbefaf3cb0 +Overide the exec method in order to use. + +``` swift +open class AsyncOperation: Operation +``` + +## Inheritance + +`Operation` + +## Properties + +### `isFinishedEvent` + +``` swift +var isFinishedEvent +``` + +### `isExecutingEvent` + +``` swift +var isExecutingEvent +``` + +### `isAsynchronous` + +``` swift +var isAsynchronous: Bool +``` + +### `isFinished` + +``` swift +var isFinished: Bool +``` + +### `isExecuting` + +``` swift +var isExecuting: Bool +``` + +## Methods + +### `execute()` + +``` swift +open func execute() +``` + +### `start()` + +``` swift +override open func start() +``` diff --git a/Documentation/BindingCommand.md b/Documentation/BindingCommand.md new file mode 100644 index 0000000..b892966 --- /dev/null +++ b/Documentation/BindingCommand.md @@ -0,0 +1,59 @@ +# BindingCommand + +Abstract:​ +Is used to give buttons and inputs actions that they will incur. Unlike the Command, this class is static. + +``` swift +public final class BindingCommand +``` + +## Initializers + +### `init(exec:undo:)` + +Initializes the Command with an executable closure and a closure to undo it. + +``` swift +public init(exec: @escaping () -> Bool, undo: (() -> Bool)? = nil) +``` + +#### Parameters + + - exec: The closure to be used for execution + - undo: The closue to be used to undo the state changes caused by the exec closure + +## Properties + +### `name` + +The name of the command + +``` swift +var name: String? +``` + +## Methods + +### `execute()` + +Executes the command if actor is non-nil. + +``` swift +@discardableResult public func execute() -> Bool +``` + +#### Returns + +a discardable `Bool` describing if the action was successful + +### `undo()` + +Does the undo variant of the command if undo is set with the last value set. + +``` swift +@discardableResult public func undo() -> Bool +``` + +#### Returns + +a discardable `Bool` describing if the action was successful diff --git a/Documentation/BindingCommandResponder.md b/Documentation/BindingCommandResponder.md new file mode 100644 index 0000000..e49f1ab --- /dev/null +++ b/Documentation/BindingCommandResponder.md @@ -0,0 +1,24 @@ +# BindingCommandResponder + +Abstract:​ +Used for a command responder chain in order to allow responsibility to be passed +but retain a strong reference to all captured types. + +``` swift +public protocol BindingCommandResponder +``` + +## Requirements + +### recieve(\_:​sender:​) + +Called on the next responder in the chain. + +``` swift +func recieve(_ command: BindingCommand, sender: Any?) +``` + +#### Parameters + + - command: The command to be run by the responder or to be passed to another responder in the chain + - sender: The sender of the command diff --git a/Documentation/Command.md b/Documentation/Command.md new file mode 100644 index 0000000..e0e881a --- /dev/null +++ b/Documentation/Command.md @@ -0,0 +1,104 @@ +# Command + +Abstract:​ +Is used to give buttons and inputs actions that they will incur. +This is helpful if you dont want to create a new command for every incurrence of the command with a different value + +``` swift +public final class Command +``` + +## Initializers + +### `init(actor:value:exec:)` + +Initializes the Command with an executable closure and the required default values to run the command + +``` swift +public convenience init(actor: ActorType?, value: ValueType?, exec: @escaping (ActorType, ValueType?) -> Bool) +``` + +### `init(actor:value:exec:undo:)` + +Initializes the Command with an executable closure and an undo closure + +``` swift +public required init(actor: ActorType?, value: ValueType?, exec: @escaping (ActorType, ValueType?) -> Bool, undo: ((ActorType, ValueType?) -> Bool)?) +``` + +## Properties + +### `undoCommand` + +The command that will undo the execCommand + +``` swift +let undoCommand: ((ActorType, ValueType?) -> Bool)? +``` + +### `value` + +This is the value that will be used in during the exec or undo commands + +``` swift +var value: ValueType? +``` + +### `actor` + +This is the current actor the comamnds will act upon + +``` swift +var actor: ActorType? +``` + +## Methods + +### `execute(with:)` + +Executes the command with the new value if actor is non-nil. + +``` swift +@discardableResult public func execute(with newValue: ValueType) -> Bool +``` + +#### Returns + +a discardable `Bool` describing if the action was successful + +### `execute()` + +Executes the command if actor is non-nil. + +``` swift +@discardableResult public func execute() -> Bool +``` + +#### Returns + +a discardable `Bool` describing if the action was successful + +### `undo()` + +Does the undo variant of the command if undo is set with the last value set. + +``` swift +@discardableResult public func undo() -> Bool +``` + +#### Returns + +a discardable `Bool` describing if the action was successful + +### `getUndo()` + +Gets a closure that can undo the last exec comamnd. +This can be helpful in queing up a list of commands that can undo previous actions. + +``` swift +@discardableResult public func getUndo() -> (() -> Bool)? +``` + +#### Returns + +a closure that returns bool for wether it was successful or not diff --git a/Documentation/Disposable.md b/Documentation/Disposable.md new file mode 100644 index 0000000..3c25957 --- /dev/null +++ b/Documentation/Disposable.md @@ -0,0 +1,21 @@ +# Disposable + +Lets you dispose of a connected type. + +``` swift +public protocol Disposable: AnyObject +``` + +## Inheritance + +`AnyObject` + +## Requirements + +### dispose() + +Tells the object to dispose itself + +``` swift +func dispose() +``` diff --git a/Documentation/DisposeContainer.md b/Documentation/DisposeContainer.md new file mode 100644 index 0000000..f4ebb13 --- /dev/null +++ b/Documentation/DisposeContainer.md @@ -0,0 +1,41 @@ +# DisposeContainer + +Automatically disposes of a event upon deinit + +``` swift +public class DisposeContainer: Disposable +``` + +## Inheritance + +[`Disposable`](./Disposable.md) + +## Initializers + +### `init(toBeDisposed:)` + +``` swift +public init(toBeDisposed: Disposable) +``` + +#### Parameters + + - toBeDisposed: The object that is to have its dispose called upon deinit + +## Methods + +### `removeDisposableFromContainer()` + +Removes the responsibility of disposing the related class from the container + +``` swift +public func removeDisposableFromContainer() -> Disposable? +``` + +### `dispose()` + +Manually disposes of the contained disposable + +``` swift +public func dispose() +``` diff --git a/Documentation/EquivalentIntegerSize.md b/Documentation/EquivalentIntegerSize.md new file mode 100644 index 0000000..1caf1c0 --- /dev/null +++ b/Documentation/EquivalentIntegerSize.md @@ -0,0 +1,9 @@ +# EquivalentIntegerSize + +``` swift +public protocol EquivalentIntegerSize: BinaryFloatingPoint +``` + +## Inheritance + +`BinaryFloatingPoint` diff --git a/Documentation/Event.md b/Documentation/Event.md new file mode 100644 index 0000000..6d3e3dd --- /dev/null +++ b/Documentation/Event.md @@ -0,0 +1,71 @@ +# Event + +Provides an interface to raise events in a more performant and pure Swift manner. +When raising an event all handlers will be called witht data from the event. +If the returned DisposeContainer leaves its scope, it will remove its related event. +If a handle throws an error then it will also be removed silently. + +``` swift +public class Event +``` + +## Nested Type Aliases + +### `Handler` + +This closure is responseible for handling the raised events data when invoked. + +``` swift +public typealias Handler = (DataType) throws -> Void +``` + +## Initializers + +### `init()` + +init + +``` swift +public init() +``` + +## Properties + +### `handlerCount` + +The count of the current handlers + +``` swift +var handlerCount: Int +``` + +## Methods + +### `raise(_:)` + +Raises the event causing all events to be called with the data from data. data is called to generate the data once per handler/listener for the event. + +``` swift +public func raise(_ data: @autoclosure () -> DataType) +``` + +#### Parameters + + - data: The data to pass to all events, it has the unique attribute of only being called once per handler. + +### `addHandler(handler:)` + +Adds a handler that will be called when the event is raised. + +``` swift +public func addHandler(handler: @escaping Handler) -> some DisposeContainer +``` + +#### Parameters + + - target: An Object passed to the handler to act on or with, usually the interested class. + - handler: A block of code to run with the target and data raised. + +#### Returns + +A handler that allows you to manually stop listenning to the event. You do not need to dispose upon deint of the Target. diff --git a/Documentation/GroupOperation.md b/Documentation/GroupOperation.md new file mode 100644 index 0000000..18cbd6e --- /dev/null +++ b/Documentation/GroupOperation.md @@ -0,0 +1,25 @@ +# GroupOperation + +``` swift +public class GroupOperation: AsyncOperation +``` + +## Inheritance + +[`AsyncOperation`](./AsyncOperation.md) + +## Properties + +### `operations` + +``` swift +var operations: [AsyncOperation] = [] +``` + +## Methods + +### `execute()` + +``` swift +override public func execute() +``` diff --git a/Documentation/Home.md b/Documentation/Home.md new file mode 100644 index 0000000..0e8cf09 --- /dev/null +++ b/Documentation/Home.md @@ -0,0 +1,86 @@ +# Types + + - [BindingCommand](./BindingCommand.md): + Abstract: + Is used to give buttons and inputs actions that they will incur. Unlike the Command, this class is static. + - [Command](./Command.md): + Abstract: + Is used to give buttons and inputs actions that they will incur. + This is helpful if you dont want to create a new command for every incurrence of the command with a different value + - [DisposeContainer](./DisposeContainer.md): + Automatically disposes of a event upon deinit + - [Event](./Event.md): + Provides an interface to raise events in a more performant and pure Swift manner. + When raising an event all handlers will be called witht data from the event. + If the returned DisposeContainer leaves its scope, it will remove its related event. + If a handle throws an error then it will also be removed silently. + - [Stack](./Stack.md) + - [AsyncOperation](./AsyncOperation.md): + Code taken from https://medium.com/flawless-app-stories/parallel-programming-with-swift-operations-54cbefaf3cb0 + Overide the exec method in order to use. + - [GroupOperation](./GroupOperation.md) + - [PThread](./PThread.md): + Contains wrapper types for C pthread operations + - [PThread.ReadWriteLock](./PThread_ReadWriteLock.md): + Wrapper for pthread\_rwlock\_t + +# Protocols + + - [BindingCommandResponder](./BindingCommandResponder.md): + Abstract: + Used for a command responder chain in order to allow responsibility to be passed + but retain a strong reference to all captured types. + - [Disposable](./Disposable.md): + Lets you dispose of a connected type. + - [Point](./Point.md): + A Swift protocol for a point in some 2 dimensional container + - [Point3](./Point3.md): + A Swift protocol for a point in some 2 dimensional container + - [EquivalentIntegerSize](./EquivalentIntegerSize.md) + - [ProgressReporting](./ProgressReporting.md) + +# Operators + + - [+(lhs:​rhs:​)](./+\(lhs:rhs:\).md) + - [-(lhs:​rhs:​)](./-\(lhs:rhs:\).md) + - [\*(lhs:​rhs:​)](./*\(lhs:rhs:\).md) + - [+(lhs:​rhs:​)](./+\(lhs:rhs:\).md) + - [-(lhs:​rhs:​)](./-\(lhs:rhs:\).md) + - [\*(lhs:​rhs:​)](./*\(lhs:rhs:\).md) + - [\!\!(wrapped:​failureText:​)](./!!\(wrapped:failureText:\).md): + Allows an error message to be states with a failed force unwrap of an optional + +# Global Functions + + - [strongify(*:​*:​)](./strongify\(_:_:\).md): + Takes a target and strongly captures it and runs the given closure body against it. + - [strongify(*:​*:​)](./strongify\(_:_:\).md): + Takes a target and strongly captures it and runs the given closure body against it. + - [strongify(*:​*:​)](./strongify\(_:_:\).md): + Takes a target and strongly captures it and runs the given closure body against it. + - [strongify(*:​*:​)](./strongify\(_:_:\).md): + Takes a target and strongly captures it and runs the given closure body against it. + - [unsafeWeakify(*:​*:​)](./unsafeWeakify\(_:_:\).md): + Takes a target and unsafely runs the given closure against it as if the target is unowned. + - [unsafeWeakify(*:​*:​)](./unsafeWeakify\(_:_:\).md): + Takes a target and unsafely runs the given closure against it as if the target is unowned. + - [unsafeWeakify(*:​*:​)](./unsafeWeakify\(_:_:\).md): + Takes a target and unsafely runs the given closure against it as if the target is unowned. + - [weakify(*:​default:​*:​)](./weakify\(_:default:_:\).md): + Takes a target and weakly runs the given closure against it + Return the default value if nil. + - [weakify(*:​*:​)](./weakify\(_:_:\).md): + Takes a target and weakly runs the given closure against it. + If nil, does nothing. + - [weakify(*:​*:​)](./weakify\(_:_:\).md): + Takes a target and weakly runs the given closure against it. + If nil, does nothing. + - [weakify(*:​default:​*:​)](./weakify\(_:default:_:\).md): + Takes a target and weakly runs the given closure against it. + If nil, does nothing. + - [weakify(*:​default:​*:​)](./weakify\(_:default:_:\).md): + Takes a target and weakly runs the given closure against it. + If nil, does nothing. + - [weakify(*:​default:​*:​)](./weakify\(_:default:_:\).md): + Takes a target and weakly runs the given closure against it + Return the default value if nil. If default value is not given, nil will be returned isntead. diff --git a/Documentation/PThread.md b/Documentation/PThread.md new file mode 100644 index 0000000..c0421a7 --- /dev/null +++ b/Documentation/PThread.md @@ -0,0 +1,7 @@ +# PThread + +Contains wrapper types for C pthread operations + +``` swift +public struct PThread +``` diff --git a/Documentation/PThread_ReadWriteLock.md b/Documentation/PThread_ReadWriteLock.md new file mode 100644 index 0000000..fecf86e --- /dev/null +++ b/Documentation/PThread_ReadWriteLock.md @@ -0,0 +1,35 @@ +# PThread.ReadWriteLock + +Wrapper for pthread\_rwlock\_t + +``` swift +public class ReadWriteLock +``` + +## Initializers + +### `init()` + +``` swift +public init() +``` + +## Methods + +### `readLock()` + +``` swift +public func readLock() +``` + +### `unlock()` + +``` swift +public func unlock() +``` + +### `readWriteLock()` + +``` swift +public func readWriteLock() +``` diff --git a/Documentation/Point.md b/Documentation/Point.md new file mode 100644 index 0000000..9b3af95 --- /dev/null +++ b/Documentation/Point.md @@ -0,0 +1,29 @@ +# Point + +A Swift protocol for a point in some 2 dimensional container + +``` swift +public protocol Point: Hashable +``` + +## Inheritance + +`Hashable` + +## Requirements + +### xCord + +Assumed to be the X cordinate in a Cartesian pt system + +``` swift +var xCord: PointValue +``` + +### yCord + +Assumed to be the Y cordinate in a Cartesian pt system + +``` swift +var yCord: PointValue +``` diff --git a/Documentation/Point3.md b/Documentation/Point3.md new file mode 100644 index 0000000..c28bafd --- /dev/null +++ b/Documentation/Point3.md @@ -0,0 +1,31 @@ +# Point3 + +A Swift protocol for a point in some 2 dimensional container + +``` swift +public protocol Point3: Hashable +``` + +## Inheritance + +`Hashable` + +## Requirements + +### xCord + +``` swift +var xCord: PointValue +``` + +### yCord + +``` swift +var yCord: PointValue +``` + +### zCord + +``` swift +var zCord: PointValue +``` diff --git a/Documentation/ProgressReporting.md b/Documentation/ProgressReporting.md new file mode 100644 index 0000000..24f96c7 --- /dev/null +++ b/Documentation/ProgressReporting.md @@ -0,0 +1,13 @@ +# ProgressReporting + +``` swift +public protocol ProgressReporting +``` + +## Requirements + +### progress + +``` swift +var progress: Progress +``` diff --git a/Documentation/Stack.md b/Documentation/Stack.md new file mode 100644 index 0000000..6cafedd --- /dev/null +++ b/Documentation/Stack.md @@ -0,0 +1,53 @@ +# Stack + +``` swift +public struct Stack +``` + +## Initializers + +### `init()` + +``` swift +public init() +``` + +## Properties + +### `isEmpty` + +``` swift +var isEmpty: Bool +``` + +### `count` + +``` swift +var count: Int +``` + +### `top` + +``` swift +var top: Element? +``` + +## Methods + +### `push(_:)` + +``` swift +public mutating func push(_ element: Element) +``` + +### `pop()` + +``` swift +public mutating func pop() -> Element? +``` + +### `forEachPop(_:)` + +``` swift +public mutating func forEachPop(_ body: (Element) throws -> Void) rethrows +``` diff --git a/Documentation/_Footer.md b/Documentation/_Footer.md new file mode 100644 index 0000000..7116425 --- /dev/null +++ b/Documentation/_Footer.md @@ -0,0 +1 @@ +Generated at 2021-01-01T13:37:44-0600 using [swift-doc](https://github.com/SwiftDocOrg/swift-doc) 1.0.0-beta.5. diff --git a/Documentation/_Sidebar.md b/Documentation/_Sidebar.md new file mode 100644 index 0000000..c3cdfca --- /dev/null +++ b/Documentation/_Sidebar.md @@ -0,0 +1,46 @@ +
+Types + + - [AsyncOperation](./AsyncOperation.md) + - [BindingCommand](./BindingCommand.md) + - [Command](./Command.md) + - [DisposeContainer](./DisposeContainer.md) + - [Event](./Event.md) + - [GroupOperation](./GroupOperation.md) + - [PThread](./PThread.md) + - [PThread.ReadWriteLock](./PThread.ReadWriteLock.md) + - [Stack](./Stack.md) + +
+ +
+Protocols + + - [BindingCommandResponder](./BindingCommandResponder.md) + - [Disposable](./Disposable.md) + - [EquivalentIntegerSize](./EquivalentIntegerSize.md) + - [Point](./Point.md) + - [Point3](./Point3.md) + - [ProgressReporting](./ProgressReporting.md) + +
+ +
+Global Functions + + - [strongify(\_:\_:)](./strongify\(_:_:\).md) + - [unsafeWeakify(\_:\_:)](./unsafeWeakify\(_:_:\).md) + - [weakify(\_:\_:)](./weakify\(_:_:\).md) + - [weakify(\_:default:\_:)](./weakify\(_:default:_:\).md) + +
+ +
+Operators + + - [\!\!(wrapped:failureText:)](./!!\(wrapped:failureText:\).md) + - [\*(lhs:rhs:)](./*\(lhs:rhs:\).md) + - [+(lhs:rhs:)](./+\(lhs:rhs:\).md) + - [-(lhs:rhs:)](./-\(lhs:rhs:\).md) + +
diff --git a/Documentation/strongify(_:_:).md b/Documentation/strongify(_:_:).md new file mode 100644 index 0000000..474b139 --- /dev/null +++ b/Documentation/strongify(_:_:).md @@ -0,0 +1,91 @@ +# strongify(\_:\_:) + +Takes a target and strongly captures it and runs the given closure body against it. + +``` swift +@inlinable public func strongify(_ target: Target, _ body: @escaping (Target) -> (Value) -> ReturnType) -> (Value) -> ReturnType +``` + +Equivalent to: + +``` +body(target) +``` + +## Parameters + + - target: The target to apply the body too + - body: T + +## Returns + +The body to execute + +# strongify(\_:\_:) + +Takes a target and strongly captures it and runs the given closure body against it. + +``` swift +@inlinable public func strongify(_ target: Target, _ body: @escaping (Target) -> () -> Void) -> () -> Void +``` + +Equivalent to: + +``` +body(target) +``` + +## Parameters + + - target: The target to apply the body too + - body: T + +## Returns + +The body to execute + +# strongify(\_:\_:) + +Takes a target and strongly captures it and runs the given closure body against it. + +``` swift +@inlinable public func strongify(_ target: Target, _ body: @escaping (Target) -> () -> ReturnType) -> () -> ReturnType +``` + +Equivalent to: + +``` +body(target) +``` + +## Parameters + + - target: The target to apply the body too + - body: T + +## Returns + +The body to execute + +# strongify(\_:\_:) + +Takes a target and strongly captures it and runs the given closure body against it. + +``` swift +@inlinable public func strongify(_ target: Target, _ body: @escaping (Target) -> (Value) -> Void) -> (Value) -> Void +``` + +Equivalent to: + +``` +body(target) +``` + +## Parameters + + - target: The target to apply the body too + - body: T + +## Returns + +The body to execute diff --git a/Documentation/unsafeWeakify(_:_:).md b/Documentation/unsafeWeakify(_:_:).md new file mode 100644 index 0000000..102cc63 --- /dev/null +++ b/Documentation/unsafeWeakify(_:_:).md @@ -0,0 +1,68 @@ +# unsafeWeakify(\_:\_:) + +Takes a target and unsafely runs the given closure against it as if the target is unowned. + +``` swift +@inlinable public func unsafeWeakify(_ target: Target, _ body: @escaping (Target) -> (Value) -> ReturnType) -> (Value) -> ReturnType +``` + +Equivalent to: + +``` +body(target) +``` + +## Parameters + + - target: The target to apply the body too + - body: T + +## Returns + +The body to execute + +# unsafeWeakify(\_:\_:) + +Takes a target and unsafely runs the given closure against it as if the target is unowned. + +``` swift +@inlinable public func unsafeWeakify(_ target: Target, _ body: @escaping (Target) -> (Value) -> Void) -> (Value) -> Void +``` + +Equivalent to: + +``` +body(target) +``` + +## Parameters + + - target: The target to apply the body too + - body: T + +## Returns + +The body to execute + +# unsafeWeakify(\_:\_:) + +Takes a target and unsafely runs the given closure against it as if the target is unowned. + +``` swift +@inlinable public func unsafeWeakify(_ target: Target, _ body: @escaping (Target) -> () -> ReturnType) -> () -> ReturnType +``` + +Equivalent to: + +``` +body(target) +``` + +## Parameters + + - target: The target to apply the body too + - body: T + +## Returns + +The body to execute diff --git a/Documentation/weakify(_:_:).md b/Documentation/weakify(_:_:).md new file mode 100644 index 0000000..255e3b1 --- /dev/null +++ b/Documentation/weakify(_:_:).md @@ -0,0 +1,53 @@ +# weakify(\_:\_:) + +Takes a target and weakly runs the given closure against it. +If nil, does nothing. + +``` swift +@inlinable public func weakify(_ target: Target?, _ body: @escaping (Target) -> ((Value) -> Void)) -> (Value) -> Void +``` + +Equivalent to: + +``` +[weak target] (value: Value) in +guard let target = target else { return } +return body(target)(value) +} +``` + +## Parameters + + - target: The target to apply the body too + - body: T + +## Returns + +The body to execute + +# weakify(\_:\_:) + +Takes a target and weakly runs the given closure against it. +If nil, does nothing. + +``` swift +@inlinable public func weakify(_ target: Target?, _ body: @escaping (Target) -> (() -> Void)) -> () -> Void +``` + +Equivalent to: + +``` +[weak target] in +guard let target = target else { return } +return body()(value) +} +``` + +## Parameters + + - target: The target to apply the body too + - body: T + +## Returns + +The body to execute diff --git a/Documentation/weakify(_:default:_:).md b/Documentation/weakify(_:default:_:).md new file mode 100644 index 0000000..6302771 --- /dev/null +++ b/Documentation/weakify(_:default:_:).md @@ -0,0 +1,109 @@ +# weakify(\_:default:\_:) + +Takes a target and weakly runs the given closure against it +Return the default value if nil. + +``` swift +@inlinable public func weakify(_ target: Target?, default defaultReturn: ReturnType, _ body: @escaping (Target) -> ((Value) -> ReturnType)) -> (Value) -> ReturnType +``` + +Equivalent to: + +``` +[weak target] (value: Value) in + guard let target = target else { return defaultReturn } +return body(target)(value) +} +``` + +## Parameters + + - target: The target to apply the body too + - defaultReturn: The value to return if target is nil + - body: T + +## Returns + +The body to execute + +# weakify(\_:default:\_:) + +Takes a target and weakly runs the given closure against it. +If nil, does nothing. + +``` swift +@inlinable public func weakify(_ target: Target?, default defaultReturn: ReturnType, _ body: @escaping (Target) -> (() -> ReturnType)) -> () -> ReturnType +``` + +Equivalent to: + +``` +[weak target] in +guard let target = target else { return } +return body()(value) +} +``` + +## Parameters + + - target: The target to apply the body too + - body: T + +## Returns + +The body to execute + +# weakify(\_:default:\_:) + +Takes a target and weakly runs the given closure against it. +If nil, does nothing. + +``` swift +@inlinable public func weakify(_ target: Target?, default defaultReturn: ReturnType? = nil, _ body: @escaping (Target) -> (() -> ReturnType?)) -> () -> ReturnType? +``` + +Equivalent to: + +``` +[weak target] in +guard let target = target else { return } +return body()(value) +} +``` + +## Parameters + + - target: The target to apply the body too + - body: T + +## Returns + +The body to execute + +# weakify(\_:default:\_:) + +Takes a target and weakly runs the given closure against it +Return the default value if nil. If default value is not given, nil will be returned isntead. + +``` swift +@inlinable public func weakify(_ target: Target?, default defaultReturn: ReturnType? = nil, _ body: @escaping (Target) -> ((Value) -> ReturnType?)) -> (Value) -> ReturnType? +``` + +Equivalent to: + +``` +[weak target] (value: Value) in +guard let target = target else { return defaultReturn } +return body(target)(value) +} +``` + +## Parameters + + - target: The target to apply the body too + - defaultReturn: The value to return if target is nil + - body: T + +## Returns + +The body to execute From c58fe9b17a7ed018b57a3bb49237c551c3e5ded3 Mon Sep 17 00:00:00 2001 From: Stephen Kac Date: Fri, 1 Jan 2021 14:47:03 -0600 Subject: [PATCH 25/26] Updated readme and doc --- README.md | 5 ++++- Sources/SAKBase/BaseTypes/Point.swift | 19 ++++++++++++++----- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index cda54c5..4953211 100644 --- a/README.md +++ b/README.md @@ -2,4 +2,7 @@ # SAKBase -A description of this package. +This is a package with a collection of useful common types, structures, algorythms for my own personal development. + +[Documentation](./Documentation/Home.md) + diff --git a/Sources/SAKBase/BaseTypes/Point.swift b/Sources/SAKBase/BaseTypes/Point.swift index e46edd0..0b4da28 100644 --- a/Sources/SAKBase/BaseTypes/Point.swift +++ b/Sources/SAKBase/BaseTypes/Point.swift @@ -11,15 +11,21 @@ import Foundation /// A Swift protocol for a point in some 2 dimensional container public protocol Point: Hashable { + /// The type of internal values associatedtype PointValue: SignedNumeric + /// Assumed to be the X cordinate in a Cartesian pt system var xCord: PointValue { get set } + + /// Assumed to be the Y cordinate in a Cartesian pt system var yCord: PointValue { get set } + /// The default initializer taking in any valid value init(xCord: PointValue, yCord: PointValue) } public extension Point where PointValue: Real { + /// An additional initializer for supporting Ints without casting init(xCord: Int, yCord: Int) { self.init(xCord: PointValue(xCord), yCord: PointValue(yCord)) } @@ -27,14 +33,18 @@ public extension Point where PointValue: Real { public extension Point { // swiftlint:disable shorthand_operator + + /// Adds the left and the right paramaters and assigns the result to the left paramater static func += (left: inout Self, right: Self) { left = left + right } + /// Subtracts the right from the left paramater and assigns the result to the left paramater static func -= (left: inout Self, right: Self) { left = left - right } + /// Multiplies the left and the right paramaters and assigns the result to the left paramater static func *= (left: inout Self, right: Self) { left = left * right } @@ -61,16 +71,15 @@ public extension Point where PointValue: Real { return within(Self.init(xCord: epsilon, yCord: epsilon), of: point) } -} - -public extension Point where PointValue: Real { + @available(*, - deprecated: 0.0.0, + deprecated: 0, message: """ Do not use equivalency with Real types as - "it will not work as expected, please use within comparisons isntead + it will not work as expected, please use within comparisons instead """) + /// Compares the two points with real numbers static func == (lhs: Self, rhs: Self) -> Bool { return lhs.xCord == rhs.xCord && lhs.yCord == rhs.yCord } From 4f43c6bce4179acb9dd5a4bf5da98ee09f8db06b Mon Sep 17 00:00:00 2001 From: Stephen Kac Date: Fri, 1 Jan 2021 14:50:32 -0600 Subject: [PATCH 26/26] Updated Xcode.proj and removed unused files --- SAKBase.xcodeproj/project.pbxproj | 62 +++++++++++++++++++++++++++++-- SAKBaseTests/Info.plist | 22 ----------- SAKBaseTests/SAKBaseTests.swift | 34 ----------------- 3 files changed, 58 insertions(+), 60 deletions(-) delete mode 100644 SAKBaseTests/Info.plist delete mode 100644 SAKBaseTests/SAKBaseTests.swift diff --git a/SAKBase.xcodeproj/project.pbxproj b/SAKBase.xcodeproj/project.pbxproj index d9a503e..244092b 100644 --- a/SAKBase.xcodeproj/project.pbxproj +++ b/SAKBase.xcodeproj/project.pbxproj @@ -70,6 +70,29 @@ CCA0A4F824BE2C40007258BA /* BindingCommandTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BindingCommandTests.swift; sourceTree = ""; }; CCA0A4FA24BE2C6B007258BA /* CommandTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommandTests.swift; sourceTree = ""; }; CCA0A4FC24BE438F007258BA /* PointTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PointTests.swift; sourceTree = ""; }; + CCA2217B259FC236000A0DAA /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + CCA22186259FC246000A0DAA /* PThread_ReadWriteLock.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = PThread_ReadWriteLock.md; sourceTree = ""; }; + CCA22187259FC246000A0DAA /* GroupOperation.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = GroupOperation.md; sourceTree = ""; }; + CCA22188259FC246000A0DAA /* Stack.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = Stack.md; sourceTree = ""; }; + CCA22189259FC246000A0DAA /* Disposable.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = Disposable.md; sourceTree = ""; }; + CCA2218A259FC246000A0DAA /* Point.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = Point.md; sourceTree = ""; }; + CCA2218B259FC246000A0DAA /* weakify(_:default:_:).md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = "weakify(_:default:_:).md"; sourceTree = ""; }; + CCA2218C259FC246000A0DAA /* weakify(_:_:).md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = "weakify(_:_:).md"; sourceTree = ""; }; + CCA2218D259FC246000A0DAA /* DisposeContainer.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = DisposeContainer.md; sourceTree = ""; }; + CCA2218E259FC246000A0DAA /* ProgressReporting.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = ProgressReporting.md; sourceTree = ""; }; + CCA2218F259FC246000A0DAA /* BindingCommandResponder.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = BindingCommandResponder.md; sourceTree = ""; }; + CCA22190259FC246000A0DAA /* Home.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = Home.md; sourceTree = ""; }; + CCA22191259FC246000A0DAA /* Command.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = Command.md; sourceTree = ""; }; + CCA22192259FC246000A0DAA /* strongify(_:_:).md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = "strongify(_:_:).md"; sourceTree = ""; }; + CCA22193259FC246000A0DAA /* unsafeWeakify(_:_:).md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = "unsafeWeakify(_:_:).md"; sourceTree = ""; }; + CCA22194259FC246000A0DAA /* BindingCommand.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = BindingCommand.md; sourceTree = ""; }; + CCA22195259FC246000A0DAA /* _Sidebar.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = _Sidebar.md; sourceTree = ""; }; + CCA22196259FC246000A0DAA /* Event.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = Event.md; sourceTree = ""; }; + CCA22197259FC246000A0DAA /* AsyncOperation.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = AsyncOperation.md; sourceTree = ""; }; + CCA22198259FC246000A0DAA /* EquivalentIntegerSize.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = EquivalentIntegerSize.md; sourceTree = ""; }; + CCA22199259FC246000A0DAA /* Point3.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = Point3.md; sourceTree = ""; }; + CCA2219A259FC246000A0DAA /* _Footer.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = _Footer.md; sourceTree = ""; }; + CCA2219B259FC246000A0DAA /* PThread.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = PThread.md; sourceTree = ""; }; CCC1C0AA23D6B3550027CD2B /* BindingCommand.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BindingCommand.swift; sourceTree = ""; }; CCC1C0AB23D6B3550027CD2B /* Stack.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Stack.swift; sourceTree = ""; }; CCC1C0AD23D6B3550027CD2B /* Event.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Event.swift; sourceTree = ""; }; @@ -160,9 +183,40 @@ path = Concurrency; sourceTree = ""; }; + CCA22185259FC246000A0DAA /* Documentation */ = { + isa = PBXGroup; + children = ( + CCA22186259FC246000A0DAA /* PThread_ReadWriteLock.md */, + CCA22187259FC246000A0DAA /* GroupOperation.md */, + CCA22188259FC246000A0DAA /* Stack.md */, + CCA22189259FC246000A0DAA /* Disposable.md */, + CCA2218A259FC246000A0DAA /* Point.md */, + CCA2218B259FC246000A0DAA /* weakify(_:default:_:).md */, + CCA2218C259FC246000A0DAA /* weakify(_:_:).md */, + CCA2218D259FC246000A0DAA /* DisposeContainer.md */, + CCA2218E259FC246000A0DAA /* ProgressReporting.md */, + CCA2218F259FC246000A0DAA /* BindingCommandResponder.md */, + CCA22190259FC246000A0DAA /* Home.md */, + CCA22191259FC246000A0DAA /* Command.md */, + CCA22192259FC246000A0DAA /* strongify(_:_:).md */, + CCA22193259FC246000A0DAA /* unsafeWeakify(_:_:).md */, + CCA22194259FC246000A0DAA /* BindingCommand.md */, + CCA22195259FC246000A0DAA /* _Sidebar.md */, + CCA22196259FC246000A0DAA /* Event.md */, + CCA22197259FC246000A0DAA /* AsyncOperation.md */, + CCA22198259FC246000A0DAA /* EquivalentIntegerSize.md */, + CCA22199259FC246000A0DAA /* Point3.md */, + CCA2219A259FC246000A0DAA /* _Footer.md */, + CCA2219B259FC246000A0DAA /* PThread.md */, + ); + path = Documentation; + sourceTree = ""; + }; CCC1C09323D6B3320027CD2B = { isa = PBXGroup; children = ( + CCA2217B259FC236000A0DAA /* README.md */, + CCA22185259FC246000A0DAA /* Documentation */, CCC1C0A823D6B3550027CD2B /* Sources */, CCE7642324AA7802002F2863 /* Tests */, CCC1C09E23D6B3320027CD2B /* Products */, @@ -471,7 +525,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Sources/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.1; "IPHONEOS_DEPLOYMENT_TARGET[sdk=macosx*]" = 13.1; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -485,7 +539,7 @@ SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = "1,2,6"; }; name = Debug; }; @@ -501,7 +555,7 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Sources/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 13.0; + IPHONEOS_DEPLOYMENT_TARGET = 13.1; "IPHONEOS_DEPLOYMENT_TARGET[sdk=macosx*]" = 13.1; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -515,7 +569,7 @@ SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = "1,2,6"; VALIDATE_PRODUCT = YES; }; name = Release; diff --git a/SAKBaseTests/Info.plist b/SAKBaseTests/Info.plist deleted file mode 100644 index 64d65ca..0000000 --- a/SAKBaseTests/Info.plist +++ /dev/null @@ -1,22 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - $(PRODUCT_BUNDLE_PACKAGE_TYPE) - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - - diff --git a/SAKBaseTests/SAKBaseTests.swift b/SAKBaseTests/SAKBaseTests.swift deleted file mode 100644 index 5ade832..0000000 --- a/SAKBaseTests/SAKBaseTests.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// SAKBaseTests.swift -// SAKBaseTests -// -// Created by Stephen Kac on 6/29/20. -// Copyright © 2020 Stephen Kac. All rights reserved. -// - -import XCTest -@testable import SAKBase - -class SAKBaseTests: XCTestCase { - - override func setUpWithError() throws { - // Put setup code here. This method is called before the invocation of each test method in the class. - } - - override func tearDownWithError() throws { - // Put teardown code here. This method is called after the invocation of each test method in the class. - } - - func testExample() throws { - // This is an example of a functional test case. - // Use XCTAssert and related functions to verify your tests produce the correct results. - } - - func testPerformanceExample() throws { - // This is an example of a performance test case. - self.measure { - // Put the code you want to measure the time of here. - } - } - -}