diff --git a/.gitignore b/.gitignore index 749a1fa9..fb31bf32 100644 --- a/.gitignore +++ b/.gitignore @@ -27,12 +27,6 @@ xcuserdata/ *.dSYM.zip *.dSYM -## Keys -Shared/Keys.swift -secret/ -scripts/FirebaseCrashreportingKeys/ -scripts/firebaseKeys - ## Playgrounds timeline.xctimeline playground.xcworkspace @@ -45,21 +39,7 @@ Gemfile.lock # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. # Packages/ .build/ - -# CocoaPods -# -# We recommend against adding the Pods directory to your .gitignore. However -# you should judge for yourself, the pros and cons are mentioned at: -# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control -# -# Pods/ - -# Carthage -# -# Add this line if you want to avoid checking in source code from Carthage dependencies. - -Carthage/Checkouts -Carthage/Build +.spm-build # fastlane # diff --git a/.gitmodules b/.gitmodules index d1396d09..ffe624ed 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,6 +2,3 @@ path = Submodules/WeTransfer-iOS-CI url = https://github.com/WeTransfer/WeTransfer-iOS-CI.git branch = master -[submodule "Submodules/ios-snapshot-test-case"] - path = Submodules/ios-snapshot-test-case - url = https://github.com/uber/ios-snapshot-test-case.git diff --git a/.swiftpm/xcode/xcshareddata/xcschemes/WeScan.xcscheme b/.swiftpm/xcode/xcshareddata/xcschemes/WeScan.xcscheme index 92953e12..f722e578 100644 --- a/.swiftpm/xcode/xcshareddata/xcschemes/WeScan.xcscheme +++ b/.swiftpm/xcode/xcshareddata/xcschemes/WeScan.xcscheme @@ -1,11 +1,39 @@ + + + + + + + + + + + + + + + + diff --git a/.swiftpm/xcode/xcuserdata/avanderlee.xcuserdatad/xcschemes/xcschememanagement.plist b/.swiftpm/xcode/xcuserdata/avanderlee.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..a8787ff2 --- /dev/null +++ b/.swiftpm/xcode/xcuserdata/avanderlee.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SchemeUserState + + WeScan.xcscheme_^#shared#^_ + + orderHint + 0 + + + + diff --git a/Dangerfile.swift b/Dangerfile.swift deleted file mode 100644 index 335f0cb6..00000000 --- a/Dangerfile.swift +++ /dev/null @@ -1,4 +0,0 @@ -import Danger -import WeTransferPRLinter - -WeTransferPRLinter.lint() diff --git a/Example/WeScan.xcodeproj/project.pbxproj b/Example/WeScan.xcodeproj/project.pbxproj new file mode 100644 index 00000000..10f99651 --- /dev/null +++ b/Example/WeScan.xcodeproj/project.pbxproj @@ -0,0 +1,438 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 52; + objects = { + +/* Begin PBXBuildFile section */ + 383C440E23C5846B0070DE47 /* EditImageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 383C440D23C5846B0070DE47 /* EditImageViewController.swift */; }; + 383C441723C587B90070DE47 /* ReviewImageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 383C441623C587B90070DE47 /* ReviewImageViewController.swift */; }; + 388A3BF423C46DAE00263DD1 /* NewCameraViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 388A3BF323C46DAE00263DD1 /* NewCameraViewController.swift */; }; + 8432556F293A41A100E3CC20 /* WeScan in Frameworks */ = {isa = PBXBuildFile; productRef = 8432556E293A41A100E3CC20 /* WeScan */; }; + A1D4BCBB202C4F3800FCDDEC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1D4BCBA202C4F3800FCDDEC /* AppDelegate.swift */; }; + A1D4BCBD202C4F3800FCDDEC /* HomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1D4BCBC202C4F3800FCDDEC /* HomeViewController.swift */; }; + A1D4BCC0202C4F3800FCDDEC /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A1D4BCBE202C4F3800FCDDEC /* Main.storyboard */; }; + A1D4BCC2202C4F3800FCDDEC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A1D4BCC1202C4F3800FCDDEC /* Assets.xcassets */; }; + A1D4BCC5202C4F3800FCDDEC /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A1D4BCC3202C4F3800FCDDEC /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + A1D4BD07202C4F4100FCDDEC /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 383C440D23C5846B0070DE47 /* EditImageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditImageViewController.swift; sourceTree = ""; }; + 383C441623C587B90070DE47 /* ReviewImageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReviewImageViewController.swift; sourceTree = ""; }; + 388A3BF323C46DAE00263DD1 /* NewCameraViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewCameraViewController.swift; sourceTree = ""; }; + 8432556D293A419500E3CC20 /* WeScan */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = WeScan; path = ..; sourceTree = ""; }; + A1D4BCB7202C4F3800FCDDEC /* WeScanSampleProject.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WeScanSampleProject.app; sourceTree = BUILT_PRODUCTS_DIR; }; + A1D4BCBA202C4F3800FCDDEC /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + A1D4BCBC202C4F3800FCDDEC /* HomeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewController.swift; sourceTree = ""; }; + A1D4BCC1202C4F3800FCDDEC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + A1D4BCC6202C4F3800FCDDEC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + B97B49D4216C6FF600B2235B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + B97B49D5216C6FF600B2235B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + A1D4BCB4202C4F3800FCDDEC /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8432556F293A41A100E3CC20 /* WeScan in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 74E27857215446C900361812 /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; + A1D4BCAE202C4F3800FCDDEC = { + isa = PBXGroup; + children = ( + 8432556D293A419500E3CC20 /* WeScan */, + A1D4BCB9202C4F3800FCDDEC /* WeScanSampleProject */, + A1D4BCB8202C4F3800FCDDEC /* Products */, + 74E27857215446C900361812 /* Frameworks */, + ); + sourceTree = ""; + }; + A1D4BCB8202C4F3800FCDDEC /* Products */ = { + isa = PBXGroup; + children = ( + A1D4BCB7202C4F3800FCDDEC /* WeScanSampleProject.app */, + ); + name = Products; + sourceTree = ""; + }; + A1D4BCB9202C4F3800FCDDEC /* WeScanSampleProject */ = { + isa = PBXGroup; + children = ( + A1D4BCBA202C4F3800FCDDEC /* AppDelegate.swift */, + A1D4BCBC202C4F3800FCDDEC /* HomeViewController.swift */, + A1D4BCBE202C4F3800FCDDEC /* Main.storyboard */, + A1D4BCC1202C4F3800FCDDEC /* Assets.xcassets */, + A1D4BCC3202C4F3800FCDDEC /* LaunchScreen.storyboard */, + A1D4BCC6202C4F3800FCDDEC /* Info.plist */, + 388A3BF323C46DAE00263DD1 /* NewCameraViewController.swift */, + 383C440D23C5846B0070DE47 /* EditImageViewController.swift */, + 383C441623C587B90070DE47 /* ReviewImageViewController.swift */, + ); + path = WeScanSampleProject; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + A1D4BCB6202C4F3800FCDDEC /* WeScanSampleProject */ = { + isa = PBXNativeTarget; + buildConfigurationList = A1D4BCDF202C4F3800FCDDEC /* Build configuration list for PBXNativeTarget "WeScanSampleProject" */; + buildPhases = ( + A1D4BCB3202C4F3800FCDDEC /* Sources */, + A1D4BCB4202C4F3800FCDDEC /* Frameworks */, + A1D4BCB5202C4F3800FCDDEC /* Resources */, + 503A25E422FB044C00534850 /* SwiftLint */, + A1D4BD07202C4F4100FCDDEC /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = WeScanSampleProject; + packageProductDependencies = ( + 8432556E293A41A100E3CC20 /* WeScan */, + ); + productName = ScannerSampleProject; + productReference = A1D4BCB7202C4F3800FCDDEC /* WeScanSampleProject.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + A1D4BCAF202C4F3800FCDDEC /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 1230; + ORGANIZATIONNAME = WeTransfer; + TargetAttributes = { + A1D4BCB6202C4F3800FCDDEC = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1020; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = A1D4BCB2202C4F3800FCDDEC /* Build configuration list for PBXProject "WeScan" */; + compatibilityVersion = "Xcode 8.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + fr, + "pt-PT", + it, + "zh-Hans", + "zh-Hant", + de, + "pt-BR", + hu, + pl, + es, + "es-419", + tr, + ru, + cs, + ar, + sv, + ja, + nl, + ko, + ); + mainGroup = A1D4BCAE202C4F3800FCDDEC; + productRefGroup = A1D4BCB8202C4F3800FCDDEC /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + A1D4BCB6202C4F3800FCDDEC /* WeScanSampleProject */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + A1D4BCB5202C4F3800FCDDEC /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + A1D4BCC5202C4F3800FCDDEC /* LaunchScreen.storyboard in Resources */, + A1D4BCC2202C4F3800FCDDEC /* Assets.xcassets in Resources */, + A1D4BCC0202C4F3800FCDDEC /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 503A25E422FB044C00534850 /* SwiftLint */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = SwiftLint; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if [ -z \"$CI\" ]; then\n if [ \"${CONFIGURATION}\" == \"Debug\" ]; then\n if test -d \"/opt/homebrew/bin/\"; then\n PATH=\"/opt/homebrew/bin/:${PATH}\"\n fi\n\n export PATH\n swiftlint --config \"${SRCROOT}/../Submodules/WeTransfer-iOS-CI/BuildTools/.swiftlint.yml\"\n else\n echo \"Info: As we're not building for Debug, no SwiftLint is running.\"\n fi\nfi\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + A1D4BCB3202C4F3800FCDDEC /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 383C440E23C5846B0070DE47 /* EditImageViewController.swift in Sources */, + 383C441723C587B90070DE47 /* ReviewImageViewController.swift in Sources */, + A1D4BCBD202C4F3800FCDDEC /* HomeViewController.swift in Sources */, + 388A3BF423C46DAE00263DD1 /* NewCameraViewController.swift in Sources */, + A1D4BCBB202C4F3800FCDDEC /* AppDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + A1D4BCBE202C4F3800FCDDEC /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + B97B49D4216C6FF600B2235B /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + A1D4BCC3202C4F3800FCDDEC /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + B97B49D5216C6FF600B2235B /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + A1D4BCDD202C4F3800FCDDEC /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = 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_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = 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_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + A1D4BCDE202C4F3800FCDDEC /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = 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_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = 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_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + A1D4BCE0202C4F3800FCDDEC /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Manual; + DEVELOPMENT_TEAM = ""; + INFOPLIST_FILE = WeScanSampleProject/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.WeTransfer.WeScanSampleProject; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; + }; + name = Debug; + }; + A1D4BCE1202C4F3800FCDDEC /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Manual; + DEVELOPMENT_TEAM = ""; + INFOPLIST_FILE = WeScanSampleProject/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.WeTransfer.WeScanSampleProject; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 1; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + A1D4BCB2202C4F3800FCDDEC /* Build configuration list for PBXProject "WeScan" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A1D4BCDD202C4F3800FCDDEC /* Debug */, + A1D4BCDE202C4F3800FCDDEC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + A1D4BCDF202C4F3800FCDDEC /* Build configuration list for PBXNativeTarget "WeScanSampleProject" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A1D4BCE0202C4F3800FCDDEC /* Debug */, + A1D4BCE1202C4F3800FCDDEC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCSwiftPackageProductDependency section */ + 8432556E293A41A100E3CC20 /* WeScan */ = { + isa = XCSwiftPackageProductDependency; + productName = WeScan; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = A1D4BCAF202C4F3800FCDDEC /* Project object */; +} diff --git a/WeScan.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Example/WeScan.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from WeScan.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to Example/WeScan.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/WeScan.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Example/WeScan.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from WeScan.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to Example/WeScan.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/Example/WeScan.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Example/WeScan.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 00000000..20c26b13 --- /dev/null +++ b/Example/WeScan.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,14 @@ +{ + "pins" : [ + { + "identity" : "swift-snapshot-testing", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-snapshot-testing", + "state" : { + "revision" : "f29e2014f6230cf7d5138fc899da51c7f513d467", + "version" : "1.10.0" + } + } + ], + "version" : 2 +} diff --git a/WeScanSampleProject/AppDelegate.swift b/Example/WeScanSampleProject/AppDelegate.swift similarity index 64% rename from WeScanSampleProject/AppDelegate.swift rename to Example/WeScanSampleProject/AppDelegate.swift index 0f6b7936..011ffd24 100644 --- a/WeScanSampleProject/AppDelegate.swift +++ b/Example/WeScanSampleProject/AppDelegate.swift @@ -13,8 +13,11 @@ final class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { return true } - + } diff --git a/WeScanSampleProject/Assets.xcassets/AppIcon.appiconset/Contents.json b/Example/WeScanSampleProject/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from WeScanSampleProject/Assets.xcassets/AppIcon.appiconset/Contents.json rename to Example/WeScanSampleProject/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/WeScanSampleProject/Assets.xcassets/AppIcon.appiconset/WeScanAppIcon20pt@2x.jpg b/Example/WeScanSampleProject/Assets.xcassets/AppIcon.appiconset/WeScanAppIcon20pt@2x.jpg similarity index 100% rename from WeScanSampleProject/Assets.xcassets/AppIcon.appiconset/WeScanAppIcon20pt@2x.jpg rename to Example/WeScanSampleProject/Assets.xcassets/AppIcon.appiconset/WeScanAppIcon20pt@2x.jpg diff --git a/WeScanSampleProject/Assets.xcassets/AppIcon.appiconset/WeScanAppIcon20pt@3x.jpg b/Example/WeScanSampleProject/Assets.xcassets/AppIcon.appiconset/WeScanAppIcon20pt@3x.jpg similarity index 100% rename from WeScanSampleProject/Assets.xcassets/AppIcon.appiconset/WeScanAppIcon20pt@3x.jpg rename to Example/WeScanSampleProject/Assets.xcassets/AppIcon.appiconset/WeScanAppIcon20pt@3x.jpg diff --git a/WeScanSampleProject/Assets.xcassets/AppIcon.appiconset/WeScanAppIcon29pt@2x.jpg b/Example/WeScanSampleProject/Assets.xcassets/AppIcon.appiconset/WeScanAppIcon29pt@2x.jpg similarity index 100% rename from WeScanSampleProject/Assets.xcassets/AppIcon.appiconset/WeScanAppIcon29pt@2x.jpg rename to Example/WeScanSampleProject/Assets.xcassets/AppIcon.appiconset/WeScanAppIcon29pt@2x.jpg diff --git a/WeScanSampleProject/Assets.xcassets/AppIcon.appiconset/WeScanAppIcon29pt@3x.jpg b/Example/WeScanSampleProject/Assets.xcassets/AppIcon.appiconset/WeScanAppIcon29pt@3x.jpg similarity index 100% rename from WeScanSampleProject/Assets.xcassets/AppIcon.appiconset/WeScanAppIcon29pt@3x.jpg rename to Example/WeScanSampleProject/Assets.xcassets/AppIcon.appiconset/WeScanAppIcon29pt@3x.jpg diff --git a/WeScanSampleProject/Assets.xcassets/AppIcon.appiconset/WeScanAppIcon40pt@2x.jpg b/Example/WeScanSampleProject/Assets.xcassets/AppIcon.appiconset/WeScanAppIcon40pt@2x.jpg similarity index 100% rename from WeScanSampleProject/Assets.xcassets/AppIcon.appiconset/WeScanAppIcon40pt@2x.jpg rename to Example/WeScanSampleProject/Assets.xcassets/AppIcon.appiconset/WeScanAppIcon40pt@2x.jpg diff --git a/WeScanSampleProject/Assets.xcassets/AppIcon.appiconset/WeScanAppIcon40pt@3x.jpg b/Example/WeScanSampleProject/Assets.xcassets/AppIcon.appiconset/WeScanAppIcon40pt@3x.jpg similarity index 100% rename from WeScanSampleProject/Assets.xcassets/AppIcon.appiconset/WeScanAppIcon40pt@3x.jpg rename to Example/WeScanSampleProject/Assets.xcassets/AppIcon.appiconset/WeScanAppIcon40pt@3x.jpg diff --git a/WeScanSampleProject/Assets.xcassets/AppIcon.appiconset/WeScanAppIcon60pt@2x.jpg b/Example/WeScanSampleProject/Assets.xcassets/AppIcon.appiconset/WeScanAppIcon60pt@2x.jpg similarity index 100% rename from WeScanSampleProject/Assets.xcassets/AppIcon.appiconset/WeScanAppIcon60pt@2x.jpg rename to Example/WeScanSampleProject/Assets.xcassets/AppIcon.appiconset/WeScanAppIcon60pt@2x.jpg diff --git a/WeScanSampleProject/Assets.xcassets/AppIcon.appiconset/WeScanAppIcon60pt@3x.jpg b/Example/WeScanSampleProject/Assets.xcassets/AppIcon.appiconset/WeScanAppIcon60pt@3x.jpg similarity index 100% rename from WeScanSampleProject/Assets.xcassets/AppIcon.appiconset/WeScanAppIcon60pt@3x.jpg rename to Example/WeScanSampleProject/Assets.xcassets/AppIcon.appiconset/WeScanAppIcon60pt@3x.jpg diff --git a/WeScanSampleProject/Assets.xcassets/Contents.json b/Example/WeScanSampleProject/Assets.xcassets/Contents.json similarity index 100% rename from WeScanSampleProject/Assets.xcassets/Contents.json rename to Example/WeScanSampleProject/Assets.xcassets/Contents.json diff --git a/WeScanSampleProject/Assets.xcassets/WeScanLogo.imageset/Contents.json b/Example/WeScanSampleProject/Assets.xcassets/WeScanLogo.imageset/Contents.json similarity index 100% rename from WeScanSampleProject/Assets.xcassets/WeScanLogo.imageset/Contents.json rename to Example/WeScanSampleProject/Assets.xcassets/WeScanLogo.imageset/Contents.json diff --git a/WeScanSampleProject/Assets.xcassets/WeScanLogo.imageset/WeScanLogo.png b/Example/WeScanSampleProject/Assets.xcassets/WeScanLogo.imageset/WeScanLogo.png similarity index 100% rename from WeScanSampleProject/Assets.xcassets/WeScanLogo.imageset/WeScanLogo.png rename to Example/WeScanSampleProject/Assets.xcassets/WeScanLogo.imageset/WeScanLogo.png diff --git a/WeScanSampleProject/Assets.xcassets/WeScanLogo.imageset/WeScanLogo@2x.png b/Example/WeScanSampleProject/Assets.xcassets/WeScanLogo.imageset/WeScanLogo@2x.png similarity index 100% rename from WeScanSampleProject/Assets.xcassets/WeScanLogo.imageset/WeScanLogo@2x.png rename to Example/WeScanSampleProject/Assets.xcassets/WeScanLogo.imageset/WeScanLogo@2x.png diff --git a/WeScanSampleProject/Assets.xcassets/WeScanLogo.imageset/WeScanLogo@3x.png b/Example/WeScanSampleProject/Assets.xcassets/WeScanLogo.imageset/WeScanLogo@3x.png similarity index 100% rename from WeScanSampleProject/Assets.xcassets/WeScanLogo.imageset/WeScanLogo@3x.png rename to Example/WeScanSampleProject/Assets.xcassets/WeScanLogo.imageset/WeScanLogo@3x.png diff --git a/WeScanSampleProject/Base.lproj/LaunchScreen.storyboard b/Example/WeScanSampleProject/Base.lproj/LaunchScreen.storyboard similarity index 100% rename from WeScanSampleProject/Base.lproj/LaunchScreen.storyboard rename to Example/WeScanSampleProject/Base.lproj/LaunchScreen.storyboard diff --git a/WeScanSampleProject/Base.lproj/Main.storyboard b/Example/WeScanSampleProject/Base.lproj/Main.storyboard similarity index 100% rename from WeScanSampleProject/Base.lproj/Main.storyboard rename to Example/WeScanSampleProject/Base.lproj/Main.storyboard diff --git a/WeScanSampleProject/EditImageViewController.swift b/Example/WeScanSampleProject/EditImageViewController.swift similarity index 72% rename from WeScanSampleProject/EditImageViewController.swift rename to Example/WeScanSampleProject/EditImageViewController.swift index 7fca1e55..abab3c59 100644 --- a/WeScanSampleProject/EditImageViewController.swift +++ b/Example/WeScanSampleProject/EditImageViewController.swift @@ -10,19 +10,23 @@ import UIKit import WeScan final class EditImageViewController: UIViewController { - + @IBOutlet private weak var editImageView: UIView! var captureImage: UIImage! var quad: Quadrilateral? var controller: WeScan.EditImageViewController! - + override func viewDidLoad() { super.viewDidLoad() setupView() } - + private func setupView() { - controller = WeScan.EditImageViewController(image: captureImage, quad: quad, strokeColor: UIColor(red: (69.0 / 255.0), green: (194.0 / 255.0), blue: (177.0 / 255.0), alpha: 1.0).cgColor) + controller = WeScan.EditImageViewController( + image: captureImage, + quad: quad, + strokeColor: UIColor(red: (69.0 / 255.0), green: (194.0 / 255.0), blue: (177.0 / 255.0), alpha: 1.0).cgColor + ) controller.view.frame = editImageView.bounds controller.willMove(toParent: self) editImageView.addSubview(controller.view) @@ -30,7 +34,7 @@ final class EditImageViewController: UIViewController { controller.didMove(toParent: self) controller.delegate = self } - + @IBAction func cropTapped(_ sender: UIButton!) { controller.cropImage() } @@ -38,7 +42,10 @@ final class EditImageViewController: UIViewController { extension EditImageViewController: EditImageViewDelegate { func cropped(image: UIImage) { - guard let controller = self.storyboard?.instantiateViewController(withIdentifier: "ReviewImageView") as? ReviewImageViewController else { return } + guard let controller = self.storyboard? + .instantiateViewController(withIdentifier: "ReviewImageView") as? ReviewImageViewController else { + return + } controller.modalPresentationStyle = .fullScreen controller.image = image navigationController?.pushViewController(controller, animated: false) diff --git a/WeScanSampleProject/HomeViewController.swift b/Example/WeScanSampleProject/HomeViewController.swift similarity index 89% rename from WeScanSampleProject/HomeViewController.swift rename to Example/WeScanSampleProject/HomeViewController.swift index 9dd02f1a..f5161145 100644 --- a/WeScanSampleProject/HomeViewController.swift +++ b/Example/WeScanSampleProject/HomeViewController.swift @@ -10,14 +10,14 @@ import UIKit import WeScan final class HomeViewController: UIViewController { - + private lazy var logoImageView: UIImageView = { let image = #imageLiteral(resourceName: "WeScanLogo") let imageView = UIImageView(image: image) imageView.translatesAutoresizingMaskIntoConstraints = false return imageView }() - + private lazy var logoLabel: UILabel = { let label = UILabel() label.text = "WeScan" @@ -26,7 +26,7 @@ final class HomeViewController: UIViewController { label.translatesAutoresizingMaskIntoConstraints = false return label }() - + private lazy var scanButton: UIButton = { let button = UIButton(type: .custom) button.titleLabel?.font = UIFont.preferredFont(forTextStyle: .headline) @@ -39,38 +39,46 @@ final class HomeViewController: UIViewController { }() // MARK: - Life Cycle - + override func viewDidLoad() { super.viewDidLoad() - + setupViews() setupConstraints() } - + // MARK: - Setups - + private func setupViews() { view.addSubview(logoImageView) view.addSubview(logoLabel) view.addSubview(scanButton) } - + private func setupConstraints() { - + let logoImageViewConstraints = [ logoImageView.widthAnchor.constraint(equalToConstant: 150.0), logoImageView.heightAnchor.constraint(equalToConstant: 150.0), logoImageView.centerXAnchor.constraint(equalTo: view.centerXAnchor), - NSLayoutConstraint(item: logoImageView, attribute: .centerY, relatedBy: .equal, toItem: view, attribute: .centerY, multiplier: 0.75, constant: 0.0) + NSLayoutConstraint( + item: logoImageView, + attribute: .centerY, + relatedBy: .equal, + toItem: view, + attribute: .centerY, + multiplier: 0.75, + constant: 0.0 + ) ] - + let logoLabelConstraints = [ logoLabel.topAnchor.constraint(equalTo: logoImageView.bottomAnchor, constant: 20.0), logoLabel.centerXAnchor.constraint(equalTo: logoImageView.centerXAnchor) ] - + NSLayoutConstraint.activate(logoLabelConstraints + logoImageViewConstraints) - + if #available(iOS 11.0, *) { let scanButtonConstraints = [ scanButton.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 16), @@ -78,7 +86,7 @@ final class HomeViewController: UIViewController { scanButton.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -16), scanButton.heightAnchor.constraint(equalToConstant: 55) ] - + NSLayoutConstraint.activate(scanButtonConstraints) } else { let scanButtonConstraints = [ @@ -87,85 +95,90 @@ final class HomeViewController: UIViewController { scanButton.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -16), scanButton.heightAnchor.constraint(equalToConstant: 55) ] - + NSLayoutConstraint.activate(scanButtonConstraints) } } - + // MARK: - Actions - + @objc func scanOrSelectImage(_ sender: UIButton) { - let actionSheet = UIAlertController(title: "Would you like to scan an image or select one from your photo library?", message: nil, preferredStyle: .actionSheet) - - let newAction = UIAlertAction(title: "A new scan", style: .default) { (_) in + let actionSheet = UIAlertController( + title: "Would you like to scan an image or select one from your photo library?", + message: nil, + preferredStyle: .actionSheet + ) + + let newAction = UIAlertAction(title: "A new scan", style: .default) { _ in guard let controller = self.storyboard?.instantiateViewController(withIdentifier: "NewCameraViewController") else { return } controller.modalPresentationStyle = .fullScreen self.present(controller, animated: true, completion: nil) } - - let scanAction = UIAlertAction(title: "Scan", style: .default) { (_) in + + let scanAction = UIAlertAction(title: "Scan", style: .default) { _ in self.scanImage() } - - let selectAction = UIAlertAction(title: "Select", style: .default) { (_) in + + let selectAction = UIAlertAction(title: "Select", style: .default) { _ in self.selectImage() } - + let cancelAction = UIAlertAction(title: "Cancel", style: .cancel, handler: nil) - + actionSheet.addAction(scanAction) actionSheet.addAction(selectAction) actionSheet.addAction(cancelAction) actionSheet.addAction(newAction) - + present(actionSheet, animated: true) } - + func scanImage() { let scannerViewController = ImageScannerController(delegate: self) scannerViewController.modalPresentationStyle = .fullScreen - + if #available(iOS 13.0, *) { scannerViewController.navigationBar.tintColor = .label } else { scannerViewController.navigationBar.tintColor = .black } - + present(scannerViewController, animated: true) } - + func selectImage() { let imagePicker = UIImagePickerController() imagePicker.delegate = self imagePicker.sourceType = .photoLibrary present(imagePicker, animated: true) } - + } extension HomeViewController: ImageScannerControllerDelegate { func imageScannerController(_ scanner: ImageScannerController, didFailWithError error: Error) { assertionFailure("Error occurred: \(error)") } - + func imageScannerController(_ scanner: ImageScannerController, didFinishScanningWithResults results: ImageScannerResults) { scanner.dismiss(animated: true, completion: nil) } - + func imageScannerControllerDidCancel(_ scanner: ImageScannerController) { scanner.dismiss(animated: true, completion: nil) } - + } extension HomeViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate { func imagePickerControllerDidCancel(_ picker: UIImagePickerController) { picker.dismiss(animated: true) } - - func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) { + + func imagePickerController(_ picker: UIImagePickerController, + didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) { picker.dismiss(animated: true) - + guard let image = info[.originalImage] as? UIImage else { return } let scannerViewController = ImageScannerController(image: image, delegate: self) present(scannerViewController, animated: true) diff --git a/WeScanSampleProject/Info.plist b/Example/WeScanSampleProject/Info.plist similarity index 100% rename from WeScanSampleProject/Info.plist rename to Example/WeScanSampleProject/Info.plist diff --git a/WeScanSampleProject/NewCameraViewController.swift b/Example/WeScanSampleProject/NewCameraViewController.swift similarity index 96% rename from WeScanSampleProject/NewCameraViewController.swift rename to Example/WeScanSampleProject/NewCameraViewController.swift index dfda8c5e..739ff9ae 100644 --- a/WeScanSampleProject/NewCameraViewController.swift +++ b/Example/WeScanSampleProject/NewCameraViewController.swift @@ -10,7 +10,7 @@ import UIKit import WeScan final class NewCameraViewController: UIViewController { - + @IBOutlet private weak var cameraView: UIView! var controller: CameraScannerViewController! @@ -18,7 +18,7 @@ final class NewCameraViewController: UIViewController { super.viewDidLoad() setupView() } - + private func setupView() { controller = CameraScannerViewController() controller.view.frame = cameraView.bounds @@ -28,11 +28,11 @@ final class NewCameraViewController: UIViewController { controller.didMove(toParent: self) controller.delegate = self } - + @IBAction func flashTapped(_ sender: UIButton) { controller.toggleFlash() } - + @IBAction func captureTapped(_ sender: UIButton) { controller.capture() } @@ -43,9 +43,10 @@ extension NewCameraViewController: CameraScannerViewOutputDelegate { func captureImageFailWithError(error: Error) { print(error) } - + func captureImageSuccess(image: UIImage, withQuad quad: Quadrilateral?) { - guard let controller = self.storyboard?.instantiateViewController(withIdentifier: "NewEditImageView") as? EditImageViewController else { return } + guard let controller = self.storyboard?.instantiateViewController(withIdentifier: "NewEditImageView") as? EditImageViewController + else { return } controller.modalPresentationStyle = .fullScreen controller.captureImage = image controller.quad = quad diff --git a/WeScanSampleProject/ReviewImageViewController.swift b/Example/WeScanSampleProject/ReviewImageViewController.swift similarity index 87% rename from WeScanSampleProject/ReviewImageViewController.swift rename to Example/WeScanSampleProject/ReviewImageViewController.swift index a76a1bfa..c2cd8cae 100644 --- a/WeScanSampleProject/ReviewImageViewController.swift +++ b/Example/WeScanSampleProject/ReviewImageViewController.swift @@ -9,13 +9,13 @@ import UIKit final class ReviewImageViewController: UIViewController { - + @IBOutlet private weak var imageView: UIImageView! var image: UIImage? - + override func viewDidLoad() { super.viewDidLoad() - guard let image = image else { return } + guard let image else { return } imageView.image = image } } diff --git a/WeScanSampleProject/nl.lproj/LaunchScreen.strings b/Example/WeScanSampleProject/nl.lproj/LaunchScreen.strings similarity index 100% rename from WeScanSampleProject/nl.lproj/LaunchScreen.strings rename to Example/WeScanSampleProject/nl.lproj/LaunchScreen.strings diff --git a/WeScanSampleProject/nl.lproj/Main.strings b/Example/WeScanSampleProject/nl.lproj/Main.strings similarity index 100% rename from WeScanSampleProject/nl.lproj/Main.strings rename to Example/WeScanSampleProject/nl.lproj/Main.strings diff --git a/Gemfile b/Gemfile deleted file mode 100644 index 642d7616..00000000 --- a/Gemfile +++ /dev/null @@ -1,4 +0,0 @@ -# frozen_string_literal: true - -ci_gems_path = File.join(File.dirname(__FILE__), "Submodules/WeTransfer-iOS-CI/Gemfile") -eval_gemfile(ci_gems_path) if File.exist?(ci_gems_path) diff --git a/Gemfile.lock b/Gemfile.lock deleted file mode 100644 index 1575da02..00000000 --- a/Gemfile.lock +++ /dev/null @@ -1,13 +0,0 @@ -GEM - remote: https://rubygems.org/ - specs: - -PLATFORMS - arm64-darwin-21 - ruby - universal-darwin-20 - -DEPENDENCIES - -BUNDLED WITH - 2.2.0 diff --git a/Package.resolved b/Package.resolved new file mode 100644 index 00000000..20c26b13 --- /dev/null +++ b/Package.resolved @@ -0,0 +1,14 @@ +{ + "pins" : [ + { + "identity" : "swift-snapshot-testing", + "kind" : "remoteSourceControl", + "location" : "https://github.com/pointfreeco/swift-snapshot-testing", + "state" : { + "revision" : "f29e2014f6230cf7d5138fc899da51c7f513d467", + "version" : "1.10.0" + } + } + ], + "version" : 2 +} diff --git a/Package.swift b/Package.swift index 8e29faac..d4af62d1 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.3 +// swift-tools-version:5.7 // We're hiding dev, test, and danger dependencies with // dev to make sure they're not fetched by users of this package. import PackageDescription @@ -6,29 +6,28 @@ let package = Package( name: "WeScan", defaultLocalization: "en", platforms: [ - .iOS(.v12) + .iOS(.v13) ], products: [ - // dev .library(name: "DangerDeps", type: .dynamic, targets: ["DangerDependencies"]), // dev - .library(name: "WeScan", type: .static, targets: ["WeScan"]) + .library(name: "WeScan", targets: ["WeScan"]) ], dependencies: [ - // dev .package(name: "danger-swift", url: "https://github.com/danger/swift", from: "3.0.0"), - // dev .package(name: "WeTransferPRLinter", path: "Submodules/WeTransfer-iOS-CI/Danger-Swift") + .package(url: "https://github.com/pointfreeco/swift-snapshot-testing", from: "1.10.0") ], targets: [ - // This is just an arbitrary Swift file in the app, that has - // no dependencies outside of Foundation, the dependencies section - // ensures that the library for Danger gets build also. - // dev .target(name: "DangerDependencies", dependencies: [.product(name: "Danger", package: "danger-swift"), "WeTransferPRLinter"], path: "Submodules/WeTransfer-iOS-CI/Danger-Swift", sources: ["DangerFakeSource.swift"]), .target(name: "WeScan", - path: "WeScan", - exclude: [ - "Info.plist", - "WeScan.h" - ], resources: [ .process("Resources") - ]) + ]), + .testTarget( + name: "WeScanTests", + dependencies: [ + "WeScan", + .product(name: "SnapshotTesting", package: "swift-snapshot-testing") + ], + resources: [ + .process("Resources") + ] + ) ] ) diff --git a/WeScan/Common/CIRectangleDetector.swift b/Sources/WeScan/Common/CIRectangleDetector.swift similarity index 97% rename from WeScan/Common/CIRectangleDetector.swift rename to Sources/WeScan/Common/CIRectangleDetector.swift index 208d7369..5c21bdad 100644 --- a/WeScan/Common/CIRectangleDetector.swift +++ b/Sources/WeScan/Common/CIRectangleDetector.swift @@ -6,17 +6,17 @@ // Copyright © 2018 WeTransfer. All rights reserved. // -import Foundation import AVFoundation import CoreImage +import Foundation /// Class used to detect rectangles from an image. enum CIRectangleDetector { - + static let rectangleDetector = CIDetector(ofType: CIDetectorTypeRectangle, context: CIContext(options: nil), options: [CIDetectorAccuracy: CIDetectorAccuracyHigh]) - + /// Detects rectangles from the given image on iOS 10. /// /// - Parameters: @@ -26,16 +26,16 @@ enum CIRectangleDetector { let biggestRectangle = rectangle(forImage: image) completion(biggestRectangle) } - + static func rectangle(forImage image: CIImage) -> Quadrilateral? { guard let rectangleFeatures = rectangleDetector?.features(in: image) as? [CIRectangleFeature] else { return nil } - + let quads = rectangleFeatures.map { rectangle in return Quadrilateral(rectangleFeature: rectangle) } - + return quads.biggest() } } diff --git a/WeScan/Common/EditScanCornerView.swift b/Sources/WeScan/Common/EditScanCornerView.swift similarity index 96% rename from WeScan/Common/EditScanCornerView.swift rename to Sources/WeScan/Common/EditScanCornerView.swift index 928caef7..42ac43b9 100644 --- a/WeScan/Common/EditScanCornerView.swift +++ b/Sources/WeScan/Common/EditScanCornerView.swift @@ -6,18 +6,18 @@ // Copyright © 2018 WeTransfer. All rights reserved. // -import UIKit import CoreImage +import UIKit /// A UIView used by corners of a quadrilateral that is aware of its position. final class EditScanCornerView: UIView { - + let position: CornerPosition - + /// The image to display when the corner view is highlighted. private var image: UIImage? private(set) var isHighlighted = false - + private lazy var circleLayer: CAShapeLayer = { let layer = CAShapeLayer() layer.fillColor = UIColor.clear.cgColor @@ -32,7 +32,7 @@ final class EditScanCornerView: UIView { circleLayer.strokeColor = strokeColor } } - + init(frame: CGRect, position: CornerPosition) { self.position = position super.init(frame: frame) @@ -40,36 +40,36 @@ final class EditScanCornerView: UIView { clipsToBounds = true layer.addSublayer(circleLayer) } - + required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + override func layoutSubviews() { super.layoutSubviews() layer.cornerRadius = bounds.width / 2.0 } - + override func draw(_ rect: CGRect) { super.draw(rect) - + let bezierPath = UIBezierPath(ovalIn: rect.insetBy(dx: circleLayer.lineWidth, dy: circleLayer.lineWidth)) circleLayer.frame = rect circleLayer.path = bezierPath.cgPath - + image?.draw(in: rect) } - + func highlightWithImage(_ image: UIImage) { isHighlighted = true self.image = image self.setNeedsDisplay() } - + func reset() { isHighlighted = false image = nil setNeedsDisplay() } - + } diff --git a/WeScan/Common/Error.swift b/Sources/WeScan/Common/Error.swift similarity index 99% rename from WeScan/Common/Error.swift rename to Sources/WeScan/Common/Error.swift index 2599af1f..f0ddf449 100644 --- a/WeScan/Common/Error.swift +++ b/Sources/WeScan/Common/Error.swift @@ -6,8 +6,8 @@ // Copyright © 2018 WeTransfer. All rights reserved. // -import Foundation import CoreImage +import Foundation /// Errors related to the `ImageScannerController` public enum ImageScannerControllerError: Error { @@ -22,7 +22,7 @@ public enum ImageScannerControllerError: Error { } extension ImageScannerControllerError: LocalizedError { - + public var errorDescription: String? { switch self { case .authorization: diff --git a/WeScan/Common/Quadrilateral.swift b/Sources/WeScan/Common/Quadrilateral.swift similarity index 86% rename from WeScan/Common/Quadrilateral.swift rename to Sources/WeScan/Common/Quadrilateral.swift index 86c1722a..e46cd918 100644 --- a/WeScan/Common/Quadrilateral.swift +++ b/Sources/WeScan/Common/Quadrilateral.swift @@ -6,24 +6,25 @@ // Copyright © 2018 WeTransfer. All rights reserved. // -import Foundation import AVFoundation -import Vision -import UIKit import CoreImage +import Foundation +import UIKit +import Vision -/// A data structure representing a quadrilateral and its position. This class exists to bypass the fact that CIRectangleFeature is read-only. +/// A data structure representing a quadrilateral and its position. +/// This class exists to bypass the fact that CIRectangleFeature is read-only. public struct Quadrilateral: Transformable { - + /// A point that specifies the top left corner of the quadrilateral. public var topLeft: CGPoint - + /// A point that specifies the top right corner of the quadrilateral. public var topRight: CGPoint - + /// A point that specifies the bottom right corner of the quadrilateral. public var bottomRight: CGPoint - + /// A point that specifies the bottom left corner of the quadrilateral. public var bottomLeft: CGPoint @@ -45,10 +46,13 @@ public struct Quadrilateral: Transformable { /// The perimeter of the Quadrilateral var perimeter: Double { - let perimeter = topLeft.distanceTo(point: topRight) + topRight.distanceTo(point: bottomRight) + bottomRight.distanceTo(point: bottomLeft) + bottomLeft.distanceTo(point: topLeft) + let perimeter = topLeft.distanceTo(point: topRight) + + topRight.distanceTo(point: bottomRight) + + bottomRight.distanceTo(point: bottomLeft) + + bottomLeft.distanceTo(point: topLeft) return Double(perimeter) } - + init(rectangleFeature: CIRectangleFeature) { self.topLeft = rectangleFeature.topLeft self.topRight = rectangleFeature.topRight @@ -70,18 +74,23 @@ public struct Quadrilateral: Transformable { self.bottomRight = bottomRight self.bottomLeft = bottomLeft } - + /// Applies a `CGAffineTransform` to the quadrilateral. /// /// - Parameters: /// - t: the transform to apply. /// - Returns: The transformed quadrilateral. func applying(_ transform: CGAffineTransform) -> Quadrilateral { - let quadrilateral = Quadrilateral(topLeft: topLeft.applying(transform), topRight: topRight.applying(transform), bottomRight: bottomRight.applying(transform), bottomLeft: bottomLeft.applying(transform)) - + let quadrilateral = Quadrilateral( + topLeft: topLeft.applying(transform), + topRight: topRight.applying(transform), + bottomRight: bottomRight.applying(transform), + bottomLeft: bottomLeft.applying(transform) + ) + return quadrilateral } - + /// Checks whether the quadrilateral is withing a given distance of another quadrilateral. /// /// - Parameters: @@ -89,55 +98,56 @@ public struct Quadrilateral: Transformable { /// - rectangleFeature: The other rectangle to compare this instance with. /// - Returns: True if the given rectangle is within the given distance of this rectangle instance. func isWithin(_ distance: CGFloat, ofRectangleFeature rectangleFeature: Quadrilateral) -> Bool { - + let topLeftRect = topLeft.surroundingSquare(withSize: distance) if !topLeftRect.contains(rectangleFeature.topLeft) { return false } - + let topRightRect = topRight.surroundingSquare(withSize: distance) if !topRightRect.contains(rectangleFeature.topRight) { return false } - + let bottomRightRect = bottomRight.surroundingSquare(withSize: distance) if !bottomRightRect.contains(rectangleFeature.bottomRight) { return false } - + let bottomLeftRect = bottomLeft.surroundingSquare(withSize: distance) if !bottomLeftRect.contains(rectangleFeature.bottomLeft) { return false } - + return true } - - /// Reorganizes the current quadrilateal, making sure that the points are at their appropriate positions. For example, it ensures that the top left point is actually the top and left point point of the quadrilateral. + + /// Reorganizes the current quadrilateal, making sure that the points are at their appropriate positions. + /// For example, it ensures that the top left point is actually the top and left point point of the quadrilateral. mutating func reorganize() { let points = [topLeft, topRight, bottomRight, bottomLeft] let ySortedPoints = sortPointsByYValue(points) - + guard ySortedPoints.count == 4 else { return } - + let topMostPoints = Array(ySortedPoints[0..<2]) let bottomMostPoints = Array(ySortedPoints[2..<4]) let xSortedTopMostPoints = sortPointsByXValue(topMostPoints) let xSortedBottomMostPoints = sortPointsByXValue(bottomMostPoints) - + guard xSortedTopMostPoints.count > 1, xSortedBottomMostPoints.count > 1 else { return } - + topLeft = xSortedTopMostPoints[0] topRight = xSortedTopMostPoints[1] bottomRight = xSortedBottomMostPoints[1] bottomLeft = xSortedBottomMostPoints[0] } - + /// Scales the quadrilateral based on the ratio of two given sizes, and optionaly applies a rotation. /// /// - Parameters: @@ -148,59 +158,62 @@ public struct Quadrilateral: Transformable { func scale(_ fromSize: CGSize, _ toSize: CGSize, withRotationAngle rotationAngle: CGFloat = 0.0) -> Quadrilateral { var invertedfromSize = fromSize let rotated = rotationAngle != 0.0 - + if rotated && rotationAngle != CGFloat.pi { invertedfromSize = CGSize(width: fromSize.height, height: fromSize.width) } - + var transformedQuad = self let invertedFromSizeWidth = invertedfromSize.width == 0 ? .leastNormalMagnitude : invertedfromSize.width let invertedFromSizeHeight = invertedfromSize.height == 0 ? .leastNormalMagnitude : invertedfromSize.height - + let scaleWidth = toSize.width / invertedFromSizeWidth let scaleHeight = toSize.height / invertedFromSizeHeight let scaledTransform = CGAffineTransform(scaleX: scaleWidth, y: scaleHeight) transformedQuad = transformedQuad.applying(scaledTransform) - + if rotated { let rotationTransform = CGAffineTransform(rotationAngle: rotationAngle) - + let fromImageBounds = CGRect(origin: .zero, size: fromSize).applying(scaledTransform).applying(rotationTransform) - + let toImageBounds = CGRect(origin: .zero, size: toSize) - let translationTransform = CGAffineTransform.translateTransform(fromCenterOfRect: fromImageBounds, toCenterOfRect: toImageBounds) - + let translationTransform = CGAffineTransform.translateTransform( + fromCenterOfRect: fromImageBounds, + toCenterOfRect: toImageBounds + ) + transformedQuad = transformedQuad.applyTransforms([rotationTransform, translationTransform]) } - + return transformedQuad } - + // Convenience functions - + /// Sorts the given `CGPoints` based on their y value. /// - Parameters: /// - points: The poinmts to sort. /// - Returns: The points sorted based on their y value. private func sortPointsByYValue(_ points: [CGPoint]) -> [CGPoint] { - return points.sorted { (point1, point2) -> Bool in + return points.sorted { point1, point2 -> Bool in point1.y < point2.y } } - + /// Sorts the given `CGPoints` based on their x value. /// - Parameters: /// - points: The points to sort. /// - Returns: The points sorted based on their x value. private func sortPointsByXValue(_ points: [CGPoint]) -> [CGPoint] { - return points.sorted { (point1, point2) -> Bool in + return points.sorted { point1, point2 -> Bool in point1.x < point2.x } } } extension Quadrilateral { - + /// Converts the current to the cartesian coordinate system (where 0 on the y axis is at the bottom). /// /// - Parameters: @@ -211,13 +224,16 @@ extension Quadrilateral { let topRight = self.topRight.cartesian(withHeight: height) let bottomRight = self.bottomRight.cartesian(withHeight: height) let bottomLeft = self.bottomLeft.cartesian(withHeight: height) - + return Quadrilateral(topLeft: topLeft, topRight: topRight, bottomRight: bottomRight, bottomLeft: bottomLeft) } } extension Quadrilateral: Equatable { public static func == (lhs: Quadrilateral, rhs: Quadrilateral) -> Bool { - return lhs.topLeft == rhs.topLeft && lhs.topRight == rhs.topRight && lhs.bottomRight == rhs.bottomRight && lhs.bottomLeft == rhs.bottomLeft + return lhs.topLeft == rhs.topLeft + && lhs.topRight == rhs.topRight + && lhs.bottomRight == rhs.bottomRight + && lhs.bottomLeft == rhs.bottomLeft } } diff --git a/WeScan/Common/QuadrilateralView.swift b/Sources/WeScan/Common/QuadrilateralView.swift similarity index 94% rename from WeScan/Common/QuadrilateralView.swift rename to Sources/WeScan/Common/QuadrilateralView.swift index fb0fe3c0..62109106 100644 --- a/WeScan/Common/QuadrilateralView.swift +++ b/Sources/WeScan/Common/QuadrilateralView.swift @@ -6,9 +6,9 @@ // Copyright © 2018 WeTransfer. All rights reserved. // -import UIKit import AVFoundation import Foundation +import UIKit /// Simple enum to keep track of the position of the corners of a quadrilateral. enum CornerPosition { @@ -20,17 +20,17 @@ enum CornerPosition { /// The `QuadrilateralView` is a simple `UIView` subclass that can draw a quadrilateral, and optionally edit it. final class QuadrilateralView: UIView { - + private let quadLayer: CAShapeLayer = { let layer = CAShapeLayer() layer.strokeColor = UIColor.white.cgColor layer.lineWidth = 1.0 layer.opacity = 1.0 layer.isHidden = true - + return layer }() - + /// We want the corner views to be displayed under the outline of the quadrilateral. /// Because of that, we need the quadrilateral to be drawn on a UIView above them. private let quadView: UIView = { @@ -39,15 +39,15 @@ final class QuadrilateralView: UIView { view.translatesAutoresizingMaskIntoConstraints = false return view }() - + /// The quadrilateral drawn on the view. private(set) var quad: Quadrilateral? - + public var editable = false { didSet { cornerViews(hidden: !editable) quadLayer.fillColor = editable ? UIColor(white: 0.0, alpha: 0.6).cgColor : UIColor(white: 1.0, alpha: 0.5).cgColor - guard let quad = quad else { + guard let quad else { return } drawQuad(quad, animated: false) @@ -65,54 +65,58 @@ final class QuadrilateralView: UIView { bottomLeftCornerView.strokeColor = strokeColor } } - + private var isHighlighted = false { didSet (oldValue) { guard oldValue != isHighlighted else { return } quadLayer.fillColor = isHighlighted ? UIColor.clear.cgColor : UIColor(white: 0.0, alpha: 0.6).cgColor - isHighlighted ? bringSubviewToFront(quadView) : sendSubviewToBack(quadView) + if isHighlighted { + bringSubviewToFront(quadView) + } else { + sendSubviewToBack(quadView) + } } } - + private lazy var topLeftCornerView: EditScanCornerView = { return EditScanCornerView(frame: CGRect(origin: .zero, size: cornerViewSize), position: .topLeft) }() - + private lazy var topRightCornerView: EditScanCornerView = { return EditScanCornerView(frame: CGRect(origin: .zero, size: cornerViewSize), position: .topRight) }() - + private lazy var bottomRightCornerView: EditScanCornerView = { return EditScanCornerView(frame: CGRect(origin: .zero, size: cornerViewSize), position: .bottomRight) }() - + private lazy var bottomLeftCornerView: EditScanCornerView = { return EditScanCornerView(frame: CGRect(origin: .zero, size: cornerViewSize), position: .bottomLeft) }() - + private let highlightedCornerViewSize = CGSize(width: 75.0, height: 75.0) private let cornerViewSize = CGSize(width: 20.0, height: 20.0) - + // MARK: - Life Cycle - + override init(frame: CGRect) { super.init(frame: frame) commonInit() } - + public required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + private func commonInit() { addSubview(quadView) setupCornerViews() setupConstraints() quadView.layer.addSublayer(quadLayer) } - + private func setupConstraints() { let quadViewConstraints = [ quadView.topAnchor.constraint(equalTo: topAnchor), @@ -120,31 +124,31 @@ final class QuadrilateralView: UIView { bottomAnchor.constraint(equalTo: quadView.bottomAnchor), trailingAnchor.constraint(equalTo: quadView.trailingAnchor) ] - + NSLayoutConstraint.activate(quadViewConstraints) } - + private func setupCornerViews() { addSubview(topLeftCornerView) addSubview(topRightCornerView) addSubview(bottomRightCornerView) addSubview(bottomLeftCornerView) } - + override public func layoutSubviews() { super.layoutSubviews() guard quadLayer.frame != bounds else { return } - + quadLayer.frame = bounds - if let quad = quad { + if let quad { drawQuadrilateral(quad: quad, animated: false) } } - + // MARK: - Drawings - + /// Draws the passed in quadrilateral. /// /// - Parameters: @@ -157,60 +161,60 @@ final class QuadrilateralView: UIView { layoutCornerViews(forQuad: quad) } } - + private func drawQuad(_ quad: Quadrilateral, animated: Bool) { var path = quad.path - + if editable { path = path.reversing() let rectPath = UIBezierPath(rect: bounds) path.append(rectPath) } - + if animated == true { let pathAnimation = CABasicAnimation(keyPath: "path") pathAnimation.duration = 0.2 quadLayer.add(pathAnimation, forKey: "path") } - + quadLayer.path = path.cgPath quadLayer.isHidden = false } - + private func layoutCornerViews(forQuad quad: Quadrilateral) { topLeftCornerView.center = quad.topLeft topRightCornerView.center = quad.topRight bottomLeftCornerView.center = quad.bottomLeft bottomRightCornerView.center = quad.bottomRight } - + func removeQuadrilateral() { quadLayer.path = nil quadLayer.isHidden = true } - + // MARK: - Actions - + func moveCorner(cornerView: EditScanCornerView, atPoint point: CGPoint) { - guard let quad = quad else { + guard let quad else { return } - + let validPoint = self.validPoint(point, forCornerViewOfSize: cornerView.bounds.size, inView: self) - + cornerView.center = validPoint let updatedQuad = update(quad, withPosition: validPoint, forCorner: cornerView.position) - + self.quad = updatedQuad drawQuad(updatedQuad, animated: false) } - + func highlightCornerAtPosition(position: CornerPosition, with image: UIImage) { guard editable else { return } isHighlighted = true - + let cornerView = cornerViewForCornerPosition(position: position) guard cornerView.isHighlighted == false else { cornerView.highlightWithImage(image) @@ -222,18 +226,18 @@ final class QuadrilateralView: UIView { cornerView.frame = CGRect(origin: origin, size: highlightedCornerViewSize) cornerView.highlightWithImage(image) } - + func resetHighlightedCornerViews() { isHighlighted = false resetHighlightedCornerViews(cornerViews: [topLeftCornerView, topRightCornerView, bottomLeftCornerView, bottomRightCornerView]) } - + private func resetHighlightedCornerViews(cornerViews: [EditScanCornerView]) { - cornerViews.forEach { (cornerView) in + cornerViews.forEach { cornerView in resetHightlightedCornerView(cornerView: cornerView) } } - + private func resetHightlightedCornerView(cornerView: EditScanCornerView) { cornerView.reset() let origin = CGPoint(x: cornerView.frame.origin.x + (cornerView.frame.size.width - cornerViewSize.width) / 2.0, @@ -241,9 +245,9 @@ final class QuadrilateralView: UIView { cornerView.frame = CGRect(origin: origin, size: cornerViewSize) cornerView.setNeedsDisplay() } - + // MARK: Validation - + /// Ensures that the given point is valid - meaning that it is within the bounds of the passed in `UIView`. /// /// - Parameters: @@ -253,34 +257,34 @@ final class QuadrilateralView: UIView { /// - Returns: A new point which is within the passed in view. private func validPoint(_ point: CGPoint, forCornerViewOfSize cornerViewSize: CGSize, inView view: UIView) -> CGPoint { var validPoint = point - + if point.x > view.bounds.width { validPoint.x = view.bounds.width } else if point.x < 0.0 { validPoint.x = 0.0 } - + if point.y > view.bounds.height { validPoint.y = view.bounds.height } else if point.y < 0.0 { validPoint.y = 0.0 } - + return validPoint } - + // MARK: - Convenience - + private func cornerViews(hidden: Bool) { topLeftCornerView.isHidden = hidden topRightCornerView.isHidden = hidden bottomRightCornerView.isHidden = hidden bottomLeftCornerView.isHidden = hidden } - + private func update(_ quad: Quadrilateral, withPosition position: CGPoint, forCorner corner: CornerPosition) -> Quadrilateral { var quad = quad - + switch corner { case .topLeft: quad.topLeft = position @@ -291,10 +295,10 @@ final class QuadrilateralView: UIView { case .bottomLeft: quad.bottomLeft = position } - + return quad } - + func cornerViewForCornerPosition(position: CornerPosition) -> EditScanCornerView { switch position { case .topLeft: diff --git a/WeScan/Common/VisionRectangleDetector.swift b/Sources/WeScan/Common/VisionRectangleDetector.swift similarity index 84% rename from WeScan/Common/VisionRectangleDetector.swift rename to Sources/WeScan/Common/VisionRectangleDetector.swift index 77ac1594..f8c81b35 100644 --- a/WeScan/Common/VisionRectangleDetector.swift +++ b/Sources/WeScan/Common/VisionRectangleDetector.swift @@ -6,18 +6,23 @@ // Copyright © 2018 WeTransfer. All rights reserved. // -import Vision -import Foundation import CoreImage +import Foundation +import Vision /// Class used to detect rectangles from an image. @available(iOS 11.0, *) enum VisionRectangleDetector { - private static func completeImageRequest(for request: VNImageRequestHandler, width: CGFloat, height: CGFloat, completion: @escaping ((Quadrilateral?) -> Void)) { + private static func completeImageRequest( + for request: VNImageRequestHandler, + width: CGFloat, + height: CGFloat, + completion: @escaping ((Quadrilateral?) -> Void) + ) { // Create the rectangle request, and, if found, return the biggest rectangle (else return nothing). let rectangleDetectionRequest: VNDetectRectanglesRequest = { - let rectDetectRequest = VNDetectRectanglesRequest(completionHandler: { (request, error) in + let rectDetectRequest = VNDetectRectanglesRequest(completionHandler: { request, error in guard error == nil, let results = request.results as? [VNRectangleObservation], !results.isEmpty else { completion(nil) return @@ -25,7 +30,8 @@ enum VisionRectangleDetector { let quads: [Quadrilateral] = results.map(Quadrilateral.init) - guard let biggest = quads.biggest() else { // This can't fail because the earlier guard protected against an empty array, but we use guard because of SwiftLint + // This can't fail because the earlier guard protected against an empty array, but we use guard because of SwiftLint + guard let biggest = quads.biggest() else { completion(nil) return } @@ -52,7 +58,7 @@ enum VisionRectangleDetector { } } - + /// Detects rectangles from the given CVPixelBuffer/CVImageBuffer on iOS 11 and above. /// /// - Parameters: @@ -66,7 +72,7 @@ enum VisionRectangleDetector { height: CGFloat(CVPixelBufferGetHeight(pixelBuffer)), completion: completion) } - + /// Detects rectangles from the given image on iOS 11 and above. /// /// - Parameters: @@ -78,8 +84,12 @@ enum VisionRectangleDetector { for: imageRequestHandler, width: image.extent.width, height: image.extent.height, completion: completion) } - - static func rectangle(forImage image: CIImage, orientation: CGImagePropertyOrientation, completion: @escaping ((Quadrilateral?) -> Void)) { + + static func rectangle( + forImage image: CIImage, + orientation: CGImagePropertyOrientation, + completion: @escaping ((Quadrilateral?) -> Void) + ) { let imageRequestHandler = VNImageRequestHandler(ciImage: image, orientation: orientation, options: [:]) let orientedImage = image.oriented(orientation) VisionRectangleDetector.completeImageRequest( diff --git a/WeScan/Edit/EditImageViewController.swift b/Sources/WeScan/Edit/EditImageViewController.swift similarity index 96% rename from WeScan/Edit/EditImageViewController.swift rename to Sources/WeScan/Edit/EditImageViewController.swift index 7c2c6cbb..f29aed01 100644 --- a/WeScan/Edit/EditImageViewController.swift +++ b/Sources/WeScan/Edit/EditImageViewController.swift @@ -6,8 +6,8 @@ // Copyright © 2020 WeTransfer. All rights reserved. // -import UIKit import AVFoundation +import UIKit /// A protocol that your delegate object will get results of EditImageViewController. public protocol EditImageViewDelegate: AnyObject { @@ -18,17 +18,17 @@ public protocol EditImageViewDelegate: AnyObject { /// A view controller that manages edit image for scanning documents or pick image from photo library /// The `EditImageViewController` class is individual for rotate, crop image public final class EditImageViewController: UIViewController { - + /// The image the quadrilateral was detected on. private var image: UIImage - + /// The detected quadrilateral that can be edited by the user. Uses the image's coordinates. private var quad: Quadrilateral private var zoomGestureController: ZoomGestureController! private var quadViewWidthConstraint = NSLayoutConstraint() private var quadViewHeightConstraint = NSLayoutConstraint() public weak var delegate: EditImageViewDelegate? - + private lazy var imageView: UIImageView = { let imageView = UIImageView() imageView.clipsToBounds = true @@ -39,7 +39,7 @@ public final class EditImageViewController: UIViewController { imageView.translatesAutoresizingMaskIntoConstraints = false return imageView }() - + private lazy var quadView: QuadrilateralView = { let quadView = QuadrilateralView() quadView.editable = true @@ -51,40 +51,40 @@ public final class EditImageViewController: UIViewController { private var strokeColor: CGColor? // MARK: - Life Cycle - + public init(image: UIImage, quad: Quadrilateral?, rotateImage: Bool = true, strokeColor: CGColor? = nil) { self.image = rotateImage ? image.applyingPortraitOrientation() : image self.quad = quad ?? EditImageViewController.defaultQuad(allOfImage: image) self.strokeColor = strokeColor super.init(nibName: nil, bundle: nil) } - + public required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + override public func viewDidLoad() { super.viewDidLoad() - + setupViews() setupConstraints() zoomGestureController = ZoomGestureController(image: image, quadView: quadView) addLongGesture(of: zoomGestureController) } - + override public func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() adjustQuadViewConstraints() displayQuad() } - + // MARK: - Setups - + private func setupViews() { view.addSubview(imageView) view.addSubview(quadView) } - + private func setupConstraints() { let imageViewConstraints = [ imageView.topAnchor.constraint(equalTo: view.topAnchor), @@ -92,34 +92,34 @@ public final class EditImageViewController: UIViewController { view.bottomAnchor.constraint(equalTo: imageView.bottomAnchor), view.leadingAnchor.constraint(equalTo: imageView.leadingAnchor) ] - + quadViewWidthConstraint = quadView.widthAnchor.constraint(equalToConstant: 0.0) quadViewHeightConstraint = quadView.heightAnchor.constraint(equalToConstant: 0.0) - + let quadViewConstraints = [ quadView.centerXAnchor.constraint(equalTo: view.centerXAnchor), quadView.centerYAnchor.constraint(equalTo: view.centerYAnchor), quadViewWidthConstraint, quadViewHeightConstraint ] - + NSLayoutConstraint.activate(quadViewConstraints + imageViewConstraints) } - + private func addLongGesture(of controller: ZoomGestureController) { let touchDown = UILongPressGestureRecognizer(target: controller, action: #selector(controller.handle(pan:))) touchDown.minimumPressDuration = 0 view.addGestureRecognizer(touchDown) } - + // MARK: - Actions /// This function allow user can crop image follow quad. the image will send back by delegate function public func cropImage() { guard let quad = quadView.quad, let ciImage = CIImage(image: image) else { return } - + let cgOrientation = CGImagePropertyOrientation(image.imageOrientation) let orientedImage = ciImage.oriented(forExifOrientation: Int32(cgOrientation.rawValue)) let scaledQuad = quad.scale(quadView.bounds.size, image.size) @@ -139,56 +139,57 @@ public final class EditImageViewController: UIViewController { let croppedImage = UIImage.from(ciImage: filteredImage) delegate?.cropped(image: croppedImage) } - + /// This function allow user to rotate image by 90 degree each and will reload image on image view. public func rotateImage() { let rotationAngle = Measurement(value: 90, unit: .degrees) reloadImage(withAngle: rotationAngle) } - + private func reloadImage(withAngle angle: Measurement) { guard let newImage = image.rotated(by: angle) else { return } let newQuad = EditImageViewController.defaultQuad(allOfImage: newImage) - + image = newImage imageView.image = image quad = newQuad adjustQuadViewConstraints() displayQuad() - + zoomGestureController = ZoomGestureController(image: image, quadView: quadView) addLongGesture(of: zoomGestureController) } - + private func displayQuad() { let imageSize = image.size let size = CGSize(width: quadViewWidthConstraint.constant, height: quadViewHeightConstraint.constant) let imageFrame = CGRect(origin: quadView.frame.origin, size: size) - + let scaleTransform = CGAffineTransform.scaleTransform(forSize: imageSize, aspectFillInSize: imageFrame.size) let transforms = [scaleTransform] let transformedQuad = quad.applyTransforms(transforms) - + quadView.drawQuadrilateral(quad: transformedQuad, animated: false) } - + /// The quadView should be lined up on top of the actual image displayed by the imageView. - /// Since there is no way to know the size of that image before run time, we adjust the constraints to make sure that the quadView is on top of the displayed image. + /// Since there is no way to know the size of that image before run time, we adjust the constraints + /// to make sure that the quadView is on top of the displayed image. private func adjustQuadViewConstraints() { let frame = AVMakeRect(aspectRatio: image.size, insideRect: imageView.bounds) quadViewWidthConstraint.constant = frame.size.width quadViewHeightConstraint.constant = frame.size.height } - + /// Generates a `Quadrilateral` object that's centered and one third of the size of the passed in image. private static func defaultQuad(forImage image: UIImage) -> Quadrilateral { let topLeft = CGPoint(x: image.size.width / 3.0, y: image.size.height / 3.0) let topRight = CGPoint(x: 2.0 * image.size.width / 3.0, y: image.size.height / 3.0) let bottomRight = CGPoint(x: 2.0 * image.size.width / 3.0, y: 2.0 * image.size.height / 3.0) let bottomLeft = CGPoint(x: image.size.width / 3.0, y: 2.0 * image.size.height / 3.0) - + let quad = Quadrilateral(topLeft: topLeft, topRight: topRight, bottomRight: bottomRight, bottomLeft: bottomLeft) - + return quad } diff --git a/WeScan/Edit/EditScanViewController.swift b/Sources/WeScan/Edit/EditScanViewController.swift similarity index 84% rename from WeScan/Edit/EditScanViewController.swift rename to Sources/WeScan/Edit/EditScanViewController.swift index 298d9ce7..344bd696 100644 --- a/WeScan/Edit/EditScanViewController.swift +++ b/Sources/WeScan/Edit/EditScanViewController.swift @@ -6,12 +6,12 @@ // Copyright © 2018 WeTransfer. All rights reserved. // -import UIKit import AVFoundation +import UIKit /// The `EditScanViewController` offers an interface for the user to edit the detected quadrilateral. final class EditScanViewController: UIViewController { - + private lazy var imageView: UIImageView = { let imageView = UIImageView() imageView.clipsToBounds = true @@ -22,92 +22,107 @@ final class EditScanViewController: UIViewController { imageView.translatesAutoresizingMaskIntoConstraints = false return imageView }() - + private lazy var quadView: QuadrilateralView = { let quadView = QuadrilateralView() quadView.editable = true quadView.translatesAutoresizingMaskIntoConstraints = false return quadView }() - + private lazy var nextButton: UIBarButtonItem = { - let title = NSLocalizedString("wescan.edit.button.next", tableName: nil, bundle: Bundle(for: EditScanViewController.self), value: "Next", comment: "A generic next button") + let title = NSLocalizedString("wescan.edit.button.next", + tableName: nil, + bundle: Bundle(for: EditScanViewController.self), + value: "Next", + comment: "A generic next button" + ) let button = UIBarButtonItem(title: title, style: .plain, target: self, action: #selector(pushReviewController)) button.tintColor = navigationController?.navigationBar.tintColor return button }() - + private lazy var cancelButton: UIBarButtonItem = { - let title = NSLocalizedString("wescan.scanning.cancel", tableName: nil, bundle: Bundle(for: EditScanViewController.self), value: "Cancel", comment: "A generic cancel button") + let title = NSLocalizedString("wescan.scanning.cancel", + tableName: nil, + bundle: Bundle(for: EditScanViewController.self), + value: "Cancel", + comment: "A generic cancel button" + ) let button = UIBarButtonItem(title: title, style: .plain, target: self, action: #selector(cancelButtonTapped)) button.tintColor = navigationController?.navigationBar.tintColor return button }() - + /// The image the quadrilateral was detected on. private let image: UIImage - + /// The detected quadrilateral that can be edited by the user. Uses the image's coordinates. private var quad: Quadrilateral - + private var zoomGestureController: ZoomGestureController! - + private var quadViewWidthConstraint = NSLayoutConstraint() private var quadViewHeightConstraint = NSLayoutConstraint() - + // MARK: - Life Cycle - + init(image: UIImage, quad: Quadrilateral?, rotateImage: Bool = true) { self.image = rotateImage ? image.applyingPortraitOrientation() : image self.quad = quad ?? EditScanViewController.defaultQuad(forImage: image) super.init(nibName: nil, bundle: nil) } - + required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + override public func viewDidLoad() { super.viewDidLoad() - + setupViews() setupConstraints() - title = NSLocalizedString("wescan.edit.title", tableName: nil, bundle: Bundle(for: EditScanViewController.self), value: "Edit Scan", comment: "The title of the EditScanViewController") + title = NSLocalizedString("wescan.edit.title", + tableName: nil, + bundle: Bundle(for: EditScanViewController.self), + value: "Edit Scan", + comment: "The title of the EditScanViewController" + ) navigationItem.rightBarButtonItem = nextButton if let firstVC = self.navigationController?.viewControllers.first, firstVC == self { navigationItem.leftBarButtonItem = cancelButton } else { navigationItem.leftBarButtonItem = nil } - + zoomGestureController = ZoomGestureController(image: image, quadView: quadView) - + let touchDown = UILongPressGestureRecognizer(target: zoomGestureController, action: #selector(zoomGestureController.handle(pan:))) touchDown.minimumPressDuration = 0 view.addGestureRecognizer(touchDown) } - + override public func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() adjustQuadViewConstraints() displayQuad() } - + override public func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) - + // Work around for an iOS 11.2 bug where UIBarButtonItems don't get back to their normal state after being pressed. navigationController?.navigationBar.tintAdjustmentMode = .normal navigationController?.navigationBar.tintAdjustmentMode = .automatic } - + // MARK: - Setups - + private func setupViews() { view.addSubview(imageView) view.addSubview(quadView) } - + private func setupConstraints() { let imageViewConstraints = [ imageView.topAnchor.constraint(equalTo: view.topAnchor), @@ -115,27 +130,27 @@ final class EditScanViewController: UIViewController { view.bottomAnchor.constraint(equalTo: imageView.bottomAnchor), view.leadingAnchor.constraint(equalTo: imageView.leadingAnchor) ] - + quadViewWidthConstraint = quadView.widthAnchor.constraint(equalToConstant: 0.0) quadViewHeightConstraint = quadView.heightAnchor.constraint(equalToConstant: 0.0) - + let quadViewConstraints = [ quadView.centerXAnchor.constraint(equalTo: view.centerXAnchor), quadView.centerYAnchor.constraint(equalTo: view.centerYAnchor), quadViewWidthConstraint, quadViewHeightConstraint ] - + NSLayoutConstraint.activate(quadViewConstraints + imageViewConstraints) } - + // MARK: - Actions @objc func cancelButtonTapped() { if let imageScannerController = navigationController as? ImageScannerController { imageScannerController.imageScannerDelegate?.imageScannerControllerDidCancel(imageScannerController) } } - + @objc func pushReviewController() { guard let quad = quadView.quad, let ciImage = CIImage(image: image) else { @@ -149,58 +164,67 @@ final class EditScanViewController: UIViewController { let orientedImage = ciImage.oriented(forExifOrientation: Int32(cgOrientation.rawValue)) let scaledQuad = quad.scale(quadView.bounds.size, image.size) self.quad = scaledQuad - + // Cropped Image var cartesianScaledQuad = scaledQuad.toCartesian(withHeight: image.size.height) cartesianScaledQuad.reorganize() - + let filteredImage = orientedImage.applyingFilter("CIPerspectiveCorrection", parameters: [ "inputTopLeft": CIVector(cgPoint: cartesianScaledQuad.bottomLeft), "inputTopRight": CIVector(cgPoint: cartesianScaledQuad.bottomRight), "inputBottomLeft": CIVector(cgPoint: cartesianScaledQuad.topLeft), "inputBottomRight": CIVector(cgPoint: cartesianScaledQuad.topRight) ]) - + let croppedImage = UIImage.from(ciImage: filteredImage) // Enhanced Image let enhancedImage = filteredImage.applyingAdaptiveThreshold()?.withFixedOrientation() let enhancedScan = enhancedImage.flatMap { ImageScannerScan(image: $0) } - - let results = ImageScannerResults(detectedRectangle: scaledQuad, originalScan: ImageScannerScan(image: image), croppedScan: ImageScannerScan(image: croppedImage), enhancedScan: enhancedScan) - + + let results = ImageScannerResults( + detectedRectangle: scaledQuad, + originalScan: ImageScannerScan(image: image), + croppedScan: ImageScannerScan(image: croppedImage), + enhancedScan: enhancedScan + ) + let reviewViewController = ReviewViewController(results: results) navigationController?.pushViewController(reviewViewController, animated: true) } - + private func displayQuad() { let imageSize = image.size - let imageFrame = CGRect(origin: quadView.frame.origin, size: CGSize(width: quadViewWidthConstraint.constant, height: quadViewHeightConstraint.constant)) - + let imageFrame = CGRect( + origin: quadView.frame.origin, + size: CGSize(width: quadViewWidthConstraint.constant, height: quadViewHeightConstraint.constant) + ) + let scaleTransform = CGAffineTransform.scaleTransform(forSize: imageSize, aspectFillInSize: imageFrame.size) let transforms = [scaleTransform] let transformedQuad = quad.applyTransforms(transforms) - + quadView.drawQuadrilateral(quad: transformedQuad, animated: false) } - + /// The quadView should be lined up on top of the actual image displayed by the imageView. - /// Since there is no way to know the size of that image before run time, we adjust the constraints to make sure that the quadView is on top of the displayed image. + /// Since there is no way to know the size of that image before run time, we adjust the constraints + /// to make sure that the quadView is on top of the displayed image. private func adjustQuadViewConstraints() { let frame = AVMakeRect(aspectRatio: image.size, insideRect: imageView.bounds) quadViewWidthConstraint.constant = frame.size.width quadViewHeightConstraint.constant = frame.size.height } - + /// Generates a `Quadrilateral` object that's centered and 90% of the size of the passed in image. private static func defaultQuad(forImage image: UIImage) -> Quadrilateral { let topLeft = CGPoint(x: image.size.width * 0.05, y: image.size.height * 0.05) let topRight = CGPoint(x: image.size.width * 0.95, y: image.size.height * 0.05) let bottomRight = CGPoint(x: image.size.width * 0.95, y: image.size.height * 0.95) let bottomLeft = CGPoint(x: image.size.width * 0.05, y: image.size.height * 0.95) - + let quad = Quadrilateral(topLeft: topLeft, topRight: topRight, bottomRight: bottomRight, bottomLeft: bottomLeft) - + return quad } - + } diff --git a/WeScan/Edit/ZoomGestureController.swift b/Sources/WeScan/Edit/ZoomGestureController.swift similarity index 88% rename from WeScan/Edit/ZoomGestureController.swift rename to Sources/WeScan/Edit/ZoomGestureController.swift index 12b892c8..65aa1b18 100644 --- a/WeScan/Edit/ZoomGestureController.swift +++ b/Sources/WeScan/Edit/ZoomGestureController.swift @@ -6,12 +6,12 @@ // Copyright © 2018 WeTransfer. All rights reserved. // -import Foundation import AVFoundation +import Foundation import UIKit final class ZoomGestureController { - + private let image: UIImage private let quadView: QuadrilateralView private var previousPanPosition: CGPoint? @@ -26,35 +26,39 @@ final class ZoomGestureController { guard let drawnQuad = quadView.quad else { return } - + guard pan.state != .ended else { self.previousPanPosition = nil self.closestCorner = nil quadView.resetHighlightedCornerViews() return } - + let position = pan.location(in: quadView) - + let previousPanPosition = self.previousPanPosition ?? position let closestCorner = self.closestCorner ?? position.closestCornerFrom(quad: drawnQuad) - + let offset = CGAffineTransform(translationX: position.x - previousPanPosition.x, y: position.y - previousPanPosition.y) let cornerView = quadView.cornerViewForCornerPosition(position: closestCorner) let draggedCornerViewCenter = cornerView.center.applying(offset) - + quadView.moveCorner(cornerView: cornerView, atPoint: draggedCornerViewCenter) - + self.previousPanPosition = position self.closestCorner = closestCorner - + let scale = image.size.width / quadView.bounds.size.width let scaledDraggedCornerViewCenter = CGPoint(x: draggedCornerViewCenter.x * scale, y: draggedCornerViewCenter.y * scale) - guard let zoomedImage = image.scaledImage(atPoint: scaledDraggedCornerViewCenter, scaleFactor: 2.5, targetSize: quadView.bounds.size) else { + guard let zoomedImage = image.scaledImage( + atPoint: scaledDraggedCornerViewCenter, + scaleFactor: 2.5, + targetSize: quadView.bounds.size + ) else { return } - + quadView.highlightCornerAtPosition(position: closestCorner, with: zoomedImage) } - + } diff --git a/WeScan/Extensions/AVCaptureVideoOrientation+Utils.swift b/Sources/WeScan/Extensions/AVCaptureVideoOrientation+Utils.swift similarity index 99% rename from WeScan/Extensions/AVCaptureVideoOrientation+Utils.swift rename to Sources/WeScan/Extensions/AVCaptureVideoOrientation+Utils.swift index 75669a35..c977ab02 100644 --- a/WeScan/Extensions/AVCaptureVideoOrientation+Utils.swift +++ b/Sources/WeScan/Extensions/AVCaptureVideoOrientation+Utils.swift @@ -6,13 +6,13 @@ // Copyright © 2018 WeTransfer. All rights reserved. // -import Foundation import AVFoundation import CoreImage +import Foundation import UIKit extension AVCaptureVideoOrientation { - + /// Maps UIDeviceOrientation to AVCaptureVideoOrientation init?(deviceOrientation: UIDeviceOrientation) { switch deviceOrientation { @@ -32,5 +32,5 @@ extension AVCaptureVideoOrientation { self.init(rawValue: AVCaptureVideoOrientation.portrait.rawValue) } } - + } diff --git a/WeScan/Extensions/Array+Utils.swift b/Sources/WeScan/Extensions/Array+Utils.swift similarity index 82% rename from WeScan/Extensions/Array+Utils.swift rename to Sources/WeScan/Extensions/Array+Utils.swift index 79214aba..0c87f46d 100644 --- a/WeScan/Extensions/Array+Utils.swift +++ b/Sources/WeScan/Extensions/Array+Utils.swift @@ -6,18 +6,18 @@ // Copyright © 2018 WeTransfer. All rights reserved. // -import Vision import Foundation +import Vision extension Array where Element == Quadrilateral { - + /// Finds the biggest rectangle within an array of `Quadrilateral` objects. func biggest() -> Quadrilateral? { - let biggestRectangle = self.max(by: { (rect1, rect2) -> Bool in + let biggestRectangle = self.max(by: { rect1, rect2 -> Bool in return rect1.perimeter < rect2.perimeter }) - + return biggestRectangle } - + } diff --git a/WeScan/Extensions/CGAffineTransform+Utils.swift b/Sources/WeScan/Extensions/CGAffineTransform+Utils.swift similarity index 98% rename from WeScan/Extensions/CGAffineTransform+Utils.swift rename to Sources/WeScan/Extensions/CGAffineTransform+Utils.swift index fa2387c8..e696d86b 100644 --- a/WeScan/Extensions/CGAffineTransform+Utils.swift +++ b/Sources/WeScan/Extensions/CGAffineTransform+Utils.swift @@ -6,12 +6,12 @@ // Copyright © 2018 WeTransfer. All rights reserved. // +import CoreImage import Foundation import UIKit -import CoreImage extension CGAffineTransform { - + /// Convenience function to easily get a scale `CGAffineTransform` instance. /// /// - Parameters: @@ -22,7 +22,7 @@ extension CGAffineTransform { let scale = max(toSize.width / fromSize.width, toSize.height / fromSize.height) return CGAffineTransform(scaleX: scale, y: scale) } - + /// Convenience function to easily get a translate `CGAffineTransform` instance. /// /// - Parameters: @@ -33,5 +33,5 @@ extension CGAffineTransform { let translate = CGPoint(x: toRect.midX - fromRect.midX, y: toRect.midY - fromRect.midY) return CGAffineTransform(translationX: translate.x, y: translate.y) } - + } diff --git a/WeScan/Extensions/CGImagePropertyOrientation.swift b/Sources/WeScan/Extensions/CGImagePropertyOrientation.swift similarity index 100% rename from WeScan/Extensions/CGImagePropertyOrientation.swift rename to Sources/WeScan/Extensions/CGImagePropertyOrientation.swift diff --git a/WeScan/Extensions/CGPoint+Utils.swift b/Sources/WeScan/Extensions/CGPoint+Utils.swift similarity index 97% rename from WeScan/Extensions/CGPoint+Utils.swift rename to Sources/WeScan/Extensions/CGPoint+Utils.swift index df332f29..c0a0766c 100644 --- a/WeScan/Extensions/CGPoint+Utils.swift +++ b/Sources/WeScan/Extensions/CGPoint+Utils.swift @@ -10,7 +10,7 @@ import Foundation import UIKit extension CGPoint { - + /// Returns a rectangle of a given size surounding the point. /// /// - Parameters: @@ -19,7 +19,7 @@ extension CGPoint { func surroundingSquare(withSize size: CGFloat) -> CGRect { return CGRect(x: x - size / 2.0, y: y - size / 2.0, width: size, height: size) } - + /// Checks wether this point is within a given distance of another point. /// /// - Parameters: @@ -29,7 +29,7 @@ extension CGPoint { func isWithin(delta: CGFloat, ofPoint point: CGPoint) -> Bool { return (abs(x - point.x) <= delta) && (abs(y - point.y) <= delta) } - + /// Returns the same `CGPoint` in the cartesian coordinate system. /// /// - Parameters: @@ -38,33 +38,33 @@ extension CGPoint { func cartesian(withHeight height: CGFloat) -> CGPoint { return CGPoint(x: x, y: height - y) } - + /// Returns the distance between two points func distanceTo(point: CGPoint) -> CGFloat { return hypot((self.x - point.x), (self.y - point.y)) } - + /// Returns the closest corner from the point func closestCornerFrom(quad: Quadrilateral) -> CornerPosition { var smallestDistance = distanceTo(point: quad.topLeft) var closestCorner = CornerPosition.topLeft - + if distanceTo(point: quad.topRight) < smallestDistance { smallestDistance = distanceTo(point: quad.topRight) closestCorner = .topRight } - + if distanceTo(point: quad.bottomRight) < smallestDistance { smallestDistance = distanceTo(point: quad.bottomRight) closestCorner = .bottomRight } - + if distanceTo(point: quad.bottomLeft) < smallestDistance { smallestDistance = distanceTo(point: quad.bottomLeft) closestCorner = .bottomLeft } - + return closestCorner } - + } diff --git a/WeScan/Extensions/CGRect+Utils.swift b/Sources/WeScan/Extensions/CGRect+Utils.swift similarity index 77% rename from WeScan/Extensions/CGRect+Utils.swift rename to Sources/WeScan/Extensions/CGRect+Utils.swift index e9b45533..86d88804 100644 --- a/WeScan/Extensions/CGRect+Utils.swift +++ b/Sources/WeScan/Extensions/CGRect+Utils.swift @@ -10,7 +10,7 @@ import Foundation import UIKit extension CGRect { - + /// Returns a new `CGRect` instance scaled up or down, with the same center as the original `CGRect` instance. /// - Parameters: /// - ratio: The ratio to scale the `CGRect` instance by. @@ -18,11 +18,14 @@ extension CGRect { func scaleAndCenter(withRatio ratio: CGFloat) -> CGRect { let scaleTransform = CGAffineTransform(scaleX: ratio, y: ratio) let scaledRect = applying(scaleTransform) - - let translateTransform = CGAffineTransform(translationX: origin.x * (1 - ratio) + (width - scaledRect.width) / 2.0, y: origin.y * (1 - ratio) + (height - scaledRect.height) / 2.0) + + let translateTransform = CGAffineTransform( + translationX: origin.x * (1 - ratio) + (width - scaledRect.width) / 2.0, + y: origin.y * (1 - ratio) + (height - scaledRect.height) / 2.0 + ) let translatedRect = scaledRect.applying(translateTransform) - + return translatedRect } - + } diff --git a/WeScan/Extensions/CGSize+Utils.swift b/Sources/WeScan/Extensions/CGSize+Utils.swift similarity index 98% rename from WeScan/Extensions/CGSize+Utils.swift rename to Sources/WeScan/Extensions/CGSize+Utils.swift index 13bb37ed..b4ebb53e 100644 --- a/WeScan/Extensions/CGSize+Utils.swift +++ b/Sources/WeScan/Extensions/CGSize+Utils.swift @@ -17,10 +17,10 @@ extension CGSize { /// - Returns: A scale factor that makes the size fit within the `maxWidth` and `maxHeight`. func scaleFactor(forMaxWidth maxWidth: CGFloat, maxHeight: CGFloat) -> CGFloat { if width < maxWidth && height < maxHeight { return 1 } - + let widthScaleFactor = 1 / (width / maxWidth) let heightScaleFactor = 1 / (height / maxHeight) - + // Use the smaller scale factor to ensure both the width and height are below the max return min(widthScaleFactor, heightScaleFactor) } diff --git a/WeScan/Extensions/CIImage+Utils.swift b/Sources/WeScan/Extensions/CIImage+Utils.swift similarity index 98% rename from WeScan/Extensions/CIImage+Utils.swift rename to Sources/WeScan/Extensions/CIImage+Utils.swift index c34c1484..c6c911d6 100644 --- a/WeScan/Extensions/CIImage+Utils.swift +++ b/Sources/WeScan/Extensions/CIImage+Utils.swift @@ -23,10 +23,10 @@ extension CIImage { } """ ) else { return nil } - + let firstInputEdge = 0.25 let secondInputEdge = 0.75 - + let arguments: [Any] = [self, firstInputEdge, secondInputEdge] guard let enhancedCIImage = colorKernel.apply(extent: self.extent, arguments: arguments) else { return nil } diff --git a/WeScan/Extensions/UIImage+Orientation.swift b/Sources/WeScan/Extensions/UIImage+Orientation.swift similarity index 96% rename from WeScan/Extensions/UIImage+Orientation.swift rename to Sources/WeScan/Extensions/UIImage+Orientation.swift index 461b2762..2283e94b 100644 --- a/WeScan/Extensions/UIImage+Orientation.swift +++ b/Sources/WeScan/Extensions/UIImage+Orientation.swift @@ -10,15 +10,15 @@ import Foundation import UIKit extension UIImage { - + /// Data structure to easily express rotation options. struct RotationOptions: OptionSet { let rawValue: Int - + static let flipOnVerticalAxis = RotationOptions(rawValue: 1) static let flipOnHorizontalAxis = RotationOptions(rawValue: 2) } - + /// Returns the same image with a portrait orientation. func applyingPortraitOrientation() -> UIImage { switch imageOrientation { @@ -43,37 +43,37 @@ extension UIImage { /// - Returns: The new image rotated and optentially flipped (@see options). func rotated(by rotationAngle: Measurement, options: RotationOptions = []) -> UIImage? { guard let cgImage = self.cgImage else { return nil } - + let rotationInRadians = CGFloat(rotationAngle.converted(to: .radians).value) let transform = CGAffineTransform(rotationAngle: rotationInRadians) let cgImageSize = CGSize(width: cgImage.width, height: cgImage.height) var rect = CGRect(origin: .zero, size: cgImageSize).applying(transform) rect.origin = .zero - + let format = UIGraphicsImageRendererFormat() format.scale = 1 - + let renderer = UIGraphicsImageRenderer(size: rect.size, format: format) - + let image = renderer.image { renderContext in renderContext.cgContext.translateBy(x: rect.midX, y: rect.midY) renderContext.cgContext.rotate(by: rotationInRadians) - + let x = options.contains(.flipOnVerticalAxis) ? -1.0 : 1.0 let y = options.contains(.flipOnHorizontalAxis) ? 1.0 : -1.0 renderContext.cgContext.scaleBy(x: CGFloat(x), y: CGFloat(y)) - + let drawRect = CGRect(origin: CGPoint(x: -cgImageSize.width / 2.0, y: -cgImageSize.height / 2.0), size: cgImageSize) renderContext.cgContext.draw(cgImage, in: drawRect) } - + return image } - + /// Rotates the image based on the information collected by the accelerometer func withFixedOrientation() -> UIImage { var imageAngle: Double = 0.0 - + var shouldRotate = true switch CaptureSession.current.editImageOrientation { case .up: @@ -87,7 +87,7 @@ extension UIImage { default: shouldRotate = false } - + if shouldRotate, let finalImage = rotated(by: Measurement(value: imageAngle, unit: .radians)) { return finalImage @@ -95,5 +95,5 @@ extension UIImage { return self } } - + } diff --git a/WeScan/Extensions/UIImage+SFSymbol.swift b/Sources/WeScan/Extensions/UIImage+SFSymbol.swift similarity index 62% rename from WeScan/Extensions/UIImage+SFSymbol.swift rename to Sources/WeScan/Extensions/UIImage+SFSymbol.swift index aa82b59e..e6465aae 100644 --- a/WeScan/Extensions/UIImage+SFSymbol.swift +++ b/Sources/WeScan/Extensions/UIImage+SFSymbol.swift @@ -9,9 +9,15 @@ import UIKit extension UIImage { - - /// Creates an image object containing a system symbol image appropriate for the specified traits if supported (iOS13 and above). Otherwise an image object using the named image asset that is compatible with the specified trait collection will be created. - convenience init?(systemName: String, named: String, in bundle: Bundle? = nil, compatibleWith traitCollection: UITraitCollection? = nil) { + + /// Creates an image object containing a system symbol image appropriate for the specified traits if supported (iOS13 and above). + /// Otherwise an image object using the named image asset that is compatible with the specified trait collection will be created. + convenience init?( + systemName: String, + named: String, + in bundle: Bundle? = nil, + compatibleWith traitCollection: UITraitCollection? = nil + ) { if #available(iOS 13.0, *) { self.init(systemName: systemName, compatibleWith: traitCollection) } else { diff --git a/WeScan/Extensions/UIImage+Utils.swift b/Sources/WeScan/Extensions/UIImage+Utils.swift similarity index 75% rename from WeScan/Extensions/UIImage+Utils.swift rename to Sources/WeScan/Extensions/UIImage+Utils.swift index 79690714..2fdabc6b 100644 --- a/WeScan/Extensions/UIImage+Utils.swift +++ b/Sources/WeScan/Extensions/UIImage+Utils.swift @@ -19,19 +19,19 @@ extension UIImage { /// - Returns: The scaled and cropped image. func scaledImage(atPoint point: CGPoint, scaleFactor: CGFloat, targetSize size: CGSize) -> UIImage? { guard let cgImage = self.cgImage else { return nil } - + let scaledSize = CGSize(width: size.width / scaleFactor, height: size.height / scaleFactor) let midX = point.x - scaledSize.width / 2.0 let midY = point.y - scaledSize.height / 2.0 let newRect = CGRect(x: midX, y: midY, width: scaledSize.width, height: scaledSize.height) - + guard let croppedImage = cgImage.cropping(to: newRect) else { return nil } - + return UIImage(cgImage: croppedImage) } - + /// Scales the image to the specified size in the RGB color space. /// /// - Parameters: @@ -39,53 +39,72 @@ extension UIImage { /// - Returns: The scaled image. func scaledImage(scaleFactor: CGFloat) -> UIImage? { guard let cgImage = self.cgImage else { return nil } - + let customColorSpace = CGColorSpaceCreateDeviceRGB() - + let width = CGFloat(cgImage.width) * scaleFactor let height = CGFloat(cgImage.height) * scaleFactor let bitsPerComponent = cgImage.bitsPerComponent let bytesPerRow = cgImage.bytesPerRow let bitmapInfo = cgImage.bitmapInfo.rawValue - - guard let context = CGContext(data: nil, width: Int(width), height: Int(height), bitsPerComponent: bitsPerComponent, bytesPerRow: bytesPerRow, space: customColorSpace, bitmapInfo: bitmapInfo) else { return nil } - + + guard let context = CGContext( + data: nil, + width: Int(width), + height: Int(height), + bitsPerComponent: bitsPerComponent, + bytesPerRow: bytesPerRow, + space: customColorSpace, + bitmapInfo: bitmapInfo + ) else { return nil } + context.interpolationQuality = .high context.draw(cgImage, in: CGRect(origin: .zero, size: CGSize(width: width, height: height))) - + return context.makeImage().flatMap { UIImage(cgImage: $0) } } - + /// Returns the data for the image in the PDF format func pdfData() -> Data? { // Typical Letter PDF page size and margins let pageBounds = CGRect(x: 0, y: 0, width: 595, height: 842) let margin: CGFloat = 40 - + let imageMaxWidth = pageBounds.width - (margin * 2) let imageMaxHeight = pageBounds.height - (margin * 2) - + let image = scaledImage(scaleFactor: size.scaleFactor(forMaxWidth: imageMaxWidth, maxHeight: imageMaxHeight)) ?? self let renderer = UIGraphicsPDFRenderer(bounds: pageBounds) - let data = renderer.pdfData { (ctx) in + let data = renderer.pdfData { ctx in ctx.beginPage() - + ctx.cgContext.interpolationQuality = .high image.draw(at: CGPoint(x: margin, y: margin)) } - + return data } - - /// Function gathered from [here](https://stackoverflow.com/questions/44462087/how-to-convert-a-uiimage-to-a-cvpixelbuffer) to convert UIImage to CVPixelBuffer + + /// Function gathered from [here](https://stackoverflow.com/questions/44462087/how-to-convert-a-uiimage-to-a-cvpixelbuffer) + /// to convert UIImage to CVPixelBuffer /// /// - Returns: new [CVPixelBuffer](apple-reference-documentation://hsVf8OXaJX) func pixelBuffer() -> CVPixelBuffer? { - let attrs = [kCVPixelBufferCGImageCompatibilityKey: kCFBooleanTrue, kCVPixelBufferCGBitmapContextCompatibilityKey: kCFBooleanTrue] as CFDictionary + let attrs = [ + kCVPixelBufferCGImageCompatibilityKey: kCFBooleanTrue, + kCVPixelBufferCGBitmapContextCompatibilityKey: kCFBooleanTrue + ] as CFDictionary var pixelBufferOpt: CVPixelBuffer? - let status = CVPixelBufferCreate(kCFAllocatorDefault, Int(self.size.width), Int(self.size.height), kCVPixelFormatType_32ARGB, attrs, &pixelBufferOpt) + let status = CVPixelBufferCreate( + kCFAllocatorDefault, + Int(self.size.width), + Int(self.size.height), + kCVPixelFormatType_32ARGB, + attrs, + &pixelBufferOpt + ) guard status == kCVReturnSuccess, let pixelBuffer = pixelBufferOpt else { return nil } @@ -94,8 +113,16 @@ extension UIImage { let pixelData = CVPixelBufferGetBaseAddress(pixelBuffer) let rgbColorSpace = CGColorSpaceCreateDeviceRGB() - guard let context = CGContext(data: pixelData, width: Int(self.size.width), height: Int(self.size.height), bitsPerComponent: 8, bytesPerRow: CVPixelBufferGetBytesPerRow(pixelBuffer), space: rgbColorSpace, bitmapInfo: CGImageAlphaInfo.noneSkipFirst.rawValue) else { - return nil + guard let context = CGContext( + data: pixelData, + width: Int(self.size.width), + height: Int(self.size.height), + bitsPerComponent: 8, + bytesPerRow: CVPixelBufferGetBytesPerRow(pixelBuffer), + space: rgbColorSpace, + bitmapInfo: CGImageAlphaInfo.noneSkipFirst.rawValue + ) else { + return nil } context.translateBy(x: 0, y: self.size.height) diff --git a/WeScan/ImageScannerController.swift b/Sources/WeScan/ImageScannerController.swift similarity index 91% rename from WeScan/ImageScannerController.swift rename to Sources/WeScan/ImageScannerController.swift index f5746970..b6d915d0 100644 --- a/WeScan/ImageScannerController.swift +++ b/Sources/WeScan/ImageScannerController.swift @@ -6,12 +6,12 @@ // Copyright © 2018 WeTransfer. All rights reserved. // -import UIKit import AVFoundation +import UIKit /// A set of methods that your delegate object must implement to interact with the image scanner interface. public protocol ImageScannerControllerDelegate: NSObjectProtocol { - + /// Tells the delegate that the user scanned a document. /// /// - Parameters: @@ -19,14 +19,14 @@ public protocol ImageScannerControllerDelegate: NSObjectProtocol { /// - results: The results of the user scanning with the camera. /// - Discussion: Your delegate's implementation of this method should dismiss the image scanner controller. func imageScannerController(_ scanner: ImageScannerController, didFinishScanningWithResults results: ImageScannerResults) - + /// Tells the delegate that the user cancelled the scan operation. /// /// - Parameters: /// - scanner: The scanner controller object managing the scanning interface. /// - Discussion: Your delegate's implementation of this method should dismiss the image scanner controller. func imageScannerControllerDidCancel(_ scanner: ImageScannerController) - + /// Tells the delegate that an error occured during the user's scanning experience. /// /// - Parameters: @@ -41,12 +41,12 @@ public protocol ImageScannerControllerDelegate: NSObjectProtocol { /// 2. Edit the detected rectangle. /// 3. Review the cropped down version of the rectangle. public final class ImageScannerController: UINavigationController { - + /// The object that acts as the delegate of the `ImageScannerController`. public weak var imageScannerDelegate: ImageScannerControllerDelegate? - + // MARK: - Life Cycle - + /// A black UIView, used to quickly display a black screen when the shutter button is presseed. internal let blackFlashView: UIView = { let view = UIView() @@ -59,12 +59,12 @@ public final class ImageScannerController: UINavigationController { override public var supportedInterfaceOrientations: UIInterfaceOrientationMask { return .portrait } - + public required init(image: UIImage? = nil, delegate: ImageScannerControllerDelegate? = nil) { super.init(rootViewController: ScannerViewController()) - + self.imageScannerDelegate = delegate - + if #available(iOS 13.0, *) { navigationBar.tintColor = .label } else { @@ -73,11 +73,11 @@ public final class ImageScannerController: UINavigationController { navigationBar.isTranslucent = false self.view.addSubview(blackFlashView) setupConstraints() - + // If an image was passed in by the host app (e.g. picked from the photo library), use it instead of the document scanner. - if let image = image { + if let image { detect(image: image) { [weak self] detectedQuad in - guard let self = self else { return } + guard let self else { return } let editViewController = EditScanViewController(image: image, quad: detectedQuad, rotateImage: false) self.setViewControllers([editViewController], animated: false) } @@ -87,23 +87,23 @@ public final class ImageScannerController: UINavigationController { override public init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) } - + public required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + private func detect(image: UIImage, completion: @escaping (Quadrilateral?) -> Void) { // Whether or not we detect a quad, present the edit view controller after attempting to detect a quad. // *** Vision *requires* a completion block to detect rectangles, but it's instant. // *** When using Vision, we'll present the normal edit view controller first, then present the updated edit view controller later. - + guard let ciImage = CIImage(image: image) else { return } let orientation = CGImagePropertyOrientation(image.imageOrientation) let orientedImage = ciImage.oriented(forExifOrientation: Int32(orientation.rawValue)) - + if #available(iOS 11.0, *) { // Use the VisionRectangleDetector on iOS 11 to attempt to find a rectangle from the initial image. - VisionRectangleDetector.rectangle(forImage: ciImage, orientation: orientation) { (quad) in + VisionRectangleDetector.rectangle(forImage: ciImage, orientation: orientation) { quad in let detectedQuad = quad?.toCartesian(withHeight: orientedImage.extent.height) completion(detectedQuad) } @@ -113,21 +113,21 @@ public final class ImageScannerController: UINavigationController { completion(detectedQuad) } } - + public func useImage(image: UIImage) { guard topViewController is ScannerViewController else { return } - + detect(image: image) { [weak self] detectedQuad in - guard let self = self else { return } + guard let self else { return } let editViewController = EditScanViewController(image: image, quad: detectedQuad, rotateImage: false) self.setViewControllers([editViewController], animated: true) } } - + public func resetScanner() { setViewControllers([ScannerViewController()], animated: true) } - + private func setupConstraints() { let blackFlashViewConstraints = [ blackFlashView.topAnchor.constraint(equalTo: view.topAnchor), @@ -135,10 +135,10 @@ public final class ImageScannerController: UINavigationController { view.bottomAnchor.constraint(equalTo: blackFlashView.bottomAnchor), view.trailingAnchor.constraint(equalTo: blackFlashView.trailingAnchor) ] - + NSLayoutConstraint.activate(blackFlashViewConstraints) } - + internal func flashToBlack() { view.bringSubviewToFront(blackFlashView) blackFlashView.isHidden = false @@ -154,9 +154,9 @@ public struct ImageScannerScan { public enum ImageScannerError: Error { case failedToGeneratePDF } - + public var image: UIImage - + public func generatePDFData(completion: @escaping (Result) -> Void) { DispatchQueue.global(qos: .userInteractive).async { if let pdfData = self.image.pdfData() { @@ -165,9 +165,9 @@ public struct ImageScannerScan { completion(.failure(.failedToGeneratePDF)) } } - + } - + mutating func rotate(by rotationAngle: Measurement) { guard rotationAngle.value != 0, rotationAngle.value != 360 else { return } image = image.rotated(by: rotationAngle) ?? image @@ -175,44 +175,52 @@ public struct ImageScannerScan { } /// Data structure containing information about a scanning session. -/// Includes the original scan, cropped scan, detected rectangle, and whether the user selected the enhanced scan. May also include an enhanced scan if no errors were encountered. +/// Includes the original scan, cropped scan, detected rectangle, and whether the user selected the enhanced scan. +/// May also include an enhanced scan if no errors were encountered. public struct ImageScannerResults { - + /// The original scan taken by the user, prior to the cropping applied by WeScan. public var originalScan: ImageScannerScan - + /// The deskewed and cropped scan using the detected rectangle, without any filters. public var croppedScan: ImageScannerScan - - /// The enhanced scan, passed through an Adaptive Thresholding function. This image will always be grayscale and may not always be available. + + /// The enhanced scan, passed through an Adaptive Thresholding function. + /// This image will always be grayscale and may not always be available. public var enhancedScan: ImageScannerScan? - + /// Whether the user selected the enhanced scan or not. /// The `enhancedScan` may still be available even if it has not been selected by the user. public var doesUserPreferEnhancedScan: Bool - + /// The detected rectangle which was used to generate the `scannedImage`. public var detectedRectangle: Quadrilateral - + @available(*, unavailable, renamed: "originalScan") public var originalImage: UIImage? - + @available(*, unavailable, renamed: "croppedScan") public var scannedImage: UIImage? - + @available(*, unavailable, renamed: "enhancedScan") public var enhancedImage: UIImage? - + @available(*, unavailable, renamed: "doesUserPreferEnhancedScan") - public var doesUserPreferEnhancedImage: Bool = false - - init(detectedRectangle: Quadrilateral, originalScan: ImageScannerScan, croppedScan: ImageScannerScan, enhancedScan: ImageScannerScan?, doesUserPreferEnhancedScan: Bool = false) { + public var doesUserPreferEnhancedImage = false + + init( + detectedRectangle: Quadrilateral, + originalScan: ImageScannerScan, + croppedScan: ImageScannerScan, + enhancedScan: ImageScannerScan?, + doesUserPreferEnhancedScan: Bool = false + ) { self.detectedRectangle = detectedRectangle - + self.originalScan = originalScan self.croppedScan = croppedScan self.enhancedScan = enhancedScan - + self.doesUserPreferEnhancedScan = doesUserPreferEnhancedScan } } diff --git a/WeScan/Protocols/CaptureDevice.swift b/Sources/WeScan/Protocols/CaptureDevice.swift similarity index 87% rename from WeScan/Protocols/CaptureDevice.swift rename to Sources/WeScan/Protocols/CaptureDevice.swift index e7f9259f..11e04626 100644 --- a/WeScan/Protocols/CaptureDevice.swift +++ b/Sources/WeScan/Protocols/CaptureDevice.swift @@ -6,17 +6,17 @@ // Copyright © 2018 WeTransfer. All rights reserved. // -import Foundation import AVFoundation +import Foundation protocol CaptureDevice: AnyObject { var torchMode: AVCaptureDevice.TorchMode { get set } var isTorchAvailable: Bool { get } - + var focusMode: AVCaptureDevice.FocusMode { get set } var focusPointOfInterest: CGPoint { get set } var isFocusPointOfInterestSupported: Bool { get } - + var exposureMode: AVCaptureDevice.ExposureMode { get set } var exposurePointOfInterest: CGPoint { get set } var isExposurePointOfInterestSupported: Bool { get } @@ -33,16 +33,16 @@ extension AVCaptureDevice: CaptureDevice { } final class MockCaptureDevice: CaptureDevice { var torchMode: AVCaptureDevice.TorchMode = .off - var isTorchAvailable: Bool = true - + var isTorchAvailable = true + var focusMode: AVCaptureDevice.FocusMode = .continuousAutoFocus var focusPointOfInterest: CGPoint = .zero - var isFocusPointOfInterestSupported: Bool = true - + var isFocusPointOfInterestSupported = true + var exposureMode: AVCaptureDevice.ExposureMode = .continuousAutoExposure var exposurePointOfInterest: CGPoint = .zero - var isExposurePointOfInterestSupported: Bool = true - var isSubjectAreaChangeMonitoringEnabled: Bool = false + var isExposurePointOfInterestSupported = true + var isSubjectAreaChangeMonitoringEnabled = false func unlockForConfiguration() { return @@ -55,7 +55,7 @@ final class MockCaptureDevice: CaptureDevice { func isFocusModeSupported(_ focusMode: AVCaptureDevice.FocusMode) -> Bool { return true } - + func isExposureModeSupported(_ exposureMode: AVCaptureDevice.ExposureMode) -> Bool { return true } diff --git a/WeScan/Protocols/Transformable.swift b/Sources/WeScan/Protocols/Transformable.swift similarity index 92% rename from WeScan/Protocols/Transformable.swift rename to Sources/WeScan/Protocols/Transformable.swift index a4451f6a..05e4b638 100644 --- a/WeScan/Protocols/Transformable.swift +++ b/Sources/WeScan/Protocols/Transformable.swift @@ -11,7 +11,7 @@ import UIKit /// Objects that conform to the Transformable protocol are capable of being transformed with a `CGAffineTransform`. protocol Transformable { - + /// Applies the given `CGAffineTransform`. /// /// - Parameters: @@ -22,21 +22,21 @@ protocol Transformable { } extension Transformable { - + /// Applies multiple given transforms in the given order. /// /// - Parameters: /// - transforms: The transforms to apply. /// - Returns: The same object transformed by the passed in `CGAffineTransform`s. func applyTransforms(_ transforms: [CGAffineTransform]) -> Self { - + var transformableObject = self - - transforms.forEach { (transform) in + + transforms.forEach { transform in transformableObject = transformableObject.applying(transform) } - + return transformableObject } - + } diff --git a/WeScan/Resources/Assets/enhance.png b/Sources/WeScan/Resources/Assets/enhance.png similarity index 100% rename from WeScan/Resources/Assets/enhance.png rename to Sources/WeScan/Resources/Assets/enhance.png diff --git a/WeScan/Resources/Assets/enhance@2x.png b/Sources/WeScan/Resources/Assets/enhance@2x.png similarity index 100% rename from WeScan/Resources/Assets/enhance@2x.png rename to Sources/WeScan/Resources/Assets/enhance@2x.png diff --git a/WeScan/Resources/Assets/enhance@3x.png b/Sources/WeScan/Resources/Assets/enhance@3x.png similarity index 100% rename from WeScan/Resources/Assets/enhance@3x.png rename to Sources/WeScan/Resources/Assets/enhance@3x.png diff --git a/WeScan/Resources/Assets/flash.png b/Sources/WeScan/Resources/Assets/flash.png similarity index 100% rename from WeScan/Resources/Assets/flash.png rename to Sources/WeScan/Resources/Assets/flash.png diff --git a/WeScan/Resources/Assets/flash@2x.png b/Sources/WeScan/Resources/Assets/flash@2x.png similarity index 100% rename from WeScan/Resources/Assets/flash@2x.png rename to Sources/WeScan/Resources/Assets/flash@2x.png diff --git a/WeScan/Resources/Assets/flash@3x.png b/Sources/WeScan/Resources/Assets/flash@3x.png similarity index 100% rename from WeScan/Resources/Assets/flash@3x.png rename to Sources/WeScan/Resources/Assets/flash@3x.png diff --git a/WeScan/Resources/Assets/flashUnavailable.png b/Sources/WeScan/Resources/Assets/flashUnavailable.png similarity index 100% rename from WeScan/Resources/Assets/flashUnavailable.png rename to Sources/WeScan/Resources/Assets/flashUnavailable.png diff --git a/WeScan/Resources/Assets/flashUnavailable@2x.png b/Sources/WeScan/Resources/Assets/flashUnavailable@2x.png similarity index 100% rename from WeScan/Resources/Assets/flashUnavailable@2x.png rename to Sources/WeScan/Resources/Assets/flashUnavailable@2x.png diff --git a/WeScan/Resources/Assets/flashUnavailable@3x.png b/Sources/WeScan/Resources/Assets/flashUnavailable@3x.png similarity index 100% rename from WeScan/Resources/Assets/flashUnavailable@3x.png rename to Sources/WeScan/Resources/Assets/flashUnavailable@3x.png diff --git a/WeScan/Resources/Assets/rotate.png b/Sources/WeScan/Resources/Assets/rotate.png similarity index 100% rename from WeScan/Resources/Assets/rotate.png rename to Sources/WeScan/Resources/Assets/rotate.png diff --git a/WeScan/Resources/Assets/rotate@2x.png b/Sources/WeScan/Resources/Assets/rotate@2x.png similarity index 100% rename from WeScan/Resources/Assets/rotate@2x.png rename to Sources/WeScan/Resources/Assets/rotate@2x.png diff --git a/WeScan/Resources/Assets/rotate@3x.png b/Sources/WeScan/Resources/Assets/rotate@3x.png similarity index 100% rename from WeScan/Resources/Assets/rotate@3x.png rename to Sources/WeScan/Resources/Assets/rotate@3x.png diff --git a/WeScan/Resources/Localisation/ar.lproj/Localizable.strings b/Sources/WeScan/Resources/Localisation/ar.lproj/Localizable.strings similarity index 100% rename from WeScan/Resources/Localisation/ar.lproj/Localizable.strings rename to Sources/WeScan/Resources/Localisation/ar.lproj/Localizable.strings diff --git a/WeScan/Resources/Localisation/cs.lproj/Localizable.strings b/Sources/WeScan/Resources/Localisation/cs.lproj/Localizable.strings similarity index 100% rename from WeScan/Resources/Localisation/cs.lproj/Localizable.strings rename to Sources/WeScan/Resources/Localisation/cs.lproj/Localizable.strings diff --git a/WeScan/Resources/Localisation/de.lproj/Localizable.strings b/Sources/WeScan/Resources/Localisation/de.lproj/Localizable.strings similarity index 100% rename from WeScan/Resources/Localisation/de.lproj/Localizable.strings rename to Sources/WeScan/Resources/Localisation/de.lproj/Localizable.strings diff --git a/WeScan/Resources/Localisation/en.lproj/Localizable.strings b/Sources/WeScan/Resources/Localisation/en.lproj/Localizable.strings similarity index 100% rename from WeScan/Resources/Localisation/en.lproj/Localizable.strings rename to Sources/WeScan/Resources/Localisation/en.lproj/Localizable.strings diff --git a/WeScan/Resources/Localisation/es-419.lproj/Localizable.strings b/Sources/WeScan/Resources/Localisation/es-419.lproj/Localizable.strings similarity index 100% rename from WeScan/Resources/Localisation/es-419.lproj/Localizable.strings rename to Sources/WeScan/Resources/Localisation/es-419.lproj/Localizable.strings diff --git a/WeScan/Resources/Localisation/es.lproj/Localizable.strings b/Sources/WeScan/Resources/Localisation/es.lproj/Localizable.strings similarity index 100% rename from WeScan/Resources/Localisation/es.lproj/Localizable.strings rename to Sources/WeScan/Resources/Localisation/es.lproj/Localizable.strings diff --git a/WeScan/Resources/Localisation/fr.lproj/Localizable.strings b/Sources/WeScan/Resources/Localisation/fr.lproj/Localizable.strings similarity index 100% rename from WeScan/Resources/Localisation/fr.lproj/Localizable.strings rename to Sources/WeScan/Resources/Localisation/fr.lproj/Localizable.strings diff --git a/WeScan/Resources/Localisation/hu.lproj/Localizable.strings b/Sources/WeScan/Resources/Localisation/hu.lproj/Localizable.strings similarity index 100% rename from WeScan/Resources/Localisation/hu.lproj/Localizable.strings rename to Sources/WeScan/Resources/Localisation/hu.lproj/Localizable.strings diff --git a/WeScan/Resources/Localisation/it.lproj/Localizable.strings b/Sources/WeScan/Resources/Localisation/it.lproj/Localizable.strings similarity index 100% rename from WeScan/Resources/Localisation/it.lproj/Localizable.strings rename to Sources/WeScan/Resources/Localisation/it.lproj/Localizable.strings diff --git a/WeScan/Resources/Localisation/ko.lproj/Localizable.strings b/Sources/WeScan/Resources/Localisation/ko.lproj/Localizable.strings similarity index 100% rename from WeScan/Resources/Localisation/ko.lproj/Localizable.strings rename to Sources/WeScan/Resources/Localisation/ko.lproj/Localizable.strings diff --git a/WeScan/Resources/Localisation/nl.lproj/Localizable.strings b/Sources/WeScan/Resources/Localisation/nl.lproj/Localizable.strings similarity index 100% rename from WeScan/Resources/Localisation/nl.lproj/Localizable.strings rename to Sources/WeScan/Resources/Localisation/nl.lproj/Localizable.strings diff --git a/WeScan/Resources/Localisation/pl.lproj/Localizable.strings b/Sources/WeScan/Resources/Localisation/pl.lproj/Localizable.strings similarity index 100% rename from WeScan/Resources/Localisation/pl.lproj/Localizable.strings rename to Sources/WeScan/Resources/Localisation/pl.lproj/Localizable.strings diff --git a/WeScan/Resources/Localisation/pt-BR.lproj/Localizable.strings b/Sources/WeScan/Resources/Localisation/pt-BR.lproj/Localizable.strings similarity index 100% rename from WeScan/Resources/Localisation/pt-BR.lproj/Localizable.strings rename to Sources/WeScan/Resources/Localisation/pt-BR.lproj/Localizable.strings diff --git a/WeScan/Resources/Localisation/pt-PT.lproj/Localizable.strings b/Sources/WeScan/Resources/Localisation/pt-PT.lproj/Localizable.strings similarity index 100% rename from WeScan/Resources/Localisation/pt-PT.lproj/Localizable.strings rename to Sources/WeScan/Resources/Localisation/pt-PT.lproj/Localizable.strings diff --git a/WeScan/Resources/Localisation/ru.lproj/Localizable.strings b/Sources/WeScan/Resources/Localisation/ru.lproj/Localizable.strings similarity index 100% rename from WeScan/Resources/Localisation/ru.lproj/Localizable.strings rename to Sources/WeScan/Resources/Localisation/ru.lproj/Localizable.strings diff --git a/WeScan/Resources/Localisation/sv.lproj/Localizable.strings b/Sources/WeScan/Resources/Localisation/sv.lproj/Localizable.strings similarity index 100% rename from WeScan/Resources/Localisation/sv.lproj/Localizable.strings rename to Sources/WeScan/Resources/Localisation/sv.lproj/Localizable.strings diff --git a/WeScan/Resources/Localisation/tr.lproj/Localizable.strings b/Sources/WeScan/Resources/Localisation/tr.lproj/Localizable.strings similarity index 100% rename from WeScan/Resources/Localisation/tr.lproj/Localizable.strings rename to Sources/WeScan/Resources/Localisation/tr.lproj/Localizable.strings diff --git a/WeScan/Resources/Localisation/zh-Hans.lproj/Localizable.strings b/Sources/WeScan/Resources/Localisation/zh-Hans.lproj/Localizable.strings similarity index 100% rename from WeScan/Resources/Localisation/zh-Hans.lproj/Localizable.strings rename to Sources/WeScan/Resources/Localisation/zh-Hans.lproj/Localizable.strings diff --git a/WeScan/Resources/Localisation/zh-Hant.lproj/Localizable.strings b/Sources/WeScan/Resources/Localisation/zh-Hant.lproj/Localizable.strings similarity index 100% rename from WeScan/Resources/Localisation/zh-Hant.lproj/Localizable.strings rename to Sources/WeScan/Resources/Localisation/zh-Hant.lproj/Localizable.strings diff --git a/WeScan/Review/ReviewViewController.swift b/Sources/WeScan/Review/ReviewViewController.swift similarity index 87% rename from WeScan/Review/ReviewViewController.swift rename to Sources/WeScan/Review/ReviewViewController.swift index acc87e6d..2aabffb5 100644 --- a/WeScan/Review/ReviewViewController.swift +++ b/Sources/WeScan/Review/ReviewViewController.swift @@ -8,13 +8,14 @@ import UIKit -/// The `ReviewViewController` offers an interface to review the image after it has been cropped and deskwed according to the passed in quadrilateral. +/// The `ReviewViewController` offers an interface to review the image after it +/// has been cropped and deskwed according to the passed in quadrilateral. final class ReviewViewController: UIViewController { - + private var rotationAngle = Measurement(value: 0, unit: .degrees) private var enhancedImageIsAvailable = false private var isCurrentlyDisplayingEnhancedImage = false - + lazy var imageView: UIImageView = { let imageView = UIImageView() imageView.clipsToBounds = true @@ -25,86 +26,96 @@ final class ReviewViewController: UIViewController { imageView.translatesAutoresizingMaskIntoConstraints = false return imageView }() - + private lazy var enhanceButton: UIBarButtonItem = { - let image = UIImage(systemName: "wand.and.rays.inverse", named: "enhance", in: Bundle(for: ScannerViewController.self), compatibleWith: nil) + let image = UIImage( + systemName: "wand.and.rays.inverse", + named: "enhance", + in: Bundle(for: ScannerViewController.self), + compatibleWith: nil + ) let button = UIBarButtonItem(image: image, style: .plain, target: self, action: #selector(toggleEnhancedImage)) button.tintColor = .white return button }() - + private lazy var rotateButton: UIBarButtonItem = { let image = UIImage(systemName: "rotate.right", named: "rotate", in: Bundle(for: ScannerViewController.self), compatibleWith: nil) let button = UIBarButtonItem(image: image, style: .plain, target: self, action: #selector(rotateImage)) button.tintColor = .white return button }() - + private lazy var doneButton: UIBarButtonItem = { let button = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(finishScan)) button.tintColor = navigationController?.navigationBar.tintColor return button }() - + private let results: ImageScannerResults - + // MARK: - Life Cycle - + init(results: ImageScannerResults) { self.results = results super.init(nibName: nil, bundle: nil) } - + required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + override func viewDidLoad() { super.viewDidLoad() enhancedImageIsAvailable = results.enhancedScan != nil - + setupViews() setupToolbar() setupConstraints() - - title = NSLocalizedString("wescan.review.title", tableName: nil, bundle: Bundle(for: ReviewViewController.self), value: "Review", comment: "The review title of the ReviewController") + + title = NSLocalizedString("wescan.review.title", + tableName: nil, + bundle: Bundle(for: ReviewViewController.self), + value: "Review", + comment: "The review title of the ReviewController" + ) navigationItem.rightBarButtonItem = doneButton } - + override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) - + // We only show the toolbar (with the enhance button) if the enhanced image is available. if enhancedImageIsAvailable { navigationController?.setToolbarHidden(false, animated: true) } } - + override func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) navigationController?.setToolbarHidden(true, animated: true) } - + // MARK: Setups - + private func setupViews() { view.addSubview(imageView) } - + private func setupToolbar() { guard enhancedImageIsAvailable else { return } - + navigationController?.toolbar.barStyle = .blackTranslucent - + let fixedSpace = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil) let flexibleSpace = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) toolbarItems = [fixedSpace, enhanceButton, flexibleSpace, rotateButton, fixedSpace] } - + private func setupConstraints() { imageView.translatesAutoresizingMaskIntoConstraints = false - + var imageViewConstraints: [NSLayoutConstraint] = [] if #available(iOS 11.0, *) { imageViewConstraints = [ @@ -121,12 +132,12 @@ final class ReviewViewController: UIViewController { view.leadingAnchor.constraint(equalTo: imageView.leadingAnchor) ] } - + NSLayoutConstraint.activate(imageViewConstraints) } - + // MARK: - Actions - + @objc private func reloadImage() { if enhancedImageIsAvailable, isCurrentlyDisplayingEnhancedImage { imageView.image = results.enhancedScan?.image.rotated(by: rotationAngle) ?? results.enhancedScan?.image @@ -134,38 +145,39 @@ final class ReviewViewController: UIViewController { imageView.image = results.croppedScan.image.rotated(by: rotationAngle) ?? results.croppedScan.image } } - + @objc func toggleEnhancedImage() { guard enhancedImageIsAvailable else { return } - + isCurrentlyDisplayingEnhancedImage.toggle() reloadImage() - + if isCurrentlyDisplayingEnhancedImage { enhanceButton.tintColor = .yellow } else { enhanceButton.tintColor = .white } } - + @objc func rotateImage() { rotationAngle.value += 90 - + if rotationAngle.value == 360 { rotationAngle.value = 0 } - + reloadImage() } - + @objc private func finishScan() { guard let imageScannerController = navigationController as? ImageScannerController else { return } - + var newResults = results newResults.croppedScan.rotate(by: rotationAngle) newResults.enhancedScan?.rotate(by: rotationAngle) newResults.doesUserPreferEnhancedScan = isCurrentlyDisplayingEnhancedImage - imageScannerController.imageScannerDelegate?.imageScannerController(imageScannerController, didFinishScanningWithResults: newResults) + imageScannerController.imageScannerDelegate? + .imageScannerController(imageScannerController, didFinishScanningWithResults: newResults) } } diff --git a/WeScan/Scan/CameraScannerViewController.swift b/Sources/WeScan/Scan/CameraScannerViewController.swift similarity index 90% rename from WeScan/Scan/CameraScannerViewController.swift rename to Sources/WeScan/Scan/CameraScannerViewController.swift index aaada0fd..03cb3c4b 100644 --- a/WeScan/Scan/CameraScannerViewController.swift +++ b/Sources/WeScan/Scan/CameraScannerViewController.swift @@ -6,8 +6,8 @@ // Copyright © 2020 WeTransfer. All rights reserved. // -import UIKit import AVFoundation +import UIKit /// A set of methods that your delegate object must implement to get capture image. /// If camera module doesn't work it will send error back to your delegate object. @@ -17,7 +17,8 @@ public protocol CameraScannerViewOutputDelegate: AnyObject { } /// A view controller that manages the camera module and auto capture of rectangle shape of document -/// The `CameraScannerViewController` class is individual camera view include touch for focus, flash control, capture control and auto detect rectangle shape of object. +/// The `CameraScannerViewController` class is individual camera view include touch for focus, flash control, +/// capture control and auto detect rectangle shape of object. public final class CameraScannerViewController: UIViewController { /// The status of auto scan. @@ -32,21 +33,21 @@ public final class CameraScannerViewController: UIViewController { private var captureSessionManager: CaptureSessionManager? private let videoPreviewLayer = AVCaptureVideoPreviewLayer() - + /// The view that shows the focus rectangle (when the user taps to focus, similar to the Camera app) private var focusRectangle: FocusRectangleView! - + /// The view that draws the detected rectangles. private let quadView = QuadrilateralView() - + /// Whether flash is enabled private var flashEnabled = false - + override public func viewDidLoad() { super.viewDidLoad() setupView() } - + override public func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) CaptureSession.current.isEditing = false @@ -54,13 +55,13 @@ public final class CameraScannerViewController: UIViewController { captureSessionManager?.start() UIApplication.shared.isIdleTimerDisabled = true } - + override public func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() - + videoPreviewLayer.frame = view.layer.bounds } - + override public func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) UIApplication.shared.isIdleTimerDisabled = false @@ -70,7 +71,7 @@ public final class CameraScannerViewController: UIViewController { toggleFlash() } } - + private func setupView() { view.backgroundColor = .darkGray view.layer.addSublayer(videoPreviewLayer) @@ -78,16 +79,21 @@ public final class CameraScannerViewController: UIViewController { quadView.editable = false view.addSubview(quadView) setupConstraints() - + captureSessionManager = CaptureSessionManager(videoPreviewLayer: videoPreviewLayer) captureSessionManager?.delegate = self - - NotificationCenter.default.addObserver(self, selector: #selector(subjectAreaDidChange), name: Notification.Name.AVCaptureDeviceSubjectAreaDidChange, object: nil) + + NotificationCenter.default.addObserver( + self, + selector: #selector(subjectAreaDidChange), + name: Notification.Name.AVCaptureDeviceSubjectAreaDidChange, + object: nil + ) } - + private func setupConstraints() { var quadViewConstraints = [NSLayoutConstraint]() - + quadViewConstraints = [ quadView.topAnchor.constraint(equalTo: view.topAnchor), view.bottomAnchor.constraint(equalTo: quadView.bottomAnchor), @@ -96,50 +102,51 @@ public final class CameraScannerViewController: UIViewController { ] NSLayoutConstraint.activate(quadViewConstraints) } - - /// Called when the AVCaptureDevice detects that the subject area has changed significantly. When it's called, we reset the focus so the camera is no longer out of focus. + + /// Called when the AVCaptureDevice detects that the subject area has changed significantly. When it's called, + /// we reset the focus so the camera is no longer out of focus. @objc private func subjectAreaDidChange() { /// Reset the focus and exposure back to automatic do { try CaptureSession.current.resetFocusToAuto() } catch { let error = ImageScannerControllerError.inputDevice - guard let captureSessionManager = captureSessionManager else { return } + guard let captureSessionManager else { return } captureSessionManager.delegate?.captureSessionManager(captureSessionManager, didFailWithError: error) return } - + /// Remove the focus rectangle if one exists CaptureSession.current.removeFocusRectangleIfNeeded(focusRectangle, animated: true) } - + override public func touchesBegan(_ touches: Set, with event: UIEvent?) { super.touchesBegan(touches, with: event) - + guard let touch = touches.first else { return } let touchPoint = touch.location(in: view) let convertedTouchPoint: CGPoint = videoPreviewLayer.captureDevicePointConverted(fromLayerPoint: touchPoint) - + CaptureSession.current.removeFocusRectangleIfNeeded(focusRectangle, animated: false) - + focusRectangle = FocusRectangleView(touchPoint: touchPoint) focusRectangle.setBorder(color: UIColor.white.cgColor) view.addSubview(focusRectangle) - + do { try CaptureSession.current.setFocusPointToTapPoint(convertedTouchPoint) } catch { let error = ImageScannerControllerError.inputDevice - guard let captureSessionManager = captureSessionManager else { return } + guard let captureSessionManager else { return } captureSessionManager.delegate?.captureSessionManager(captureSessionManager, didFailWithError: error) return } } - + public func capture() { captureSessionManager?.capturePhoto() } - + public func toggleFlash() { let state = CaptureSession.current.toggleFlash() switch state { @@ -161,26 +168,26 @@ extension CameraScannerViewController: RectangleDetectionDelegateProtocol { func captureSessionManager(_ captureSessionManager: CaptureSessionManager, didFailWithError error: Error) { delegate?.captureImageFailWithError(error: error) } - + func didStartCapturingPicture(for captureSessionManager: CaptureSessionManager) { captureSessionManager.stop() } - + func captureSessionManager(_ captureSessionManager: CaptureSessionManager, didCapturePicture picture: UIImage, withQuad quad: Quadrilateral?) { delegate?.captureImageSuccess(image: picture, withQuad: quad) } - + func captureSessionManager(_ captureSessionManager: CaptureSessionManager, didDetectQuad quad: Quadrilateral?, _ imageSize: CGSize) { - guard let quad = quad else { + guard let quad else { // If no quad has been detected, we remove the currently displayed on on the quadView. quadView.removeQuadrilateral() return } - + let portraitImageSize = CGSize(width: imageSize.height, height: imageSize.width) let scaleTransform = CGAffineTransform.scaleTransform(forSize: portraitImageSize, aspectFillInSize: quadView.bounds.size) let scaledImageSize = imageSize.applying(scaleTransform) diff --git a/WeScan/Scan/CaptureSessionManager.swift b/Sources/WeScan/Scan/CaptureSessionManager.swift similarity index 82% rename from WeScan/Scan/CaptureSessionManager.swift rename to Sources/WeScan/Scan/CaptureSessionManager.swift index 82daee37..366a7067 100644 --- a/WeScan/Scan/CaptureSessionManager.swift +++ b/Sources/WeScan/Scan/CaptureSessionManager.swift @@ -6,35 +6,39 @@ // Copyright © 2018 WeTransfer. All rights reserved. // -import Foundation -import CoreMotion import AVFoundation +import CoreMotion +import Foundation import UIKit /// A set of functions that inform the delegate object of the state of the detection. protocol RectangleDetectionDelegateProtocol: NSObjectProtocol { - + /// Called when the capture of a picture has started. /// /// - Parameters: /// - captureSessionManager: The `CaptureSessionManager` instance that started capturing a picture. func didStartCapturingPicture(for captureSessionManager: CaptureSessionManager) - + /// Called when a quadrilateral has been detected. /// - Parameters: /// - captureSessionManager: The `CaptureSessionManager` instance that has detected a quadrilateral. /// - quad: The detected quadrilateral in the coordinates of the image. /// - imageSize: The size of the image the quadrilateral has been detected on. func captureSessionManager(_ captureSessionManager: CaptureSessionManager, didDetectQuad quad: Quadrilateral?, _ imageSize: CGSize) - + /// Called when a picture with or without a quadrilateral has been captured. /// /// - Parameters: /// - captureSessionManager: The `CaptureSessionManager` instance that has captured a picture. /// - picture: The picture that has been captured. /// - quad: The quadrilateral that was detected in the picture's coordinates if any. - func captureSessionManager(_ captureSessionManager: CaptureSessionManager, didCapturePicture picture: UIImage, withQuad quad: Quadrilateral?) - + func captureSessionManager( + _ captureSessionManager: CaptureSessionManager, + didCapturePicture picture: UIImage, + withQuad quad: Quadrilateral? + ) + /// Called when an error occured with the capture session manager. /// - Parameters: /// - captureSessionManager: The `CaptureSessionManager` that encountered an error. @@ -44,52 +48,52 @@ protocol RectangleDetectionDelegateProtocol: NSObjectProtocol { /// The CaptureSessionManager is responsible for setting up and managing the AVCaptureSession and the functions related to capturing. final class CaptureSessionManager: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate { - + private let videoPreviewLayer: AVCaptureVideoPreviewLayer private let captureSession = AVCaptureSession() private let rectangleFunnel = RectangleFeaturesFunnel() weak var delegate: RectangleDetectionDelegateProtocol? private var displayedRectangleResult: RectangleDetectorResult? private var photoOutput = AVCapturePhotoOutput() - + /// Whether the CaptureSessionManager should be detecting quadrilaterals. private var isDetecting = true - + /// The number of times no rectangles have been found in a row. private var noRectangleCount = 0 - + /// The minimum number of time required by `noRectangleCount` to validate that no rectangles have been found. private let noRectangleThreshold = 3 - + // MARK: Life Cycle - + init?(videoPreviewLayer: AVCaptureVideoPreviewLayer, delegate: RectangleDetectionDelegateProtocol? = nil) { self.videoPreviewLayer = videoPreviewLayer - + if delegate != nil { self.delegate = delegate } - + super.init() - + guard let device = AVCaptureDevice.default(for: AVMediaType.video) else { let error = ImageScannerControllerError.inputDevice delegate?.captureSessionManager(self, didFailWithError: error) return nil } - + captureSession.beginConfiguration() - + photoOutput.isHighResolutionCaptureEnabled = true - + let videoOutput = AVCaptureVideoDataOutput() videoOutput.alwaysDiscardsLateVideoFrames = true - + defer { device.unlockForConfiguration() captureSession.commitConfiguration() } - + guard let deviceInput = try? AVCaptureDeviceInput(device: device), captureSession.canAddInput(deviceInput), captureSession.canAddOutput(photoOutput), @@ -98,7 +102,7 @@ final class CaptureSessionManager: NSObject, AVCaptureVideoDataOutputSampleBuffe delegate?.captureSessionManager(self, didFailWithError: error) return } - + do { try device.lockForConfiguration() } catch { @@ -106,35 +110,35 @@ final class CaptureSessionManager: NSObject, AVCaptureVideoDataOutputSampleBuffe delegate?.captureSessionManager(self, didFailWithError: error) return } - + device.isSubjectAreaChangeMonitoringEnabled = true - + captureSession.addInput(deviceInput) captureSession.addOutput(photoOutput) captureSession.addOutput(videoOutput) - + let photoPreset = AVCaptureSession.Preset.photo if captureSession.canSetSessionPreset(photoPreset) { captureSession.sessionPreset = photoPreset - + if photoOutput.isLivePhotoCaptureSupported { photoOutput.isLivePhotoCaptureEnabled = true } } - + videoPreviewLayer.session = captureSession videoPreviewLayer.videoGravity = .resizeAspectFill - + videoOutput.setSampleBufferDelegate(self, queue: DispatchQueue(label: "video_ouput_queue")) } - + // MARK: Capture Session Life Cycle - + /// Starts the camera and detecting quadrilaterals. internal func start() { let authorizationStatus = AVCaptureDevice.authorizationStatus(for: .video) - + switch authorizationStatus { case .authorized: DispatchQueue.main.async { @@ -142,7 +146,7 @@ final class CaptureSessionManager: NSObject, AVCaptureVideoDataOutputSampleBuffe } isDetecting = true case .notDetermined: - AVCaptureDevice.requestAccess(for: AVMediaType.video, completionHandler: { (_) in + AVCaptureDevice.requestAccess(for: AVMediaType.video, completionHandler: { _ in DispatchQueue.main.async { [weak self] in self?.start() } @@ -152,11 +156,11 @@ final class CaptureSessionManager: NSObject, AVCaptureVideoDataOutputSampleBuffe delegate?.captureSessionManager(self, didFailWithError: error) } } - + internal func stop() { captureSession.stopRunning() } - + internal func capturePhoto() { guard let connection = photoOutput.connection(with: .video), connection.isEnabled, connection.isActive else { let error = ImageScannerControllerError.capture @@ -169,9 +173,9 @@ final class CaptureSessionManager: NSObject, AVCaptureVideoDataOutputSampleBuffe photoSettings.isAutoStillImageStabilizationEnabled = true photoOutput.capturePhoto(with: photoSettings, delegate: self) } - + // MARK: - AVCaptureVideoDataOutputSampleBufferDelegate - + func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) { guard isDetecting == true, let pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { @@ -181,109 +185,119 @@ final class CaptureSessionManager: NSObject, AVCaptureVideoDataOutputSampleBuffe let imageSize = CGSize(width: CVPixelBufferGetWidth(pixelBuffer), height: CVPixelBufferGetHeight(pixelBuffer)) if #available(iOS 11.0, *) { - VisionRectangleDetector.rectangle(forPixelBuffer: pixelBuffer) { (rectangle) in + VisionRectangleDetector.rectangle(forPixelBuffer: pixelBuffer) { rectangle in self.processRectangle(rectangle: rectangle, imageSize: imageSize) } } else { let finalImage = CIImage(cvPixelBuffer: pixelBuffer) - CIRectangleDetector.rectangle(forImage: finalImage) { (rectangle) in + CIRectangleDetector.rectangle(forImage: finalImage) { rectangle in self.processRectangle(rectangle: rectangle, imageSize: imageSize) } } } - + private func processRectangle(rectangle: Quadrilateral?, imageSize: CGSize) { - if let rectangle = rectangle { - + if let rectangle { + self.noRectangleCount = 0 - self.rectangleFunnel.add(rectangle, currentlyDisplayedRectangle: self.displayedRectangleResult?.rectangle) { [weak self] (result, rectangle) in - - guard let strongSelf = self else { + self.rectangleFunnel + .add(rectangle, currentlyDisplayedRectangle: self.displayedRectangleResult?.rectangle) { [weak self] result, rectangle in + + guard let self else { return } - + let shouldAutoScan = (result == .showAndAutoScan) - strongSelf.displayRectangleResult(rectangleResult: RectangleDetectorResult(rectangle: rectangle, imageSize: imageSize)) + self.displayRectangleResult(rectangleResult: RectangleDetectorResult(rectangle: rectangle, imageSize: imageSize)) if shouldAutoScan, CaptureSession.current.isAutoScanEnabled, !CaptureSession.current.isEditing { capturePhoto() } } - + } else { - + DispatchQueue.main.async { [weak self] in - guard let strongSelf = self else { + guard let self else { return } - strongSelf.noRectangleCount += 1 - - if strongSelf.noRectangleCount > strongSelf.noRectangleThreshold { + self.noRectangleCount += 1 + + if self.noRectangleCount > self.noRectangleThreshold { // Reset the currentAutoScanPassCount, so the threshold is restarted the next time a rectangle is found - strongSelf.rectangleFunnel.currentAutoScanPassCount = 0 - + self.rectangleFunnel.currentAutoScanPassCount = 0 + // Remove the currently displayed rectangle as no rectangles are being found anymore - strongSelf.displayedRectangleResult = nil - strongSelf.delegate?.captureSessionManager(strongSelf, didDetectQuad: nil, imageSize) + self.displayedRectangleResult = nil + self.delegate?.captureSessionManager(self, didDetectQuad: nil, imageSize) } } return - + } } - + @discardableResult private func displayRectangleResult(rectangleResult: RectangleDetectorResult) -> Quadrilateral { displayedRectangleResult = rectangleResult - + let quad = rectangleResult.rectangle.toCartesian(withHeight: rectangleResult.imageSize.height) - + DispatchQueue.main.async { [weak self] in - guard let strongSelf = self else { + guard let self else { return } - - strongSelf.delegate?.captureSessionManager(strongSelf, didDetectQuad: quad, rectangleResult.imageSize) + + self.delegate?.captureSessionManager(self, didDetectQuad: quad, rectangleResult.imageSize) } - + return quad } - + } extension CaptureSessionManager: AVCapturePhotoCaptureDelegate { // swiftlint:disable function_parameter_count - func photoOutput(_ captureOutput: AVCapturePhotoOutput, didFinishProcessingPhoto photoSampleBuffer: CMSampleBuffer?, previewPhoto previewPhotoSampleBuffer: CMSampleBuffer?, resolvedSettings: AVCaptureResolvedPhotoSettings, bracketSettings: AVCaptureBracketedStillImageSettings?, error: Error?) { - if let error = error { + func photoOutput(_ captureOutput: AVCapturePhotoOutput, + didFinishProcessingPhoto photoSampleBuffer: CMSampleBuffer?, + previewPhoto previewPhotoSampleBuffer: CMSampleBuffer?, + resolvedSettings: AVCaptureResolvedPhotoSettings, + bracketSettings: AVCaptureBracketedStillImageSettings?, + error: Error? + ) { + if let error { delegate?.captureSessionManager(self, didFailWithError: error) return } - + isDetecting = false rectangleFunnel.currentAutoScanPassCount = 0 delegate?.didStartCapturingPicture(for: self) - + if let sampleBuffer = photoSampleBuffer, - let imageData = AVCapturePhotoOutput.jpegPhotoDataRepresentation(forJPEGSampleBuffer: sampleBuffer, previewPhotoSampleBuffer: nil) { + let imageData = AVCapturePhotoOutput.jpegPhotoDataRepresentation( + forJPEGSampleBuffer: sampleBuffer, + previewPhotoSampleBuffer: nil + ) { completeImageCapture(with: imageData) } else { let error = ImageScannerControllerError.capture delegate?.captureSessionManager(self, didFailWithError: error) return } - + } - + @available(iOS 11.0, *) func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) { - if let error = error { + if let error { delegate?.captureSessionManager(self, didFailWithError: error) return } - + isDetecting = false rectangleFunnel.currentAutoScanPassCount = 0 delegate?.didStartCapturingPicture(for: self) - + if let imageData = photo.fileDataRepresentation() { completeImageCapture(with: imageData) } else { @@ -292,7 +306,7 @@ extension CaptureSessionManager: AVCapturePhotoCaptureDelegate { return } } - + /// Completes the image capture by processing the image, and passing it to the delegate object. /// This function is necessary because the capture functions for iOS 10 and 11 are decoupled. private func completeImageCapture(with imageData: Data) { @@ -301,16 +315,16 @@ extension CaptureSessionManager: AVCapturePhotoCaptureDelegate { guard let image = UIImage(data: imageData) else { let error = ImageScannerControllerError.capture DispatchQueue.main.async { - guard let strongSelf = self else { + guard let self else { return } - strongSelf.delegate?.captureSessionManager(strongSelf, didFailWithError: error) + self.delegate?.captureSessionManager(self, didFailWithError: error) } return } - + var angle: CGFloat = 0.0 - + switch image.imageOrientation { case .right: angle = CGFloat.pi / 2 @@ -319,18 +333,18 @@ extension CaptureSessionManager: AVCapturePhotoCaptureDelegate { default: break } - + var quad: Quadrilateral? if let displayedRectangleResult = self?.displayedRectangleResult { quad = self?.displayRectangleResult(rectangleResult: displayedRectangleResult) quad = quad?.scale(displayedRectangleResult.imageSize, image.size, withRotationAngle: angle) } - + DispatchQueue.main.async { - guard let strongSelf = self else { + guard let self else { return } - strongSelf.delegate?.captureSessionManager(strongSelf, didCapturePicture: image, withQuad: quad) + self.delegate?.captureSessionManager(self, didCapturePicture: image, withQuad: quad) } } } @@ -338,11 +352,11 @@ extension CaptureSessionManager: AVCapturePhotoCaptureDelegate { /// Data structure representing the result of the detection of a quadrilateral. private struct RectangleDetectorResult { - + /// The detected quadrilateral. let rectangle: Quadrilateral - + /// The size of the image the quadrilateral was detected on. let imageSize: CGSize - + } diff --git a/WeScan/Scan/FocusRectangleView.swift b/Sources/WeScan/Scan/FocusRectangleView.swift similarity index 82% rename from WeScan/Scan/FocusRectangleView.swift rename to Sources/WeScan/Scan/FocusRectangleView.swift index a8d83182..ed58c599 100644 --- a/WeScan/Scan/FocusRectangleView.swift +++ b/Sources/WeScan/Scan/FocusRectangleView.swift @@ -13,27 +13,34 @@ final class FocusRectangleView: UIView { convenience init(touchPoint: CGPoint) { let originalSize: CGFloat = 200 let finalSize: CGFloat = 80 - + // Here, we create the frame to be the `originalSize`, with it's center being the `touchPoint`. - self.init(frame: CGRect(x: touchPoint.x - (originalSize / 2), y: touchPoint.y - (originalSize / 2), width: originalSize, height: originalSize)) - + self.init( + frame: CGRect( + x: touchPoint.x - (originalSize / 2), + y: touchPoint.y - (originalSize / 2), + width: originalSize, + height: originalSize + ) + ) + backgroundColor = .clear layer.borderWidth = 2.0 layer.cornerRadius = 6.0 layer.borderColor = UIColor.yellow.cgColor - + // Here, we animate the rectangle from the `originalSize` to the `finalSize` by calculating the difference. UIView.animate(withDuration: 0.2, delay: 0.0, options: .curveEaseOut, animations: { self.frame.origin.x += (originalSize - finalSize) / 2 self.frame.origin.y += (originalSize - finalSize) / 2 - + self.frame.size.width -= (originalSize - finalSize) self.frame.size.height -= (originalSize - finalSize) }) } - + public func setBorder(color: CGColor) { layer.borderColor = color } - + } diff --git a/WeScan/Scan/RectangleFeaturesFunnel.swift b/Sources/WeScan/Scan/RectangleFeaturesFunnel.swift similarity index 96% rename from WeScan/Scan/RectangleFeaturesFunnel.swift rename to Sources/WeScan/Scan/RectangleFeaturesFunnel.swift index 1c47e78c..7ca750bd 100644 --- a/WeScan/Scan/RectangleFeaturesFunnel.swift +++ b/Sources/WeScan/Scan/RectangleFeaturesFunnel.swift @@ -5,9 +5,10 @@ // Created by Boris Emorine on 2/9/18. // Copyright © 2018 WeTransfer. All rights reserved. // +// swiftlint:disable line_length -import Foundation import AVFoundation +import Foundation enum AddResult { case showAndAutoScan @@ -17,24 +18,24 @@ enum AddResult { /// `RectangleFeaturesFunnel` is used to improve the confidence of the detected rectangles. /// Feed rectangles to a `RectangleFeaturesFunnel` instance, and it will call the completion block with a rectangle whose confidence is high enough to be displayed. final class RectangleFeaturesFunnel { - + /// `RectangleMatch` is a class used to assign matching scores to rectangles. private final class RectangleMatch: NSObject { /// The rectangle feature object associated to this `RectangleMatch` instance. let rectangleFeature: Quadrilateral - + /// The score to indicate how strongly the rectangle of this instance matches other recently added rectangles. /// A higher score indicates that many recently added rectangles are very close to the rectangle of this instance. var matchingScore = 0 - + init(rectangleFeature: Quadrilateral) { self.rectangleFeature = rectangleFeature } - + override var description: String { return "Matching score: \(matchingScore) - Rectangle: \(rectangleFeature)" } - + /// Whether the rectangle of this instance is within the distance of the given rectangle. /// /// - Parameters: @@ -45,33 +46,33 @@ final class RectangleFeaturesFunnel { return rectangleFeature.isWithin(threshold, ofRectangleFeature: rectangle) } } - + /// The queue of last added rectangles. The first rectangle is oldest one, and the last rectangle is the most recently added one. private var rectangles = [RectangleMatch]() - + /// The maximum number of rectangles to compare newly added rectangles with. Determines the maximum size of `rectangles`. Increasing this value will impact performance. let maxNumberOfRectangles = 8 - + /// The minimum number of rectangles needed to start making comparaisons and determining which rectangle to display. This value should always be inferior than `maxNumberOfRectangles`. /// A higher value will delay the first time a rectangle is displayed. let minNumberOfRectangles = 3 - + /// The value in pixels used to determine if two rectangle match or not. A higher value will prevent displayed rectangles to be refreshed. On the opposite, a smaller value will make new rectangles be displayed constantly. let matchingThreshold: CGFloat = 40.0 - + /// The minumum number of matching rectangles (within the `rectangle` queue), to be confident enough to display a rectangle. let minNumberOfMatches = 3 - + /// The number of similar rectangles that need to be found to auto scan. let autoScanThreshold = 35 - + /// The number of times the rectangle has passed the threshold to be auto-scanned var currentAutoScanPassCount = 0 - + /// The value in pixels used to determine if a rectangle is accurate enough to be auto scanned. /// A higher value means the auto scan is quicker, but the rectangle will be less accurate. On the other hand, the lower the value, the longer it'll take for the auto scan, but it'll be way more accurate var autoScanMatchingThreshold: CGFloat = 6.0 - + /// Add a rectangle to the funnel, and if a new rectangle should be displayed, the completion block will be called. /// The algorithm works the following way: /// 1. Makes sure that the funnel has been fed enough rectangles @@ -87,21 +88,21 @@ final class RectangleFeaturesFunnel { func add(_ rectangleFeature: Quadrilateral, currentlyDisplayedRectangle currentRectangle: Quadrilateral?, completion: (AddResult, Quadrilateral) -> Void) { let rectangleMatch = RectangleMatch(rectangleFeature: rectangleFeature) rectangles.append(rectangleMatch) - + guard rectangles.count >= minNumberOfRectangles else { return } - + if rectangles.count > maxNumberOfRectangles { rectangles.removeFirst() } - + updateRectangleMatches() - + guard let bestRectangle = bestRectangle(withCurrentlyDisplayedRectangle: currentRectangle) else { return } - + if let previousRectangle = currentRectangle, bestRectangle.rectangleFeature.isWithin(autoScanMatchingThreshold, ofRectangleFeature: previousRectangle) { currentAutoScanPassCount += 1 @@ -113,7 +114,7 @@ final class RectangleFeaturesFunnel { completion(AddResult.showOnly, bestRectangle.rectangleFeature) } } - + /// Determines which rectangle is best to displayed. /// The criteria used to find the best rectangle is its matching score. /// If multiple rectangles have the same matching score, we use a tie breaker to find the best rectangle (@see breakTie(forRectangles:)). @@ -123,27 +124,27 @@ final class RectangleFeaturesFunnel { private func bestRectangle(withCurrentlyDisplayedRectangle currentRectangle: Quadrilateral?) -> RectangleMatch? { var bestMatch: RectangleMatch? guard !rectangles.isEmpty else { return nil } - rectangles.reversed().forEach { (rectangle) in + rectangles.reversed().forEach { rectangle in guard let best = bestMatch else { bestMatch = rectangle return } - + if rectangle.matchingScore > best.matchingScore { bestMatch = rectangle return } else if rectangle.matchingScore == best.matchingScore { - guard let currentRectangle = currentRectangle else { + guard let currentRectangle else { return } - + bestMatch = breakTie(between: best, rect2: rectangle, currentRectangle: currentRectangle) } } - + return bestMatch } - + /// Breaks a tie between two rectangles to find out which is best to display. /// The first passed rectangle is returned if no other criteria could be used to break the tie. /// If the first passed rectangle (rect1) is close to the currently displayed rectangle, we pick it. @@ -160,10 +161,10 @@ final class RectangleFeaturesFunnel { } else if rect2.rectangleFeature.isWithin(matchingThreshold, ofRectangleFeature: currentRectangle) { return rect2 } - + return rect1 } - + /// Loops through all of the rectangles of the queue, and gives them a score depending on how many they match. @see `RectangleMatch.matchingScore` private func updateRectangleMatches() { resetMatchingScores() @@ -177,7 +178,7 @@ final class RectangleFeaturesFunnel { } } } - + /// Resets the matching score of all of the rectangles in the queue to 0 private func resetMatchingScores() { guard !rectangles.isEmpty else { return } @@ -185,5 +186,5 @@ final class RectangleFeaturesFunnel { rectangle.matchingScore = 1 } } - + } diff --git a/WeScan/Scan/ScannerViewController.swift b/Sources/WeScan/Scan/ScannerViewController.swift similarity index 95% rename from WeScan/Scan/ScannerViewController.swift rename to Sources/WeScan/Scan/ScannerViewController.swift index d3c74461..50c10765 100644 --- a/WeScan/Scan/ScannerViewController.swift +++ b/Sources/WeScan/Scan/ScannerViewController.swift @@ -5,35 +5,36 @@ // Created by Boris Emorine on 2/8/18. // Copyright © 2018 WeTransfer. All rights reserved. // +// swiftlint:disable line_length -import UIKit import AVFoundation +import UIKit /// The `ScannerViewController` offers an interface to give feedback to the user regarding quadrilaterals that are detected. It also gives the user the opportunity to capture an image with a detected rectangle. public final class ScannerViewController: UIViewController { - + private var captureSessionManager: CaptureSessionManager? private let videoPreviewLayer = AVCaptureVideoPreviewLayer() - + /// The view that shows the focus rectangle (when the user taps to focus, similar to the Camera app) private var focusRectangle: FocusRectangleView! - + /// The view that draws the detected rectangles. private let quadView = QuadrilateralView() - + /// Whether flash is enabled private var flashEnabled = false - + /// The original bar style that was set by the host app private var originalBarStyle: UIBarStyle? - + private lazy var shutterButton: ShutterButton = { let button = ShutterButton() button.translatesAutoresizingMaskIntoConstraints = false button.addTarget(self, action: #selector(captureImage(_:)), for: .touchUpInside) return button }() - + private lazy var cancelButton: UIButton = { let button = UIButton() button.setTitle(NSLocalizedString("wescan.scanning.cancel", tableName: nil, bundle: Bundle(for: ScannerViewController.self), value: "Cancel", comment: "The cancel button"), for: .normal) @@ -41,23 +42,23 @@ public final class ScannerViewController: UIViewController { button.addTarget(self, action: #selector(cancelImageScannerController), for: .touchUpInside) return button }() - + private lazy var autoScanButton: UIBarButtonItem = { let title = NSLocalizedString("wescan.scanning.auto", tableName: nil, bundle: Bundle(for: ScannerViewController.self), value: "Auto", comment: "The auto button state") let button = UIBarButtonItem(title: title, style: .plain, target: self, action: #selector(toggleAutoScan)) button.tintColor = .white - + return button }() - + private lazy var flashButton: UIBarButtonItem = { let image = UIImage(systemName: "bolt.fill", named: "flash", in: Bundle(for: ScannerViewController.self), compatibleWith: nil) let button = UIBarButtonItem(image: image, style: .plain, target: self, action: #selector(toggleFlash)) button.tintColor = .white - + return button }() - + private lazy var activityIndicator: UIActivityIndicatorView = { let activityIndicator = UIActivityIndicatorView(style: .gray) activityIndicator.hidesWhenStopped = true @@ -69,43 +70,43 @@ public final class ScannerViewController: UIViewController { override public func viewDidLoad() { super.viewDidLoad() - + title = nil view.backgroundColor = UIColor.black - + setupViews() setupNavigationBar() setupConstraints() - + captureSessionManager = CaptureSessionManager(videoPreviewLayer: videoPreviewLayer, delegate: self) - + originalBarStyle = navigationController?.navigationBar.barStyle - + NotificationCenter.default.addObserver(self, selector: #selector(subjectAreaDidChange), name: Notification.Name.AVCaptureDeviceSubjectAreaDidChange, object: nil) } - + override public func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) setNeedsStatusBarAppearanceUpdate() - + CaptureSession.current.isEditing = false quadView.removeQuadrilateral() captureSessionManager?.start() UIApplication.shared.isIdleTimerDisabled = true - + navigationController?.navigationBar.barStyle = .blackTranslucent } - + override public func viewDidLayoutSubviews() { super.viewDidLayoutSubviews() - + videoPreviewLayer.frame = view.layer.bounds } - + override public func viewWillDisappear(_ animated: Bool) { super.viewWillDisappear(animated) UIApplication.shared.isIdleTimerDisabled = false - + navigationController?.navigationBar.isTranslucent = false navigationController?.navigationBar.barStyle = originalBarStyle ?? .default captureSessionManager?.stop() @@ -114,9 +115,9 @@ public final class ScannerViewController: UIViewController { toggleFlash() } } - + // MARK: - Setups - + private func setupViews() { view.backgroundColor = .darkGray view.layer.addSublayer(videoPreviewLayer) @@ -127,48 +128,48 @@ public final class ScannerViewController: UIViewController { view.addSubview(shutterButton) view.addSubview(activityIndicator) } - + private func setupNavigationBar() { navigationItem.setLeftBarButton(flashButton, animated: false) navigationItem.setRightBarButton(autoScanButton, animated: false) - + if UIImagePickerController.isFlashAvailable(for: .rear) == false { let flashOffImage = UIImage(systemName: "bolt.slash.fill", named: "flashUnavailable", in: Bundle(for: ScannerViewController.self), compatibleWith: nil) flashButton.image = flashOffImage flashButton.tintColor = UIColor.lightGray } } - + private func setupConstraints() { var quadViewConstraints = [NSLayoutConstraint]() var cancelButtonConstraints = [NSLayoutConstraint]() var shutterButtonConstraints = [NSLayoutConstraint]() var activityIndicatorConstraints = [NSLayoutConstraint]() - + quadViewConstraints = [ quadView.topAnchor.constraint(equalTo: view.topAnchor), view.bottomAnchor.constraint(equalTo: quadView.bottomAnchor), view.trailingAnchor.constraint(equalTo: quadView.trailingAnchor), quadView.leadingAnchor.constraint(equalTo: view.leadingAnchor) ] - + shutterButtonConstraints = [ shutterButton.centerXAnchor.constraint(equalTo: view.centerXAnchor), shutterButton.widthAnchor.constraint(equalToConstant: 65.0), shutterButton.heightAnchor.constraint(equalToConstant: 65.0) ] - + activityIndicatorConstraints = [ activityIndicator.centerXAnchor.constraint(equalTo: view.centerXAnchor), activityIndicator.centerYAnchor.constraint(equalTo: view.centerYAnchor) ] - + if #available(iOS 11.0, *) { cancelButtonConstraints = [ cancelButton.leftAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leftAnchor, constant: 24.0), view.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: cancelButton.bottomAnchor, constant: (65.0 / 2) - 10.0) ] - + let shutterButtonBottomConstraint = view.safeAreaLayoutGuide.bottomAnchor.constraint(equalTo: shutterButton.bottomAnchor, constant: 8.0) shutterButtonConstraints.append(shutterButtonBottomConstraint) } else { @@ -176,16 +177,16 @@ public final class ScannerViewController: UIViewController { cancelButton.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 24.0), view.bottomAnchor.constraint(equalTo: cancelButton.bottomAnchor, constant: (65.0 / 2) - 10.0) ] - + let shutterButtonBottomConstraint = view.bottomAnchor.constraint(equalTo: shutterButton.bottomAnchor, constant: 8.0) shutterButtonConstraints.append(shutterButtonBottomConstraint) } - + NSLayoutConstraint.activate(quadViewConstraints + cancelButtonConstraints + shutterButtonConstraints + activityIndicatorConstraints) } - + // MARK: - Tap to Focus - + /// Called when the AVCaptureDevice detects that the subject area has changed significantly. When it's called, we reset the focus so the camera is no longer out of focus. @objc private func subjectAreaDidChange() { /// Reset the focus and exposure back to automatic @@ -193,45 +194,45 @@ public final class ScannerViewController: UIViewController { try CaptureSession.current.resetFocusToAuto() } catch { let error = ImageScannerControllerError.inputDevice - guard let captureSessionManager = captureSessionManager else { return } + guard let captureSessionManager else { return } captureSessionManager.delegate?.captureSessionManager(captureSessionManager, didFailWithError: error) return } - + /// Remove the focus rectangle if one exists CaptureSession.current.removeFocusRectangleIfNeeded(focusRectangle, animated: true) } - + override public func touchesBegan(_ touches: Set, with event: UIEvent?) { super.touchesBegan(touches, with: event) - + guard let touch = touches.first else { return } let touchPoint = touch.location(in: view) let convertedTouchPoint: CGPoint = videoPreviewLayer.captureDevicePointConverted(fromLayerPoint: touchPoint) - + CaptureSession.current.removeFocusRectangleIfNeeded(focusRectangle, animated: false) - + focusRectangle = FocusRectangleView(touchPoint: touchPoint) view.addSubview(focusRectangle) - + do { try CaptureSession.current.setFocusPointToTapPoint(convertedTouchPoint) } catch { let error = ImageScannerControllerError.inputDevice - guard let captureSessionManager = captureSessionManager else { return } + guard let captureSessionManager else { return } captureSessionManager.delegate?.captureSessionManager(captureSessionManager, didFailWithError: error) return } } - + // MARK: - Actions - + @objc private func captureImage(_ sender: UIButton) { (navigationController as? ImageScannerController)?.flashToBlack() shutterButton.isUserInteractionEnabled = false captureSessionManager?.capturePhoto() } - + @objc private func toggleAutoScan() { if CaptureSession.current.isAutoScanEnabled { CaptureSession.current.isAutoScanEnabled = false @@ -241,13 +242,13 @@ public final class ScannerViewController: UIViewController { autoScanButton.title = NSLocalizedString("wescan.scanning.auto", tableName: nil, bundle: Bundle(for: ScannerViewController.self), value: "Auto", comment: "The auto button state") } } - + @objc private func toggleFlash() { let state = CaptureSession.current.toggleFlash() - + let flashImage = UIImage(systemName: "bolt.fill", named: "flash", in: Bundle(for: ScannerViewController.self), compatibleWith: nil) let flashOffImage = UIImage(systemName: "bolt.slash.fill", named: "flashUnavailable", in: Bundle(for: ScannerViewController.self), compatibleWith: nil) - + switch state { case .on: flashEnabled = true @@ -263,62 +264,62 @@ public final class ScannerViewController: UIViewController { flashButton.tintColor = UIColor.lightGray } } - + @objc private func cancelImageScannerController() { guard let imageScannerController = navigationController as? ImageScannerController else { return } imageScannerController.imageScannerDelegate?.imageScannerControllerDidCancel(imageScannerController) } - + } extension ScannerViewController: RectangleDetectionDelegateProtocol { func captureSessionManager(_ captureSessionManager: CaptureSessionManager, didFailWithError error: Error) { - + activityIndicator.stopAnimating() shutterButton.isUserInteractionEnabled = true - + guard let imageScannerController = navigationController as? ImageScannerController else { return } imageScannerController.imageScannerDelegate?.imageScannerController(imageScannerController, didFailWithError: error) } - + func didStartCapturingPicture(for captureSessionManager: CaptureSessionManager) { activityIndicator.startAnimating() captureSessionManager.stop() shutterButton.isUserInteractionEnabled = false } - + func captureSessionManager(_ captureSessionManager: CaptureSessionManager, didCapturePicture picture: UIImage, withQuad quad: Quadrilateral?) { activityIndicator.stopAnimating() - + let editVC = EditScanViewController(image: picture, quad: quad) navigationController?.pushViewController(editVC, animated: false) - + shutterButton.isUserInteractionEnabled = true } - + func captureSessionManager(_ captureSessionManager: CaptureSessionManager, didDetectQuad quad: Quadrilateral?, _ imageSize: CGSize) { - guard let quad = quad else { + guard let quad else { // If no quad has been detected, we remove the currently displayed on on the quadView. quadView.removeQuadrilateral() return } - + let portraitImageSize = CGSize(width: imageSize.height, height: imageSize.width) - + let scaleTransform = CGAffineTransform.scaleTransform(forSize: portraitImageSize, aspectFillInSize: quadView.bounds.size) let scaledImageSize = imageSize.applying(scaleTransform) - + let rotationTransform = CGAffineTransform(rotationAngle: CGFloat.pi / 2.0) let imageBounds = CGRect(origin: .zero, size: scaledImageSize).applying(rotationTransform) let translationTransform = CGAffineTransform.translateTransform(fromCenterOfRect: imageBounds, toCenterOfRect: quadView.bounds) - + let transforms = [scaleTransform, rotationTransform, translationTransform] - + let transformedQuad = quad.applyTransforms(transforms) - + quadView.drawQuadrilateral(quad: transformedQuad, animated: true) } - + } diff --git a/WeScan/Scan/ShutterButton.swift b/Sources/WeScan/Scan/ShutterButton.swift similarity index 90% rename from WeScan/Scan/ShutterButton.swift rename to Sources/WeScan/Scan/ShutterButton.swift index aa9b88df..872c3da1 100644 --- a/WeScan/Scan/ShutterButton.swift +++ b/Sources/WeScan/Scan/ShutterButton.swift @@ -10,15 +10,15 @@ import UIKit /// A simple button used for the shutter. final class ShutterButton: UIControl { - + private let outterRingLayer = CAShapeLayer() private let innerCircleLayer = CAShapeLayer() - + private let outterRingRatio: CGFloat = 0.80 private let innerRingRatio: CGFloat = 0.75 - + private let impactFeedbackGenerator = UIImpactFeedbackGenerator(style: .light) - + override var isHighlighted: Bool { didSet { if oldValue != isHighlighted { @@ -26,9 +26,9 @@ final class ShutterButton: UIControl { } } } - + // MARK: - Life Cycle - + override init(frame: CGRect) { super.init(frame: frame) layer.addSublayer(outterRingLayer) @@ -38,13 +38,13 @@ final class ShutterButton: UIControl { accessibilityTraits = UIAccessibilityTraits.button impactFeedbackGenerator.prepare() } - + required init?(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } - + // MARK: - Drawing - + override func draw(_ rect: CGRect) { super.draw(rect) @@ -53,19 +53,24 @@ final class ShutterButton: UIControl { outterRingLayer.fillColor = UIColor.white.cgColor outterRingLayer.rasterizationScale = UIScreen.main.scale outterRingLayer.shouldRasterize = true - + innerCircleLayer.frame = rect innerCircleLayer.path = pathForInnerCircle(inRect: rect).cgPath innerCircleLayer.fillColor = UIColor.white.cgColor innerCircleLayer.rasterizationScale = UIScreen.main.scale innerCircleLayer.shouldRasterize = true } - + // MARK: - Animation - + private func animateInnerCircleLayer(forHighlightedState isHighlighted: Bool) { let animation = CAKeyframeAnimation(keyPath: "transform") - var values = [CATransform3DMakeScale(1.0, 1.0, 1.0), CATransform3DMakeScale(0.9, 0.9, 0.9), CATransform3DMakeScale(0.93, 0.93, 0.93), CATransform3DMakeScale(0.9, 0.9, 0.9)] + var values = [ + CATransform3DMakeScale(1.0, 1.0, 1.0), + CATransform3DMakeScale(0.9, 0.9, 0.9), + CATransform3DMakeScale(0.93, 0.93, 0.93), + CATransform3DMakeScale(0.9, 0.9, 0.9) + ] if isHighlighted == false { values = [CATransform3DMakeScale(0.9, 0.9, 0.9), CATransform3DMakeScale(1.0, 1.0, 1.0)] } @@ -73,29 +78,29 @@ final class ShutterButton: UIControl { animation.isRemovedOnCompletion = false animation.fillMode = CAMediaTimingFillMode.forwards animation.duration = isHighlighted ? 0.35 : 0.10 - + innerCircleLayer.add(animation, forKey: "transform") impactFeedbackGenerator.impactOccurred() } - + // MARK: - Paths - + private func pathForOutterRing(inRect rect: CGRect) -> UIBezierPath { let path = UIBezierPath(ovalIn: rect) - + let innerRect = rect.scaleAndCenter(withRatio: outterRingRatio) let innerPath = UIBezierPath(ovalIn: innerRect).reversing() - + path.append(innerPath) - + return path } - + private func pathForInnerCircle(inRect rect: CGRect) -> UIBezierPath { let rect = rect.scaleAndCenter(withRatio: innerRingRatio) let path = UIBezierPath(ovalIn: rect) - + return path } - + } diff --git a/WeScan/Session/CaptureSession+Flash.swift b/Sources/WeScan/Session/CaptureSession+Flash.swift similarity index 88% rename from WeScan/Session/CaptureSession+Flash.swift rename to Sources/WeScan/Session/CaptureSession+Flash.swift index 5a37cb6c..9c44841a 100644 --- a/WeScan/Session/CaptureSession+Flash.swift +++ b/Sources/WeScan/Session/CaptureSession+Flash.swift @@ -17,21 +17,21 @@ extension CaptureSession { case unavailable case unknown } - + /// Toggles the current device's flashlight on or off. func toggleFlash() -> FlashState { - guard let device = device, device.isTorchAvailable else { return .unavailable } - + guard let device, device.isTorchAvailable else { return .unavailable } + do { try device.lockForConfiguration() } catch { return .unknown } - + defer { device.unlockForConfiguration() } - + if device.torchMode == .on { device.torchMode = .off return .off @@ -39,7 +39,7 @@ extension CaptureSession { device.torchMode = .on return .on } - + return .unknown } } diff --git a/WeScan/Session/CaptureSession+Focus.swift b/Sources/WeScan/Session/CaptureSession+Focus.swift similarity index 89% rename from WeScan/Session/CaptureSession+Focus.swift rename to Sources/WeScan/Session/CaptureSession+Focus.swift index 244bbb7d..6e878b3e 100644 --- a/WeScan/Session/CaptureSession+Focus.swift +++ b/Sources/WeScan/Session/CaptureSession+Focus.swift @@ -13,57 +13,57 @@ import UIKit extension CaptureSession { /// Sets the camera's exposure and focus point to the given point func setFocusPointToTapPoint(_ tapPoint: CGPoint) throws { - guard let device = device else { + guard let device else { let error = ImageScannerControllerError.inputDevice throw error } - + try device.lockForConfiguration() - + defer { device.unlockForConfiguration() } - + if device.isFocusPointOfInterestSupported && device.isFocusModeSupported(.autoFocus) { device.focusPointOfInterest = tapPoint device.focusMode = .autoFocus } - + if device.isExposurePointOfInterestSupported, device.isExposureModeSupported(.continuousAutoExposure) { device.exposurePointOfInterest = tapPoint device.exposureMode = .continuousAutoExposure } } - + /// Resets the camera's exposure and focus point to automatic func resetFocusToAuto() throws { - guard let device = device else { + guard let device else { let error = ImageScannerControllerError.inputDevice throw error } - + try device.lockForConfiguration() - + defer { device.unlockForConfiguration() } - + if device.isFocusPointOfInterestSupported && device.isFocusModeSupported(.continuousAutoFocus) { device.focusMode = .continuousAutoFocus } - + if device.isExposurePointOfInterestSupported, device.isExposureModeSupported(.continuousAutoExposure) { device.exposureMode = .continuousAutoExposure } } - + /// Removes an existing focus rectangle if one exists, optionally animating the exit func removeFocusRectangleIfNeeded(_ focusRectangle: FocusRectangleView?, animated: Bool) { - guard let focusRectangle = focusRectangle else { return } + guard let focusRectangle else { return } if animated { UIView.animate(withDuration: 0.3, delay: 1.0, animations: { focusRectangle.alpha = 0.0 - }, completion: { (_) in + }, completion: { _ in focusRectangle.removeFromSuperview() }) } else { diff --git a/WeScan/Session/CaptureSession+Orientation.swift b/Sources/WeScan/Session/CaptureSession+Orientation.swift similarity index 94% rename from WeScan/Session/CaptureSession+Orientation.swift rename to Sources/WeScan/Session/CaptureSession+Orientation.swift index 522adf96..2d66df6e 100644 --- a/WeScan/Session/CaptureSession+Orientation.swift +++ b/Sources/WeScan/Session/CaptureSession+Orientation.swift @@ -16,21 +16,21 @@ extension CaptureSession { /// Detect the current orientation of the device with CoreMotion and use it to set the `editImageOrientation`. func setImageOrientation() { let motion = CMMotionManager() - + /// This value should be 0.2, but since we only need one cycle (and stop updates immediately), /// we set it low to get the orientation immediately motion.accelerometerUpdateInterval = 0.01 - + guard motion.isAccelerometerAvailable else { return } - + motion.startAccelerometerUpdates(to: OperationQueue()) { data, error in - guard let data = data, error == nil else { return } - + guard let data, error == nil else { return } + /// The minimum amount of sensitivity for the landscape orientations /// This is to prevent the landscape orientation being incorrectly used /// Higher = easier for landscape to be detected, lower = easier for portrait to be detected let motionThreshold = 0.35 - + if data.acceleration.x >= motionThreshold { self.editImageOrientation = .left } else if data.acceleration.x <= -motionThreshold { @@ -41,9 +41,9 @@ extension CaptureSession { /// Which prevents accidentally making the document be scanned upside down self.editImageOrientation = .up } - + motion.stopAccelerometerUpdates() - + // If the device is reporting a specific landscape orientation, we'll use it over the accelerometer's update. // We don't use this to check for "portrait" because only the accelerometer works when portrait lock is enabled. // For some reason, the left/right orientations are incorrect (flipped) :/ diff --git a/WeScan/Session/CaptureSession.swift b/Sources/WeScan/Session/CaptureSession.swift similarity index 96% rename from WeScan/Session/CaptureSession.swift rename to Sources/WeScan/Session/CaptureSession.swift index 1348abfb..d08ee102 100644 --- a/WeScan/Session/CaptureSession.swift +++ b/Sources/WeScan/Session/CaptureSession.swift @@ -6,32 +6,32 @@ // Copyright © 2018 WeTransfer. All rights reserved. // -import Foundation import AVFoundation +import Foundation /// A class containing global variables and settings for this capture session final class CaptureSession { - + static let current = CaptureSession() - + /// The AVCaptureDevice used for the flash and focus setting var device: CaptureDevice? - + /// Whether the user is past the scanning screen or not (needed to disable auto scan on other screens) var isEditing: Bool - + /// The status of auto scan. Auto scan tries to automatically scan a detected rectangle if it has a high enough accuracy. var isAutoScanEnabled: Bool - + /// The orientation of the captured image var editImageOrientation: CGImagePropertyOrientation - + private init(isAutoScanEnabled: Bool = true, editImageOrientation: CGImagePropertyOrientation = .up) { self.device = AVCaptureDevice.default(for: .video) - + self.isEditing = false self.isAutoScanEnabled = isAutoScanEnabled self.editImageOrientation = editImageOrientation } - + } diff --git a/WeScan/ja.lproj/Localizable.strings b/Sources/WeScan/ja.lproj/Localizable.strings similarity index 100% rename from WeScan/ja.lproj/Localizable.strings rename to Sources/WeScan/ja.lproj/Localizable.strings diff --git a/Submodules/WeTransfer-iOS-CI b/Submodules/WeTransfer-iOS-CI index 9d07bc5e..e0c191fb 160000 --- a/Submodules/WeTransfer-iOS-CI +++ b/Submodules/WeTransfer-iOS-CI @@ -1 +1 @@ -Subproject commit 9d07bc5e7c23c7ccfe40840d5b369c2257734d72 +Subproject commit e0c191fb2f5112e73b0585b077330c0f529de08f diff --git a/Submodules/ios-snapshot-test-case b/Submodules/ios-snapshot-test-case deleted file mode 160000 index 6be2bd76..00000000 --- a/Submodules/ios-snapshot-test-case +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 6be2bd7696f088c5b65898c5cfadd859eb7d350b diff --git a/WeScanTests/AVCaptureVideoOrientationTests.swift b/Tests/WeScanTests/AVCaptureVideoOrientationTests.swift similarity index 98% rename from WeScanTests/AVCaptureVideoOrientationTests.swift rename to Tests/WeScanTests/AVCaptureVideoOrientationTests.swift index f1da7cb7..43059844 100644 --- a/WeScanTests/AVCaptureVideoOrientationTests.swift +++ b/Tests/WeScanTests/AVCaptureVideoOrientationTests.swift @@ -6,39 +6,39 @@ // Copyright © 2018 WeTransfer. All rights reserved. // -import XCTest import AVFoundation +import XCTest @testable import WeScan final class AVCaptureVideoOrientationTests: XCTestCase { - + func testPortaitsMapToPortrait() { XCTAssertEqual(AVCaptureVideoOrientation(deviceOrientation: .portrait), .portrait) } - + func testPortaitsUpsideDownMapToPortraitUpsideDown() { XCTAssertEqual(AVCaptureVideoOrientation(deviceOrientation: .portraitUpsideDown), .portraitUpsideDown) } - + func testLandscapeLeftMapToLandscapeLeft() { XCTAssertEqual(AVCaptureVideoOrientation(deviceOrientation: .landscapeLeft), .landscapeLeft) } - + func testLandscapeRightMapToLandscapeRight() { XCTAssertEqual(AVCaptureVideoOrientation(deviceOrientation: .landscapeRight), .landscapeRight) } - + func testFaceUpMapToPortrait() { XCTAssertEqual(AVCaptureVideoOrientation(deviceOrientation: .faceUp), .portrait) } - + func testFaceDownMapToPortraitUpsideDown() { XCTAssertEqual(AVCaptureVideoOrientation(deviceOrientation: .faceDown), .portraitUpsideDown) } - + func testDefaultToPortrait() { XCTAssertEqual(AVCaptureVideoOrientation(deviceOrientation: .unknown), .portrait) } - + } diff --git a/WeScanTests/ArrayTests.swift b/Tests/WeScanTests/ArrayTests.swift similarity index 95% rename from WeScanTests/ArrayTests.swift rename to Tests/WeScanTests/ArrayTests.swift index a07d4ce6..cb3d668b 100644 --- a/WeScanTests/ArrayTests.swift +++ b/Tests/WeScanTests/ArrayTests.swift @@ -6,41 +6,41 @@ // Copyright © 2018 WeTransfer. All rights reserved. // -import XCTest @testable import WeScan +import XCTest final class ArrayTests: XCTestCase { - + var funnel = RectangleFeaturesFunnel() - + override func setUp() { super.setUp() funnel = RectangleFeaturesFunnel() } - + func testBiggestRectangle() { var rects1 = ImageFeatureTestHelpers.getRectangleFeatures(from: .rect1, withCount: 10) var rects2 = ImageFeatureTestHelpers.getRectangleFeatures(from: .rect2, withCount: 10) - + var rectangles: [Quadrilateral] = rects1 + rects2 - + var biggestRectangle = rectangles.biggest() XCTAssert(biggestRectangle!.isWithin(1.0, ofRectangleFeature: rects1[0])) - + rects1 = ImageFeatureTestHelpers.getRectangleFeatures(from: .rect2, withCount: 10) rects2 = ImageFeatureTestHelpers.getRectangleFeatures(from: .rect1, withCount: 10) - + rectangles = rects1 + rects2 - + biggestRectangle = rectangles.biggest() XCTAssert(biggestRectangle!.isWithin(1.0, ofRectangleFeature: rects2[0])) - + rects1 = ImageFeatureTestHelpers.getRectangleFeatures(from: .rect1, withCount: 10) rects2 = ImageFeatureTestHelpers.getRectangleFeatures(from: .rect2, withCount: 10) let rects3 = ImageFeatureTestHelpers.getRectangleFeatures(from: .rect3, withCount: 10) - + rectangles = rects1 + rects2 + rects3 - + biggestRectangle = rectangles.biggest() XCTAssert(biggestRectangle!.isWithin(1.0, ofRectangleFeature: rects3[0])) } diff --git a/WeScanTests/CGAffineTransformTests.swift b/Tests/WeScanTests/CGAffineTransformTests.swift similarity index 96% rename from WeScanTests/CGAffineTransformTests.swift rename to Tests/WeScanTests/CGAffineTransformTests.swift index 8b82da92..fb28c83c 100644 --- a/WeScanTests/CGAffineTransformTests.swift +++ b/Tests/WeScanTests/CGAffineTransformTests.swift @@ -6,44 +6,44 @@ // Copyright © 2018 WeTransfer. All rights reserved. // -import XCTest @testable import WeScan +import XCTest final class CGAffineTransformTests: XCTestCase { func testScalesUpCorrectly() { - + let fromSize = CGSize(width: 1, height: 1) let toSize = CGSize(width: 2, height: 2) - + let scale = CGAffineTransform.scaleTransform(forSize: fromSize, aspectFillInSize: toSize) XCTAssertEqual(scale.a, 2) XCTAssertEqual(scale.d, 2) } - + func testScalesDownCorrectly() { - + let fromSize = CGSize(width: 2, height: 2) let toSize = CGSize(width: 1, height: 1) - + let scale = CGAffineTransform.scaleTransform(forSize: fromSize, aspectFillInSize: toSize) - + XCTAssertEqual(scale.a, 0.5) XCTAssertEqual(scale.d, 0.5) } func testTranslatesCorrectly() { - + let fromRect = CGRect(x: 0, y: 0, width: 10, height: 10) let toRect = CGRect(x: 5, y: 5, width: 10, height: 10) - + let translate = CGAffineTransform.translateTransform(fromCenterOfRect: fromRect, toCenterOfRect: toRect) - + XCTAssertEqual(translate.a, 1.0) XCTAssertEqual(translate.d, 1.0) XCTAssertEqual(translate.tx, 5.0) XCTAssertEqual(translate.ty, 5.0) } - + } diff --git a/WeScanTests/CGPointTests.swift b/Tests/WeScanTests/CGPointTests.swift similarity index 95% rename from WeScanTests/CGPointTests.swift rename to Tests/WeScanTests/CGPointTests.swift index 887f81a8..e06cbd14 100644 --- a/WeScanTests/CGPointTests.swift +++ b/Tests/WeScanTests/CGPointTests.swift @@ -6,43 +6,43 @@ // Copyright © 2018 WeTransfer. All rights reserved. // -import XCTest @testable import WeScan +import XCTest final class CGPointTests: XCTestCase { - + func testSurroundingRect() { var point = CGPoint.zero var rect = point.surroundingSquare(withSize: 10.0) var expectedRect = CGRect(x: -5.0, y: -5.0, width: 10.0, height: 10.0) XCTAssert(rect == expectedRect) - + point = CGPoint(x: 50.0, y: 50.0) rect = point.surroundingSquare(withSize: 20.0) expectedRect = CGRect(x: 40.0, y: 40.0, width: 20.0, height: 20.0) XCTAssert(rect == expectedRect) } - + func testIsWithinDelta() { let point1 = CGPoint.zero var point2 = CGPoint.zero - + XCTAssert(point1.isWithin(delta: 10.0, ofPoint: point2) == true) XCTAssert(point1.isWithin(delta: 0.0, ofPoint: point2) == true) - + point2 = CGPoint(x: 1.0, y: 1.0) - + XCTAssert(point1.isWithin(delta: 1.1, ofPoint: point2) == true) XCTAssert(point1.isWithin(delta: 0.9, ofPoint: point2) == false) } - + func testDistanceTo() { var point1 = CGPoint.zero var point2 = CGPoint(x: 10.0, y: 10.0) - + var distance = point1.distanceTo(point: point2) XCTAssertTrue(distance == 14.142135623730951) - + point1 = CGPoint(x: -1, y: 3) point2 = CGPoint(x: 3, y: -4) distance = point1.distanceTo(point: point2) diff --git a/WeScanTests/CGRectTests.swift b/Tests/WeScanTests/CGRectTests.swift similarity index 95% rename from WeScanTests/CGRectTests.swift rename to Tests/WeScanTests/CGRectTests.swift index b2918ba3..a3483ec9 100644 --- a/WeScanTests/CGRectTests.swift +++ b/Tests/WeScanTests/CGRectTests.swift @@ -6,28 +6,28 @@ // Copyright © 2018 WeTransfer. All rights reserved. // -import XCTest @testable import WeScan +import XCTest final class CGRectTests: XCTestCase { - + func testScaleAndCenter() { var rect = CGRect(x: 0.0, y: 0.0, width: 100.0, height: 100.0) rect = rect.scaleAndCenter(withRatio: 0.5) var expectedRect = CGRect(x: 25.0, y: 25.0, width: 50.0, height: 50.0) - + XCTAssert(rect == expectedRect) - + rect = CGRect(x: 100.0, y: 100.0, width: 200.0, height: 200.0) rect = rect.scaleAndCenter(withRatio: 0.75) expectedRect = CGRect(x: 125.0, y: 125.0, width: 150.0, height: 150.0) - + XCTAssert(rect == expectedRect) - + rect = CGRect(x: 100.0, y: 100.0, width: 200.0, height: 200.0) rect = rect.scaleAndCenter(withRatio: 2.0) expectedRect = CGRect(x: 0.0, y: 0.0, width: 400.0, height: 400.0) - + XCTAssert(rect == expectedRect) } diff --git a/WeScanTests/CGSizeTests.swift b/Tests/WeScanTests/CGSizeTests.swift similarity index 95% rename from WeScanTests/CGSizeTests.swift rename to Tests/WeScanTests/CGSizeTests.swift index 8a8f5fb0..da80281d 100644 --- a/WeScanTests/CGSizeTests.swift +++ b/Tests/WeScanTests/CGSizeTests.swift @@ -6,25 +6,25 @@ // Copyright © 2019 WeTransfer. All rights reserved. // -import XCTest @testable import WeScan +import XCTest final class CGSizeTests: XCTestCase { - + func testScaleFactorIsWithinMax() { let size = CGSize(width: 5000, height: 1000) let scaleFactor = size.scaleFactor(forMaxWidth: 1000, maxHeight: 5000) - + let updatedSize = CGSize(width: size.width * scaleFactor, height: size.height * scaleFactor) - + XCTAssert(updatedSize.width <= 1000) XCTAssert(updatedSize.width <= 5000) } - + func testScaleFactorCorrectWhenBelowMax() { let size = CGSize(width: 10, height: 10) let scaleFactor = size.scaleFactor(forMaxWidth: 1000, maxHeight: 5000) - + XCTAssertEqual(scaleFactor, 1) } } diff --git a/WeScanTests/CIRectangleDetectorTests.swift b/Tests/WeScanTests/CIRectangleDetectorTests.swift similarity index 78% rename from WeScanTests/CIRectangleDetectorTests.swift rename to Tests/WeScanTests/CIRectangleDetectorTests.swift index 9e2cb5ab..26ef7ddb 100644 --- a/WeScanTests/CIRectangleDetectorTests.swift +++ b/Tests/WeScanTests/CIRectangleDetectorTests.swift @@ -6,53 +6,46 @@ // Copyright © 2018 WeTransfer. All rights reserved. // -import FBSnapshotTestCase -import XCTest +import SnapshotTesting @testable import WeScan +import XCTest + +final class CIRectangleDetectorTests: XCTestCase { -final class CIRectangleDetectorTests: FBSnapshotTestCase { - - override func setUp() { - super.setUp() - - recordMode = false - fileNameOptions = .OS - } - func testCorrectlyDetectsAndReturnsQuadilateral() { - + let targetSize = CGSize(width: 150, height: 150) - + let containerLayer = CALayer() containerLayer.backgroundColor = UIColor.white.cgColor containerLayer.frame = CGRect(origin: .zero, size: targetSize) containerLayer.masksToBounds = true - + let targetLayer = CALayer() targetLayer.backgroundColor = UIColor.black.cgColor targetLayer.frame = containerLayer.frame.insetBy(dx: 5, dy: 5) - + containerLayer.addSublayer(targetLayer) - + UIGraphicsBeginImageContextWithOptions(targetSize, true, 0.0) - + containerLayer.render(in: UIGraphicsGetCurrentContext()!) - + let image = UIGraphicsGetImageFromCurrentImageContext()! UIGraphicsEndImageContext() - + let ciImage = CIImage(cgImage: image.cgImage!) let quad = CIRectangleDetector.rectangle(forImage: ciImage)! - + let resultView = UIView(frame: containerLayer.frame) resultView.layer.addSublayer(containerLayer) - + let quadView = QuadrilateralView(frame: resultView.bounds) quadView.drawQuadrilateral(quad: quad, animated: false) quadView.backgroundColor = UIColor.red resultView.addSubview(quadView) - - FBSnapshotVerifyView(resultView, perPixelTolerance: 6 / 256) + + assertSnapshot(matching: resultView, as: .image(perceptualPrecision: 0.97)) } - + } diff --git a/WeScanTests/CaptureSessionTests.swift b/Tests/WeScanTests/CaptureSessionTests.swift similarity index 96% rename from WeScanTests/CaptureSessionTests.swift rename to Tests/WeScanTests/CaptureSessionTests.swift index 4ab093aa..cbb3e42f 100644 --- a/WeScanTests/CaptureSessionTests.swift +++ b/Tests/WeScanTests/CaptureSessionTests.swift @@ -6,43 +6,43 @@ // Copyright © 2018 WeTransfer. All rights reserved. // -import XCTest import AVFoundation @testable import WeScan +import XCTest final class CaptureSessionTests: XCTestCase { - + private let session = CaptureSession.current - + func testAutoScanEnabledByDefault() { XCTAssertTrue(session.isAutoScanEnabled) } - + func testEditOrientationUpByDefault() { XCTAssertEqual(session.editImageOrientation, CGImagePropertyOrientation.up) } - + func testCaptureDeviceIsAvailable() { session.device = MockCaptureDevice() XCTAssertNotNil(session.device) } - + func testCanToggleFlash() { session.device = MockCaptureDevice() - + let state = session.toggleFlash() XCTAssertEqual(state, CaptureSession.FlashState.on) } - + func testCanSetFocusPoint() { session.device = MockCaptureDevice() XCTAssertNoThrow(try session.setFocusPointToTapPoint(.zero)) } - + func testCanResetFocusToAuto() { session.device = MockCaptureDevice() XCTAssertNoThrow(try session.resetFocusToAuto()) XCTAssertEqual(session.device?.focusMode, AVCaptureDevice.FocusMode.continuousAutoFocus) } - + } diff --git a/WeScanTests/FocusRectangleViewTests.swift b/Tests/WeScanTests/FocusRectangleViewTests.swift similarity index 95% rename from WeScanTests/FocusRectangleViewTests.swift rename to Tests/WeScanTests/FocusRectangleViewTests.swift index e1edeac4..a49bfc4d 100644 --- a/WeScanTests/FocusRectangleViewTests.swift +++ b/Tests/WeScanTests/FocusRectangleViewTests.swift @@ -6,20 +6,20 @@ // Copyright © 2018 WeTransfer. All rights reserved. // -import XCTest @testable import WeScan +import XCTest final class FocusRectangleViewTests: XCTestCase { - + func testFocusRectangleIsRemovedCorrectly() { let hostView = UIView() let session = CaptureSession.current - + let focusRectangle = FocusRectangleView(touchPoint: CGPoint(x: 1, y: 1)) hostView.addSubview(focusRectangle) session.removeFocusRectangleIfNeeded(focusRectangle, animated: false) - + XCTAssertTrue(focusRectangle.superview == nil) } - + } diff --git a/WeScanTests/ImageFeatureTestHelpers.swift b/Tests/WeScanTests/ImageFeatureTestHelpers.swift similarity index 91% rename from WeScanTests/ImageFeatureTestHelpers.swift rename to Tests/WeScanTests/ImageFeatureTestHelpers.swift index fab8bb55..f7b9d4b7 100644 --- a/WeScanTests/ImageFeatureTestHelpers.swift +++ b/Tests/WeScanTests/ImageFeatureTestHelpers.swift @@ -6,8 +6,8 @@ // Copyright © 2018 WeTransfer. All rights reserved. // -import UIKit import AVFoundation +import UIKit @testable import WeScan enum ResourceImage: String { @@ -17,22 +17,22 @@ enum ResourceImage: String { } final class ImageFeatureTestHelpers: NSObject { - + static func getRectangleFeatures(from resourceImage: ResourceImage, withCount count: Int) -> [Quadrilateral] { var rectangleFeatures = [Quadrilateral]() - + for _ in 0 ..< count { rectangleFeatures.append(ImageFeatureTestHelpers.getRectangleFeature(from: resourceImage)) } - + return rectangleFeatures } - + static func getRectangleFeature(from resourceImage: ResourceImage) -> Quadrilateral { - let image = UIImage(named: resourceImage.rawValue, in: Bundle(for: ImageFeatureTestHelpers.self), compatibleWith: nil) + let image = UIImage(named: resourceImage.rawValue, in: Bundle.module, compatibleWith: nil) let ciImage = CIImage(image: image!)! return CIRectangleDetector.rectangle(forImage: ciImage)! } - + } diff --git a/WeScanTests/Info.plist b/Tests/WeScanTests/Info.plist similarity index 100% rename from WeScanTests/Info.plist rename to Tests/WeScanTests/Info.plist diff --git a/WeScanTests/QuadrilateralTests.swift b/Tests/WeScanTests/QuadrilateralTests.swift similarity index 95% rename from WeScanTests/QuadrilateralTests.swift rename to Tests/WeScanTests/QuadrilateralTests.swift index 60918099..9d641f24 100644 --- a/WeScanTests/QuadrilateralTests.swift +++ b/Tests/WeScanTests/QuadrilateralTests.swift @@ -5,190 +5,191 @@ // Created by Boris Emorine on 2/14/18. // Copyright © 2018 WeTransfer. All rights reserved. // +// swiftlint:disable function_body_length -import XCTest @testable import WeScan +import XCTest final class QuadrilateralTests: XCTestCase { - + func testEquatable() { let topLeft = CGPoint(x: 0.0, y: 100.0) let topRight = CGPoint(x: 100.0, y: 100.0) let bottomRight = CGPoint(x: 100.0, y: 0.0) let bottomLeft = CGPoint.zero - + let quad1 = Quadrilateral(topLeft: topLeft, topRight: topRight, bottomRight: bottomRight, bottomLeft: bottomLeft) var quad2 = Quadrilateral(topLeft: topLeft, topRight: topRight, bottomRight: bottomRight, bottomLeft: bottomLeft) - + XCTAssertTrue(quad1 == quad2) - + quad2 = Quadrilateral(topLeft: topLeft, topRight: topLeft, bottomRight: bottomRight, bottomLeft: bottomLeft) - + XCTAssertFalse(quad1 == quad2) } - + func testSquale() { var topLeft = CGPoint.zero var topRight = CGPoint(x: 100.0, y: 0.0) var bottomRight = CGPoint(x: 100.0, y: 100.0) var bottomLeft = CGPoint(x: 0.0, y: 100.0) - + var quad = Quadrilateral(topLeft: topLeft, topRight: topRight, bottomRight: bottomRight, bottomLeft: bottomLeft) var fromImageSize = CGSize(width: 500.0, height: 500.0) var toImageSize = CGSize(width: 1000.0, height: 1000.0) - + var scaledQuad = quad.scale(fromImageSize, toImageSize) - + XCTAssert(scaledQuad.topLeft == .zero) XCTAssert(scaledQuad.topRight == CGPoint(x: 200.0, y: 0.0)) XCTAssert(scaledQuad.bottomRight == CGPoint(x: 200.0, y: 200.0)) XCTAssert(scaledQuad.bottomLeft == CGPoint(x: 0.0, y: 200.0)) - + topLeft = CGPoint(x: 50.0, y: 75.0) topRight = CGPoint(x: 100.0, y: 25.0) bottomRight = CGPoint(x: 75.0, y: 100.0) bottomLeft = CGPoint(x: 25.0, y: 200.0) - + quad = Quadrilateral(topLeft: topLeft, topRight: topRight, bottomRight: bottomRight, bottomLeft: bottomLeft) scaledQuad = quad.scale(fromImageSize, toImageSize) - + XCTAssert(scaledQuad.topLeft == CGPoint(x: 100.0, y: 150.0)) XCTAssert(scaledQuad.topRight == CGPoint(x: 200.0, y: 50.0)) XCTAssert(scaledQuad.bottomRight == CGPoint(x: 150.0, y: 200.0)) XCTAssert(scaledQuad.bottomLeft == CGPoint(x: 50.0, y: 400.0)) - + fromImageSize = CGSize(width: 500.0, height: 500.0) toImageSize = CGSize(width: 250.0, height: 250.0) - + scaledQuad = quad.scale(fromImageSize, toImageSize) - + XCTAssert(scaledQuad.topLeft == CGPoint(x: 25.0, y: 37.5)) XCTAssert(scaledQuad.topRight == CGPoint(x: 50.0, y: 12.5)) XCTAssert(scaledQuad.bottomRight == CGPoint(x: 37.5, y: 50.0)) XCTAssert(scaledQuad.bottomLeft == CGPoint(x: 12.5, y: 100.0)) } - + func testScaleFixRotation() { var topLeft = CGPoint.zero var topRight = CGPoint(x: 500.0, y: 0.0) var bottomRight = CGPoint(x: 500.0, y: 500.0) var bottomLeft = CGPoint(x: 0.0, y: 500.0) - + var quad = Quadrilateral(topLeft: topLeft, topRight: topRight, bottomRight: bottomRight, bottomLeft: bottomLeft) var fromImageSize = CGSize(width: 500.0, height: 500.0) var toImageSize = CGSize(width: 500.0, height: 500.0) - + var scaledQuad = quad.scale(fromImageSize, toImageSize, withRotationAngle: CGFloat.pi / 2.0) - + XCTAssertTrue(scaledQuad.topLeft.isWithin(delta: 0.01, ofPoint: CGPoint(x: 500.0, y: 0.0))) XCTAssertTrue(scaledQuad.topRight.isWithin(delta: 0.01, ofPoint: CGPoint(x: 500.0, y: 500.0))) XCTAssertTrue(scaledQuad.bottomRight.isWithin(delta: 0.01, ofPoint: CGPoint(x: 0.0, y: 500.0))) XCTAssertTrue(scaledQuad.bottomLeft.isWithin(delta: 0.01, ofPoint: .zero)) - + topLeft = CGPoint(x: 0.0, y: 500.0) topRight = CGPoint(x: 1000.0, y: 500.0) bottomRight = CGPoint(x: 1000.0, y: 0.0) bottomLeft = .zero - + quad = Quadrilateral(topLeft: topLeft, topRight: topRight, bottomRight: bottomRight, bottomLeft: bottomLeft) - + fromImageSize = CGSize(width: 1000.0, height: 500.0) toImageSize = CGSize(width: 500.0, height: 1000.0) - + scaledQuad = quad.scale(fromImageSize, toImageSize, withRotationAngle: CGFloat.pi / 2.0) - + XCTAssertTrue(scaledQuad.topLeft.isWithin(delta: 0.01, ofPoint: .zero)) XCTAssertTrue(scaledQuad.topRight.isWithin(delta: 0.01, ofPoint: CGPoint(x: 0.0, y: 1000.0))) XCTAssertTrue(scaledQuad.bottomRight.isWithin(delta: 0.01, ofPoint: CGPoint(x: 500.0, y: 1000.0))) XCTAssertTrue(scaledQuad.bottomLeft.isWithin(delta: 0.01, ofPoint: CGPoint(x: 500.0, y: 0.0))) - + topLeft = CGPoint(x: 0.0, y: 500.0) topRight = CGPoint(x: 1000.0, y: 500.0) bottomRight = CGPoint(x: 1000.0, y: 0.0) bottomLeft = .zero - + quad = Quadrilateral(topLeft: topLeft, topRight: topRight, bottomRight: bottomRight, bottomLeft: bottomLeft) - + fromImageSize = CGSize(width: 1000.0, height: 500.0) toImageSize = CGSize(width: 250.0, height: 500.0) - + scaledQuad = quad.scale(fromImageSize, toImageSize, withRotationAngle: CGFloat.pi / 2.0) - + XCTAssertTrue(scaledQuad.topLeft.isWithin(delta: 0.01, ofPoint: .zero)) XCTAssertTrue(scaledQuad.topRight.isWithin(delta: 0.01, ofPoint: CGPoint(x: 0.0, y: 500.0))) XCTAssertTrue(scaledQuad.bottomRight.isWithin(delta: 0.01, ofPoint: CGPoint(x: 250.0, y: 500.0))) XCTAssertTrue(scaledQuad.bottomLeft.isWithin(delta: 0.01, ofPoint: CGPoint(x: 250.0, y: 0.0))) - + topLeft = CGPoint(x: 250.0, y: 500.0) topRight = CGPoint(x: 500.0, y: 500.0) bottomRight = CGPoint(x: 500.0, y: 250.0) bottomLeft = CGPoint(x: 250.0, y: 250.0) - + quad = Quadrilateral(topLeft: topLeft, topRight: topRight, bottomRight: bottomRight, bottomLeft: bottomLeft) - + fromImageSize = CGSize(width: 750.0, height: 750.0) toImageSize = CGSize(width: 750.0, height: 750.0) - + scaledQuad = quad.scale(fromImageSize, toImageSize, withRotationAngle: CGFloat.pi / 2.0) - + XCTAssertTrue(scaledQuad.topLeft.isWithin(delta: 0.01, ofPoint: CGPoint(x: 250.0, y: 250.0))) XCTAssertTrue(scaledQuad.topRight.isWithin(delta: 0.01, ofPoint: CGPoint(x: 250.0, y: 500.0))) XCTAssertTrue(scaledQuad.bottomRight.isWithin(delta: 0.01, ofPoint: CGPoint(x: 500.0, y: 500.0))) XCTAssertTrue(scaledQuad.bottomLeft.isWithin(delta: 0.01, ofPoint: CGPoint(x: 500.0, y: 250.0))) - + topLeft = CGPoint(x: 250.0, y: 500.0) topRight = CGPoint(x: 750.0, y: 500.0) bottomRight = CGPoint(x: 750.0, y: 250.0) bottomLeft = CGPoint(x: 250.0, y: 250.0) - + quad = Quadrilateral(topLeft: topLeft, topRight: topRight, bottomRight: bottomRight, bottomLeft: bottomLeft) - + fromImageSize = CGSize(width: 1000.0, height: 750.0) toImageSize = CGSize(width: 1500.0, height: 2000.0) - + scaledQuad = quad.scale(fromImageSize, toImageSize, withRotationAngle: CGFloat.pi / 2.0) - + XCTAssertTrue(scaledQuad.topLeft.isWithin(delta: 0.01, ofPoint: CGPoint(x: 500.0, y: 500.0))) XCTAssertTrue(scaledQuad.topRight.isWithin(delta: 0.01, ofPoint: CGPoint(x: 500.0, y: 1500.0))) XCTAssertTrue(scaledQuad.bottomRight.isWithin(delta: 0.01, ofPoint: CGPoint(x: 1000.0, y: 1500.0))) XCTAssertTrue(scaledQuad.bottomLeft.isWithin(delta: 0.01, ofPoint: CGPoint(x: 1000.0, y: 500.0))) - + topLeft = CGPoint(x: 100.0, y: 400.0) topRight = CGPoint(x: 200.0, y: 400.0) bottomRight = CGPoint(x: 200.0, y: 300.0) bottomLeft = CGPoint(x: 100.0, y: 300.0) - + quad = Quadrilateral(topLeft: topLeft, topRight: topRight, bottomRight: bottomRight, bottomLeft: bottomLeft) - + fromImageSize = CGSize(width: 1000.0, height: 500.0) toImageSize = CGSize(width: 1000.0, height: 2000.0) - + scaledQuad = quad.scale(fromImageSize, toImageSize, withRotationAngle: CGFloat.pi / 2.0) - + XCTAssertTrue(scaledQuad.topLeft.isWithin(delta: 0.01, ofPoint: CGPoint(x: 200.0, y: 200.0))) XCTAssertTrue(scaledQuad.topRight.isWithin(delta: 0.01, ofPoint: CGPoint(x: 200.0, y: 400.0))) XCTAssertTrue(scaledQuad.bottomRight.isWithin(delta: 0.01, ofPoint: CGPoint(x: 400.0, y: 400.0))) XCTAssertTrue(scaledQuad.bottomLeft.isWithin(delta: 0.01, ofPoint: CGPoint(x: 400.0, y: 200.0))) } - + func testReorganize() { var topLeft = CGPoint.zero var topRight = CGPoint(x: 100.0, y: 0.0) var bottomRight = CGPoint(x: 100.0, y: 100.0) var bottomLeft = CGPoint(x: 0.0, y: 100.0) - + var quad = Quadrilateral(topLeft: topLeft, topRight: topRight, bottomRight: bottomRight, bottomLeft: bottomLeft) quad.reorganize() var expectedQuad = Quadrilateral(topLeft: topLeft, topRight: topRight, bottomRight: bottomRight, bottomLeft: bottomLeft) - + XCTAssertTrue(quad == expectedQuad) - + quad = Quadrilateral(topLeft: bottomRight, topRight: bottomLeft, bottomRight: topLeft, bottomLeft: topRight) quad.reorganize() expectedQuad = Quadrilateral(topLeft: topLeft, topRight: topRight, bottomRight: bottomRight, bottomLeft: bottomLeft) - + XCTAssertTrue(quad == expectedQuad) - + topLeft = CGPoint(x: 0.0, y: 50.0) topRight = CGPoint(x: 70.0, y: 00.0) bottomRight = CGPoint(x: 100.0, y: 70.0) @@ -197,14 +198,14 @@ final class QuadrilateralTests: XCTestCase { quad = Quadrilateral(topLeft: topLeft, topRight: topRight, bottomRight: bottomRight, bottomLeft: bottomLeft) quad.reorganize() expectedQuad = Quadrilateral(topLeft: topLeft, topRight: topRight, bottomRight: bottomRight, bottomLeft: bottomLeft) - + XCTAssertTrue(quad == expectedQuad) - + quad = Quadrilateral(topLeft: topRight, topRight: topLeft, bottomRight: bottomLeft, bottomLeft: bottomRight) quad.reorganize() expectedQuad = Quadrilateral(topLeft: topLeft, topRight: topRight, bottomRight: bottomRight, bottomLeft: bottomLeft) - + XCTAssertTrue(quad == expectedQuad) } - + } diff --git a/WeScanTests/QuadrilateralViewTests.swift b/Tests/WeScanTests/QuadrilateralViewTests.swift similarity index 96% rename from WeScanTests/QuadrilateralViewTests.swift rename to Tests/WeScanTests/QuadrilateralViewTests.swift index dc126101..1d69c0df 100644 --- a/WeScanTests/QuadrilateralViewTests.swift +++ b/Tests/WeScanTests/QuadrilateralViewTests.swift @@ -5,75 +5,76 @@ // Created by Bobo on 6/9/18. // Copyright © 2018 WeTransfer. All rights reserved. // +// swiftlint:disable line_length -import XCTest @testable import WeScan +import XCTest final class QuadrilateralViewTests: XCTestCase { - + let vc = UIViewController() - + override func setUp() { super.setUp() vc.loadView() } - + func testNonEditable() { let quadView = QuadrilateralView(frame: CGRect(x: 0.0, y: 0.0, width: 200.0, height: 200.0)) quadView.editable = false vc.view.addSubview(quadView) - + let topLeftCornerView = quadView.cornerViewForCornerPosition(position: .topLeft) - + XCTAssertTrue(topLeftCornerView.isHidden, "A non editable QuadrilateralView should have hidden corners") } - + func testHightlight() { let quadView = QuadrilateralView(frame: CGRect(x: 0.0, y: 0.0, width: 200.0, height: 200.0)) quadView.editable = true vc.view.addSubview(quadView) - + let quad = Quadrilateral(topLeft: .zero, topRight: CGPoint(x: 200.0, y: 0.0), bottomRight: CGPoint(x: 200.0, y: 200.0), bottomLeft: CGPoint(x: 0.0, y: 220.0)) - + quadView.drawQuadrilateral(quad: quad, animated: false) - + let topRightCornerView = quadView.cornerViewForCornerPosition(position: .topRight) - + XCTAssertFalse(topRightCornerView.isHidden, "An editable QuadrilateralView should not have hidden corners") - + let defaultTopLeftCornerViewFrame = topRightCornerView.frame - + quadView.highlightCornerAtPosition(position: .topRight, with: UIImage()) - + let highlitedTopLeftCornerViewFrame = topRightCornerView.frame - + XCTAssert(defaultTopLeftCornerViewFrame.width < highlitedTopLeftCornerViewFrame.width, "A highlighted corner view should be bigger than in its default state") XCTAssert(defaultTopLeftCornerViewFrame.height < highlitedTopLeftCornerViewFrame.height, "A highlighted corner view should be bigger than in its default state") XCTAssertEqual(defaultTopLeftCornerViewFrame.origin.x + defaultTopLeftCornerViewFrame.width / 2.0, highlitedTopLeftCornerViewFrame.origin.x + highlitedTopLeftCornerViewFrame.width / 2.0, "A highlighted corner view should have the same center as in its default state") XCTAssertEqual(defaultTopLeftCornerViewFrame.origin.y + defaultTopLeftCornerViewFrame.height / 2.0, highlitedTopLeftCornerViewFrame.origin.y + highlitedTopLeftCornerViewFrame.height / 2.0, "A highlighted corner view should have the same center as in its default state") XCTAssertTrue(topRightCornerView.isHighlighted) - + quadView.resetHighlightedCornerViews() - + XCTAssert(defaultTopLeftCornerViewFrame.width == topRightCornerView.frame.width, "After reseting, the corner view frame should be back to its inital value") XCTAssert(defaultTopLeftCornerViewFrame.height == topRightCornerView.frame.height, "After reseting, the corner view frame should be back to its inital value") XCTAssertEqual(defaultTopLeftCornerViewFrame.origin.x + defaultTopLeftCornerViewFrame.width / 2.0, topRightCornerView.center.x, "After reseting, the corner view center should still not have moved") XCTAssertEqual(defaultTopLeftCornerViewFrame.origin.y + defaultTopLeftCornerViewFrame.height / 2.0, topRightCornerView.center.y, "After reseting, the corner view center should still not have moved") XCTAssertFalse(topRightCornerView.isHighlighted) } - + func testDrawQuad() { let quadView = QuadrilateralView(frame: CGRect(x: 0.0, y: 0.0, width: 200.0, height: 200.0)) quadView.editable = true vc.view.addSubview(quadView) - + let quad = Quadrilateral(topLeft: .zero, topRight: CGPoint(x: 200.0, y: 0.0), bottomRight: CGPoint(x: 200.0, y: 200.0), bottomLeft: CGPoint(x: 0.0, y: 220.0)) - + quadView.drawQuadrilateral(quad: quad, animated: false) - + let topLeftCornerView = quadView.cornerViewForCornerPosition(position: .topLeft) XCTAssertEqual(topLeftCornerView.center, quad.topLeft) - + let topRightCornerView = quadView.cornerViewForCornerPosition(position: .topRight) XCTAssertEqual(topRightCornerView.center, quad.topRight) @@ -83,5 +84,5 @@ final class QuadrilateralViewTests: XCTestCase { let bottomLeftCornerView = quadView.cornerViewForCornerPosition(position: .bottomLeft) XCTAssertEqual(bottomLeftCornerView.center, quad.bottomLeft) } - + } diff --git a/WeScanTests/RectangleFeaturesFunnelTests.swift b/Tests/WeScanTests/RectangleFeaturesFunnelTests.swift similarity index 88% rename from WeScanTests/RectangleFeaturesFunnelTests.swift rename to Tests/WeScanTests/RectangleFeaturesFunnelTests.swift index 69f80188..abc70196 100644 --- a/WeScanTests/RectangleFeaturesFunnelTests.swift +++ b/Tests/WeScanTests/RectangleFeaturesFunnelTests.swift @@ -6,127 +6,128 @@ // Copyright © 2018 WeTransfer. All rights reserved. // -import XCTest @testable import WeScan +import XCTest final class RectangleFeaturesFunnelTests: XCTestCase { - + var funnel = RectangleFeaturesFunnel() - + override func setUp() { super.setUp() funnel = RectangleFeaturesFunnel() } - + /// Ensures that feeding the funnel with less than the minimum number of rectangles doesn't trigger the completion block. func testAddMinUnderThreshold() { let rectangleFeatures = ImageFeatureTestHelpers.getRectangleFeatures(from: .rect1, withCount: funnel.minNumberOfRectangles - 1) - + let expectation = XCTestExpectation(description: "Funnel add callback") expectation.isInverted = true - + for i in 0 ..< rectangleFeatures.count { - funnel.add(rectangleFeatures[i], currentlyDisplayedRectangle: nil) { (_, _) in + funnel.add(rectangleFeatures[i], currentlyDisplayedRectangle: nil) { _, _ in expectation.fulfill() } } - + wait(for: [expectation], timeout: 3.0) } - + /// Ensures that feeding the funnel with the minimum number of rectangles triggers the completion block. func testAddMinThreshold() { let rectangleFeatures = ImageFeatureTestHelpers.getRectangleFeatures(from: .rect1, withCount: funnel.minNumberOfRectangles) - + let expectation = XCTestExpectation(description: "Funnel add callback") - + for i in 0 ..< rectangleFeatures.count { - funnel.add(rectangleFeatures[i], currentlyDisplayedRectangle: nil) { (_, _) in + funnel.add(rectangleFeatures[i], currentlyDisplayedRectangle: nil) { _, _ in expectation.fulfill() } } - + wait(for: [expectation], timeout: 3.0) } - + /// Ensures that feeding the funnel with a lot of rectangles triggers the completion block the appropriate amount of time. func testAddMaxThreshold() { let rectangleFeatures = ImageFeatureTestHelpers.getRectangleFeatures(from: .rect1, withCount: funnel.maxNumberOfRectangles * 2) - + let expectation = XCTestExpectation(description: "Funnel add callback") expectation.expectedFulfillmentCount = rectangleFeatures.count - funnel.minNumberOfRectangles - + for i in 0 ..< rectangleFeatures.count { - funnel.add(rectangleFeatures[i], currentlyDisplayedRectangle: nil) { (_, _) in + funnel.add(rectangleFeatures[i], currentlyDisplayedRectangle: nil) { _, _ in expectation.fulfill() } } - + wait(for: [expectation], timeout: 3.0) } - + /// Ensures that feeding the funnel with rectangles similar to the currently displayed one doesn't trigger the completion block. func testAddPreviouslyDisplayedRect() { let rectangleFeatures = ImageFeatureTestHelpers.getRectangleFeatures(from: .rect1, withCount: funnel.maxNumberOfRectangles * 2) - + let expectation = XCTestExpectation(description: "Funnel add callback") expectation.isInverted = true - + let currentlyDisplayedRect = rectangleFeatures.first! - + for i in 0 ..< rectangleFeatures.count { - funnel.add(rectangleFeatures[i], currentlyDisplayedRectangle: currentlyDisplayedRect) { (_, _) in + funnel.add(rectangleFeatures[i], currentlyDisplayedRectangle: currentlyDisplayedRect) { _, _ in expectation.fulfill() } } - + wait(for: [expectation], timeout: 3.0) } - - /// Ensures that feeding the funnel with 2 images alternatively (image 1, image 2, image 1, image 2, image 1 etc.), doesn't make the completion block get called every time a new one is added. + + /// Ensures that feeding the funnel with 2 images alternatively (image 1, image 2, image 1, image 2, image 1 etc.), + /// doesn't make the completion block get called every time a new one is added. func testAddAlternateImage() { let count = 100 let type1RectangleFeatures = ImageFeatureTestHelpers.getRectangleFeatures(from: .rect1, withCount: count) let type2RectangleFeatures = ImageFeatureTestHelpers.getRectangleFeatures(from: .rect2, withCount: count) var currentlyDisplayedRect: Quadrilateral? - + let expectation = XCTestExpectation(description: "Funnel add callback") expectation.isInverted = true - + for i in 0 ..< count { let rectangleFeature = i % 2 == 0 ? type1RectangleFeatures[i] : type2RectangleFeatures[i] - - funnel.add(rectangleFeature, currentlyDisplayedRectangle: currentlyDisplayedRect, completion: { (result, rectFeature) in - + + funnel.add(rectangleFeature, currentlyDisplayedRectangle: currentlyDisplayedRect, completion: { result, rectFeature in + currentlyDisplayedRect = rectFeature if i >= funnel.maxNumberOfRectangles && result == .showOnly { expectation.fulfill() } }) } - + wait(for: [expectation], timeout: 3.0) } - + func testAddMultipleImages() { let count = max(funnel.minNumberOfMatches + 2, funnel.minNumberOfRectangles) - + let rectangleFeaturesType1 = ImageFeatureTestHelpers.getRectangleFeatures(from: .rect1, withCount: count - 1) let rectangleFeatureType2 = ImageFeatureTestHelpers.getRectangleFeature(from: .rect2) - + for i in 0 ..< rectangleFeaturesType1.count { funnel.add(rectangleFeaturesType1[i], currentlyDisplayedRectangle: nil, completion: {_, _ in }) } - + let expectationType1 = XCTestExpectation(description: "Funnel add callback") - - funnel.add(rectangleFeatureType2, currentlyDisplayedRectangle: nil) { (_, rectangle) in + + funnel.add(rectangleFeatureType2, currentlyDisplayedRectangle: nil) { _, rectangle in XCTAssert(rectangle.isWithin(1.0, ofRectangleFeature: rectangleFeaturesType1[0])) expectationType1.fulfill() } - + wait(for: [expectationType1], timeout: 3.0) } - + } diff --git a/WeScanTests/Resources/BigRectangle.jpg b/Tests/WeScanTests/Resources/BigRectangle.jpg similarity index 100% rename from WeScanTests/Resources/BigRectangle.jpg rename to Tests/WeScanTests/Resources/BigRectangle.jpg diff --git a/WeScanTests/Resources/Rectangle.jpg b/Tests/WeScanTests/Resources/Rectangle.jpg similarity index 100% rename from WeScanTests/Resources/Rectangle.jpg rename to Tests/WeScanTests/Resources/Rectangle.jpg diff --git a/WeScanTests/Resources/Square.jpg b/Tests/WeScanTests/Resources/Square.jpg similarity index 100% rename from WeScanTests/Resources/Square.jpg rename to Tests/WeScanTests/Resources/Square.jpg diff --git a/WeScanTests/ReviewViewControllerTests.swift b/Tests/WeScanTests/ReviewViewControllerTests.swift similarity index 88% rename from WeScanTests/ReviewViewControllerTests.swift rename to Tests/WeScanTests/ReviewViewControllerTests.swift index 86009b39..fe0b8105 100644 --- a/WeScanTests/ReviewViewControllerTests.swift +++ b/Tests/WeScanTests/ReviewViewControllerTests.swift @@ -5,104 +5,104 @@ // Created by Julian Schiavo on 7/1/2019. // Copyright © 2019 WeTransfer. All rights reserved. // +// swiftlint:disable line_length -import XCTest -import FBSnapshotTestCase import CoreGraphics +import SnapshotTesting @testable import WeScan +import XCTest + +final class ReviewViewControllerTests: XCTestCase { -final class ReviewViewControllerTests: FBSnapshotTestCase { - var demoScan: ImageScannerScan! var enhancedDemoScan: ImageScannerScan! var demoQuad = Quadrilateral(topLeft: .zero, topRight: .zero, bottomRight: .zero, bottomLeft: .zero) - + override func setUp() { super.setUp() - recordMode = false // Set up the demo image using rectangles (purposefully made to be different on each rotation) let detailSize = CGSize(width: 20, height: 40) let detailLayer = CALayer() detailLayer.backgroundColor = UIColor.red.cgColor detailLayer.frame = CGRect(x: 30, y: 0, width: detailSize.width, height: detailSize.height) - + let backgroundSize = CGSize(width: 50, height: 120) let backgroundLayer = CALayer() backgroundLayer.backgroundColor = UIColor.white.cgColor backgroundLayer.frame = CGRect(x: 0, y: 0, width: backgroundSize.width, height: backgroundSize.height) backgroundLayer.addSublayer(detailLayer) - + UIGraphicsBeginImageContextWithOptions(backgroundSize, true, 1.0) backgroundLayer.render(in: UIGraphicsGetCurrentContext()!) let image = UIGraphicsGetImageFromCurrentImageContext()! demoScan = ImageScannerScan(image: image) - + backgroundLayer.backgroundColor = UIColor.black.cgColor backgroundLayer.render(in: UIGraphicsGetCurrentContext()!) let enhancedImage = UIGraphicsGetImageFromCurrentImageContext()! enhancedDemoScan = ImageScannerScan(image: enhancedImage) - + UIGraphicsEndImageContext() } - + func testDemoImageIsCorrect() { let results = ImageScannerResults(detectedRectangle: demoQuad, originalScan: demoScan, croppedScan: demoScan, enhancedScan: demoScan, doesUserPreferEnhancedScan: false) let vc = ReviewViewController(results: results) vc.viewDidLoad() - FBSnapshotVerifyView(vc.imageView) + assertSnapshot(matching: vc.imageView, as: .image) } - + func testImageIsCorrectlyRotated90() { let results = ImageScannerResults(detectedRectangle: demoQuad, originalScan: demoScan, croppedScan: demoScan, enhancedScan: demoScan, doesUserPreferEnhancedScan: false) let vc = ReviewViewController(results: results) vc.viewDidLoad() vc.rotateImage() - FBSnapshotVerifyView(vc.imageView) + assertSnapshot(matching: vc.imageView, as: .image) } - + func testImageIsCorrectlyRotated180() { let results = ImageScannerResults(detectedRectangle: demoQuad, originalScan: demoScan, croppedScan: demoScan, enhancedScan: demoScan, doesUserPreferEnhancedScan: false) let vc = ReviewViewController(results: results) vc.viewDidLoad() - + vc.rotateImage() vc.rotateImage() - - FBSnapshotVerifyView(vc.imageView) + + assertSnapshot(matching: vc.imageView, as: .image) } - + func testImageIsCorrectlyRotated270() { let results = ImageScannerResults(detectedRectangle: demoQuad, originalScan: demoScan, croppedScan: demoScan, enhancedScan: demoScan, doesUserPreferEnhancedScan: false) let vc = ReviewViewController(results: results) vc.viewDidLoad() - + vc.rotateImage() vc.rotateImage() vc.rotateImage() - - FBSnapshotVerifyView(vc.imageView) + + assertSnapshot(matching: vc.imageView, as: .image) } - + func testImageIsCorrectlyRotated360() { let results = ImageScannerResults(detectedRectangle: demoQuad, originalScan: demoScan, croppedScan: demoScan, enhancedScan: demoScan, doesUserPreferEnhancedScan: false) let vc = ReviewViewController(results: results) vc.viewDidLoad() - + vc.rotateImage() vc.rotateImage() vc.rotateImage() vc.rotateImage() - - FBSnapshotVerifyView(vc.imageView) + + assertSnapshot(matching: vc.imageView, as: .image) } - + func testEnhancedImage() { let results = ImageScannerResults(detectedRectangle: demoQuad, originalScan: demoScan, croppedScan: demoScan, enhancedScan: enhancedDemoScan, doesUserPreferEnhancedScan: false) let viewController = ReviewViewController(results: results) viewController.viewDidLoad() - + viewController.toggleEnhancedImage() - FBSnapshotVerifyView(viewController.imageView) + assertSnapshot(matching: viewController.imageView, as: .image) } } diff --git a/WeScanTests/UIImageTests.swift b/Tests/WeScanTests/UIImageTests.swift similarity index 66% rename from WeScanTests/UIImageTests.swift rename to Tests/WeScanTests/UIImageTests.swift index 47b2b7fb..24201ec1 100644 --- a/WeScanTests/UIImageTests.swift +++ b/Tests/WeScanTests/UIImageTests.swift @@ -6,90 +6,84 @@ // Copyright © 2018 WeTransfer. All rights reserved. // -import XCTest -import FBSnapshotTestCase +import SnapshotTesting @testable import WeScan +import XCTest + +final class UIImageTests: XCTestCase { -final class UIImageTests: FBSnapshotTestCase { - - override func setUp() { - super.setUp() - - recordMode = false - } - func testRotateUpFacingImageCorrectly() { - let image = UIImage(named: ResourceImage.rect2.rawValue, in: Bundle(for: ImageFeatureTestHelpers.self), compatibleWith: nil) + let image = UIImage(named: ResourceImage.rect2.rawValue, in: Bundle.module, compatibleWith: nil) let orientatedImage = UIImage(cgImage: image!.cgImage!, scale: 1.0, orientation: .up) - + let view = UIImageView(image: orientatedImage.applyingPortraitOrientation()) view.sizeToFit() - - FBSnapshotVerifyView(view, perPixelTolerance: 6 / 256) + + assertSnapshot(matching: view, as: .image) } - + func testRotateDownFacingImageCorrectly() { - let image = UIImage(named: ResourceImage.rect2.rawValue, in: Bundle(for: ImageFeatureTestHelpers.self), compatibleWith: nil) + let image = UIImage(named: ResourceImage.rect2.rawValue, in: Bundle.module, compatibleWith: nil) let orientatedImage = UIImage(cgImage: image!.cgImage!, scale: 1.0, orientation: .down) - + let view = UIImageView(image: orientatedImage.applyingPortraitOrientation()) view.sizeToFit() - - FBSnapshotVerifyView(view, perPixelTolerance: 6 / 256) + + assertSnapshot(matching: view, as: .image) } - + func testRotateLeftFacingImageCorrectly() { - let image = UIImage(named: ResourceImage.rect2.rawValue, in: Bundle(for: ImageFeatureTestHelpers.self), compatibleWith: nil) + let image = UIImage(named: ResourceImage.rect2.rawValue, in: Bundle.module, compatibleWith: nil) let orientatedImage = UIImage(cgImage: image!.cgImage!, scale: 1.0, orientation: .left) - + let view = UIImageView(image: orientatedImage.applyingPortraitOrientation()) view.sizeToFit() - - FBSnapshotVerifyView(view, perPixelTolerance: 6 / 256) + + assertSnapshot(matching: view, as: .image) } - + func testRotateRightFacingImageCorrectly() { - let image = UIImage(named: ResourceImage.rect2.rawValue, in: Bundle(for: ImageFeatureTestHelpers.self), compatibleWith: nil) + let image = UIImage(named: ResourceImage.rect2.rawValue, in: Bundle.module, compatibleWith: nil) let orientatedImage = UIImage(cgImage: image!.cgImage!, scale: 1.0, orientation: .right) - + let view = UIImageView(image: orientatedImage.applyingPortraitOrientation()) view.sizeToFit() - - FBSnapshotVerifyView(view, perPixelTolerance: 6 / 256) + + assertSnapshot(matching: view, as: .image) } - + func testRotateDefaultFacingImageCorrectly() { - let image = UIImage(named: ResourceImage.rect2.rawValue, in: Bundle(for: ImageFeatureTestHelpers.self), compatibleWith: nil) + let image = UIImage(named: ResourceImage.rect2.rawValue, in: Bundle.module, compatibleWith: nil) let orientatedImage = UIImage(cgImage: image!.cgImage!, scale: 1.0, orientation: .rightMirrored) - + let view = UIImageView(image: orientatedImage.applyingPortraitOrientation()) view.sizeToFit() - - FBSnapshotVerifyView(view, perPixelTolerance: 6 / 256) + + assertSnapshot(matching: view, as: .image) } - + func testRotateImageCorrectly() { - let image = UIImage(named: ResourceImage.rect2.rawValue, in: Bundle(for: ImageFeatureTestHelpers.self), compatibleWith: nil) - + let image = UIImage(named: ResourceImage.rect2.rawValue, in: Bundle.module, compatibleWith: nil) + let view = UIImageView(image: image!.rotated(by: Measurement(value: Double.pi * 0.2, unit: .radians), options: [])) view.sizeToFit() - - FBSnapshotVerifyView(view, perPixelTolerance: 6 / 256) + + assertSnapshot(matching: view, as: .image) } - + func testScaledImageSuccessfully() { - let image = UIImage(named: ResourceImage.rect2.rawValue, in: Bundle(for: ImageFeatureTestHelpers.self), compatibleWith: nil)! + let image = UIImage(named: ResourceImage.rect2.rawValue, in: Bundle.module, compatibleWith: nil)! XCTAssertNotNil(image.scaledImage(scaleFactor: 0.2)) } - + func testScaledImageCorrectly() { - let image = UIImage(named: ResourceImage.rect2.rawValue, in: Bundle(for: ImageFeatureTestHelpers.self), compatibleWith: nil)! + let image = UIImage(named: ResourceImage.rect2.rawValue, in: Bundle.module, compatibleWith: nil)! XCTAssertEqual(image.size, CGSize(width: 500, height: 500)) XCTAssertEqual(image.scaledImage(scaleFactor: 0.2)!.size, CGSize(width: 100, height: 100)) } - + func testPDFDataCreationSuccessful() { - let image = UIImage(named: ResourceImage.rect2.rawValue, in: Bundle(for: ImageFeatureTestHelpers.self), compatibleWith: nil)! + let image = UIImage(named: ResourceImage.rect2.rawValue, in: Bundle.module, compatibleWith: nil)! XCTAssertNotNil(image.pdfData()) } } diff --git a/Tests/WeScanTests/VisionRectangleDetectorTests.swift b/Tests/WeScanTests/VisionRectangleDetectorTests.swift new file mode 100644 index 00000000..bd4632e0 --- /dev/null +++ b/Tests/WeScanTests/VisionRectangleDetectorTests.swift @@ -0,0 +1,102 @@ +// +// VisionRectangleDetectorTests.swift +// WeScanTests +// +// Created by James Campbell on 8/8/18. +// Copyright © 2018 WeTransfer. All rights reserved. +// + +import SnapshotTesting +@testable import WeScan +import XCTest + +final class VisionRectangleDetectorTests: XCTestCase { + + private var containerLayer: CALayer! + private var image: UIImage! + + override func setUp() { + super.setUp() + + // Setting up containerLayer and creating the image to be tested on both tests in this class. + containerLayer = CALayer() + let targetSize = CGSize(width: 150, height: 150) + + containerLayer.backgroundColor = UIColor.white.cgColor + containerLayer.frame = CGRect(origin: .zero, size: targetSize) + containerLayer.masksToBounds = true + + let targetLayer = CALayer() + targetLayer.backgroundColor = UIColor.black.cgColor + targetLayer.frame = containerLayer.frame.insetBy(dx: 5, dy: 5) + + containerLayer.addSublayer(targetLayer) + + UIGraphicsBeginImageContextWithOptions(targetSize, true, 0.0) + + containerLayer.render(in: UIGraphicsGetCurrentContext()!) + + self.image = UIGraphicsGetImageFromCurrentImageContext()! + + UIGraphicsEndImageContext() + + } + + override func tearDown() { + super.tearDown() + containerLayer = nil + image = nil + } + + func testCorrectlyDetectsAndReturnsQuadilateral() { + + let ciImage = CIImage(cgImage: image.cgImage!) + let expectation = XCTestExpectation(description: "Detect rectangle on CIImage") + + VisionRectangleDetector.rectangle(forImage: ciImage) { quad in + + DispatchQueue.main.async { + + let resultView = UIView(frame: self.containerLayer.frame) + resultView.layer.addSublayer(self.containerLayer) + + let quadView = QuadrilateralView(frame: resultView.bounds) + quadView.drawQuadrilateral(quad: quad!, animated: false) + quadView.backgroundColor = UIColor.red + resultView.addSubview(quadView) + + assertSnapshot(matching: resultView, as: .image) + expectation.fulfill() + } + } + + wait(for: [expectation], timeout: 3.0) + } + + func testCorrectlyDetectsAndReturnsQuadilateralPixelBuffer() { + + let expectation = XCTestExpectation(description: "Detect rectangle on CVPixelBuffer") + if let pixelBuffer = image.pixelBuffer() { + VisionRectangleDetector.rectangle(forPixelBuffer: pixelBuffer) { quad in + + DispatchQueue.main.async { + + let resultView = UIView(frame: self.containerLayer.frame) + resultView.layer.addSublayer(self.containerLayer) + + let quadView = QuadrilateralView(frame: resultView.bounds) + quadView.drawQuadrilateral(quad: quad!, animated: false) + quadView.backgroundColor = UIColor.red + resultView.addSubview(quadView) + + assertSnapshot(matching: resultView, as: .image) + expectation.fulfill() + } + } + } else { + XCTFail("could not convert image to pixelBuffer") + } + + wait(for: [expectation], timeout: 3.0) + } +} diff --git a/WeScanTests/WeScanTests-Bridging-Header.h b/Tests/WeScanTests/WeScanTests-Bridging-Header.h similarity index 100% rename from WeScanTests/WeScanTests-Bridging-Header.h rename to Tests/WeScanTests/WeScanTests-Bridging-Header.h diff --git a/WeScanTests/FailureDiffs/WeScanTests.VisionRectangleDetectorTests/failed_testCorrectlyDetectsAndReturnsQuadilateral@3x.png b/Tests/WeScanTests/__Snapshots__/CIRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateral.1.png similarity index 100% rename from WeScanTests/FailureDiffs/WeScanTests.VisionRectangleDetectorTests/failed_testCorrectlyDetectsAndReturnsQuadilateral@3x.png rename to Tests/WeScanTests/__Snapshots__/CIRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateral.1.png diff --git a/Tests/WeScanTests/__Snapshots__/ReviewViewControllerTests/testDemoImageIsCorrect.1.png b/Tests/WeScanTests/__Snapshots__/ReviewViewControllerTests/testDemoImageIsCorrect.1.png new file mode 100644 index 00000000..b7cb5d7f Binary files /dev/null and b/Tests/WeScanTests/__Snapshots__/ReviewViewControllerTests/testDemoImageIsCorrect.1.png differ diff --git a/Tests/WeScanTests/__Snapshots__/ReviewViewControllerTests/testEnhancedImage.1.png b/Tests/WeScanTests/__Snapshots__/ReviewViewControllerTests/testEnhancedImage.1.png new file mode 100644 index 00000000..ee772aba Binary files /dev/null and b/Tests/WeScanTests/__Snapshots__/ReviewViewControllerTests/testEnhancedImage.1.png differ diff --git a/Tests/WeScanTests/__Snapshots__/ReviewViewControllerTests/testImageIsCorrectlyRotated180.1.png b/Tests/WeScanTests/__Snapshots__/ReviewViewControllerTests/testImageIsCorrectlyRotated180.1.png new file mode 100644 index 00000000..51a9f31c Binary files /dev/null and b/Tests/WeScanTests/__Snapshots__/ReviewViewControllerTests/testImageIsCorrectlyRotated180.1.png differ diff --git a/Tests/WeScanTests/__Snapshots__/ReviewViewControllerTests/testImageIsCorrectlyRotated270.1.png b/Tests/WeScanTests/__Snapshots__/ReviewViewControllerTests/testImageIsCorrectlyRotated270.1.png new file mode 100644 index 00000000..d36ba16c Binary files /dev/null and b/Tests/WeScanTests/__Snapshots__/ReviewViewControllerTests/testImageIsCorrectlyRotated270.1.png differ diff --git a/Tests/WeScanTests/__Snapshots__/ReviewViewControllerTests/testImageIsCorrectlyRotated360.1.png b/Tests/WeScanTests/__Snapshots__/ReviewViewControllerTests/testImageIsCorrectlyRotated360.1.png new file mode 100644 index 00000000..b7cb5d7f Binary files /dev/null and b/Tests/WeScanTests/__Snapshots__/ReviewViewControllerTests/testImageIsCorrectlyRotated360.1.png differ diff --git a/Tests/WeScanTests/__Snapshots__/ReviewViewControllerTests/testImageIsCorrectlyRotated90.1.png b/Tests/WeScanTests/__Snapshots__/ReviewViewControllerTests/testImageIsCorrectlyRotated90.1.png new file mode 100644 index 00000000..4ce65d9d Binary files /dev/null and b/Tests/WeScanTests/__Snapshots__/ReviewViewControllerTests/testImageIsCorrectlyRotated90.1.png differ diff --git a/WeScanTests/ReferenceImages_64/WeScanTests.UIImageTests/testRotateDefaultFacingImageCorrectly@3x.png b/Tests/WeScanTests/__Snapshots__/UIImageTests/testRotateDefaultFacingImageCorrectly.1.png similarity index 53% rename from WeScanTests/ReferenceImages_64/WeScanTests.UIImageTests/testRotateDefaultFacingImageCorrectly@3x.png rename to Tests/WeScanTests/__Snapshots__/UIImageTests/testRotateDefaultFacingImageCorrectly.1.png index a2300319..0d03b18a 100644 Binary files a/WeScanTests/ReferenceImages_64/WeScanTests.UIImageTests/testRotateDefaultFacingImageCorrectly@3x.png and b/Tests/WeScanTests/__Snapshots__/UIImageTests/testRotateDefaultFacingImageCorrectly.1.png differ diff --git a/WeScanTests/FailureDiffs/WeScanTests.UIImageTests/reference_testRotateDownFacingImageCorrectly@3x.png b/Tests/WeScanTests/__Snapshots__/UIImageTests/testRotateDownFacingImageCorrectly.1.png similarity index 100% rename from WeScanTests/FailureDiffs/WeScanTests.UIImageTests/reference_testRotateDownFacingImageCorrectly@3x.png rename to Tests/WeScanTests/__Snapshots__/UIImageTests/testRotateDownFacingImageCorrectly.1.png diff --git a/WeScanTests/FailureDiffs/WeScanTests.UIImageTests/reference_testRotateImageCorrectly@3x.png b/Tests/WeScanTests/__Snapshots__/UIImageTests/testRotateImageCorrectly.1.png similarity index 100% rename from WeScanTests/FailureDiffs/WeScanTests.UIImageTests/reference_testRotateImageCorrectly@3x.png rename to Tests/WeScanTests/__Snapshots__/UIImageTests/testRotateImageCorrectly.1.png diff --git a/Tests/WeScanTests/__Snapshots__/UIImageTests/testRotateLeftFacingImageCorrectly.1.png b/Tests/WeScanTests/__Snapshots__/UIImageTests/testRotateLeftFacingImageCorrectly.1.png new file mode 100644 index 00000000..039150bf Binary files /dev/null and b/Tests/WeScanTests/__Snapshots__/UIImageTests/testRotateLeftFacingImageCorrectly.1.png differ diff --git a/WeScanTests/FailureDiffs/WeScanTests.UIImageTests/reference_testRotateRightFacingImageCorrectly@3x.png b/Tests/WeScanTests/__Snapshots__/UIImageTests/testRotateRightFacingImageCorrectly.1.png similarity index 100% rename from WeScanTests/FailureDiffs/WeScanTests.UIImageTests/reference_testRotateRightFacingImageCorrectly@3x.png rename to Tests/WeScanTests/__Snapshots__/UIImageTests/testRotateRightFacingImageCorrectly.1.png diff --git a/WeScanTests/FailureDiffs/WeScanTests.UIImageTests/reference_testRotateUpFacingImageCorrectly@3x.png b/Tests/WeScanTests/__Snapshots__/UIImageTests/testRotateUpFacingImageCorrectly.1.png similarity index 100% rename from WeScanTests/FailureDiffs/WeScanTests.UIImageTests/reference_testRotateUpFacingImageCorrectly@3x.png rename to Tests/WeScanTests/__Snapshots__/UIImageTests/testRotateUpFacingImageCorrectly.1.png diff --git a/WeScanTests/ReferenceImages_64/WeScanTests.VisionRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateral@3x.png b/Tests/WeScanTests/__Snapshots__/VisionRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateral.1.png similarity index 100% rename from WeScanTests/ReferenceImages_64/WeScanTests.VisionRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateral@3x.png rename to Tests/WeScanTests/__Snapshots__/VisionRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateral.1.png diff --git a/WeScanTests/ReferenceImages_64/WeScanTests.VisionRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateralPixelBuffer@3x.png b/Tests/WeScanTests/__Snapshots__/VisionRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateralPixelBuffer.1.png similarity index 100% rename from WeScanTests/ReferenceImages_64/WeScanTests.VisionRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateralPixelBuffer@3x.png rename to Tests/WeScanTests/__Snapshots__/VisionRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateralPixelBuffer.1.png diff --git a/WeScan.xcodeproj/project.pbxproj b/WeScan.xcodeproj/project.pbxproj deleted file mode 100644 index 66af5af4..00000000 --- a/WeScan.xcodeproj/project.pbxproj +++ /dev/null @@ -1,1327 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 48; - objects = { - -/* Begin PBXBuildFile section */ - 362967782294C23700B9FC4A /* CGImagePropertyOrientation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 362967772294C23700B9FC4A /* CGImagePropertyOrientation.swift */; }; - 382D0B0323C348A800A81619 /* CameraScannerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 382D0B0223C348A800A81619 /* CameraScannerViewController.swift */; }; - 383C440E23C5846B0070DE47 /* EditImageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 383C440D23C5846B0070DE47 /* EditImageViewController.swift */; }; - 383C441723C587B90070DE47 /* ReviewImageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 383C441623C587B90070DE47 /* ReviewImageViewController.swift */; }; - 388A3BF423C46DAE00263DD1 /* NewCameraViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 388A3BF323C46DAE00263DD1 /* NewCameraViewController.swift */; }; - 388A3BFD23C46FF000263DD1 /* EditImageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 388A3BFC23C46FF000263DD1 /* EditImageViewController.swift */; }; - 50FF76632577CCFB0099D918 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 50FF764B2577CCFB0099D918 /* Localizable.strings */; }; - 50FF77162577EAD80099D918 /* WeScan.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1D4BCED202C4F4100FCDDEC /* WeScan.framework */; }; - 74E27858215446C900361812 /* FBSnapshotTestCase.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 74E27850215446C200361812 /* FBSnapshotTestCase.framework */; }; - 74F7D034211ACBD90046AF7E /* CIRectangleDetectorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74F7D033211ACBD90046AF7E /* CIRectangleDetectorTests.swift */; }; - 74F7D036211ACBEE0046AF7E /* CaptureSessionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74F7D035211ACBEE0046AF7E /* CaptureSessionTests.swift */; }; - 74F7D038211ACBF90046AF7E /* VisionRectangleDetectorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74F7D037211ACBF90046AF7E /* VisionRectangleDetectorTests.swift */; }; - 74F7D03A211ACC4B0046AF7E /* UIImageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74F7D039211ACC4B0046AF7E /* UIImageTests.swift */; }; - 74F7D03C211ACC6B0046AF7E /* CGAffineTransformTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74F7D03B211ACC6B0046AF7E /* CGAffineTransformTests.swift */; }; - 74F7D03E211ACC890046AF7E /* AVCaptureVideoOrientationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74F7D03D211ACC890046AF7E /* AVCaptureVideoOrientationTests.swift */; }; - 8A1BB199249C9B45000278F2 /* UIImage+SFSymbol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8A1BB198249C9B45000278F2 /* UIImage+SFSymbol.swift */; }; - A11C5B9C2046A20C005075FE /* Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = A11C5B9B2046A20C005075FE /* Error.swift */; }; - A11C5CD920495EA1005075FE /* RectangleFeaturesFunnelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A11C5CD820495EA1005075FE /* RectangleFeaturesFunnelTests.swift */; }; - A11C5CDB20495EC9005075FE /* AVCaptureVideoOrientation+Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = A11C5CDA20495EC9005075FE /* AVCaptureVideoOrientation+Utils.swift */; }; - A14089BA204D92EA0009530F /* EditScanCornerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A14089B9204D92EA0009530F /* EditScanCornerView.swift */; }; - A14089EE204EAA180009530F /* ArrayTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A14089ED204EAA180009530F /* ArrayTests.swift */; }; - A14089F0204EAC050009530F /* BigRectangle.jpg in Resources */ = {isa = PBXBuildFile; fileRef = A14089EF204EAC040009530F /* BigRectangle.jpg */; }; - A165F67E2044741B002D5ED6 /* ShutterButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = A165F67D2044741B002D5ED6 /* ShutterButton.swift */; }; - A1675BAB2041693A00852865 /* Square.jpg in Resources */ = {isa = PBXBuildFile; fileRef = A1675BAA2041693A00852865 /* Square.jpg */; }; - A1675BAF204178BD00852865 /* ImageFeatureTestHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1675BAE204178BD00852865 /* ImageFeatureTestHelpers.swift */; }; - A194E96F2042BD77003493E2 /* Rectangle.jpg in Resources */ = {isa = PBXBuildFile; fileRef = A194E96E2042BD77003493E2 /* Rectangle.jpg */; }; - A194E97220431DD7003493E2 /* ReviewViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A194E97120431DD7003493E2 /* ReviewViewController.swift */; }; - A1C4DD4E2044ADE600D36684 /* CGRect+Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1C4DD4D2044ADE600D36684 /* CGRect+Utils.swift */; }; - A1C4DD502044B1D500D36684 /* CGRectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1C4DD4F2044B1D500D36684 /* CGRectTests.swift */; }; - A1D4BCBB202C4F3800FCDDEC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1D4BCBA202C4F3800FCDDEC /* AppDelegate.swift */; }; - A1D4BCBD202C4F3800FCDDEC /* HomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1D4BCBC202C4F3800FCDDEC /* HomeViewController.swift */; }; - A1D4BCC0202C4F3800FCDDEC /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A1D4BCBE202C4F3800FCDDEC /* Main.storyboard */; }; - A1D4BCC2202C4F3800FCDDEC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A1D4BCC1202C4F3800FCDDEC /* Assets.xcassets */; }; - A1D4BCC5202C4F3800FCDDEC /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A1D4BCC3202C4F3800FCDDEC /* LaunchScreen.storyboard */; }; - A1D4BCF6202C4F4100FCDDEC /* WeScan.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A1D4BCED202C4F4100FCDDEC /* WeScan.framework */; }; - A1D4BCFF202C4F4100FCDDEC /* WeScan.h in Headers */ = {isa = PBXBuildFile; fileRef = A1D4BCEF202C4F4100FCDDEC /* WeScan.h */; settings = {ATTRIBUTES = (Public, ); }; }; - A1D4BD03202C4F4100FCDDEC /* WeScan.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = A1D4BCED202C4F4100FCDDEC /* WeScan.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - A1D4BD0C202C504F00FCDDEC /* ScannerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1D4BD0B202C504F00FCDDEC /* ScannerViewController.swift */; }; - A1D4BD0E202C57A400FCDDEC /* CaptureSessionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1D4BD0D202C57A400FCDDEC /* CaptureSessionManager.swift */; }; - A1D4BD15202C6CC000FCDDEC /* Array+Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1D4BD14202C6CC000FCDDEC /* Array+Utils.swift */; }; - A1DF90A02031D89D00841A11 /* ImageScannerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1DF909F2031D89D00841A11 /* ImageScannerController.swift */; }; - A1DF90A420331A0B00841A11 /* CIRectangleDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1DF90A320331A0B00841A11 /* CIRectangleDetector.swift */; }; - A1DF90C32034919400841A11 /* QuadrilateralTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1DF90C22034919400841A11 /* QuadrilateralTests.swift */; }; - A1DF90E420358CB100841A11 /* Transformable.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1DF90E320358CB000841A11 /* Transformable.swift */; }; - A1DF90F22035992A00841A11 /* CGAffineTransform+Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1DF90F12035992A00841A11 /* CGAffineTransform+Utils.swift */; }; - A1DF90F62037187500841A11 /* UIImage+Orientation.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1DF90F52037187500841A11 /* UIImage+Orientation.swift */; }; - A1DF90FD203B412600841A11 /* CGPointTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1DF90FC203B412600841A11 /* CGPointTests.swift */; }; - A1F22E99202C7B66001723AD /* QuadrilateralView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1F22E98202C7B66001723AD /* QuadrilateralView.swift */; }; - A1F22E9F202C8D70001723AD /* Quadrilateral.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1F22E9E202C8D70001723AD /* Quadrilateral.swift */; }; - A1F22EA3202DAA74001723AD /* RectangleFeaturesFunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1F22EA2202DAA74001723AD /* RectangleFeaturesFunnel.swift */; }; - A1F22EA5202DB3AA001723AD /* CGPoint+Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1F22EA4202DB3AA001723AD /* CGPoint+Utils.swift */; }; - A1F22ECE2031937E001723AD /* EditScanViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A1F22ECD2031937E001723AD /* EditScanViewController.swift */; }; - B9253F3F22190B7400C5DE7C /* CGSize+Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9253F3E22190B7400C5DE7C /* CGSize+Utils.swift */; }; - B9274D3A219B951000F9FCD1 /* CIImage+Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9274D39219B951000F9FCD1 /* CIImage+Utils.swift */; }; - B940E38A21A77192003B3C0B /* enhance@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = B940E38721A77192003B3C0B /* enhance@3x.png */; }; - B940E38B21A77192003B3C0B /* enhance@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = B940E38821A77192003B3C0B /* enhance@2x.png */; }; - B940E38C21A77192003B3C0B /* enhance.png in Resources */ = {isa = PBXBuildFile; fileRef = B940E38921A77192003B3C0B /* enhance.png */; }; - B940E3AD21A829EE003B3C0B /* CaptureSession+Orientation.swift in Sources */ = {isa = PBXBuildFile; fileRef = B940E3AC21A829ED003B3C0B /* CaptureSession+Orientation.swift */; }; - B940E3B821A95C44003B3C0B /* FocusRectangleViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B940E3B721A95C44003B3C0B /* FocusRectangleViewTests.swift */; }; - B940E3D821AE0B42003B3C0B /* CaptureDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = B940E3D721AE0B42003B3C0B /* CaptureDevice.swift */; }; - B940E3DA21AE2919003B3C0B /* CaptureSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = B940E3D921AE2919003B3C0B /* CaptureSession.swift */; }; - B940E3DC21AE2A64003B3C0B /* CaptureSession+Flash.swift in Sources */ = {isa = PBXBuildFile; fileRef = B940E3DB21AE2A64003B3C0B /* CaptureSession+Flash.swift */; }; - B940E3DE21AE2A79003B3C0B /* CaptureSession+Focus.swift in Sources */ = {isa = PBXBuildFile; fileRef = B940E3DD21AE2A79003B3C0B /* CaptureSession+Focus.swift */; }; - B94E76AC221AB7D100C1945D /* CGSizeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B94E76AB221AB7D100C1945D /* CGSizeTests.swift */; }; - B94FBBAD2178652B001ED1B4 /* flash.png in Resources */ = {isa = PBXBuildFile; fileRef = B94FBBAA2178652B001ED1B4 /* flash.png */; }; - B94FBBAE2178652B001ED1B4 /* flash@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = B94FBBAB2178652B001ED1B4 /* flash@3x.png */; }; - B94FBBAF2178652B001ED1B4 /* flash@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = B94FBBAC2178652B001ED1B4 /* flash@2x.png */; }; - B94FBBB32178653C001ED1B4 /* flashUnavailable@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = B94FBBB02178653C001ED1B4 /* flashUnavailable@3x.png */; }; - B94FBBB42178653C001ED1B4 /* flashUnavailable.png in Resources */ = {isa = PBXBuildFile; fileRef = B94FBBB12178653C001ED1B4 /* flashUnavailable.png */; }; - B94FBBB52178653C001ED1B4 /* flashUnavailable@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = B94FBBB22178653C001ED1B4 /* flashUnavailable@2x.png */; }; - B9611EC721E36B9F000AB105 /* ReviewViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9611EC621E36B9F000AB105 /* ReviewViewControllerTests.swift */; }; - B992E831210C36A400C33A21 /* VisionRectangleDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = B992E830210C36A400C33A21 /* VisionRectangleDetector.swift */; }; - B9AAE88B219E6C0400205620 /* FocusRectangleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B9AAE88A219E6C0400205620 /* FocusRectangleView.swift */; }; - B9E49DBE21BFD3BB000E994D /* rotate.png in Resources */ = {isa = PBXBuildFile; fileRef = B9E49DBB21BFD3BA000E994D /* rotate.png */; }; - B9E49DBF21BFD3BB000E994D /* rotate@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = B9E49DBC21BFD3BB000E994D /* rotate@2x.png */; }; - B9E49DC021BFD3BB000E994D /* rotate@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = B9E49DBD21BFD3BB000E994D /* rotate@3x.png */; }; - C31DD8A320C087D80072D439 /* ZoomGestureController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C31DD8A220C087D80072D439 /* ZoomGestureController.swift */; }; - C3789C9B20CC69AD001B423F /* QuadrilateralViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3789C9A20CC69AD001B423F /* QuadrilateralViewTests.swift */; }; - C3E2EB8E20B8970800A42E58 /* UIImage+Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = C3E2EB8D20B8970800A42E58 /* UIImage+Utils.swift */; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 74E2784F215446C200361812 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 74E27848215446C200361812 /* FBSnapshotTestCase.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = B31987F01AB782D000B0A900; - remoteInfo = "FBSnapshotTestCase iOS"; - }; - 74E27851215446C200361812 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 74E27848215446C200361812 /* FBSnapshotTestCase.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = B31987FB1AB782D100B0A900; - remoteInfo = "FBSnapshotTestCase iOS Tests"; - }; - 74E27853215446C200361812 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 74E27848215446C200361812 /* FBSnapshotTestCase.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 8271377A1C63AB6F00354E42; - remoteInfo = "FBSnapshotTestCase tvOS"; - }; - 74E27855215446C200361812 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 74E27848215446C200361812 /* FBSnapshotTestCase.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 827137831C63AB7000354E42; - remoteInfo = "FBSnapshotTestCase tvOS Tests"; - }; - 74E27859215446D100361812 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 74E27848215446C200361812 /* FBSnapshotTestCase.xcodeproj */; - proxyType = 1; - remoteGlobalIDString = B31987EF1AB782D000B0A900; - remoteInfo = "FBSnapshotTestCase iOS"; - }; - 74E278622154528300361812 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 74E2785B2154528300361812 /* FBSnapshotTestCase.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = B31987F01AB782D000B0A900; - remoteInfo = "FBSnapshotTestCase iOS"; - }; - 74E278642154528300361812 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 74E2785B2154528300361812 /* FBSnapshotTestCase.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = B31987FB1AB782D100B0A900; - remoteInfo = "FBSnapshotTestCase iOS Tests"; - }; - 74E278662154528300361812 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 74E2785B2154528300361812 /* FBSnapshotTestCase.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 8271377A1C63AB6F00354E42; - remoteInfo = "FBSnapshotTestCase tvOS"; - }; - 74E278682154528300361812 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 74E2785B2154528300361812 /* FBSnapshotTestCase.xcodeproj */; - proxyType = 2; - remoteGlobalIDString = 827137831C63AB7000354E42; - remoteInfo = "FBSnapshotTestCase tvOS Tests"; - }; - A1D4BCF7202C4F4100FCDDEC /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = A1D4BCAF202C4F3800FCDDEC /* Project object */; - proxyType = 1; - remoteGlobalIDString = A1D4BCEC202C4F4100FCDDEC; - remoteInfo = Scanner; - }; - A1D4BCF9202C4F4100FCDDEC /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = A1D4BCAF202C4F3800FCDDEC /* Project object */; - proxyType = 1; - remoteGlobalIDString = A1D4BCB6202C4F3800FCDDEC; - remoteInfo = ScannerSampleProject; - }; - A1D4BD00202C4F4100FCDDEC /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = A1D4BCAF202C4F3800FCDDEC /* Project object */; - proxyType = 1; - remoteGlobalIDString = A1D4BCEC202C4F4100FCDDEC; - remoteInfo = Scanner; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - A1D4BD07202C4F4100FCDDEC /* Embed Frameworks */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 10; - files = ( - A1D4BD03202C4F4100FCDDEC /* WeScan.framework in Embed Frameworks */, - ); - name = "Embed Frameworks"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 362967772294C23700B9FC4A /* CGImagePropertyOrientation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CGImagePropertyOrientation.swift; sourceTree = ""; }; - 382D0B0223C348A800A81619 /* CameraScannerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraScannerViewController.swift; sourceTree = ""; }; - 383C440D23C5846B0070DE47 /* EditImageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditImageViewController.swift; sourceTree = ""; }; - 383C441623C587B90070DE47 /* ReviewImageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReviewImageViewController.swift; sourceTree = ""; }; - 388A3BF323C46DAE00263DD1 /* NewCameraViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewCameraViewController.swift; sourceTree = ""; }; - 388A3BFC23C46FF000263DD1 /* EditImageViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditImageViewController.swift; sourceTree = ""; }; - 3C3B86AC2680907400BE449B /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/Localizable.strings; sourceTree = ""; }; - 50FF764C2577CCFB0099D918 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = ""; }; - 50FF764D2577CCFB0099D918 /* ar */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ar; path = ar.lproj/Localizable.strings; sourceTree = ""; }; - 50FF764E2577CCFB0099D918 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = ""; }; - 50FF764F2577CCFB0099D918 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; - 50FF76502577CCFB0099D918 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = ""; }; - 50FF76512577CCFB0099D918 /* es-419 */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-419"; path = "es-419.lproj/Localizable.strings"; sourceTree = ""; }; - 50FF76522577CCFB0099D918 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = it.lproj/Localizable.strings; sourceTree = ""; }; - 50FF76532577CCFB0099D918 /* cs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = cs; path = cs.lproj/Localizable.strings; sourceTree = ""; }; - 50FF76542577CCFB0099D918 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "zh-Hant.lproj/Localizable.strings"; sourceTree = ""; }; - 50FF76552577CCFB0099D918 /* hu */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = hu; path = hu.lproj/Localizable.strings; sourceTree = ""; }; - 50FF76562577CCFB0099D918 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Localizable.strings; sourceTree = ""; }; - 50FF76572577CCFB0099D918 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/Localizable.strings; sourceTree = ""; }; - 50FF76582577CCFB0099D918 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Localizable.strings"; sourceTree = ""; }; - 50FF76592577CCFB0099D918 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = ""; }; - 50FF765A2577CCFB0099D918 /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/Localizable.strings; sourceTree = ""; }; - 50FF765B2577CCFB0099D918 /* pt-PT */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-PT"; path = "pt-PT.lproj/Localizable.strings"; sourceTree = ""; }; - 5A5A8B1525804C7D0058A37C /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = ""; }; - 74E27848215446C200361812 /* FBSnapshotTestCase.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = FBSnapshotTestCase.xcodeproj; path = "Submodules/ios-snapshot-test-case/FBSnapshotTestCase.xcodeproj"; sourceTree = ""; }; - 74E2785B2154528300361812 /* FBSnapshotTestCase.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = FBSnapshotTestCase.xcodeproj; path = "Submodules/ios-snapshot-test-case/FBSnapshotTestCase.xcodeproj"; sourceTree = SOURCE_ROOT; }; - 74F7D033211ACBD90046AF7E /* CIRectangleDetectorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIRectangleDetectorTests.swift; sourceTree = ""; }; - 74F7D035211ACBEE0046AF7E /* CaptureSessionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CaptureSessionTests.swift; sourceTree = ""; }; - 74F7D037211ACBF90046AF7E /* VisionRectangleDetectorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VisionRectangleDetectorTests.swift; sourceTree = ""; }; - 74F7D039211ACC4B0046AF7E /* UIImageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIImageTests.swift; sourceTree = ""; }; - 74F7D03B211ACC6B0046AF7E /* CGAffineTransformTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CGAffineTransformTests.swift; sourceTree = ""; }; - 74F7D03D211ACC890046AF7E /* AVCaptureVideoOrientationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AVCaptureVideoOrientationTests.swift; sourceTree = ""; }; - 8A1BB198249C9B45000278F2 /* UIImage+SFSymbol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+SFSymbol.swift"; sourceTree = ""; }; - A11C5B9B2046A20C005075FE /* Error.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Error.swift; sourceTree = ""; }; - A11C5CD820495EA1005075FE /* RectangleFeaturesFunnelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RectangleFeaturesFunnelTests.swift; sourceTree = ""; }; - A11C5CDA20495EC9005075FE /* AVCaptureVideoOrientation+Utils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "AVCaptureVideoOrientation+Utils.swift"; sourceTree = ""; }; - A14089B9204D92EA0009530F /* EditScanCornerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditScanCornerView.swift; sourceTree = ""; }; - A14089ED204EAA180009530F /* ArrayTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArrayTests.swift; sourceTree = ""; }; - A14089EF204EAC040009530F /* BigRectangle.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = BigRectangle.jpg; sourceTree = ""; }; - A165F67D2044741B002D5ED6 /* ShutterButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShutterButton.swift; sourceTree = ""; }; - A1675BAA2041693A00852865 /* Square.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = Square.jpg; sourceTree = ""; }; - A1675BAE204178BD00852865 /* ImageFeatureTestHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageFeatureTestHelpers.swift; sourceTree = ""; }; - A194E96E2042BD77003493E2 /* Rectangle.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = Rectangle.jpg; sourceTree = ""; }; - A194E97120431DD7003493E2 /* ReviewViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReviewViewController.swift; sourceTree = ""; }; - A1C4DD4D2044ADE600D36684 /* CGRect+Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CGRect+Utils.swift"; sourceTree = ""; }; - A1C4DD4F2044B1D500D36684 /* CGRectTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CGRectTests.swift; sourceTree = ""; }; - A1D4BCB7202C4F3800FCDDEC /* WeScanSampleProject.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WeScanSampleProject.app; sourceTree = BUILT_PRODUCTS_DIR; }; - A1D4BCBA202C4F3800FCDDEC /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; - A1D4BCBC202C4F3800FCDDEC /* HomeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewController.swift; sourceTree = ""; }; - A1D4BCC1202C4F3800FCDDEC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; - A1D4BCC6202C4F3800FCDDEC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - A1D4BCED202C4F4100FCDDEC /* WeScan.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = WeScan.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - A1D4BCEF202C4F4100FCDDEC /* WeScan.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WeScan.h; sourceTree = ""; }; - A1D4BCF0202C4F4100FCDDEC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - A1D4BCF5202C4F4100FCDDEC /* WeScanTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = WeScanTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - A1D4BCFE202C4F4100FCDDEC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - A1D4BD0B202C504F00FCDDEC /* ScannerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScannerViewController.swift; sourceTree = ""; }; - A1D4BD0D202C57A400FCDDEC /* CaptureSessionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CaptureSessionManager.swift; sourceTree = ""; }; - A1D4BD14202C6CC000FCDDEC /* Array+Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Utils.swift"; sourceTree = ""; }; - A1DF909F2031D89D00841A11 /* ImageScannerController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageScannerController.swift; sourceTree = ""; }; - A1DF90A320331A0B00841A11 /* CIRectangleDetector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIRectangleDetector.swift; sourceTree = ""; }; - A1DF90C12034919300841A11 /* WeScanTests-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "WeScanTests-Bridging-Header.h"; sourceTree = ""; }; - A1DF90C22034919400841A11 /* QuadrilateralTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuadrilateralTests.swift; sourceTree = ""; }; - A1DF90E320358CB000841A11 /* Transformable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Transformable.swift; sourceTree = ""; }; - A1DF90F12035992A00841A11 /* CGAffineTransform+Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CGAffineTransform+Utils.swift"; sourceTree = ""; }; - A1DF90F52037187500841A11 /* UIImage+Orientation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+Orientation.swift"; sourceTree = ""; }; - A1DF90FC203B412600841A11 /* CGPointTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CGPointTests.swift; sourceTree = ""; }; - A1F22E98202C7B66001723AD /* QuadrilateralView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuadrilateralView.swift; sourceTree = ""; }; - A1F22E9E202C8D70001723AD /* Quadrilateral.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Quadrilateral.swift; sourceTree = ""; }; - A1F22EA2202DAA74001723AD /* RectangleFeaturesFunnel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RectangleFeaturesFunnel.swift; sourceTree = ""; }; - A1F22EA4202DB3AA001723AD /* CGPoint+Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CGPoint+Utils.swift"; sourceTree = ""; }; - A1F22ECD2031937E001723AD /* EditScanViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditScanViewController.swift; sourceTree = ""; }; - B9253F3E22190B7400C5DE7C /* CGSize+Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CGSize+Utils.swift"; sourceTree = ""; }; - B9274D39219B951000F9FCD1 /* CIImage+Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CIImage+Utils.swift"; sourceTree = ""; }; - B940E38721A77192003B3C0B /* enhance@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "enhance@3x.png"; sourceTree = ""; }; - B940E38821A77192003B3C0B /* enhance@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "enhance@2x.png"; sourceTree = ""; }; - B940E38921A77192003B3C0B /* enhance.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = enhance.png; sourceTree = ""; }; - B940E3AC21A829ED003B3C0B /* CaptureSession+Orientation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CaptureSession+Orientation.swift"; sourceTree = ""; }; - B940E3B721A95C44003B3C0B /* FocusRectangleViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FocusRectangleViewTests.swift; sourceTree = ""; }; - B940E3D721AE0B42003B3C0B /* CaptureDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CaptureDevice.swift; sourceTree = ""; }; - B940E3D921AE2919003B3C0B /* CaptureSession.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CaptureSession.swift; sourceTree = ""; }; - B940E3DB21AE2A64003B3C0B /* CaptureSession+Flash.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CaptureSession+Flash.swift"; sourceTree = ""; }; - B940E3DD21AE2A79003B3C0B /* CaptureSession+Focus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CaptureSession+Focus.swift"; sourceTree = ""; }; - B94E76AB221AB7D100C1945D /* CGSizeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CGSizeTests.swift; sourceTree = ""; }; - B94FBBAA2178652B001ED1B4 /* flash.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = flash.png; sourceTree = ""; }; - B94FBBAB2178652B001ED1B4 /* flash@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "flash@3x.png"; sourceTree = ""; }; - B94FBBAC2178652B001ED1B4 /* flash@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "flash@2x.png"; sourceTree = ""; }; - B94FBBB02178653C001ED1B4 /* flashUnavailable@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "flashUnavailable@3x.png"; sourceTree = ""; }; - B94FBBB12178653C001ED1B4 /* flashUnavailable.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = flashUnavailable.png; sourceTree = ""; }; - B94FBBB22178653C001ED1B4 /* flashUnavailable@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "flashUnavailable@2x.png"; sourceTree = ""; }; - B9611EC621E36B9F000AB105 /* ReviewViewControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReviewViewControllerTests.swift; sourceTree = ""; }; - B97B49D4216C6FF600B2235B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; - B97B49D5216C6FF600B2235B /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; - B992E830210C36A400C33A21 /* VisionRectangleDetector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VisionRectangleDetector.swift; sourceTree = ""; }; - B9AAE88A219E6C0400205620 /* FocusRectangleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FocusRectangleView.swift; sourceTree = ""; }; - B9E49DBB21BFD3BA000E994D /* rotate.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = rotate.png; sourceTree = ""; }; - B9E49DBC21BFD3BB000E994D /* rotate@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "rotate@2x.png"; sourceTree = ""; }; - B9E49DBD21BFD3BB000E994D /* rotate@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "rotate@3x.png"; sourceTree = ""; }; - BF16DBD5258D2E99008A979B /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/Localizable.strings; sourceTree = ""; }; - C31DD8A220C087D80072D439 /* ZoomGestureController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZoomGestureController.swift; sourceTree = ""; }; - C3789C9A20CC69AD001B423F /* QuadrilateralViewTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuadrilateralViewTests.swift; sourceTree = ""; }; - C3E2EB8D20B8970800A42E58 /* UIImage+Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+Utils.swift"; sourceTree = ""; }; -/* End PBXFileReference section */ - -/* Begin PBXFrameworksBuildPhase section */ - A1D4BCB4202C4F3800FCDDEC /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 50FF77162577EAD80099D918 /* WeScan.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - A1D4BCE9202C4F4100FCDDEC /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - A1D4BCF2202C4F4100FCDDEC /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 74E27858215446C900361812 /* FBSnapshotTestCase.framework in Frameworks */, - A1D4BCF6202C4F4100FCDDEC /* WeScan.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 50FF76492577CCFB0099D918 /* Resources */ = { - isa = PBXGroup; - children = ( - B94FBB9D2178647B001ED1B4 /* Assets */, - 50FF764A2577CCFB0099D918 /* Localisation */, - ); - path = Resources; - sourceTree = ""; - }; - 50FF764A2577CCFB0099D918 /* Localisation */ = { - isa = PBXGroup; - children = ( - 50FF764B2577CCFB0099D918 /* Localizable.strings */, - ); - path = Localisation; - sourceTree = ""; - }; - 74E27849215446C200361812 /* Products */ = { - isa = PBXGroup; - children = ( - 74E27850215446C200361812 /* FBSnapshotTestCase.framework */, - 74E27852215446C200361812 /* FBSnapshotTestCase iOS Tests.xctest */, - 74E27854215446C200361812 /* FBSnapshotTestCase.framework */, - 74E27856215446C200361812 /* FBSnapshotTestCase tvOS Tests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - 74E27857215446C900361812 /* Frameworks */ = { - isa = PBXGroup; - children = ( - ); - name = Frameworks; - sourceTree = ""; - }; - 74E2785C2154528300361812 /* Products */ = { - isa = PBXGroup; - children = ( - 74E278632154528300361812 /* FBSnapshotTestCase.framework */, - 74E278652154528300361812 /* FBSnapshotTestCase iOS Tests.xctest */, - 74E278672154528300361812 /* FBSnapshotTestCase.framework */, - 74E278692154528300361812 /* FBSnapshotTestCase tvOS Tests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - A1675BA92041692800852865 /* Resources */ = { - isa = PBXGroup; - children = ( - A14089EF204EAC040009530F /* BigRectangle.jpg */, - A1675BAA2041693A00852865 /* Square.jpg */, - A194E96E2042BD77003493E2 /* Rectangle.jpg */, - ); - path = Resources; - sourceTree = ""; - }; - A194E97020431DB1003493E2 /* Review */ = { - isa = PBXGroup; - children = ( - A194E97120431DD7003493E2 /* ReviewViewController.swift */, - ); - path = Review; - sourceTree = ""; - }; - A1D4BCAE202C4F3800FCDDEC = { - isa = PBXGroup; - children = ( - 74E27848215446C200361812 /* FBSnapshotTestCase.xcodeproj */, - A1D4BCB9202C4F3800FCDDEC /* WeScanSampleProject */, - A1D4BCEE202C4F4100FCDDEC /* WeScan */, - A1D4BCFB202C4F4100FCDDEC /* WeScanTests */, - A1D4BCB8202C4F3800FCDDEC /* Products */, - 74E27857215446C900361812 /* Frameworks */, - ); - sourceTree = ""; - }; - A1D4BCB8202C4F3800FCDDEC /* Products */ = { - isa = PBXGroup; - children = ( - A1D4BCB7202C4F3800FCDDEC /* WeScanSampleProject.app */, - A1D4BCED202C4F4100FCDDEC /* WeScan.framework */, - A1D4BCF5202C4F4100FCDDEC /* WeScanTests.xctest */, - ); - name = Products; - sourceTree = ""; - }; - A1D4BCB9202C4F3800FCDDEC /* WeScanSampleProject */ = { - isa = PBXGroup; - children = ( - A1D4BCBA202C4F3800FCDDEC /* AppDelegate.swift */, - A1D4BCBC202C4F3800FCDDEC /* HomeViewController.swift */, - A1D4BCBE202C4F3800FCDDEC /* Main.storyboard */, - A1D4BCC1202C4F3800FCDDEC /* Assets.xcassets */, - A1D4BCC3202C4F3800FCDDEC /* LaunchScreen.storyboard */, - A1D4BCC6202C4F3800FCDDEC /* Info.plist */, - 388A3BF323C46DAE00263DD1 /* NewCameraViewController.swift */, - 383C440D23C5846B0070DE47 /* EditImageViewController.swift */, - 383C441623C587B90070DE47 /* ReviewImageViewController.swift */, - ); - path = WeScanSampleProject; - sourceTree = ""; - }; - A1D4BCEE202C4F4100FCDDEC /* WeScan */ = { - isa = PBXGroup; - children = ( - A1DF909F2031D89D00841A11 /* ImageScannerController.swift */, - A1DF90E220358C9E00841A11 /* Protocols */, - A1F22ECF203199E7001723AD /* Scan */, - A1F22ED020319A03001723AD /* Edit */, - A194E97020431DB1003493E2 /* Review */, - B940E3A021A82931003B3C0B /* Session */, - A1D4BD11202C6C7900FCDDEC /* Extensions */, - A1DF90F720371A0800841A11 /* Common */, - 50FF76492577CCFB0099D918 /* Resources */, - A1D4BCEF202C4F4100FCDDEC /* WeScan.h */, - A1D4BCF0202C4F4100FCDDEC /* Info.plist */, - ); - path = WeScan; - sourceTree = ""; - }; - A1D4BCFB202C4F4100FCDDEC /* WeScanTests */ = { - isa = PBXGroup; - children = ( - 74E2785B2154528300361812 /* FBSnapshotTestCase.xcodeproj */, - A1675BA92041692800852865 /* Resources */, - A1D4BCFE202C4F4100FCDDEC /* Info.plist */, - A14089ED204EAA180009530F /* ArrayTests.swift */, - 74F7D03D211ACC890046AF7E /* AVCaptureVideoOrientationTests.swift */, - 74F7D035211ACBEE0046AF7E /* CaptureSessionTests.swift */, - 74F7D033211ACBD90046AF7E /* CIRectangleDetectorTests.swift */, - 74F7D03B211ACC6B0046AF7E /* CGAffineTransformTests.swift */, - A1DF90FC203B412600841A11 /* CGPointTests.swift */, - A1C4DD4F2044B1D500D36684 /* CGRectTests.swift */, - B94E76AB221AB7D100C1945D /* CGSizeTests.swift */, - B940E3B721A95C44003B3C0B /* FocusRectangleViewTests.swift */, - A1675BAE204178BD00852865 /* ImageFeatureTestHelpers.swift */, - A1DF90C22034919400841A11 /* QuadrilateralTests.swift */, - C3789C9A20CC69AD001B423F /* QuadrilateralViewTests.swift */, - A11C5CD820495EA1005075FE /* RectangleFeaturesFunnelTests.swift */, - B9611EC621E36B9F000AB105 /* ReviewViewControllerTests.swift */, - 74F7D039211ACC4B0046AF7E /* UIImageTests.swift */, - 74F7D037211ACBF90046AF7E /* VisionRectangleDetectorTests.swift */, - A1DF90C12034919300841A11 /* WeScanTests-Bridging-Header.h */, - ); - path = WeScanTests; - sourceTree = ""; - }; - A1D4BD11202C6C7900FCDDEC /* Extensions */ = { - isa = PBXGroup; - children = ( - A1D4BD14202C6CC000FCDDEC /* Array+Utils.swift */, - A11C5CDA20495EC9005075FE /* AVCaptureVideoOrientation+Utils.swift */, - A1F22EA4202DB3AA001723AD /* CGPoint+Utils.swift */, - A1DF90F12035992A00841A11 /* CGAffineTransform+Utils.swift */, - A1C4DD4D2044ADE600D36684 /* CGRect+Utils.swift */, - B9253F3E22190B7400C5DE7C /* CGSize+Utils.swift */, - B9274D39219B951000F9FCD1 /* CIImage+Utils.swift */, - C3E2EB8D20B8970800A42E58 /* UIImage+Utils.swift */, - A1DF90F52037187500841A11 /* UIImage+Orientation.swift */, - 8A1BB198249C9B45000278F2 /* UIImage+SFSymbol.swift */, - 362967772294C23700B9FC4A /* CGImagePropertyOrientation.swift */, - ); - path = Extensions; - sourceTree = ""; - }; - A1DF90E220358C9E00841A11 /* Protocols */ = { - isa = PBXGroup; - children = ( - A1DF90E320358CB000841A11 /* Transformable.swift */, - B940E3D721AE0B42003B3C0B /* CaptureDevice.swift */, - ); - path = Protocols; - sourceTree = ""; - }; - A1DF90F720371A0800841A11 /* Common */ = { - isa = PBXGroup; - children = ( - A1DF90A320331A0B00841A11 /* CIRectangleDetector.swift */, - B992E830210C36A400C33A21 /* VisionRectangleDetector.swift */, - A1F22E9E202C8D70001723AD /* Quadrilateral.swift */, - A1F22E98202C7B66001723AD /* QuadrilateralView.swift */, - A11C5B9B2046A20C005075FE /* Error.swift */, - A14089B9204D92EA0009530F /* EditScanCornerView.swift */, - ); - path = Common; - sourceTree = ""; - }; - A1F22ECF203199E7001723AD /* Scan */ = { - isa = PBXGroup; - children = ( - 382D0B0223C348A800A81619 /* CameraScannerViewController.swift */, - A1D4BD0D202C57A400FCDDEC /* CaptureSessionManager.swift */, - B9AAE88A219E6C0400205620 /* FocusRectangleView.swift */, - A1F22EA2202DAA74001723AD /* RectangleFeaturesFunnel.swift */, - A1D4BD0B202C504F00FCDDEC /* ScannerViewController.swift */, - A165F67D2044741B002D5ED6 /* ShutterButton.swift */, - ); - path = Scan; - sourceTree = ""; - }; - A1F22ED020319A03001723AD /* Edit */ = { - isa = PBXGroup; - children = ( - 388A3BFC23C46FF000263DD1 /* EditImageViewController.swift */, - A1F22ECD2031937E001723AD /* EditScanViewController.swift */, - C31DD8A220C087D80072D439 /* ZoomGestureController.swift */, - ); - path = Edit; - sourceTree = ""; - }; - B9274D2D219AE10B00F9FCD1 /* Flash */ = { - isa = PBXGroup; - children = ( - B94FBBAA2178652B001ED1B4 /* flash.png */, - B94FBBAC2178652B001ED1B4 /* flash@2x.png */, - B94FBBAB2178652B001ED1B4 /* flash@3x.png */, - ); - name = Flash; - sourceTree = ""; - }; - B9274D30219AE16000F9FCD1 /* FlashUnavailable */ = { - isa = PBXGroup; - children = ( - B94FBBB12178653C001ED1B4 /* flashUnavailable.png */, - B94FBBB22178653C001ED1B4 /* flashUnavailable@2x.png */, - B94FBBB02178653C001ED1B4 /* flashUnavailable@3x.png */, - ); - name = FlashUnavailable; - sourceTree = ""; - }; - B940E3A021A82931003B3C0B /* Session */ = { - isa = PBXGroup; - children = ( - B940E3D921AE2919003B3C0B /* CaptureSession.swift */, - B940E3DB21AE2A64003B3C0B /* CaptureSession+Flash.swift */, - B940E3DD21AE2A79003B3C0B /* CaptureSession+Focus.swift */, - B940E3AC21A829ED003B3C0B /* CaptureSession+Orientation.swift */, - ); - path = Session; - sourceTree = ""; - }; - B94FBB9D2178647B001ED1B4 /* Assets */ = { - isa = PBXGroup; - children = ( - B9E49DB421BFCE7A000E994D /* Rotate */, - B983380121A6CA4E00AAE307 /* Enhance */, - B9274D2D219AE10B00F9FCD1 /* Flash */, - B9274D30219AE16000F9FCD1 /* FlashUnavailable */, - ); - path = Assets; - sourceTree = ""; - }; - B983380121A6CA4E00AAE307 /* Enhance */ = { - isa = PBXGroup; - children = ( - B940E38921A77192003B3C0B /* enhance.png */, - B940E38821A77192003B3C0B /* enhance@2x.png */, - B940E38721A77192003B3C0B /* enhance@3x.png */, - ); - name = Enhance; - sourceTree = ""; - }; - B9E49DB421BFCE7A000E994D /* Rotate */ = { - isa = PBXGroup; - children = ( - B9E49DBB21BFD3BA000E994D /* rotate.png */, - B9E49DBC21BFD3BB000E994D /* rotate@2x.png */, - B9E49DBD21BFD3BB000E994D /* rotate@3x.png */, - ); - name = Rotate; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXHeadersBuildPhase section */ - A1D4BCEA202C4F4100FCDDEC /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - A1D4BCFF202C4F4100FCDDEC /* WeScan.h in Headers */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXHeadersBuildPhase section */ - -/* Begin PBXNativeTarget section */ - A1D4BCB6202C4F3800FCDDEC /* WeScanSampleProject */ = { - isa = PBXNativeTarget; - buildConfigurationList = A1D4BCDF202C4F3800FCDDEC /* Build configuration list for PBXNativeTarget "WeScanSampleProject" */; - buildPhases = ( - A1D4BCB3202C4F3800FCDDEC /* Sources */, - A1D4BCB4202C4F3800FCDDEC /* Frameworks */, - A1D4BCB5202C4F3800FCDDEC /* Resources */, - 503A25E422FB044C00534850 /* SwiftLint */, - A1D4BD07202C4F4100FCDDEC /* Embed Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - A1D4BD01202C4F4100FCDDEC /* PBXTargetDependency */, - ); - name = WeScanSampleProject; - packageProductDependencies = ( - ); - productName = ScannerSampleProject; - productReference = A1D4BCB7202C4F3800FCDDEC /* WeScanSampleProject.app */; - productType = "com.apple.product-type.application"; - }; - A1D4BCEC202C4F4100FCDDEC /* WeScan */ = { - isa = PBXNativeTarget; - buildConfigurationList = A1D4BD04202C4F4100FCDDEC /* Build configuration list for PBXNativeTarget "WeScan" */; - buildPhases = ( - A1D4BCEA202C4F4100FCDDEC /* Headers */, - A1D4BCE8202C4F4100FCDDEC /* Sources */, - A1D4BCE9202C4F4100FCDDEC /* Frameworks */, - A1D4BCEB202C4F4100FCDDEC /* Resources */, - 503A25EC22FB045E00534850 /* SwiftLint */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = WeScan; - productName = Scanner; - productReference = A1D4BCED202C4F4100FCDDEC /* WeScan.framework */; - productType = "com.apple.product-type.framework"; - }; - A1D4BCF4202C4F4100FCDDEC /* WeScanTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = A1D4BD08202C4F4100FCDDEC /* Build configuration list for PBXNativeTarget "WeScanTests" */; - buildPhases = ( - A1D4BCF1202C4F4100FCDDEC /* Sources */, - A1D4BCF2202C4F4100FCDDEC /* Frameworks */, - A1D4BCF3202C4F4100FCDDEC /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 74E2785A215446D100361812 /* PBXTargetDependency */, - A1D4BCF8202C4F4100FCDDEC /* PBXTargetDependency */, - A1D4BCFA202C4F4100FCDDEC /* PBXTargetDependency */, - ); - name = WeScanTests; - productName = ScannerTests; - productReference = A1D4BCF5202C4F4100FCDDEC /* WeScanTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - A1D4BCAF202C4F3800FCDDEC /* Project object */ = { - isa = PBXProject; - attributes = { - LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1230; - ORGANIZATIONNAME = WeTransfer; - TargetAttributes = { - A1D4BCB6202C4F3800FCDDEC = { - CreatedOnToolsVersion = 9.2; - LastSwiftMigration = 1020; - ProvisioningStyle = Manual; - }; - A1D4BCEC202C4F4100FCDDEC = { - CreatedOnToolsVersion = 9.2; - LastSwiftMigration = 1020; - ProvisioningStyle = Manual; - }; - A1D4BCF4202C4F4100FCDDEC = { - CreatedOnToolsVersion = 9.2; - LastSwiftMigration = 1020; - ProvisioningStyle = Automatic; - TestTargetID = A1D4BCB6202C4F3800FCDDEC; - }; - }; - }; - buildConfigurationList = A1D4BCB2202C4F3800FCDDEC /* Build configuration list for PBXProject "WeScan" */; - compatibilityVersion = "Xcode 8.0"; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - fr, - "pt-PT", - it, - "zh-Hans", - "zh-Hant", - de, - "pt-BR", - hu, - pl, - es, - "es-419", - tr, - ru, - cs, - ar, - sv, - ja, - nl, - ko, - ); - mainGroup = A1D4BCAE202C4F3800FCDDEC; - productRefGroup = A1D4BCB8202C4F3800FCDDEC /* Products */; - projectDirPath = ""; - projectReferences = ( - { - ProductGroup = 74E2785C2154528300361812 /* Products */; - ProjectRef = 74E2785B2154528300361812 /* FBSnapshotTestCase.xcodeproj */; - }, - { - ProductGroup = 74E27849215446C200361812 /* Products */; - ProjectRef = 74E27848215446C200361812 /* FBSnapshotTestCase.xcodeproj */; - }, - ); - projectRoot = ""; - targets = ( - A1D4BCB6202C4F3800FCDDEC /* WeScanSampleProject */, - A1D4BCEC202C4F4100FCDDEC /* WeScan */, - A1D4BCF4202C4F4100FCDDEC /* WeScanTests */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXReferenceProxy section */ - 74E27850215446C200361812 /* FBSnapshotTestCase.framework */ = { - isa = PBXReferenceProxy; - fileType = wrapper.framework; - path = FBSnapshotTestCase.framework; - remoteRef = 74E2784F215446C200361812 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 74E27852215446C200361812 /* FBSnapshotTestCase iOS Tests.xctest */ = { - isa = PBXReferenceProxy; - fileType = wrapper.cfbundle; - path = "FBSnapshotTestCase iOS Tests.xctest"; - remoteRef = 74E27851215446C200361812 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 74E27854215446C200361812 /* FBSnapshotTestCase.framework */ = { - isa = PBXReferenceProxy; - fileType = wrapper.framework; - path = FBSnapshotTestCase.framework; - remoteRef = 74E27853215446C200361812 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 74E27856215446C200361812 /* FBSnapshotTestCase tvOS Tests.xctest */ = { - isa = PBXReferenceProxy; - fileType = wrapper.cfbundle; - path = "FBSnapshotTestCase tvOS Tests.xctest"; - remoteRef = 74E27855215446C200361812 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 74E278632154528300361812 /* FBSnapshotTestCase.framework */ = { - isa = PBXReferenceProxy; - fileType = wrapper.framework; - path = FBSnapshotTestCase.framework; - remoteRef = 74E278622154528300361812 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 74E278652154528300361812 /* FBSnapshotTestCase iOS Tests.xctest */ = { - isa = PBXReferenceProxy; - fileType = wrapper.cfbundle; - path = "FBSnapshotTestCase iOS Tests.xctest"; - remoteRef = 74E278642154528300361812 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 74E278672154528300361812 /* FBSnapshotTestCase.framework */ = { - isa = PBXReferenceProxy; - fileType = wrapper.framework; - path = FBSnapshotTestCase.framework; - remoteRef = 74E278662154528300361812 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; - 74E278692154528300361812 /* FBSnapshotTestCase tvOS Tests.xctest */ = { - isa = PBXReferenceProxy; - fileType = wrapper.cfbundle; - path = "FBSnapshotTestCase tvOS Tests.xctest"; - remoteRef = 74E278682154528300361812 /* PBXContainerItemProxy */; - sourceTree = BUILT_PRODUCTS_DIR; - }; -/* End PBXReferenceProxy section */ - -/* Begin PBXResourcesBuildPhase section */ - A1D4BCB5202C4F3800FCDDEC /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - A1D4BCC5202C4F3800FCDDEC /* LaunchScreen.storyboard in Resources */, - A1D4BCC2202C4F3800FCDDEC /* Assets.xcassets in Resources */, - A1D4BCC0202C4F3800FCDDEC /* Main.storyboard in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - A1D4BCEB202C4F4100FCDDEC /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - B9E49DC021BFD3BB000E994D /* rotate@3x.png in Resources */, - B9E49DBF21BFD3BB000E994D /* rotate@2x.png in Resources */, - 50FF76632577CCFB0099D918 /* Localizable.strings in Resources */, - B94FBBB52178653C001ED1B4 /* flashUnavailable@2x.png in Resources */, - B94FBBAD2178652B001ED1B4 /* flash.png in Resources */, - B94FBBAF2178652B001ED1B4 /* flash@2x.png in Resources */, - B940E38C21A77192003B3C0B /* enhance.png in Resources */, - B94FBBB32178653C001ED1B4 /* flashUnavailable@3x.png in Resources */, - B9E49DBE21BFD3BB000E994D /* rotate.png in Resources */, - B94FBBAE2178652B001ED1B4 /* flash@3x.png in Resources */, - B940E38A21A77192003B3C0B /* enhance@3x.png in Resources */, - B940E38B21A77192003B3C0B /* enhance@2x.png in Resources */, - B94FBBB42178653C001ED1B4 /* flashUnavailable.png in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - A1D4BCF3202C4F4100FCDDEC /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - A1675BAB2041693A00852865 /* Square.jpg in Resources */, - A194E96F2042BD77003493E2 /* Rectangle.jpg in Resources */, - A14089F0204EAC050009530F /* BigRectangle.jpg in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXShellScriptBuildPhase section */ - 503A25E422FB044C00534850 /* SwiftLint */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - name = SwiftLint; - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "if [ -z \"$CI\" ]; then\n if [ \"${CONFIGURATION}\" == \"Debug\" ]; then\n if test -d \"/opt/homebrew/bin/\"; then\n PATH=\"/opt/homebrew/bin/:${PATH}\"\n fi\n\n export PATH\n swiftlint --config \"${SRCROOT}/../Submodules/WeTransfer-iOS-CI/BuildTools/.swiftlint.yml\"\n else\n echo \"Info: As we're not building for Debug, no SwiftLint is running.\"\n fi\nfi\n"; - }; - 503A25EC22FB045E00534850 /* SwiftLint */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - ); - name = SwiftLint; - outputFileListPaths = ( - ); - outputPaths = ( - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "if [ -z \"$CI\" ]; then\n if [ \"${CONFIGURATION}\" == \"Debug\" ]; then\n if test -d \"/opt/homebrew/bin/\"; then\n PATH=\"/opt/homebrew/bin/:${PATH}\"\n fi\n\n export PATH\n swiftlint --config \"${SRCROOT}/../Submodules/WeTransfer-iOS-CI/BuildTools/.swiftlint.yml\"\n else\n echo \"Info: As we're not building for Debug, no SwiftLint is running.\"\n fi\nfi\n"; - }; -/* End PBXShellScriptBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - A1D4BCB3202C4F3800FCDDEC /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 383C440E23C5846B0070DE47 /* EditImageViewController.swift in Sources */, - 383C441723C587B90070DE47 /* ReviewImageViewController.swift in Sources */, - A1D4BCBD202C4F3800FCDDEC /* HomeViewController.swift in Sources */, - 388A3BF423C46DAE00263DD1 /* NewCameraViewController.swift in Sources */, - A1D4BCBB202C4F3800FCDDEC /* AppDelegate.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - A1D4BCE8202C4F4100FCDDEC /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - A11C5CDB20495EC9005075FE /* AVCaptureVideoOrientation+Utils.swift in Sources */, - A1DF90F22035992A00841A11 /* CGAffineTransform+Utils.swift in Sources */, - A1F22EA3202DAA74001723AD /* RectangleFeaturesFunnel.swift in Sources */, - A1D4BD0E202C57A400FCDDEC /* CaptureSessionManager.swift in Sources */, - B9AAE88B219E6C0400205620 /* FocusRectangleView.swift in Sources */, - A1F22ECE2031937E001723AD /* EditScanViewController.swift in Sources */, - A1F22E9F202C8D70001723AD /* Quadrilateral.swift in Sources */, - A1DF90E420358CB100841A11 /* Transformable.swift in Sources */, - 388A3BFD23C46FF000263DD1 /* EditImageViewController.swift in Sources */, - B9253F3F22190B7400C5DE7C /* CGSize+Utils.swift in Sources */, - A1D4BD0C202C504F00FCDDEC /* ScannerViewController.swift in Sources */, - 382D0B0323C348A800A81619 /* CameraScannerViewController.swift in Sources */, - A11C5B9C2046A20C005075FE /* Error.swift in Sources */, - B940E3DE21AE2A79003B3C0B /* CaptureSession+Focus.swift in Sources */, - 8A1BB199249C9B45000278F2 /* UIImage+SFSymbol.swift in Sources */, - B940E3DC21AE2A64003B3C0B /* CaptureSession+Flash.swift in Sources */, - A1D4BD15202C6CC000FCDDEC /* Array+Utils.swift in Sources */, - C3E2EB8E20B8970800A42E58 /* UIImage+Utils.swift in Sources */, - A165F67E2044741B002D5ED6 /* ShutterButton.swift in Sources */, - B940E3AD21A829EE003B3C0B /* CaptureSession+Orientation.swift in Sources */, - 362967782294C23700B9FC4A /* CGImagePropertyOrientation.swift in Sources */, - B992E831210C36A400C33A21 /* VisionRectangleDetector.swift in Sources */, - B940E3D821AE0B42003B3C0B /* CaptureDevice.swift in Sources */, - A1F22EA5202DB3AA001723AD /* CGPoint+Utils.swift in Sources */, - A14089BA204D92EA0009530F /* EditScanCornerView.swift in Sources */, - A1DF90A02031D89D00841A11 /* ImageScannerController.swift in Sources */, - B9274D3A219B951000F9FCD1 /* CIImage+Utils.swift in Sources */, - C31DD8A320C087D80072D439 /* ZoomGestureController.swift in Sources */, - A1C4DD4E2044ADE600D36684 /* CGRect+Utils.swift in Sources */, - A1DF90A420331A0B00841A11 /* CIRectangleDetector.swift in Sources */, - A1DF90F62037187500841A11 /* UIImage+Orientation.swift in Sources */, - A1F22E99202C7B66001723AD /* QuadrilateralView.swift in Sources */, - A194E97220431DD7003493E2 /* ReviewViewController.swift in Sources */, - B940E3DA21AE2919003B3C0B /* CaptureSession.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; - A1D4BCF1202C4F4100FCDDEC /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - C3789C9B20CC69AD001B423F /* QuadrilateralViewTests.swift in Sources */, - 74F7D038211ACBF90046AF7E /* VisionRectangleDetectorTests.swift in Sources */, - A1DF90FD203B412600841A11 /* CGPointTests.swift in Sources */, - 74F7D03E211ACC890046AF7E /* AVCaptureVideoOrientationTests.swift in Sources */, - B94E76AC221AB7D100C1945D /* CGSizeTests.swift in Sources */, - A1C4DD502044B1D500D36684 /* CGRectTests.swift in Sources */, - 74F7D03A211ACC4B0046AF7E /* UIImageTests.swift in Sources */, - B9611EC721E36B9F000AB105 /* ReviewViewControllerTests.swift in Sources */, - 74F7D03C211ACC6B0046AF7E /* CGAffineTransformTests.swift in Sources */, - 74F7D036211ACBEE0046AF7E /* CaptureSessionTests.swift in Sources */, - A11C5CD920495EA1005075FE /* RectangleFeaturesFunnelTests.swift in Sources */, - A14089EE204EAA180009530F /* ArrayTests.swift in Sources */, - A1DF90C32034919400841A11 /* QuadrilateralTests.swift in Sources */, - A1675BAF204178BD00852865 /* ImageFeatureTestHelpers.swift in Sources */, - 74F7D034211ACBD90046AF7E /* CIRectangleDetectorTests.swift in Sources */, - B940E3B821A95C44003B3C0B /* FocusRectangleViewTests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 74E2785A215446D100361812 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - name = "FBSnapshotTestCase iOS"; - targetProxy = 74E27859215446D100361812 /* PBXContainerItemProxy */; - }; - A1D4BCF8202C4F4100FCDDEC /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = A1D4BCEC202C4F4100FCDDEC /* WeScan */; - targetProxy = A1D4BCF7202C4F4100FCDDEC /* PBXContainerItemProxy */; - }; - A1D4BCFA202C4F4100FCDDEC /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = A1D4BCB6202C4F3800FCDDEC /* WeScanSampleProject */; - targetProxy = A1D4BCF9202C4F4100FCDDEC /* PBXContainerItemProxy */; - }; - A1D4BD01202C4F4100FCDDEC /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = A1D4BCEC202C4F4100FCDDEC /* WeScan */; - targetProxy = A1D4BD00202C4F4100FCDDEC /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin PBXVariantGroup section */ - 50FF764B2577CCFB0099D918 /* Localizable.strings */ = { - isa = PBXVariantGroup; - children = ( - 50FF764C2577CCFB0099D918 /* de */, - 50FF764D2577CCFB0099D918 /* ar */, - 50FF764E2577CCFB0099D918 /* zh-Hans */, - 50FF764F2577CCFB0099D918 /* en */, - 50FF76502577CCFB0099D918 /* es */, - 50FF76512577CCFB0099D918 /* es-419 */, - 50FF76522577CCFB0099D918 /* it */, - 50FF76532577CCFB0099D918 /* cs */, - 50FF76542577CCFB0099D918 /* zh-Hant */, - 50FF76552577CCFB0099D918 /* hu */, - 50FF76562577CCFB0099D918 /* tr */, - 50FF76572577CCFB0099D918 /* pl */, - 50FF76582577CCFB0099D918 /* pt-BR */, - 50FF76592577CCFB0099D918 /* ru */, - 50FF765A2577CCFB0099D918 /* fr */, - 50FF765B2577CCFB0099D918 /* pt-PT */, - 5A5A8B1525804C7D0058A37C /* sv */, - BF16DBD5258D2E99008A979B /* nl */, - 3C3B86AC2680907400BE449B /* ko */, - ); - name = Localizable.strings; - sourceTree = ""; - }; - A1D4BCBE202C4F3800FCDDEC /* Main.storyboard */ = { - isa = PBXVariantGroup; - children = ( - B97B49D4216C6FF600B2235B /* Base */, - ); - name = Main.storyboard; - sourceTree = ""; - }; - A1D4BCC3202C4F3800FCDDEC /* LaunchScreen.storyboard */ = { - isa = PBXVariantGroup; - children = ( - B97B49D5216C6FF600B2235B /* Base */, - ); - name = LaunchScreen.storyboard; - sourceTree = ""; - }; -/* End PBXVariantGroup section */ - -/* Begin XCBuildConfiguration section */ - A1D4BCDD202C4F3800FCDDEC /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = 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_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = 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_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = dwarf; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_OPTIMIZATION_LEVEL = 0; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; - MTL_ENABLE_DEBUG_INFO = YES; - ONLY_ACTIVE_ARCH = YES; - SDKROOT = iphoneos; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - }; - name = Debug; - }; - A1D4BCDE202C4F3800FCDDEC /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_ARC = 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_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = 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_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_IDENTITY = "iPhone Developer"; - COPY_PHASE_STRIP = NO; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; - MTL_ENABLE_DEBUG_INFO = NO; - SDKROOT = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - VALIDATE_PRODUCT = YES; - }; - name = Release; - }; - A1D4BCE0202C4F3800FCDDEC /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Manual; - DEVELOPMENT_TEAM = ""; - INFOPLIST_FILE = WeScanSampleProject/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.WeTransfer.WeScanSampleProject; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - }; - name = Debug; - }; - A1D4BCE1202C4F3800FCDDEC /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Manual; - DEVELOPMENT_TEAM = ""; - INFOPLIST_FILE = WeScanSampleProject/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = com.WeTransfer.WeScanSampleProject; - PRODUCT_NAME = "$(TARGET_NAME)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - }; - name = Release; - }; - A1D4BD05202C4F4100FCDDEC /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = ""; - CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = ""; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = WeScan/Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = WeTransfer.WeScan; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; - SKIP_INSTALL = YES; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - A1D4BD06202C4F4100FCDDEC /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_IDENTITY = ""; - CODE_SIGN_STYLE = Manual; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = ""; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - INFOPLIST_FILE = WeScan/Info.plist; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = WeTransfer.WeScan; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - PROVISIONING_PROFILE_SPECIFIER = ""; - "PROVISIONING_PROFILE_SPECIFIER[sdk=macosx*]" = ""; - SKIP_INSTALL = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = 1; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; - A1D4BD09202C4F4100FCDDEC /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = 74N8K5J2N7; - INFOPLIST_FILE = WeScanTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = WeTransfer.WeScanTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "WeScanTests/WeScanTests-Bridging-Header.h"; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/WeScanSampleProject.app/WeScanSampleProject"; - }; - name = Debug; - }; - A1D4BD0A202C4F4100FCDDEC /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; - CLANG_ENABLE_MODULES = YES; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = 74N8K5J2N7; - INFOPLIST_FILE = WeScanTests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - PRODUCT_BUNDLE_IDENTIFIER = WeTransfer.WeScanTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_OBJC_BRIDGING_HEADER = "WeScanTests/WeScanTests-Bridging-Header.h"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/WeScanSampleProject.app/WeScanSampleProject"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - A1D4BCB2202C4F3800FCDDEC /* Build configuration list for PBXProject "WeScan" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - A1D4BCDD202C4F3800FCDDEC /* Debug */, - A1D4BCDE202C4F3800FCDDEC /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - A1D4BCDF202C4F3800FCDDEC /* Build configuration list for PBXNativeTarget "WeScanSampleProject" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - A1D4BCE0202C4F3800FCDDEC /* Debug */, - A1D4BCE1202C4F3800FCDDEC /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - A1D4BD04202C4F4100FCDDEC /* Build configuration list for PBXNativeTarget "WeScan" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - A1D4BD05202C4F4100FCDDEC /* Debug */, - A1D4BD06202C4F4100FCDDEC /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - A1D4BD08202C4F4100FCDDEC /* Build configuration list for PBXNativeTarget "WeScanTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - A1D4BD09202C4F4100FCDDEC /* Debug */, - A1D4BD0A202C4F4100FCDDEC /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = A1D4BCAF202C4F3800FCDDEC /* Project object */; -} diff --git a/WeScan.xcodeproj/xcshareddata/xcschemes/WeScan.xcscheme b/WeScan.xcodeproj/xcshareddata/xcschemes/WeScan.xcscheme deleted file mode 100644 index dba3818a..00000000 --- a/WeScan.xcodeproj/xcshareddata/xcschemes/WeScan.xcscheme +++ /dev/null @@ -1,108 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/WeScan/Info.plist b/WeScan/Info.plist deleted file mode 100644 index 1007fd9d..00000000 --- a/WeScan/Info.plist +++ /dev/null @@ -1,24 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - FMWK - CFBundleShortVersionString - 1.0 - CFBundleVersion - $(CURRENT_PROJECT_VERSION) - NSPrincipalClass - - - diff --git a/WeScan/WeScan.h b/WeScan/WeScan.h deleted file mode 100644 index 5a06c989..00000000 --- a/WeScan/WeScan.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// WeScan.h -// WeScan -// -// Created by Boris Emorine on 2/8/18. -// Copyright © 2018 WeTransfer. All rights reserved. -// - -#import - -//! Project version number for Scanner. -FOUNDATION_EXPORT double ScannerVersionNumber; - -//! Project version string for Scanner. -FOUNDATION_EXPORT const unsigned char ScannerVersionString[]; - -// In this header, you should import all the public headers of your framework using statements like #import diff --git a/WeScanTests/FailureDiffs/WeScanTests.CIRectangleDetectorTests/diff_testCorrectlyDetectsAndReturnsQuadilateral@3x.png b/WeScanTests/FailureDiffs/WeScanTests.CIRectangleDetectorTests/diff_testCorrectlyDetectsAndReturnsQuadilateral@3x.png deleted file mode 100644 index b92a679d..00000000 Binary files a/WeScanTests/FailureDiffs/WeScanTests.CIRectangleDetectorTests/diff_testCorrectlyDetectsAndReturnsQuadilateral@3x.png and /dev/null differ diff --git a/WeScanTests/FailureDiffs/WeScanTests.CIRectangleDetectorTests/diff_testCorrectlyDetectsAndReturnsQuadilateral_12_1@2x.png b/WeScanTests/FailureDiffs/WeScanTests.CIRectangleDetectorTests/diff_testCorrectlyDetectsAndReturnsQuadilateral_12_1@2x.png deleted file mode 100644 index c0ae2c4d..00000000 Binary files a/WeScanTests/FailureDiffs/WeScanTests.CIRectangleDetectorTests/diff_testCorrectlyDetectsAndReturnsQuadilateral_12_1@2x.png and /dev/null differ diff --git a/WeScanTests/FailureDiffs/WeScanTests.CIRectangleDetectorTests/failed_testCorrectlyDetectsAndReturnsQuadilateral@3x.png b/WeScanTests/FailureDiffs/WeScanTests.CIRectangleDetectorTests/failed_testCorrectlyDetectsAndReturnsQuadilateral@3x.png deleted file mode 100644 index 8cab024f..00000000 Binary files a/WeScanTests/FailureDiffs/WeScanTests.CIRectangleDetectorTests/failed_testCorrectlyDetectsAndReturnsQuadilateral@3x.png and /dev/null differ diff --git a/WeScanTests/FailureDiffs/WeScanTests.CIRectangleDetectorTests/failed_testCorrectlyDetectsAndReturnsQuadilateral_12_1@2x.png b/WeScanTests/FailureDiffs/WeScanTests.CIRectangleDetectorTests/failed_testCorrectlyDetectsAndReturnsQuadilateral_12_1@2x.png deleted file mode 100644 index 7d97046c..00000000 Binary files a/WeScanTests/FailureDiffs/WeScanTests.CIRectangleDetectorTests/failed_testCorrectlyDetectsAndReturnsQuadilateral_12_1@2x.png and /dev/null differ diff --git a/WeScanTests/FailureDiffs/WeScanTests.CIRectangleDetectorTests/reference_testCorrectlyDetectsAndReturnsQuadilateral@3x.png b/WeScanTests/FailureDiffs/WeScanTests.CIRectangleDetectorTests/reference_testCorrectlyDetectsAndReturnsQuadilateral@3x.png deleted file mode 100644 index 189caca1..00000000 Binary files a/WeScanTests/FailureDiffs/WeScanTests.CIRectangleDetectorTests/reference_testCorrectlyDetectsAndReturnsQuadilateral@3x.png and /dev/null differ diff --git a/WeScanTests/FailureDiffs/WeScanTests.CIRectangleDetectorTests/reference_testCorrectlyDetectsAndReturnsQuadilateral_12_1@2x.png b/WeScanTests/FailureDiffs/WeScanTests.CIRectangleDetectorTests/reference_testCorrectlyDetectsAndReturnsQuadilateral_12_1@2x.png deleted file mode 100644 index 6a3549a5..00000000 Binary files a/WeScanTests/FailureDiffs/WeScanTests.CIRectangleDetectorTests/reference_testCorrectlyDetectsAndReturnsQuadilateral_12_1@2x.png and /dev/null differ diff --git a/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/diff_testDemoImageIsCorrect@2x.png b/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/diff_testDemoImageIsCorrect@2x.png deleted file mode 100644 index 8dff7252..00000000 Binary files a/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/diff_testDemoImageIsCorrect@2x.png and /dev/null differ diff --git a/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/diff_testDemoImageIsCorrect@3x.png b/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/diff_testDemoImageIsCorrect@3x.png deleted file mode 100644 index e886155a..00000000 Binary files a/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/diff_testDemoImageIsCorrect@3x.png and /dev/null differ diff --git a/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/diff_testEnhancedImage@2x.png b/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/diff_testEnhancedImage@2x.png deleted file mode 100644 index 0cb7a9a1..00000000 Binary files a/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/diff_testEnhancedImage@2x.png and /dev/null differ diff --git a/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/diff_testImageIsCorrectlyRotated180@2x.png b/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/diff_testImageIsCorrectlyRotated180@2x.png deleted file mode 100644 index 26c6fa51..00000000 Binary files a/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/diff_testImageIsCorrectlyRotated180@2x.png and /dev/null differ diff --git a/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/diff_testImageIsCorrectlyRotated270@2x.png b/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/diff_testImageIsCorrectlyRotated270@2x.png deleted file mode 100644 index 5dfa2b23..00000000 Binary files a/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/diff_testImageIsCorrectlyRotated270@2x.png and /dev/null differ diff --git a/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/diff_testImageIsCorrectlyRotated360@2x.png b/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/diff_testImageIsCorrectlyRotated360@2x.png deleted file mode 100644 index 8dff7252..00000000 Binary files a/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/diff_testImageIsCorrectlyRotated360@2x.png and /dev/null differ diff --git a/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/diff_testImageIsCorrectlyRotated90@2x.png b/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/diff_testImageIsCorrectlyRotated90@2x.png deleted file mode 100644 index 24a4aa42..00000000 Binary files a/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/diff_testImageIsCorrectlyRotated90@2x.png and /dev/null differ diff --git a/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/failed_testDemoImageIsCorrect@2x.png b/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/failed_testDemoImageIsCorrect@2x.png deleted file mode 100644 index 01c5601f..00000000 Binary files a/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/failed_testDemoImageIsCorrect@2x.png and /dev/null differ diff --git a/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/failed_testDemoImageIsCorrect@3x.png b/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/failed_testDemoImageIsCorrect@3x.png deleted file mode 100644 index 8528aff9..00000000 Binary files a/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/failed_testDemoImageIsCorrect@3x.png and /dev/null differ diff --git a/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/failed_testEnhancedImage@2x.png b/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/failed_testEnhancedImage@2x.png deleted file mode 100644 index b14781f4..00000000 Binary files a/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/failed_testEnhancedImage@2x.png and /dev/null differ diff --git a/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/failed_testImageIsCorrectlyRotated180@2x.png b/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/failed_testImageIsCorrectlyRotated180@2x.png deleted file mode 100644 index 40411086..00000000 Binary files a/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/failed_testImageIsCorrectlyRotated180@2x.png and /dev/null differ diff --git a/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/failed_testImageIsCorrectlyRotated270@2x.png b/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/failed_testImageIsCorrectlyRotated270@2x.png deleted file mode 100644 index e7349048..00000000 Binary files a/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/failed_testImageIsCorrectlyRotated270@2x.png and /dev/null differ diff --git a/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/failed_testImageIsCorrectlyRotated360@2x.png b/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/failed_testImageIsCorrectlyRotated360@2x.png deleted file mode 100644 index 01c5601f..00000000 Binary files a/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/failed_testImageIsCorrectlyRotated360@2x.png and /dev/null differ diff --git a/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/failed_testImageIsCorrectlyRotated90@2x.png b/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/failed_testImageIsCorrectlyRotated90@2x.png deleted file mode 100644 index 7ac428f8..00000000 Binary files a/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/failed_testImageIsCorrectlyRotated90@2x.png and /dev/null differ diff --git a/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/reference_testDemoImageIsCorrect@2x.png b/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/reference_testDemoImageIsCorrect@2x.png deleted file mode 100644 index fa0208af..00000000 Binary files a/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/reference_testDemoImageIsCorrect@2x.png and /dev/null differ diff --git a/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/reference_testDemoImageIsCorrect@3x.png b/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/reference_testDemoImageIsCorrect@3x.png deleted file mode 100644 index 15373fc9..00000000 Binary files a/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/reference_testDemoImageIsCorrect@3x.png and /dev/null differ diff --git a/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/reference_testEnhancedImage@2x.png b/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/reference_testEnhancedImage@2x.png deleted file mode 100644 index 1a0f1b6d..00000000 Binary files a/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/reference_testEnhancedImage@2x.png and /dev/null differ diff --git a/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/reference_testImageIsCorrectlyRotated180@2x.png b/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/reference_testImageIsCorrectlyRotated180@2x.png deleted file mode 100644 index 13ba176a..00000000 Binary files a/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/reference_testImageIsCorrectlyRotated180@2x.png and /dev/null differ diff --git a/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/reference_testImageIsCorrectlyRotated270@2x.png b/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/reference_testImageIsCorrectlyRotated270@2x.png deleted file mode 100644 index 441fbaaf..00000000 Binary files a/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/reference_testImageIsCorrectlyRotated270@2x.png and /dev/null differ diff --git a/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/reference_testImageIsCorrectlyRotated360@2x.png b/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/reference_testImageIsCorrectlyRotated360@2x.png deleted file mode 100644 index fa0208af..00000000 Binary files a/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/reference_testImageIsCorrectlyRotated360@2x.png and /dev/null differ diff --git a/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/reference_testImageIsCorrectlyRotated90@2x.png b/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/reference_testImageIsCorrectlyRotated90@2x.png deleted file mode 100644 index 27726973..00000000 Binary files a/WeScanTests/FailureDiffs/WeScanTests.ReviewViewControllerTests/reference_testImageIsCorrectlyRotated90@2x.png and /dev/null differ diff --git a/WeScanTests/FailureDiffs/WeScanTests.UIImageTests/diff_testRotateDownFacingImageCorrectly@3x.png b/WeScanTests/FailureDiffs/WeScanTests.UIImageTests/diff_testRotateDownFacingImageCorrectly@3x.png deleted file mode 100644 index 2b10cdc6..00000000 Binary files a/WeScanTests/FailureDiffs/WeScanTests.UIImageTests/diff_testRotateDownFacingImageCorrectly@3x.png and /dev/null differ diff --git a/WeScanTests/FailureDiffs/WeScanTests.UIImageTests/diff_testRotateImageCorrectly@3x.png b/WeScanTests/FailureDiffs/WeScanTests.UIImageTests/diff_testRotateImageCorrectly@3x.png deleted file mode 100644 index 31c99c4b..00000000 Binary files a/WeScanTests/FailureDiffs/WeScanTests.UIImageTests/diff_testRotateImageCorrectly@3x.png and /dev/null differ diff --git a/WeScanTests/FailureDiffs/WeScanTests.UIImageTests/diff_testRotateRightFacingImageCorrectly@3x.png b/WeScanTests/FailureDiffs/WeScanTests.UIImageTests/diff_testRotateRightFacingImageCorrectly@3x.png deleted file mode 100644 index 27af53e0..00000000 Binary files a/WeScanTests/FailureDiffs/WeScanTests.UIImageTests/diff_testRotateRightFacingImageCorrectly@3x.png and /dev/null differ diff --git a/WeScanTests/FailureDiffs/WeScanTests.UIImageTests/diff_testRotateUpFacingImageCorrectly@3x.png b/WeScanTests/FailureDiffs/WeScanTests.UIImageTests/diff_testRotateUpFacingImageCorrectly@3x.png deleted file mode 100644 index d4a2dee0..00000000 Binary files a/WeScanTests/FailureDiffs/WeScanTests.UIImageTests/diff_testRotateUpFacingImageCorrectly@3x.png and /dev/null differ diff --git a/WeScanTests/FailureDiffs/WeScanTests.UIImageTests/failed_testRotateDownFacingImageCorrectly@3x.png b/WeScanTests/FailureDiffs/WeScanTests.UIImageTests/failed_testRotateDownFacingImageCorrectly@3x.png deleted file mode 100644 index 1a1003ec..00000000 Binary files a/WeScanTests/FailureDiffs/WeScanTests.UIImageTests/failed_testRotateDownFacingImageCorrectly@3x.png and /dev/null differ diff --git a/WeScanTests/FailureDiffs/WeScanTests.UIImageTests/failed_testRotateImageCorrectly@3x.png b/WeScanTests/FailureDiffs/WeScanTests.UIImageTests/failed_testRotateImageCorrectly@3x.png deleted file mode 100644 index 0ea83fea..00000000 Binary files a/WeScanTests/FailureDiffs/WeScanTests.UIImageTests/failed_testRotateImageCorrectly@3x.png and /dev/null differ diff --git a/WeScanTests/FailureDiffs/WeScanTests.UIImageTests/failed_testRotateRightFacingImageCorrectly@3x.png b/WeScanTests/FailureDiffs/WeScanTests.UIImageTests/failed_testRotateRightFacingImageCorrectly@3x.png deleted file mode 100644 index 9e9c697f..00000000 Binary files a/WeScanTests/FailureDiffs/WeScanTests.UIImageTests/failed_testRotateRightFacingImageCorrectly@3x.png and /dev/null differ diff --git a/WeScanTests/FailureDiffs/WeScanTests.UIImageTests/failed_testRotateUpFacingImageCorrectly@3x.png b/WeScanTests/FailureDiffs/WeScanTests.UIImageTests/failed_testRotateUpFacingImageCorrectly@3x.png deleted file mode 100644 index bcfe9b76..00000000 Binary files a/WeScanTests/FailureDiffs/WeScanTests.UIImageTests/failed_testRotateUpFacingImageCorrectly@3x.png and /dev/null differ diff --git a/WeScanTests/FailureDiffs/WeScanTests.VisionRectangleDetectorTests/diff_testCorrectlyDetectsAndReturnsQuadilateral@2x.png b/WeScanTests/FailureDiffs/WeScanTests.VisionRectangleDetectorTests/diff_testCorrectlyDetectsAndReturnsQuadilateral@2x.png deleted file mode 100644 index e5d10293..00000000 Binary files a/WeScanTests/FailureDiffs/WeScanTests.VisionRectangleDetectorTests/diff_testCorrectlyDetectsAndReturnsQuadilateral@2x.png and /dev/null differ diff --git a/WeScanTests/FailureDiffs/WeScanTests.VisionRectangleDetectorTests/diff_testCorrectlyDetectsAndReturnsQuadilateral@3x.png b/WeScanTests/FailureDiffs/WeScanTests.VisionRectangleDetectorTests/diff_testCorrectlyDetectsAndReturnsQuadilateral@3x.png deleted file mode 100644 index 3371f6b3..00000000 Binary files a/WeScanTests/FailureDiffs/WeScanTests.VisionRectangleDetectorTests/diff_testCorrectlyDetectsAndReturnsQuadilateral@3x.png and /dev/null differ diff --git a/WeScanTests/FailureDiffs/WeScanTests.VisionRectangleDetectorTests/failed_testCorrectlyDetectsAndReturnsQuadilateral@2x.png b/WeScanTests/FailureDiffs/WeScanTests.VisionRectangleDetectorTests/failed_testCorrectlyDetectsAndReturnsQuadilateral@2x.png deleted file mode 100644 index d6af0329..00000000 Binary files a/WeScanTests/FailureDiffs/WeScanTests.VisionRectangleDetectorTests/failed_testCorrectlyDetectsAndReturnsQuadilateral@2x.png and /dev/null differ diff --git a/WeScanTests/FailureDiffs/WeScanTests.VisionRectangleDetectorTests/reference_testCorrectlyDetectsAndReturnsQuadilateral@2x.png b/WeScanTests/FailureDiffs/WeScanTests.VisionRectangleDetectorTests/reference_testCorrectlyDetectsAndReturnsQuadilateral@2x.png deleted file mode 100644 index 40802f8d..00000000 Binary files a/WeScanTests/FailureDiffs/WeScanTests.VisionRectangleDetectorTests/reference_testCorrectlyDetectsAndReturnsQuadilateral@2x.png and /dev/null differ diff --git a/WeScanTests/FailureDiffs/WeScanTests.VisionRectangleDetectorTests/reference_testCorrectlyDetectsAndReturnsQuadilateral@3x.png b/WeScanTests/FailureDiffs/WeScanTests.VisionRectangleDetectorTests/reference_testCorrectlyDetectsAndReturnsQuadilateral@3x.png deleted file mode 100644 index 8d0e57b3..00000000 Binary files a/WeScanTests/FailureDiffs/WeScanTests.VisionRectangleDetectorTests/reference_testCorrectlyDetectsAndReturnsQuadilateral@3x.png and /dev/null differ diff --git a/WeScanTests/ReferenceImages_64/WeScanTests.CIRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateral@3x.png b/WeScanTests/ReferenceImages_64/WeScanTests.CIRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateral@3x.png deleted file mode 100644 index 6ce753e1..00000000 Binary files a/WeScanTests/ReferenceImages_64/WeScanTests.CIRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateral@3x.png and /dev/null differ diff --git a/WeScanTests/ReferenceImages_64/WeScanTests.CIRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateral_11_2@2x.png b/WeScanTests/ReferenceImages_64/WeScanTests.CIRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateral_11_2@2x.png deleted file mode 100644 index 7a3c5e2d..00000000 Binary files a/WeScanTests/ReferenceImages_64/WeScanTests.CIRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateral_11_2@2x.png and /dev/null differ diff --git a/WeScanTests/ReferenceImages_64/WeScanTests.CIRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateral_12_0@2x.png b/WeScanTests/ReferenceImages_64/WeScanTests.CIRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateral_12_0@2x.png deleted file mode 100644 index 6a3549a5..00000000 Binary files a/WeScanTests/ReferenceImages_64/WeScanTests.CIRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateral_12_0@2x.png and /dev/null differ diff --git a/WeScanTests/ReferenceImages_64/WeScanTests.CIRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateral_12_1@2x.png b/WeScanTests/ReferenceImages_64/WeScanTests.CIRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateral_12_1@2x.png deleted file mode 100644 index 7d97046c..00000000 Binary files a/WeScanTests/ReferenceImages_64/WeScanTests.CIRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateral_12_1@2x.png and /dev/null differ diff --git a/WeScanTests/ReferenceImages_64/WeScanTests.CIRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateral_12_1@3x.png b/WeScanTests/ReferenceImages_64/WeScanTests.CIRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateral_12_1@3x.png deleted file mode 100644 index 6ce753e1..00000000 Binary files a/WeScanTests/ReferenceImages_64/WeScanTests.CIRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateral_12_1@3x.png and /dev/null differ diff --git a/WeScanTests/ReferenceImages_64/WeScanTests.CIRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateral_12_2@2x.png b/WeScanTests/ReferenceImages_64/WeScanTests.CIRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateral_12_2@2x.png deleted file mode 100644 index 7d97046c..00000000 Binary files a/WeScanTests/ReferenceImages_64/WeScanTests.CIRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateral_12_2@2x.png and /dev/null differ diff --git a/WeScanTests/ReferenceImages_64/WeScanTests.CIRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateral_13_1@2x.png b/WeScanTests/ReferenceImages_64/WeScanTests.CIRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateral_13_1@2x.png deleted file mode 100644 index 22364e47..00000000 Binary files a/WeScanTests/ReferenceImages_64/WeScanTests.CIRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateral_13_1@2x.png and /dev/null differ diff --git a/WeScanTests/ReferenceImages_64/WeScanTests.CIRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateral_13_3.png b/WeScanTests/ReferenceImages_64/WeScanTests.CIRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateral_13_3.png deleted file mode 100644 index 22364e47..00000000 Binary files a/WeScanTests/ReferenceImages_64/WeScanTests.CIRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateral_13_3.png and /dev/null differ diff --git a/WeScanTests/ReferenceImages_64/WeScanTests.CIRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateral_14_2.png b/WeScanTests/ReferenceImages_64/WeScanTests.CIRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateral_14_2.png deleted file mode 100644 index 6b41d86a..00000000 Binary files a/WeScanTests/ReferenceImages_64/WeScanTests.CIRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateral_14_2.png and /dev/null differ diff --git a/WeScanTests/ReferenceImages_64/WeScanTests.CIRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateral_14_3.png b/WeScanTests/ReferenceImages_64/WeScanTests.CIRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateral_14_3.png deleted file mode 100644 index d8a958ce..00000000 Binary files a/WeScanTests/ReferenceImages_64/WeScanTests.CIRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateral_14_3.png and /dev/null differ diff --git a/WeScanTests/ReferenceImages_64/WeScanTests.ReviewViewControllerTests/testDemoImageIsCorrect@2x.png b/WeScanTests/ReferenceImages_64/WeScanTests.ReviewViewControllerTests/testDemoImageIsCorrect@2x.png deleted file mode 100644 index a0d7ad52..00000000 Binary files a/WeScanTests/ReferenceImages_64/WeScanTests.ReviewViewControllerTests/testDemoImageIsCorrect@2x.png and /dev/null differ diff --git a/WeScanTests/ReferenceImages_64/WeScanTests.ReviewViewControllerTests/testDemoImageIsCorrect@3x.png b/WeScanTests/ReferenceImages_64/WeScanTests.ReviewViewControllerTests/testDemoImageIsCorrect@3x.png deleted file mode 100644 index 89be7aa0..00000000 Binary files a/WeScanTests/ReferenceImages_64/WeScanTests.ReviewViewControllerTests/testDemoImageIsCorrect@3x.png and /dev/null differ diff --git a/WeScanTests/ReferenceImages_64/WeScanTests.ReviewViewControllerTests/testEnhancedImage@2x.png b/WeScanTests/ReferenceImages_64/WeScanTests.ReviewViewControllerTests/testEnhancedImage@2x.png deleted file mode 100644 index 67ab6e0c..00000000 Binary files a/WeScanTests/ReferenceImages_64/WeScanTests.ReviewViewControllerTests/testEnhancedImage@2x.png and /dev/null differ diff --git a/WeScanTests/ReferenceImages_64/WeScanTests.ReviewViewControllerTests/testEnhancedImage@3x.png b/WeScanTests/ReferenceImages_64/WeScanTests.ReviewViewControllerTests/testEnhancedImage@3x.png deleted file mode 100644 index 140e7fc2..00000000 Binary files a/WeScanTests/ReferenceImages_64/WeScanTests.ReviewViewControllerTests/testEnhancedImage@3x.png and /dev/null differ diff --git a/WeScanTests/ReferenceImages_64/WeScanTests.ReviewViewControllerTests/testImageIsCorrectlyRotated180@2x.png b/WeScanTests/ReferenceImages_64/WeScanTests.ReviewViewControllerTests/testImageIsCorrectlyRotated180@2x.png deleted file mode 100644 index 0f494b0e..00000000 Binary files a/WeScanTests/ReferenceImages_64/WeScanTests.ReviewViewControllerTests/testImageIsCorrectlyRotated180@2x.png and /dev/null differ diff --git a/WeScanTests/ReferenceImages_64/WeScanTests.ReviewViewControllerTests/testImageIsCorrectlyRotated180@3x.png b/WeScanTests/ReferenceImages_64/WeScanTests.ReviewViewControllerTests/testImageIsCorrectlyRotated180@3x.png deleted file mode 100644 index 14344cca..00000000 Binary files a/WeScanTests/ReferenceImages_64/WeScanTests.ReviewViewControllerTests/testImageIsCorrectlyRotated180@3x.png and /dev/null differ diff --git a/WeScanTests/ReferenceImages_64/WeScanTests.ReviewViewControllerTests/testImageIsCorrectlyRotated270@2x.png b/WeScanTests/ReferenceImages_64/WeScanTests.ReviewViewControllerTests/testImageIsCorrectlyRotated270@2x.png deleted file mode 100644 index 368ad996..00000000 Binary files a/WeScanTests/ReferenceImages_64/WeScanTests.ReviewViewControllerTests/testImageIsCorrectlyRotated270@2x.png and /dev/null differ diff --git a/WeScanTests/ReferenceImages_64/WeScanTests.ReviewViewControllerTests/testImageIsCorrectlyRotated270@3x.png b/WeScanTests/ReferenceImages_64/WeScanTests.ReviewViewControllerTests/testImageIsCorrectlyRotated270@3x.png deleted file mode 100644 index 9f87bb22..00000000 Binary files a/WeScanTests/ReferenceImages_64/WeScanTests.ReviewViewControllerTests/testImageIsCorrectlyRotated270@3x.png and /dev/null differ diff --git a/WeScanTests/ReferenceImages_64/WeScanTests.ReviewViewControllerTests/testImageIsCorrectlyRotated360@2x.png b/WeScanTests/ReferenceImages_64/WeScanTests.ReviewViewControllerTests/testImageIsCorrectlyRotated360@2x.png deleted file mode 100644 index a0d7ad52..00000000 Binary files a/WeScanTests/ReferenceImages_64/WeScanTests.ReviewViewControllerTests/testImageIsCorrectlyRotated360@2x.png and /dev/null differ diff --git a/WeScanTests/ReferenceImages_64/WeScanTests.ReviewViewControllerTests/testImageIsCorrectlyRotated360@3x.png b/WeScanTests/ReferenceImages_64/WeScanTests.ReviewViewControllerTests/testImageIsCorrectlyRotated360@3x.png deleted file mode 100644 index 89be7aa0..00000000 Binary files a/WeScanTests/ReferenceImages_64/WeScanTests.ReviewViewControllerTests/testImageIsCorrectlyRotated360@3x.png and /dev/null differ diff --git a/WeScanTests/ReferenceImages_64/WeScanTests.ReviewViewControllerTests/testImageIsCorrectlyRotated90@2x.png b/WeScanTests/ReferenceImages_64/WeScanTests.ReviewViewControllerTests/testImageIsCorrectlyRotated90@2x.png deleted file mode 100644 index d42dfeb9..00000000 Binary files a/WeScanTests/ReferenceImages_64/WeScanTests.ReviewViewControllerTests/testImageIsCorrectlyRotated90@2x.png and /dev/null differ diff --git a/WeScanTests/ReferenceImages_64/WeScanTests.ReviewViewControllerTests/testImageIsCorrectlyRotated90@3x.png b/WeScanTests/ReferenceImages_64/WeScanTests.ReviewViewControllerTests/testImageIsCorrectlyRotated90@3x.png deleted file mode 100644 index ba634215..00000000 Binary files a/WeScanTests/ReferenceImages_64/WeScanTests.ReviewViewControllerTests/testImageIsCorrectlyRotated90@3x.png and /dev/null differ diff --git a/WeScanTests/ReferenceImages_64/WeScanTests.UIImageTests/testRotateDefaultFacingImageCorrectly@2x.png b/WeScanTests/ReferenceImages_64/WeScanTests.UIImageTests/testRotateDefaultFacingImageCorrectly@2x.png deleted file mode 100644 index 3d32b860..00000000 Binary files a/WeScanTests/ReferenceImages_64/WeScanTests.UIImageTests/testRotateDefaultFacingImageCorrectly@2x.png and /dev/null differ diff --git a/WeScanTests/ReferenceImages_64/WeScanTests.UIImageTests/testRotateDownFacingImageCorrectly@2x.png b/WeScanTests/ReferenceImages_64/WeScanTests.UIImageTests/testRotateDownFacingImageCorrectly@2x.png deleted file mode 100644 index c4cf090c..00000000 Binary files a/WeScanTests/ReferenceImages_64/WeScanTests.UIImageTests/testRotateDownFacingImageCorrectly@2x.png and /dev/null differ diff --git a/WeScanTests/ReferenceImages_64/WeScanTests.UIImageTests/testRotateDownFacingImageCorrectly@3x.png b/WeScanTests/ReferenceImages_64/WeScanTests.UIImageTests/testRotateDownFacingImageCorrectly@3x.png deleted file mode 100644 index 1a1003ec..00000000 Binary files a/WeScanTests/ReferenceImages_64/WeScanTests.UIImageTests/testRotateDownFacingImageCorrectly@3x.png and /dev/null differ diff --git a/WeScanTests/ReferenceImages_64/WeScanTests.UIImageTests/testRotateImageCorrectly@2x.png b/WeScanTests/ReferenceImages_64/WeScanTests.UIImageTests/testRotateImageCorrectly@2x.png deleted file mode 100644 index 4161e1a8..00000000 Binary files a/WeScanTests/ReferenceImages_64/WeScanTests.UIImageTests/testRotateImageCorrectly@2x.png and /dev/null differ diff --git a/WeScanTests/ReferenceImages_64/WeScanTests.UIImageTests/testRotateImageCorrectly@3x.png b/WeScanTests/ReferenceImages_64/WeScanTests.UIImageTests/testRotateImageCorrectly@3x.png deleted file mode 100644 index 0ea83fea..00000000 Binary files a/WeScanTests/ReferenceImages_64/WeScanTests.UIImageTests/testRotateImageCorrectly@3x.png and /dev/null differ diff --git a/WeScanTests/ReferenceImages_64/WeScanTests.UIImageTests/testRotateLeftFacingImageCorrectly@2x.png b/WeScanTests/ReferenceImages_64/WeScanTests.UIImageTests/testRotateLeftFacingImageCorrectly@2x.png deleted file mode 100644 index 83db0251..00000000 Binary files a/WeScanTests/ReferenceImages_64/WeScanTests.UIImageTests/testRotateLeftFacingImageCorrectly@2x.png and /dev/null differ diff --git a/WeScanTests/ReferenceImages_64/WeScanTests.UIImageTests/testRotateLeftFacingImageCorrectly@3x.png b/WeScanTests/ReferenceImages_64/WeScanTests.UIImageTests/testRotateLeftFacingImageCorrectly@3x.png deleted file mode 100644 index cc41aded..00000000 Binary files a/WeScanTests/ReferenceImages_64/WeScanTests.UIImageTests/testRotateLeftFacingImageCorrectly@3x.png and /dev/null differ diff --git a/WeScanTests/ReferenceImages_64/WeScanTests.UIImageTests/testRotateRightFacingImageCorrectly@2x.png b/WeScanTests/ReferenceImages_64/WeScanTests.UIImageTests/testRotateRightFacingImageCorrectly@2x.png deleted file mode 100644 index a0353965..00000000 Binary files a/WeScanTests/ReferenceImages_64/WeScanTests.UIImageTests/testRotateRightFacingImageCorrectly@2x.png and /dev/null differ diff --git a/WeScanTests/ReferenceImages_64/WeScanTests.UIImageTests/testRotateRightFacingImageCorrectly@3x.png b/WeScanTests/ReferenceImages_64/WeScanTests.UIImageTests/testRotateRightFacingImageCorrectly@3x.png deleted file mode 100644 index 9e9c697f..00000000 Binary files a/WeScanTests/ReferenceImages_64/WeScanTests.UIImageTests/testRotateRightFacingImageCorrectly@3x.png and /dev/null differ diff --git a/WeScanTests/ReferenceImages_64/WeScanTests.UIImageTests/testRotateUpFacingImageCorrectly@2x.png b/WeScanTests/ReferenceImages_64/WeScanTests.UIImageTests/testRotateUpFacingImageCorrectly@2x.png deleted file mode 100644 index 9c1f771e..00000000 Binary files a/WeScanTests/ReferenceImages_64/WeScanTests.UIImageTests/testRotateUpFacingImageCorrectly@2x.png and /dev/null differ diff --git a/WeScanTests/ReferenceImages_64/WeScanTests.UIImageTests/testRotateUpFacingImageCorrectly@3x.png b/WeScanTests/ReferenceImages_64/WeScanTests.UIImageTests/testRotateUpFacingImageCorrectly@3x.png deleted file mode 100644 index bcfe9b76..00000000 Binary files a/WeScanTests/ReferenceImages_64/WeScanTests.UIImageTests/testRotateUpFacingImageCorrectly@3x.png and /dev/null differ diff --git a/WeScanTests/ReferenceImages_64/WeScanTests.VisionRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateral@2x.png b/WeScanTests/ReferenceImages_64/WeScanTests.VisionRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateral@2x.png deleted file mode 100644 index d6af0329..00000000 Binary files a/WeScanTests/ReferenceImages_64/WeScanTests.VisionRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateral@2x.png and /dev/null differ diff --git a/WeScanTests/ReferenceImages_64/WeScanTests.VisionRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateralPixelBuffer@2x.png b/WeScanTests/ReferenceImages_64/WeScanTests.VisionRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateralPixelBuffer@2x.png deleted file mode 100644 index 3887d813..00000000 Binary files a/WeScanTests/ReferenceImages_64/WeScanTests.VisionRectangleDetectorTests/testCorrectlyDetectsAndReturnsQuadilateralPixelBuffer@2x.png and /dev/null differ diff --git a/WeScanTests/VisionRectangleDetectorTests.swift b/WeScanTests/VisionRectangleDetectorTests.swift deleted file mode 100644 index 8ba5b5ab..00000000 --- a/WeScanTests/VisionRectangleDetectorTests.swift +++ /dev/null @@ -1,103 +0,0 @@ -// -// VisionRectangleDetectorTests.swift -// WeScanTests -// -// Created by James Campbell on 8/8/18. -// Copyright © 2018 WeTransfer. All rights reserved. -// - -import FBSnapshotTestCase -import XCTest -@testable import WeScan - -final class VisionRectangleDetectorTests: FBSnapshotTestCase { - - private var containerLayer: CALayer! - private var image: UIImage! - - override func setUp() { - super.setUp() - recordMode = false - - // Setting up containerLayer and creating the image to be tested on both tests in this class. - containerLayer = CALayer() - let targetSize = CGSize(width: 150, height: 150) - - containerLayer.backgroundColor = UIColor.white.cgColor - containerLayer.frame = CGRect(origin: .zero, size: targetSize) - containerLayer.masksToBounds = true - - let targetLayer = CALayer() - targetLayer.backgroundColor = UIColor.black.cgColor - targetLayer.frame = containerLayer.frame.insetBy(dx: 5, dy: 5) - - containerLayer.addSublayer(targetLayer) - - UIGraphicsBeginImageContextWithOptions(targetSize, true, 0.0) - - containerLayer.render(in: UIGraphicsGetCurrentContext()!) - - self.image = UIGraphicsGetImageFromCurrentImageContext()! - - UIGraphicsEndImageContext() - - } - - override func tearDown() { - super.tearDown() - containerLayer = nil - image = nil - } - - func testCorrectlyDetectsAndReturnsQuadilateral() { - - let ciImage = CIImage(cgImage: image.cgImage!) - let expectation = XCTestExpectation(description: "Detect rectangle on CIImage") - - VisionRectangleDetector.rectangle(forImage: ciImage) { (quad) in - - DispatchQueue.main.async { - - let resultView = UIView(frame: self.containerLayer.frame) - resultView.layer.addSublayer(self.containerLayer) - - let quadView = QuadrilateralView(frame: resultView.bounds) - quadView.drawQuadrilateral(quad: quad!, animated: false) - quadView.backgroundColor = UIColor.red - resultView.addSubview(quadView) - - self.FBSnapshotVerifyView(resultView, overallTolerance: 0.05) - expectation.fulfill() - } - } - - wait(for: [expectation], timeout: 3.0) - } - - func testCorrectlyDetectsAndReturnsQuadilateralPixelBuffer() { - - let expectation = XCTestExpectation(description: "Detect rectangle on CVPixelBuffer") - if let pixelBuffer = image.pixelBuffer() { - VisionRectangleDetector.rectangle(forPixelBuffer: pixelBuffer) { (quad) in - - DispatchQueue.main.async { - - let resultView = UIView(frame: self.containerLayer.frame) - resultView.layer.addSublayer(self.containerLayer) - - let quadView = QuadrilateralView(frame: resultView.bounds) - quadView.drawQuadrilateral(quad: quad!, animated: false) - quadView.backgroundColor = UIColor.red - resultView.addSubview(quadView) - - self.FBSnapshotVerifyView(resultView, perPixelTolerance: 6 / 256) - expectation.fulfill() - } - } - } else { - XCTFail("could not convert image to pixelBuffer") - } - - wait(for: [expectation], timeout: 3.0) - } -} diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 3f3b4455..96d327ee 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -1,12 +1,15 @@ # Fastlane requirements fastlane_version "1.109.0" -import "./../Submodules/WeTransfer-iOS-CI/Fastlane/Fastfile" +import "./../Submodules/WeTransfer-iOS-CI/Fastlane/testing_lanes.rb" +import "./../Submodules/WeTransfer-iOS-CI/Fastlane/shared_lanes.rb" desc "Run the tests and prepare for Danger" lane :test do |options| - test_project( - project_name: "WeScan", - scheme: "WeScan", - device: "iPhone 12 Pro") + test_package( + package_name: 'WeScan', + package_path: ENV['PWD'], + disable_automatic_package_resolution: false, + device: "iPhone 14 Pro" + ) end