diff --git a/FirstView.xib b/FirstView.xib new file mode 100644 index 0000000..fbb961c --- /dev/null +++ b/FirstView.xib @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Podfile b/Podfile new file mode 100644 index 0000000..9b971bc --- /dev/null +++ b/Podfile @@ -0,0 +1,5 @@ +platform :ios, '8.0' + +pod 'SwiftyJSON', '~> 2.3.2' +use_frameworks! + diff --git a/Podfile.lock b/Podfile.lock new file mode 100644 index 0000000..54df8e6 --- /dev/null +++ b/Podfile.lock @@ -0,0 +1,10 @@ +PODS: + - SwiftyJSON (2.3.2) + +DEPENDENCIES: + - SwiftyJSON (~> 2.3.2) + +SPEC CHECKSUMS: + SwiftyJSON: 04ccea08915aa0109039157c7974cf0298da292a + +COCOAPODS: 0.39.0 diff --git a/Pods/Manifest.lock b/Pods/Manifest.lock new file mode 100644 index 0000000..54df8e6 --- /dev/null +++ b/Pods/Manifest.lock @@ -0,0 +1,10 @@ +PODS: + - SwiftyJSON (2.3.2) + +DEPENDENCIES: + - SwiftyJSON (~> 2.3.2) + +SPEC CHECKSUMS: + SwiftyJSON: 04ccea08915aa0109039157c7974cf0298da292a + +COCOAPODS: 0.39.0 diff --git a/Pods/Pods.xcodeproj/project.pbxproj b/Pods/Pods.xcodeproj/project.pbxproj new file mode 100644 index 0000000..71e5ca1 --- /dev/null +++ b/Pods/Pods.xcodeproj/project.pbxproj @@ -0,0 +1,502 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 44C4F64A26E04DB2A5E41745E111B2B4 /* Pods-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 894E5DA93A9F359521A89826BE6DA777 /* Pods-dummy.m */; }; + 58D67DC97D9F4E0F057A87223A15E8FB /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3E4E89230EF59BC255123B67864ACF77 /* Foundation.framework */; }; + 691886E3537DED119660000DC865BCC9 /* SwiftyJSON-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 05CA09B061E193906653AC21FD3A94F1 /* SwiftyJSON-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 832B19BD04D972B79C25420D469F68F7 /* Pods-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 2BCC458FDD5F692BBB2BFC64BB5701FC /* Pods-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + CE9A434FAFE8158B3A66D6FCB4F507ED /* SwiftyJSON-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 32B9BBA8F5B33E56C38F5546432F66B7 /* SwiftyJSON-dummy.m */; }; + D18A87B789253DF5E376C49857A964CA /* SwiftyJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = BDE506ECE97701EBA44FDBFF401C1A04 /* SwiftyJSON.swift */; }; + F7B9462ED43D98E27777C1C566CAEC0A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3E4E89230EF59BC255123B67864ACF77 /* Foundation.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + DED02675A046C8DF39C3792995788A89 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D41D8CD98F00B204E9800998ECF8427E /* Project object */; + proxyType = 1; + remoteGlobalIDString = A2BFFC9999EB12DBCF06F86A19A1D5E4; + remoteInfo = SwiftyJSON; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 05CA09B061E193906653AC21FD3A94F1 /* SwiftyJSON-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SwiftyJSON-umbrella.h"; sourceTree = ""; }; + 2BCC458FDD5F692BBB2BFC64BB5701FC /* Pods-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-umbrella.h"; sourceTree = ""; }; + 2E7F7C62EE34D2D271E6E1234A2002B2 /* SwiftyJSON.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = SwiftyJSON.xcconfig; sourceTree = ""; }; + 32B9BBA8F5B33E56C38F5546432F66B7 /* SwiftyJSON-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "SwiftyJSON-dummy.m"; sourceTree = ""; }; + 3E4E89230EF59BC255123B67864ACF77 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.0.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; + 4BF91E2E9C3BEBFD7F8F2C550EBC91BB /* Pods.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 79A9DEDC89FE8336BF5FEDAAF75BF7FC /* Pods.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = Pods.modulemap; sourceTree = ""; }; + 87B213035BAC5F75386F62D3C75D2342 /* Pods-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-acknowledgements.plist"; sourceTree = ""; }; + 894E5DA93A9F359521A89826BE6DA777 /* Pods-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-dummy.m"; sourceTree = ""; }; + 977577C045EDA9D9D1F46E2598D19FC7 /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Pods.debug.xcconfig; sourceTree = ""; }; + BA6428E9F66FD5A23C0A2E06ED26CD2F /* Podfile */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; + BBF610E40BDF6E542FA21FF7E5491205 /* SwiftyJSON-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "SwiftyJSON-prefix.pch"; sourceTree = ""; }; + BDE506ECE97701EBA44FDBFF401C1A04 /* SwiftyJSON.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = SwiftyJSON.swift; path = Source/SwiftyJSON.swift; sourceTree = ""; }; + C4A9A658C966F6BB111BEDBDEA1F874A /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + CBC0F7C552B739C909B650A0F42F7F38 /* Pods-resources.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-resources.sh"; sourceTree = ""; }; + D032A29E584B77AE187E586922267CF5 /* SwiftyJSON.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = "sourcecode.module-map"; path = SwiftyJSON.modulemap; sourceTree = ""; }; + D0405803033A2A777B8E4DFA0C1800ED /* Pods-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-acknowledgements.markdown"; sourceTree = ""; }; + DA312349A49333542E6F4B36B329960E /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = Pods.release.xcconfig; sourceTree = ""; }; + E7E2E816A2F4822B9C40407757AC32FC /* SwiftyJSON.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftyJSON.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + E7F21354943D9F42A70697D5A5EF72E9 /* Pods-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-frameworks.sh"; sourceTree = ""; }; + E8446514FBAD26C0E18F24A5715AEF67 /* Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 5C07BF63857AF434CC4D1987C7E0A35C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + F7B9462ED43D98E27777C1C566CAEC0A /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + EE8BDF3F4F4FB025B6184D78EBD0236A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 58D67DC97D9F4E0F057A87223A15E8FB /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 70A8E4D3C62B6C203D238581DA091C21 /* SwiftyJSON */ = { + isa = PBXGroup; + children = ( + BDE506ECE97701EBA44FDBFF401C1A04 /* SwiftyJSON.swift */, + 8EC39E1DC11846207722B71AAFE40F98 /* Support Files */, + ); + path = SwiftyJSON; + sourceTree = ""; + }; + 75D98FF52E597A11900E131B6C4E1ADA /* Pods */ = { + isa = PBXGroup; + children = ( + E8446514FBAD26C0E18F24A5715AEF67 /* Info.plist */, + 79A9DEDC89FE8336BF5FEDAAF75BF7FC /* Pods.modulemap */, + D0405803033A2A777B8E4DFA0C1800ED /* Pods-acknowledgements.markdown */, + 87B213035BAC5F75386F62D3C75D2342 /* Pods-acknowledgements.plist */, + 894E5DA93A9F359521A89826BE6DA777 /* Pods-dummy.m */, + E7F21354943D9F42A70697D5A5EF72E9 /* Pods-frameworks.sh */, + CBC0F7C552B739C909B650A0F42F7F38 /* Pods-resources.sh */, + 2BCC458FDD5F692BBB2BFC64BB5701FC /* Pods-umbrella.h */, + 977577C045EDA9D9D1F46E2598D19FC7 /* Pods.debug.xcconfig */, + DA312349A49333542E6F4B36B329960E /* Pods.release.xcconfig */, + ); + name = Pods; + path = "Target Support Files/Pods"; + sourceTree = ""; + }; + 7DB346D0F39D3F0E887471402A8071AB = { + isa = PBXGroup; + children = ( + BA6428E9F66FD5A23C0A2E06ED26CD2F /* Podfile */, + BC3CA7F9E30CC8F7E2DD044DD34432FC /* Frameworks */, + BD1E3C710F12F4B4C092FC8E2AD22110 /* Pods */, + D90755D1F4FB9E07B7AB8EC8656D2CB2 /* Products */, + B7B80995527643776607AFFA75B91E24 /* Targets Support Files */, + ); + sourceTree = ""; + }; + 8EC39E1DC11846207722B71AAFE40F98 /* Support Files */ = { + isa = PBXGroup; + children = ( + C4A9A658C966F6BB111BEDBDEA1F874A /* Info.plist */, + D032A29E584B77AE187E586922267CF5 /* SwiftyJSON.modulemap */, + 2E7F7C62EE34D2D271E6E1234A2002B2 /* SwiftyJSON.xcconfig */, + 32B9BBA8F5B33E56C38F5546432F66B7 /* SwiftyJSON-dummy.m */, + BBF610E40BDF6E542FA21FF7E5491205 /* SwiftyJSON-prefix.pch */, + 05CA09B061E193906653AC21FD3A94F1 /* SwiftyJSON-umbrella.h */, + ); + name = "Support Files"; + path = "../Target Support Files/SwiftyJSON"; + sourceTree = ""; + }; + B7B80995527643776607AFFA75B91E24 /* Targets Support Files */ = { + isa = PBXGroup; + children = ( + 75D98FF52E597A11900E131B6C4E1ADA /* Pods */, + ); + name = "Targets Support Files"; + sourceTree = ""; + }; + BC3CA7F9E30CC8F7E2DD044DD34432FC /* Frameworks */ = { + isa = PBXGroup; + children = ( + BF6342C8B29F4CEEA088EFF7AB4DE362 /* iOS */, + ); + name = Frameworks; + sourceTree = ""; + }; + BD1E3C710F12F4B4C092FC8E2AD22110 /* Pods */ = { + isa = PBXGroup; + children = ( + 70A8E4D3C62B6C203D238581DA091C21 /* SwiftyJSON */, + ); + name = Pods; + sourceTree = ""; + }; + BF6342C8B29F4CEEA088EFF7AB4DE362 /* iOS */ = { + isa = PBXGroup; + children = ( + 3E4E89230EF59BC255123B67864ACF77 /* Foundation.framework */, + ); + name = iOS; + sourceTree = ""; + }; + D90755D1F4FB9E07B7AB8EC8656D2CB2 /* Products */ = { + isa = PBXGroup; + children = ( + 4BF91E2E9C3BEBFD7F8F2C550EBC91BB /* Pods.framework */, + E7E2E816A2F4822B9C40407757AC32FC /* SwiftyJSON.framework */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 6ABB360E8394F11C736FE13C2F536B02 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 691886E3537DED119660000DC865BCC9 /* SwiftyJSON-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 715876937915782C81EF25AA9F36FA29 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 832B19BD04D972B79C25420D469F68F7 /* Pods-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 9BD196C3DED20BF11A665D7D92FCF62F /* Pods */ = { + isa = PBXNativeTarget; + buildConfigurationList = 17C9FB0AF1871D3CD0FC142E1B11F8A2 /* Build configuration list for PBXNativeTarget "Pods" */; + buildPhases = ( + A89D2DC68AA6BA0747B6BE468C884F81 /* Sources */, + EE8BDF3F4F4FB025B6184D78EBD0236A /* Frameworks */, + 715876937915782C81EF25AA9F36FA29 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + B7611EE004F3F6735B42A562A6ED4A44 /* PBXTargetDependency */, + ); + name = Pods; + productName = Pods; + productReference = 4BF91E2E9C3BEBFD7F8F2C550EBC91BB /* Pods.framework */; + productType = "com.apple.product-type.framework"; + }; + A2BFFC9999EB12DBCF06F86A19A1D5E4 /* SwiftyJSON */ = { + isa = PBXNativeTarget; + buildConfigurationList = 2875175DCCB624F6634D9D136CA3FD29 /* Build configuration list for PBXNativeTarget "SwiftyJSON" */; + buildPhases = ( + E4E2BA9FAE9985A76A2433B1DD1ED9FA /* Sources */, + 5C07BF63857AF434CC4D1987C7E0A35C /* Frameworks */, + 6ABB360E8394F11C736FE13C2F536B02 /* Headers */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = SwiftyJSON; + productName = SwiftyJSON; + productReference = E7E2E816A2F4822B9C40407757AC32FC /* SwiftyJSON.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + D41D8CD98F00B204E9800998ECF8427E /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0700; + LastUpgradeCheck = 0700; + }; + buildConfigurationList = 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 7DB346D0F39D3F0E887471402A8071AB; + productRefGroup = D90755D1F4FB9E07B7AB8EC8656D2CB2 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 9BD196C3DED20BF11A665D7D92FCF62F /* Pods */, + A2BFFC9999EB12DBCF06F86A19A1D5E4 /* SwiftyJSON */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + A89D2DC68AA6BA0747B6BE468C884F81 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 44C4F64A26E04DB2A5E41745E111B2B4 /* Pods-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + E4E2BA9FAE9985A76A2433B1DD1ED9FA /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + CE9A434FAFE8158B3A66D6FCB4F507ED /* SwiftyJSON-dummy.m in Sources */, + D18A87B789253DF5E376C49857A964CA /* SwiftyJSON.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + B7611EE004F3F6735B42A562A6ED4A44 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = SwiftyJSON; + target = A2BFFC9999EB12DBCF06F86A19A1D5E4 /* SwiftyJSON */; + targetProxy = DED02675A046C8DF39C3792995788A89 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 32A42E82E2F1BC57F2F4F2041EFCF509 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = DA312349A49333542E6F4B36B329960E /* Pods.release.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + INFOPLIST_FILE = "Target Support Files/Pods/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods/Pods.modulemap"; + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_NAME = Pods; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 97CFFDC80E52D464D3897018EB8A49B2 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 2E7F7C62EE34D2D271E6E1234A2002B2 /* SwiftyJSON.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_PREFIX_HEADER = "Target Support Files/SwiftyJSON/SwiftyJSON-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/SwiftyJSON/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/SwiftyJSON/SwiftyJSON.modulemap"; + MTL_ENABLE_DEBUG_INFO = YES; + PRODUCT_NAME = SwiftyJSON; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + A70CDAD61F90AC503C7D04CC22DA2923 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + ONLY_ACTIVE_ARCH = YES; + STRIP_INSTALLED_PRODUCT = NO; + SYMROOT = "${SRCROOT}/../build"; + }; + name = Debug; + }; + CE014A0FB6009D69CD58B065F7528C66 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 977577C045EDA9D9D1F46E2598D19FC7 /* Pods.debug.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + INFOPLIST_FILE = "Target Support Files/Pods/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods/Pods.modulemap"; + MTL_ENABLE_DEBUG_INFO = YES; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_NAME = Pods; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + D1A45F25A023953C2544B61B84AF31FF /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 2E7F7C62EE34D2D271E6E1234A2002B2 /* SwiftyJSON.xcconfig */; + buildSettings = { + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_PREFIX_HEADER = "Target Support Files/SwiftyJSON/SwiftyJSON-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/SwiftyJSON/Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + MODULEMAP_FILE = "Target Support Files/SwiftyJSON/SwiftyJSON.modulemap"; + MTL_ENABLE_DEBUG_INFO = NO; + PRODUCT_NAME = SwiftyJSON; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + FB45FFD90572718D82AB9092B750F0CA /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_PREPROCESSOR_DEFINITIONS = "RELEASE=1"; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + STRIP_INSTALLED_PRODUCT = NO; + SYMROOT = "${SRCROOT}/../build"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 17C9FB0AF1871D3CD0FC142E1B11F8A2 /* Build configuration list for PBXNativeTarget "Pods" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + CE014A0FB6009D69CD58B065F7528C66 /* Debug */, + 32A42E82E2F1BC57F2F4F2041EFCF509 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 2875175DCCB624F6634D9D136CA3FD29 /* Build configuration list for PBXNativeTarget "SwiftyJSON" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97CFFDC80E52D464D3897018EB8A49B2 /* Debug */, + D1A45F25A023953C2544B61B84AF31FF /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 2D8E8EC45A3A1A1D94AE762CB5028504 /* Build configuration list for PBXProject "Pods" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A70CDAD61F90AC503C7D04CC22DA2923 /* Debug */, + FB45FFD90572718D82AB9092B750F0CA /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = D41D8CD98F00B204E9800998ECF8427E /* Project object */; +} diff --git a/Pods/Pods.xcodeproj/xcuserdata/ZeroJian.xcuserdatad/xcschemes/Pods.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/ZeroJian.xcuserdatad/xcschemes/Pods.xcscheme new file mode 100644 index 0000000..b094004 --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/ZeroJian.xcuserdatad/xcschemes/Pods.xcscheme @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Pods/Pods.xcodeproj/xcuserdata/ZeroJian.xcuserdatad/xcschemes/SwiftyJSON.xcscheme b/Pods/Pods.xcodeproj/xcuserdata/ZeroJian.xcuserdatad/xcschemes/SwiftyJSON.xcscheme new file mode 100644 index 0000000..509184f --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/ZeroJian.xcuserdatad/xcschemes/SwiftyJSON.xcscheme @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Pods/Pods.xcodeproj/xcuserdata/ZeroJian.xcuserdatad/xcschemes/xcschememanagement.plist b/Pods/Pods.xcodeproj/xcuserdata/ZeroJian.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..cbc528a --- /dev/null +++ b/Pods/Pods.xcodeproj/xcuserdata/ZeroJian.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,32 @@ + + + + + SchemeUserState + + Pods.xcscheme + + isShown + + + SwiftyJSON.xcscheme + + isShown + + + + SuppressBuildableAutocreation + + 9BD196C3DED20BF11A665D7D92FCF62F + + primary + + + A2BFFC9999EB12DBCF06F86A19A1D5E4 + + primary + + + + + diff --git a/Pods/SwiftyJSON/LICENSE b/Pods/SwiftyJSON/LICENSE new file mode 100644 index 0000000..a7af196 --- /dev/null +++ b/Pods/SwiftyJSON/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Ruoyu Fu + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/Pods/SwiftyJSON/README.md b/Pods/SwiftyJSON/README.md new file mode 100644 index 0000000..c0846f9 --- /dev/null +++ b/Pods/SwiftyJSON/README.md @@ -0,0 +1,376 @@ +#SwiftyJSON [中文介绍](http://tangplin.github.io/swiftyjson/) + +[![Travis CI](https://travis-ci.org/SwiftyJSON/SwiftyJSON.svg?branch=master)](https://travis-ci.org/SwiftyJSON/SwiftyJSON) + +SwiftyJSON makes it easy to deal with JSON data in Swift. + +1. [Why is the typical JSON handling in Swift NOT good](#why-is-the-typical-json-handling-in-swift-not-good) +1. [Requirements](#requirements) +1. [Integration](#integration) +1. [Usage](#usage) + - [Initialization](#initialization) + - [Subscript](#subscript) + - [Loop](#loop) + - [Error](#error) + - [Optional getter](#optional-getter) + - [Non-optional getter](#non-optional-getter) + - [Setter](#setter) + - [Raw object](#raw-object) + - [Literal convertibles](#literal-convertibles) +1. [Work with Alamofire](#work-with-alamofire) + +##Why is the typical JSON handling in Swift NOT good? +Swift is very strict about types. But although explicit typing is good for saving us from mistakes, it becomes painful when dealing with JSON and other areas that are, by nature, implicit about types. + +Take the Twitter API for example. Say we want to retrieve a user's "name" value of some tweet in Swift (according to Twitter's API https://dev.twitter.com/docs/api/1.1/get/statuses/home_timeline). + +The code would look like this: + +```swift + +if let statusesArray = try? NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments) as? [[String: AnyObject]], + let user = statusesArray[0]["user"] as? [String: AnyObject], + let username = user["name"] as? String { + // Finally we got the username +} + +``` + +It's not good. + +Even if we use optional chaining, it would be messy: + +```swift + +if let JSONObject = try NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments) as? [[String: AnyObject]], + let username = (JSONObject[0]["user"] as? [String: AnyObject])?["name"] as? String { + // There's our username +} + +``` +An unreadable mess--for something that should really be simple! + +With SwiftyJSON all you have to do is: + +```swift + +let json = JSON(data: dataFromNetworking) +if let userName = json[0]["user"]["name"].string { + //Now you got your value +} + +``` + +And don't worry about the Optional Wrapping thing. It's done for you automatically. + +```swift + +let json = JSON(data: dataFromNetworking) +if let userName = json[999999]["wrong_key"]["wrong_name"].string { + //Calm down, take it easy, the ".string" property still produces the correct Optional String type with safety +} else { + //Print the error + print(json[999999]["wrong_key"]["wrong_name"]) +} + +``` + +## Requirements + +- iOS 7.0+ / Mac OS X 10.9+ +- Xcode 7 + +##Integration + +####CocoaPods (iOS 8+, OS X 10.9+) +You can use [Cocoapods](http://cocoapods.org/) to install `SwiftyJSON`by adding it to your `Podfile`: +```ruby +platform :ios, '8.0' +use_frameworks! + +target 'MyApp' do + pod 'SwiftyJSON', :git => 'https://github.com/SwiftyJSON/SwiftyJSON.git' +end +``` +Note that this requires CocoaPods version 36, and your iOS deployment target to be at least 8.0: + +####Carthage (iOS 8+, OS X 10.9+) +You can use [Carthage](https://github.com/Carthage/Carthage) to install `SwiftyJSON` by adding it to your `Cartfile`: +``` +github "SwiftyJSON/SwiftyJSON" +``` + +####Manually (iOS 7+, OS X 10.9+) + +To use this library in your project manually you may: + +1. for Projects, just drag SwiftyJSON.swift to the project tree +2. for Workspaces, include the whole SwiftyJSON.xcodeproj + +## Usage + +####Initialization +```swift +import SwiftyJSON +``` +```swift +let json = JSON(data: dataFromNetworking) +``` +```swift +let json = JSON(jsonObject) +``` +```swift +if let dataFromString = jsonString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false) { + let json = JSON(data: dataFromString) +} +``` + +####Subscript +```swift +//Getting a double from a JSON Array +let name = json[0].double +``` +```swift +//Getting a string from a JSON Dictionary +let name = json["name"].stringValue +``` +```swift +//Getting a string using a path to the element +let path = [1,"list",2,"name"] +let name = json[path].string +//Just the same +let name = json[1]["list"][2]["name"].string +//Alternatively +let name = json[1,"list",2,"name"].string +``` +```swift +//With a hard way +let name = json[].string +``` +```swift +//With a custom way +let keys:[SubscriptType] = [1,"list",2,"name"] +let name = json[keys].string +``` +####Loop +```swift +//If json is .Dictionary +for (key,subJson):(String, JSON) in json { + //Do something you want +} +``` +*The first element is always a String, even if the JSON is an Array* +```swift +//If json is .Array +//The `index` is 0.. = json["list"].arrayValue +``` +```swift +//If not a Dictionary or nil, return [:] +let user: Dictionary = json["user"].dictionaryValue +``` + +####Setter +```swift +json["name"] = JSON("new-name") +json[0] = JSON(1) +``` +```swift +json["id"].int = 1234567890 +json["coordinate"].double = 8766.766 +json["name"].string = "Jack" +json.arrayObject = [1,2,3,4] +json.dictionary = ["name":"Jack", "age":25] +``` + +####Raw object +```swift +let jsonObject: AnyObject = json.object +``` +```swift +if let jsonObject: AnyObject = json.rawValue +``` +```swift +//convert the JSON to raw NSData +if let data = json.rawData() { + //Do something you want +} +``` +```swift +//convert the JSON to a raw String +if let string = json.rawString() { + //Do something you want +} +``` +####Existance +```swift +//shows you whether value specified in JSON or not +if json["name"].isExists() +``` + +####Literal convertibles +For more info about literal convertibles: [Swift Literal Convertibles](http://nshipster.com/swift-literal-convertible/) +```swift +//StringLiteralConvertible +let json: JSON = "I'm a json" +``` +```swift +//IntegerLiteralConvertible +let json: JSON = 12345 +``` +```swift +//BooleanLiteralConvertible +let json: JSON = true +``` +```swift +//FloatLiteralConvertible +let json: JSON = 2.8765 +``` +```swift +//DictionaryLiteralConvertible +let json: JSON = ["I":"am", "a":"json"] +``` +```swift +//ArrayLiteralConvertible +let json: JSON = ["I", "am", "a", "json"] +``` +```swift +//NilLiteralConvertible +let json: JSON = nil +``` +```swift +//With subscript in array +var json: JSON = [1,2,3] +json[0] = 100 +json[1] = 200 +json[2] = 300 +json[999] = 300 //Don't worry, nothing will happen +``` +```swift +//With subscript in dictionary +var json: JSON = ["name": "Jack", "age": 25] +json["name"] = "Mike" +json["age"] = "25" //It's OK to set String +json["address"] = "L.A." // Add the "address": "L.A." in json +``` +```swift +//Array & Dictionary +var json: JSON = ["name": "Jack", "age": 25, "list": ["a", "b", "c", ["what": "this"]]] +json["list"][3]["what"] = "that" +json["list",3,"what"] = "that" +let path = ["list",3,"what"] +json[path] = "that" +``` +##Work with Alamofire + +SwiftyJSON nicely wraps the result of the Alamofire JSON response handler: +```swift +Alamofire.request(.GET, url).validate().responseJSON { response in + switch response.result { + case .Success: + if let value = response.result.value { + let json = JSON(value) + print("JSON: \(json)") + } + case .Failure(let error): + print(error) + } +} +``` diff --git a/Pods/SwiftyJSON/Source/SwiftyJSON.swift b/Pods/SwiftyJSON/Source/SwiftyJSON.swift new file mode 100644 index 0000000..fa8635e --- /dev/null +++ b/Pods/SwiftyJSON/Source/SwiftyJSON.swift @@ -0,0 +1,1376 @@ +// SwiftyJSON.swift +// +// Copyright (c) 2014 Ruoyu Fu, Pinglin Tang +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import Foundation + +// MARK: - Error + +///Error domain +public let ErrorDomain: String = "SwiftyJSONErrorDomain" + +///Error code +public let ErrorUnsupportedType: Int = 999 +public let ErrorIndexOutOfBounds: Int = 900 +public let ErrorWrongType: Int = 901 +public let ErrorNotExist: Int = 500 +public let ErrorInvalidJSON: Int = 490 + +// MARK: - JSON Type + +/** +JSON's type definitions. + +See http://www.json.org +*/ +public enum Type :Int{ + + case Number + case String + case Bool + case Array + case Dictionary + case Null + case Unknown +} + +// MARK: - JSON Base + +public struct JSON { + + /** + Creates a JSON using the data. + + - parameter data: The NSData used to convert to json.Top level object in data is an NSArray or NSDictionary + - parameter opt: The JSON serialization reading options. `.AllowFragments` by default. + - parameter error: error The NSErrorPointer used to return the error. `nil` by default. + + - returns: The created JSON + */ + public init(data:NSData, options opt: NSJSONReadingOptions = .AllowFragments, error: NSErrorPointer = nil) { + do { + let object: AnyObject = try NSJSONSerialization.JSONObjectWithData(data, options: opt) + self.init(object) + } catch let aError as NSError { + if error != nil { + error.memory = aError + } + self.init(NSNull()) + } + } + + /** + Create a JSON from JSON string + - parameter string: Normal json string like '{"a":"b"}' + + - returns: The created JSON + */ + public static func parse(string:String) -> JSON { + return string.dataUsingEncoding(NSUTF8StringEncoding) + .flatMap({JSON(data: $0)}) ?? JSON(NSNull()) + } + + /** + Creates a JSON using the object. + + - parameter object: The object must have the following properties: All objects are NSString/String, NSNumber/Int/Float/Double/Bool, NSArray/Array, NSDictionary/Dictionary, or NSNull; All dictionary keys are NSStrings/String; NSNumbers are not NaN or infinity. + + - returns: The created JSON + */ + public init(_ object: AnyObject) { + self.object = object + } + + /** + Creates a JSON from a [JSON] + + - parameter jsonArray: A Swift array of JSON objects + + - returns: The created JSON + */ + public init(_ jsonArray:[JSON]) { + self.init(jsonArray.map { $0.object }) + } + + /** + Creates a JSON from a [String: JSON] + + - parameter jsonDictionary: A Swift dictionary of JSON objects + + - returns: The created JSON + */ + public init(_ jsonDictionary:[String: JSON]) { + var dictionary = [String: AnyObject]() + for (key, json) in jsonDictionary { + dictionary[key] = json.object + } + self.init(dictionary) + } + + /// Private object + private var rawArray: [AnyObject] = [] + private var rawDictionary: [String : AnyObject] = [:] + private var rawString: String = "" + private var rawNumber: NSNumber = 0 + private var rawNull: NSNull = NSNull() + /// Private type + private var _type: Type = .Null + /// prviate error + private var _error: NSError? = nil + + /// Object in JSON + public var object: AnyObject { + get { + switch self.type { + case .Array: + return self.rawArray + case .Dictionary: + return self.rawDictionary + case .String: + return self.rawString + case .Number: + return self.rawNumber + case .Bool: + return self.rawNumber + default: + return self.rawNull + } + } + set { + _error = nil + switch newValue { + case let number as NSNumber: + if number.isBool { + _type = .Bool + } else { + _type = .Number + } + self.rawNumber = number + case let string as String: + _type = .String + self.rawString = string + case _ as NSNull: + _type = .Null + case let array as [AnyObject]: + _type = .Array + self.rawArray = array + case let dictionary as [String : AnyObject]: + _type = .Dictionary + self.rawDictionary = dictionary + default: + _type = .Unknown + _error = NSError(domain: ErrorDomain, code: ErrorUnsupportedType, userInfo: [NSLocalizedDescriptionKey: "It is a unsupported type"]) + } + } + } + + /// json type + public var type: Type { get { return _type } } + + /// Error in JSON + public var error: NSError? { get { return self._error } } + + /// The static null json + @available(*, unavailable, renamed="null") + public static var nullJSON: JSON { get { return null } } + public static var null: JSON { get { return JSON(NSNull()) } } +} + +// MARK: - CollectionType, SequenceType, Indexable +extension JSON : Swift.CollectionType, Swift.SequenceType, Swift.Indexable { + + public typealias Generator = JSONGenerator + + public typealias Index = JSONIndex + + public var startIndex: JSON.Index { + switch self.type { + case .Array: + return JSONIndex(arrayIndex: self.rawArray.startIndex) + case .Dictionary: + return JSONIndex(dictionaryIndex: self.rawDictionary.startIndex) + default: + return JSONIndex() + } + } + + public var endIndex: JSON.Index { + switch self.type { + case .Array: + return JSONIndex(arrayIndex: self.rawArray.endIndex) + case .Dictionary: + return JSONIndex(dictionaryIndex: self.rawDictionary.endIndex) + default: + return JSONIndex() + } + } + + public subscript (position: JSON.Index) -> JSON.Generator.Element { + switch self.type { + case .Array: + return (String(position.arrayIndex), JSON(self.rawArray[position.arrayIndex!])) + case .Dictionary: + let (key, value) = self.rawDictionary[position.dictionaryIndex!] + return (key, JSON(value)) + default: + return ("", JSON.null) + } + } + + /// If `type` is `.Array` or `.Dictionary`, return `array.empty` or `dictonary.empty` otherwise return `true`. + public var isEmpty: Bool { + get { + switch self.type { + case .Array: + return self.rawArray.isEmpty + case .Dictionary: + return self.rawDictionary.isEmpty + default: + return true + } + } + } + + /// If `type` is `.Array` or `.Dictionary`, return `array.count` or `dictonary.count` otherwise return `0`. + public var count: Int { + switch self.type { + case .Array: + return self.rawArray.count + case .Dictionary: + return self.rawDictionary.count + default: + return 0 + } + } + + public func underestimateCount() -> Int { + switch self.type { + case .Array: + return self.rawArray.underestimateCount() + case .Dictionary: + return self.rawDictionary.underestimateCount() + default: + return 0 + } + } + + /** + If `type` is `.Array` or `.Dictionary`, return a generator over the elements like `Array` or `Dictionary`, otherwise return a generator over empty. + + - returns: Return a *generator* over the elements of JSON. + */ + public func generate() -> JSON.Generator { + return JSON.Generator(self) + } +} + +public struct JSONIndex: ForwardIndexType, _Incrementable, Equatable, Comparable { + + let arrayIndex: Int? + let dictionaryIndex: DictionaryIndex? + + let type: Type + + init(){ + self.arrayIndex = nil + self.dictionaryIndex = nil + self.type = .Unknown + } + + init(arrayIndex: Int) { + self.arrayIndex = arrayIndex + self.dictionaryIndex = nil + self.type = .Array + } + + init(dictionaryIndex: DictionaryIndex) { + self.arrayIndex = nil + self.dictionaryIndex = dictionaryIndex + self.type = .Dictionary + } + + public func successor() -> JSONIndex { + switch self.type { + case .Array: + return JSONIndex(arrayIndex: self.arrayIndex!.successor()) + case .Dictionary: + return JSONIndex(dictionaryIndex: self.dictionaryIndex!.successor()) + default: + return JSONIndex() + } + } +} + +public func ==(lhs: JSONIndex, rhs: JSONIndex) -> Bool { + switch (lhs.type, rhs.type) { + case (.Array, .Array): + return lhs.arrayIndex == rhs.arrayIndex + case (.Dictionary, .Dictionary): + return lhs.dictionaryIndex == rhs.dictionaryIndex + default: + return false + } +} + +public func <(lhs: JSONIndex, rhs: JSONIndex) -> Bool { + switch (lhs.type, rhs.type) { + case (.Array, .Array): + return lhs.arrayIndex < rhs.arrayIndex + case (.Dictionary, .Dictionary): + return lhs.dictionaryIndex < rhs.dictionaryIndex + default: + return false + } +} + +public func <=(lhs: JSONIndex, rhs: JSONIndex) -> Bool { + switch (lhs.type, rhs.type) { + case (.Array, .Array): + return lhs.arrayIndex <= rhs.arrayIndex + case (.Dictionary, .Dictionary): + return lhs.dictionaryIndex <= rhs.dictionaryIndex + default: + return false + } +} + +public func >=(lhs: JSONIndex, rhs: JSONIndex) -> Bool { + switch (lhs.type, rhs.type) { + case (.Array, .Array): + return lhs.arrayIndex >= rhs.arrayIndex + case (.Dictionary, .Dictionary): + return lhs.dictionaryIndex >= rhs.dictionaryIndex + default: + return false + } +} + +public func >(lhs: JSONIndex, rhs: JSONIndex) -> Bool { + switch (lhs.type, rhs.type) { + case (.Array, .Array): + return lhs.arrayIndex > rhs.arrayIndex + case (.Dictionary, .Dictionary): + return lhs.dictionaryIndex > rhs.dictionaryIndex + default: + return false + } +} + +public struct JSONGenerator : GeneratorType { + + public typealias Element = (String, JSON) + + private let type: Type + private var dictionayGenerate: DictionaryGenerator? + private var arrayGenerate: IndexingGenerator<[AnyObject]>? + private var arrayIndex: Int = 0 + + init(_ json: JSON) { + self.type = json.type + if type == .Array { + self.arrayGenerate = json.rawArray.generate() + }else { + self.dictionayGenerate = json.rawDictionary.generate() + } + } + + public mutating func next() -> JSONGenerator.Element? { + switch self.type { + case .Array: + if let o = self.arrayGenerate?.next() { + return (String(self.arrayIndex++), JSON(o)) + } else { + return nil + } + case .Dictionary: + if let (k, v): (String, AnyObject) = self.dictionayGenerate?.next() { + return (k, JSON(v)) + } else { + return nil + } + default: + return nil + } + } +} + +// MARK: - Subscript + +/** +* To mark both String and Int can be used in subscript. +*/ +public enum JSONKey { + case Index(Int) + case Key(String) +} + +public protocol JSONSubscriptType { + var jsonKey:JSONKey { get } +} + +extension Int: JSONSubscriptType { + public var jsonKey:JSONKey { + return JSONKey.Index(self) + } +} + +extension String: JSONSubscriptType { + public var jsonKey:JSONKey { + return JSONKey.Key(self) + } +} + +extension JSON { + + /// If `type` is `.Array`, return json which's object is `array[index]`, otherwise return null json with error. + private subscript(index index: Int) -> JSON { + get { + if self.type != .Array { + var r = JSON.null + r._error = self._error ?? NSError(domain: ErrorDomain, code: ErrorWrongType, userInfo: [NSLocalizedDescriptionKey: "Array[\(index)] failure, It is not an array"]) + return r + } else if index >= 0 && index < self.rawArray.count { + return JSON(self.rawArray[index]) + } else { + var r = JSON.null + r._error = NSError(domain: ErrorDomain, code:ErrorIndexOutOfBounds , userInfo: [NSLocalizedDescriptionKey: "Array[\(index)] is out of bounds"]) + return r + } + } + set { + if self.type == .Array { + if self.rawArray.count > index && newValue.error == nil { + self.rawArray[index] = newValue.object + } + } + } + } + + /// If `type` is `.Dictionary`, return json which's object is `dictionary[key]` , otherwise return null json with error. + private subscript(key key: String) -> JSON { + get { + var r = JSON.null + if self.type == .Dictionary { + if let o = self.rawDictionary[key] { + r = JSON(o) + } else { + r._error = NSError(domain: ErrorDomain, code: ErrorNotExist, userInfo: [NSLocalizedDescriptionKey: "Dictionary[\"\(key)\"] does not exist"]) + } + } else { + r._error = self._error ?? NSError(domain: ErrorDomain, code: ErrorWrongType, userInfo: [NSLocalizedDescriptionKey: "Dictionary[\"\(key)\"] failure, It is not an dictionary"]) + } + return r + } + set { + if self.type == .Dictionary && newValue.error == nil { + self.rawDictionary[key] = newValue.object + } + } + } + + /// If `sub` is `Int`, return `subscript(index:)`; If `sub` is `String`, return `subscript(key:)`. + private subscript(sub sub: JSONSubscriptType) -> JSON { + get { + switch sub.jsonKey { + case .Index(let index): return self[index: index] + case .Key(let key): return self[key: key] + } + } + set { + switch sub.jsonKey { + case .Index(let index): self[index: index] = newValue + case .Key(let key): self[key: key] = newValue + } + } + } + + /** + Find a json in the complex data structuresby using the Int/String's array. + + - parameter path: The target json's path. Example: + + let json = JSON[data] + let path = [9,"list","person","name"] + let name = json[path] + + The same as: let name = json[9]["list"]["person"]["name"] + + - returns: Return a json found by the path or a null json with error + */ + public subscript(path: [JSONSubscriptType]) -> JSON { + get { + return path.reduce(self) { $0[sub: $1] } + } + set { + switch path.count { + case 0: + return + case 1: + self[sub:path[0]].object = newValue.object + default: + var aPath = path; aPath.removeAtIndex(0) + var nextJSON = self[sub: path[0]] + nextJSON[aPath] = newValue + self[sub: path[0]] = nextJSON + } + } + } + + /** + Find a json in the complex data structuresby using the Int/String's array. + + - parameter path: The target json's path. Example: + + let name = json[9,"list","person","name"] + + The same as: let name = json[9]["list"]["person"]["name"] + + - returns: Return a json found by the path or a null json with error + */ + public subscript(path: JSONSubscriptType...) -> JSON { + get { + return self[path] + } + set { + self[path] = newValue + } + } +} + +// MARK: - LiteralConvertible + +extension JSON: Swift.StringLiteralConvertible { + + public init(stringLiteral value: StringLiteralType) { + self.init(value) + } + + public init(extendedGraphemeClusterLiteral value: StringLiteralType) { + self.init(value) + } + + public init(unicodeScalarLiteral value: StringLiteralType) { + self.init(value) + } +} + +extension JSON: Swift.IntegerLiteralConvertible { + + public init(integerLiteral value: IntegerLiteralType) { + self.init(value) + } +} + +extension JSON: Swift.BooleanLiteralConvertible { + + public init(booleanLiteral value: BooleanLiteralType) { + self.init(value) + } +} + +extension JSON: Swift.FloatLiteralConvertible { + + public init(floatLiteral value: FloatLiteralType) { + self.init(value) + } +} + +extension JSON: Swift.DictionaryLiteralConvertible { + + public init(dictionaryLiteral elements: (String, AnyObject)...) { + self.init(elements.reduce([String : AnyObject]()){(dictionary: [String : AnyObject], element:(String, AnyObject)) -> [String : AnyObject] in + var d = dictionary + d[element.0] = element.1 + return d + }) + } +} + +extension JSON: Swift.ArrayLiteralConvertible { + + public init(arrayLiteral elements: AnyObject...) { + self.init(elements) + } +} + +extension JSON: Swift.NilLiteralConvertible { + + public init(nilLiteral: ()) { + self.init(NSNull()) + } +} + +// MARK: - Raw + +extension JSON: Swift.RawRepresentable { + + public init?(rawValue: AnyObject) { + if JSON(rawValue).type == .Unknown { + return nil + } else { + self.init(rawValue) + } + } + + public var rawValue: AnyObject { + return self.object + } + + public func rawData(options opt: NSJSONWritingOptions = NSJSONWritingOptions(rawValue: 0)) throws -> NSData { + guard NSJSONSerialization.isValidJSONObject(self.object) else { + throw NSError(domain: ErrorDomain, code: ErrorInvalidJSON, userInfo: [NSLocalizedDescriptionKey: "JSON is invalid"]) + } + + return try NSJSONSerialization.dataWithJSONObject(self.object, options: opt) + } + + public func rawString(encoding: UInt = NSUTF8StringEncoding, options opt: NSJSONWritingOptions = .PrettyPrinted) -> String? { + switch self.type { + case .Array, .Dictionary: + do { + let data = try self.rawData(options: opt) + return NSString(data: data, encoding: encoding) as? String + } catch _ { + return nil + } + case .String: + return self.rawString + case .Number: + return self.rawNumber.stringValue + case .Bool: + return self.rawNumber.boolValue.description + case .Null: + return "null" + default: + return nil + } + } +} + +// MARK: - Printable, DebugPrintable + +extension JSON: Swift.Printable, Swift.DebugPrintable { + + public var description: String { + if let string = self.rawString(options:.PrettyPrinted) { + return string + } else { + return "unknown" + } + } + + public var debugDescription: String { + return description + } +} + +// MARK: - Array + +extension JSON { + + //Optional [JSON] + public var array: [JSON]? { + get { + if self.type == .Array { + return self.rawArray.map{ JSON($0) } + } else { + return nil + } + } + } + + //Non-optional [JSON] + public var arrayValue: [JSON] { + get { + return self.array ?? [] + } + } + + //Optional [AnyObject] + public var arrayObject: [AnyObject]? { + get { + switch self.type { + case .Array: + return self.rawArray + default: + return nil + } + } + set { + if let array = newValue { + self.object = array + } else { + self.object = NSNull() + } + } + } +} + +// MARK: - Dictionary + +extension JSON { + + //Optional [String : JSON] + public var dictionary: [String : JSON]? { + if self.type == .Dictionary { + return self.rawDictionary.reduce([String : JSON]()) { (dictionary: [String : JSON], element: (String, AnyObject)) -> [String : JSON] in + var d = dictionary + d[element.0] = JSON(element.1) + return d + } + } else { + return nil + } + } + + //Non-optional [String : JSON] + public var dictionaryValue: [String : JSON] { + return self.dictionary ?? [:] + } + + //Optional [String : AnyObject] + public var dictionaryObject: [String : AnyObject]? { + get { + switch self.type { + case .Dictionary: + return self.rawDictionary + default: + return nil + } + } + set { + if let v = newValue { + self.object = v + } else { + self.object = NSNull() + } + } + } +} + +// MARK: - Bool + +extension JSON: Swift.BooleanType { + + //Optional bool + public var bool: Bool? { + get { + switch self.type { + case .Bool: + return self.rawNumber.boolValue + default: + return nil + } + } + set { + if let newValue = newValue { + self.object = NSNumber(bool: newValue) + } else { + self.object = NSNull() + } + } + } + + //Non-optional bool + public var boolValue: Bool { + get { + switch self.type { + case .Bool, .Number, .String: + return self.object.boolValue + default: + return false + } + } + set { + self.object = NSNumber(bool: newValue) + } + } +} + +// MARK: - String + +extension JSON { + + //Optional string + public var string: String? { + get { + switch self.type { + case .String: + return self.object as? String + default: + return nil + } + } + set { + if let newValue = newValue { + self.object = NSString(string:newValue) + } else { + self.object = NSNull() + } + } + } + + //Non-optional string + public var stringValue: String { + get { + switch self.type { + case .String: + return self.object as? String ?? "" + case .Number: + return self.object.stringValue + case .Bool: + return (self.object as? Bool).map { String($0) } ?? "" + default: + return "" + } + } + set { + self.object = NSString(string:newValue) + } + } +} + +// MARK: - Number +extension JSON { + + //Optional number + public var number: NSNumber? { + get { + switch self.type { + case .Number, .Bool: + return self.rawNumber + default: + return nil + } + } + set { + self.object = newValue ?? NSNull() + } + } + + //Non-optional number + public var numberValue: NSNumber { + get { + switch self.type { + case .String: + let decimal = NSDecimalNumber(string: self.object as? String) + if decimal == NSDecimalNumber.notANumber() { // indicates parse error + return NSDecimalNumber.zero() + } + return decimal + case .Number, .Bool: + return self.object as? NSNumber ?? NSNumber(int: 0) + default: + return NSNumber(double: 0.0) + } + } + set { + self.object = newValue + } + } +} + +//MARK: - Null +extension JSON { + + public var null: NSNull? { + get { + switch self.type { + case .Null: + return self.rawNull + default: + return nil + } + } + set { + self.object = NSNull() + } + } + public func isExists() -> Bool{ + if let errorValue = error where errorValue.code == ErrorNotExist{ + return false + } + return true + } +} + +//MARK: - URL +extension JSON { + + //Optional URL + public var URL: NSURL? { + get { + switch self.type { + case .String: + if let encodedString_ = self.rawString.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet()) { + return NSURL(string: encodedString_) + } else { + return nil + } + default: + return nil + } + } + set { + self.object = newValue?.absoluteString ?? NSNull() + } + } +} + +// MARK: - Int, Double, Float, Int8, Int16, Int32, Int64 + +extension JSON { + + public var double: Double? { + get { + return self.number?.doubleValue + } + set { + if let newValue = newValue { + self.object = NSNumber(double: newValue) + } else { + self.object = NSNull() + } + } + } + + public var doubleValue: Double { + get { + return self.numberValue.doubleValue + } + set { + self.object = NSNumber(double: newValue) + } + } + + public var float: Float? { + get { + return self.number?.floatValue + } + set { + if let newValue = newValue { + self.object = NSNumber(float: newValue) + } else { + self.object = NSNull() + } + } + } + + public var floatValue: Float { + get { + return self.numberValue.floatValue + } + set { + self.object = NSNumber(float: newValue) + } + } + + public var int: Int? { + get { + return self.number?.longValue + } + set { + if let newValue = newValue { + self.object = NSNumber(integer: newValue) + } else { + self.object = NSNull() + } + } + } + + public var intValue: Int { + get { + return self.numberValue.integerValue + } + set { + self.object = NSNumber(integer: newValue) + } + } + + public var uInt: UInt? { + get { + return self.number?.unsignedLongValue + } + set { + if let newValue = newValue { + self.object = NSNumber(unsignedLong: newValue) + } else { + self.object = NSNull() + } + } + } + + public var uIntValue: UInt { + get { + return self.numberValue.unsignedLongValue + } + set { + self.object = NSNumber(unsignedLong: newValue) + } + } + + public var int8: Int8? { + get { + return self.number?.charValue + } + set { + if let newValue = newValue { + self.object = NSNumber(char: newValue) + } else { + self.object = NSNull() + } + } + } + + public var int8Value: Int8 { + get { + return self.numberValue.charValue + } + set { + self.object = NSNumber(char: newValue) + } + } + + public var uInt8: UInt8? { + get { + return self.number?.unsignedCharValue + } + set { + if let newValue = newValue { + self.object = NSNumber(unsignedChar: newValue) + } else { + self.object = NSNull() + } + } + } + + public var uInt8Value: UInt8 { + get { + return self.numberValue.unsignedCharValue + } + set { + self.object = NSNumber(unsignedChar: newValue) + } + } + + public var int16: Int16? { + get { + return self.number?.shortValue + } + set { + if let newValue = newValue { + self.object = NSNumber(short: newValue) + } else { + self.object = NSNull() + } + } + } + + public var int16Value: Int16 { + get { + return self.numberValue.shortValue + } + set { + self.object = NSNumber(short: newValue) + } + } + + public var uInt16: UInt16? { + get { + return self.number?.unsignedShortValue + } + set { + if let newValue = newValue { + self.object = NSNumber(unsignedShort: newValue) + } else { + self.object = NSNull() + } + } + } + + public var uInt16Value: UInt16 { + get { + return self.numberValue.unsignedShortValue + } + set { + self.object = NSNumber(unsignedShort: newValue) + } + } + + public var int32: Int32? { + get { + return self.number?.intValue + } + set { + if let newValue = newValue { + self.object = NSNumber(int: newValue) + } else { + self.object = NSNull() + } + } + } + + public var int32Value: Int32 { + get { + return self.numberValue.intValue + } + set { + self.object = NSNumber(int: newValue) + } + } + + public var uInt32: UInt32? { + get { + return self.number?.unsignedIntValue + } + set { + if let newValue = newValue { + self.object = NSNumber(unsignedInt: newValue) + } else { + self.object = NSNull() + } + } + } + + public var uInt32Value: UInt32 { + get { + return self.numberValue.unsignedIntValue + } + set { + self.object = NSNumber(unsignedInt: newValue) + } + } + + public var int64: Int64? { + get { + return self.number?.longLongValue + } + set { + if let newValue = newValue { + self.object = NSNumber(longLong: newValue) + } else { + self.object = NSNull() + } + } + } + + public var int64Value: Int64 { + get { + return self.numberValue.longLongValue + } + set { + self.object = NSNumber(longLong: newValue) + } + } + + public var uInt64: UInt64? { + get { + return self.number?.unsignedLongLongValue + } + set { + if let newValue = newValue { + self.object = NSNumber(unsignedLongLong: newValue) + } else { + self.object = NSNull() + } + } + } + + public var uInt64Value: UInt64 { + get { + return self.numberValue.unsignedLongLongValue + } + set { + self.object = NSNumber(unsignedLongLong: newValue) + } + } +} + +//MARK: - Comparable +extension JSON : Swift.Comparable {} + +public func ==(lhs: JSON, rhs: JSON) -> Bool { + + switch (lhs.type, rhs.type) { + case (.Number, .Number): + return lhs.rawNumber == rhs.rawNumber + case (.String, .String): + return lhs.rawString == rhs.rawString + case (.Bool, .Bool): + return lhs.rawNumber.boolValue == rhs.rawNumber.boolValue + case (.Array, .Array): + return lhs.rawArray as NSArray == rhs.rawArray as NSArray + case (.Dictionary, .Dictionary): + return lhs.rawDictionary as NSDictionary == rhs.rawDictionary as NSDictionary + case (.Null, .Null): + return true + default: + return false + } +} + +public func <=(lhs: JSON, rhs: JSON) -> Bool { + + switch (lhs.type, rhs.type) { + case (.Number, .Number): + return lhs.rawNumber <= rhs.rawNumber + case (.String, .String): + return lhs.rawString <= rhs.rawString + case (.Bool, .Bool): + return lhs.rawNumber.boolValue == rhs.rawNumber.boolValue + case (.Array, .Array): + return lhs.rawArray as NSArray == rhs.rawArray as NSArray + case (.Dictionary, .Dictionary): + return lhs.rawDictionary as NSDictionary == rhs.rawDictionary as NSDictionary + case (.Null, .Null): + return true + default: + return false + } +} + +public func >=(lhs: JSON, rhs: JSON) -> Bool { + + switch (lhs.type, rhs.type) { + case (.Number, .Number): + return lhs.rawNumber >= rhs.rawNumber + case (.String, .String): + return lhs.rawString >= rhs.rawString + case (.Bool, .Bool): + return lhs.rawNumber.boolValue == rhs.rawNumber.boolValue + case (.Array, .Array): + return lhs.rawArray as NSArray == rhs.rawArray as NSArray + case (.Dictionary, .Dictionary): + return lhs.rawDictionary as NSDictionary == rhs.rawDictionary as NSDictionary + case (.Null, .Null): + return true + default: + return false + } +} + +public func >(lhs: JSON, rhs: JSON) -> Bool { + + switch (lhs.type, rhs.type) { + case (.Number, .Number): + return lhs.rawNumber > rhs.rawNumber + case (.String, .String): + return lhs.rawString > rhs.rawString + default: + return false + } +} + +public func <(lhs: JSON, rhs: JSON) -> Bool { + + switch (lhs.type, rhs.type) { + case (.Number, .Number): + return lhs.rawNumber < rhs.rawNumber + case (.String, .String): + return lhs.rawString < rhs.rawString + default: + return false + } +} + +private let trueNumber = NSNumber(bool: true) +private let falseNumber = NSNumber(bool: false) +private let trueObjCType = String.fromCString(trueNumber.objCType) +private let falseObjCType = String.fromCString(falseNumber.objCType) + +// MARK: - NSNumber: Comparable + +extension NSNumber { + var isBool:Bool { + get { + let objCType = String.fromCString(self.objCType) + if (self.compare(trueNumber) == NSComparisonResult.OrderedSame && objCType == trueObjCType) + || (self.compare(falseNumber) == NSComparisonResult.OrderedSame && objCType == falseObjCType){ + return true + } else { + return false + } + } + } +} + +func ==(lhs: NSNumber, rhs: NSNumber) -> Bool { + switch (lhs.isBool, rhs.isBool) { + case (false, true): + return false + case (true, false): + return false + default: + return lhs.compare(rhs) == NSComparisonResult.OrderedSame + } +} + +func !=(lhs: NSNumber, rhs: NSNumber) -> Bool { + return !(lhs == rhs) +} + +func <(lhs: NSNumber, rhs: NSNumber) -> Bool { + + switch (lhs.isBool, rhs.isBool) { + case (false, true): + return false + case (true, false): + return false + default: + return lhs.compare(rhs) == NSComparisonResult.OrderedAscending + } +} + +func >(lhs: NSNumber, rhs: NSNumber) -> Bool { + + switch (lhs.isBool, rhs.isBool) { + case (false, true): + return false + case (true, false): + return false + default: + return lhs.compare(rhs) == NSComparisonResult.OrderedDescending + } +} + +func <=(lhs: NSNumber, rhs: NSNumber) -> Bool { + + switch (lhs.isBool, rhs.isBool) { + case (false, true): + return false + case (true, false): + return false + default: + return lhs.compare(rhs) != NSComparisonResult.OrderedDescending + } +} + +func >=(lhs: NSNumber, rhs: NSNumber) -> Bool { + + switch (lhs.isBool, rhs.isBool) { + case (false, true): + return false + case (true, false): + return false + default: + return lhs.compare(rhs) != NSComparisonResult.OrderedAscending + } +} diff --git a/Pods/Target Support Files/Pods/Info.plist b/Pods/Target Support Files/Pods/Info.plist new file mode 100644 index 0000000..6974542 --- /dev/null +++ b/Pods/Target Support Files/Pods/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + org.cocoapods.${PRODUCT_NAME:rfc1034identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Pods/Target Support Files/Pods/Pods-acknowledgements.markdown b/Pods/Target Support Files/Pods/Pods-acknowledgements.markdown new file mode 100644 index 0000000..e94f7c4 --- /dev/null +++ b/Pods/Target Support Files/Pods/Pods-acknowledgements.markdown @@ -0,0 +1,28 @@ +# Acknowledgements +This application makes use of the following third party libraries: + +## SwiftyJSON + +The MIT License (MIT) + +Copyright (c) 2014 Ruoyu Fu + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Generated by CocoaPods - http://cocoapods.org diff --git a/Pods/Target Support Files/Pods/Pods-acknowledgements.plist b/Pods/Target Support Files/Pods/Pods-acknowledgements.plist new file mode 100644 index 0000000..b9eceab --- /dev/null +++ b/Pods/Target Support Files/Pods/Pods-acknowledgements.plist @@ -0,0 +1,58 @@ + + + + + PreferenceSpecifiers + + + FooterText + This application makes use of the following third party libraries: + Title + Acknowledgements + Type + PSGroupSpecifier + + + FooterText + The MIT License (MIT) + +Copyright (c) 2014 Ruoyu Fu + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + Title + SwiftyJSON + Type + PSGroupSpecifier + + + FooterText + Generated by CocoaPods - http://cocoapods.org + Title + + Type + PSGroupSpecifier + + + StringsTable + Acknowledgements + Title + Acknowledgements + + diff --git a/Pods/Target Support Files/Pods/Pods-dummy.m b/Pods/Target Support Files/Pods/Pods-dummy.m new file mode 100644 index 0000000..ade64bd --- /dev/null +++ b/Pods/Target Support Files/Pods/Pods-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Pods : NSObject +@end +@implementation PodsDummy_Pods +@end diff --git a/Pods/Target Support Files/Pods/Pods-frameworks.sh b/Pods/Target Support Files/Pods/Pods-frameworks.sh new file mode 100755 index 0000000..d385bd1 --- /dev/null +++ b/Pods/Target Support Files/Pods/Pods-frameworks.sh @@ -0,0 +1,91 @@ +#!/bin/sh +set -e + +echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" +mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + +SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" + +install_framework() +{ + if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then + local source="${BUILT_PRODUCTS_DIR}/$1" + elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then + local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" + elif [ -r "$1" ]; then + local source="$1" + fi + + local destination="${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + + if [ -L "${source}" ]; then + echo "Symlinked..." + source="$(readlink "${source}")" + fi + + # use filter instead of exclude so missing patterns dont' throw errors + echo "rsync -av --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" + rsync -av --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" + + local basename + basename="$(basename -s .framework "$1")" + binary="${destination}/${basename}.framework/${basename}" + if ! [ -r "$binary" ]; then + binary="${destination}/${basename}" + fi + + # Strip invalid architectures so "fat" simulator / device frameworks work on device + if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then + strip_invalid_archs "$binary" + fi + + # Resign the code if required by the build settings to avoid unstable apps + code_sign_if_enabled "${destination}/$(basename "$1")" + + # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. + if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then + local swift_runtime_libs + swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) + for lib in $swift_runtime_libs; do + echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" + rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" + code_sign_if_enabled "${destination}/${lib}" + done + fi +} + +# Signs a framework with the provided identity +code_sign_if_enabled() { + if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then + # Use the current code_sign_identitiy + echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" + echo "/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} --preserve-metadata=identifier,entitlements \"$1\"" + /usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} --preserve-metadata=identifier,entitlements "$1" + fi +} + +# Strip invalid architectures +strip_invalid_archs() { + binary="$1" + # Get architectures for current file + archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | rev)" + stripped="" + for arch in $archs; do + if ! [[ "${VALID_ARCHS}" == *"$arch"* ]]; then + # Strip non-valid architectures in-place + lipo -remove "$arch" -output "$binary" "$binary" || exit 1 + stripped="$stripped $arch" + fi + done + if [[ "$stripped" ]]; then + echo "Stripped $binary of architectures:$stripped" + fi +} + + +if [[ "$CONFIGURATION" == "Debug" ]]; then + install_framework "Pods/SwiftyJSON.framework" +fi +if [[ "$CONFIGURATION" == "Release" ]]; then + install_framework "Pods/SwiftyJSON.framework" +fi diff --git a/Pods/Target Support Files/Pods/Pods-resources.sh b/Pods/Target Support Files/Pods/Pods-resources.sh new file mode 100755 index 0000000..16774fb --- /dev/null +++ b/Pods/Target Support Files/Pods/Pods-resources.sh @@ -0,0 +1,95 @@ +#!/bin/sh +set -e + +mkdir -p "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" + +RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt +> "$RESOURCES_TO_COPY" + +XCASSET_FILES=() + +realpath() { + DIRECTORY="$(cd "${1%/*}" && pwd)" + FILENAME="${1##*/}" + echo "$DIRECTORY/$FILENAME" +} + +install_resource() +{ + case $1 in + *.storyboard) + echo "ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .storyboard`.storyboardc ${PODS_ROOT}/$1 --sdk ${SDKROOT}" + ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .storyboard`.storyboardc" "${PODS_ROOT}/$1" --sdk "${SDKROOT}" + ;; + *.xib) + echo "ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .xib`.nib ${PODS_ROOT}/$1 --sdk ${SDKROOT}" + ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .xib`.nib" "${PODS_ROOT}/$1" --sdk "${SDKROOT}" + ;; + *.framework) + echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + echo "rsync -av ${PODS_ROOT}/$1 ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + rsync -av "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + ;; + *.xcdatamodel) + echo "xcrun momc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1"`.mom\"" + xcrun momc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodel`.mom" + ;; + *.xcdatamodeld) + echo "xcrun momc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodeld`.momd\"" + xcrun momc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodeld`.momd" + ;; + *.xcmappingmodel) + echo "xcrun mapc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcmappingmodel`.cdm\"" + xcrun mapc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcmappingmodel`.cdm" + ;; + *.xcassets) + ABSOLUTE_XCASSET_FILE=$(realpath "${PODS_ROOT}/$1") + XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") + ;; + /*) + echo "$1" + echo "$1" >> "$RESOURCES_TO_COPY" + ;; + *) + echo "${PODS_ROOT}/$1" + echo "${PODS_ROOT}/$1" >> "$RESOURCES_TO_COPY" + ;; + esac +} + +mkdir -p "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then + mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" + rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +fi +rm -f "$RESOURCES_TO_COPY" + +if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] +then + case "${TARGETED_DEVICE_FAMILY}" in + 1,2) + TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" + ;; + 1) + TARGET_DEVICE_ARGS="--target-device iphone" + ;; + 2) + TARGET_DEVICE_ARGS="--target-device ipad" + ;; + *) + TARGET_DEVICE_ARGS="--target-device mac" + ;; + esac + + # Find all other xcassets (this unfortunately includes those of path pods and other targets). + OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) + while read line; do + if [[ $line != "`realpath $PODS_ROOT`*" ]]; then + XCASSET_FILES+=("$line") + fi + done <<<"$OTHER_XCASSETS" + + printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${IPHONEOS_DEPLOYMENT_TARGET}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" +fi diff --git a/Pods/Target Support Files/Pods/Pods-umbrella.h b/Pods/Target Support Files/Pods/Pods-umbrella.h new file mode 100644 index 0000000..21dcfd2 --- /dev/null +++ b/Pods/Target Support Files/Pods/Pods-umbrella.h @@ -0,0 +1,6 @@ +#import + + +FOUNDATION_EXPORT double PodsVersionNumber; +FOUNDATION_EXPORT const unsigned char PodsVersionString[]; + diff --git a/Pods/Target Support Files/Pods/Pods.debug.xcconfig b/Pods/Target Support Files/Pods/Pods.debug.xcconfig new file mode 100644 index 0000000..217d667 --- /dev/null +++ b/Pods/Target Support Files/Pods/Pods.debug.xcconfig @@ -0,0 +1,8 @@ +EMBEDDED_CONTENT_CONTAINS_SWIFT = YES +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' +OTHER_CFLAGS = $(inherited) -iquote "$CONFIGURATION_BUILD_DIR/SwiftyJSON.framework/Headers" +OTHER_LDFLAGS = $(inherited) -framework "SwiftyJSON" +OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" +PODS_FRAMEWORK_BUILD_PATH = $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods +PODS_ROOT = ${SRCROOT}/Pods \ No newline at end of file diff --git a/Pods/Target Support Files/Pods/Pods.modulemap b/Pods/Target Support Files/Pods/Pods.modulemap new file mode 100644 index 0000000..8413413 --- /dev/null +++ b/Pods/Target Support Files/Pods/Pods.modulemap @@ -0,0 +1,6 @@ +framework module Pods { + umbrella header "Pods-umbrella.h" + + export * + module * { export * } +} diff --git a/Pods/Target Support Files/Pods/Pods.release.xcconfig b/Pods/Target Support Files/Pods/Pods.release.xcconfig new file mode 100644 index 0000000..217d667 --- /dev/null +++ b/Pods/Target Support Files/Pods/Pods.release.xcconfig @@ -0,0 +1,8 @@ +EMBEDDED_CONTENT_CONTAINS_SWIFT = YES +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' +OTHER_CFLAGS = $(inherited) -iquote "$CONFIGURATION_BUILD_DIR/SwiftyJSON.framework/Headers" +OTHER_LDFLAGS = $(inherited) -framework "SwiftyJSON" +OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" +PODS_FRAMEWORK_BUILD_PATH = $(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/Pods +PODS_ROOT = ${SRCROOT}/Pods \ No newline at end of file diff --git a/Pods/Target Support Files/SwiftyJSON/Info.plist b/Pods/Target Support Files/SwiftyJSON/Info.plist new file mode 100644 index 0000000..23e7588 --- /dev/null +++ b/Pods/Target Support Files/SwiftyJSON/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + org.cocoapods.${PRODUCT_NAME:rfc1034identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 2.3.2 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Pods/Target Support Files/SwiftyJSON/SwiftyJSON-dummy.m b/Pods/Target Support Files/SwiftyJSON/SwiftyJSON-dummy.m new file mode 100644 index 0000000..3159bec --- /dev/null +++ b/Pods/Target Support Files/SwiftyJSON/SwiftyJSON-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_SwiftyJSON : NSObject +@end +@implementation PodsDummy_SwiftyJSON +@end diff --git a/Pods/Target Support Files/SwiftyJSON/SwiftyJSON-prefix.pch b/Pods/Target Support Files/SwiftyJSON/SwiftyJSON-prefix.pch new file mode 100644 index 0000000..aa992a4 --- /dev/null +++ b/Pods/Target Support Files/SwiftyJSON/SwiftyJSON-prefix.pch @@ -0,0 +1,4 @@ +#ifdef __OBJC__ +#import +#endif + diff --git a/Pods/Target Support Files/SwiftyJSON/SwiftyJSON-umbrella.h b/Pods/Target Support Files/SwiftyJSON/SwiftyJSON-umbrella.h new file mode 100644 index 0000000..ce00ad0 --- /dev/null +++ b/Pods/Target Support Files/SwiftyJSON/SwiftyJSON-umbrella.h @@ -0,0 +1,6 @@ +#import + + +FOUNDATION_EXPORT double SwiftyJSONVersionNumber; +FOUNDATION_EXPORT const unsigned char SwiftyJSONVersionString[]; + diff --git a/Pods/Target Support Files/SwiftyJSON/SwiftyJSON.modulemap b/Pods/Target Support Files/SwiftyJSON/SwiftyJSON.modulemap new file mode 100644 index 0000000..6f41751 --- /dev/null +++ b/Pods/Target Support Files/SwiftyJSON/SwiftyJSON.modulemap @@ -0,0 +1,6 @@ +framework module SwiftyJSON { + umbrella header "SwiftyJSON-umbrella.h" + + export * + module * { export * } +} diff --git a/Pods/Target Support Files/SwiftyJSON/SwiftyJSON.xcconfig b/Pods/Target Support Files/SwiftyJSON/SwiftyJSON.xcconfig new file mode 100644 index 0000000..6fb4cff --- /dev/null +++ b/Pods/Target Support Files/SwiftyJSON/SwiftyJSON.xcconfig @@ -0,0 +1,5 @@ +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/SwiftyJSON" "${PODS_ROOT}/Headers/Public" +OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" +PODS_ROOT = ${SRCROOT} +SKIP_INSTALL = YES \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..ea36495 --- /dev/null +++ b/README.md @@ -0,0 +1,27 @@ +
+![UmbrellaWeather](https://github.com/ZeroJian/ZeroJian.github.io/blob/master/img/SupportLogo.png?raw=true) +
+##UmbrellaWeather +###使用 Swift 编写的一款天气应用,现已上架 [AppStore](https://itunes.apple.com/cn/app/umbrellaweather-xia-yu-tong/id1079751819?mt=8) + +![launch](https://github.com/ZeroJian/ZeroJian.github.io/blob/master/img/launch.gif?raw=true) +![addCity](https://github.com/ZeroJian/ZeroJian.github.io/blob/master/img/cityView.gif?raw=true) +![support](https://github.com/ZeroJian/ZeroJian.github.io/blob/master/img/supportView.gif?raw=true) +###项目特点 +- 使用自带 API NSURLSession CoreLocation 网络请求和定位 +- 使用纯 storyboard 布局 +- 7点本地通知提醒 +- 加入了后台刷新更新数据功能 +- NSKeyedArchiver 持久化储蓄 +- UIView 和 layout 动画 +- 加入了内购和跳转到商店功能 + +###使用的第三方库和资源 +JSON 解析: [swiftyJSON](https://github.com/SwiftyJSON/SwiftyJSON) + +天气 Icons 使用的免费资源,自己使用 Skench 增加了颜色 [免费 Icons 网址](http://sm-artists.com/?page_id=925) + +###注意事项 +使用的和风天气免费 API 服务,每天访问次数 3000 + +如果访问超出限制可自行前往注册 [和风天气官网](http://www.heweather.com) diff --git a/UmbrellaWeather.xcodeproj/project.pbxproj b/UmbrellaWeather.xcodeproj/project.pbxproj new file mode 100644 index 0000000..7bb9314 --- /dev/null +++ b/UmbrellaWeather.xcodeproj/project.pbxproj @@ -0,0 +1,550 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 320A8F191C74EC600024F518 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 320A8F1B1C74EC600024F518 /* Main.storyboard */; }; + 3215992B1C3E35CC00F68FA4 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3215992A1C3E35CC00F68FA4 /* AppDelegate.swift */; }; + 3215994C1C3E371300F68FA4 /* ServiceResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3215994B1C3E371300F68FA4 /* ServiceResult.swift */; }; + 3215994E1C3E371900F68FA4 /* WeatherResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3215994D1C3E371900F68FA4 /* WeatherResult.swift */; }; + 321599501C3E371E00F68FA4 /* DailyResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3215994F1C3E371E00F68FA4 /* DailyResult.swift */; }; + 321599541C3E372800F68FA4 /* LocationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 321599531C3E372800F68FA4 /* LocationService.swift */; }; + 321599581C3E376C00F68FA4 /* DimmingPresentationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 321599571C3E376C00F68FA4 /* DimmingPresentationController.swift */; }; + 3215995A1C3E377200F68FA4 /* GradientView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 321599591C3E377200F68FA4 /* GradientView.swift */; }; + 3215995E1C3E378F00F68FA4 /* City.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3215995D1C3E378F00F68FA4 /* City.swift */; }; + 321599601C3E379400F68FA4 /* ParserXML.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3215995F1C3E379400F68FA4 /* ParserXML.swift */; }; + 321599621C3E379900F68FA4 /* Citys.xml in Resources */ = {isa = PBXBuildFile; fileRef = 321599611C3E379900F68FA4 /* Citys.xml */; }; + 321599681C3E3B8E00F68FA4 /* DataModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 321599671C3E3B8E00F68FA4 /* DataModel.swift */; }; + 3215996E1C3E3C1300F68FA4 /* TimePickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3215996D1C3E3C1300F68FA4 /* TimePickerViewController.swift */; }; + 321599701C3E3CA500F68FA4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3215996F1C3E3CA500F68FA4 /* Assets.xcassets */; }; + 322172481C5B7C03008EBA1D /* CityListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 322172471C5B7C03008EBA1D /* CityListCell.swift */; }; + 3221724A1C5B8194008EBA1D /* CityListCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 322172491C5B8194008EBA1D /* CityListCell.xib */; }; + 326730E31C5A1CC100340338 /* FirstView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 326730E21C5A1CC100340338 /* FirstView.xib */; }; + 326730E51C5A1CE700340338 /* FirstView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 326730E41C5A1CE700340338 /* FirstView.swift */; }; + 326730E71C5A649F00340338 /* WaterSound.wav in Resources */ = {isa = PBXBuildFile; fileRef = 326730E61C5A649E00340338 /* WaterSound.wav */; }; + 32B94B481C55E0DD006EE4BB /* UIImageView+WeatherImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32B94B471C55E0DD006EE4BB /* UIImageView+WeatherImage.swift */; }; + 32D152F41C48B0EE000A4959 /* DINNextLTPro-Light.otf in Resources */ = {isa = PBXBuildFile; fileRef = 32D152F31C48ACEB000A4959 /* DINNextLTPro-Light.otf */; }; + 32D153711C50BFED000A4959 /* WeekWeatherCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 32D153701C50BFED000A4959 /* WeekWeatherCell.xib */; }; + 32D153791C50DD5D000A4959 /* WeekWeatherCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32D153781C50DD5D000A4959 /* WeekWeatherCell.swift */; }; + 32D1539D1C50FB05000A4959 /* HomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32D1539C1C50FB05000A4959 /* HomeViewController.swift */; }; + 32D153A31C52B4E5000A4959 /* WeekWeatherCollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32D153A21C52B4E5000A4959 /* WeekWeatherCollectionView.swift */; }; + 32D153A61C52B8AB000A4959 /* MainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32D153A51C52B8AB000A4959 /* MainView.swift */; }; + 32F211B81C5358DE00B80498 /* CityListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32F211B71C5358DE00B80498 /* CityListViewController.swift */; }; + 32F211BC1C5361E600B80498 /* SupportTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32F211BB1C5361E600B80498 /* SupportTableViewController.swift */; }; + 32F211BE1C53EA6000B80498 /* PullView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32F211BD1C53EA6000B80498 /* PullView.swift */; }; + 32F211C01C53EFC900B80498 /* InteractiveAnimation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 32F211BF1C53EFC900B80498 /* InteractiveAnimation.swift */; }; + A70F355714621E5F27718690 /* Pods.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EC37378A4ED40604FEEFD272 /* Pods.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 320A8F1A1C74EC600024F518 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 320A8F1C1C74ECBA0024F518 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Main.strings"; sourceTree = ""; }; + 321599271C3E35CC00F68FA4 /* UmbrellaWeather.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = UmbrellaWeather.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 3215992A1C3E35CC00F68FA4 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = AppDelegate.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + 321599361C3E35CC00F68FA4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = ../Info.plist; sourceTree = ""; }; + 3215994B1C3E371300F68FA4 /* ServiceResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = ServiceResult.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + 3215994D1C3E371900F68FA4 /* WeatherResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WeatherResult.swift; path = ../Server/WeatherResult.swift; sourceTree = ""; }; + 3215994F1C3E371E00F68FA4 /* DailyResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DailyResult.swift; path = ../Server/DailyResult.swift; sourceTree = ""; }; + 321599531C3E372800F68FA4 /* LocationService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = LocationService.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + 321599571C3E376C00F68FA4 /* DimmingPresentationController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DimmingPresentationController.swift; sourceTree = ""; }; + 321599591C3E377200F68FA4 /* GradientView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GradientView.swift; sourceTree = ""; }; + 3215995D1C3E378F00F68FA4 /* City.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = City.swift; sourceTree = ""; }; + 3215995F1C3E379400F68FA4 /* ParserXML.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ParserXML.swift; sourceTree = ""; }; + 321599611C3E379900F68FA4 /* Citys.xml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = Citys.xml; sourceTree = ""; }; + 321599671C3E3B8E00F68FA4 /* DataModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataModel.swift; sourceTree = ""; }; + 3215996D1C3E3C1300F68FA4 /* TimePickerViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TimePickerViewController.swift; sourceTree = ""; }; + 3215996F1C3E3CA500F68FA4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = ../Assets.xcassets; sourceTree = ""; }; + 322172471C5B7C03008EBA1D /* CityListCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = CityListCell.swift; path = ../CityListCell.swift; sourceTree = ""; }; + 322172491C5B8194008EBA1D /* CityListCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = CityListCell.xib; path = ../CityListCell.xib; sourceTree = ""; }; + 326730E21C5A1CC100340338 /* FirstView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = FirstView.xib; path = ../../FirstView.xib; sourceTree = ""; }; + 326730E41C5A1CE700340338 /* FirstView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = FirstView.swift; path = ../FirstView.swift; sourceTree = ""; }; + 326730E61C5A649E00340338 /* WaterSound.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; name = WaterSound.wav; path = ../WaterSound.wav; sourceTree = ""; }; + 32B94B471C55E0DD006EE4BB /* UIImageView+WeatherImage.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIImageView+WeatherImage.swift"; sourceTree = ""; }; + 32D152F31C48ACEB000A4959 /* DINNextLTPro-Light.otf */ = {isa = PBXFileReference; lastKnownFileType = file; name = "DINNextLTPro-Light.otf"; path = "../DINNextLTPro-Light.otf"; sourceTree = ""; }; + 32D153701C50BFED000A4959 /* WeekWeatherCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; name = WeekWeatherCell.xib; path = ../WeekWeatherCell.xib; sourceTree = ""; }; + 32D153781C50DD5D000A4959 /* WeekWeatherCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WeekWeatherCell.swift; path = ../WeekWeatherCell.swift; sourceTree = ""; }; + 32D1539C1C50FB05000A4959 /* HomeViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = HomeViewController.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + 32D153A21C52B4E5000A4959 /* WeekWeatherCollectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = WeekWeatherCollectionView.swift; path = ../WeekWeatherCollectionView.swift; sourceTree = ""; }; + 32D153A51C52B8AB000A4959 /* MainView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = MainView.swift; path = ../MainView.swift; sourceTree = ""; }; + 32F211B71C5358DE00B80498 /* CityListViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CityListViewController.swift; sourceTree = ""; }; + 32F211BB1C5361E600B80498 /* SupportTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SupportTableViewController.swift; sourceTree = ""; }; + 32F211BD1C53EA6000B80498 /* PullView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; name = PullView.swift; path = ../PullView.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; + 32F211BF1C53EFC900B80498 /* InteractiveAnimation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = InteractiveAnimation.swift; sourceTree = ""; }; + E76A1C10B9FEC9B6E129190B /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = ""; }; + EC37378A4ED40604FEEFD272 /* Pods.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + FF206AD08664F268F593CBC0 /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 321599241C3E35CC00F68FA4 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + A70F355714621E5F27718690 /* Pods.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 3215991E1C3E35CC00F68FA4 = { + isa = PBXGroup; + children = ( + 321599291C3E35CC00F68FA4 /* UmbrellaWeather */, + 321599281C3E35CC00F68FA4 /* Products */, + 7EE8AE5283A59FF782005F6F /* Pods */, + E3678054E28D90CF25381A9E /* Frameworks */, + ); + sourceTree = ""; + }; + 321599281C3E35CC00F68FA4 /* Products */ = { + isa = PBXGroup; + children = ( + 321599271C3E35CC00F68FA4 /* UmbrellaWeather.app */, + ); + name = Products; + sourceTree = ""; + }; + 321599291C3E35CC00F68FA4 /* UmbrellaWeather */ = { + isa = PBXGroup; + children = ( + 3215992A1C3E35CC00F68FA4 /* AppDelegate.swift */, + 32D1539C1C50FB05000A4959 /* HomeViewController.swift */, + 3215996D1C3E3C1300F68FA4 /* TimePickerViewController.swift */, + 32F211BB1C5361E600B80498 /* SupportTableViewController.swift */, + 32F211B71C5358DE00B80498 /* CityListViewController.swift */, + 3221724B1C5B8854008EBA1D /* View */, + 321599421C3E36BD00F68FA4 /* Animated */, + 321599411C3E36A700F68FA4 /* Service */, + 321599401C3E368C00F68FA4 /* DataModel */, + 3221724D1C5B89D4008EBA1D /* Interface */, + 3221724C1C5B8960008EBA1D /* Sources */, + ); + path = UmbrellaWeather; + sourceTree = ""; + }; + 321599401C3E368C00F68FA4 /* DataModel */ = { + isa = PBXGroup; + children = ( + 321599671C3E3B8E00F68FA4 /* DataModel.swift */, + 3215994D1C3E371900F68FA4 /* WeatherResult.swift */, + 3215994F1C3E371E00F68FA4 /* DailyResult.swift */, + 3215995D1C3E378F00F68FA4 /* City.swift */, + 3215995F1C3E379400F68FA4 /* ParserXML.swift */, + 321599611C3E379900F68FA4 /* Citys.xml */, + ); + path = DataModel; + sourceTree = ""; + }; + 321599411C3E36A700F68FA4 /* Service */ = { + isa = PBXGroup; + children = ( + 3215994B1C3E371300F68FA4 /* ServiceResult.swift */, + 321599531C3E372800F68FA4 /* LocationService.swift */, + 32B94B471C55E0DD006EE4BB /* UIImageView+WeatherImage.swift */, + ); + name = Service; + path = Server; + sourceTree = ""; + }; + 321599421C3E36BD00F68FA4 /* Animated */ = { + isa = PBXGroup; + children = ( + 321599571C3E376C00F68FA4 /* DimmingPresentationController.swift */, + 321599591C3E377200F68FA4 /* GradientView.swift */, + 32F211BF1C53EFC900B80498 /* InteractiveAnimation.swift */, + ); + path = Animated; + sourceTree = ""; + }; + 3221724B1C5B8854008EBA1D /* View */ = { + isa = PBXGroup; + children = ( + 32F211BD1C53EA6000B80498 /* PullView.swift */, + 32D153A51C52B8AB000A4959 /* MainView.swift */, + 32D153A21C52B4E5000A4959 /* WeekWeatherCollectionView.swift */, + 32D153781C50DD5D000A4959 /* WeekWeatherCell.swift */, + 322172471C5B7C03008EBA1D /* CityListCell.swift */, + 326730E41C5A1CE700340338 /* FirstView.swift */, + ); + path = View; + sourceTree = ""; + }; + 3221724C1C5B8960008EBA1D /* Sources */ = { + isa = PBXGroup; + children = ( + 32D152F31C48ACEB000A4959 /* DINNextLTPro-Light.otf */, + 326730E61C5A649E00340338 /* WaterSound.wav */, + 3215996F1C3E3CA500F68FA4 /* Assets.xcassets */, + 321599361C3E35CC00F68FA4 /* Info.plist */, + ); + path = Sources; + sourceTree = ""; + }; + 3221724D1C5B89D4008EBA1D /* Interface */ = { + isa = PBXGroup; + children = ( + 320A8F1B1C74EC600024F518 /* Main.storyboard */, + 32D153701C50BFED000A4959 /* WeekWeatherCell.xib */, + 322172491C5B8194008EBA1D /* CityListCell.xib */, + 326730E21C5A1CC100340338 /* FirstView.xib */, + ); + path = Interface; + sourceTree = ""; + }; + 7EE8AE5283A59FF782005F6F /* Pods */ = { + isa = PBXGroup; + children = ( + FF206AD08664F268F593CBC0 /* Pods.debug.xcconfig */, + E76A1C10B9FEC9B6E129190B /* Pods.release.xcconfig */, + ); + name = Pods; + sourceTree = ""; + }; + E3678054E28D90CF25381A9E /* Frameworks */ = { + isa = PBXGroup; + children = ( + EC37378A4ED40604FEEFD272 /* Pods.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 321599261C3E35CC00F68FA4 /* UmbrellaWeather */ = { + isa = PBXNativeTarget; + buildConfigurationList = 321599391C3E35CC00F68FA4 /* Build configuration list for PBXNativeTarget "UmbrellaWeather" */; + buildPhases = ( + 78B24D53E7F9E0A23C8E8425 /* Check Pods Manifest.lock */, + 321599231C3E35CC00F68FA4 /* Sources */, + 321599241C3E35CC00F68FA4 /* Frameworks */, + 321599251C3E35CC00F68FA4 /* Resources */, + EA3A16AFAEF3D099302BE743 /* Embed Pods Frameworks */, + 4139B63036010023A30CB969 /* Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = UmbrellaWeather; + productName = UmbrellaWeather; + productReference = 321599271C3E35CC00F68FA4 /* UmbrellaWeather.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 3215991F1C3E35CC00F68FA4 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0720; + LastUpgradeCheck = 0720; + ORGANIZATIONNAME = ZeroJian; + TargetAttributes = { + 321599261C3E35CC00F68FA4 = { + CreatedOnToolsVersion = 7.2; + DevelopmentTeam = MAXTGUJN5L; + SystemCapabilities = { + com.apple.BackgroundModes = { + enabled = 1; + }; + com.apple.InterAppAudio = { + enabled = 0; + }; + com.apple.Push = { + enabled = 0; + }; + }; + }; + }; + }; + buildConfigurationList = 321599221C3E35CC00F68FA4 /* Build configuration list for PBXProject "UmbrellaWeather" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 3215991E1C3E35CC00F68FA4; + productRefGroup = 321599281C3E35CC00F68FA4 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 321599261C3E35CC00F68FA4 /* UmbrellaWeather */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 321599251C3E35CC00F68FA4 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 32D152F41C48B0EE000A4959 /* DINNextLTPro-Light.otf in Resources */, + 320A8F191C74EC600024F518 /* Main.storyboard in Resources */, + 321599621C3E379900F68FA4 /* Citys.xml in Resources */, + 3221724A1C5B8194008EBA1D /* CityListCell.xib in Resources */, + 321599701C3E3CA500F68FA4 /* Assets.xcassets in Resources */, + 326730E71C5A649F00340338 /* WaterSound.wav in Resources */, + 326730E31C5A1CC100340338 /* FirstView.xib in Resources */, + 32D153711C50BFED000A4959 /* WeekWeatherCell.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 4139B63036010023A30CB969 /* Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + 78B24D53E7F9E0A23C8E8425 /* Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; + EA3A16AFAEF3D099302BE743 /* Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 321599231C3E35CC00F68FA4 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 3215992B1C3E35CC00F68FA4 /* AppDelegate.swift in Sources */, + 321599541C3E372800F68FA4 /* LocationService.swift in Sources */, + 32D153791C50DD5D000A4959 /* WeekWeatherCell.swift in Sources */, + 3215994C1C3E371300F68FA4 /* ServiceResult.swift in Sources */, + 32F211B81C5358DE00B80498 /* CityListViewController.swift in Sources */, + 32F211C01C53EFC900B80498 /* InteractiveAnimation.swift in Sources */, + 322172481C5B7C03008EBA1D /* CityListCell.swift in Sources */, + 32F211BC1C5361E600B80498 /* SupportTableViewController.swift in Sources */, + 32D1539D1C50FB05000A4959 /* HomeViewController.swift in Sources */, + 32F211BE1C53EA6000B80498 /* PullView.swift in Sources */, + 32D153A61C52B8AB000A4959 /* MainView.swift in Sources */, + 32D153A31C52B4E5000A4959 /* WeekWeatherCollectionView.swift in Sources */, + 32B94B481C55E0DD006EE4BB /* UIImageView+WeatherImage.swift in Sources */, + 321599601C3E379400F68FA4 /* ParserXML.swift in Sources */, + 3215994E1C3E371900F68FA4 /* WeatherResult.swift in Sources */, + 321599501C3E371E00F68FA4 /* DailyResult.swift in Sources */, + 3215995E1C3E378F00F68FA4 /* City.swift in Sources */, + 3215996E1C3E3C1300F68FA4 /* TimePickerViewController.swift in Sources */, + 3215995A1C3E377200F68FA4 /* GradientView.swift in Sources */, + 326730E51C5A1CE700340338 /* FirstView.swift in Sources */, + 321599581C3E376C00F68FA4 /* DimmingPresentationController.swift in Sources */, + 321599681C3E3B8E00F68FA4 /* DataModel.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 320A8F1B1C74EC600024F518 /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 320A8F1A1C74EC600024F518 /* Base */, + 320A8F1C1C74ECBA0024F518 /* zh-Hans */, + ); + name = Main.storyboard; + path = ..; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 321599371C3E35CC00F68FA4 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + 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 = 9.2; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 321599381C3E35CC00F68FA4 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + 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 = 9.2; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 3215993A1C3E35CC00F68FA4 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = FF206AD08664F268F593CBC0 /* Pods.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = "Brand Assets"; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "iPhone Distribution: Jian Zhang (MAXTGUJN5L)"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution: Jian Zhang (MAXTGUJN5L)"; + INFOPLIST_FILE = UmbrellaWeather/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/lib", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.zerojian.UmbWeather; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE = ""; + SWIFT_OBJC_BRIDGING_HEADER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + TARGETED_DEVICE_FAMILY = 1; + }; + name = Debug; + }; + 3215993B1C3E35CC00F68FA4 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = E76A1C10B9FEC9B6E129190B /* Pods.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = "Brand Assets"; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_IDENTITY = "iPhone Distribution: Jian Zhang (MAXTGUJN5L)"; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution: Jian Zhang (MAXTGUJN5L)"; + INFOPLIST_FILE = UmbrellaWeather/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/lib", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.zerojian.UmbWeather; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE = ""; + SWIFT_OBJC_BRIDGING_HEADER = ""; + TARGETED_DEVICE_FAMILY = 1; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 321599221C3E35CC00F68FA4 /* Build configuration list for PBXProject "UmbrellaWeather" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 321599371C3E35CC00F68FA4 /* Debug */, + 321599381C3E35CC00F68FA4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 321599391C3E35CC00F68FA4 /* Build configuration list for PBXNativeTarget "UmbrellaWeather" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 3215993A1C3E35CC00F68FA4 /* Debug */, + 3215993B1C3E35CC00F68FA4 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 3215991F1C3E35CC00F68FA4 /* Project object */; +} diff --git a/UmbrellaWeather.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/UmbrellaWeather.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..9e1e774 --- /dev/null +++ b/UmbrellaWeather.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/UmbrellaWeather.xcodeproj/project.xcworkspace/xcuserdata/ZeroJian.xcuserdatad/UserInterfaceState.xcuserstate b/UmbrellaWeather.xcodeproj/project.xcworkspace/xcuserdata/ZeroJian.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..fa36060 Binary files /dev/null and b/UmbrellaWeather.xcodeproj/project.xcworkspace/xcuserdata/ZeroJian.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/UmbrellaWeather.xcodeproj/xcuserdata/ZeroJian.xcuserdatad/xcschemes/Background Fetch.xcscheme b/UmbrellaWeather.xcodeproj/xcuserdata/ZeroJian.xcuserdatad/xcschemes/Background Fetch.xcscheme new file mode 100644 index 0000000..f36d3a6 --- /dev/null +++ b/UmbrellaWeather.xcodeproj/xcuserdata/ZeroJian.xcuserdatad/xcschemes/Background Fetch.xcscheme @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UmbrellaWeather.xcodeproj/xcuserdata/ZeroJian.xcuserdatad/xcschemes/UmbrellaWeather.xcscheme b/UmbrellaWeather.xcodeproj/xcuserdata/ZeroJian.xcuserdatad/xcschemes/UmbrellaWeather.xcscheme new file mode 100644 index 0000000..fda95e5 --- /dev/null +++ b/UmbrellaWeather.xcodeproj/xcuserdata/ZeroJian.xcuserdatad/xcschemes/UmbrellaWeather.xcscheme @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UmbrellaWeather.xcodeproj/xcuserdata/ZeroJian.xcuserdatad/xcschemes/xcschememanagement.plist b/UmbrellaWeather.xcodeproj/xcuserdata/ZeroJian.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..69b3e2d --- /dev/null +++ b/UmbrellaWeather.xcodeproj/xcuserdata/ZeroJian.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,27 @@ + + + + + SchemeUserState + + Background Fetch.xcscheme + + orderHint + 1 + + UmbrellaWeather.xcscheme + + orderHint + 0 + + + SuppressBuildableAutocreation + + 321599261C3E35CC00F68FA4 + + primary + + + + + diff --git a/UmbrellaWeather.xcworkspace/contents.xcworkspacedata b/UmbrellaWeather.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..c9c50c0 --- /dev/null +++ b/UmbrellaWeather.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/UmbrellaWeather.xcworkspace/xcuserdata/ZeroJian.xcuserdatad/UserInterfaceState.xcuserstate b/UmbrellaWeather.xcworkspace/xcuserdata/ZeroJian.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..6a98ca0 Binary files /dev/null and b/UmbrellaWeather.xcworkspace/xcuserdata/ZeroJian.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/UmbrellaWeather.xcworkspace/xcuserdata/ZeroJian.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/UmbrellaWeather.xcworkspace/xcuserdata/ZeroJian.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 0000000..ed9a9b4 --- /dev/null +++ b/UmbrellaWeather.xcworkspace/xcuserdata/ZeroJian.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,5 @@ + + + diff --git a/UmbrellaWeather/Animated/DimmingPresentationController.swift b/UmbrellaWeather/Animated/DimmingPresentationController.swift new file mode 100644 index 0000000..46006f3 --- /dev/null +++ b/UmbrellaWeather/Animated/DimmingPresentationController.swift @@ -0,0 +1,21 @@ +// +// DimmingPresentationController.swift +// UmberellaWeather +// +// Created by ZeroJianMBP on 15/12/30. +// Copyright © 2015年 ZeroJian. All rights reserved. +// + +import UIKit + +class DimmingPresentationController: UIPresentationController{ + override func shouldRemovePresentersView() -> Bool { + return false + } + + lazy var dimmingView = GradientView(frame: CGRect.zero) + override func presentationTransitionWillBegin() { + dimmingView.frame = containerView!.bounds + containerView!.insertSubview(dimmingView, atIndex: 0) + } +} diff --git a/UmbrellaWeather/Animated/GradientView.swift b/UmbrellaWeather/Animated/GradientView.swift new file mode 100644 index 0000000..baae383 --- /dev/null +++ b/UmbrellaWeather/Animated/GradientView.swift @@ -0,0 +1,23 @@ +// +// GradientView.swift +// UmberellaWeather +// +// Created by ZeroJianMBP on 15/12/30. +// Copyright © 2015年 ZeroJian. All rights reserved. +// + +import UIKit + +class GradientView: UIView{ + + override init(frame: CGRect) { + super.init(frame: frame) + + backgroundColor = UIColor.clearColor() + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + backgroundColor = UIColor.clearColor() + } +} diff --git a/UmbrellaWeather/Animated/InteractiveAnimation.swift b/UmbrellaWeather/Animated/InteractiveAnimation.swift new file mode 100644 index 0000000..e903b0e --- /dev/null +++ b/UmbrellaWeather/Animated/InteractiveAnimation.swift @@ -0,0 +1,68 @@ +// +// InteractiveAnimation.swift +// UmbrellaWeather +// +// Created by ZeroJianMBP on 16/1/24. +// Copyright © 2016年 ZeroJian. All rights reserved. +// + +import UIKit + +typealias animationFinshion = (Bool) -> Void + + + func springAnimation1(currtntView: UIView){ + currtntView.transform = CGAffineTransformMakeScale(0.8, 0.8) + UIView.animateWithDuration(0.3, delay: 0.0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.0, options: [], animations: { () -> Void in + currtntView.transform = CGAffineTransformMakeScale(1.0, 1.0) + }, completion: nil) + } + + func animationWithColor(superView: UIView,color: UIColor){ + UIView.transitionWithView(superView, duration: 1.0, options: UIViewAnimationOptions.TransitionCrossDissolve, animations: { () -> Void in + superView.backgroundColor = color + }, completion: nil) + } + + func loadingAnimation(imageView: UIImageView){ + if imageView.hidden{ + imageView.hidden = false + } + rotationAnimated(imageView) + } + + private func rotationAnimated(imageView: UIImageView){ + let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation.z") + rotationAnimation.toValue = NSNumber(double: M_PI * 2.0) + rotationAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut) + rotationAnimation.duration = 0.3 + rotationAnimation.repeatCount = MAXFLOAT + rotationAnimation.cumulative = false + rotationAnimation.removedOnCompletion = false + rotationAnimation.fillMode = kCAFillModeForwards + imageView.layer.addAnimation(rotationAnimation, forKey: "Rotation") + } + + func launchAnimation(currentView: UIView,finishion: animationFinshion){ + var animationDone = false + let launchView = UIImageView(frame: currentView.bounds) + launchView.imageWithResolution() + currentView.transform = CGAffineTransformMakeScale(0.8, 0.8) + launchView.transform = CGAffineTransformMakeScale(1.25, 1.25) + currentView.addSubview(launchView) + //启动图片扩大消失动画 + UIView.animateWithDuration(0.3, delay: 0.9, options: [], animations: { + launchView.transform = CGAffineTransformMakeScale(2.0, 2.0) + launchView.alpha = 0.0 + }) { (_) in + launchView.removeFromSuperview() + } + //主视图扩大弹跳动画 + UIView.animateWithDuration(0.3, delay: 1.0, usingSpringWithDamping: 0.5, initialSpringVelocity: 1.0, options: [], animations: { () -> Void in + currentView.transform = CGAffineTransformMakeScale(1.0, 1.0) + }){ (_) in + animationDone = true + finishion(animationDone) + } + } + diff --git a/UmbrellaWeather/AppDelegate.swift b/UmbrellaWeather/AppDelegate.swift new file mode 100644 index 0000000..b3e8aa0 --- /dev/null +++ b/UmbrellaWeather/AppDelegate.swift @@ -0,0 +1,63 @@ +// +// AppDelegate.swift +// UmbrellaWeather +// +// Created by ZeroJianMBP on 16/1/7. +// Copyright © 2016年 ZeroJian. All rights reserved. +// + +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + var dataModel = DataModel() + + func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { + + let controller = self.window?.rootViewController as! HomeViewController + controller.dataModel = dataModel + + UIApplication.sharedApplication().setMinimumBackgroundFetchInterval(NSTimeInterval(3600 * 12)) + + return true + } + + func application(application: UIApplication, performFetchWithCompletionHandler completionHandler: (UIBackgroundFetchResult) -> Void) { + let serviceResult = ServiceResult() + serviceResult.fetchDailyResults(dataModel.currentCity) { [weak self] dailyResults in + self?.dataModel.dailyResults = dailyResults + self?.dataModel.saveData() + completionHandler(.NewData) + } + } + + func applicationWillResignActive(application: UIApplication) { + } + + func applicationDidEnterBackground(application: UIApplication) { + saveData() + } + + func applicationWillEnterForeground(application: UIApplication) { + let serviceResult = ServiceResult() + serviceResult.fetchDailyResults(dataModel.currentCity) { [weak self] dailyResults in + self?.dataModel.dailyResults = dailyResults + } + } + + func applicationDidBecomeActive(application: UIApplication) { + + } + + func applicationWillTerminate(application: UIApplication) { + saveData() + } + + func saveData(){ + dataModel.saveData() + } + +} + diff --git a/UmbrellaWeather/Assets.xcassets/AppIcon.appiconset/Contents.json b/UmbrellaWeather/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..20ff628 --- /dev/null +++ b/UmbrellaWeather/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,44 @@ +{ + "images" : [ + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-58.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-87.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-80.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-120.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-121.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-180.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/UmbrellaWeather/Assets.xcassets/AppIcon.appiconset/Icon-120.png b/UmbrellaWeather/Assets.xcassets/AppIcon.appiconset/Icon-120.png new file mode 100644 index 0000000..65253ad Binary files /dev/null and b/UmbrellaWeather/Assets.xcassets/AppIcon.appiconset/Icon-120.png differ diff --git a/UmbrellaWeather/Assets.xcassets/AppIcon.appiconset/Icon-121.png b/UmbrellaWeather/Assets.xcassets/AppIcon.appiconset/Icon-121.png new file mode 100644 index 0000000..65253ad Binary files /dev/null and b/UmbrellaWeather/Assets.xcassets/AppIcon.appiconset/Icon-121.png differ diff --git a/UmbrellaWeather/Assets.xcassets/AppIcon.appiconset/Icon-180.png b/UmbrellaWeather/Assets.xcassets/AppIcon.appiconset/Icon-180.png new file mode 100644 index 0000000..da6dd69 Binary files /dev/null and b/UmbrellaWeather/Assets.xcassets/AppIcon.appiconset/Icon-180.png differ diff --git a/UmbrellaWeather/Assets.xcassets/AppIcon.appiconset/Icon-58.png b/UmbrellaWeather/Assets.xcassets/AppIcon.appiconset/Icon-58.png new file mode 100644 index 0000000..5b285e9 Binary files /dev/null and b/UmbrellaWeather/Assets.xcassets/AppIcon.appiconset/Icon-58.png differ diff --git a/UmbrellaWeather/Assets.xcassets/AppIcon.appiconset/Icon-80.png b/UmbrellaWeather/Assets.xcassets/AppIcon.appiconset/Icon-80.png new file mode 100644 index 0000000..7f62e8f Binary files /dev/null and b/UmbrellaWeather/Assets.xcassets/AppIcon.appiconset/Icon-80.png differ diff --git a/UmbrellaWeather/Assets.xcassets/AppIcon.appiconset/Icon-87.png b/UmbrellaWeather/Assets.xcassets/AppIcon.appiconset/Icon-87.png new file mode 100644 index 0000000..59c5bde Binary files /dev/null and b/UmbrellaWeather/Assets.xcassets/AppIcon.appiconset/Icon-87.png differ diff --git a/UmbrellaWeather/Assets.xcassets/Arrows.imageset/Arrows.pdf b/UmbrellaWeather/Assets.xcassets/Arrows.imageset/Arrows.pdf new file mode 100644 index 0000000..8172ac0 Binary files /dev/null and b/UmbrellaWeather/Assets.xcassets/Arrows.imageset/Arrows.pdf differ diff --git a/UmbrellaWeather/Assets.xcassets/Arrows.imageset/Contents.json b/UmbrellaWeather/Assets.xcassets/Arrows.imageset/Contents.json new file mode 100644 index 0000000..5ad514d --- /dev/null +++ b/UmbrellaWeather/Assets.xcassets/Arrows.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Arrows.pdf", + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/UmbrellaWeather/Assets.xcassets/Brand Assets.launchimage/Contents.json b/UmbrellaWeather/Assets.xcassets/Brand Assets.launchimage/Contents.json new file mode 100644 index 0000000..5acb3d5 --- /dev/null +++ b/UmbrellaWeather/Assets.xcassets/Brand Assets.launchimage/Contents.json @@ -0,0 +1,43 @@ +{ + "images" : [ + { + "extent" : "full-screen", + "idiom" : "iphone", + "subtype" : "736h", + "filename" : "IPhone 6PLus.png", + "minimum-system-version" : "8.0", + "orientation" : "portrait", + "scale" : "3x" + }, + { + "extent" : "full-screen", + "idiom" : "iphone", + "subtype" : "667h", + "filename" : "IPhone6.png", + "minimum-system-version" : "8.0", + "orientation" : "portrait", + "scale" : "2x" + }, + { + "orientation" : "portrait", + "idiom" : "iphone", + "filename" : "IPhone4.png", + "extent" : "full-screen", + "minimum-system-version" : "7.0", + "scale" : "2x" + }, + { + "extent" : "full-screen", + "idiom" : "iphone", + "subtype" : "retina4", + "filename" : "IPhone5.png", + "minimum-system-version" : "7.0", + "orientation" : "portrait", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/UmbrellaWeather/Assets.xcassets/Brand Assets.launchimage/IPhone 6PLus.png b/UmbrellaWeather/Assets.xcassets/Brand Assets.launchimage/IPhone 6PLus.png new file mode 100644 index 0000000..b09411a Binary files /dev/null and b/UmbrellaWeather/Assets.xcassets/Brand Assets.launchimage/IPhone 6PLus.png differ diff --git a/UmbrellaWeather/Assets.xcassets/Brand Assets.launchimage/IPhone4.png b/UmbrellaWeather/Assets.xcassets/Brand Assets.launchimage/IPhone4.png new file mode 100644 index 0000000..af5c7ca Binary files /dev/null and b/UmbrellaWeather/Assets.xcassets/Brand Assets.launchimage/IPhone4.png differ diff --git a/UmbrellaWeather/Assets.xcassets/Brand Assets.launchimage/IPhone5.png b/UmbrellaWeather/Assets.xcassets/Brand Assets.launchimage/IPhone5.png new file mode 100644 index 0000000..562d033 Binary files /dev/null and b/UmbrellaWeather/Assets.xcassets/Brand Assets.launchimage/IPhone5.png differ diff --git a/UmbrellaWeather/Assets.xcassets/Brand Assets.launchimage/IPhone6.png b/UmbrellaWeather/Assets.xcassets/Brand Assets.launchimage/IPhone6.png new file mode 100644 index 0000000..b0957b0 Binary files /dev/null and b/UmbrellaWeather/Assets.xcassets/Brand Assets.launchimage/IPhone6.png differ diff --git a/UmbrellaWeather/Assets.xcassets/Contents.json b/UmbrellaWeather/Assets.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/UmbrellaWeather/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/UmbrellaWeather/Assets.xcassets/Detail.imageset/Contents.json b/UmbrellaWeather/Assets.xcassets/Detail.imageset/Contents.json new file mode 100644 index 0000000..c00941e --- /dev/null +++ b/UmbrellaWeather/Assets.xcassets/Detail.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Detail.pdf", + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/UmbrellaWeather/Assets.xcassets/Detail.imageset/Detail.pdf b/UmbrellaWeather/Assets.xcassets/Detail.imageset/Detail.pdf new file mode 100644 index 0000000..ff941fc Binary files /dev/null and b/UmbrellaWeather/Assets.xcassets/Detail.imageset/Detail.pdf differ diff --git a/UmbrellaWeather/Assets.xcassets/DetailColor.imageset/Contents.json b/UmbrellaWeather/Assets.xcassets/DetailColor.imageset/Contents.json new file mode 100644 index 0000000..c5b0909 --- /dev/null +++ b/UmbrellaWeather/Assets.xcassets/DetailColor.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "DetailColor.pdf", + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/UmbrellaWeather/Assets.xcassets/DetailColor.imageset/DetailColor.pdf b/UmbrellaWeather/Assets.xcassets/DetailColor.imageset/DetailColor.pdf new file mode 100644 index 0000000..1165883 Binary files /dev/null and b/UmbrellaWeather/Assets.xcassets/DetailColor.imageset/DetailColor.pdf differ diff --git a/UmbrellaWeather/Assets.xcassets/Launch.imageset/Contents.json b/UmbrellaWeather/Assets.xcassets/Launch.imageset/Contents.json new file mode 100644 index 0000000..9d00fef --- /dev/null +++ b/UmbrellaWeather/Assets.xcassets/Launch.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "IPhone6-1.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "IPhone 6PLus-1.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/UmbrellaWeather/Assets.xcassets/Launch.imageset/IPhone 6PLus-1.png b/UmbrellaWeather/Assets.xcassets/Launch.imageset/IPhone 6PLus-1.png new file mode 100644 index 0000000..d14df43 Binary files /dev/null and b/UmbrellaWeather/Assets.xcassets/Launch.imageset/IPhone 6PLus-1.png differ diff --git a/UmbrellaWeather/Assets.xcassets/Launch.imageset/IPhone6-1.png b/UmbrellaWeather/Assets.xcassets/Launch.imageset/IPhone6-1.png new file mode 100644 index 0000000..a38d4d8 Binary files /dev/null and b/UmbrellaWeather/Assets.xcassets/Launch.imageset/IPhone6-1.png differ diff --git a/UmbrellaWeather/Assets.xcassets/Launch4.imageset/Contents.json b/UmbrellaWeather/Assets.xcassets/Launch4.imageset/Contents.json new file mode 100644 index 0000000..6862349 --- /dev/null +++ b/UmbrellaWeather/Assets.xcassets/Launch4.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "IPhone4-1.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/UmbrellaWeather/Assets.xcassets/Launch4.imageset/IPhone4-1.png b/UmbrellaWeather/Assets.xcassets/Launch4.imageset/IPhone4-1.png new file mode 100644 index 0000000..1e38762 Binary files /dev/null and b/UmbrellaWeather/Assets.xcassets/Launch4.imageset/IPhone4-1.png differ diff --git a/UmbrellaWeather/Assets.xcassets/Launch5.imageset/Contents.json b/UmbrellaWeather/Assets.xcassets/Launch5.imageset/Contents.json new file mode 100644 index 0000000..b6ab509 --- /dev/null +++ b/UmbrellaWeather/Assets.xcassets/Launch5.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "IPhone5-1.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/UmbrellaWeather/Assets.xcassets/Launch5.imageset/IPhone5-1.png b/UmbrellaWeather/Assets.xcassets/Launch5.imageset/IPhone5-1.png new file mode 100644 index 0000000..2ec6881 Binary files /dev/null and b/UmbrellaWeather/Assets.xcassets/Launch5.imageset/IPhone5-1.png differ diff --git a/UmbrellaWeather/Assets.xcassets/Line.imageset/Contents.json b/UmbrellaWeather/Assets.xcassets/Line.imageset/Contents.json new file mode 100644 index 0000000..62cfcff --- /dev/null +++ b/UmbrellaWeather/Assets.xcassets/Line.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Line@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "Line@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/UmbrellaWeather/Assets.xcassets/Line.imageset/Line@2x.png b/UmbrellaWeather/Assets.xcassets/Line.imageset/Line@2x.png new file mode 100644 index 0000000..4811ee8 Binary files /dev/null and b/UmbrellaWeather/Assets.xcassets/Line.imageset/Line@2x.png differ diff --git a/UmbrellaWeather/Assets.xcassets/Line.imageset/Line@3x.png b/UmbrellaWeather/Assets.xcassets/Line.imageset/Line@3x.png new file mode 100644 index 0000000..8a68441 Binary files /dev/null and b/UmbrellaWeather/Assets.xcassets/Line.imageset/Line@3x.png differ diff --git a/UmbrellaWeather/Assets.xcassets/Loading.imageset/Contents.json b/UmbrellaWeather/Assets.xcassets/Loading.imageset/Contents.json new file mode 100644 index 0000000..7fe66cf --- /dev/null +++ b/UmbrellaWeather/Assets.xcassets/Loading.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Loading@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "Loading@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/UmbrellaWeather/Assets.xcassets/Loading.imageset/Loading@2x.png b/UmbrellaWeather/Assets.xcassets/Loading.imageset/Loading@2x.png new file mode 100644 index 0000000..f2ce96f Binary files /dev/null and b/UmbrellaWeather/Assets.xcassets/Loading.imageset/Loading@2x.png differ diff --git a/UmbrellaWeather/Assets.xcassets/Loading.imageset/Loading@3x.png b/UmbrellaWeather/Assets.xcassets/Loading.imageset/Loading@3x.png new file mode 100644 index 0000000..b66adb7 Binary files /dev/null and b/UmbrellaWeather/Assets.xcassets/Loading.imageset/Loading@3x.png differ diff --git a/UmbrellaWeather/Assets.xcassets/Location.imageset/Contents.json b/UmbrellaWeather/Assets.xcassets/Location.imageset/Contents.json new file mode 100644 index 0000000..7836425 --- /dev/null +++ b/UmbrellaWeather/Assets.xcassets/Location.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Location@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "Location@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/UmbrellaWeather/Assets.xcassets/Location.imageset/Location@2x.png b/UmbrellaWeather/Assets.xcassets/Location.imageset/Location@2x.png new file mode 100644 index 0000000..0451c64 Binary files /dev/null and b/UmbrellaWeather/Assets.xcassets/Location.imageset/Location@2x.png differ diff --git a/UmbrellaWeather/Assets.xcassets/Location.imageset/Location@3x.png b/UmbrellaWeather/Assets.xcassets/Location.imageset/Location@3x.png new file mode 100644 index 0000000..334ccf9 Binary files /dev/null and b/UmbrellaWeather/Assets.xcassets/Location.imageset/Location@3x.png differ diff --git a/UmbrellaWeather/Assets.xcassets/Logo.imageset/Contents.json b/UmbrellaWeather/Assets.xcassets/Logo.imageset/Contents.json new file mode 100644 index 0000000..df525c6 --- /dev/null +++ b/UmbrellaWeather/Assets.xcassets/Logo.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Logo.pdf", + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/UmbrellaWeather/Assets.xcassets/Logo.imageset/Logo.pdf b/UmbrellaWeather/Assets.xcassets/Logo.imageset/Logo.pdf new file mode 100644 index 0000000..45251f5 Binary files /dev/null and b/UmbrellaWeather/Assets.xcassets/Logo.imageset/Logo.pdf differ diff --git a/UmbrellaWeather/Assets.xcassets/Shape.imageset/Contents.json b/UmbrellaWeather/Assets.xcassets/Shape.imageset/Contents.json new file mode 100644 index 0000000..0b38677 --- /dev/null +++ b/UmbrellaWeather/Assets.xcassets/Shape.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Shape@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "Shape@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/UmbrellaWeather/Assets.xcassets/Shape.imageset/Shape@2x.png b/UmbrellaWeather/Assets.xcassets/Shape.imageset/Shape@2x.png new file mode 100644 index 0000000..6b9b586 Binary files /dev/null and b/UmbrellaWeather/Assets.xcassets/Shape.imageset/Shape@2x.png differ diff --git a/UmbrellaWeather/Assets.xcassets/Shape.imageset/Shape@3x.png b/UmbrellaWeather/Assets.xcassets/Shape.imageset/Shape@3x.png new file mode 100644 index 0000000..52950b0 Binary files /dev/null and b/UmbrellaWeather/Assets.xcassets/Shape.imageset/Shape@3x.png differ diff --git a/UmbrellaWeather/Assets.xcassets/WeatherImage/Cloudy.imageset/Cloudy.pdf b/UmbrellaWeather/Assets.xcassets/WeatherImage/Cloudy.imageset/Cloudy.pdf new file mode 100644 index 0000000..aff48e4 Binary files /dev/null and b/UmbrellaWeather/Assets.xcassets/WeatherImage/Cloudy.imageset/Cloudy.pdf differ diff --git a/UmbrellaWeather/Assets.xcassets/WeatherImage/Cloudy.imageset/Contents.json b/UmbrellaWeather/Assets.xcassets/WeatherImage/Cloudy.imageset/Contents.json new file mode 100644 index 0000000..1998bce --- /dev/null +++ b/UmbrellaWeather/Assets.xcassets/WeatherImage/Cloudy.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Cloudy.pdf", + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/UmbrellaWeather/Assets.xcassets/WeatherImage/Contents.json b/UmbrellaWeather/Assets.xcassets/WeatherImage/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/UmbrellaWeather/Assets.xcassets/WeatherImage/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/UmbrellaWeather/Assets.xcassets/WeatherImage/Fog.imageset/Contents.json b/UmbrellaWeather/Assets.xcassets/WeatherImage/Fog.imageset/Contents.json new file mode 100644 index 0000000..3cb2a7a --- /dev/null +++ b/UmbrellaWeather/Assets.xcassets/WeatherImage/Fog.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "Fog@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "Fog@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/UmbrellaWeather/Assets.xcassets/WeatherImage/Fog.imageset/Fog@2x.png b/UmbrellaWeather/Assets.xcassets/WeatherImage/Fog.imageset/Fog@2x.png new file mode 100644 index 0000000..123eb52 Binary files /dev/null and b/UmbrellaWeather/Assets.xcassets/WeatherImage/Fog.imageset/Fog@2x.png differ diff --git a/UmbrellaWeather/Assets.xcassets/WeatherImage/Fog.imageset/Fog@3x.png b/UmbrellaWeather/Assets.xcassets/WeatherImage/Fog.imageset/Fog@3x.png new file mode 100644 index 0000000..81d5a87 Binary files /dev/null and b/UmbrellaWeather/Assets.xcassets/WeatherImage/Fog.imageset/Fog@3x.png differ diff --git a/UmbrellaWeather/Assets.xcassets/WeatherImage/Rain.imageset/Contents.json b/UmbrellaWeather/Assets.xcassets/WeatherImage/Rain.imageset/Contents.json new file mode 100644 index 0000000..f54b288 --- /dev/null +++ b/UmbrellaWeather/Assets.xcassets/WeatherImage/Rain.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Rain.pdf", + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/UmbrellaWeather/Assets.xcassets/WeatherImage/Rain.imageset/Rain.pdf b/UmbrellaWeather/Assets.xcassets/WeatherImage/Rain.imageset/Rain.pdf new file mode 100644 index 0000000..8515a73 Binary files /dev/null and b/UmbrellaWeather/Assets.xcassets/WeatherImage/Rain.imageset/Rain.pdf differ diff --git a/UmbrellaWeather/Assets.xcassets/WeatherImage/Snow.imageset/Contents.json b/UmbrellaWeather/Assets.xcassets/WeatherImage/Snow.imageset/Contents.json new file mode 100644 index 0000000..65f59e0 --- /dev/null +++ b/UmbrellaWeather/Assets.xcassets/WeatherImage/Snow.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Snow.pdf", + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/UmbrellaWeather/Assets.xcassets/WeatherImage/Snow.imageset/Snow.pdf b/UmbrellaWeather/Assets.xcassets/WeatherImage/Snow.imageset/Snow.pdf new file mode 100644 index 0000000..c3827f9 Binary files /dev/null and b/UmbrellaWeather/Assets.xcassets/WeatherImage/Snow.imageset/Snow.pdf differ diff --git a/UmbrellaWeather/Assets.xcassets/WeatherImage/Sun.imageset/Contents.json b/UmbrellaWeather/Assets.xcassets/WeatherImage/Sun.imageset/Contents.json new file mode 100644 index 0000000..708962b --- /dev/null +++ b/UmbrellaWeather/Assets.xcassets/WeatherImage/Sun.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "Sun9.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/UmbrellaWeather/Assets.xcassets/WeatherImage/Sun.imageset/Sun9.pdf b/UmbrellaWeather/Assets.xcassets/WeatherImage/Sun.imageset/Sun9.pdf new file mode 100644 index 0000000..a655fc8 Binary files /dev/null and b/UmbrellaWeather/Assets.xcassets/WeatherImage/Sun.imageset/Sun9.pdf differ diff --git a/UmbrellaWeather/Base.lproj/Main.storyboard b/UmbrellaWeather/Base.lproj/Main.storyboard new file mode 100644 index 0000000..a9d0d9c --- /dev/null +++ b/UmbrellaWeather/Base.lproj/Main.storyboard @@ -0,0 +1,891 @@ + + + + + + + + + + DINNextLTPro-Light + DINNextLTPro-Light + DINNextLTPro-Light + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UmbrellaWeather/CityListCell.swift b/UmbrellaWeather/CityListCell.swift new file mode 100644 index 0000000..99ccc96 --- /dev/null +++ b/UmbrellaWeather/CityListCell.swift @@ -0,0 +1,19 @@ +// +// CityListCell.swift +// UmbrellaWeather +// +// Created by ZeroJianMBP on 16/1/29. +// Copyright © 2016年 ZeroJian. All rights reserved. +// + +import UIKit + +class CityListCell: UITableViewCell{ + + @IBOutlet weak var cityLabel: UILabel! + + func addCityName(city: City){ + cityLabel.text = city.cityCN + } + +} diff --git a/UmbrellaWeather/CityListCell.xib b/UmbrellaWeather/CityListCell.xib new file mode 100644 index 0000000..40729a1 --- /dev/null +++ b/UmbrellaWeather/CityListCell.xib @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UmbrellaWeather/CityListViewController.swift b/UmbrellaWeather/CityListViewController.swift new file mode 100644 index 0000000..90b3b1f --- /dev/null +++ b/UmbrellaWeather/CityListViewController.swift @@ -0,0 +1,172 @@ +// +// CityListViewController.swift +// UmbrellaWeather +// +// Created by ZeroJianMBP on 16/1/23. +// Copyright © 2016年 ZeroJian. All rights reserved. +// + +import UIKit + +protocol CityListViewControllerDelegate: class{ + func cityListViewControolerDidSelectCity(controller: CityListViewController, didSelectCity city: City) + func cityListViewControllerCancel(controller: CityListViewController) + + func cityListViewControllerDeleteCity(controller: CityListViewController, currentCities cities: [City]) + +} + + +class CityListViewController: UIViewController { + + @IBOutlet weak var tableView: UITableView! + @IBOutlet weak var searchBar: UISearchBar! + @IBOutlet weak var headerView: UIView! + @IBOutlet weak var headerYConstraion: NSLayoutConstraint! + weak var delegate: CityListViewControllerDelegate? + + var cities = [City]() + var filteredCities = [City]() + var parserCities = [City]() + var parserXML:ParserXML! + + override func viewDidLoad() { + super.viewDidLoad() + tableView.contentInset = UIEdgeInsets(top: 64, left: 0, bottom: 0, right: 0) + headerYConstraion.constant -= view.bounds.height - 60 + + let cellNib = UINib(nibName: "CityListCell", bundle: nil) + tableView.registerNib(cellNib, forCellReuseIdentifier: "CityListCell") + } + + override func viewDidAppear(animated: Bool) { + super.viewDidAppear(animated) + headerView.hidden = false + headerYConstraion.constant = 0 + + UIView.animateWithDuration(0.3, delay: 0.0, options: UIViewAnimationOptions.CurveEaseOut, animations: { () -> Void in + self.view.layoutIfNeeded() + }, completion: nil) + + } + + func filterControllerForSearchText(searchText: String, scope: String = "ALL"){ + filteredCities = parserCities.filter({ (city) -> Bool in + return city.cityCN.lowercaseString.containsString(searchText.lowercaseString) + }) + tableView.reloadData() + } + + override func didReceiveMemoryWarning() { + super.didReceiveMemoryWarning() + // Dispose of any resources that can be recreated. + } + + deinit{ + parserXML = nil + filteredCities = [] + searchBar.delegate = nil + } +} + +extension CityListViewController: UISearchBarDelegate{ + + func searchBarSearchButtonClicked(searchBar: UISearchBar) { + searchBar.resignFirstResponder() + filterControllerForSearchText(searchBar.text!) + } + + func searchBar(searchBar: UISearchBar, textDidChange searchText: String) { + if parserCities.isEmpty{ + parserXML = ParserXML() + parserCities = parserXML.cities + } + + filterControllerForSearchText(searchText) + } +} + +extension CityListViewController : UITableViewDelegate{ + + func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { + if let delegate = delegate{ + let city: City + + if !filteredCities.isEmpty{ + city = filteredCities[indexPath.row] + }else{ + city = cities[indexPath.row] + } + delegate.cityListViewControolerDidSelectCity(self, didSelectCity: city) + } + searchBar.resignFirstResponder() + tableView.deselectRowAtIndexPath(indexPath, animated: true) + + } + + func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool { + if searchBar.text != ""{ + return false + }else{ + return true + } + } + + func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) { + cities.removeAtIndex(indexPath.row) + tableView.deleteRowsAtIndexPaths([indexPath], withRowAnimation: .Fade) + delegate?.cityListViewControllerDeleteCity(self, currentCities: cities) + } + + func scrollViewDidScroll(scrollView: UIScrollView) { + let offSetY = scrollView.contentOffset.y + searchBar.resignFirstResponder() + + if offSetY < -64{ + headerYConstraion.constant = -(abs(offSetY) - 64) + }else{ + headerYConstraion.constant = 0 + } + + } + + func scrollViewDidEndDragging(scrollView: UIScrollView, willDecelerate decelerate: Bool) { + + let offSetY = scrollView.contentOffset.y + + if decelerate && offSetY < -110{ + headerView.hidden = true + delegate?.cityListViewControllerCancel(self) + } + } + +} + +extension CityListViewController: UITableViewDataSource{ + + func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { + + let city: City + if searchBar.text != ""{ + let cell = tableView.dequeueReusableCellWithIdentifier("CityCell", forIndexPath: indexPath) + cell.textLabel?.textColor = UIColor.whiteColor() + city = filteredCities[indexPath.row] + cell.textLabel?.text = city.cityCN + return cell + }else{ + let cell = tableView.dequeueReusableCellWithIdentifier("CityListCell", forIndexPath: indexPath) as! CityListCell + city = cities[indexPath.row] + cell.addCityName(city) + return cell + } + } + + func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + if !filteredCities.isEmpty || searchBar.text != ""{ + return filteredCities.count + } + return cities.count + } + +} + diff --git a/UmbrellaWeather/DINNextLTPro-Light.otf b/UmbrellaWeather/DINNextLTPro-Light.otf new file mode 100755 index 0000000..26ac87c Binary files /dev/null and b/UmbrellaWeather/DINNextLTPro-Light.otf differ diff --git a/UmbrellaWeather/DataModel/City.swift b/UmbrellaWeather/DataModel/City.swift new file mode 100644 index 0000000..ecf77f7 --- /dev/null +++ b/UmbrellaWeather/DataModel/City.swift @@ -0,0 +1,28 @@ +// +// Citys.swift +// UmberellaWeather +// +// Created by ZeroJianMBP on 15/12/25. +// Copyright © 2015年 ZeroJian. All rights reserved. +// + +import Foundation + +class City: NSObject,NSCoding{ + var cityCN = "" + + override init(){ + super.init() + } + + + required init?(coder aDecoder: NSCoder) { + cityCN = aDecoder.decodeObjectForKey("CityCN") as! String + super.init() + } + + func encodeWithCoder(aCoder: NSCoder) { + aCoder.encodeObject(cityCN, forKey: "CityCN") + } + +} \ No newline at end of file diff --git a/UmbrellaWeather/DataModel/Citys.xml b/UmbrellaWeather/DataModel/Citys.xml new file mode 100644 index 0000000..f93c5e6 --- /dev/null +++ b/UmbrellaWeather/DataModel/Citys.xml @@ -0,0 +1,2569 @@ + + beijing北京 + haidian海淀 + chaoyang朝阳 + shunyi顺义 + huairou怀柔 + tongzhou通州 + changping昌平 + yanqing延庆 + fengtai丰台 + shijingshan石景山 + daxing大兴 + fangshan房山 + miyun密云 + mentougou门头沟 + pinggu平谷 + shanghai上海 + minhang闵行 + baoshan宝山 + jiading嘉定 + nanhui浦东南汇 + jinshan金山 + qingpu青浦 + songjiang松江 + fengxian奉贤 + chongming崇明 + xujiahui徐家汇 + pudong浦东 + tianjin天津 + wuqing武清 + baodi宝坻 + dongli东丽 + xiqing西青 + beichen北辰 + ninghe宁河 + hangu汉沽 + jinghai静海 + jinnan津南 + tanggu塘沽 + dagang大港 + jixian蓟县 + chongqing重庆 + yongchuan永川 + hechuan合川 + nanchuan南川 + jiangjin江津 + wansheng万盛 + yubei渝北 + beibei北碚 + banan巴南 + changshou长寿 + qianjiang黔江 + wanzhou万州 + fuling涪陵 + kaixian开县 + chengkou城口 + yunyang云阳 + wuxi巫溪 + fengjie奉节 + wushan巫山 + tongnan潼南 + dianjiang垫江 + liangping梁平 + zhongxian忠县 + shizhu石柱 + dazu大足 + rongchang荣昌 + tongliang铜梁 + bishan璧山 + fengdu丰都 + wulong武隆 + pengshui彭水 + qijiang綦江 + youyang酉阳 + xiushan秀山 + haerbin哈尔滨 + shuangcheng双城 + hulan呼兰 + acheng阿城 + binxian宾县 + yilan依兰 + bayan巴彦 + tonghe通河 + fangzheng方正 + yanshou延寿 + shangzhi尚志 + wuchang五常 + mulan木兰 + qiqihaer齐齐哈尔 + nehe讷河 + longjiang龙江 + gannan甘南 + fuyu富裕 + yian依安 + baiquan拜泉 + keshan克山 + kedong克东 + tailai泰来 + mudanjiang牡丹江 + hailin海林 + muling穆棱 + linkou林口 + suifenhe绥芬河 + ningan宁安 + dongning东宁 + jiamusi佳木斯 + tangyuan汤原 + fuyuan抚远 + huachuan桦川 + huanan桦南 + tongjiang同江 + fujin富锦 + suihua绥化 + zhaodong肇东 + anda安达 + hailun海伦 + mingshui明水 + wangkui望奎 + lanxi兰西 + qinggang青冈 + qingan庆安 + suiling绥棱 + heihe黑河 + nenjiang嫩江 + sunwu孙吴 + xunke逊克 + wudalianchi五大连池 + beian北安 + daxinganling大兴安岭 + tahe塔河 + mohe漠河 + huma呼玛 + huzhong呼中 + xinlin新林 + jiagedaqi加格达奇 + yichun伊春 + wuyiling乌伊岭 + wuying五营 + tieli铁力 + jiayin嘉荫 + daqing大庆 + lindian林甸 + zhaozhou肇州 + zhaoyuan肇源 + duerbote杜尔伯特 + qitaihe七台河 + boli勃利 + jixi鸡西 + hulin虎林 + mishan密山 + jidong鸡东 + hegang鹤岗 + suibin绥滨 + luobei萝北 + shuangyashan双鸭山 + jixian集贤 + baoqing宝清 + raohe饶河 + youyi友谊 + changchun长春 + nongan农安 + dehui德惠 + jiutai九台 + yushu榆树 + shuangyang双阳 + jilin吉林 + shulan舒兰 + yongji永吉 + jiaohe蛟河 + panshi磐石 + huadian桦甸 + yanji延吉 + dunhua敦化 + antu安图 + wangqing汪清 + helong和龙 + longjing龙井 + hunchun珲春 + tumen图们 + siping四平 + shuangliao双辽 + lishu梨树 + gongzhuling公主岭 + yitong伊通 + tonghua通化 + meihekou梅河口 + liuhe柳河 + huinan辉南 + jian集安 + tonghuaxian通化县 + baicheng白城 + taonan洮南 + daan大安 + zhenlai镇赉 + tongyu通榆 + liaoyuan辽源 + dongfeng东丰 + dongliao东辽 + songyuan松原 + qianan乾安 + qianguo前郭 + changling长岭 + fuyu扶余 + baishan白山 + jingyu靖宇 + linjiang临江 + donggang东岗 + changbai长白 + fusong抚松 + jiangyuan江源 + shenyang沈阳 + liaozhong辽中 + kangping康平 + faku法库 + xinmin新民 + dalian大连 + wafangdian瓦房店 + jinzhou金州 + pulandian普兰店 + lvshun旅顺 + changhai长海 + zhuanghe庄河 + anshan鞍山 + taian台安 + xiuyan岫岩 + haicheng海城 + fushun抚顺 + xinbin新宾 + qingyuan清原 + zhangdang章党 + benxi本溪 + benxixian本溪县 + huanren桓仁 + dandong丹东 + fengcheng凤城 + kuandian宽甸 + donggang东港 + jinzhou锦州 + linghai凌海 + yixian义县 + heishan黑山 + beizhen北镇 + yingkou营口 + dashiqiao大石桥 + gaizhou盖州 + fuxin阜新 + zhangwu彰武 + liaoyang辽阳 + liaoyangxian辽阳县 + dengta灯塔 + gongchangling弓长岭 + tieling铁岭 + kaiyuan开原 + changtu昌图 + xifeng西丰 + tiefa调兵山 + chaoyang朝阳 + lingyuan凌源 + kazuo喀左 + beipiao北票 + jianpingxian建平县 + panjin盘锦 + dawa大洼 + panshan盘山 + huludao葫芦岛 + jianchang建昌 + suizhong绥中 + xingcheng兴城 + huhehaote呼和浩特 + tuzuoqi土左旗 + tuoxian托县 + helin和林 + qingshuihe清水河 + hushijiaoqu呼市郊区 + wuchuan武川 + baotou包头 + baiyunebo白云鄂博 + mandula满都拉 + tuyouqi土右旗 + guyang固阳 + damaoqi达茂旗 + xilamuren希拉穆仁 + wuhai乌海 + jining集宁 + zhuozi卓资 + huade化德 + shangdu商都 + xinghe兴和 + liangcheng凉城 + chayouqianqi察右前旗 + chayouzhongqi察右中旗 + chayouhouqi察右后旗 + siziwangqi四子王旗 + fengzhen丰镇 + tongliao通辽 + shebotu舍伯吐 + kezuozhongqi科左中旗 + kezuohouqi科左后旗 + qinglongshan青龙山 + kailu开鲁 + kulun库伦 + naiman奈曼 + zhalute扎鲁特 + gaoliban高力板 + bayaertuhushuo巴雅尔吐胡硕 + chifeng赤峰 + aluqi阿鲁旗 + haoertu浩尔吐 + balinzuoqi巴林左旗 + balinyouqi巴林右旗 + linxi林西 + keshiketeng克什克腾 + wengniute翁牛特 + gangzi岗子 + kalaqin喀喇沁 + balihan八里罕 + ningcheng宁城 + aohan敖汉 + baoguotu宝国吐 + eerduosi鄂尔多斯 + dalate达拉特 + zhungeer准格尔 + eqianqi鄂前旗 + henan河南 + yikewusu伊克乌素 + etuoke鄂托克 + hangjinqi杭锦旗 + wushenqi乌审旗 + yijinhuoluo伊金霍洛 + wushenzhao乌审召 + dongsheng东胜 + linhe临河 + wuyuan五原 + dengkou磴口 + wuqianqi乌前旗 + dashetai大佘太 + wuzhongqi乌中旗 + wuhouqi乌后旗 + hailisu海力素 + narenbaolige那仁宝力格 + hangjinhouqi杭锦后旗 + xilinhaote锡林浩特 + erlianhaote二连浩特 + abaga阿巴嘎 + suzuoqi苏左旗 + suyouqi苏右旗 + zhurihe朱日和 + dongwuqi东乌旗 + xiwuqi西乌旗 + taipusiqi太仆寺 + xianghuang镶黄旗 + zhengxiangbaiqi正镶白旗 + zhenglanqi正蓝旗 + duolun多伦 + boketu博克图 + wulagai乌拉盖 + hailaer海拉尔 + xiaoergou小二沟 + arongqi阿荣旗 + molidawa莫力达瓦 + elunchunqi鄂伦春旗 + ewenkeqi鄂温克旗 + chenqi陈旗 + xinzuoqi新左旗 + xinyouqi新右旗 + manzhouli满洲里 + yakeshi牙克石 + zhalantun扎兰屯 + eerguna额尔古纳 + genhe根河 + tulihe图里河 + wulanhaote乌兰浩特 + aershan阿尔山 + keyouzhongqi科右中旗 + huerle胡尔勒 + zhanlaite扎赉特 + suolun索伦 + tuquan突泉 + huolinguole霍林郭勒 + keyouqianqi科右前旗 + azuoqi阿左旗 + ayouqi阿右旗 + ejina额济纳 + guanzihu拐子湖 + jilantai吉兰太 + xilingaole锡林高勒 + toudaohu头道湖 + zhongquanzi中泉子 + nuoergong诺尔公 + yabulai雅布赖 + wusitai乌斯泰 + luanjingtan孪井滩 + shijiazhuang石家庄 + jingxing井陉 + zhengding正定 + luancheng栾城 + xingtang行唐 + lingshou灵寿 + gaoyi高邑 + shenze深泽 + zanhuang赞皇 + wuji无极 + pingshan平山 + yuanshi元氏 + zhaoxian赵县 + xinji辛集 + gaocheng藁城 + jinzhou晋州 + xinle新乐 + luquan鹿泉 + baoding保定 + mancheng满城 + fuping阜平 + xushui徐水 + tangxian唐县 + gaoyang高阳 + rongcheng容城 + laiyuan涞源 + wangdu望都 + anxin安新 + yixian易县 + quyang曲阳 + lixian蠡县 + shunping顺平 + xiongxian雄县 + zhuozhou涿州 + dingzhou定州 + anguo安国 + gaobeidian高碑店 + laishui涞水 + dingxing定兴 + qingyuan清苑 + boye博野 + zhangjiakou张家口 + xuanhua宣化 + zhangbei张北 + kangbao康保 + guyuan沽源 + shangyi尚义 + yuxian蔚县 + yangyuan阳原 + huaian怀安 + wanquan万全 + huailai怀来 + zhuolu涿鹿 + chicheng赤城 + chongli崇礼 + chengde承德 + chengdexian承德县 + xinglong兴隆 + pingquan平泉 + luanping滦平 + longhua隆化 + fengning丰宁 + kuancheng宽城 + weichang围场 + tangshan唐山 + fengnan丰南 + fengrun丰润 + luanxian滦县 + luannan滦南 + leting乐亭 + qianxi迁西 + yutian玉田 + tanghai唐海 + zunhua遵化 + qianan迁安 + caofeidian曹妃甸 + langfang廊坊 + guan固安 + yongqing永清 + xianghe香河 + dacheng大城 + wenan文安 + dachang大厂 + bazhou霸州 + sanhe三河 + cangzhou沧州 + qingxian青县 + dongguang东光 + haixing海兴 + yanshan盐山 + suning肃宁 + nanpi南皮 + wuqiao吴桥 + xianxian献县 + mengcun孟村 + botou泊头 + renqiu任丘 + huanghua黄骅 + hejian河间 + cangxian沧县 + hengshui衡水 + zaoqiang枣强 + wuyi武邑 + wuqiang武强 + raoyang饶阳 + anping安平 + gucheng故城 + jingxian景县 + fucheng阜城 + jizhou冀州 + shenzhou深州 + xingtai邢台 + lincheng临城 + neiqiu内丘 + baixiang柏乡 + longyao隆尧 + nanhe南和 + ningjin宁晋 + julu巨鹿 + xinhe新河 + guangzong广宗 + pingxiang平乡 + weixian威县 + qinghe清河 + linxi临西 + nangong南宫 + shahe沙河 + renxian任县 + handan邯郸 + fengfeng峰峰 + linzhang临漳 + chengan成安 + daming大名 + shexian涉县 + cixian磁县 + feixiang肥乡 + yongnian永年 + qiuxian邱县 + jize鸡泽 + guangping广平 + guantao馆陶 + weixian魏县 + quzhou曲周 + wuan武安 + qinhuangdao秦皇岛 + qinglong青龙 + changli昌黎 + funing抚宁 + lulong卢龙 + beidaihe北戴河 + taiyuan太原 + qingxu清徐 + yangqu阳曲 + loufan娄烦 + gujiao古交 + jiancaopingqu尖草坪区 + xiaodianqu小店区 + datong大同 + yanggao阳高 + datongxian大同县 + tianzhen天镇 + guangling广灵 + lingqiu灵丘 + hunyuan浑源 + zuoyun左云 + yangquan阳泉 + yuxian盂县 + pingding平定 + jinzhong晋中 + yuci榆次 + yushe榆社 + zuoquan左权 + heshun和顺 + xiyang昔阳 + shouyang寿阳 + taigu太谷 + qixian祁县 + pingyao平遥 + lingshi灵石 + jiexiu介休 + changzhi长治 + licheng黎城 + tunliu屯留 + lucheng潞城 + xiangyuan襄垣 + pingshun平顺 + wuxiang武乡 + qinxian沁县 + zhangzi长子 + qinyuan沁源 + huguan壶关 + jincheng晋城 + qinshui沁水 + yangcheng阳城 + lingchuan陵川 + gaoping高平 + zezhou泽州 + linfen临汾 + quwo曲沃 + yonghe永和 + xixian隰县 + daning大宁 + jixian吉县 + xiangfen襄汾 + puxian蒲县 + fenxi汾西 + hongtong洪洞 + huozhou霍州 + xiangning乡宁 + yicheng翼城 + houma侯马 + fushan浮山 + anze安泽 + guxian古县 + yuncheng运城 + linyi临猗 + jishan稷山 + wanrong万荣 + hejin河津 + xinjiang新绛 + jiangxian绛县 + wenxi闻喜 + yuanqu垣曲 + yongji永济 + ruicheng芮城 + xiaxian夏县 + pinglu平陆 + shuozhou朔州 + pinglu平鲁 + shanyin山阴 + youyu右玉 + yingxian应县 + huairen怀仁 + xinzhou忻州 + dingxiang定襄 + wutaixian五台县 + hequ河曲 + pianguan偏关 + shenchi神池 + ningwu宁武 + daixian代县 + fanshi繁峙 + wutaishan五台山 + bode保德 + jingle静乐 + kelan岢岚 + wuzhai五寨 + yuanping原平 + lvliang吕梁 + lishi离石 + linxian临县 + xingxian兴县 + lanxian岚县 + liulin柳林 + shilou石楼 + fangshan方山 + jiaokou交口 + zhongyang中阳 + xiaoyi孝义 + fenyang汾阳 + wenshui文水 + jiaocheng交城 + xian西安 + changan长安 + lintong临潼 + lantian蓝田 + zhouzhi周至 + huxian户县 + gaoling高陵 + xianyang咸阳 + sanyuan三原 + liquan礼泉 + yongshou永寿 + chunhua淳化 + jingyang泾阳 + wugong武功 + qianxian乾县 + binxian彬县 + changwu长武 + xunyi旬邑 + xingping兴平 + yanan延安 + yanchang延长 + yanchuan延川 + zichang子长 + yichuan宜川 + fuxian富县 + zhidan志丹 + ansai安塞 + ganquan甘泉 + luochuan洛川 + huangling黄陵 + huanglong黄龙 + wuqi吴起 + yulin榆林 + fugu府谷 + shenmu神木 + jiaxian佳县 + dingbian定边 + jingbian靖边 + hengshan横山 + mizhi米脂 + zizhou子洲 + suide绥德 + wubu吴堡 + qingjian清涧 + yuyang榆阳 + weinan渭南 + huaxian华县 + tongguan潼关 + dali大荔 + baishui白水 + fuping富平 + pucheng蒲城 + chengcheng澄城 + heyang合阳 + hancheng韩城 + huayin华阴 + shangluo商洛 + luonan洛南 + zhashui柞水 + shangzhou商州 + zhenan镇安 + danfeng丹凤 + shangnan商南 + shanyang山阳 + ankang安康 + ziyang紫阳 + shiquan石泉 + hanyin汉阴 + xunyang旬阳 + langao岚皋 + pingli平利 + baihe白河 + zhenping镇坪 + ningshan宁陕 + hanzhong汉中 + lueyang略阳 + mianxian勉县 + liuba留坝 + yangxian洋县 + chenggu城固 + xixiang西乡 + fuoping佛坪 + ningqiang宁强 + nanzheng南郑 + zhenba镇巴 + baoji宝鸡 + qianyang千阳 + linyou麟游 + qishan岐山 + fengxiang凤翔 + fufeng扶风 + meixian眉县 + taibai太白 + fengxian凤县 + longxian陇县 + chencang陈仓 + tongchuan铜川 + yaoxian耀县 + yijun宜君 + yaozhou耀州 + yangling杨凌 + jinan济南 + changqing长清 + shanghe商河 + zhangqiu章丘 + pingyin平阴 + jiyang济阳 + qingdao青岛 + laoshan崂山 + jimo即墨 + jiaozhou胶州 + huangdao黄岛 + laixi莱西 + pingdu平度 + zibo淄博 + zichuan淄川 + boshan博山 + gaoqing高青 + zhoucun周村 + yiyuan沂源 + huantai桓台 + linzi临淄 + dezhou德州 + wucheng武城 + linyi临邑 + lingxian陵县 + qihe齐河 + leling乐陵 + qingyun庆云 + pingyuan平原 + ningjin宁津 + xiajin夏津 + yucheng禹城 + yantai烟台 + laizhou莱州 + changdao长岛 + penglai蓬莱 + longkou龙口 + zhaoyuan招远 + qixia栖霞 + fushan福山 + moup牟平 + laiyang莱阳 + haiyang海阳 + weifang潍坊 + qingzhou青州 + shouguang寿光 + linqu临朐 + changle昌乐 + changyi昌邑 + anqiu安丘 + gaomi高密 + zhucheng诸城 + jining济宁 + jiaxiang嘉祥 + weishan微山 + yutai鱼台 + yanzhou兖州 + jinxiang金乡 + wenshang汶上 + sishui泗水 + liangshan梁山 + qufu曲阜 + zoucheng邹城 + taian泰安 + xintai新泰 + feicheng肥城 + dongping东平 + ningyang宁阳 + linyi临沂 + junan莒南 + yinan沂南 + lanling兰陵 + linshu临沭 + tancheng郯城 + mengyin蒙阴 + pingyi平邑 + feixian费县 + yishui沂水 + heze菏泽 + juancheng鄄城 + yuncheng郓城 + dongming东明 + dingtao定陶 + juye巨野 + caoxian曹县 + chengwu成武 + shanxian单县 + binzhou滨州 + boxing博兴 + wudi无棣 + yangxin阳信 + huimin惠民 + zhanhua沾化 + zouping邹平 + dongying东营 + hekou河口 + kenli垦利 + lijin利津 + guangrao广饶 + weihai威海 + wendeng文登 + rongcheng荣成 + rushan乳山 + chengshantou成山头 + shidao石岛 + zaozhuang枣庄 + xuecheng薛城 + yicheng峄城 + taierzhuang台儿庄 + tengzhou滕州 + rizhao日照 + wulian五莲 + juxian莒县 + laiwu莱芜 + liaocheng聊城 + guanxian冠县 + yanggu阳谷 + gaotang高唐 + chiping茌平 + donge东阿 + linqing临清 + shenxian莘县 + wulumuqi乌鲁木齐 + xiaoquzi小渠子 + dabancheng达坂城 + wulumuqimushizhan乌鲁木齐牧试站 + tianchi天池 + baiyanggou白杨沟 + kelamayi克拉玛依 + wuerhe乌尔禾 + baijiantan白碱滩 + shihezi石河子 + paotai炮台 + mosuowan莫索湾 + changji昌吉 + hutubi呼图壁 + miquan米泉 + fukang阜康 + jimusaer吉木萨尔 + qitai奇台 + manasi玛纳斯 + mulei木垒 + caijiahu蔡家湖 + tulufan吐鲁番 + tuokexun托克逊 + shanshan鄯善 + kuerle库尔勒 + luntai轮台 + yuli尉犁 + ruoqiang若羌 + qiemo且末 + hejing和静 + yanqi焉耆 + shuo和硕 + bayinbuluke巴音布鲁克 + tieganlike铁干里克 + bohu博湖 + tazhong塔中 + baluntai巴仑台 + alaer阿拉尔 + akesu阿克苏 + wushi乌什 + wensu温宿 + baicheng拜城 + xinhe新和 + shaya沙雅 + kuche库车 + keping柯坪 + awati阿瓦提 + kashi喀什 + yingjisha英吉沙 + tashikuergan塔什库尔干 + maigaiti麦盖提 + shache莎车 + yecheng叶城 + zepu泽普 + bachu巴楚 + yuepuhu岳普湖 + jiashi伽师 + shufu疏附 + shule疏勒 + yining伊宁 + chabuchaer察布查尔 + nileke尼勒克 + yiningxian伊宁县 + gongliu巩留 + xinyuan新源 + zhaosu昭苏 + tekesi特克斯 + huocheng霍城 + huoerguosi霍尔果斯 + kuitunshi奎屯 + tacheng塔城 + yumin裕民 + emin额敏 + hebukesaier和布克赛尔 + tuoli托里 + wusu乌苏 + shawan沙湾 + hami哈密 + balikun巴里坤 + yiwu伊吾 + hetian和田 + pishan皮山 + cele策勒 + moyu墨玉 + luopu洛浦 + minfeng民丰 + yutian于田 + aletai阿勒泰 + habahe哈巴河 + jimunai吉木乃 + buerjin布尔津 + fuhai福海 + fuyun富蕴 + qinghe青河 + atushi阿图什 + wuqia乌恰 + aketao阿克陶 + aheqi阿合奇 + bole博乐 + wenquan温泉 + jinghe精河 + alashankou阿拉山口 + lasa拉萨 + dangxiong当雄 + nimu尼木 + linzhou林周 + duilongdeqing堆龙德庆 + qushui曲水 + dazi达孜 + mozhugongka墨竹工卡 + rikaze日喀则 + lazi拉孜 + nanmulin南木林 + nielamu聂拉木 + anri定日 + jiangzi江孜 + pali帕里 + zhongba仲巴 + saga萨嘎 + jilong吉隆 + angren昂仁 + dingjie定结 + sajia萨迦 + xietongmen谢通门 + gangba岗巴 + bailang白朗 + yadong亚东 + kangma康马 + renbu仁布 + shannan山南 + gongga贡嘎 + zhanan札囊 + jiacha加查 + langkazi浪卡子 + cuona错那 + longzi隆子 + zedang泽当 + naidong乃东 + sangri桑日 + luozha洛扎 + cuomei措美 + qiongjie琼结 + qusong曲松 + linzhi林芝 + bomi波密 + milin米林 + chayu察隅 + gongbujiangda工布江达 + langxian朗县 + motuo墨脱 + changdu昌都 + dingqing丁青 + bianba边坝 + luolong洛隆 + zuogong左贡 + mangkang芒康 + leiwuqi类乌齐 + basu八宿 + jiangda江达 + chaya察雅 + gongjue贡觉 + naqu那曲 + nima尼玛 + jiali嘉黎 + bange班戈 + anduo安多 + suoxian索县 + nierong聂荣 + baqing巴青 + biru比如 + shuanghu双湖 + ali阿里 + gaize改则 + shenzha申扎 + shiquanhe狮泉河 + pulan普兰 + zhada札达 + gaer噶尔 + ritu日土 + geji革吉 + cuoqin措勤 + xining西宁 + datong大通 + huangyuan湟源 + huangzhong湟中 + haidong海东 + ledu乐都 + minhe民和 + huzhu互助 + hualong化隆 + xunhua循化 + lenghu冷湖 + pingan平安 + huangnan黄南 + jianzha尖扎 + zeku泽库 + henan河南 + tongren同仁 + hainan海南 + guide贵德 + xinghai兴海 + guinan贵南 + tongde同德 + gonghe共和 + guoluo果洛 + banma班玛 + gande甘德 + dari达日 + jiuzhi久治 + madu玛多 + maqin玛沁 + yushu玉树 + chenduo称多 + zhiduo治多 + zaduo杂多 + nangqian囊谦 + qumacai曲麻莱 + haixi海西 + tianjun天峻 + wulan乌兰 + mangai茫崖 + dachaidan大柴旦 + delingha德令哈 + haibei海北 + menyuan门源 + qilian祁连 + haiman海晏 + gangcha刚察 + geermu格尔木 + dulan都兰 + lanzhou兰州 + gaolan皋兰 + yongdeng永登 + yuzhong榆中 + dingxi定西 + tongwei通渭 + longxi陇西 + weiyuan渭源 + lintao临洮 + zhangxian漳县 + minxian岷县 + anding安定 + pingliang平凉 + jingchuan泾川 + lingtai灵台 + chongxin崇信 + huating华亭 + zhuanglang庄浪 + jingning静宁 + kongtong崆峒 + qingyang庆阳 + huanxian环县 + huachi华池 + heshui合水 + zhengning正宁 + ningxian宁县 + zhenyuan镇原 + qingcheng庆城 + wuwei武威 + minqin民勤 + gulang古浪 + tianzhu天祝 + jinchang金昌 + yongchang永昌 + zhangye张掖 + sunan肃南 + minle民乐 + linze临泽 + gaotai高台 + shandan山丹 + jiuquan酒泉 + jinta金塔 + akesai阿克塞 + guazhou瓜州 + subei肃北 + yumen玉门 + dunhuang敦煌 + tianshui天水 + qingshui清水 + qinan秦安 + gangu甘谷 + wushan武山 + zhangjiachuan张家川 + maiji麦积 + wudu武都 + chengxian成县 + wenxian文县 + dangchang宕昌 + kangxian康县 + xihe西和 + lixian礼县 + huixian徽县 + liangdang两当 + linxia临夏 + kangle康乐 + yongjing永靖 + guanghe广河 + hezheng和政 + dongxiang东乡 + jishishan积石山 + hezuo合作 + lintan临潭 + zhuoni卓尼 + zhouqu舟曲 + diebu迭部 + maqu玛曲 + luqu碌曲 + xiahe夏河 + baiyin白银 + jingyuan靖远 + huining会宁 + pingchuan平川 + jingtai景泰 + jiayuguan嘉峪关 + yinchuan银川 + yongning永宁 + lingwu灵武 + helan贺兰 + shizuishan石嘴山 + huinong惠农 + pingluo平罗 + taole陶乐 + wuzhong吴忠 + tongxin同心 + yanchi盐池 + qingtongxia青铜峡 + guyuan固原 + xiji西吉 + longde隆德 + jinyuan泾源 + pengyang彭阳 + zhongwei中卫 + zhongning中宁 + haiyuan海原 + zhengzhou郑州 + gongyi巩义 + xingyang荥阳 + dengfeng登封 + xinmi新密 + xinzheng新郑 + zhongmou中牟 + shangjie上街 + anyang安阳 + tangyin汤阴 + huaxian滑县 + neihuang内黄 + linzhou林州 + xinxiang新乡 + huojia获嘉 + yuanyang原阳 + huixian辉县 + weihui卫辉 + yanjin延津 + fengqiu封丘 + changyuan长垣 + xuchang许昌 + yanling鄢陵 + xiangcheng襄城 + changge长葛 + yuzhou禹州 + pingdingshan平顶山 + jiaxian郏县 + baofeng宝丰 + ruzhou汝州 + yexian叶县 + wugang舞钢 + lushan鲁山 + shilong石龙 + xinyang信阳 + xixian息县 + luoshan罗山 + guangshan光山 + xinxian新县 + huaibin淮滨 + huangchuan潢川 + gushi固始 + shangcheng商城 + nanyang南阳 + nanzhao南召 + fangcheng方城 + sheqi社旗 + xixia西峡 + neixiang内乡 + zhenping镇平 + xichuan淅川 + xinye新野 + tanghe唐河 + dengzhou邓州 + tongbai桐柏 + kaifeng开封 + qixian杞县 + weishi尉氏 + tongxu通许 + lankao兰考 + luoyang洛阳 + xinan新安 + mengjin孟津 + yiyang宜阳 + luoning洛宁 + yichuan伊川 + songxian嵩县 + yanshi偃师 + luanchuan栾川 + ruyang汝阳 + jili吉利 + shangqiu商丘 + suixian睢县 + minquan民权 + yucheng虞城 + zhecheng柘城 + ningling宁陵 + xiayi夏邑 + yongcheng永城 + jiaozuo焦作 + xiuwu修武 + wuzhi武陟 + qinyang沁阳 + boai博爱 + wenxian温县 + mengzhou孟州 + hebi鹤壁 + xunxian浚县 + qixian淇县 + puyang濮阳 + taiqian台前 + nanle南乐 + qingfeng清丰 + fanxian范县 + zhoukou周口 + fugou扶沟 + taikang太康 + huaiyang淮阳 + xihua西华 + shangshui商水 + xiangcheng项城 + dancheng郸城 + luyi鹿邑 + shenqiu沈丘 + luohe漯河 + linying临颍 + wuyang舞阳 + zhumadian驻马店 + xiping西平 + suiping遂平 + shangcai上蔡 + runan汝南 + biyang泌阳 + pingyu平舆 + xincai新蔡 + queshan确山 + zhengyang正阳 + sanmenxia三门峡 + lingbao灵宝 + mianchi渑池 + lushi卢氏 + yima义马 + shanxian陕县 + jiyuan济源 + nanjing南京 + lishui溧水 + gaochun高淳 + jiangning江宁 + luhe六合 + jiangpu江浦 + pukou浦口 + wuxi无锡 + jiangyin江阴 + yixing宜兴 + xishan锡山 + zhenjiang镇江 + danyang丹阳 + yangzhong扬中 + jurong句容 + dantu丹徒 + suzhou苏州 + changshu常熟 + zhangjiagang张家港 + kunshan昆山 + wuzhong吴中 + wujiang吴江 + taicang太仓 + nantong南通 + haian海安 + rugao如皋 + rudong如东 + qidong启东 + haimen海门 + tongzhou通州 + yangzhou扬州 + baoying宝应 + yizheng仪征 + gaoyou高邮 + jiangdu江都 + hanjiang邗江 + yancheng盐城 + xiangshui响水 + binhai滨海 + funing阜宁 + sheyang射阳 + jianhu建湖 + dongtai东台 + dafeng大丰 + yandu盐都 + xuzhou徐州 + tongshan铜山 + fengxian丰县 + peixian沛县 + pizhou邳州 + suining睢宁 + xinyi新沂 + huaian淮安 + jinhu金湖 + xuyi盱眙 + hongze洪泽 + lianshui涟水 + huaiyinqu淮阴区 + huaianqu淮安区 + lianyungang连云港 + donghai东海 + ganyu赣榆 + guanyun灌云 + guannan灌南 + changzhou常州 + liyang溧阳 + jintan金坛 + wujin武进 + taizhou泰州 + xinghua兴化 + taixing泰兴 + jiangyan姜堰 + jingjiang靖江 + suqian宿迁 + shuyang沭阳 + siyang泗阳 + sihong泗洪 + suyu宿豫 + wuhan武汉 + caidian蔡甸 + huangpi黄陂 + xinzhou新洲 + jiangxia江夏 + dongxihu东西湖 + xiangyang襄阳 + xiangzhou襄州 + baokang保康 + nanzhang南漳 + yicheng宜城 + laohekou老河口 + gucheng谷城 + zaoyang枣阳 + ezhou鄂州 + liangzihu梁子湖 + xiaogan孝感 + anlu安陆 + yunmeng云梦 + dawu大悟 + yingcheng应城 + hanchuan汉川 + xiaochang孝昌 + huanggang黄冈 + hongan红安 + macheng麻城 + luotian罗田 + yingshan英山 + xishui浠水 + qichun蕲春 + huangmei黄梅 + wuxue武穴 + tuanfeng团风 + huangshi黄石 + daye大冶 + yangxin阳新 + tieshan铁山 + xialu下陆 + xisaishan西塞山 + xianning咸宁 + chibi赤壁 + jiayu嘉鱼 + chongyang崇阳 + tongcheng通城 + tongshan通山 + jingzhou荆州 + jiangling江陵 + gongan公安 + shishou石首 + jianli监利 + honghu洪湖 + songzi松滋 + yichang宜昌 + yuanan远安 + zigui秭归 + xingshan兴山 + wufeng五峰 + dangyang当阳 + changyang长阳 + yidu宜都 + zhijiang枝江 + sanxia三峡 + yiling夷陵 + enshi恩施 + lichuan利川 + jianshi建始 + xianfeng咸丰 + xuanen宣恩 + hefeng鹤峰 + laifeng来凤 + badong巴东 + shiyan十堰 + zhuxi竹溪 + yunxi郧西 + yunxian郧县 + zhushan竹山 + fangxian房县 + danjiangkou丹江口 + maojian茅箭 + zhangwan张湾 + shennongjia神农架 + suizhou随州 + guangshui广水 + jingmen荆门 + zhongxiang钟祥 + jingshan京山 + duodao掇刀 + shayang沙洋 + shashi沙市 + tianmen天门 + xiantao仙桃 + qianjiang潜江 + hangzhou杭州 + xiaoshan萧山 + tonglu桐庐 + chunan淳安 + jiande建德 + yuhang余杭 + linan临安 + fuyang富阳 + huzhou湖州 + changxing长兴 + anji安吉 + deqing德清 + jiaxing嘉兴 + jiashan嘉善 + haining海宁 + tongxiang桐乡 + pinghu平湖 + haiyan海盐 + ningbo宁波 + cixi慈溪 + yuyao余姚 + fenghua奉化 + xiangshan象山 + ninghai宁海 + beilun北仑 + yinzhou鄞州 + zhenhai镇海 + shaoxing绍兴 + zhuji诸暨 + shangyu上虞 + xinchang新昌 + shengzhou嵊州 + taizhou台州 + yuhuan玉环 + sanmen三门 + tiantai天台 + xianju仙居 + wenling温岭 + hongjia洪家 + linhai临海 + jiaojiang椒江 + huangyan黄岩 + luqiao路桥 + wenzhou温州 + taishun泰顺 + wencheng文成 + pingyang平阳 + ruian瑞安 + dongtou洞头 + yueqing乐清 + yongjia永嘉 + cangnan苍南 + lishui丽水 + suichang遂昌 + longquan龙泉 + jinyun缙云 + qingtian青田 + yunhe云和 + qingyuan庆元 + songyang松阳 + jingning景宁 + jinhua金华 + pujiang浦江 + lanxi兰溪 + yiwu义乌 + dongyang东阳 + wuyi武义 + yongkang永康 + panan磐安 + quzhou衢州 + changshan常山 + kaihua开化 + longyou龙游 + jiangshan江山 + qujiang衢江 + zhoushan舟山 + shengsi嵊泗 + daishan岱山 + putuo普陀 + dinghai定海 + hefei合肥 + changfeng长丰 + feidong肥东 + feixi肥西 + chaohu巢湖 + lujiang庐江 + bengbu蚌埠 + huaiyuan怀远 + guzhen固镇 + wuhe五河 + wuhu芜湖 + fanyang繁昌 + wuhuxian芜湖县 + nanling南陵 + wuwei无为 + huainan淮南 + fengtai凤台 + panji潘集 + maanshan马鞍山 + dangtu当涂 + hanshan含山 + hexian和县 + anqing安庆 + zongyang枞阳 + taihu太湖 + qianshan潜山 + huaining怀宁 + susong宿松 + wangjiang望江 + yuexi岳西 + tongcheng桐城 + suzhou宿州 + dangshan砀山 + lingbi灵璧 + sixian泗县 + xiaoxian萧县 + fuyang阜阳 + funan阜南 + yingshang颍上 + linquan临泉 + jieshou界首 + taihe太和 + bozhou亳州 + guoyang涡阳 + lixin利辛 + mengcheng蒙城 + huangshan黄山 + huangshanqu黄山区 + tunxi屯溪 + qimen祁门 + yixian黟县 + shexian歙县 + xiuning休宁 + huangshanfengjingqu黄山风景区 + chuzhou滁州 + fengyang凤阳 + mingguang明光 + dingyuan定远 + quanjiao全椒 + laian来安 + tianchang天长 + huaibei淮北 + suixi濉溪 + tongling铜陵 + xuancheng宣城 + jingxian泾县 + jingde旌德 + ningguo宁国 + jixi绩溪 + guangde广德 + langxi郎溪 + luan六安 + huoqiu霍邱 + shouxian寿县 + jinzhai金寨 + huoshan霍山 + shucheng舒城 + chizhou池州 + dongzhi东至 + qingyang青阳 + jiuhuashan九华山 + shitai石台 + fuzhou福州 + minqing闽清 + minhou闽侯 + luoyuan罗源 + lianjiang连江 + yongtai永泰 + pingtan平潭 + changle长乐 + fuqing福清 + xiamen厦门 + tongan同安 + ningde宁德 + gutian古田 + xiapu霞浦 + shouning寿宁 + zhouning周宁 + fuan福安 + zherong柘荣 + fuding福鼎 + pingnan屏南 + putian莆田 + xianyou仙游 + xiuyugang秀屿港 + hanjiang涵江 + xiuyu秀屿 + licheng荔城 + chengxiang城厢 + quanzhou泉州 + anxi安溪 + yongchun永春 + dehua德化 + nanan南安 + chongwu崇武 + huian惠安 + jinjiang晋江 + shishi石狮 + zhangzhou漳州 + changtai长泰 + nanjing南靖 + pinghe平和 + longhai龙海 + zhangpu漳浦 + zhaoan诏安 + dongshan东山 + yunxiao云霄 + huaan华安 + longyan龙岩 + changting长汀 + liancheng连城 + wuping武平 + shanghang上杭 + yongding永定 + zhangping漳平 + sanming三明 + ninghua宁化 + qingliu清流 + taining泰宁 + jiangle将乐 + jianning建宁 + mingxi明溪 + shaxian沙县 + youxi尤溪 + yongan永安 + datian大田 + nanping南平 + shunchang顺昌 + guangze光泽 + shaowu邵武 + wuyishan武夷山 + pucheng浦城 + jianyang建阳 + songxi松溪 + zhenghe政和 + jianou建瓯 + diaoyudao钓鱼岛 + nanchang南昌 + xinjian新建 + nanchangxian南昌县 + anyi安义 + jinxian进贤 + jiujiang九江 + ruichang瑞昌 + lushan庐山 + wuning武宁 + dean德安 + yongxiu永修 + hukou湖口 + pengze彭泽 + xingzi星子 + duchang都昌 + xiushui修水 + shangrao上饶 + poyang鄱阳 + wuyuan婺源 + yugan余干 + wannian万年 + dexing德兴 + shangraoxian上饶县 + yiyang弋阳 + hengfeng横峰 + yanshan铅山 + yushan玉山 + guangfeng广丰 + fuzhou抚州 + guangchang广昌 + anle乐安 + chongren崇仁 + jinxi金溪 + zixi资溪 + yihuang宜黄 + nancheng南城 + nanfeng南丰 + lichuan黎川 + dongxiang东乡 + yichun宜春 + tonggu铜鼓 + yifeng宜丰 + wanzai万载 + shanggao上高 + jingan靖安 + fengxin奉新 + gaoan高安 + zhangshu樟树 + fengcheng丰城 + jian吉安 + jianxian吉安县 + jishui吉水 + xingan新干 + xiajiang峡江 + yongfeng永丰 + yongxin永新 + jinggangshan井冈山 + wanan万安 + suichuan遂川 + taihe泰和 + anfu安福 + ninggang宁冈 + ganzhou赣州 + chongyi崇义 + shangyou上犹 + nankang南康 + dayu大余 + xinfeng信丰 + ningdu宁都 + shicheng石城 + ruijin瑞金 + yudu于都 + huichang会昌 + anyuan安远 + quannan全南 + longnan龙南 + dingnan定南 + xunwu寻乌 + xingguo兴国 + ganxian赣县 + jingdezhen景德镇 + leping乐平 + fuliang浮梁 + pingxiang萍乡 + lianhua莲花 + shangli上栗 + anyuan安源 + luxi芦溪 + xiangdong湘东 + xinyu新余 + fenyi分宜 + yingtan鹰潭 + yujiang余江 + guixi贵溪 + changsha长沙 + ningxiang宁乡 + liuyang浏阳 + mapoling马坡岭 + wangcheng望城 + xiangtan湘潭 + shaoshan韶山 + xiangxiang湘乡 + zhuzhou株洲 + youxian攸县 + liling醴陵 + chaling茶陵 + yanling炎陵 + hengyang衡阳 + hengshan衡山 + hengdong衡东 + qidong祁东 + hengyangxian衡阳县 + changning常宁 + hengnan衡南 + leiyang耒阳 + nanyue南岳 + chenzhou郴州 + guiyang桂阳 + jiahe嘉禾 + yizhang宜章 + linwu临武 + zixing资兴 + rucheng汝城 + anren安仁 + yongxing永兴 + guidong桂东 + suxian苏仙 + changde常德 + anxiang安乡 + taoyuan桃源 + hanshou汉寿 + lixian澧县 + linli临澧 + shimen石门 + jinshi津市 + yiyang益阳 + heshanqu赫山区 + nanxian南县 + taojiang桃江 + anhua安化 + yuanjiang沅江 + loudi娄底 + shuangfeng双峰 + lengshuijiang冷水江 + xinhua新化 + lianyuan涟源 + shaoyang邵阳 + longhui隆回 + dongkou洞口 + xinshao新邵 + shaodong邵东 + suining绥宁 + xinning新宁 + wugang武冈 + chengbu城步 + shaoyangxian邵阳县 + yueyang岳阳 + huarong华容 + xiangyin湘阴 + miluo汨罗 + pingjiang平江 + linxiang临湘 + zhangjiajie张家界 + sangzhi桑植 + cili慈利 + wulingyuan武陵源 + huaihua怀化 + yuanling沅陵 + chenxi辰溪 + jingzhou靖州 + huitong会同 + tongdao通道 + mayang麻阳 + xinhuang新晃 + zhijiang芷江 + xupu溆浦 + zhongfang中方 + hongjiang洪江 + yongzhou永州 + qiyang祁阳 + dongan东安 + shuangpai双牌 + daoxian道县 + ningyuan宁远 + jiangyong江永 + lanshan蓝山 + xintian新田 + jianghua江华 + lengshuitan冷水滩 + jishou吉首 + baojing保靖 + yongshun永顺 + guzhang古丈 + fenghuang凤凰 + luxi泸溪 + longshan龙山 + huayuan花垣 + guiyang贵阳 + baiyun白云 + huaxi花溪 + wudang乌当 + xifeng息烽 + kaiyang开阳 + xiuwen修文 + qingzhen清镇 + xiaohe小河 + yunyan云岩 + nanming南明 + zunyi遵义 + zunyixian遵义县 + renhuai仁怀 + suiyang绥阳 + meitan湄潭 + fenggang凤冈 + tongzi桐梓 + chishui赤水 + xishui习水 + daozhen道真 + zhengan正安 + wuchuan务川 + yuqing余庆 + huichuan汇川 + honghuagang红花岗 + anshun安顺 + puding普定 + zhenning镇宁 + pingba平坝 + ziyun紫云 + guanling关岭 + duyun都匀 + guiding贵定 + wengan瓮安 + changshun长顺 + fuquan福泉 + huishui惠水 + longli龙里 + luodian罗甸 + pingtang平塘 + dushan独山 + sandu三都 + libo荔波 + kaili凯里 + cengong岑巩 + shibing施秉 + zhenyuan镇远 + huangping黄平 + majiang麻江 + danzhai丹寨 + sansui三穗 + taijiang台江 + jianhe剑河 + leishan雷山 + liping黎平 + tianzhu天柱 + jinping锦屏 + rongjiang榕江 + congjiang从江 + tongren铜仁 + jiangkou江口 + yuping玉屏 + wanshan万山 + sinan思南 + yinjiang印江 + shiqian石阡 + yanhe沿河 + dejiang德江 + songtao松桃 + bijie毕节 + hezhang赫章 + jinsha金沙 + weining威宁 + dafang大方 + nayong纳雍 + zhijin织金 + qianxi黔西 + shuicheng水城 + liuzhi六枝 + panxian盘县 + xingyi兴义 + qinglong晴隆 + xingren兴仁 + zhenfeng贞丰 + wangmo望谟 + anlong安龙 + ceheng册亨 + puan普安 + chengdu成都 + longquanyi龙泉驿 + xindu新都 + wenjiang温江 + jintang金堂 + shuangliu双流 + pixian郫县 + dayi大邑 + pujiang蒲江 + xinjin新津 + dujiangyan都江堰 + pengzhou彭州 + qionglai邛崃 + chongzhou崇州 + qingbaijiang青白江 + panzhihua攀枝花 + renhe仁和 + miyi米易 + yanbian盐边 + zigong自贡 + fushun富顺 + rongxian荣县 + mianyang绵阳 + santai三台 + yanting盐亭 + anxian安县 + zitong梓潼 + beichuan北川 + pingwu平武 + jiangyou江油 + nanchong南充 + nanbu南部 + yingshan营山 + pengan蓬安 + yilong仪陇 + xichong西充 + langzhong阆中 + dazhou达州 + xuanhan宣汉 + kaijiang开江 + dazhu大竹 + quxian渠县 + wanyuan万源 + tongchuan通川 + daxian达县 + suining遂宁 + pengxi蓬溪 + shehong射洪 + guangan广安 + yuechi岳池 + wusheng武胜 + linshui邻水 + huaying华蓥 + bazhong巴中 + tongjiang通江 + nanjiang南江 + pingchang平昌 + luzhou泸州 + luxian泸县 + hejiang合江 + xuyong叙永 + gulin古蔺 + naxi纳溪 + yibin宜宾 + yibinxian宜宾县 + nanxi南溪 + jiangan江安 + changning长宁 + gaoxian高县 + gongxian珙县 + junlian筠连 + xingwen兴文 + pingshan屏山 + neijiang内江 + dongxing东兴 + weiyuan威远 + zizhong资中 + longchang隆昌 + ziyang资阳 + anyue安岳 + lezhi乐至 + jianyang简阳 + leshan乐山 + qianwei犍为 + jingyan井研 + jiajiang夹江 + muchuan沐川 + ebian峨边 + mabian马边 + emei峨眉 + emeishan峨眉山 + meishan眉山 + renshou仁寿 + pengshan彭山 + hongya洪雅 + danleng丹棱 + qingshen青神 + liangshan凉山 + muli木里 + yanyuan盐源 + dechang德昌 + huili会理 + huidong会东 + ningnan宁南 + puge普格 + xichang西昌 + jinyang金阳 + zhaojue昭觉 + xide喜德 + mianning冕宁 + yuexi越西 + ganluo甘洛 + leibo雷波 + meigu美姑 + butuo布拖 + yaan雅安 + mingshan名山 + yingjing荥经 + hanyuan汉源 + shimian石棉 + tianquan天全 + lushan芦山 + baoxing宝兴 + ganzi甘孜 + kangding康定 + luding泸定 + danba丹巴 + jiulong九龙 + yajiang雅江 + daofu道孚 + luhuo炉霍 + xinlong新龙 + dege德格 + baiyu白玉 + shiqu石渠 + seda色达 + litang理塘 + batang巴塘 + xiangcheng乡城 + daocheng稻城 + derong得荣 + aba阿坝 + wenchuan汶川 + lixian理县 + maoxian茂县 + songfan松潘 + jiuzhaigou九寨沟 + jinchuan金川 + xiaojin小金 + heishui黑水 + maerkang马尔康 + rangtang壤塘 + nuoergai若尔盖 + hongyuan红原 + deyang德阳 + zhongjiang中江 + guanghan广汉 + shifang什邡 + mianzhu绵竹 + luojiang罗江 + guangyuan广元 + wangcang旺苍 + qingchuan青川 + jiange剑阁 + cangxi苍溪 + guangzhou广州 + panyu番禺 + conghua从化 + zengcheng增城 + huadu花都 + shaoguan韶关 + ruyuan乳源 + shixing始兴 + wengyuan翁源 + lechang乐昌 + renhua仁化 + nanxiong南雄 + xinfeng新丰 + qujiang曲江 + zhenjiang浈江 + wujiang武江 + huizhou惠州 + boluo博罗 + huiyang惠阳 + huidong惠东 + longmen龙门 + meizhou梅州 + xingning兴宁 + jiaoling蕉岭 + dabu大埔 + fengshun丰顺 + pingyuan平远 + wuhua五华 + meixian梅县 + shantou汕头 + chaoyang潮阳 + chenghai澄海 + nanao南澳 + shenzhen深圳 + zhuhai珠海 + doumen斗门 + jinwan金湾 + foshan佛山 + shunde顺德 + sanshui三水 + nanhai南海 + gaoming高明 + zhaoqing肇庆 + guangning广宁 + sihui四会 + deqing德庆 + huaiji怀集 + fengkai封开 + gaoyao高要 + zhanjiang湛江 + wuchuan吴川 + leizhou雷州 + xuwen徐闻 + lianjiang廉江 + chikan赤坎 + suixi遂溪 + potou坡头 + xiashan霞山 + mazhang麻章 + jiangmen江门 + kaiping开平 + xinhui新会 + enping恩平 + taishan台山 + pengjiang蓬江 + heshan鹤山 + jianghai江海 + heyuan河源 + zijin紫金 + lianping连平 + heping和平 + longchuan龙川 + dongyuan东源 + qingyuan清远 + liannan连南 + lianzhou连州 + lianshan连山 + yangshan阳山 + fogang佛冈 + yingde英德 + qingxin清新 + yunfu云浮 + luoding罗定 + xinxing新兴 + yunan郁南 + yunan云安 + chaozhou潮州 + raoping饶平 + chaoan潮安 + dongguan东莞 + zhongshan中山 + yangjiang阳江 + yangchun阳春 + yangdong阳东 + yangxi阳西 + jieyang揭阳 + jiexi揭西 + puning普宁 + huilai惠来 + jiedong揭东 + maoming茂名 + gaozhou高州 + huazhou化州 + dianbai电白 + xinyi信宜 + maogang茂港 + shanwei汕尾 + haifeng海丰 + lufeng陆丰 + luhe陆河 + kunming昆明 + dongchuan东川 + xundian寻甸 + jinning晋宁 + yiliang宜良 + shilin石林 + chenggong呈贡 + fumin富民 + songming嵩明 + luquan禄劝 + anning安宁 + taihuashan太华山 + dali大理 + yunlong云龙 + yangbi漾濞 + yongping永平 + binchuan宾川 + midu弥渡 + xiangyun祥云 + weishan巍山 + jianchuan剑川 + eryuan洱源 + heqing鹤庆 + nanjian南涧 + honghe红河 + shiping石屏 + jianshui建水 + mile弥勒 + yuanyang元阳 + lvchun绿春 + kaiyuan开远 + gejiu个旧 + mengzi蒙自 + pingbian屏边 + luxi泸西 + jinping金平 + hekou河口 + qujing曲靖 + zhanyi沾益 + luliang陆良 + fuyuan富源 + malong马龙 + shizong师宗 + luoping罗平 + huize会泽 + xuanwei宣威 + baoshan保山 + longling龙陵 + sidian施甸 + changning昌宁 + tengchong腾冲 + wenshan文山 + xichou西畴 + maguan马关 + malipo麻栗坡 + yanshan砚山 + qiubei丘北 + guangnan广南 + funing富宁 + yuxi玉溪 + chengjiang澄江 + jiangchuan江川 + tonghai通海 + huaning华宁 + xinping新平 + yimen易门 + eshan峨山 + yuanjiang元江 + chuxiong楚雄 + dayao大姚 + yuanmou元谋 + yaoan姚安 + mouding牟定 + nanhua南华 + wuding武定 + lufeng禄丰 + shuangbai双柏 + yongren永仁 + puer普洱 + jinggu景谷 + jingdong景东 + lancang澜沧 + mojiang墨江 + jiangcheng江城 + menglian孟连 + ximeng西盟 + zhenyuan镇沅 + ninger宁洱 + zhaotong昭通 + ludian鲁甸 + yiliang彝良 + zhenxiong镇雄 + weixin威信 + qiaojia巧家 + suijiang绥江 + yongshan永善 + yanjin盐津 + daguan大关 + shuifu水富 + lincang临沧 + cangyuan沧源 + gengma耿马 + shuangjiang双江 + fengqing凤庆 + yongde永德 + yunxian云县 + zhenkang镇康 + nujiang怒江 + fugong福贡 + lanping兰坪 + lushui泸水 + liuku六库 + gongshan贡山 + xianggelila香格里拉 + deqin德钦 + weixi维西 + zhongdian中甸 + lijiang丽江 + yongsheng永胜 + huaping华坪 + ninglang宁蒗 + dehong德宏 + longchuan陇川 + yingjiang盈江 + ruili瑞丽 + lianghe梁河 + luxi芒市 + jinghong景洪 + menghai勐海 + mengla勐腊 + nanning南宁 + yongning邕宁 + hengxian横县 + longan隆安 + mashan马山 + shanglin上林 + wuming武鸣 + binyang宾阳 + chongzuo崇左 + tiandeng天等 + longzhou龙州 + pingxiang凭祥 + daxin大新 + fusui扶绥 + ningming宁明 + liuzhou柳州 + liucheng柳城 + luzhai鹿寨 + liujiang柳江 + rongan融安 + rongshui融水 + sanjiang三江 + laibin来宾 + xicheng忻城 + jinxiu金秀 + xiangzhou象州 + wuxuan武宣 + heshan合山 + guilin桂林 + longsheng龙胜 + yongfu永福 + lingui临桂 + xingan兴安 + lingchuan灵川 + quanzhou全州 + guanyang灌阳 + yangshuo阳朔 + gongcheng恭城 + pingle平乐 + lipu荔浦 + ziyuan资源 + wuzhou梧州 + tengxian藤县 + cangwu苍梧 + mengshan蒙山 + cenxi岑溪 + hezhou贺州 + zhaoping昭平 + fuchuan富川 + zhongshan钟山 + guigang贵港 + guiping桂平 + pingnan平南 + yulin玉林 + bobai博白 + beiliu北流 + rongxian容县 + luchuan陆川 + xingye兴业 + baise百色 + napo那坡 + tianyang田阳 + debao德保 + jingxi靖西 + tiandong田东 + pingguo平果 + longlin隆林 + xilin西林 + leye乐业 + lingyun凌云 + tianlin田林 + qinzhou钦州 + pubei浦北 + lingshan灵山 + hechi河池 + tiane天峨 + donglan东兰 + bama巴马 + huanjiang环江 + luocheng罗城 + yizhou宜州 + fengshan凤山 + nandan南丹 + andu都安 + dahua大化 + beihai北海 + hepu合浦 + weizhoudao涠洲岛 + fangchenggang防城港 + shangsi上思 + dongxing东兴 + fangcheng防城 + haikou海口 + sanya三亚 + dongfang东方 + lingao临高 + chengmai澄迈 + danzhou儋州 + changjiang昌江 + baisha白沙 + qiongzhong琼中 + dingan定安 + tunchang屯昌 + qionghai琼海 + wenchang文昌 + baoting保亭 + wanning万宁 + lingshui陵水 + xisha西沙 + nansha南沙 + ledong乐东 + wuzhishan五指山 + zhongsha中沙 + hongkong香港 + jiulong九龙 + xinjie新界 + macao澳门 + dangzidao氹仔岛 + luhuandao路环岛 + taibeixian台北 + taoyuan桃园 + xinzhu新竹 + yilan宜兰 + gaoxiong高雄 + jiayi嘉义 + tainan台南 + taidong台东 + pingdong屏东 + taizhong台中 + miaoli苗栗 + zhanghua彰化 + nantou南投 + hualian花莲 + yunlin云林 + nanzidao南子岛 +
\ No newline at end of file diff --git a/UmbrellaWeather/DataModel/DataModel.swift b/UmbrellaWeather/DataModel/DataModel.swift new file mode 100644 index 0000000..c95a0e2 --- /dev/null +++ b/UmbrellaWeather/DataModel/DataModel.swift @@ -0,0 +1,138 @@ +// +// DataModel.swift +// UmberellaWeather +// +// Created by ZeroJianMBP on 16/1/3. +// Copyright © 2016年 ZeroJian. All rights reserved. +// + +import UIKit + +class DataModel{ + var currentCity = "" + var dueString = "__ : __" + var shouldRemind = false + var cities = [City]() + var dailyResults = [DailyResult]() + + init(){ + handleFirstTime() + loadData() + } + + func scheduleNotification(){ + + removeLocalNotification() + + if shouldRemind && currentCity != ""{ + + let formatter = NSDateFormatter() + formatter.dateFormat = "yyyy-MM-ddHH:mm" + + for dailyResult in dailyResults{ + + let dateString = dailyResult.dailyDate + let pop = dailyResult.dailyPop + + let stringFormTime = dateString + dueString + + guard let notificationTime = formatter.dateFromString(stringFormTime) else{ return } + + if pop >= 10 && notificationTime.compare(NSDate()) != .OrderedAscending{ + + let localNotification = UILocalNotification() + localNotification.timeZone = NSTimeZone.defaultTimeZone() + localNotification.soundName = "WaterSound.wav" + let alertBody = "\(dateString) \(dailyResult.dailyState) 今天下雨几率为 \(pop) 记得带伞☂" + localNotification.fireDate = notificationTime + localNotification.alertBody = alertBody + UIApplication.sharedApplication().scheduleLocalNotification(localNotification) + } + } + guard let dataString = dailyResults.last?.dailyDate else{ + return + } + let localNotification = UILocalNotification() + let dueTime = formatter.dateFromString(dataString + dueString) + localNotification.timeZone = NSTimeZone.defaultTimeZone() + localNotification.soundName = "WaterSound.wav" + localNotification.fireDate = dueTime!.dateByAddingTimeInterval(30) + localNotification.alertBody = "你已经一周没有打开过软件,提醒通知将取消" + UIApplication.sharedApplication().scheduleLocalNotification(localNotification) + } + } + + private func removeLocalNotification(){ + let allNotifications = UIApplication.sharedApplication().scheduledLocalNotifications + if let allNotifications = allNotifications where !allNotifications.isEmpty{ + UIApplication.sharedApplication().cancelAllLocalNotifications() + } + } + + func appendCity(city:City){ + for thisCity in cities{ + if thisCity.cityCN == city.cityCN{ + return + } + } + cities.append(city) + } + + + private func documentsDirectory() -> String{ + let paths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true) + return paths[0] + } + + private func dataFilePath() -> String{ + return (documentsDirectory() as NSString).stringByAppendingPathComponent("DataModel.plist") + } + + func saveData(){ + let data = NSMutableData() + let archiver = NSKeyedArchiver(forWritingWithMutableData: data) + archiver.encodeObject(currentCity, forKey: "CurrentCity") + archiver.encodeObject(dueString, forKey: "DueString") + archiver.encodeBool(shouldRemind, forKey: "ShouldRemind") + archiver.encodeObject(cities, forKey: "Cities") + archiver.encodeObject(dailyResults, forKey: "DailyResults") + archiver.finishEncoding() + data.writeToFile(dataFilePath(), atomically: true) + scheduleNotification() + } + + func loadData(){ + let path = dataFilePath() + if NSFileManager.defaultManager().fileExistsAtPath(path){ + if let data = NSData(contentsOfFile: path){ + let unarchiver = NSKeyedUnarchiver(forReadingWithData: data) + currentCity = unarchiver.decodeObjectForKey("CurrentCity") as! String + dueString = unarchiver.decodeObjectForKey("DueString") as! String + shouldRemind = unarchiver.decodeBoolForKey("ShouldRemind") + cities = unarchiver.decodeObjectForKey("Cities") as! [City] + dailyResults = unarchiver.decodeObjectForKey("DailyResults") as! [DailyResult] + unarchiver.finishDecoding() + } + } + } + + //上线的新版本改变了数据结构 + func handleFirstTime(){ + let userDefaults = NSUserDefaults.standardUserDefaults() + userDefaults.registerDefaults(["FirstTime":true]) + userDefaults.registerDefaults(["IsOldData":true]) + + let isOldData = userDefaults.boolForKey("IsOldData") + if isOldData{ + let fileMager = NSFileManager() + do{ + try fileMager.removeItemAtPath(dataFilePath()) + }catch{ + print("FirstTime") + } + userDefaults.setBool(false, forKey: "IsOldData") + userDefaults.synchronize() + } + } + +} \ No newline at end of file diff --git a/UmbrellaWeather/DataModel/ParserXML.swift b/UmbrellaWeather/DataModel/ParserXML.swift new file mode 100644 index 0000000..6af754e --- /dev/null +++ b/UmbrellaWeather/DataModel/ParserXML.swift @@ -0,0 +1,41 @@ +// +// ParserXML.swift +// UmberellaWeather +// +// Created by ZeroJianMBP on 16/1/1. +// Copyright © 2016年 ZeroJian. All rights reserved. +// + +import Foundation + +class ParserXML: NSObject,NSXMLParserDelegate{ + + var elementName = "" + var cities = [City]() + + override init(){ + super.init() + parseXMLResource() + } + + func parseXMLResource(){ + let parser = NSXMLParser(contentsOfURL: NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("Citys", ofType: "xml")!)) + if let parser = parser{ + parser.delegate = self + parser.parse() + } + } + + func parser(parser: NSXMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) { + self.elementName = elementName + } + + func parser(parser: NSXMLParser, foundCharacters string: String) { + let str = string.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()) + if elementName == "city"{ + let city = City() + city.cityCN = str + cities.append(city) + } + } +} \ No newline at end of file diff --git a/UmbrellaWeather/FirstView.swift b/UmbrellaWeather/FirstView.swift new file mode 100644 index 0000000..5cb2717 --- /dev/null +++ b/UmbrellaWeather/FirstView.swift @@ -0,0 +1,33 @@ +// +// FirstView.swift +// UmbrellaWeather +// +// Created by ZeroJianMBP on 16/1/28. +// Copyright © 2016年 ZeroJian. All rights reserved. +// + +import UIKit + +class FirstView: UIView { + + override init(frame: CGRect) { + super.init(frame: frame) + loadViewFormNib() + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + loadViewFormNib() + fatalError("init(coder:) has not been implemented") + } + + func loadViewFormNib(){ + let bundle = NSBundle(forClass: self.dynamicType) + let nib = UINib(nibName: "FirstView", bundle: bundle) + let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView + view.frame = bounds + self.addSubview(view) + } + + +} diff --git a/UmbrellaWeather/HomeViewController.swift b/UmbrellaWeather/HomeViewController.swift new file mode 100644 index 0000000..2c00075 --- /dev/null +++ b/UmbrellaWeather/HomeViewController.swift @@ -0,0 +1,412 @@ +// +// HomeViewController.swift +// UmbrellaWeather +// +// Created by ZeroJianMBP on 16/1/21. +// Copyright © 2016年 ZeroJian. All rights reserved. +// + +import UIKit +import AudioToolbox + +class HomeViewController: UIViewController,TimePickerViewControllerDelegate,SupportTableViewControllerDelegate,CityListViewControllerDelegate{ + + @IBOutlet weak var scrollView: UIScrollView! + @IBOutlet weak var collectionView: UICollectionView! + @IBOutlet weak var pullDownView: PullView! + @IBOutlet weak var pullTopView: PullView! + @IBOutlet weak var mainView: MainView! + @IBOutlet weak var cityButton: UIButton! + @IBOutlet weak var dateLabel: UILabel! + @IBOutlet weak var dateView: UIView! + @IBOutlet weak var detailButton: UIButton! + + var weatherResult = WeatherResult() + var serviceResult = ServiceResult() + var dataModel: DataModel! + var firstView: UIView! + var dateString: String! + var shoudRemand = false + var soundID: SystemSoundID = 0 + + override func viewDidLoad() { + super.viewDidLoad() + loadSoundEffect("WaterSound.wav") + initialUI() + launchAnimation(self.view) { (finishion) in + if finishion{ + self.handleFirstTime() + } + } + if dataModel.shouldRemind{ + detailButton.setBackgroundImage(UIImage(named: "DetailColor"), forState: .Normal) + } + } + + @IBAction func locationButton(sender: UIButton){ + updateWeatherResult() + scrollView.setContentOffset(CGPoint(x: 0, y: 0), animated: false) + } + + func updateWeatherResult(){ + initialUI() + + if dataModel.currentCity == "" || cityButton.touchInside{ + locationCity() + }else{ + perforRequest() + } + } + + func locationCity(){ + let locationService = LocationService.singleClass + locationService.beginLocation() + + locationService.afterUpdatedCityAction = { + [weak self] sucess in + if !sucess{ + self?.mainView.loadingFinish() + let message = "定位失败,请稍后重试或上滑视图打开城市搜索" + self?.showAlert(message, "好的", againReuqest: false, shouldRemind: false) + } + self?.showLocationStatus(locationService) + } + showLocationStatus(locationService) + } + + func showLocationStatus(locationService: LocationService){ + switch locationService.locationState{ + case .NoService: + if mainView.loading{ + mainView.loadingFinish() + } + let message = "没有打开定位服务,请在系统设置里激活程序的定位服务,或上拉视图到底部打开城市搜索添加城市" + showAlert(message, "好的", againReuqest: false, shouldRemind: false) + case .Loading: + if !mainView.loading{ + mainView.loadingAnimated() + } + case .Result(let city): + mainView.loadingFinish() + dataModel.currentCity = city + perforRequest() + case .Normal: + return + } + } + + override func viewDidDisappear(animated: Bool) { + initialUI() + } + + + override func didReceiveMemoryWarning() { + super.didReceiveMemoryWarning() + // Dispose of any resources that can be recreated. + } + + func initialUI(){ + mainView.animationBegin() + + //不设置两次 Button 的 Title 会出现几帧省略号 bug? + cityButton.titleLabel?.text = " " + cityButton.setTitle(" ", forState: .Normal) + + dateLabel.text = "" + } + + func perforRequest(){ + serviceResult.performResult(dataModel.currentCity) { success in + print("网络请求") + if !success{ + self.mainView.loadingFinish() + let message = "网络请求出现错误,请稍后重试" + self.showAlert(message,"取消", againReuqest: true,shouldRemind: false) + } + self.showRequestStatus() + } + showRequestStatus() + } + + func showRequestStatus(){ + switch serviceResult.state{ + case .Loading: + if !mainView.loading{ + mainView.loadingAnimated() + } + case .Results(let result): + weatherResult = result + dataModel.dailyResults = weatherResult.dailyResults + updateUI() + mainView.loadingFinish() + case .NoRequest: + let message = "今天服务器请求已经超过访问次数,请明天再试" + showAlert(message,"好的", againReuqest: false,shouldRemind: false) + mainView.loadingFinish() + case .NonsupportCity: + dataModel.currentCity = "" + let message = "很抱歉,服务器暂不支持此城市,请在城市搜索界面查找其他城市" + showAlert(message, "好的", againReuqest: false, shouldRemind: false) + mainView.loadingFinish() + case .NoYet: + return + } + + } + + func updateUI(){ + if weatherResult.city == ""{ + return + } + mainView.updateAndAnimation(weatherResult) + cityButton.setTitle(weatherResult.city, forState: .Normal) + let currentdate = NSDate() + let dateFormatter = NSDateFormatter() + dateFormatter.dateFormat = "EEEE" + let convertedDate = dateFormatter.stringFromDate(currentdate) + dateLabel.text = convertedDate + dateString = convertedDate + collectionView.reloadData() + } + + //设置第一次启动引导 + func handleFirstTime(){ + let userDefaults = NSUserDefaults.standardUserDefaults() + let firstTime = userDefaults.boolForKey("FirstTime") + if firstTime{ + showViewWithFirstTime() + userDefaults.setBool(false, forKey: "FirstTime") + userDefaults.synchronize() + }else{ + updateWeatherResult() + } + } + + func showAlert(message: String, _ actionTitle: String,againReuqest: Bool,shouldRemind: Bool){ + + let alertTitle = shouldRemind ? "确定取消天气通知吗?" : "发生错误" + let alertStyle = shouldRemind ? UIAlertControllerStyle.ActionSheet : .Alert + let actionDoneTitle = shouldRemind ? "取消通知" : "重试" + + let alert = UIAlertController(title: alertTitle, message: message , preferredStyle: alertStyle) + + if againReuqest || shouldRemind{ + let actionRequest = UIAlertAction(title: actionDoneTitle, style: .Default, handler: { (action) -> Void in + if shouldRemind{ + + self.shoudldNotification(false) + }else{ + self.perforRequest() + } + }) + alert.addAction(actionRequest) + } + + let actionCancel = UIAlertAction(title: actionTitle, style: .Default, handler: {(_) in + self.updateUI() + }) + + alert.addAction(actionCancel) + + presentViewController(alert, animated: true, completion: nil) + } + + func shoudldNotification(should: Bool){ + if should{ + playSoundEffect() + dataModel.shouldRemind = true + dateLabel.text = dataModel.dueString + dateView.backgroundColor = mainView.rainPercentLabel.textColor + springAnimation1(dateView) + detailButton.setBackgroundImage(UIImage(named: "DetailColor"), forState: .Normal) + + let notificationSettings = UIUserNotificationSettings(forTypes: [.Alert, .Sound], categories: nil) + UIApplication.sharedApplication().registerUserNotificationSettings(notificationSettings) + }else{ + springAnimation1(dateView) + self.dateView.backgroundColor = self.view.tintColor + self.dataModel.dueString = "__ : __" + self.dateLabel.text = dataModel.dueString + self.dataModel.shouldRemind = false + detailButton.setBackgroundImage(UIImage(named: "Detail"), forState: .Normal) + } + } + + // MARK: - Sound Effect + func loadSoundEffect(name: String){ + if let path = NSBundle.mainBundle().pathForResource(name, ofType: nil){ + let fileURL = NSURL.fileURLWithPath(path, isDirectory: false) + let error = AudioServicesCreateSystemSoundID(fileURL, &soundID) + if error != kAudioServicesNoError{ + print("Sound Error: \(error), path: \(path)") + } + } + } + + func unloadSoundEffect(){ + AudioServicesDisposeSystemSoundID(soundID) + soundID = 0 + } + + func playSoundEffect(){ + AudioServicesPlaySystemSound(soundID) + } + + func timePickerViewControllerDidSelect(controller: TimePickerViewController, didSelectTime time: String) { + dataModel.dueString = time + shoudldNotification(true) + dismissViewControllerAnimated(true, completion: nil) + } + + func timePickerViewControllerDidCancel(controller: TimePickerViewController) { + dismissViewControllerAnimated(true, completion: nil) + } + + func supportTableViewController(controller: SupportTableViewController) { + scrollView.setContentOffset(CGPoint(x: 0, y: 0), animated: false) + self.performSelector("updateUI", withObject: nil, afterDelay: 0.3) + dismissViewControllerAnimated(true, completion: nil) + } + + func cityListViewControolerDidSelectCity(controller: CityListViewController, didSelectCity city: City) { + + //减少网络请求次数,相同城市只有动画效果不重新加载网络请求 + if dataModel.currentCity == city.cityCN{ + self.performSelector("updateUI", withObject: nil, afterDelay: 0.3) + }else{ + dataModel.currentCity = city.cityCN + updateWeatherResult() + } + dataModel.appendCity(city) + scrollView.setContentOffset(CGPoint(x: 0, y: 0), animated: false) + dismissViewControllerAnimated(true, completion: nil) + } + + func cityListViewControllerDeleteCity(controller: CityListViewController, currentCities cities: [City]){ + dataModel.cities = cities + } + + func cityListViewControllerCancel(controller: CityListViewController) { + scrollView.setContentOffset(CGPoint(x: 0, y: 0), animated: false) + self.performSelector("updateUI", withObject: nil, afterDelay: 0.3) + dismissViewControllerAnimated(true, completion: nil) + } + + func showViewWithFirstTime(){ + firstView = FirstView() + firstView.frame = view.bounds + let button = UIButton() + button.bounds.size = CGSize(width: 100, height: 50) + button.center = view.center + button.setTitle("开始吧!", forState: .Normal) + button.backgroundColor = view.tintColor + firstView.addSubview(button) + button.addTarget(self, action: Selector("touchBegin:"), forControlEvents: UIControlEvents.TouchUpInside) + + view.addSubview(firstView) + } + + func touchBegin(sender: UIButton){ + firstView.removeFromSuperview() + updateWeatherResult() + } + + override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { + if segue.identifier == "TimePicker"{ + let controller = segue.destinationViewController as! TimePickerViewController + controller.delegate = self + } + if segue.identifier == "SupportView"{ + let controller = segue.destinationViewController as! SupportTableViewController + controller.delegate = self + } + if segue.identifier == "CityList"{ + let controller = segue.destinationViewController as! CityListViewController + controller.delegate = self + controller.cities = dataModel.cities + } + } +} + +extension HomeViewController: UIScrollViewDelegate{ + func scrollViewDidScroll(scrollView: UIScrollView) { + + let offSety = scrollView.contentOffset.y + + if offSety < -30{ + pullDownView.lineAnimation() + pullDownView.topHeightConstraion.constant = abs(offSety) + pullDownView.hidden = false + if offSety <= -60 && offSety > -100{ + shoudRemand = false + + pullDownView.viewDidScroll("↓下拉设置通知", labelColor: UIColor.whiteColor(), alpha: 0.04 * (abs(offSety) - 60), hidden: false) + dateLabel.text = dataModel.dueString + dateView.backgroundColor = view.tintColor + animationWithColor(pullDownView,color:view.tintColor) + if offSety < -85{ + shoudRemand = true + let remindString: String! + if dataModel.shouldRemind{ + remindString = "释放更换通知时间" + }else{ + remindString = "释放激活下雨通知" + } + pullDownView.viewDidScroll(remindString, labelColor: UIColor.whiteColor(), alpha: 1, hidden: false) + } + } + if dataModel.shouldRemind && offSety <= -100{ + shoudRemand = false + pullDownView.remindLabel.text = "释放取消通知" + pullDownView.remindLabel.textColor = view.tintColor + dateLabel.text = dataModel.dueString + dateView.backgroundColor = collectionView.backgroundColor + animationWithColor(pullDownView, color: collectionView.backgroundColor!) + } + }else if offSety >= 40 && offSety <= 140{ + mainView.weatherImage.alpha = 1 - (0.02 * (offSety - 30)) + }else if offSety > 200{ + pullTopView.buttonHeightConstraion.constant = offSety - 190 + }else{ + shoudRemand = false + pullDownView.hiddenView() + pullDownView.backgroundColor = collectionView.backgroundColor + pullDownView.initialLine() + dateLabel.text = dateString + dateView.backgroundColor = collectionView.backgroundColor + mainView.weatherImage.alpha = 1 + } + } + + func scrollViewDidEndDragging(scrollView: UIScrollView, willDecelerate decelerate: Bool) { + let offSety = scrollView.contentOffset.y + if decelerate{ + if shoudRemand{ + pullDownView.hiddenView() + performSegueWithIdentifier("TimePicker", sender: scrollView) + } + if offSety <= -100 && pullDownView.remindLabel.text == "释放取消通知"{ + pullDownView.hiddenView() + showAlert("","不取消", againReuqest: false, shouldRemind: true) + } + if offSety >= 260{ + performSegueWithIdentifier("CityList", sender: scrollView) + } + } + } +} + +extension HomeViewController: UICollectionViewDataSource{ + func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + return weatherResult.dailyResults.count + } + + func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { + let cell = collectionView.dequeueReusableCellWithReuseIdentifier("WeekWeatherCell", forIndexPath: indexPath) as! WeekWeatherCell + + let dailyResult = weatherResult.dailyResults[indexPath.item] + cell.configureForDailyResult(dailyResult) + + return cell + } +} diff --git a/UmbrellaWeather/Info.plist b/UmbrellaWeather/Info.plist new file mode 100644 index 0000000..e2ffa95 --- /dev/null +++ b/UmbrellaWeather/Info.plist @@ -0,0 +1,59 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.1 + CFBundleSignature + ???? + CFBundleVersion + 8 + LSRequiresIPhoneOS + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + + NSLocationWhenInUseUsageDescription + + UIAppFonts + + DINNextLTPro-Light.otf + + UIBackgroundModes + + fetch + + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UIRequiresFullScreen + + UIStatusBarHidden + + UIStatusBarStyle + UIStatusBarStyleLightContent + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/UmbrellaWeather/MainView.swift b/UmbrellaWeather/MainView.swift new file mode 100644 index 0000000..00fb64c --- /dev/null +++ b/UmbrellaWeather/MainView.swift @@ -0,0 +1,87 @@ +// +// MainView.swift +// UmbrellaWeather +// +// Created by ZeroJianMBP on 16/1/23. +// Copyright © 2016年 ZeroJian. All rights reserved. +// + +import UIKit + +class MainView: UIView { + + + @IBOutlet weak var weatherImage: UIImageView! + @IBOutlet weak var tmpMaxLabel: UILabel! + @IBOutlet weak var tmpMinLabel: UILabel! + @IBOutlet weak var rainPercentLabel: UILabel! + @IBOutlet weak var schemaLabel: UILabel! + @IBOutlet weak var progress: UIProgressView! + @IBOutlet weak var maxLabelConstraint: NSLayoutConstraint! + @IBOutlet weak var minLabelConstraint: NSLayoutConstraint! + @IBOutlet weak var percentConstraint: NSLayoutConstraint! + var loadingImageView: UIImageView! + var loading = false + + func updateAndAnimation(weatherResult: WeatherResult){ + animation() + + schemaLabel.text = weatherResult.state + weatherImage.imageWithCode(weatherResult.stateCode) + tmpMaxLabel.text = weatherResult.dayTemMax + tmpMinLabel.text = weatherResult.dayTmpMin + if let FloatRain = Float(weatherResult.dayRain){ + let value = FloatRain / 100 + progress.setProgress(value, animated: true) + } + rainPercentLabel.text = weatherResult.dayRain + "%" + } + + func loadingAnimated(){ + loadingImageView = UIImageView(image: UIImage(named: "Loading")!) + loadingImageView.center.x = self.center.x + loadingImageView.center.y = self.center.y - 50 + loadingImageView.bounds.size.width = self.bounds.size.width / 5 + loadingImageView.bounds.size.height = loadingImageView.bounds.size.width + self.addSubview(loadingImageView) + loadingAnimation(loadingImageView) + loading = true + } + + func loadingFinish(){ + loadingImageView.layer.removeAllAnimations() + loadingImageView.removeFromSuperview() + loading = false + } + + func animationBegin(){ + maxLabelConstraint.constant = 25 + minLabelConstraint.constant = 25 + percentConstraint.constant = 10 + weatherImage.hidden = true + weatherImage.transform = CGAffineTransformMakeScale(0.7, 0.7) + schemaLabel.text = "" + tmpMaxLabel.text = "" + tmpMinLabel.text = "" + rainPercentLabel.text = "" + } + + private func animation(){ + UIView.animateWithDuration(0.66, delay: 0.0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.0, options: .CurveEaseInOut, animations: { () -> Void in + self.weatherImage.hidden = false + self.weatherImage.transform = CGAffineTransformMakeScale(1.0, 1.0) + }, completion: nil) + self.maxLabelConstraint.constant = 5 + UIView.animateWithDuration(0.66, delay: 0.0, options: .CurveEaseInOut, animations: { () -> Void in + self.layoutIfNeeded() + }, completion: nil) + UIView.animateWithDuration(0.66, delay: 0.2, options: .CurveEaseInOut, animations: { () -> Void in + self.minLabelConstraint.constant = 5 + self.layoutIfNeeded() + }, completion: nil) + UIView.animateWithDuration(0.66, delay: 0.2, options: .CurveEaseInOut, animations: { () -> Void in + self.percentConstraint.constant = 30 + self.layoutIfNeeded() + }, completion: nil) + } +} diff --git a/UmbrellaWeather/PullView.swift b/UmbrellaWeather/PullView.swift new file mode 100644 index 0000000..4e71abf --- /dev/null +++ b/UmbrellaWeather/PullView.swift @@ -0,0 +1,45 @@ +// +// PullViewController.swift +// UmbrellaWeather +// +// Created by ZeroJianMBP on 16/1/24. +// Copyright © 2016年 ZeroJian. All rights reserved. +// + +import UIKit + +class PullView: UIView { + + + @IBOutlet weak var remindLabel: UILabel! + @IBOutlet weak var lineImage: UIImageView! + @IBOutlet weak var lineXConstraion: NSLayoutConstraint! + @IBOutlet weak var topHeightConstraion: NSLayoutConstraint! + @IBOutlet weak var buttonHeightConstraion: NSLayoutConstraint! + + func viewDidScroll(labelText: String!,labelColor: UIColor,alpha: CGFloat,hidden: Bool){ + remindLabel.hidden = hidden + remindLabel.text = labelText + remindLabel.textColor = labelColor + remindLabel.alpha = alpha + } + + func lineAnimation(){ + lineXConstraion.constant = self.bounds.width + UIView.animateWithDuration(0.5, delay: 0.0, options: UIViewAnimationOptions.CurveEaseInOut, animations: { () -> Void in + self.layoutIfNeeded() + }, completion: nil) + } + + func initialLine(){ + lineXConstraion.constant -= lineImage.bounds.width + } + + func hiddenView(){ + self.hidden = true + remindLabel.hidden = true + remindLabel.alpha = 0 + topHeightConstraion.constant = 0 + } + +} diff --git a/UmbrellaWeather/Server/DailyResult.swift b/UmbrellaWeather/Server/DailyResult.swift new file mode 100644 index 0000000..4600e9d --- /dev/null +++ b/UmbrellaWeather/Server/DailyResult.swift @@ -0,0 +1,43 @@ +// +// ArrayWeatherResult.swift +// Umbrella Weather +// +// Created by ZeroJianMBP on 15/12/17. +// Copyright © 2015年 ZeroJian. All rights reserved. +// + +import Foundation + +class DailyResult: NSObject,NSCoding{ + var dailyTmpMax = "" + var dailyTmpMin = "" + var dailyState = "" + var dailyDate = "" + var dailyPop = 0 + var dailyStateCode = 0 + + + override init() { + super.init() + } + + + required init?(coder aDecoder: NSCoder) { + dailyTmpMax = aDecoder.decodeObjectForKey("DailyTmpMax") as! String + dailyTmpMin = aDecoder.decodeObjectForKey("DailyTmpMin") as! String + dailyState = aDecoder.decodeObjectForKey("DailyState") as! String + dailyDate = aDecoder.decodeObjectForKey("DailyDate") as! String + dailyPop = aDecoder.decodeIntegerForKey("DailyPop") + dailyStateCode = aDecoder.decodeIntegerForKey("DailyStateCode") + super.init() + } + + func encodeWithCoder(aCoder: NSCoder) { + aCoder.encodeObject(dailyTmpMax, forKey: "DailyTmpMax") + aCoder.encodeObject(dailyTmpMin, forKey: "DailyTmpMin") + aCoder.encodeObject(dailyState, forKey: "DailyState") + aCoder.encodeObject(dailyDate, forKey: "DailyDate") + aCoder.encodeInteger(dailyPop, forKey: "DailyPop") + aCoder.encodeInteger(dailyStateCode, forKey: "DailyStateCode") + } +} \ No newline at end of file diff --git a/UmbrellaWeather/Server/LocationService.swift b/UmbrellaWeather/Server/LocationService.swift new file mode 100644 index 0000000..01f36e5 --- /dev/null +++ b/UmbrellaWeather/Server/LocationService.swift @@ -0,0 +1,113 @@ +// +// Location.swift +// UmberellaWeather +// +// Created by ZeroJianMBP on 16/1/4. +// Copyright © 2016年 ZeroJian. All rights reserved. +// + +import UIKit +import CoreLocation + +class LocationService: NSObject,CLLocationManagerDelegate{ + + + var updatingLocation = false + var parserXML:ParserXML! + + static let singleClass = LocationService() + + enum LocationState{ + case Loading + case Result(String) + case NoService + case Normal + } + + var afterUpdatedCityAction: (Bool -> Void)? + + private(set) var locationState: LocationState = .Normal + + private let locationManager = CLLocationManager() + + private func startLocation(){ + if CLLocationManager.locationServicesEnabled(){ + locationManager.delegate = self + locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters + locationManager.startUpdatingLocation() + updatingLocation = true + locationState = .Loading + }else{ + locationState = .NoService + } + } + +// lazy var locationManager: CLLocationManager = { +// let locationManager = CLLocationManager() +// locationManager.delegate = self +// locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters +// return locationManager +// }() + + func beginLocation(){ + let authStatus = CLLocationManager.authorizationStatus() + if authStatus == .NotDetermined{ + locationManager.requestWhenInUseAuthorization() + } + if authStatus == .Denied || authStatus == .Restricted{ + locationState = .NoService + return + } + if updatingLocation{ + stopLocationManager() + }else{ + startLocation() + } + } + + private func stopLocationManager(){ + if updatingLocation{ + locationManager.stopUpdatingLocation() + locationManager.delegate = nil + parserXML = nil + updatingLocation = false + } + } + + func locationManager(manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { + guard let newLocation = locations.last else{ + self.locationState = .NoService + return + } + stopLocationManager() + let geocoder = CLGeocoder() + geocoder.reverseGeocodeLocation(newLocation) { (placemarks, error) -> Void in + var sucess = false + if error != nil{ + self.locationState = .NoService + }else if let p = placemarks where !p.isEmpty{ + + if let locality = p.last?.locality{ + print(locality) + let city = self.rangeOfCities(locality) + sucess = true + self.locationState = .Result(city) + + self.stopLocationManager() + } + } + self.afterUpdatedCityAction?(sucess) + } + } + + private func rangeOfCities(placemark: String) -> String{ + parserXML = ParserXML() + let cities = parserXML.cities + for (_ , value) in cities.enumerate(){ + if placemark.rangeOfString(value.cityCN) != nil{ + return value.cityCN + } + } + return placemark + } +} \ No newline at end of file diff --git a/UmbrellaWeather/Server/ServiceResult.swift b/UmbrellaWeather/Server/ServiceResult.swift new file mode 100644 index 0000000..ada009e --- /dev/null +++ b/UmbrellaWeather/Server/ServiceResult.swift @@ -0,0 +1,185 @@ +// +// ServiceResult.swift +// UmberellaWeather +// +// Created by ZeroJianMBP on 15/12/21. +// Copyright © 2015年 ZeroJian. All rights reserved. +// + +import Foundation +import SwiftyJSON + +typealias ResultComplete = (Bool) -> Void + +class ServiceResult { + + enum State{ + case Loading + case NoRequest + case NonsupportCity + case NoYet + case Results(WeatherResult) + } + + + private(set) var state: State = .NoYet + private var dataTask: NSURLSessionDataTask? = nil + + func performResult(cityName: String,completion: ResultComplete){ + if !cityName.isEmpty{ + + dataTask?.cancel() + state = .Loading + + let url = citySearchText(cityName) + let session = NSURLSession.sharedSession() + dataTask = session.dataTaskWithURL(url) { (data, response, error) -> Void in + var success = false + self.state = .NoYet + if error != nil{ + print(error) + } + if let httpResponse = response as? NSHTTPURLResponse where httpResponse.statusCode == 200 ,let data = data{ + let dictionary = self.parseJSON(data) + let weatherResult = self.parseDictionary(dictionary) + if weatherResult.ServiceStatus == "no more requests"{ + self.state = .NoRequest + }else if weatherResult.ServiceStatus == "unknown city"{ + self.state = .NonsupportCity + }else{ + self.state = .Results(weatherResult) + } + success = true + } + dispatch_async(dispatch_get_main_queue(), { + completion(success) + }) + } + dataTask?.resume() + } + } + + func fetchDailyResults(cityName: String,completion: ([DailyResult] -> Void)?){ + + if !cityName.isEmpty{ + + let url = citySearchText(cityName) + let session = NSURLSession.sharedSession() + let dateTask = session.dataTaskWithURL(url) { (data, response, error) + in + if error != nil{ + return + } + if let data = data{ + let dictionary = self.parseJSON(data) + let dailyResults = self.parseWithDairyResults(dictionary) + completion?(dailyResults) + } + } + dateTask.resume() + } + } + + + private func citySearchText(searchText: String) -> NSURL{ + + //api 每天免费请求次数3000,如果超过请求次数请自行注册 + //和风天气网址: http://www.heweather.com + + let apiKeyTest = "cf386feb168149f4a45eb87d4f4b647f" + + let escapedSearchText = searchText.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())! + let urlString = String(format: "https://api.heweather.com/x3/weather?city=%@&key="+apiKeyTest,escapedSearchText) + let url = NSURL(string: urlString) + return url! + } + + private func parseJSON(data: NSData) -> JSON{ + return JSON(data: data) + } + + private func parseDictionary(dictionary: JSON) -> WeatherResult{ + let weatherResult = WeatherResult() + let json = dictionary["HeWeather data service 3.0"][0] + if let ServiceState = json["status"].string{ + weatherResult.ServiceStatus = ServiceState + } + + if let jsonCity = json["basic"]["city"].string{ + weatherResult.city = jsonCity + } + if let state = json["now"]["cond"]["txt"].string{ + weatherResult.state = state + } + if let stateCode = json["now"]["cond"]["code"].string{ + weatherResult.stateCode = Int(stateCode)! + } + + let dailyArrays = json["daily_forecast"] + let dailyDayTmp = dailyArrays[0]["tmp"] + if let pop = dailyArrays[0]["pop"].string{ + weatherResult.dayRain = pop + } + if let dayTemMax = dailyDayTmp["max"].string{ + weatherResult.dayTemMax = dayTemMax + "˚" + } + if let dayTmpMin = dailyDayTmp["min"].string{ + weatherResult.dayTmpMin = dayTmpMin + "˚" + } + + for (_,subJson):(String, JSON) in json["daily_forecast"]{ + let dailyResult = DailyResult() + + if let dates = subJson["date"].string{ + dailyResult.dailyDate = dates + } + if let pop = subJson["pop"].string{ + dailyResult.dailyPop = Int(pop)! + } + if let tmpsMax = subJson["tmp"]["max"].string{ + dailyResult.dailyTmpMax = tmpsMax + "˚" + } + if let tmpsMin = subJson["tmp"]["min"].string{ + dailyResult.dailyTmpMin = tmpsMin + "˚" + } + if let conds = subJson["cond"]["txt_d"].string{ + dailyResult.dailyState = conds + } + if let stateCode = subJson["cond"]["code_d"].string{ + dailyResult.dailyStateCode = Int(stateCode)! + } + weatherResult.dailyResults.append(dailyResult) + } + return weatherResult + } + + private func parseWithDairyResults(dictionary: JSON) -> [DailyResult]{ + var dailyResults = [DailyResult]() + let json = dictionary["HeWeather data service 3.0"][0] + for (_,subJson):(String, JSON) in json["daily_forecast"]{ + let dailyResult = DailyResult() + if let dates = subJson["date"].string{ + dailyResult.dailyDate = dates + } + if let pop = subJson["pop"].string{ + dailyResult.dailyPop = Int(pop)! + } + if let tmpsMax = subJson["tmp"]["max"].string{ + dailyResult.dailyTmpMax = tmpsMax + "˚" + } + if let tmpsMin = subJson["tmp"]["min"].string{ + dailyResult.dailyTmpMin = tmpsMin + "˚" + } + if let conds = subJson["cond"]["txt_d"].string{ + dailyResult.dailyState = conds + } + if let stateCode = subJson["cond"]["code_d"].string{ + dailyResult.dailyStateCode = Int(stateCode)! + } + dailyResults.append(dailyResult) + } + return dailyResults + } + +} + diff --git a/UmbrellaWeather/Server/UIImageView+WeatherImage.swift b/UmbrellaWeather/Server/UIImageView+WeatherImage.swift new file mode 100644 index 0000000..62cd465 --- /dev/null +++ b/UmbrellaWeather/Server/UIImageView+WeatherImage.swift @@ -0,0 +1,41 @@ +// +// UIImageView+WeatherImage.swift +// UmbrellaWeather +// +// Created by ZeroJianMBP on 16/1/25. +// Copyright © 2016年 ZeroJian. All rights reserved. +// + +import UIKit + +extension UIImageView{ + func imageWithCode(code: Int){ + if code == 100{ + self.image = UIImage(named: "Sun") + }else if code >= 101 && code <= 213{ + self.image = UIImage(named: "Cloudy") + }else if code >= 300 && code <= 313{ + self.image = UIImage(named: "Rain") + }else if code >= 400 && code <= 407{ + self.image = UIImage(named: "Snow") + }else if code >= 500 && code <= 504{ + self.image = UIImage(named: "Fog") + }else{ + self.image = UIImage(named: "Cloudy") + } + } + + func imageWithResolution(){ + let screenheight = UIScreen.mainScreen().bounds.size.height + let scale = UIScreen.mainScreen().scale + let heightRes = screenheight * scale + switch heightRes{ + case 960.0: + self.image = UIImage(named: "Launch4") + case 1136.0: + self.image = UIImage(named: "Launch5") + default: + self.image = UIImage(named: "Launch") + } + } +} \ No newline at end of file diff --git a/UmbrellaWeather/Server/WeatherResult.swift b/UmbrellaWeather/Server/WeatherResult.swift new file mode 100644 index 0000000..aeae54d --- /dev/null +++ b/UmbrellaWeather/Server/WeatherResult.swift @@ -0,0 +1,20 @@ +// +// Result.swift +// Umbrella Weather +// +// Created by ZeroJianMBP on 15/12/16. +// Copyright © 2015年 ZeroJian. All rights reserved. +// + +import Foundation + +class WeatherResult { + var city = "" + var state = "" + var dayRain = "" + var dayTemMax = "" + var dayTmpMin = "" + var stateCode = 0 + var dailyResults = [DailyResult]() + var ServiceStatus = "" +} \ No newline at end of file diff --git a/UmbrellaWeather/SupportTableViewController.swift b/UmbrellaWeather/SupportTableViewController.swift new file mode 100644 index 0000000..3a8775d --- /dev/null +++ b/UmbrellaWeather/SupportTableViewController.swift @@ -0,0 +1,192 @@ +// +// SupportTableViewController.swift +// UmbrellaWeather +// +// Created by ZeroJianMBP on 16/1/23. +// Copyright © 2016年 ZeroJian. All rights reserved. +// + +import UIKit +import StoreKit + +protocol SupportTableViewControllerDelegate: class{ + func supportTableViewController(controller: SupportTableViewController) +} + + +class SupportTableViewController: UITableViewController,SKStoreProductViewControllerDelegate,SKPaymentTransactionObserver,SKProductsRequestDelegate { + + @IBOutlet weak var loadingImageView: UIImageView! + @IBOutlet weak var row1ImageView: UIImageView! + @IBOutlet weak var row2ImageView: UIImageView! + @IBOutlet weak var row3ImageView: UIImageView! + @IBOutlet weak var row1WidthCon: NSLayoutConstraint! + @IBOutlet weak var row2WidthCon: NSLayoutConstraint! + @IBOutlet weak var row3WidthCon: NSLayoutConstraint! + + weak var delegate:SupportTableViewControllerDelegate? + var request: SKProductsRequest! + var requestPay = false + + override func viewDidLoad() { + super.viewDidLoad() + SKPaymentQueue.defaultQueue().addTransactionObserver(self) + } + + deinit{ + if requestPay{ + request.delegate = nil + } + SKPaymentQueue.defaultQueue().removeTransactionObserver(self) + } + + + + func requestProducts(pid: String){ + let set: Set = [pid] + request = SKProductsRequest(productIdentifiers: set) + request.delegate = self + request.start() + requestPay = true + } + + + func productsRequest(request: SKProductsRequest, didReceiveResponse response: SKProductsResponse) { + buyProduct(response.products[0]) + } + + func buyProduct(product: SKProduct){ + let payment = SKPayment(product: product) + SKPaymentQueue.defaultQueue().addPayment(payment) + print("请求线程购买") + } + + func paymentQueue(queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) { + + for transaction in transactions { + print("队列状态变化 \(transaction.payment.productIdentifier)==\(transaction.transactionState.rawValue))") + switch transaction.transactionState { + case .Purchasing: + print("商品添加进列表 \(transaction.payment.productIdentifier)") + case .Purchased: + switch transaction.payment.productIdentifier{ + case "UmbrellaWeather_1": + row1WidthCon.constant = 0 + hiddenLoadingImageView(row1ImageView) + case "UmbrellaWeather_6": + row2WidthCon.constant = 0 + hiddenLoadingImageView(row2ImageView) + case "UmbrellaWeather_18": + row3WidthCon.constant = 0 + hiddenLoadingImageView(row3ImageView) + default: + break + } + self.finishTransaction(transaction) + case .Failed: + if transaction.error?.code == 0{ + showAlert("感谢你的支持,无法连接到 iTunes Store,请稍后重试") + } + switch transaction.payment.productIdentifier{ + case "UmbrellaWeather_1": + row1WidthCon.constant = 0 + hiddenLoadingImageView(row1ImageView) + case "UmbrellaWeather_6": + row2WidthCon.constant = 0 + hiddenLoadingImageView(row2ImageView) + case "UmbrellaWeather_18": + row3WidthCon.constant = 0 + hiddenLoadingImageView(row3ImageView) + default: + break + } + print("交易失败error==\(transaction.error)") + self.finishTransaction(transaction) + case .Restored: + print("已经购买过商品") + self.finishTransaction(transaction) + case .Deferred: + print("Allow the user to continue using your app.") + break + } + } + } + + + func finishTransaction(transaction:SKPaymentTransaction) { + // 将交易从交易队列中删除 + SKPaymentQueue.defaultQueue().finishTransaction(transaction) + } + + override func didReceiveMemoryWarning() { + super.didReceiveMemoryWarning() + // Dispose of any resources that can be recreated. + } + + + override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { + + switch (indexPath.section,indexPath.row){ + + case (1,0): + loadingAnimation(loadingImageView) + let storeViewController = SKStoreProductViewController() + storeViewController.delegate = self + storeViewController.loadProductWithParameters([SKStoreProductParameterITunesItemIdentifier : 1079751819], completionBlock: { (result, error) -> Void in + if result{ + self.presentViewController(storeViewController, animated: true, completion: { () -> Void in + self.hiddenLoadingImageView(self.loadingImageView) + }) + }else{ + self.hiddenLoadingImageView(self.loadingImageView) + self.showAlert("加载商店页面出现错误,请稍后重试") + } + }) + case (2,0): + SKPaymentQueue.defaultQueue().addTransactionObserver(self) + requestProducts("UmbrellaWeather_1") + row1WidthCon.constant = row1ImageView.bounds.height + loadingAnimation(row1ImageView) + case (2,1): + requestProducts("UmbrellaWeather_6") + row2WidthCon.constant = row2ImageView.bounds.height + loadingAnimation(row2ImageView) + case (2,2): + requestProducts("UmbrellaWeather_18") + row3WidthCon.constant = row3ImageView.bounds.height + loadingAnimation(row3ImageView) + default: + return + } + tableView.deselectRowAtIndexPath(indexPath, animated: true) + } + + func hiddenLoadingImageView(imageView: UIImageView){ + imageView.layer.removeAllAnimations() + imageView.hidden = true + } + + func showAlert(message: String){ + + let alert = UIAlertController(title: "加载错误", message: message, preferredStyle: UIAlertControllerStyle.Alert) + let action = UIAlertAction(title: "好的", style: UIAlertActionStyle.Cancel, handler: nil) + alert.addAction(action) + presentViewController(alert, animated: true, completion: nil) + + } + + + func productViewControllerDidFinish(viewController: SKStoreProductViewController) { + dismissViewControllerAnimated(true, completion: nil) + } + + + override func scrollViewDidEndDragging(scrollView: UIScrollView, willDecelerate decelerate: Bool) { + let offSety = scrollView.contentOffset.y + + if offSety < -50 && decelerate{ + delegate?.supportTableViewController(self) + } + } + +} diff --git a/UmbrellaWeather/TimePickerViewController.swift b/UmbrellaWeather/TimePickerViewController.swift new file mode 100644 index 0000000..ace10a8 --- /dev/null +++ b/UmbrellaWeather/TimePickerViewController.swift @@ -0,0 +1,50 @@ +// +// TimePickerViewController.swift +// UmberellaWeather +// +// Created by ZeroJianMBP on 15/12/27. +// Copyright © 2015年 ZeroJian. All rights reserved. +// + +import UIKit + +protocol TimePickerViewControllerDelegate: class{ + func timePickerViewControllerDidSelect(controller: TimePickerViewController, didSelectTime time: String) + func timePickerViewControllerDidCancel(controller: TimePickerViewController) + } + +class TimePickerViewController: UIViewController { + @IBOutlet weak var datePicker: UIDatePicker! + + weak var delegate: TimePickerViewControllerDelegate? + + + @IBAction func cancel(sender: AnyObject) { + delegate?.timePickerViewControllerDidCancel(self) + } + + @IBAction func done(sender: AnyObject) { + let formatter = NSDateFormatter() + formatter.dateFormat = "HH:mm" + let dateString = formatter.stringFromDate(datePicker.date) + + delegate?.timePickerViewControllerDidSelect(self, didSelectTime: dateString) + } + + override func viewDidLoad() { + super.viewDidLoad() + view.backgroundColor = UIColor.clearColor() + } + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + modalPresentationStyle = .Custom + transitioningDelegate = self + } +} + +extension TimePickerViewController: UIViewControllerTransitioningDelegate{ + func presentationControllerForPresentedViewController(presented: UIViewController, presentingViewController presenting: UIViewController, sourceViewController source: UIViewController) -> UIPresentationController? { + return DimmingPresentationController(presentedViewController: presented, presentingViewController: presenting) + } +} \ No newline at end of file diff --git a/UmbrellaWeather/WaterSound.wav b/UmbrellaWeather/WaterSound.wav new file mode 100644 index 0000000..4ad54e4 Binary files /dev/null and b/UmbrellaWeather/WaterSound.wav differ diff --git a/UmbrellaWeather/WeekWeatherCell.swift b/UmbrellaWeather/WeekWeatherCell.swift new file mode 100644 index 0000000..338a33a --- /dev/null +++ b/UmbrellaWeather/WeekWeatherCell.swift @@ -0,0 +1,34 @@ +// +// WeekWeatherCell.swift +// UmbrellaWeather +// +// Created by ZeroJianMBP on 16/1/21. +// Copyright © 2016年 ZeroJian. All rights reserved. +// + +import UIKit + +class WeekWeatherCell: UICollectionViewCell{ + @IBOutlet weak var dateLabel: UILabel! + @IBOutlet weak var weatherImage: UIImageView! + @IBOutlet weak var tmpMaxLabel: UILabel! + @IBOutlet weak var tmpMinLabel: UILabel! + @IBOutlet weak var rainPercentLabel: UILabel! + @IBOutlet weak var schemaLabel: UILabel! + + override func awakeFromNib() { + super.awakeFromNib() + } + + func configureForDailyResult(dailyResult: DailyResult){ + + dateLabel.text = dailyResult.dailyDate + schemaLabel.text = dailyResult.dailyState + tmpMaxLabel.text = dailyResult.dailyTmpMax + " /" + tmpMinLabel.text = dailyResult.dailyTmpMin + rainPercentLabel.text = + "\(dailyResult.dailyPop)" + "%" + weatherImage.imageWithCode(dailyResult.dailyStateCode) + } + +} diff --git a/UmbrellaWeather/WeekWeatherCell.xib b/UmbrellaWeather/WeekWeatherCell.xib new file mode 100644 index 0000000..323f354 --- /dev/null +++ b/UmbrellaWeather/WeekWeatherCell.xib @@ -0,0 +1,129 @@ + + + + + + + + + DINNextLTPro-Light + DINNextLTPro-Light + DINNextLTPro-Light + DINNextLTPro-Light + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UmbrellaWeather/WeekWeatherCollectionView.swift b/UmbrellaWeather/WeekWeatherCollectionView.swift new file mode 100644 index 0000000..fa9fcd7 --- /dev/null +++ b/UmbrellaWeather/WeekWeatherCollectionView.swift @@ -0,0 +1,20 @@ +// +// WeekWeatherCollectionView.swift +// UmbrellaWeather +// +// Created by ZeroJianMBP on 16/1/23. +// Copyright © 2016年 ZeroJian. All rights reserved. +// + +import UIKit + +class WeekWeatherCollectionView: UICollectionView { + + + required init?(coder aDecoder: NSCoder) { + super.init(coder: aDecoder) + let cellNib = UINib(nibName: "WeekWeatherCell", bundle: nil) + self.registerNib(cellNib, forCellWithReuseIdentifier: "WeekWeatherCell") + } + +} diff --git a/UmbrellaWeather/zh-Hans.lproj/Main.strings b/UmbrellaWeather/zh-Hans.lproj/Main.strings new file mode 100644 index 0000000..98d67a4 --- /dev/null +++ b/UmbrellaWeather/zh-Hans.lproj/Main.strings @@ -0,0 +1,66 @@ + +/* Class = "UILabel"; text = "-70˚"; ObjectID = "5JO-bH-Kwe"; */ +"5JO-bH-Kwe.text" = "-70˚"; + +/* Class = "UILabel"; text = " 因为是个人开发作品 ,使用的免费服务器,每天的访问有限制,如果给你造成了不便,还请见谅! 如果你喜欢这款产品,可以支持我,更加优化这款产品,谢谢 ! "; ObjectID = "A8R-ya-9Cc"; */ +"A8R-ya-9Cc.text" = " 因为是个人开发作品 ,使用的免费服务器,每天的访问有限制,如果给你造成了不便,还请见谅! 如果你喜欢这款产品,可以支持我,更加优化这款产品,谢谢 ! "; + +/* Class = "UILabel"; text = "日期"; ObjectID = "C1I-Yj-Xp0"; */ +"C1I-Yj-Xp0.text" = "日期"; + +/* Class = "UILabel"; text = "希望你喜欢"; ObjectID = "CyL-ZV-w3d"; */ +"CyL-ZV-w3d.text" = "希望你喜欢"; + +/* Class = "UILabel"; text = "¥18"; ObjectID = "Ghv-X4-bOK"; */ +"Ghv-X4-bOK.text" = "¥18"; + +/* Class = "UILabel"; text = "¥1"; ObjectID = "H7D-4Y-Zs1"; */ +"H7D-4Y-Zs1.text" = "¥1"; + +/* Class = "UILabel"; text = "选择是否通知"; ObjectID = "IBz-Zj-geL"; */ +"IBz-Zj-geL.text" = "选择是否通知"; + +/* Class = "UISearchBar"; placeholder = "输入城市中文名(下拉可关闭视图)"; ObjectID = "JOy-4e-Cla"; */ +"JOy-4e-Cla.placeholder" = "输入城市中文名(下拉可关闭视图)"; + +/* Class = "UIButton"; normalTitle = "城市"; ObjectID = "JVc-HV-ccf"; */ +"JVc-HV-ccf.normalTitle" = "城市"; + +/* Class = "UILabel"; text = "¥6"; ObjectID = "Kxk-ia-tVa"; */ +"Kxk-ia-tVa.text" = "¥6"; + +/* Class = "UILabel"; text = "天气状态"; ObjectID = "NX8-qI-A2s"; */ +"NX8-qI-A2s.text" = "天气状态"; + +/* Class = "UILabel"; text = "100%"; ObjectID = "P8w-7Y-G0Q"; */ +"P8w-7Y-G0Q.text" = "100%"; + +/* Class = "UILabel"; text = "Email: zj17223412@outlook.com"; ObjectID = "RFc-Ov-47K"; */ +"RFc-Ov-47K.text" = "Email: zj17223412@outlook.com"; + +/* Class = "UILabel"; text = "个人开发作品"; ObjectID = "TYM-eS-1yu"; */ +"TYM-eS-1yu.text" = "个人开发作品"; + +/* Class = "UILabel"; text = "感谢你的支持"; ObjectID = "U9f-B0-LRL"; */ +"U9f-B0-LRL.text" = "感谢你的支持"; + +/* Class = "UILabel"; text = "-10˚"; ObjectID = "Xs1-lm-1HU"; */ +"Xs1-lm-1HU.text" = "-10˚"; + +/* Class = "UILabel"; text = "❤️前往商店评价"; ObjectID = "YHn-5e-2Py"; */ +"YHn-5e-2Py.text" = "❤️前往商店评价"; + +/* Class = "UIButton"; normalTitle = "取消"; ObjectID = "hq7-yA-kVV"; */ +"hq7-yA-kVV.normalTitle" = "取消"; + +/* Class = "UITableViewSection"; headerTitle = "感谢你的支持! (下拉关闭视图)"; ObjectID = "nL3-LY-Zdq"; */ +"nL3-LY-Zdq.headerTitle" = "感谢你的支持! (下拉关闭视图)"; + +/* Class = "UILabel"; text = "Copyright © 2016 ZeroJian 保留所有权利"; ObjectID = "ruO-Mh-HML"; */ +"ruO-Mh-HML.text" = "Copyright © 2016 ZeroJian 保留所有权利"; + +/* Class = "UIButton"; normalTitle = "确定"; ObjectID = "rvF-eN-mcX"; */ +"rvF-eN-mcX.normalTitle" = "确定"; + +/* Class = "UILabel"; text = "––––––––––––––––"; ObjectID = "sEP-g7-0oi"; */ +"sEP-g7-0oi.text" = "––––––––––––––––";