From: Ruben Beltran del Rio Date: Tue, 2 Feb 2021 08:16:46 +0000 (+0100) Subject: Initial commit X-Git-Tag: 1.0.0~2 X-Git-Url: https://git.r.bdr.sh/rbdr/map/commitdiff_plain/1b85f723b48d38cf345bb9a4f3fd01aa6039b50b Initial commit --- 1b85f723b48d38cf345bb9a4f3fd01aa6039b50b diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c20bac4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,27 @@ +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## User settings +xcuserdata/ + +## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) +*.xcscmblueprint +*.xccheckout + +## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) +build/ +DerivedData/ +*.moved-aside +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 + +## Gcc Patch +/*.gcno + diff --git a/Map.xcodeproj/project.pbxproj b/Map.xcodeproj/project.pbxproj new file mode 100644 index 0000000..b7dc2fa --- /dev/null +++ b/Map.xcodeproj/project.pbxproj @@ -0,0 +1,643 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + B526257225C874F9003E73B7 /* MapApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = B526257125C874F9003E73B7 /* MapApp.swift */; }; + B526257425C874F9003E73B7 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B526257325C874F9003E73B7 /* ContentView.swift */; }; + B526257625C874FA003E73B7 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B526257525C874FA003E73B7 /* Assets.xcassets */; }; + B526257925C874FA003E73B7 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B526257825C874FA003E73B7 /* Preview Assets.xcassets */; }; + B526257B25C874FA003E73B7 /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = B526257A25C874FA003E73B7 /* Persistence.swift */; }; + B526257E25C874FA003E73B7 /* Map.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = B526257C25C874FA003E73B7 /* Map.xcdatamodeld */; }; + B526258A25C874FA003E73B7 /* MapTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B526258925C874FA003E73B7 /* MapTests.swift */; }; + B526259525C874FA003E73B7 /* MapUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B526259425C874FA003E73B7 /* MapUITests.swift */; }; + B52625A625C876C3003E73B7 /* MapDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52625A525C876C3003E73B7 /* MapDetail.swift */; }; + B52625AB25C87909003E73B7 /* Date+format.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52625AA25C87909003E73B7 /* Date+format.swift */; }; + B52625B025C87C14003E73B7 /* MapRender.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52625AF25C87C14003E73B7 /* MapRender.swift */; }; + B52625B625C87D69003E73B7 /* Binding+unwrap.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52625B525C87D69003E73B7 /* Binding+unwrap.swift */; }; + B52625BB25C884C2003E73B7 /* Map+parse.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52625BA25C884C2003E73B7 /* Map+parse.swift */; }; + B52625C625C8BD2A003E73B7 /* Stage.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52625C525C8BD2A003E73B7 /* Stage.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + B526258625C874FA003E73B7 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = B526256625C874F9003E73B7 /* Project object */; + proxyType = 1; + remoteGlobalIDString = B526256D25C874F9003E73B7; + remoteInfo = Map; + }; + B526259125C874FA003E73B7 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = B526256625C874F9003E73B7 /* Project object */; + proxyType = 1; + remoteGlobalIDString = B526256D25C874F9003E73B7; + remoteInfo = Map; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + B526256E25C874F9003E73B7 /* Map.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Map.app; sourceTree = BUILT_PRODUCTS_DIR; }; + B526257125C874F9003E73B7 /* MapApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapApp.swift; sourceTree = ""; }; + B526257325C874F9003E73B7 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + B526257525C874FA003E73B7 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + B526257825C874FA003E73B7 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + B526257A25C874FA003E73B7 /* Persistence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Persistence.swift; sourceTree = ""; }; + B526257D25C874FA003E73B7 /* Map.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Map.xcdatamodel; sourceTree = ""; }; + B526257F25C874FA003E73B7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + B526258025C874FA003E73B7 /* Map.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Map.entitlements; sourceTree = ""; }; + B526258525C874FA003E73B7 /* MapTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MapTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + B526258925C874FA003E73B7 /* MapTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapTests.swift; sourceTree = ""; }; + B526258B25C874FA003E73B7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + B526259025C874FA003E73B7 /* MapUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MapUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + B526259425C874FA003E73B7 /* MapUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapUITests.swift; sourceTree = ""; }; + B526259625C874FA003E73B7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + B52625A525C876C3003E73B7 /* MapDetail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapDetail.swift; sourceTree = ""; }; + B52625AA25C87909003E73B7 /* Date+format.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+format.swift"; sourceTree = ""; }; + B52625AF25C87C14003E73B7 /* MapRender.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapRender.swift; sourceTree = ""; }; + B52625B525C87D69003E73B7 /* Binding+unwrap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Binding+unwrap.swift"; sourceTree = ""; }; + B52625BA25C884C2003E73B7 /* Map+parse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Map+parse.swift"; sourceTree = ""; }; + B52625C525C8BD2A003E73B7 /* Stage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Stage.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + B526256B25C874F9003E73B7 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B526258225C874FA003E73B7 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B526258D25C874FA003E73B7 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + B526256525C874F9003E73B7 = { + isa = PBXGroup; + children = ( + B526257025C874F9003E73B7 /* Map */, + B526258825C874FA003E73B7 /* MapTests */, + B526259325C874FA003E73B7 /* MapUITests */, + B526256F25C874F9003E73B7 /* Products */, + ); + sourceTree = ""; + }; + B526256F25C874F9003E73B7 /* Products */ = { + isa = PBXGroup; + children = ( + B526256E25C874F9003E73B7 /* Map.app */, + B526258525C874FA003E73B7 /* MapTests.xctest */, + B526259025C874FA003E73B7 /* MapUITests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + B526257025C874F9003E73B7 /* Map */ = { + isa = PBXGroup; + children = ( + B52625B425C87D54003E73B7 /* Extensions */, + B526257125C874F9003E73B7 /* MapApp.swift */, + B526257325C874F9003E73B7 /* ContentView.swift */, + B526257525C874FA003E73B7 /* Assets.xcassets */, + B526257A25C874FA003E73B7 /* Persistence.swift */, + B526257F25C874FA003E73B7 /* Info.plist */, + B526258025C874FA003E73B7 /* Map.entitlements */, + B526257C25C874FA003E73B7 /* Map.xcdatamodeld */, + B526257725C874FA003E73B7 /* Preview Content */, + B52625A525C876C3003E73B7 /* MapDetail.swift */, + B52625AF25C87C14003E73B7 /* MapRender.swift */, + B52625C525C8BD2A003E73B7 /* Stage.swift */, + ); + path = Map; + sourceTree = ""; + }; + B526257725C874FA003E73B7 /* Preview Content */ = { + isa = PBXGroup; + children = ( + B526257825C874FA003E73B7 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + B526258825C874FA003E73B7 /* MapTests */ = { + isa = PBXGroup; + children = ( + B526258925C874FA003E73B7 /* MapTests.swift */, + B526258B25C874FA003E73B7 /* Info.plist */, + ); + path = MapTests; + sourceTree = ""; + }; + B526259325C874FA003E73B7 /* MapUITests */ = { + isa = PBXGroup; + children = ( + B526259425C874FA003E73B7 /* MapUITests.swift */, + B526259625C874FA003E73B7 /* Info.plist */, + ); + path = MapUITests; + sourceTree = ""; + }; + B52625B425C87D54003E73B7 /* Extensions */ = { + isa = PBXGroup; + children = ( + B52625BA25C884C2003E73B7 /* Map+parse.swift */, + B52625AA25C87909003E73B7 /* Date+format.swift */, + B52625B525C87D69003E73B7 /* Binding+unwrap.swift */, + ); + path = Extensions; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + B526256D25C874F9003E73B7 /* Map */ = { + isa = PBXNativeTarget; + buildConfigurationList = B526259925C874FA003E73B7 /* Build configuration list for PBXNativeTarget "Map" */; + buildPhases = ( + B526256A25C874F9003E73B7 /* Sources */, + B526256B25C874F9003E73B7 /* Frameworks */, + B526256C25C874F9003E73B7 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Map; + productName = Map; + productReference = B526256E25C874F9003E73B7 /* Map.app */; + productType = "com.apple.product-type.application"; + }; + B526258425C874FA003E73B7 /* MapTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = B526259C25C874FA003E73B7 /* Build configuration list for PBXNativeTarget "MapTests" */; + buildPhases = ( + B526258125C874FA003E73B7 /* Sources */, + B526258225C874FA003E73B7 /* Frameworks */, + B526258325C874FA003E73B7 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + B526258725C874FA003E73B7 /* PBXTargetDependency */, + ); + name = MapTests; + productName = MapTests; + productReference = B526258525C874FA003E73B7 /* MapTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + B526258F25C874FA003E73B7 /* MapUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = B526259F25C874FA003E73B7 /* Build configuration list for PBXNativeTarget "MapUITests" */; + buildPhases = ( + B526258C25C874FA003E73B7 /* Sources */, + B526258D25C874FA003E73B7 /* Frameworks */, + B526258E25C874FA003E73B7 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + B526259225C874FA003E73B7 /* PBXTargetDependency */, + ); + name = MapUITests; + productName = MapUITests; + productReference = B526259025C874FA003E73B7 /* MapUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + B526256625C874F9003E73B7 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1240; + LastUpgradeCheck = 1240; + TargetAttributes = { + B526256D25C874F9003E73B7 = { + CreatedOnToolsVersion = 12.4; + }; + B526258425C874FA003E73B7 = { + CreatedOnToolsVersion = 12.4; + TestTargetID = B526256D25C874F9003E73B7; + }; + B526258F25C874FA003E73B7 = { + CreatedOnToolsVersion = 12.4; + TestTargetID = B526256D25C874F9003E73B7; + }; + }; + }; + buildConfigurationList = B526256925C874F9003E73B7 /* Build configuration list for PBXProject "Map" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = B526256525C874F9003E73B7; + productRefGroup = B526256F25C874F9003E73B7 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + B526256D25C874F9003E73B7 /* Map */, + B526258425C874FA003E73B7 /* MapTests */, + B526258F25C874FA003E73B7 /* MapUITests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + B526256C25C874F9003E73B7 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B526257925C874FA003E73B7 /* Preview Assets.xcassets in Resources */, + B526257625C874FA003E73B7 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B526258325C874FA003E73B7 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B526258E25C874FA003E73B7 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + B526256A25C874F9003E73B7 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B52625B025C87C14003E73B7 /* MapRender.swift in Sources */, + B52625AB25C87909003E73B7 /* Date+format.swift in Sources */, + B52625B625C87D69003E73B7 /* Binding+unwrap.swift in Sources */, + B52625BB25C884C2003E73B7 /* Map+parse.swift in Sources */, + B52625C625C8BD2A003E73B7 /* Stage.swift in Sources */, + B526257B25C874FA003E73B7 /* Persistence.swift in Sources */, + B526257425C874F9003E73B7 /* ContentView.swift in Sources */, + B526257E25C874FA003E73B7 /* Map.xcdatamodeld in Sources */, + B526257225C874F9003E73B7 /* MapApp.swift in Sources */, + B52625A625C876C3003E73B7 /* MapDetail.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B526258125C874FA003E73B7 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B526258A25C874FA003E73B7 /* MapTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + B526258C25C874FA003E73B7 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + B526259525C874FA003E73B7 /* MapUITests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + B526258725C874FA003E73B7 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = B526256D25C874F9003E73B7 /* Map */; + targetProxy = B526258625C874FA003E73B7 /* PBXContainerItemProxy */; + }; + B526259225C874FA003E73B7 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = B526256D25C874F9003E73B7 /* Map */; + targetProxy = B526259125C874FA003E73B7 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + B526259725C874FA003E73B7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + 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_ENABLE_OBJC_WEAK = 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; + 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; + MACOSX_DEPLOYMENT_TARGET = 11.1; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + B526259825C874FA003E73B7 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + 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_ENABLE_OBJC_WEAK = 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; + 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; + MACOSX_DEPLOYMENT_TARGET = 11.1; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + B526259A25C874FA003E73B7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = Map/Map.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_ASSET_PATHS = "\"Map/Preview Content\""; + DEVELOPMENT_TEAM = S68NHQVJXW; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = Map/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 11.0; + PRODUCT_BUNDLE_IDENTIFIER = pizza.unlimited.Map; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + B526259B25C874FA003E73B7 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = Map/Map.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_ASSET_PATHS = "\"Map/Preview Content\""; + DEVELOPMENT_TEAM = S68NHQVJXW; + ENABLE_HARDENED_RUNTIME = YES; + ENABLE_PREVIEWS = YES; + INFOPLIST_FILE = Map/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 11.0; + PRODUCT_BUNDLE_IDENTIFIER = pizza.unlimited.Map; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + B526259D25C874FA003E73B7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = S68NHQVJXW; + INFOPLIST_FILE = MapTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 11.0; + PRODUCT_BUNDLE_IDENTIFIER = pizza.unlimited.MapTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Map.app/Contents/MacOS/Map"; + }; + name = Debug; + }; + B526259E25C874FA003E73B7 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = S68NHQVJXW; + INFOPLIST_FILE = MapTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 11.0; + PRODUCT_BUNDLE_IDENTIFIER = pizza.unlimited.MapTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Map.app/Contents/MacOS/Map"; + }; + name = Release; + }; + B52625A025C874FA003E73B7 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = S68NHQVJXW; + INFOPLIST_FILE = MapUITests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = pizza.unlimited.MapUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_TARGET_NAME = Map; + }; + name = Debug; + }; + B52625A125C874FA003E73B7 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = S68NHQVJXW; + INFOPLIST_FILE = MapUITests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = pizza.unlimited.MapUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_TARGET_NAME = Map; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + B526256925C874F9003E73B7 /* Build configuration list for PBXProject "Map" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B526259725C874FA003E73B7 /* Debug */, + B526259825C874FA003E73B7 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + B526259925C874FA003E73B7 /* Build configuration list for PBXNativeTarget "Map" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B526259A25C874FA003E73B7 /* Debug */, + B526259B25C874FA003E73B7 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + B526259C25C874FA003E73B7 /* Build configuration list for PBXNativeTarget "MapTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B526259D25C874FA003E73B7 /* Debug */, + B526259E25C874FA003E73B7 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + B526259F25C874FA003E73B7 /* Build configuration list for PBXNativeTarget "MapUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + B52625A025C874FA003E73B7 /* Debug */, + B52625A125C874FA003E73B7 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCVersionGroup section */ + B526257C25C874FA003E73B7 /* Map.xcdatamodeld */ = { + isa = XCVersionGroup; + children = ( + B526257D25C874FA003E73B7 /* Map.xcdatamodel */, + ); + currentVersion = B526257D25C874FA003E73B7 /* Map.xcdatamodel */; + path = Map.xcdatamodeld; + sourceTree = ""; + versionGroupType = wrapper.xcdatamodel; + }; +/* End XCVersionGroup section */ + }; + rootObject = B526256625C874F9003E73B7 /* Project object */; +} diff --git a/Map.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Map.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/Map.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Map.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Map.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/Map.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Map/Assets.xcassets/AccentColor.colorset/Contents.json b/Map/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/Map/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Map/Assets.xcassets/AppIcon.appiconset/Contents.json b/Map/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..3f00db4 --- /dev/null +++ b/Map/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,58 @@ +{ + "images" : [ + { + "idiom" : "mac", + "scale" : "1x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "16x16" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "32x32" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "128x128" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "256x256" + }, + { + "idiom" : "mac", + "scale" : "1x", + "size" : "512x512" + }, + { + "idiom" : "mac", + "scale" : "2x", + "size" : "512x512" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Map/Assets.xcassets/Contents.json b/Map/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Map/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Map/ContentView.swift b/Map/ContentView.swift new file mode 100644 index 0000000..ae5b6cd --- /dev/null +++ b/Map/ContentView.swift @@ -0,0 +1,97 @@ +// +// ContentView.swift +// Map +// +// Created by Ruben Beltran del Rio on 2/1/21. +// + +import SwiftUI +import CoreData + +struct ContentView: View { + @Environment(\.managedObjectContext) private var viewContext + + @FetchRequest( + sortDescriptors: [NSSortDescriptor(keyPath: \Map.createdAt, ascending: true)], + animation: .default) + private var maps: FetchedResults + + var body: some View { + NavigationView { + List { + ForEach(maps) { map in + NavigationLink(destination: MapDetailView(map: map)) { + HStack { + Text(map.title ?? "Untitled Map") + Text(map.createdAt!.format()) + .font(.footnote) + .foregroundColor(Color.accentColor) + } + } + } + .onDelete(perform: deleteMaps) + } + .toolbar { + HStack { + Button(action: toggleSidebar) { + Label("Toggle Sidebar", systemImage: "sidebar.left") + } + Button(action: addMap) { + Label("Add Map", systemImage: "plus") + } + } + } + } + } + + private func toggleSidebar() { + NSApp.keyWindow?.firstResponder?.tryToPerform(#selector(NSSplitViewController.toggleSidebar(_:)), with: nil) + } + + private func addMap() { + withAnimation { + let newMap = Map(context: viewContext) + newMap.uuid = UUID() + newMap.createdAt = Date() + newMap.title = "Map \(newMap.createdAt!.format())" + newMap.content = "" + + do { + try viewContext.save() + } catch { + // Replace this implementation with code to handle the error appropriately. + // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. + let nsError = error as NSError + fatalError("Unresolved error \(nsError), \(nsError.userInfo)") + } + } + } + + private func deleteMaps(offsets: IndexSet) { + withAnimation { + offsets.map { maps[$0] }.forEach(viewContext.delete) + + do { + try viewContext.save() + } catch { + // Replace this implementation with code to handle the error appropriately. + // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. + let nsError = error as NSError + fatalError("Unresolved error \(nsError), \(nsError.userInfo)") + } + } + } +} + +private let mapFormatter: DateFormatter = { + let formatter = DateFormatter() + formatter.dateStyle = .short + formatter.timeStyle = .medium + return formatter +}() + +struct ContentView_Previews: PreviewProvider { + static var previews: some View { + ContentView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext) + } +} diff --git a/Map/Extensions/Binding+unwrap.swift b/Map/Extensions/Binding+unwrap.swift new file mode 100644 index 0000000..3498cc3 --- /dev/null +++ b/Map/Extensions/Binding+unwrap.swift @@ -0,0 +1,12 @@ +import SwiftUI + +extension Binding { + init(_ source: Binding, _ defaultValue: Value) { + // Ensure a non-nil value in `source`. + if source.wrappedValue == nil { + source.wrappedValue = defaultValue + } + // Unsafe unwrap because *we* know it's non-nil now. + self.init(source)! + } +} diff --git a/Map/Extensions/Date+format.swift b/Map/Extensions/Date+format.swift new file mode 100644 index 0000000..c12e67d --- /dev/null +++ b/Map/Extensions/Date+format.swift @@ -0,0 +1,17 @@ +// +// Date+format.swift +// Map +// +// Created by Ruben Beltran del Rio on 2/1/21. +// + +import Foundation + +extension Date { + func format() -> String { + let formatter = DateFormatter() + formatter.dateStyle = .short + formatter.timeStyle = .medium + return formatter.string(from: self) + } +} diff --git a/Map/Extensions/Map+parse.swift b/Map/Extensions/Map+parse.swift new file mode 100644 index 0000000..03e7993 --- /dev/null +++ b/Map/Extensions/Map+parse.swift @@ -0,0 +1,106 @@ +import CoreGraphics +import Foundation + +let VERTEX_PATTERN = "([^\\(]+?)[\\s]*\\([\\s]*([0-9]+.?[0-9]*)[\\s]*,[\\s]*([0-9]+.?[0-9]*)[\\s]*\\)" +let EDGE_PATTERN = "(.+?)[\\s]*->[\\s]*(.+)" + +struct ParsedMap { + let vertices: [Vertex] + let edges: [MapEdge] +} + +struct Vertex { + let id: Int + let label: String + let position: CGPoint +} + +struct MapEdge { + let id: Int + let origin: CGPoint + let destination: CGPoint +} + +// Extracts the vertices from the text + +func parseVertices(_ text: String) -> [String: CGPoint] { + + var result: [String: CGPoint] = [:] + let regex = try! NSRegularExpression(pattern: VERTEX_PATTERN, options: .caseInsensitive) + + let lines = text.split(whereSeparator: \.isNewline) + + for line in lines { + let range = NSRange(location: 0, length: line.utf16.count) + let matches = regex.matches(in: String(line), options: [], range: range) + + if matches.count > 0 && matches[0].numberOfRanges == 4{ + + let match = matches[0]; + let key = String(line[Range(match.range(at: 1), in: line)!]) + let xString = String(line[Range(match.range(at: 2), in: line)!]) + let yString = String(line[Range(match.range(at: 3), in: line)!]) + let x = CGFloat(truncating: NumberFormatter().number(from:xString) ?? 0.0) + let y = CGFloat(truncating: NumberFormatter().number(from:yString) ?? 0.0) + let point = CGPoint(x: x, y: y) + + result[key] = point + } + } + + return result +} + +// Extracts the edges from the text + +func parseEdges(_ text: String, vertices: [String: CGPoint]) -> [MapEdge] { + + var result: [MapEdge] = [] + let regex = try! NSRegularExpression(pattern: EDGE_PATTERN, options: .caseInsensitive) + + let lines = text.split(whereSeparator: \.isNewline) + + for (index, line) in lines.enumerated() { + let range = NSRange(location: 0, length: line.utf16.count) + let matches = regex.matches(in: String(line), options: [], range: range) + + if matches.count > 0 && matches[0].numberOfRanges == 3 { + + let match = matches[0]; + let vertexA = String(line[Range(match.range(at: 1), in: line)!]) + let vertexB = String(line[Range(match.range(at: 2), in: line)!]) + + if let origin = vertices[vertexA] { + if let destination = vertices[vertexB] { + result.append(MapEdge(id: index, origin: origin, destination: destination)) + } + } + } + } + + return result +} + +// Converts vetex dictionary to array + +func mapVertices(_ vertices: [String: CGPoint]) -> [Vertex] { + var i = 0 + return vertices.map { label, position in + i += 1; + return Vertex(id: i, label: label, position: position) + } +} + +extension Map { + func parse() -> ParsedMap { + + let text = self.content ?? "" + let vertices = parseVertices(text) + let mappedVertices = mapVertices(vertices) + let edges = parseEdges(text, vertices: vertices) + print(mappedVertices) + print(edges) + + return ParsedMap(vertices: mappedVertices, edges: edges) + } +} diff --git a/Map/Info.plist b/Map/Info.plist new file mode 100644 index 0000000..69c84ae --- /dev/null +++ b/Map/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + + diff --git a/Map/Map.entitlements b/Map/Map.entitlements new file mode 100644 index 0000000..f2ef3ae --- /dev/null +++ b/Map/Map.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.app-sandbox + + com.apple.security.files.user-selected.read-only + + + diff --git a/Map/Map.xcdatamodeld/.xccurrentversion b/Map/Map.xcdatamodeld/.xccurrentversion new file mode 100644 index 0000000..ee72e60 --- /dev/null +++ b/Map/Map.xcdatamodeld/.xccurrentversion @@ -0,0 +1,8 @@ + + + + + _XCCurrentVersionName + Map.xcdatamodel + + diff --git a/Map/Map.xcdatamodeld/Map.xcdatamodel/contents b/Map/Map.xcdatamodeld/Map.xcdatamodel/contents new file mode 100644 index 0000000..ea8b276 --- /dev/null +++ b/Map/Map.xcdatamodeld/Map.xcdatamodel/contents @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/Map/MapApp.swift b/Map/MapApp.swift new file mode 100644 index 0000000..10b6bcb --- /dev/null +++ b/Map/MapApp.swift @@ -0,0 +1,20 @@ +// +// MapApp.swift +// Map +// +// Created by Ruben Beltran del Rio on 2/1/21. +// + +import SwiftUI + +@main +struct MapApp: App { + let persistenceController = PersistenceController.shared + + var body: some Scene { + WindowGroup { + ContentView() + .environment(\.managedObjectContext, persistenceController.container.viewContext) + } + } +} diff --git a/Map/MapDetail.swift b/Map/MapDetail.swift new file mode 100644 index 0000000..74e3c8d --- /dev/null +++ b/Map/MapDetail.swift @@ -0,0 +1,44 @@ +// +// ContentView.swift +// Map +// +// Created by Ruben Beltran del Rio on 2/1/21. +// + +import SwiftUI +import CoreData + +struct MapDetailView: View { + @Environment(\.managedObjectContext) private var viewContext + + @ObservedObject var map: Map + + @State private var selectedEvolution = StageType.General + + var body: some View { + VSplitView { + VStack { + TextField("Title", text: Binding($map.title, ""), onCommit: { + try? viewContext.save() + }) + Picker("Evolution", selection: $selectedEvolution) { + ForEach(StageType.allCases) { stage in + Text(Stage.title(stage)).tag(stage) + } + } + TextEditor(text: Binding($map.content, "")).onChange(of: map.content) { _ in + try? viewContext.save() + }.font(Font.system(size: 16, design: .monospaced)) + } + ScrollView([.horizontal, .vertical]) { + MapRenderView(map: map, evolution: Stage.stages(selectedEvolution)) + } + } + } +} + +struct MapDetailView_Previews: PreviewProvider { + static var previews: some View { + MapDetailView(map: Map()).environment(\.managedObjectContext, PersistenceController.preview.container.viewContext) + } +} diff --git a/Map/MapRender.swift b/Map/MapRender.swift new file mode 100644 index 0000000..79d80b5 --- /dev/null +++ b/Map/MapRender.swift @@ -0,0 +1,125 @@ +// +// ContentView.swift +// Map +// +// Created by Ruben Beltran del Rio on 2/1/21. +// + +import SwiftUI +import CoreData +import CoreGraphics + +struct MapRenderView: View { + @ObservedObject var map: Map + let evolution: Stage + + let VERTEX_SIZE = CGSize(width: 25.0, height: 25.0) + let ARROWHEAD_SIZE = CGFloat(10.0) + let LINE_WIDTH = CGFloat(1.0) + let PADDING = CGFloat(4.0) + let STAGE_FRAME = CGSize(width: 245, height: 50) + + var parsedMap: ParsedMap { + return map.parse() + } + + var body: some View { + ZStack(alignment: .topLeading) { + ZStack (alignment: .topLeading) { + // The Axes + Path { path in + path.move(to: CGPoint(x: 0, y: 0)) + path.addLine(to: CGPoint(x: 0, y: 1000)) + path.addLine(to: CGPoint(x: 1000, y: 1000)) + path.move(to: CGPoint(x: 1000, y: 1000)) + path.closeSubpath() + + }.strokedPath(StrokeStyle(lineWidth: LINE_WIDTH * 2)) + Text("Visible").font(.title3).rotationEffect(Angle(degrees: -90.0)).offset(CGSize(width: -35.0, height: 0.0)) + Text("Value Chain").font(.title).rotationEffect(Angle(degrees: -90.0)).offset(CGSize(width: -72.0, height: 480.0)) + Text("Invisible").font(.title3).rotationEffect(Angle(degrees: -90.0)).offset(CGSize(width: -40.0, height: 980.0)) + Text(evolution.I).font(.title3).frame(width: STAGE_FRAME.width, height: STAGE_FRAME.height, alignment: .topLeading).offset(CGSize(width: 0.0, height: 1000.0)) + Text(evolution.II).font(.title3).frame(width: STAGE_FRAME.width, height: STAGE_FRAME.height, alignment: .topLeading).offset(CGSize(width: 250.0, height: 1000.0)) + Text(evolution.III).font(.title3).frame(width: STAGE_FRAME.width, height: STAGE_FRAME.height, alignment: .topLeading).offset(CGSize(width: 500.0, height: 1000.0)) + Text(evolution.IV).font(.title3).frame(width: STAGE_FRAME.width, height: STAGE_FRAME.height, alignment: .topLeading).offset(CGSize(width: 750.0, height: 1000.0)) + }.offset(CGSize(width: 0.0, height: 0.0)) + + // The Lanes + Path { path in + path.move(to: CGPoint(x: 250, y: 0)) + path.addLine(to: CGPoint(x: 250, y: 1000)) + path.move(to: CGPoint(x: 500, y: 0)) + path.addLine(to: CGPoint(x: 500, y: 1000)) + path.move(to: CGPoint(x: 750, y: 0)) + path.addLine(to: CGPoint(x: 750, y: 1000)) + path.move(to: CGPoint(x: 250, y: 0)) + path.closeSubpath() + }.strokedPath(StrokeStyle(lineWidth: LINE_WIDTH, dash: [10.0])) + + Path { path in + path.addRect(CGRect(x: 0, y: 0, width: 250, height: 1000)) + }.fill(Color.red).opacity(0.1) + Path { path in + path.addRect(CGRect(x: 250, y: 0, width: 250, height: 1000)) + }.fill(Color.orange).opacity(0.1) + Path { path in + path.addRect(CGRect(x: 500, y: 0, width: 250, height: 1000)) + }.fill(Color.yellow).opacity(0.1) + Path { path in + path.addRect(CGRect(x: 750, y: 0, width: 250, height: 1000)) + }.fill(Color.green).opacity(0.1) + + // The Vertices + + ForEach(parsedMap.vertices, id: \.id) { vertex in + Path { path in + path.addEllipse(in: CGRect( + origin: vertex.position, size: VERTEX_SIZE + )) + } + Text(vertex.label).foregroundColor(Color.gray).offset(CGSize( + width: vertex.position.x + VERTEX_SIZE.width + PADDING, + height: vertex.position.y)) + } + + // The Edges + + ForEach(parsedMap.edges, id: \.id) { edge in + Path { path in + + let slope = (edge.destination.y - edge.origin.y) / (edge.destination.x - edge.origin.x) + let angle = atan(slope) + let multiplier = CGFloat(slope < 0 ? -1.0 : 1.0) + let upperAngle = angle - CGFloat.pi / 4.0 + let lowerAngle = angle + CGFloat.pi / 4.0 + + let offsetOrigin = CGPoint(x: edge.origin.x + multiplier * (VERTEX_SIZE.width / 2.0) * cos(angle), y: edge.origin.y + multiplier * (VERTEX_SIZE.height / 2.0) * sin(angle)) + let offsetDestination = CGPoint(x: edge.destination.x - multiplier * (VERTEX_SIZE.width / 2.0) * cos(angle), y: edge.destination.y - multiplier * (VERTEX_SIZE.height / 2.0) * sin(angle)) + + path.move(to: offsetOrigin) + path.addLine(to: offsetDestination) + + // Arrowheads + path.move(to: offsetDestination) + path.addLine(to: CGPoint(x: offsetDestination.x - multiplier * ARROWHEAD_SIZE * cos(upperAngle), y: + offsetDestination.y - multiplier * ARROWHEAD_SIZE * sin(upperAngle))) + + path.move(to: offsetDestination) + path.addLine(to: CGPoint(x: offsetDestination.x - multiplier * ARROWHEAD_SIZE * cos(lowerAngle), y: + offsetDestination.y - multiplier * ARROWHEAD_SIZE * sin(lowerAngle))) + path.move(to: offsetDestination) + path.closeSubpath() + }.applying(CGAffineTransform(translationX: VERTEX_SIZE.width / 2.0, y: VERTEX_SIZE.height / 2.0)).strokedPath(StrokeStyle(lineWidth: LINE_WIDTH)) + } + }.frame( + minWidth: 1050.0, maxWidth: .infinity, + minHeight: 1050.0, maxHeight: .infinity, alignment: .topLeading + ).padding(30.0) + } +} + +struct MapRenderView_Previews: PreviewProvider { + static var previews: some View { + MapDetailView(map: Map()).environment(\.managedObjectContext, PersistenceController.preview.container.viewContext) + } +} diff --git a/Map/Persistence.swift b/Map/Persistence.swift new file mode 100644 index 0000000..49bb2d9 --- /dev/null +++ b/Map/Persistence.swift @@ -0,0 +1,58 @@ +// +// Persistence.swift +// Map +// +// Created by Ruben Beltran del Rio on 2/1/21. +// + +import CoreData + +struct PersistenceController { + static let shared = PersistenceController() + + static var preview: PersistenceController = { + let result = PersistenceController(inMemory: true) + let viewContext = result.container.viewContext + for _ in 0..<10 { + let newMap = Map(context: viewContext) + newMap.uuid = UUID() + newMap.createdAt = Date() + newMap.title = "Map \(newMap.createdAt!.format())" + newMap.content = "" + } + do { + try viewContext.save() + } catch { + // Replace this implementation with code to handle the error appropriately. + // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. + let nsError = error as NSError + fatalError("Unresolved error \(nsError), \(nsError.userInfo)") + } + return result + }() + + let container: NSPersistentCloudKitContainer + + init(inMemory: Bool = false) { + container = NSPersistentCloudKitContainer(name: "Map") + if inMemory { + container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null") + } + container.loadPersistentStores(completionHandler: { (storeDescription, error) in + if let error = error as NSError? { + // Replace this implementation with code to handle the error appropriately. + // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. + + /* + Typical reasons for an error here include: + * The parent directory does not exist, cannot be created, or disallows writing. + * The persistent store is not accessible, due to permissions or data protection when the device is locked. + * The device is out of space. + * The store could not be migrated to the current model version. + Check the error message to determine what the actual problem was. + */ + fatalError("Unresolved error \(error), \(error.userInfo)") + } + }) + } +} diff --git a/Map/Preview Content/Preview Assets.xcassets/Contents.json b/Map/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Map/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Map/Stage.swift b/Map/Stage.swift new file mode 100644 index 0000000..4a53ada --- /dev/null +++ b/Map/Stage.swift @@ -0,0 +1,108 @@ +struct Stage { + let I: String + let II: String + let III: String + let IV: String + + static func stages(_ type: StageType) -> Stage { + switch(type) { + case .General: + return Stage(I: "Genesis", II: "Custom Built", III: "Product (+rental)", IV: "Commodity (+utility)") + case .Ubiquity: + return Stage(I: "Rare", II: "Slowly Increasing Consumption", III: "Rapidly Increasing Consumption", IV: "Widespread and stabilising") + case .Certainty: + return Stage(I: "Poorly Understood", II: "Rapid Increase In Learning", III: "Rapid Increase in Use / fit for purpose", IV: "Commonly understood (in terms of use)") + case .PublicationTypes: + return Stage(I: "Normally describing the wonder of the thing", II: "Build / construct / awareness and learning", III: "Maintenance / operations / installation / feature", IV: "Focused on use") + case .Market: + return Stage(I: "Undefined Market", II: "Forming Market", III: "Growing Market", IV: "Mature Market") + case .KnowledgeManagement: + return Stage(I: "Uncertain", II: "Learning on use", III: "Learning on operation", IV: "Known / accepted") + case .MarketPerception: + return Stage(I: "Chaotic (non-linear)", II: "Domain of experts", III: "Increasing expectation of use", IV: "Ordered (appearance of being trivial) / trivial") + case .UserPerception: + return Stage(I: "Different / confusing / exciting / surprising", II: "Leading edge / emerging", III: "Increasingly common / disappointed if not used", IV: "Standard / expected") + case .PerceptionInIndustry: + return Stage(I: "Competitive advantage / unpredictable / unknown", II: "Competitive advantage / ROI / case examples", III: "Advantage through implementation / features", IV: "Cost of doing business") + case .FocusOfValue: + return Stage(I: "High future worth", II: "Seeking profit / ROI", III: "High profitability", IV: "High volume / reducing margin") + case .Understanding: + return Stage(I: "Poorly Understood / unpredictable", II: "Increasing understanding / development of measures", III: "Increasing education / constant refinement of needs / measures", IV: "Believed to be well defined / stable / measurable") + case .Comparison: + return Stage(I: "Constantly changing / a differential / unstable", II: "Learning from others / testing the water / some evidential support", III: "Feature difference", IV: "Essential / operational advantage") + case .Failure: + return Stage(I: "High / tolerated / assumed", II: "Moderate / unsurprising but disappointed", III: "Not tolerated, focus on constant improvement", IV: "Operational efficiency and surprised by failure") + case .MarketAction: + return Stage(I: "Gambling / driven by gut", II: "Exploring a \"found\" value", III: "Market analysis / listening to customers", IV: "Metric driven / build what is needed") + case .Efficiency: + return Stage(I: "Reducing the cost of change (experimentation)", II: "Reducing cost of waste (Learning)", III: "Reducing cost of waste (Learning)", IV: "Reducing cost of deviation (Volume)") + case .DecisionDrivers: + return Stage(I: "Heritage / culture", II: "Analyses & synthesis", III: "Analyses & synthesis", IV: "Previous Experience") + case .Behavior: + return Stage(I: "Needs to be pushed", II: "Shown in simple situations", III: "Shown in moderately complex situations ", IV: "Always shown") + } + } + + static func title(_ type: StageType) -> String { + switch(type) { + case .General: + return "General" + case .Ubiquity: + return "Ubiquity" + case .Certainty: + return "Certainty" + case .PublicationTypes: + return "Publication Types" + case .Market: + return "Market" + case .KnowledgeManagement: + return "Knowledge Management" + case .MarketPerception: + return "Market Perception" + case .UserPerception: + return "User Perception" + case .PerceptionInIndustry: + return "Perception In Industry" + case .FocusOfValue: + return "Focus Of Value" + case .Understanding: + return "Understanding" + case .Comparison: + return "Comparison" + case .Failure: + return "Failure" + case .MarketAction: + return "Market Action" + case .Efficiency: + return "Efficiency" + case .DecisionDrivers: + return "Decision Drivers" + case .Behavior: + return "Behavior" + } + } +} + +enum StageType: String, CaseIterable, Identifiable { + case General + case Ubiquity + case Certainty + case PublicationTypes + case Market + case KnowledgeManagement + case MarketPerception + case UserPerception + case PerceptionInIndustry + case FocusOfValue + case Understanding + case Comparison + case Failure + case MarketAction + case Efficiency + case DecisionDrivers + case Behavior + + var id: String { self.rawValue } +} + + diff --git a/MapTests/Info.plist b/MapTests/Info.plist new file mode 100644 index 0000000..64d65ca --- /dev/null +++ b/MapTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/MapTests/MapTests.swift b/MapTests/MapTests.swift new file mode 100644 index 0000000..4f2a183 --- /dev/null +++ b/MapTests/MapTests.swift @@ -0,0 +1,33 @@ +// +// MapTests.swift +// MapTests +// +// Created by Ruben Beltran del Rio on 2/1/21. +// + +import XCTest +@testable import Map + +class MapTests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testPerformanceExample() throws { + // This is an example of a performance test case. + self.measure { + // Put the code you want to measure the time of here. + } + } + +} diff --git a/MapUITests/Info.plist b/MapUITests/Info.plist new file mode 100644 index 0000000..64d65ca --- /dev/null +++ b/MapUITests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/MapUITests/MapUITests.swift b/MapUITests/MapUITests.swift new file mode 100644 index 0000000..ee1eeca --- /dev/null +++ b/MapUITests/MapUITests.swift @@ -0,0 +1,42 @@ +// +// MapUITests.swift +// MapUITests +// +// Created by Ruben Beltran del Rio on 2/1/21. +// + +import XCTest + +class MapUITests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + + // In UI tests it is usually best to stop immediately when a failure occurs. + continueAfterFailure = false + + // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // UI tests must launch the application that they test. + let app = XCUIApplication() + app.launch() + + // Use recording to get started writing UI tests. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testLaunchPerformance() throws { + if #available(macOS 10.15, iOS 13.0, tvOS 13.0, *) { + // This measures how long it takes to launch your application. + measure(metrics: [XCTApplicationLaunchMetric()]) { + XCUIApplication().launch() + } + } + } +}