diff --git a/.swiftlint.yml b/.swiftlint.yml index bd75bb3..37f673e 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -3,6 +3,8 @@ disabled_rules: - type_name - file_length - fallthrough + - identifier_name + - cyclomatic_complexity function_body_length: - 100 # warning diff --git a/PuzzleMaker.gif b/PuzzleMaker.gif index 9e9aab6..eb82c9f 100644 Binary files a/PuzzleMaker.gif and b/PuzzleMaker.gif differ diff --git a/PuzzleMaker.podspec b/PuzzleMaker.podspec index 6a21caa..11ec803 100755 --- a/PuzzleMaker.podspec +++ b/PuzzleMaker.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'PuzzleMaker' - s.version = '1.0.6' + s.version = '1.2.0' s.license = 'MIT' s.summary = 'Swift framework responsible for generating puzzles from the image' s.homepage = 'https://github.com/PGSSoft/PuzzleMaker' diff --git a/PuzzleMaker.xcodeproj/project.pbxproj b/PuzzleMaker.xcodeproj/project.pbxproj index 940e530..2233337 100644 --- a/PuzzleMaker.xcodeproj/project.pbxproj +++ b/PuzzleMaker.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 487E92E4225635BF00AAC84D /* Array+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 487E92E3225635BF00AAC84D /* Array+Extensions.swift */; }; 48BAFF471D5CA4FC00AE346A /* PuzzleMakerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48BAFF461D5CA4FC00AE346A /* PuzzleMakerTests.swift */; }; 48BAFF491D5CA4FC00AE346A /* PuzzleMaker.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 48DC7DCC1D59E3260024C008 /* PuzzleMaker.framework */; }; 48BAFF511D5CA5C300AE346A /* image.png in Resources */ = {isa = PBXBuildFile; fileRef = 48BAFF501D5CA5C300AE346A /* image.png */; }; @@ -17,7 +18,6 @@ 48DC7DDE1D59F01A0024C008 /* PuzzleUnit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48DC7DDD1D59F01A0024C008 /* PuzzleUnit.swift */; }; 48DC7DE01D59F0330024C008 /* Segment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48DC7DDF1D59F0330024C008 /* Segment.swift */; }; 48DC7DE21D59F04C0024C008 /* CubicBezierCurve.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48DC7DE11D59F04C0024C008 /* CubicBezierCurve.swift */; }; - 48DC7DE41D59F0740024C008 /* Double+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48DC7DE31D59F0740024C008 /* Double+Extensions.swift */; }; 48DC7DE61D59F0880024C008 /* Bool+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48DC7DE51D59F0880024C008 /* Bool+Extensions.swift */; }; 48DC7DE81D59F09A0024C008 /* UIImage+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48DC7DE71D59F09A0024C008 /* UIImage+Extensions.swift */; }; 48DC7DEA1D59F0AE0024C008 /* UIBezierPath+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48DC7DE91D59F0AE0024C008 /* UIBezierPath+Extensions.swift */; }; @@ -37,6 +37,7 @@ /* Begin PBXFileReference section */ 481D07501D5A077E0099CEDF /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; + 487E92E3225635BF00AAC84D /* Array+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Extensions.swift"; sourceTree = ""; }; 48BAFF441D5CA4FC00AE346A /* PuzzleMakerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PuzzleMakerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 48BAFF461D5CA4FC00AE346A /* PuzzleMakerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PuzzleMakerTests.swift; sourceTree = ""; }; 48BAFF481D5CA4FC00AE346A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -50,7 +51,6 @@ 48DC7DDD1D59F01A0024C008 /* PuzzleUnit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PuzzleUnit.swift; sourceTree = ""; }; 48DC7DDF1D59F0330024C008 /* Segment.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Segment.swift; sourceTree = ""; }; 48DC7DE11D59F04C0024C008 /* CubicBezierCurve.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CubicBezierCurve.swift; sourceTree = ""; }; - 48DC7DE31D59F0740024C008 /* Double+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Double+Extensions.swift"; sourceTree = ""; }; 48DC7DE51D59F0880024C008 /* Bool+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Bool+Extensions.swift"; sourceTree = ""; }; 48DC7DE71D59F09A0024C008 /* UIImage+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImage+Extensions.swift"; sourceTree = ""; }; 48DC7DE91D59F0AE0024C008 /* UIBezierPath+Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIBezierPath+Extensions.swift"; sourceTree = ""; }; @@ -92,8 +92,8 @@ 481D074B1D5A06D60099CEDF /* Foundation Extensions */ = { isa = PBXGroup; children = ( + 487E92E3225635BF00AAC84D /* Array+Extensions.swift */, 48DC7DE51D59F0880024C008 /* Bool+Extensions.swift */, - 48DC7DE31D59F0740024C008 /* Double+Extensions.swift */, 48DC7DE91D59F0AE0024C008 /* UIBezierPath+Extensions.swift */, 48DC7DE71D59F09A0024C008 /* UIImage+Extensions.swift */, ); @@ -225,15 +225,16 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0730; - LastUpgradeCheck = 0910; + LastUpgradeCheck = 1020; TargetAttributes = { 48BAFF431D5CA4FC00AE346A = { CreatedOnToolsVersion = 7.3.1; - LastSwiftMigration = 0910; + LastSwiftMigration = 1020; + ProvisioningStyle = Automatic; }; 48DC7DCB1D59E3260024C008 = { CreatedOnToolsVersion = 7.3.1; - LastSwiftMigration = 0910; + LastSwiftMigration = 1020; }; }; }; @@ -242,6 +243,7 @@ developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( + English, en, ); mainGroup = 48DC7DBD1D59E1B80024C008; @@ -308,7 +310,7 @@ 48DC7DDA1D59EFB70024C008 /* PuzzleUnitFactory.swift in Sources */, 48DC7DEA1D59F0AE0024C008 /* UIBezierPath+Extensions.swift in Sources */, 48DC7DE01D59F0330024C008 /* Segment.swift in Sources */, - 48DC7DE41D59F0740024C008 /* Double+Extensions.swift in Sources */, + 487E92E4225635BF00AAC84D /* Array+Extensions.swift in Sources */, 48DC7DDE1D59F01A0024C008 /* PuzzleUnit.swift in Sources */, 48DC7DE21D59F04C0024C008 /* CubicBezierCurve.swift in Sources */, 48DC7DDC1D59EFF50024C008 /* PuzzleElement.swift in Sources */, @@ -347,9 +349,12 @@ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = ""; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; @@ -373,10 +378,10 @@ ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.PuzzleMakerTests; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = iphoneos; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_SWIFT3_OBJC_INFERENCE = On; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -398,9 +403,12 @@ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = ""; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; @@ -417,9 +425,9 @@ MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = com.PuzzleMakerTests; PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = iphoneos; - SWIFT_SWIFT3_OBJC_INFERENCE = On; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 5.0; VALIDATE_PRODUCT = YES; }; name = Release; @@ -427,15 +435,18 @@ 48DC7DC21D59E1B80024C008 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; @@ -459,15 +470,18 @@ 48DC7DC31D59E1B80024C008 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; CLANG_WARN_STRICT_PROTOTYPES = YES; @@ -541,8 +555,7 @@ SDKROOT = iphoneos; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -595,8 +608,7 @@ PRODUCT_NAME = PuzzleMaker; SDKROOT = iphoneos; SKIP_INSTALL = YES; - SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; diff --git a/PuzzleMaker.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/PuzzleMaker.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/PuzzleMaker.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/PuzzleMaker.xcodeproj/xcshareddata/xcschemes/PuzzleMaker iOS.xcscheme b/PuzzleMaker.xcodeproj/xcshareddata/xcschemes/PuzzleMaker iOS.xcscheme index 7e32974..1eb1fe3 100644 --- a/PuzzleMaker.xcodeproj/xcshareddata/xcschemes/PuzzleMaker iOS.xcscheme +++ b/PuzzleMaker.xcodeproj/xcshareddata/xcschemes/PuzzleMaker iOS.xcscheme @@ -1,6 +1,6 @@ + + + + @@ -56,7 +66,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/PuzzleMaker.xcodeproj/xcshareddata/xcschemes/PuzzleMakerTests.xcscheme b/PuzzleMaker.xcodeproj/xcshareddata/xcschemes/PuzzleMakerTests.xcscheme index 29e1f7d..042ad46 100644 --- a/PuzzleMaker.xcodeproj/xcshareddata/xcschemes/PuzzleMakerTests.xcscheme +++ b/PuzzleMaker.xcodeproj/xcshareddata/xcschemes/PuzzleMakerTests.xcscheme @@ -1,6 +1,6 @@ + codeCoverageEnabled = "YES" + shouldUseLaunchSchemeArgsEnv = "YES"> @@ -32,7 +31,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/PuzzleMaker.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/PuzzleMaker.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/PuzzleMaker.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/PuzzleMakerTests/PuzzleMakerTests.swift b/PuzzleMakerTests/PuzzleMakerTests.swift index 146f275..c83b434 100644 --- a/PuzzleMakerTests/PuzzleMakerTests.swift +++ b/PuzzleMakerTests/PuzzleMakerTests.swift @@ -6,8 +6,8 @@ // // -import XCTest @testable import PuzzleMaker +import XCTest class PuzzleMakerTests: XCTestCase { @@ -31,7 +31,7 @@ class PuzzleMakerTests: XCTestCase { let asyncExpectation = expectation(description: "Asynchronously generating puzzles") - puzzleMaker.generatePuzzles { (throwableClosure) in + puzzleMaker.generatePuzzles { throwableClosure in let puzzleElements = try! throwableClosure() XCTAssertTrue(puzzleElements.count == numRows) @@ -56,11 +56,11 @@ class PuzzleMakerTests: XCTestCase { let asyncExpectation = expectation(description: "Asynchronously generating puzzles") - puzzleMaker.generatePuzzles { (throwableClosure) in + puzzleMaker.generatePuzzles { throwableClosure in do { _ = try throwableClosure() XCTFail("Should throw exception") - } catch let error { + } catch { XCTAssertTrue(error as! PuzzleMakerError == PuzzleMakerError.invalidGridSize) } asyncExpectation.fulfill() @@ -84,11 +84,11 @@ class PuzzleMakerTests: XCTestCase { let asyncExpectation = expectation(description: "Asynchronously generating puzzles") - puzzleMaker.generatePuzzles { (throwableClosure) in + puzzleMaker.generatePuzzles { throwableClosure in do { _ = try throwableClosure() XCTFail("Should throw exception") - } catch let error { + } catch { XCTAssertTrue(error as! PuzzleMakerError == PuzzleMakerError.invalidImageSize) } asyncExpectation.fulfill() @@ -169,42 +169,53 @@ class PuzzleMakerTests: XCTestCase { let lower = 0.3333 let upper = 0.9999 - let random0 = Double.randomInRange(lower, upper) + let random0 = Double.random(in: lower ... upper) XCTAssertTrue(random0 >= lower && random0 <= upper) - let random1 = Double.randomInRange(lower, upper) + let random1 = Double.random(in: lower ... upper) XCTAssertTrue(random1 >= lower && random1 <= upper) - let random2 = Double.randomInRange(lower, upper) + let random2 = Double.random(in: lower ... upper) XCTAssertTrue(random2 >= lower && random2 <= upper) - let random3 = Double.randomInRange(lower, upper) + let random3 = Double.random(in: lower ... upper) XCTAssertTrue(random3 >= lower && random3 <= upper) - let random4 = Double.randomInRange(lower, upper) + let random4 = Double.random(in: lower ... upper) XCTAssertTrue(random4 >= lower && random4 <= upper) - let random5 = Double.randomInRange(lower, upper) + let random5 = Double.random(in: lower ... upper) XCTAssertTrue(random5 >= lower && random5 <= upper) - let random6 = Double.randomInRange(lower, upper) + let random6 = Double.random(in: lower ... upper) XCTAssertTrue(random6 >= lower && random6 <= upper) - let random7 = Double.randomInRange(lower, upper) + let random7 = Double.random(in: lower ... upper) XCTAssertTrue(random7 >= lower && random7 <= upper) - let random8 = Double.randomInRange(lower, upper) + let random8 = Double.random(in: lower ... upper) XCTAssertTrue(random8 >= lower && random8 <= upper) - let random9 = Double.randomInRange(lower, upper) + let random9 = Double.random(in: lower ... upper) XCTAssertTrue(random9 >= lower && random9 <= upper) } + func testArrayExtension() { + let array = [1, 2, 3] + XCTAssertTrue(array[safe: 0] != nil) + XCTAssertTrue(array[safe: 1] != nil) + XCTAssertTrue(array[safe: 2] != nil) + XCTAssertTrue(array[safe: 3] == nil) + XCTAssertTrue(array[safe: 999_999] == nil) + XCTAssertTrue(array[safe: -1] == nil) + XCTAssertTrue(array[safe: -999_999] == nil) + } + // MARK: - Helpers func checkFlattenedSegment(_ segment: Segment) { XCTAssertTrue(segment.cubicBezierCurves.count == 4) - segment.cubicBezierCurves.forEach { (cubicBezierCurve) in + segment.cubicBezierCurves.forEach { cubicBezierCurve in XCTAssertTrue(cubicBezierCurve.point.y == 0) XCTAssertTrue(cubicBezierCurve.controlPoint1.y == 0) XCTAssertTrue(cubicBezierCurve.controlPoint2.y == 0) @@ -215,9 +226,9 @@ class PuzzleMakerTests: XCTestCase { let segmentPattern = PuzzleUnitFactory.segmentPattern XCTAssertTrue(segment.cubicBezierCurves.count == 4) for (idx, cubicBezierCurve) in segment.cubicBezierCurves.enumerated() { - XCTAssertTrue(fabs(cubicBezierCurve.point.y) - fabs(segmentPattern.cubicBezierCurves[idx].point.y) == 0.0) - XCTAssertTrue(fabs(cubicBezierCurve.controlPoint1.y) - fabs(segmentPattern.cubicBezierCurves[idx].controlPoint1.y) == 0.0) - XCTAssertTrue(fabs(cubicBezierCurve.controlPoint2.y) - fabs(segmentPattern.cubicBezierCurves[idx].controlPoint2.y) == 0.0) + XCTAssertTrue(abs(cubicBezierCurve.point.y) - abs(segmentPattern.cubicBezierCurves[idx].point.y) == 0.0) + XCTAssertTrue(abs(cubicBezierCurve.controlPoint1.y) - abs(segmentPattern.cubicBezierCurves[idx].controlPoint1.y) == 0.0) + XCTAssertTrue(abs(cubicBezierCurve.controlPoint2.y) - abs(segmentPattern.cubicBezierCurves[idx].controlPoint2.y) == 0.0) } } @@ -238,12 +249,12 @@ class PuzzleMakerTests: XCTestCase { let segmentPattern = PuzzleUnitFactory.segmentPattern XCTAssertTrue(segment.cubicBezierCurves.count == 4) for (idx, cubicBezierCurve) in segment.cubicBezierCurves.enumerated() { - XCTAssertTrue(fabs(cubicBezierCurve.point.x) == fabs(segmentPattern.cubicBezierCurves[idx].point.x * scale.width)) - XCTAssertTrue(fabs(cubicBezierCurve.point.y) == fabs(segmentPattern.cubicBezierCurves[idx].point.y * scale.height)) - XCTAssertTrue(fabs(cubicBezierCurve.controlPoint1.x) == fabs(segmentPattern.cubicBezierCurves[idx].controlPoint1.x * scale.width)) - XCTAssertTrue(fabs(cubicBezierCurve.controlPoint1.y) == fabs(segmentPattern.cubicBezierCurves[idx].controlPoint1.y * scale.height)) - XCTAssertTrue(fabs(cubicBezierCurve.controlPoint2.x) == fabs(segmentPattern.cubicBezierCurves[idx].controlPoint2.x * scale.width)) - XCTAssertTrue(fabs(cubicBezierCurve.controlPoint2.y) == fabs(segmentPattern.cubicBezierCurves[idx].controlPoint2.y * scale.height)) + XCTAssertTrue(abs(cubicBezierCurve.point.x) == abs(segmentPattern.cubicBezierCurves[idx].point.x * scale.width)) + XCTAssertTrue(abs(cubicBezierCurve.point.y) == abs(segmentPattern.cubicBezierCurves[idx].point.y * scale.height)) + XCTAssertTrue(abs(cubicBezierCurve.controlPoint1.x) == abs(segmentPattern.cubicBezierCurves[idx].controlPoint1.x * scale.width)) + XCTAssertTrue(abs(cubicBezierCurve.controlPoint1.y) == abs(segmentPattern.cubicBezierCurves[idx].controlPoint1.y * scale.height)) + XCTAssertTrue(abs(cubicBezierCurve.controlPoint2.x) == abs(segmentPattern.cubicBezierCurves[idx].controlPoint2.x * scale.width)) + XCTAssertTrue(abs(cubicBezierCurve.controlPoint2.y) == abs(segmentPattern.cubicBezierCurves[idx].controlPoint2.y * scale.height)) } } diff --git a/Sources/Array+Extensions.swift b/Sources/Array+Extensions.swift new file mode 100644 index 0000000..a194557 --- /dev/null +++ b/Sources/Array+Extensions.swift @@ -0,0 +1,19 @@ +// +// Array+Extensions.swift +// PuzzleMaker iOS +// +// Created by Pawel Kania on 04/04/2019. +// + +import Foundation + +// MARK: - Array + +extension Array { + + // MARK: Subscripts + + public subscript(safe index: Int) -> Element? { + return index < count && index >= 0 ? self[index] : nil + } +} diff --git a/Sources/Bool+Extensions.swift b/Sources/Bool+Extensions.swift index 39ab1c5..aa856eb 100644 --- a/Sources/Bool+Extensions.swift +++ b/Sources/Bool+Extensions.swift @@ -14,6 +14,6 @@ extension Bool { /// Returns random value, true or false public static var random: Bool { - return Double.randomInRange(0, 1) > 0.5 + return Double.random(in: 0 ... 1) > 0.5 } } diff --git a/Sources/Double+Extensions.swift b/Sources/Double+Extensions.swift deleted file mode 100644 index 209b2d0..0000000 --- a/Sources/Double+Extensions.swift +++ /dev/null @@ -1,19 +0,0 @@ -// -// Double+Extensions.swift -// PuzzleMaker -// -// Created by PaweΕ‚ Kania on 09/08/16. -// -// - -import Foundation - -extension Double { - - // MARK: Properties - - /// Returns random double number between two values - public static var randomInRange: (_ lower: Double, _ upper: Double) -> Double = { lower, upper in - return (Double(arc4random()) / 0xffffffff) * (upper - lower) + lower - } -} diff --git a/Sources/Info-iOS.plist b/Sources/Info-iOS.plist index b37cc20..c91bec4 100644 --- a/Sources/Info-iOS.plist +++ b/Sources/Info-iOS.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 1.0.0 + 1.2.0 CFBundleSignature ???? CFBundleVersion diff --git a/Sources/PuzzleMaker.swift b/Sources/PuzzleMaker.swift index 84e7fd0..e28c9c0 100644 --- a/Sources/PuzzleMaker.swift +++ b/Sources/PuzzleMaker.swift @@ -15,7 +15,7 @@ import UIKit - invalidImageSize: Might happen when cropping image in case, when the rect parameter defines an area that is not in the image */ public enum PuzzleMakerError: Error { - case invalidGridSize, invalidImageSize + case invalidGridSize, invalidImageSize, puzzleUnitUnavailable } /** @@ -53,14 +53,14 @@ public struct PuzzleMaker { // MARK: Methods /** - Asynchronously generates set of the puzzles + Asynchronously generates set of the puzzles - - parameter completion: Throwable closure so you need 'try catch' it. On success it returns [[PuzzleElement]], otherwise handle error. See attached example - */ - public func generatePuzzles(_ completion: @escaping (_ throwableClosure: () throws -> [[PuzzleElement]]) -> Void) { + - parameter completion: Throwable closure so you need 'try catch' it. On success it returns [[PuzzleElement]], otherwise handle error. See attached example + */ + public func generatePuzzles(_ completion: @escaping (_ throwableClosure: () throws -> [[PuzzleElement?]]) -> Void) { // Number of rows or columns cannot be less than 2 if numRows < 2 || numColumns < 2 { - completion({ throw PuzzleMakerError.invalidGridSize }) + completion { throw PuzzleMakerError.invalidGridSize } return } @@ -71,72 +71,85 @@ public struct PuzzleMaker { let group = DispatchGroup() // Multidimensional array for final puzzle elements - var puzzleElements = [[PuzzleElement!]](repeating: [PuzzleElement!](repeating: nil, count: numColumns), count: numRows) + var puzzleElements = [[PuzzleElement?]](repeating: [PuzzleElement?](repeating: nil, count: numColumns), count: numRows) // First, all puzzle units must be generated and stored somewhere - var puzzleUnits = [[PuzzleUnit!]](repeating: [PuzzleUnit!](repeating: nil, count: numColumns), count: numRows) - var puzzleUnit: PuzzleUnit! + var puzzleUnits = [[PuzzleUnit?]](repeating: [PuzzleUnit?](repeating: nil, count: numColumns), count: numRows) + var puzzleUnit: PuzzleUnit? // Flag which indicates if there was a problem in generating at least one puzzle element. True means total failure and raises exception var invalidImageSize = false + // Flag which indicates that for some reason, puzzle unit could not be created + var puzzleUnitUnavailable = false // The order matters (row and column) - top and left edge of each puzzle unit must be known before generating next item - // You might think that we'll kill many kitties in code below, but... It's just your imagination... No animals were harmed while executing this code! Cats are in the box and they're fine Mr. SchrΓΆdinger! 🐱! + /* - Cheatsheet: + Cheatsheet: + + β”Œβ”€β”€β”€β”¬β”€β”€β”€β”¬β”€β”€β”€β” + β”‚ 0 β”‚ 1 β”‚ 2 β”‚ + β”œβ”€β”€β”€β”Όβ”€β”€β”€β”Όβ”€β”€β”€β”€ + β”‚ 3 β”‚ 4 β”‚ 5 β”‚ + β”œβ”€β”€β”€β”Όβ”€β”€β”€β”Όβ”€β”€β”€β”€ + β”‚ 6 β”‚ 7 β”‚ 8 β”‚ + β””β”€β”€β”€β”΄β”€β”€β”€β”΄β”€β”€β”€β”˜ - β”Œβ”€β”€β”€β”¬β”€β”€β”€β”¬β”€β”€β”€β” - β”‚ 0 β”‚ 1 β”‚ 2 β”‚ - β”œβ”€β”€β”€β”Όβ”€β”€β”€β”Όβ”€β”€β”€β”€ - β”‚ 3 β”‚ 4 β”‚ 5 β”‚ - β”œβ”€β”€β”€β”Όβ”€β”€β”€β”Όβ”€β”€β”€β”€ - β”‚ 6 β”‚ 7 β”‚ 8 β”‚ - β””β”€β”€β”€β”΄β”€β”€β”€β”΄β”€β”€β”€β”˜ + */ - */ + let throwException = { completion { throw PuzzleMakerError.puzzleUnitUnavailable } } + + func getPuzzleUnit(_ row: Int, _ col: Int) -> PuzzleUnit? { + if puzzleUnits.count > row - 1, row >= 0 { + let rows = puzzleUnits[row] + if rows.count > col - 1, col >= 0 { + return puzzleUnits[row][col] + } else { + return nil + } + } else { + return nil + } + } for row in 0 ..< numRows { for column in 0 ..< numColumns { switch (row, column) { - // Cheatsheet: 0 + // Cheatsheet: 0 case (0, 0): puzzleUnit = PuzzleUnitFactory.generatePuzzleUnit(forSize: puzzleUnitSize, topEdge: .flat, rightEdge: .missing, bottomEdge: .missing, leftEdge: .flat) - // Cheatsheet: 2 - case (let r, let c) where r == 0 && c == numColumns - 1: - let leftNeighbor = puzzleUnits[r][c - 1]! + // Cheatsheet: 2 + case let (r, c) where r == 0 && c == numColumns - 1: + guard let leftNeighbor = getPuzzleUnit(r, c - 1) else { return throwException() } puzzleUnit = PuzzleUnitFactory.generatePuzzleUnit(forSize: puzzleUnitSize, topEdge: .flat, rightEdge: .flat, bottomEdge: .missing, leftEdge: .mirror(leftNeighbor.rightSegment)) - // Cheatsheet: 6 - case (let r, let c) where r == numRows - 1 && c == 0: - let topNeighbor = puzzleUnits[r - 1][c]! + // Cheatsheet: 6 + case let (r, c) where r == numRows - 1 && c == 0: + guard let topNeighbor = getPuzzleUnit(r - 1, c) else { return throwException() } puzzleUnit = PuzzleUnitFactory.generatePuzzleUnit(forSize: puzzleUnitSize, topEdge: .mirror(topNeighbor.bottomSegment), rightEdge: .missing, bottomEdge: .flat, leftEdge: .flat) - // Cheatsheet: 8 - case (let r, let c) where r == numRows - 1 && c == numColumns - 1: - let topNeighbor = puzzleUnits[r - 1][c]! - let leftNeighbor = puzzleUnits[r][c - 1]! + // Cheatsheet: 8 + case let (r, c) where r == numRows - 1 && c == numColumns - 1: + guard let topNeighbor = getPuzzleUnit(r - 1, c), let leftNeighbor = getPuzzleUnit(r, c - 1) else { return throwException() } puzzleUnit = PuzzleUnitFactory.generatePuzzleUnit(forSize: puzzleUnitSize, topEdge: .mirror(topNeighbor.bottomSegment), rightEdge: .flat, bottomEdge: .flat, leftEdge: .mirror(leftNeighbor.rightSegment)) - // Cheatsheet: 1 - case (let r, let c) where r == 0: - let leftNeighbor = puzzleUnits[r][c - 1]! + // Cheatsheet: 1 + case let (r, c) where r == 0: + guard let leftNeighbor = getPuzzleUnit(r, c - 1) else { return throwException() } puzzleUnit = PuzzleUnitFactory.generatePuzzleUnit(forSize: puzzleUnitSize, topEdge: .flat, rightEdge: .missing, bottomEdge: .missing, leftEdge: .mirror(leftNeighbor.rightSegment)) - // Cheatsheet: 5 - case (let r, let c) where c == numColumns - 1: - let topNeighbor = puzzleUnits[r - 1][c]! - let leftNeighbor = puzzleUnits[r][c - 1]! + // Cheatsheet: 5 + case let (r, c) where c == numColumns - 1: + guard let topNeighbor = getPuzzleUnit(r - 1, c), let leftNeighbor = getPuzzleUnit(r, c - 1) else { return throwException() } puzzleUnit = PuzzleUnitFactory.generatePuzzleUnit(forSize: puzzleUnitSize, topEdge: .mirror(topNeighbor.bottomSegment), rightEdge: .flat, bottomEdge: .missing, leftEdge: .mirror(leftNeighbor.rightSegment)) - // Cheatsheet: 7 - case (let r, let c) where r == numRows - 1: - let topNeighbor = puzzleUnits[r - 1][c]! - let leftNeighbor = puzzleUnits[r][c - 1]! + // Cheatsheet: 7 + case let (r, c) where r == numRows - 1: + guard let topNeighbor = getPuzzleUnit(r - 1, c), let leftNeighbor = getPuzzleUnit(r, c - 1) else { return throwException() } puzzleUnit = PuzzleUnitFactory.generatePuzzleUnit(forSize: puzzleUnitSize, topEdge: .mirror(topNeighbor.bottomSegment), rightEdge: .missing, bottomEdge: .flat, leftEdge: .mirror(leftNeighbor.rightSegment)) - // Cheatsheet: 3 - case (let r, let c) where c == 0: - let topNeighbor = puzzleUnits[r - 1][c]! + // Cheatsheet: 3 + case let (r, c) where c == 0: + guard let topNeighbor = getPuzzleUnit(r - 1, c) else { return throwException() } puzzleUnit = PuzzleUnitFactory.generatePuzzleUnit(forSize: puzzleUnitSize, topEdge: .mirror(topNeighbor.bottomSegment), rightEdge: .missing, bottomEdge: .missing, leftEdge: .flat) - // Cheatsheet: 4 - case (let r, let c): - let topNeighbor = puzzleUnits[r - 1][c]! - let leftNeighbor = puzzleUnits[r][c - 1]! + // Cheatsheet: 4 + case let (r, c): + guard let topNeighbor = getPuzzleUnit(r - 1, c), let leftNeighbor = getPuzzleUnit(r, c - 1) else { return throwException() } puzzleUnit = PuzzleUnitFactory.generatePuzzleUnit(forSize: puzzleUnitSize, topEdge: .mirror(topNeighbor.bottomSegment), rightEdge: .missing, bottomEdge: .missing, leftEdge: .mirror(leftNeighbor.rightSegment)) } @@ -150,7 +163,12 @@ public struct PuzzleMaker { return } - let puzzleUnit: PuzzleUnit = puzzleUnits[row][column] + guard let puzzleUnit = puzzleUnits[row][column] else { + puzzleUnitUnavailable = true + group.leave() + return + } + let path = puzzleUnit.path // Because we must fix X and Y position, we need to know offset. Outer height for top and left segment will be useful @@ -199,10 +217,12 @@ public struct PuzzleMaker { debugPrint("Puzzles generated in: \(executionTime) second(s)") DispatchQueue.main.async { - if !invalidImageSize { - completion({ puzzleElements }) + if invalidImageSize { + completion { throw PuzzleMakerError.invalidImageSize } + } else if puzzleUnitUnavailable { + completion { throw PuzzleMakerError.puzzleUnitUnavailable } } else { - completion({ throw PuzzleMakerError.invalidImageSize }) + completion { puzzleElements } } } } diff --git a/Sources/PuzzleUnitFactory.swift b/Sources/PuzzleUnitFactory.swift index bb31e87..e4d2455 100644 --- a/Sources/PuzzleUnitFactory.swift +++ b/Sources/PuzzleUnitFactory.swift @@ -40,16 +40,16 @@ public struct PuzzleUnitFactory { // MARK: Methods /** - Generates complete puzzle unit for indicated size and desired edges + Generates complete puzzle unit for indicated size and desired edges - - parameter size: Size of the puzzle unit (final value might be bigger, due to outer edges) - - parameter topEdge: Desired top edge - - parameter rightEdge: Desired right edge - - parameter bottomEdge: Desired bottom edge - - parameter leftEdge: Desired left edge + - parameter size: Size of the puzzle unit (final value might be bigger, due to outer edges) + - parameter topEdge: Desired top edge + - parameter rightEdge: Desired right edge + - parameter bottomEdge: Desired bottom edge + - parameter leftEdge: Desired left edge - - returns: Complete puzzle unit with individual segments (edges) and path (concatenation of all segments) - */ + - returns: Complete puzzle unit with individual segments (edges) and path (concatenation of all segments) + */ public static func generatePuzzleUnit(forSize size: CGSize, topEdge: PuzzleEdge, rightEdge: PuzzleEdge, bottomEdge: PuzzleEdge, leftEdge: PuzzleEdge) -> PuzzleUnit { // Segments which will be held by puzzle unit and these segments will be used to generate next units var topSegment: Segment! @@ -71,7 +71,7 @@ public struct PuzzleUnitFactory { fallthrough case .missing: topSegment.scale(size.width, syFactor: size.height) - case .mirror(var segment): + case var .mirror(segment): segment.mirror() topSegment = segment } @@ -89,7 +89,7 @@ public struct PuzzleUnitFactory { if Bool.random { // Pick up randomly whether segment should be 'outer' or 'inner' rightSegment.mirror() } - case .mirror(var segment): + case var .mirror(segment): segment.mirror() rightSegment = segment } @@ -108,7 +108,7 @@ public struct PuzzleUnitFactory { if Bool.random { bottomSegment.mirror() } - case .mirror(var segment): + case var .mirror(segment): segment.mirror() bottomSegment = segment } @@ -125,7 +125,7 @@ public struct PuzzleUnitFactory { fallthrough case .missing: leftSegment.scale(size.height, syFactor: size.height) - case .mirror(var segment): + case var .mirror(segment): segment.mirror() leftSegment = segment } diff --git a/Sources/Segment.swift b/Sources/Segment.swift index 841af3f..e3202ef 100644 --- a/Sources/Segment.swift +++ b/Sources/Segment.swift @@ -40,11 +40,11 @@ public struct Segment { // MARK: Methods /** - Loops through all cubic bezier curves and set all y positions to 0 - */ + Loops through all cubic bezier curves and set all y positions to 0 + */ public mutating func makeFlat() { var cubicBezierCurvesTmp = [CubicBezierCurve]() - cubicBezierCurves.forEach { (cubicBezier) in + cubicBezierCurves.forEach { cubicBezier in let point = CGPoint(x: cubicBezier.point.x, y: 0) let controlPoint1 = CGPoint(x: cubicBezier.controlPoint1.x, y: 0) let controlPoint2 = CGPoint(x: cubicBezier.controlPoint2.x, y: 0) @@ -54,11 +54,11 @@ public struct Segment { } /** - Loops through all cubic bezier curves and set all y positions to -y (horizontal mirror) - */ + Loops through all cubic bezier curves and set all y positions to -y (horizontal mirror) + */ public mutating func mirror() { var cubicBezierCurvesTmp = [CubicBezierCurve]() - cubicBezierCurves.forEach { (cubicBezier) in + cubicBezierCurves.forEach { cubicBezier in let point = CGPoint(x: cubicBezier.point.x, y: -cubicBezier.point.y) let controlPoint1 = CGPoint(x: cubicBezier.controlPoint1.x, y: -cubicBezier.controlPoint1.y) let controlPoint2 = CGPoint(x: cubicBezier.controlPoint2.x, y: -cubicBezier.controlPoint2.y) @@ -68,14 +68,14 @@ public struct Segment { } /** - Loops through all cubic bezier curves and apply scale transform + Loops through all cubic bezier curves and apply scale transform - - parameter sxFactor: The factor by which to scale the x-axis of the coordinate system - - parameter syFactor: The factor by which to scale the y-axis of the coordinate system - */ + - parameter sxFactor: The factor by which to scale the x-axis of the coordinate system + - parameter syFactor: The factor by which to scale the y-axis of the coordinate system + */ public mutating func scale(_ sxFactor: CGFloat, syFactor: CGFloat) { var cubicBezierCurvesTmp = [CubicBezierCurve]() - cubicBezierCurves.forEach { (cubicBezier) in + cubicBezierCurves.forEach { cubicBezier in let transform = CGAffineTransform(scaleX: sxFactor, y: syFactor) let point = CGPoint(x: cubicBezier.point.x, y: cubicBezier.point.y).applying(transform) let controlPoint1 = CGPoint(x: cubicBezier.controlPoint1.x, y: cubicBezier.controlPoint1.y).applying(transform) @@ -86,13 +86,13 @@ public struct Segment { } /** - Loops through all cubic bezier curves and apply rotation transform (only y value, x is permanently set to 0) + Loops through all cubic bezier curves and apply rotation transform (only y value, x is permanently set to 0) - - parameter tyValue: The value by which to move y values with the affine transform - */ + - parameter tyValue: The value by which to move y values with the affine transform + */ public mutating func rotate(forYValue tyValue: CGFloat) { var cubicBezierCurvesTmp = [CubicBezierCurve]() - cubicBezierCurves.forEach { (cubicBezier) in + cubicBezierCurves.forEach { cubicBezier in let transform = CGAffineTransform(rotationAngle: CGFloat(-Double.pi / 2)).translatedBy(x: 0, y: tyValue) let point = cubicBezier.point.applying(transform) let controlPoint1 = cubicBezier.controlPoint1.applying(transform) diff --git a/Sources/UIBezierPath+Extensions.swift b/Sources/UIBezierPath+Extensions.swift index c10df19..d1e2198 100644 --- a/Sources/UIBezierPath+Extensions.swift +++ b/Sources/UIBezierPath+Extensions.swift @@ -13,20 +13,20 @@ extension UIBezierPath { // MARK: Methods /** - Initializes new bezier path with start point and returns function which can be used to add more points to the path + Initializes new bezier path with start point and returns function which can be used to add more points to the path - - attention: - At the end, path must be closed manually + - attention: + At the end, path must be closed manually - - parameter firstPoint: First point of the entire path + - parameter firstPoint: First point of the entire path - - returns: Function which can be used to add more points to the path - */ + - returns: Function which can be used to add more points to the path + */ public static func pathMaker(forFirstPoint firstPoint: CGPoint) -> ([CubicBezierCurve]) -> UIBezierPath { let path = UIBezierPath() path.move(to: firstPoint) return { cubicBeziers in - cubicBeziers.forEach { (cubicBezier) in + cubicBeziers.forEach { cubicBezier in path.addCurve(to: cubicBezier.point, controlPoint1: cubicBezier.controlPoint1, controlPoint2: cubicBezier.controlPoint2) } return path diff --git a/Sources/UIImage+Extensions.swift b/Sources/UIImage+Extensions.swift index 2fc865f..b23e1e4 100644 --- a/Sources/UIImage+Extensions.swift +++ b/Sources/UIImage+Extensions.swift @@ -13,12 +13,12 @@ extension UIImage { // MARK: Methods /** - Crops image to a rect + Crops image to a rect - - parameter rect: Rect of the cropped image + - parameter rect: Rect of the cropped image - - returns: Cropped image. Might be nil if the rect parameter defines an area that is not in the image - */ + - returns: Cropped image. Might be nil if the rect parameter defines an area that is not in the image + */ public func cropImage(toRect rect: CGRect) -> UIImage? { if let cgImage = cgImage, let croppedImage = cgImage.cropping(to: rect) { return UIImage(cgImage: croppedImage, scale: scale, orientation: imageOrientation) @@ -27,12 +27,12 @@ extension UIImage { } /** - Clips image to a bezier path + Clips image to a bezier path - - parameter path: Bezier path + - parameter path: Bezier path - - returns: Clipped image - */ + - returns: Clipped image + */ public func clipImage(toPath path: UIBezierPath) -> UIImage? { UIGraphicsBeginImageContextWithOptions(size, false, scale) @@ -54,15 +54,15 @@ extension UIImage { } /** - Adds inner shadow to the image + Adds inner shadow to the image - - parameter path: Path on which shadow should be routed - - parameter shadowColor: Color used for the shadow - - parameter shadowOffset: Offset in user space of the shadow from the original drawing - - parameter shadowBlurRadius: Blur radius of the shadow + - parameter path: Path on which shadow should be routed + - parameter shadowColor: Color used for the shadow + - parameter shadowOffset: Offset in user space of the shadow from the original drawing + - parameter shadowBlurRadius: Blur radius of the shadow - - returns: New image with inner shadow - */ + - returns: New image with inner shadow + */ public func applyInnerShadow(forPath path: UIBezierPath, shadowColor: UIColor, shadowOffset: CGSize, shadowBlurRadius: CGFloat) -> UIImage? { UIGraphicsBeginImageContextWithOptions(size, false, scale) diff --git a/iOS Example/iOS Example.xcodeproj/project.pbxproj b/iOS Example/iOS Example.xcodeproj/project.pbxproj index 453260c..a983b76 100644 --- a/iOS Example/iOS Example.xcodeproj/project.pbxproj +++ b/iOS Example/iOS Example.xcodeproj/project.pbxproj @@ -145,18 +145,19 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0730; - LastUpgradeCheck = 0910; + LastUpgradeCheck = 1020; ORGANIZATIONNAME = "PaweΕ‚ Kania"; TargetAttributes = { 481D075D1D5A08DE0099CEDF = { CreatedOnToolsVersion = 7.3.1; - LastSwiftMigration = 0910; + LastSwiftMigration = 1020; + ProvisioningStyle = Automatic; }; }; }; buildConfigurationList = 481D07591D5A08DE0099CEDF /* Build configuration list for PBXProject "iOS Example" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, @@ -252,6 +253,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -261,12 +263,14 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; @@ -307,6 +311,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -316,12 +321,14 @@ CLANG_WARN_BOOL_CONVERSION = YES; CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; @@ -357,12 +364,15 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = "iOS Example/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.puzzlemaker.iosexample; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.0; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -371,12 +381,15 @@ buildSettings = { ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "iPhone Developer"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = ""; INFOPLIST_FILE = "iOS Example/Info.plist"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.puzzlemaker.iosexample; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.0; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; }; name = Release; }; diff --git a/iOS Example/iOS Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/iOS Example/iOS Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/iOS Example/iOS Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/iOS Example/iOS Example.xcodeproj/xcshareddata/xcschemes/iOS Example.xcscheme b/iOS Example/iOS Example.xcodeproj/xcshareddata/xcschemes/iOS Example.xcscheme index 9e66b6a..14d2970 100644 --- a/iOS Example/iOS Example.xcodeproj/xcshareddata/xcschemes/iOS Example.xcscheme +++ b/iOS Example/iOS Example.xcodeproj/xcshareddata/xcschemes/iOS Example.xcscheme @@ -1,6 +1,6 @@ + + + + @@ -56,7 +66,6 @@ buildConfiguration = "Debug" selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - language = "" launchStyle = "0" useCustomWorkingDirectory = "NO" ignoresPersistentStateOnLaunch = "NO" diff --git a/iOS Example/iOS Example/AppDelegate.swift b/iOS Example/iOS Example/AppDelegate.swift index 52b0d76..16571a1 100644 --- a/iOS Example/iOS Example/AppDelegate.swift +++ b/iOS Example/iOS Example/AppDelegate.swift @@ -13,7 +13,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. return true } @@ -39,5 +39,4 @@ class AppDelegate: UIResponder, UIApplicationDelegate { func applicationWillTerminate(_ application: UIApplication) { // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. } - } diff --git a/iOS Example/iOS Example/Info.plist b/iOS Example/iOS Example/Info.plist index 91ac5c0..6ec7f1e 100644 --- a/iOS Example/iOS Example/Info.plist +++ b/iOS Example/iOS Example/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType APPL CFBundleShortVersionString - 1.0.0 + 1.2.0 CFBundleSignature ???? CFBundleVersion diff --git a/iOS Example/iOS Example/ViewController.swift b/iOS Example/iOS Example/ViewController.swift index 405a20a..8561e79 100644 --- a/iOS Example/iOS Example/ViewController.swift +++ b/iOS Example/iOS Example/ViewController.swift @@ -6,8 +6,8 @@ // Copyright Β© 2016 PaweΕ‚ Kania. All rights reserved. // -import UIKit import PuzzleMaker +import UIKit class ViewController: UIViewController { @@ -18,12 +18,12 @@ class ViewController: UIViewController { super.viewDidLoad() let puzzleMaker = PuzzleMaker(image: UIImage(named: "image")!, numRows: ViewController.numRows, numColumns: ViewController.numColumns) - puzzleMaker.generatePuzzles { (throwableClosure) in + puzzleMaker.generatePuzzles { throwableClosure in do { let puzzleElements = try throwableClosure() for row in 0 ..< ViewController.numRows { for column in 0 ..< ViewController.numColumns { - let puzzleElement = puzzleElements[row][column] + guard let puzzleElement = puzzleElements[row][column] else { continue } let position = puzzleElement.position let image = puzzleElement.image let imgView = UIImageView(frame: CGRect(x: position.x, y: position.y, width: image.size.width, height: image.size.height)) @@ -32,7 +32,7 @@ class ViewController: UIViewController { } } - } catch let error { + } catch { debugPrint(error) } }