diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bfa0a1f --- /dev/null +++ b/.gitignore @@ -0,0 +1,29 @@ +# OS X +.DS_Store + +# Xcode +build/ +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata +*.xccheckout +profile +*.moved-aside +DerivedData +*.hmap +*.ipa +*.xcuserdata +*.xcuserstate + +# CocoaPods +Pods +.idea + +/Docs/ +report.xml diff --git a/CatchTestSam/CatchTestSam.xcodeproj/project.pbxproj b/CatchTestSam/CatchTestSam.xcodeproj/project.pbxproj new file mode 100644 index 0000000..13482ab --- /dev/null +++ b/CatchTestSam/CatchTestSam.xcodeproj/project.pbxproj @@ -0,0 +1,663 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXBuildFile section */ + 02F66CA7E0B618EC2F944C38 /* Pods_CatchTestSam.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3373FAB84A04592636481986 /* Pods_CatchTestSam.framework */; }; + 1719E81D24CC129A00BAD2B8 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1719E81C24CC129A00BAD2B8 /* AppDelegate.swift */; }; + 1719E81F24CC129A00BAD2B8 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1719E81E24CC129A00BAD2B8 /* SceneDelegate.swift */; }; + 1719E82424CC129A00BAD2B8 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1719E82224CC129A00BAD2B8 /* Main.storyboard */; }; + 1719E82624CC129C00BAD2B8 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1719E82524CC129C00BAD2B8 /* Assets.xcassets */; }; + 1719E82924CC129C00BAD2B8 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1719E82724CC129C00BAD2B8 /* LaunchScreen.storyboard */; }; + 1719E83424CC129C00BAD2B8 /* CatchTestSamTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1719E83324CC129C00BAD2B8 /* CatchTestSamTests.swift */; }; + 1719E83F24CC19D200BAD2B8 /* ListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1719E83E24CC19D200BAD2B8 /* ListViewController.swift */; }; + 1719E84124CC1DA300BAD2B8 /* NavController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1719E84024CC1DA300BAD2B8 /* NavController.swift */; }; + 1719E84324CC57D200BAD2B8 /* SpinnerAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1719E84224CC57D200BAD2B8 /* SpinnerAnimator.swift */; }; + 1719E84724CC7EEA00BAD2B8 /* RecordCollection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1719E84624CC7EEA00BAD2B8 /* RecordCollection.swift */; }; + 1719E84924CC7F1B00BAD2B8 /* Record.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1719E84824CC7F1B00BAD2B8 /* Record.swift */; }; + 1719E84F24CC8BFF00BAD2B8 /* ListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1719E84E24CC8BFF00BAD2B8 /* ListCell.swift */; }; + 1719E85224CC8DF200BAD2B8 /* ContentViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1719E85124CC8DF200BAD2B8 /* ContentViewController.swift */; }; + 1719E85424CCA67800BAD2B8 /* data.json in Resources */ = {isa = PBXBuildFile; fileRef = 1719E85324CCA67800BAD2B8 /* data.json */; }; + 86B9D2383A87C361B2FC2FB5 /* Pods_CatchTestSam_CatchTestSamTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D132563F0256775B9946A3B7 /* Pods_CatchTestSam_CatchTestSamTests.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 1719E83024CC129C00BAD2B8 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 1719E81124CC129A00BAD2B8 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 1719E81824CC129A00BAD2B8; + remoteInfo = CatchTestSam; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 1719E81924CC129A00BAD2B8 /* CatchTestSam.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CatchTestSam.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 1719E81C24CC129A00BAD2B8 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 1719E81E24CC129A00BAD2B8 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + 1719E82324CC129A00BAD2B8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 1719E82524CC129C00BAD2B8 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 1719E82824CC129C00BAD2B8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 1719E82A24CC129C00BAD2B8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 1719E82F24CC129C00BAD2B8 /* CatchTestSamTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CatchTestSamTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 1719E83324CC129C00BAD2B8 /* CatchTestSamTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CatchTestSamTests.swift; sourceTree = ""; }; + 1719E83524CC129C00BAD2B8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 1719E83E24CC19D200BAD2B8 /* ListViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListViewController.swift; sourceTree = ""; }; + 1719E84024CC1DA300BAD2B8 /* NavController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavController.swift; sourceTree = ""; }; + 1719E84224CC57D200BAD2B8 /* SpinnerAnimator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpinnerAnimator.swift; sourceTree = ""; }; + 1719E84624CC7EEA00BAD2B8 /* RecordCollection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecordCollection.swift; sourceTree = ""; }; + 1719E84824CC7F1B00BAD2B8 /* Record.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Record.swift; sourceTree = ""; }; + 1719E84E24CC8BFF00BAD2B8 /* ListCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListCell.swift; sourceTree = ""; }; + 1719E85124CC8DF200BAD2B8 /* ContentViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentViewController.swift; sourceTree = ""; }; + 1719E85324CCA67800BAD2B8 /* data.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = data.json; path = ../../data/data.json; sourceTree = ""; }; + 3373FAB84A04592636481986 /* Pods_CatchTestSam.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_CatchTestSam.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 4EF20C84B09E3EE0DDD9218C /* Pods-CatchTestSam.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CatchTestSam.release.xcconfig"; path = "Target Support Files/Pods-CatchTestSam/Pods-CatchTestSam.release.xcconfig"; sourceTree = ""; }; + 7F64B585860EAC6A89C88C25 /* Pods-CatchTestSam-CatchTestSamTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CatchTestSam-CatchTestSamTests.debug.xcconfig"; path = "Target Support Files/Pods-CatchTestSam-CatchTestSamTests/Pods-CatchTestSam-CatchTestSamTests.debug.xcconfig"; sourceTree = ""; }; + B2A5A9246AC3E1F3DAFC4DAC /* Pods-CatchTestSam-CatchTestSamTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CatchTestSam-CatchTestSamTests.release.xcconfig"; path = "Target Support Files/Pods-CatchTestSam-CatchTestSamTests/Pods-CatchTestSam-CatchTestSamTests.release.xcconfig"; sourceTree = ""; }; + B2E6CD030A2EEC7080DDF6E8 /* Pods-CatchTestSam.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CatchTestSam.debug.xcconfig"; path = "Target Support Files/Pods-CatchTestSam/Pods-CatchTestSam.debug.xcconfig"; sourceTree = ""; }; + D132563F0256775B9946A3B7 /* Pods_CatchTestSam_CatchTestSamTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_CatchTestSam_CatchTestSamTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 1719E81624CC129A00BAD2B8 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 02F66CA7E0B618EC2F944C38 /* Pods_CatchTestSam.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 1719E82C24CC129C00BAD2B8 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 86B9D2383A87C361B2FC2FB5 /* Pods_CatchTestSam_CatchTestSamTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 1719E81024CC129A00BAD2B8 = { + isa = PBXGroup; + children = ( + 1719E81B24CC129A00BAD2B8 /* CatchTestSam */, + 1719E83224CC129C00BAD2B8 /* CatchTestSamTests */, + 1719E81A24CC129A00BAD2B8 /* Products */, + A92D916B4EF45C1E120C8047 /* Pods */, + 54FA3F16E9B4DB13C465FA3C /* Frameworks */, + ); + sourceTree = ""; + }; + 1719E81A24CC129A00BAD2B8 /* Products */ = { + isa = PBXGroup; + children = ( + 1719E81924CC129A00BAD2B8 /* CatchTestSam.app */, + 1719E82F24CC129C00BAD2B8 /* CatchTestSamTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 1719E81B24CC129A00BAD2B8 /* CatchTestSam */ = { + isa = PBXGroup; + children = ( + 1719E85524CCB0C900BAD2B8 /* Root */, + 1719E85624CCB0E300BAD2B8 /* Resources */, + 1719E84B24CC8B9900BAD2B8 /* List */, + 1719E85024CC8DC600BAD2B8 /* Content */, + 1719E84A24CC7F2000BAD2B8 /* Records */, + ); + path = CatchTestSam; + sourceTree = ""; + }; + 1719E83224CC129C00BAD2B8 /* CatchTestSamTests */ = { + isa = PBXGroup; + children = ( + 1719E85324CCA67800BAD2B8 /* data.json */, + 1719E83324CC129C00BAD2B8 /* CatchTestSamTests.swift */, + 1719E83524CC129C00BAD2B8 /* Info.plist */, + ); + path = CatchTestSamTests; + sourceTree = ""; + }; + 1719E84A24CC7F2000BAD2B8 /* Records */ = { + isa = PBXGroup; + children = ( + 1719E84624CC7EEA00BAD2B8 /* RecordCollection.swift */, + 1719E84824CC7F1B00BAD2B8 /* Record.swift */, + ); + path = Records; + sourceTree = ""; + }; + 1719E84B24CC8B9900BAD2B8 /* List */ = { + isa = PBXGroup; + children = ( + 1719E83E24CC19D200BAD2B8 /* ListViewController.swift */, + 1719E84E24CC8BFF00BAD2B8 /* ListCell.swift */, + 1719E84224CC57D200BAD2B8 /* SpinnerAnimator.swift */, + ); + path = List; + sourceTree = ""; + }; + 1719E85024CC8DC600BAD2B8 /* Content */ = { + isa = PBXGroup; + children = ( + 1719E85124CC8DF200BAD2B8 /* ContentViewController.swift */, + ); + path = Content; + sourceTree = ""; + }; + 1719E85524CCB0C900BAD2B8 /* Root */ = { + isa = PBXGroup; + children = ( + 1719E81C24CC129A00BAD2B8 /* AppDelegate.swift */, + 1719E81E24CC129A00BAD2B8 /* SceneDelegate.swift */, + 1719E84024CC1DA300BAD2B8 /* NavController.swift */, + ); + path = Root; + sourceTree = ""; + }; + 1719E85624CCB0E300BAD2B8 /* Resources */ = { + isa = PBXGroup; + children = ( + 1719E82A24CC129C00BAD2B8 /* Info.plist */, + 1719E82524CC129C00BAD2B8 /* Assets.xcassets */, + 1719E82224CC129A00BAD2B8 /* Main.storyboard */, + 1719E82724CC129C00BAD2B8 /* LaunchScreen.storyboard */, + ); + path = Resources; + sourceTree = ""; + }; + 54FA3F16E9B4DB13C465FA3C /* Frameworks */ = { + isa = PBXGroup; + children = ( + 3373FAB84A04592636481986 /* Pods_CatchTestSam.framework */, + D132563F0256775B9946A3B7 /* Pods_CatchTestSam_CatchTestSamTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + A92D916B4EF45C1E120C8047 /* Pods */ = { + isa = PBXGroup; + children = ( + B2E6CD030A2EEC7080DDF6E8 /* Pods-CatchTestSam.debug.xcconfig */, + 4EF20C84B09E3EE0DDD9218C /* Pods-CatchTestSam.release.xcconfig */, + 7F64B585860EAC6A89C88C25 /* Pods-CatchTestSam-CatchTestSamTests.debug.xcconfig */, + B2A5A9246AC3E1F3DAFC4DAC /* Pods-CatchTestSam-CatchTestSamTests.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 1719E81824CC129A00BAD2B8 /* CatchTestSam */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1719E83824CC129C00BAD2B8 /* Build configuration list for PBXNativeTarget "CatchTestSam" */; + buildPhases = ( + F52D4A2D10869B9622FBE11B /* [CP] Check Pods Manifest.lock */, + 1719E81524CC129A00BAD2B8 /* Sources */, + 1719E81624CC129A00BAD2B8 /* Frameworks */, + 1719E81724CC129A00BAD2B8 /* Resources */, + DD9CFCAEF4BF80C47D868911 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = CatchTestSam; + productName = CatchTestSam; + productReference = 1719E81924CC129A00BAD2B8 /* CatchTestSam.app */; + productType = "com.apple.product-type.application"; + }; + 1719E82E24CC129C00BAD2B8 /* CatchTestSamTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1719E83B24CC129C00BAD2B8 /* Build configuration list for PBXNativeTarget "CatchTestSamTests" */; + buildPhases = ( + 70000E67363AEFD257DD5552 /* [CP] Check Pods Manifest.lock */, + 1719E82B24CC129C00BAD2B8 /* Sources */, + 1719E82C24CC129C00BAD2B8 /* Frameworks */, + 1719E82D24CC129C00BAD2B8 /* Resources */, + A8833EAF5500F7812EF3F9A7 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 1719E83124CC129C00BAD2B8 /* PBXTargetDependency */, + ); + name = CatchTestSamTests; + productName = CatchTestSamTests; + productReference = 1719E82F24CC129C00BAD2B8 /* CatchTestSamTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 1719E81124CC129A00BAD2B8 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1140; + LastUpgradeCheck = 1140; + ORGANIZATIONNAME = smoriyasu; + TargetAttributes = { + 1719E81824CC129A00BAD2B8 = { + CreatedOnToolsVersion = 11.4.1; + }; + 1719E82E24CC129C00BAD2B8 = { + CreatedOnToolsVersion = 11.4.1; + TestTargetID = 1719E81824CC129A00BAD2B8; + }; + }; + }; + buildConfigurationList = 1719E81424CC129A00BAD2B8 /* Build configuration list for PBXProject "CatchTestSam" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 1719E81024CC129A00BAD2B8; + productRefGroup = 1719E81A24CC129A00BAD2B8 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 1719E81824CC129A00BAD2B8 /* CatchTestSam */, + 1719E82E24CC129C00BAD2B8 /* CatchTestSamTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 1719E81724CC129A00BAD2B8 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1719E82924CC129C00BAD2B8 /* LaunchScreen.storyboard in Resources */, + 1719E82624CC129C00BAD2B8 /* Assets.xcassets in Resources */, + 1719E82424CC129A00BAD2B8 /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 1719E82D24CC129C00BAD2B8 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1719E85424CCA67800BAD2B8 /* data.json in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 70000E67363AEFD257DD5552 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-CatchTestSam-CatchTestSamTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + A8833EAF5500F7812EF3F9A7 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-CatchTestSam-CatchTestSamTests/Pods-CatchTestSam-CatchTestSamTests-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-CatchTestSam-CatchTestSamTests/Pods-CatchTestSam-CatchTestSamTests-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-CatchTestSam-CatchTestSamTests/Pods-CatchTestSam-CatchTestSamTests-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + DD9CFCAEF4BF80C47D868911 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-CatchTestSam/Pods-CatchTestSam-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-CatchTestSam/Pods-CatchTestSam-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-CatchTestSam/Pods-CatchTestSam-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + F52D4A2D10869B9622FBE11B /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-CatchTestSam-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 1719E81524CC129A00BAD2B8 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1719E84124CC1DA300BAD2B8 /* NavController.swift in Sources */, + 1719E84324CC57D200BAD2B8 /* SpinnerAnimator.swift in Sources */, + 1719E81D24CC129A00BAD2B8 /* AppDelegate.swift in Sources */, + 1719E83F24CC19D200BAD2B8 /* ListViewController.swift in Sources */, + 1719E85224CC8DF200BAD2B8 /* ContentViewController.swift in Sources */, + 1719E84724CC7EEA00BAD2B8 /* RecordCollection.swift in Sources */, + 1719E84F24CC8BFF00BAD2B8 /* ListCell.swift in Sources */, + 1719E84924CC7F1B00BAD2B8 /* Record.swift in Sources */, + 1719E81F24CC129A00BAD2B8 /* SceneDelegate.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 1719E82B24CC129C00BAD2B8 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1719E83424CC129C00BAD2B8 /* CatchTestSamTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 1719E83124CC129C00BAD2B8 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 1719E81824CC129A00BAD2B8 /* CatchTestSam */; + targetProxy = 1719E83024CC129C00BAD2B8 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 1719E82224CC129A00BAD2B8 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 1719E82324CC129A00BAD2B8 /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 1719E82724CC129C00BAD2B8 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 1719E82824CC129C00BAD2B8 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 1719E83624CC129C00BAD2B8 /* 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_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; + IPHONEOS_DEPLOYMENT_TARGET = 13.4; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 1719E83724CC129C00BAD2B8 /* 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_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; + IPHONEOS_DEPLOYMENT_TARGET = 13.4; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 1719E83924CC129C00BAD2B8 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = B2E6CD030A2EEC7080DDF6E8 /* Pods-CatchTestSam.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = S8Q9MXVC48; + INFOPLIST_FILE = CatchTestSam/Resources/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = nz.co.smoriyasu.CatchTestSam; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 1719E83A24CC129C00BAD2B8 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 4EF20C84B09E3EE0DDD9218C /* Pods-CatchTestSam.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = S8Q9MXVC48; + INFOPLIST_FILE = CatchTestSam/Resources/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = nz.co.smoriyasu.CatchTestSam; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 1719E83C24CC129C00BAD2B8 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7F64B585860EAC6A89C88C25 /* Pods-CatchTestSam-CatchTestSamTests.debug.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = S8Q9MXVC48; + INFOPLIST_FILE = CatchTestSamTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.4; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = nz.co.smoriyasu.CatchTestSamTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/CatchTestSam.app/CatchTestSam"; + }; + name = Debug; + }; + 1719E83D24CC129C00BAD2B8 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = B2A5A9246AC3E1F3DAFC4DAC /* Pods-CatchTestSam-CatchTestSamTests.release.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = S8Q9MXVC48; + INFOPLIST_FILE = CatchTestSamTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.4; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = nz.co.smoriyasu.CatchTestSamTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/CatchTestSam.app/CatchTestSam"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1719E81424CC129A00BAD2B8 /* Build configuration list for PBXProject "CatchTestSam" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1719E83624CC129C00BAD2B8 /* Debug */, + 1719E83724CC129C00BAD2B8 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1719E83824CC129C00BAD2B8 /* Build configuration list for PBXNativeTarget "CatchTestSam" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1719E83924CC129C00BAD2B8 /* Debug */, + 1719E83A24CC129C00BAD2B8 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1719E83B24CC129C00BAD2B8 /* Build configuration list for PBXNativeTarget "CatchTestSamTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1719E83C24CC129C00BAD2B8 /* Debug */, + 1719E83D24CC129C00BAD2B8 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 1719E81124CC129A00BAD2B8 /* Project object */; +} diff --git a/CatchTestSam/CatchTestSam.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/CatchTestSam/CatchTestSam.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..b2c566f --- /dev/null +++ b/CatchTestSam/CatchTestSam.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/CatchTestSam/CatchTestSam.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/CatchTestSam/CatchTestSam.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/CatchTestSam/CatchTestSam.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/CatchTestSam/CatchTestSam.xcworkspace/contents.xcworkspacedata b/CatchTestSam/CatchTestSam.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..b392137 --- /dev/null +++ b/CatchTestSam/CatchTestSam.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/CatchTestSam/CatchTestSam.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/CatchTestSam/CatchTestSam.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/CatchTestSam/CatchTestSam.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/CatchTestSam/CatchTestSam/Content/ContentViewController.swift b/CatchTestSam/CatchTestSam/Content/ContentViewController.swift new file mode 100644 index 0000000..80eb780 --- /dev/null +++ b/CatchTestSam/CatchTestSam/Content/ContentViewController.swift @@ -0,0 +1,24 @@ +// +// ContentViewController.swift +// CatchTestSam +// +// Created by smoriyasu on 26/07/20. +// Copyright © 2020 smoriyasu. All rights reserved. +// + +import UIKit + +class ContentViewController: UIViewController { + + var record : Record?; + + @IBOutlet var contentTextView: UITextView! + + override func viewDidLoad() { + super.viewDidLoad() + + // Do any additional setup after loading the view. + contentTextView.text = record?.content; + title = record?.title; + } +} diff --git a/CatchTestSam/CatchTestSam/List/ListCell.swift b/CatchTestSam/CatchTestSam/List/ListCell.swift new file mode 100644 index 0000000..0c440c8 --- /dev/null +++ b/CatchTestSam/CatchTestSam/List/ListCell.swift @@ -0,0 +1,26 @@ +// +// ListCell.swift +// CatchTestSam +// +// Created by smoriyasu on 26/07/20. +// Copyright © 2020 smoriyasu. All rights reserved. +// + +import UIKit + +class ListCell: UITableViewCell +{ + var record : Record?; + + override func awakeFromNib() { + super.awakeFromNib() + // Initialization code + } + + func configure(record : Record) + { + self.record = record; + self.textLabel?.text = record.title; + self.detailTextLabel?.text = record.subtitle; + } +} diff --git a/CatchTestSam/CatchTestSam/List/ListViewController.swift b/CatchTestSam/CatchTestSam/List/ListViewController.swift new file mode 100644 index 0000000..a97f712 --- /dev/null +++ b/CatchTestSam/CatchTestSam/List/ListViewController.swift @@ -0,0 +1,127 @@ +// +// ListViewController.swift +// CatchTestSam +// +// Created by smoriyasu on 25/07/20. +// Copyright © 2020 smoriyasu. All rights reserved. +// + +import UIKit +import Refreshable + +class ListViewController: UITableViewController { + + let recordCollection = RecordCollection(fetchURL: URL(string: "https://raw.githubusercontent.com/catchnz/ios-test/master/data/data.json")!); + let pullDownHeight : CGFloat = 111; + let animator = SpinnerAnimator(); + + + var coverView : UIView! + var logoView : UIImageView! + + override func viewDidLoad() { + super.viewDidLoad() + + self.tableView.addPullToRefresh(withAnimator: animator, height: pullDownHeight, action: self.refresh) + + self.coverView = UIView(); + self.coverView.isUserInteractionEnabled = false; + self.coverView.backgroundColor = .black; + self.logoView = UIImageView(image: UIImage(named: "Logo")); + self.coverView.addSubview(self.logoView); + + self.tableView.addSubview(self.coverView) + } + + override func viewDidLayoutSubviews() { + let safeAreaBounds = CGRect(x: 0, y: 0, width: self.tableView.bounds.width, height: self.tableView.contentSize.height); + self.coverView.frame = safeAreaBounds; + + let midPoint = (self.tableView.bounds.height - (self.tableView.safeAreaInsets.top + self.tableView.safeAreaInsets.bottom)) / 2; + self.logoView.center = CGPoint(x: self.coverView.center.x, y: midPoint) ; + } + + func refresh() + { + self.recordCollection.fetchRecords + { + self.tableView.reloadData(); + self.hideCover(); + self.tableView.stopPullToRefresh(); + } + } + + + func hideCover() + { + UIView.animate(withDuration: 0.3, animations: + { + self.coverView.alpha = 0.25; + }) + { (finished) in + + } + } + + // MARK: Status Bar settings + + override var prefersStatusBarHidden: Bool { + get { + return false + } + } + + override var preferredStatusBarStyle: UIStatusBarStyle { + return .lightContent + } + + // MARK: - Table view delegate + + override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + return 60; + } + + // MARK: - Table view data source + + override func numberOfSections(in tableView: UITableView) -> Int { + return 1 + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return recordCollection.records.count; + } + + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "ListCell", for: indexPath); + + if let listCell = cell as? ListCell + { + let record = recordCollection.records[indexPath.row]; + listCell.configure(record: record); + } + + return cell + } + + // MARK: Scroll View Delegate + + override func scrollViewDidScroll(_ scrollView: UIScrollView) + { + let fixedOffset = -(self.tableView.contentOffset.y + self.tableView.safeAreaInsets.top); + let maxOffset = pullDownHeight - self.tableView.safeAreaInsets.top; + animator.setColorFromDisplacement(offset: fixedOffset, maxOffset: maxOffset); + } + + + // MARK: - Navigation + override func prepare(for segue: UIStoryboardSegue, sender: Any?) { + + if let destination = segue.destination as? ContentViewController, let tappedCell = sender as? ListCell + { + destination.record = tappedCell.record; + } + } + + +} diff --git a/CatchTestSam/CatchTestSam/List/SpinnerAnimator.swift b/CatchTestSam/CatchTestSam/List/SpinnerAnimator.swift new file mode 100644 index 0000000..df147cd --- /dev/null +++ b/CatchTestSam/CatchTestSam/List/SpinnerAnimator.swift @@ -0,0 +1,94 @@ +// +// SpinnerAnimator.swift +// CatchTestSam +// +// Created by smoriyasu on 26/07/20. +// Copyright © 2020 smoriyasu. All rights reserved. +// + +import UIKit +import Refreshable + +class SpinnerAnimator: UIView, PullToRefreshDelegate +{ + var spinner : UIImageView! + var header : UIView! + + let backgroundBrightness = 0.4; + + override init(frame: CGRect) { + super.init(frame: frame) + autoresizingMask = .flexibleWidth + self.backgroundColor = .black; + + // The space above the Refresh Control is empty, so this view is prevent the table background from being visible. + let headerFrame = CGRect(x: 0, y: -1000, width: 2000, height: 1000); + header = UIView(frame: headerFrame); + self.header.backgroundColor = UIColor(white: backgroundBrightness, alpha: 1.0); + addSubview(self.header); + + self.spinner = UIImageView(image: UIImage(named: "Spinner")); + addSubview(spinner) + spinner.isHidden = true + } + + public required init(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func layoutSubviews() { + self.spinner.center = self.center; + } + + func pullToRefreshAnimationDidStart(_ view: PullToRefreshView) + { + startAnimation(); + } + + func pullToRefreshAnimationDidEnd(_ view: PullToRefreshView) + { + spinner.isHidden = true + } + + func pullToRefresh(_ view: PullToRefreshView, stateDidChange state: PullToRefreshState) + { + } + + // Change the background color based on how far down it has been pulled. + func setColorFromDisplacement(offset: CGFloat, maxOffset: CGFloat) + { + let brightness = CGFloat((max(min(offset, maxOffset), 0) / maxOffset) * backgroundBrightness); + self.backgroundColor = UIColor(white: brightness, alpha: 1.0); + } + + func startAnimation() + { + self.spinner.isHidden = false; + self.spinner.alpha = 0.0; + + UIView.animate(withDuration: 0.1) { + self.spinner.alpha = 1.0; + } + animateSpinner(); + } + func animateSpinner() + { + UIView.animate( + withDuration: Double(0.3), + delay: Double(0.0), + options: UIView.AnimationOptions.curveLinear, + animations: { + // Rotate the spinner by PI/2 = 90 degrees + self.spinner.transform = self.spinner.transform.rotated(by: -.pi/2) + + }, + completion: { finished in + if(!self.spinner.isHidden) + { + // Keep animating until it is hidden. + self.animateSpinner(); + } + } + ) + } +} diff --git a/CatchTestSam/CatchTestSam/Records/Record.swift b/CatchTestSam/CatchTestSam/Records/Record.swift new file mode 100644 index 0000000..d8ce7c6 --- /dev/null +++ b/CatchTestSam/CatchTestSam/Records/Record.swift @@ -0,0 +1,26 @@ +// +// Record.swift +// CatchTestSam +// +// Created by smoriyasu on 26/07/20. +// Copyright © 2020 smoriyasu. All rights reserved. +// + +import UIKit +import SwiftyJSON + +class Record: NSObject +{ + var id : Int = -1; + var title : String = ""; + var subtitle: String = ""; + var content: String = ""; + + init(json : JSON) + { + id = json["id"].intValue; + title = json["title"].stringValue; + subtitle = json["subtitle"].stringValue; + content = json["content"].stringValue; + } +} diff --git a/CatchTestSam/CatchTestSam/Records/RecordCollection.swift b/CatchTestSam/CatchTestSam/Records/RecordCollection.swift new file mode 100644 index 0000000..c2cc672 --- /dev/null +++ b/CatchTestSam/CatchTestSam/Records/RecordCollection.swift @@ -0,0 +1,58 @@ +// +// RecordCollection.swift +// CatchTestSam +// +// Created by smoriyasu on 26/07/20. +// Copyright © 2020 smoriyasu. All rights reserved. +// + +import UIKit +import SwiftyJSON + +class RecordCollection: NSObject +{ + var fetchURL : URL; + var records : [Record] = []; + + init(fetchURL : URL) + { + self.fetchURL = fetchURL; + } + + func fetchRecords(completionHandler: @escaping() -> Void) + { + var request = URLRequest(url: fetchURL) + request.httpMethod = "GET" + request.addValue("application/json", forHTTPHeaderField: "Content-Type") + + let session = URLSession.shared + let task = session.dataTask(with: request, completionHandler: { data, response, error -> Void in + print(response!) + do { + let json = try JSON(data: data!); + self.parseRecords(json: json); + //print(json) + } catch { + print("error") + } + DispatchQueue.main.async { + completionHandler(); + } + }) + + task.resume() + } + + func parseRecords(json : JSON) + { + self.records = []; + if let array = json.array + { + for singleObject in array + { + let record = Record(json: singleObject); + self.records.append(record); + } + } + } +} diff --git a/CatchTestSam/CatchTestSam/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json b/CatchTestSam/CatchTestSam/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..9221b9b --- /dev/null +++ b/CatchTestSam/CatchTestSam/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/CatchTestSam/CatchTestSam/Resources/Assets.xcassets/Contents.json b/CatchTestSam/CatchTestSam/Resources/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/CatchTestSam/CatchTestSam/Resources/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/CatchTestSam/CatchTestSam/Resources/Assets.xcassets/Logo.imageset/Contents.json b/CatchTestSam/CatchTestSam/Resources/Assets.xcassets/Logo.imageset/Contents.json new file mode 100644 index 0000000..d6c0040 --- /dev/null +++ b/CatchTestSam/CatchTestSam/Resources/Assets.xcassets/Logo.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Logo Desktop.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/CatchTestSam/CatchTestSam/Resources/Assets.xcassets/Logo.imageset/Logo Desktop.pdf b/CatchTestSam/CatchTestSam/Resources/Assets.xcassets/Logo.imageset/Logo Desktop.pdf new file mode 100644 index 0000000..31527ad Binary files /dev/null and b/CatchTestSam/CatchTestSam/Resources/Assets.xcassets/Logo.imageset/Logo Desktop.pdf differ diff --git a/CatchTestSam/CatchTestSam/Resources/Assets.xcassets/Spinner.imageset/Contents.json b/CatchTestSam/CatchTestSam/Resources/Assets.xcassets/Spinner.imageset/Contents.json new file mode 100644 index 0000000..470ae12 --- /dev/null +++ b/CatchTestSam/CatchTestSam/Resources/Assets.xcassets/Spinner.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "Oval.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/CatchTestSam/CatchTestSam/Resources/Assets.xcassets/Spinner.imageset/Oval.pdf b/CatchTestSam/CatchTestSam/Resources/Assets.xcassets/Spinner.imageset/Oval.pdf new file mode 100644 index 0000000..dee0016 Binary files /dev/null and b/CatchTestSam/CatchTestSam/Resources/Assets.xcassets/Spinner.imageset/Oval.pdf differ diff --git a/CatchTestSam/CatchTestSam/Resources/Base.lproj/LaunchScreen.storyboard b/CatchTestSam/CatchTestSam/Resources/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..ac31766 --- /dev/null +++ b/CatchTestSam/CatchTestSam/Resources/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CatchTestSam/CatchTestSam/Resources/Base.lproj/Main.storyboard b/CatchTestSam/CatchTestSam/Resources/Base.lproj/Main.storyboard new file mode 100644 index 0000000..3f2f04b --- /dev/null +++ b/CatchTestSam/CatchTestSam/Resources/Base.lproj/Main.storyboard @@ -0,0 +1,118 @@ + + + + + + + + + + + + + + + + + + + + + Across the centuries tesseract great turbulent clouds the ash of stellar alchemy a very small stage in a vast cosmic arena the only home we've ever known. Jean-François Champollion radio telescope vanquish the impossible permanence of the stars not a sunrise but a galaxyrise vanquish the impossible. A mote of dust suspended in a sunbeam vanquish the impossible finite but unbounded not a sunrise but a galaxyrise Cambrian explosion globular star cluster. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/CatchTestSam/CatchTestSam/Resources/Info.plist b/CatchTestSam/CatchTestSam/Resources/Info.plist new file mode 100644 index 0000000..06060fc --- /dev/null +++ b/CatchTestSam/CatchTestSam/Resources/Info.plist @@ -0,0 +1,66 @@ + + + + + UIViewControllerBasedStatusBarAppearance + + 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 + LSRequiresIPhoneOS + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + UISceneStoryboardFile + Main + + + + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/CatchTestSam/CatchTestSam/Root/AppDelegate.swift b/CatchTestSam/CatchTestSam/Root/AppDelegate.swift new file mode 100644 index 0000000..d7c7cbd --- /dev/null +++ b/CatchTestSam/CatchTestSam/Root/AppDelegate.swift @@ -0,0 +1,37 @@ +// +// AppDelegate.swift +// CatchTestSam +// +// Created by smoriyasu on 25/07/20. +// Copyright © 2020 smoriyasu. All rights reserved. +// + +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + return true + } + + // MARK: UISceneSession Lifecycle + + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { + // Called when a new scene session is being created. + // Use this method to select a configuration to create the new scene with. + return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + } + + func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { + // Called when the user discards a scene session. + // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. + // Use this method to release any resources that were specific to the discarded scenes, as they will not return. + } + + +} + diff --git a/CatchTestSam/CatchTestSam/Root/NavController.swift b/CatchTestSam/CatchTestSam/Root/NavController.swift new file mode 100644 index 0000000..ff24349 --- /dev/null +++ b/CatchTestSam/CatchTestSam/Root/NavController.swift @@ -0,0 +1,38 @@ +// +// NavController.swift +// CatchTestSam +// +// Created by smoriyasu on 25/07/20. +// Copyright © 2020 smoriyasu. All rights reserved. +// + +import UIKit + +class NavController: UINavigationController, UINavigationControllerDelegate +{ + + override func viewDidLoad() { + super.viewDidLoad() + + // Do any additional setup after loading the view. + self.delegate = self; + } + + override var prefersStatusBarHidden: Bool { + get { + return false + } + } + + override var preferredStatusBarStyle: UIStatusBarStyle { + return .darkContent + } + + func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) { + if viewController == self.viewControllers.first { + self.setNavigationBarHidden(true, animated: animated) + } else { + self.setNavigationBarHidden(false, animated: animated) + } + } +} diff --git a/CatchTestSam/CatchTestSam/Root/SceneDelegate.swift b/CatchTestSam/CatchTestSam/Root/SceneDelegate.swift new file mode 100644 index 0000000..70dac57 --- /dev/null +++ b/CatchTestSam/CatchTestSam/Root/SceneDelegate.swift @@ -0,0 +1,53 @@ +// +// SceneDelegate.swift +// CatchTestSam +// +// Created by smoriyasu on 25/07/20. +// Copyright © 2020 smoriyasu. All rights reserved. +// + +import UIKit + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + + var window: UIWindow? + + + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. + // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. + // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). + guard let _ = (scene as? UIWindowScene) else { return } + } + + func sceneDidDisconnect(_ scene: UIScene) { + // Called as the scene is being released by the system. + // This occurs shortly after the scene enters the background, or when its session is discarded. + // Release any resources associated with this scene that can be re-created the next time the scene connects. + // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). + } + + func sceneDidBecomeActive(_ scene: UIScene) { + // Called when the scene has moved from an inactive state to an active state. + // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. + } + + func sceneWillResignActive(_ scene: UIScene) { + // Called when the scene will move from an active state to an inactive state. + // This may occur due to temporary interruptions (ex. an incoming phone call). + } + + func sceneWillEnterForeground(_ scene: UIScene) { + // Called as the scene transitions from the background to the foreground. + // Use this method to undo the changes made on entering the background. + } + + func sceneDidEnterBackground(_ scene: UIScene) { + // Called as the scene transitions from the foreground to the background. + // Use this method to save data, release shared resources, and store enough scene-specific state information + // to restore the scene back to its current state. + } + + +} + diff --git a/CatchTestSam/CatchTestSamTests/CatchTestSamTests.swift b/CatchTestSam/CatchTestSamTests/CatchTestSamTests.swift new file mode 100644 index 0000000..0aaa168 --- /dev/null +++ b/CatchTestSam/CatchTestSamTests/CatchTestSamTests.swift @@ -0,0 +1,67 @@ +// +// CatchTestSamTests.swift +// CatchTestSamTests +// +// Created by smoriyasu on 25/07/20. +// Copyright © 2020 smoriyasu. All rights reserved. +// + +import XCTest +import SwiftyJSON +@testable import CatchTestSam + +class CatchTestSamTests: 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 testParsingSingleRecord() throws { + let singleJson = JSON(parseJSON: """ + { + "id": 1, + "title": "nonummy integer", + "subtitle": "sapien arcu", + "content": "Donec diam neque, vestibulum eget, vulputate ut." + } + """); + + let record = Record(json: singleJson); + + XCTAssertEqual(record.id, 1); + XCTAssertEqual(record.title, "nonummy integer"); + XCTAssertEqual(record.subtitle, "sapien arcu"); + XCTAssertEqual(record.content, "Donec diam neque, vestibulum eget, vulputate ut."); + } + + func testParsingAllRecords() throws { + if let path = Bundle.main.path(forResource: "data", ofType: "json") { + do { + let testData = try Data(contentsOf: URL(fileURLWithPath: path)) + let json = try JSON(data: testData); + let recordCollection = RecordCollection(fetchURL: URL(string: "a.com")!); + recordCollection.parseRecords(json: json) + XCTAssertGreaterThan(recordCollection.records.count, 0); + } catch { + debugPrint("local json test data missing") + XCTAssert(false); + } + } + } + + func testDownloadingData() throws { + let expectation = XCTestExpectation(description: "Download json data") + + + let recordCollection = RecordCollection(fetchURL: URL(string: "https://raw.githubusercontent.com/catchnz/ios-test/master/data/data.json")!); + recordCollection.fetchRecords { + XCTAssertGreaterThan(recordCollection.records.count, 0); + expectation.fulfill() + } + wait(for: [expectation], timeout: 10.0) + } +} diff --git a/CatchTestSam/CatchTestSamTests/Info.plist b/CatchTestSam/CatchTestSamTests/Info.plist new file mode 100644 index 0000000..64d65ca --- /dev/null +++ b/CatchTestSam/CatchTestSamTests/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/CatchTestSam/Podfile b/CatchTestSam/Podfile new file mode 100644 index 0000000..e84c1fe --- /dev/null +++ b/CatchTestSam/Podfile @@ -0,0 +1,12 @@ +platform :ios, '10.0' +source 'https://github.com/CocoaPods/Specs.git' + +def shared_pods +end + +target 'CatchTestSam' do + use_frameworks! + pod 'SwiftyJSON' + pod 'Refreshable' + target :CatchTestSamTests +end diff --git a/CatchTestSam/Podfile.lock b/CatchTestSam/Podfile.lock new file mode 100644 index 0000000..46cf935 --- /dev/null +++ b/CatchTestSam/Podfile.lock @@ -0,0 +1,20 @@ +PODS: + - Refreshable (1.2.0) + - SwiftyJSON (5.0.0) + +DEPENDENCIES: + - Refreshable + - SwiftyJSON + +SPEC REPOS: + https://github.com/CocoaPods/Specs.git: + - Refreshable + - SwiftyJSON + +SPEC CHECKSUMS: + Refreshable: 0f76b6d198178e6cd2823a553fbf4e84a3dba527 + SwiftyJSON: 36413e04c44ee145039d332b4f4e2d3e8d6c4db7 + +PODFILE CHECKSUM: 5b1a3ec72c82c0f5a7932417afa8c1904c9d1df6 + +COCOAPODS: 1.8.4 diff --git a/README.md b/README.md index d2ccd6d..839da45 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,15 @@ # iOS Developer Test -A simple test for iOS Developers +Completed by Samuel Moriyasu ## Instructions -1. Fork or clone this repo -2. Create an iOS project -3. Use the design located in the design folder -4. Make the app load the json file remotely from this repo (i.e. `https://raw.githubusercontent.com/catchnz/ios-test/master/data/data.json`) as per the design -5. Display the data as per the design -6. Create a pull request or email us at dev@catch.co.nz +1. Open a Terminal window in the CatchTestSam folder. +2. Ensure Cocoapods is installed. +3. Enter `pod install` in the Terminal and press enter. +4. Open `CatchTestSam.xcworkspace` in XCode. +5. You can now build and run the app in Simulator, or run the Unit Tests. -### Guidelines +### Time Spent +About 9 hours, about half spent nitpicking the refresh animation and status bar appearance. -1. Your repo needs to include at minimum anything required to get the app working. Detailed instructions should be provided in the `README.md` file to setup and run the app. -2. Let us know how long you spent on it - -### Bonus Points - -* Consume dependencies with tools like cocoapods -* Unit tests diff --git a/design/design.sketch b/design/design.sketch index 90328c7..98b1ab3 100644 Binary files a/design/design.sketch and b/design/design.sketch differ