commit cbc85a02d29448295a14e5da0b34eafafa2333b8 Author: zu Date: Mon Sep 6 18:47:38 2021 +0800 xplan ios start diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..746a8ad0 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +Flutter filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..160a0d0f --- /dev/null +++ b/.gitignore @@ -0,0 +1,68 @@ +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## User settings +xcuserdata/ + +## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) +*.xcscmblueprint +*.xccheckout + +## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) +build/ +DerivedData/ +*.moved-aside +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 + +## Obj-C/Swift specific +*.hmap + +## App packaging +*.ipa +*.dSYM.zip +*.dSYM + +# CocoaPods +# +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control +# +Pods/ +# +# Add this line if you want to avoid checking in source code from the Xcode workspace +# *.xcworkspace + +# Carthage +# +# Add this line if you want to avoid checking in source code from Carthage dependencies. +# Carthage/Checkouts + +Carthage/Build/ + +# fastlane +# +# It is recommended to not store the screenshots in the git repo. +# Instead, use fastlane to re-generate the screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://docs.fastlane.tools/best-practices/source-control/#source-control + +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots/**/*.png +fastlane/test_output + +# Code Injection +# +# After new code Injection tools there's a generated folder /iOSInjectionProject +# https://github.com/johnno1962/injectionforxcode + +iOSInjectionProject/ diff --git a/Podfile b/Podfile new file mode 100644 index 00000000..e1334fc8 --- /dev/null +++ b/Podfile @@ -0,0 +1,17 @@ +# Uncomment the next line to define a global platform for your project +# platform :ios, '9.0' + +target 'xplan-ios' do + # Comment the next line if you don't want to use dynamic frameworks + use_frameworks! + + # Pods for xplan-ios + pod 'AFNetworking' + pod 'YYText' + pod 'Masonry' + pod 'ReactiveObjC' + pod 'MBProgressHUD' + #易盾 本机一键登录 + pod 'NTESQuickPass', '~> 2.1.6' + +end diff --git a/Podfile.lock b/Podfile.lock new file mode 100644 index 00000000..c876a118 --- /dev/null +++ b/Podfile.lock @@ -0,0 +1,50 @@ +PODS: + - AFNetworking (4.0.1): + - AFNetworking/NSURLSession (= 4.0.1) + - AFNetworking/Reachability (= 4.0.1) + - AFNetworking/Security (= 4.0.1) + - AFNetworking/Serialization (= 4.0.1) + - AFNetworking/UIKit (= 4.0.1) + - AFNetworking/NSURLSession (4.0.1): + - AFNetworking/Reachability + - AFNetworking/Security + - AFNetworking/Serialization + - AFNetworking/Reachability (4.0.1) + - AFNetworking/Security (4.0.1) + - AFNetworking/Serialization (4.0.1) + - AFNetworking/UIKit (4.0.1): + - AFNetworking/NSURLSession + - Masonry (1.1.0) + - MBProgressHUD (1.2.0) + - NTESQuickPass (2.1.9) + - ReactiveObjC (3.1.1) + - YYText (1.0.7) + +DEPENDENCIES: + - AFNetworking + - Masonry + - MBProgressHUD + - NTESQuickPass (~> 2.1.6) + - ReactiveObjC + - YYText + +SPEC REPOS: + trunk: + - AFNetworking + - Masonry + - MBProgressHUD + - NTESQuickPass + - ReactiveObjC + - YYText + +SPEC CHECKSUMS: + AFNetworking: 7864c38297c79aaca1500c33288e429c3451fdce + Masonry: 678fab65091a9290e40e2832a55e7ab731aad201 + MBProgressHUD: 3ee5efcc380f6a79a7cc9b363dd669c5e1ae7406 + NTESQuickPass: 8431dc52737c95883cd382c2ee75664d58f39377 + ReactiveObjC: 011caa393aa0383245f2dcf9bf02e86b80b36040 + YYText: 5c461d709e24d55a182d1441c41dc639a18a4849 + +PODFILE CHECKSUM: f8d8c44df1ae85a1571fe37ee767fc992db1ff6b + +COCOAPODS: 1.10.1 diff --git a/xplan-ios.xcodeproj/project.pbxproj b/xplan-ios.xcodeproj/project.pbxproj new file mode 100644 index 00000000..aec1b597 --- /dev/null +++ b/xplan-ios.xcodeproj/project.pbxproj @@ -0,0 +1,699 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXBuildFile section */ + 189DD52E26DE255300AB55B1 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 189DD52D26DE255300AB55B1 /* AppDelegate.m */; }; + 189DD53426DE255300AB55B1 /* TabbarViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 189DD53326DE255300AB55B1 /* TabbarViewController.m */; }; + 189DD53926DE255600AB55B1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 189DD53826DE255600AB55B1 /* Assets.xcassets */; }; + 189DD53C26DE255600AB55B1 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 189DD53A26DE255600AB55B1 /* LaunchScreen.storyboard */; }; + 189DD53F26DE255600AB55B1 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 189DD53E26DE255600AB55B1 /* main.m */; }; + 189DD54B26DE338800AB55B1 /* BaseViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 189DD54A26DE338800AB55B1 /* BaseViewController.m */; }; + 189DD55026DE37F900AB55B1 /* MvpViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 189DD54F26DE37F900AB55B1 /* MvpViewController.m */; }; + 189DD55A26DE39D200AB55B1 /* BaseMvpPresenter.m in Sources */ = {isa = PBXBuildFile; fileRef = 189DD55926DE39D200AB55B1 /* BaseMvpPresenter.m */; }; + 189DD56526DE465A00AB55B1 /* LoginViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 189DD56426DE465A00AB55B1 /* LoginViewController.m */; }; + 189DD58F26DF97E700AB55B1 /* LoginPresenter.m in Sources */ = {isa = PBXBuildFile; fileRef = 189DD58E26DF97E700AB55B1 /* LoginPresenter.m */; }; + 189DD67E26E1FD8900AB55B1 /* UIImage+Utils.m in Sources */ = {isa = PBXBuildFile; fileRef = 189DD67D26E1FD8900AB55B1 /* UIImage+Utils.m */; }; + 189DD68426E1FDBB00AB55B1 /* XCHUDTool.m in Sources */ = {isa = PBXBuildFile; fileRef = 189DD68226E1FDBB00AB55B1 /* XCHUDTool.m */; }; + 189DD6FF26E20E5900AB55B1 /* HttpRequestHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 189DD6FE26E20E5900AB55B1 /* HttpRequestHelper.m */; }; + 189DD73D26E21C3F00AB55B1 /* YYUtility+Device.m in Sources */ = {isa = PBXBuildFile; fileRef = 189DD73826E21C3F00AB55B1 /* YYUtility+Device.m */; }; + 189DD73E26E21C3F00AB55B1 /* YYUtility.m in Sources */ = {isa = PBXBuildFile; fileRef = 189DD73926E21C3F00AB55B1 /* YYUtility.m */; }; + 189DD73F26E21C3F00AB55B1 /* YYUtility+Carrier.m in Sources */ = {isa = PBXBuildFile; fileRef = 189DD73A26E21C3F00AB55B1 /* YYUtility+Carrier.m */; }; + 189DD74026E21C3F00AB55B1 /* YYUtility+App.m in Sources */ = {isa = PBXBuildFile; fileRef = 189DD73B26E21C3F00AB55B1 /* YYUtility+App.m */; }; + 189DD74526E21CCC00AB55B1 /* YYReachability.m in Sources */ = {isa = PBXBuildFile; fileRef = 189DD74426E21CCC00AB55B1 /* YYReachability.m */; }; + 189DD74A26E21D8400AB55B1 /* SSKeychain.m in Sources */ = {isa = PBXBuildFile; fileRef = 189DD74926E21D8400AB55B1 /* SSKeychain.m */; }; + 189DD75026E21D9000AB55B1 /* GCDHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 189DD74E26E21D9000AB55B1 /* GCDHelper.m */; }; + 189DD75926E6003C00AB55B1 /* Api.m in Sources */ = {isa = PBXBuildFile; fileRef = 189DD75826E6003C00AB55B1 /* Api.m */; }; + 189DD76226E60DDC00AB55B1 /* Api+Login.m in Sources */ = {isa = PBXBuildFile; fileRef = 189DD76126E60DDC00AB55B1 /* Api+Login.m */; }; + 73FFADDC93E195344047A2EC /* Pods_xplan_ios.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CACF623970097D653132D69A /* Pods_xplan_ios.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 189DD52926DE255300AB55B1 /* xplan-ios.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "xplan-ios.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 189DD52C26DE255300AB55B1 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 189DD52D26DE255300AB55B1 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 189DD53226DE255300AB55B1 /* TabbarViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = TabbarViewController.h; sourceTree = ""; }; + 189DD53326DE255300AB55B1 /* TabbarViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = TabbarViewController.m; sourceTree = ""; }; + 189DD53826DE255600AB55B1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 189DD53B26DE255600AB55B1 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 189DD53D26DE255600AB55B1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 189DD53E26DE255600AB55B1 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 189DD54926DE338800AB55B1 /* BaseViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BaseViewController.h; sourceTree = ""; }; + 189DD54A26DE338800AB55B1 /* BaseViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BaseViewController.m; sourceTree = ""; }; + 189DD54E26DE37F900AB55B1 /* MvpViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MvpViewController.h; sourceTree = ""; }; + 189DD54F26DE37F900AB55B1 /* MvpViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MvpViewController.m; sourceTree = ""; }; + 189DD55826DE39D200AB55B1 /* BaseMvpPresenter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BaseMvpPresenter.h; sourceTree = ""; }; + 189DD55926DE39D200AB55B1 /* BaseMvpPresenter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = BaseMvpPresenter.m; sourceTree = ""; }; + 189DD55E26DE3BBE00AB55B1 /* BaseMvpProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BaseMvpProtocol.h; sourceTree = ""; }; + 189DD56326DE465A00AB55B1 /* LoginViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LoginViewController.h; sourceTree = ""; }; + 189DD56426DE465A00AB55B1 /* LoginViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LoginViewController.m; sourceTree = ""; }; + 189DD57226DF5F0C00AB55B1 /* Base.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Base.pch; sourceTree = ""; }; + 189DD58D26DF97E700AB55B1 /* LoginPresenter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LoginPresenter.h; sourceTree = ""; }; + 189DD58E26DF97E700AB55B1 /* LoginPresenter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LoginPresenter.m; sourceTree = ""; }; + 189DD59426DF986300AB55B1 /* LoginProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LoginProtocol.h; sourceTree = ""; }; + 189DD67C26E1FD8900AB55B1 /* UIImage+Utils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIImage+Utils.h"; sourceTree = ""; }; + 189DD67D26E1FD8900AB55B1 /* UIImage+Utils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIImage+Utils.m"; sourceTree = ""; }; + 189DD68226E1FDBB00AB55B1 /* XCHUDTool.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = XCHUDTool.m; sourceTree = ""; }; + 189DD68326E1FDBB00AB55B1 /* XCHUDTool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XCHUDTool.h; sourceTree = ""; }; + 189DD6FD26E20E5900AB55B1 /* HttpRequestHelper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HttpRequestHelper.h; sourceTree = ""; }; + 189DD6FE26E20E5900AB55B1 /* HttpRequestHelper.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HttpRequestHelper.m; sourceTree = ""; }; + 189DD73726E21C3F00AB55B1 /* CarrierIdentifier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CarrierIdentifier.h; sourceTree = ""; }; + 189DD73826E21C3F00AB55B1 /* YYUtility+Device.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "YYUtility+Device.m"; sourceTree = ""; }; + 189DD73926E21C3F00AB55B1 /* YYUtility.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YYUtility.m; sourceTree = ""; }; + 189DD73A26E21C3F00AB55B1 /* YYUtility+Carrier.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "YYUtility+Carrier.m"; sourceTree = ""; }; + 189DD73B26E21C3F00AB55B1 /* YYUtility+App.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "YYUtility+App.m"; sourceTree = ""; }; + 189DD73C26E21C3F00AB55B1 /* YYUtility.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YYUtility.h; sourceTree = ""; }; + 189DD74326E21CCC00AB55B1 /* YYReachability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = YYReachability.h; sourceTree = ""; }; + 189DD74426E21CCC00AB55B1 /* YYReachability.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = YYReachability.m; sourceTree = ""; }; + 189DD74826E21D8400AB55B1 /* SSKeychain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SSKeychain.h; sourceTree = ""; }; + 189DD74926E21D8400AB55B1 /* SSKeychain.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SSKeychain.m; sourceTree = ""; }; + 189DD74E26E21D9000AB55B1 /* GCDHelper.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GCDHelper.m; sourceTree = ""; }; + 189DD74F26E21D9000AB55B1 /* GCDHelper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GCDHelper.h; sourceTree = ""; }; + 189DD75726E6003C00AB55B1 /* Api.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Api.h; sourceTree = ""; }; + 189DD75826E6003C00AB55B1 /* Api.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Api.m; sourceTree = ""; }; + 189DD76026E60DDC00AB55B1 /* Api+Login.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Api+Login.h"; sourceTree = ""; }; + 189DD76126E60DDC00AB55B1 /* Api+Login.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "Api+Login.m"; sourceTree = ""; }; + 189DD78026E620FE00AB55B1 /* ApiHost.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ApiHost.h; sourceTree = ""; }; + 7DB00EC07F1D0ADFF900B38D /* Pods-xplan-ios.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-xplan-ios.debug.xcconfig"; path = "Target Support Files/Pods-xplan-ios/Pods-xplan-ios.debug.xcconfig"; sourceTree = ""; }; + B66633E061B1B34177CD011C /* Pods-xplan-ios.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-xplan-ios.release.xcconfig"; path = "Target Support Files/Pods-xplan-ios/Pods-xplan-ios.release.xcconfig"; sourceTree = ""; }; + CACF623970097D653132D69A /* Pods_xplan_ios.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_xplan_ios.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 189DD52626DE255300AB55B1 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 73FFADDC93E195344047A2EC /* Pods_xplan_ios.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 187EEEA926E62679002833B2 /* Api */ = { + isa = PBXGroup; + children = ( + 189DD75726E6003C00AB55B1 /* Api.h */, + 189DD75826E6003C00AB55B1 /* Api.m */, + ); + path = Api; + sourceTree = ""; + }; + 189DD52026DE255300AB55B1 = { + isa = PBXGroup; + children = ( + 189DD52B26DE255300AB55B1 /* xplan-ios */, + 189DD52A26DE255300AB55B1 /* Products */, + D09C770DC30B9BAAEAFC7945 /* Pods */, + BFB922F5D81845AC32D1E1ED /* Frameworks */, + ); + sourceTree = ""; + }; + 189DD52A26DE255300AB55B1 /* Products */ = { + isa = PBXGroup; + children = ( + 189DD52926DE255300AB55B1 /* xplan-ios.app */, + ); + name = Products; + sourceTree = ""; + }; + 189DD52B26DE255300AB55B1 /* xplan-ios */ = { + isa = PBXGroup; + children = ( + 189DD56B26DF5B0900AB55B1 /* Base */, + 189DD56126DE45F800AB55B1 /* Main */, + 189DD52C26DE255300AB55B1 /* AppDelegate.h */, + 189DD52D26DE255300AB55B1 /* AppDelegate.m */, + 189DD53826DE255600AB55B1 /* Assets.xcassets */, + 189DD53A26DE255600AB55B1 /* LaunchScreen.storyboard */, + 189DD53D26DE255600AB55B1 /* Info.plist */, + 189DD53E26DE255600AB55B1 /* main.m */, + ); + path = "xplan-ios"; + sourceTree = ""; + }; + 189DD54826DE327B00AB55B1 /* MVP */ = { + isa = PBXGroup; + children = ( + 187EEEA926E62679002833B2 /* Api */, + 189DD56026DE456100AB55B1 /* Model */, + 189DD55326DE382E00AB55B1 /* View */, + 189DD55726DE398A00AB55B1 /* Presenter */, + 189DD55D26DE3BA300AB55B1 /* Protocol */, + ); + path = MVP; + sourceTree = ""; + }; + 189DD55326DE382E00AB55B1 /* View */ = { + isa = PBXGroup; + children = ( + 189DD54E26DE37F900AB55B1 /* MvpViewController.h */, + 189DD54F26DE37F900AB55B1 /* MvpViewController.m */, + ); + path = View; + sourceTree = ""; + }; + 189DD55726DE398A00AB55B1 /* Presenter */ = { + isa = PBXGroup; + children = ( + 189DD55826DE39D200AB55B1 /* BaseMvpPresenter.h */, + 189DD55926DE39D200AB55B1 /* BaseMvpPresenter.m */, + ); + path = Presenter; + sourceTree = ""; + }; + 189DD55D26DE3BA300AB55B1 /* Protocol */ = { + isa = PBXGroup; + children = ( + 189DD55E26DE3BBE00AB55B1 /* BaseMvpProtocol.h */, + ); + path = Protocol; + sourceTree = ""; + }; + 189DD56026DE456100AB55B1 /* Model */ = { + isa = PBXGroup; + children = ( + ); + path = Model; + sourceTree = ""; + }; + 189DD56126DE45F800AB55B1 /* Main */ = { + isa = PBXGroup; + children = ( + 189DD54826DE327B00AB55B1 /* MVP */, + 189DD56226DE460400AB55B1 /* Login */, + 189DD53226DE255300AB55B1 /* TabbarViewController.h */, + 189DD53326DE255300AB55B1 /* TabbarViewController.m */, + ); + path = Main; + sourceTree = ""; + }; + 189DD56226DE460400AB55B1 /* Login */ = { + isa = PBXGroup; + children = ( + 189DD75326E2211000AB55B1 /* Api */, + 189DD58926DF977D00AB55B1 /* Model */, + 189DD58A26DF978700AB55B1 /* View */, + 189DD58B26DF978F00AB55B1 /* Presenter */, + 189DD59126DF97F600AB55B1 /* Protocol */, + ); + path = Login; + sourceTree = ""; + }; + 189DD56B26DF5B0900AB55B1 /* Base */ = { + isa = PBXGroup; + children = ( + 189DD61E26E0ED0100AB55B1 /* Net */, + 189DD5A726DFA09700AB55B1 /* Tool */, + 189DD56C26DF5B5400AB55B1 /* UI */, + 189DD57226DF5F0C00AB55B1 /* Base.pch */, + ); + path = Base; + sourceTree = ""; + }; + 189DD56C26DF5B5400AB55B1 /* UI */ = { + isa = PBXGroup; + children = ( + 189DD54926DE338800AB55B1 /* BaseViewController.h */, + 189DD54A26DE338800AB55B1 /* BaseViewController.m */, + 189DD67C26E1FD8900AB55B1 /* UIImage+Utils.h */, + 189DD67D26E1FD8900AB55B1 /* UIImage+Utils.m */, + 189DD68326E1FDBB00AB55B1 /* XCHUDTool.h */, + 189DD68226E1FDBB00AB55B1 /* XCHUDTool.m */, + ); + path = UI; + sourceTree = ""; + }; + 189DD58926DF977D00AB55B1 /* Model */ = { + isa = PBXGroup; + children = ( + ); + path = Model; + sourceTree = ""; + }; + 189DD58A26DF978700AB55B1 /* View */ = { + isa = PBXGroup; + children = ( + 189DD56326DE465A00AB55B1 /* LoginViewController.h */, + 189DD56426DE465A00AB55B1 /* LoginViewController.m */, + ); + path = View; + sourceTree = ""; + }; + 189DD58B26DF978F00AB55B1 /* Presenter */ = { + isa = PBXGroup; + children = ( + 189DD58D26DF97E700AB55B1 /* LoginPresenter.h */, + 189DD58E26DF97E700AB55B1 /* LoginPresenter.m */, + ); + path = Presenter; + sourceTree = ""; + }; + 189DD59126DF97F600AB55B1 /* Protocol */ = { + isa = PBXGroup; + children = ( + 189DD59426DF986300AB55B1 /* LoginProtocol.h */, + ); + path = Protocol; + sourceTree = ""; + }; + 189DD5A726DFA09700AB55B1 /* Tool */ = { + isa = PBXGroup; + children = ( + 189DD74D26E21D9000AB55B1 /* GCDHelper */, + 189DD74726E21D8400AB55B1 /* KeyChain */, + 189DD74226E21CCC00AB55B1 /* Reachability */, + 189DD73626E21C3F00AB55B1 /* YYUtility */, + ); + path = Tool; + sourceTree = ""; + }; + 189DD61E26E0ED0100AB55B1 /* Net */ = { + isa = PBXGroup; + children = ( + 189DD78026E620FE00AB55B1 /* ApiHost.h */, + 189DD6FD26E20E5900AB55B1 /* HttpRequestHelper.h */, + 189DD6FE26E20E5900AB55B1 /* HttpRequestHelper.m */, + ); + path = Net; + sourceTree = ""; + }; + 189DD73626E21C3F00AB55B1 /* YYUtility */ = { + isa = PBXGroup; + children = ( + 189DD73726E21C3F00AB55B1 /* CarrierIdentifier.h */, + 189DD73C26E21C3F00AB55B1 /* YYUtility.h */, + 189DD73926E21C3F00AB55B1 /* YYUtility.m */, + 189DD73B26E21C3F00AB55B1 /* YYUtility+App.m */, + 189DD73A26E21C3F00AB55B1 /* YYUtility+Carrier.m */, + 189DD73826E21C3F00AB55B1 /* YYUtility+Device.m */, + ); + path = YYUtility; + sourceTree = ""; + }; + 189DD74226E21CCC00AB55B1 /* Reachability */ = { + isa = PBXGroup; + children = ( + 189DD74326E21CCC00AB55B1 /* YYReachability.h */, + 189DD74426E21CCC00AB55B1 /* YYReachability.m */, + ); + path = Reachability; + sourceTree = ""; + }; + 189DD74726E21D8400AB55B1 /* KeyChain */ = { + isa = PBXGroup; + children = ( + 189DD74826E21D8400AB55B1 /* SSKeychain.h */, + 189DD74926E21D8400AB55B1 /* SSKeychain.m */, + ); + path = KeyChain; + sourceTree = ""; + }; + 189DD74D26E21D9000AB55B1 /* GCDHelper */ = { + isa = PBXGroup; + children = ( + 189DD74E26E21D9000AB55B1 /* GCDHelper.m */, + 189DD74F26E21D9000AB55B1 /* GCDHelper.h */, + ); + path = GCDHelper; + sourceTree = ""; + }; + 189DD75326E2211000AB55B1 /* Api */ = { + isa = PBXGroup; + children = ( + 189DD76026E60DDC00AB55B1 /* Api+Login.h */, + 189DD76126E60DDC00AB55B1 /* Api+Login.m */, + ); + path = Api; + sourceTree = ""; + }; + BFB922F5D81845AC32D1E1ED /* Frameworks */ = { + isa = PBXGroup; + children = ( + CACF623970097D653132D69A /* Pods_xplan_ios.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + D09C770DC30B9BAAEAFC7945 /* Pods */ = { + isa = PBXGroup; + children = ( + 7DB00EC07F1D0ADFF900B38D /* Pods-xplan-ios.debug.xcconfig */, + B66633E061B1B34177CD011C /* Pods-xplan-ios.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 189DD52826DE255300AB55B1 /* xplan-ios */ = { + isa = PBXNativeTarget; + buildConfigurationList = 189DD54226DE255600AB55B1 /* Build configuration list for PBXNativeTarget "xplan-ios" */; + buildPhases = ( + 1865B406E358C680125F108D /* [CP] Check Pods Manifest.lock */, + 189DD52526DE255300AB55B1 /* Sources */, + 189DD52626DE255300AB55B1 /* Frameworks */, + 189DD52726DE255300AB55B1 /* Resources */, + 8311720C3643AC2030B96510 /* [CP] Embed Pods Frameworks */, + 4C25F8F9E2D1F501119C383D /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "xplan-ios"; + productName = "xplan-ios"; + productReference = 189DD52926DE255300AB55B1 /* xplan-ios.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 189DD52126DE255300AB55B1 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1240; + TargetAttributes = { + 189DD52826DE255300AB55B1 = { + CreatedOnToolsVersion = 12.4; + }; + }; + }; + buildConfigurationList = 189DD52426DE255300AB55B1 /* Build configuration list for PBXProject "xplan-ios" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 189DD52026DE255300AB55B1; + productRefGroup = 189DD52A26DE255300AB55B1 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 189DD52826DE255300AB55B1 /* xplan-ios */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 189DD52726DE255300AB55B1 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 189DD53C26DE255600AB55B1 /* LaunchScreen.storyboard in Resources */, + 189DD53926DE255600AB55B1 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 1865B406E358C680125F108D /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-xplan-ios-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 4C25F8F9E2D1F501119C383D /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-xplan-ios/Pods-xplan-ios-resources-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-xplan-ios/Pods-xplan-ios-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-xplan-ios/Pods-xplan-ios-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + 8311720C3643AC2030B96510 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-xplan-ios/Pods-xplan-ios-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-xplan-ios/Pods-xplan-ios-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-xplan-ios/Pods-xplan-ios-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 189DD52526DE255300AB55B1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 189DD76226E60DDC00AB55B1 /* Api+Login.m in Sources */, + 189DD73E26E21C3F00AB55B1 /* YYUtility.m in Sources */, + 189DD53426DE255300AB55B1 /* TabbarViewController.m in Sources */, + 189DD55A26DE39D200AB55B1 /* BaseMvpPresenter.m in Sources */, + 189DD6FF26E20E5900AB55B1 /* HttpRequestHelper.m in Sources */, + 189DD74A26E21D8400AB55B1 /* SSKeychain.m in Sources */, + 189DD68426E1FDBB00AB55B1 /* XCHUDTool.m in Sources */, + 189DD73F26E21C3F00AB55B1 /* YYUtility+Carrier.m in Sources */, + 189DD54B26DE338800AB55B1 /* BaseViewController.m in Sources */, + 189DD67E26E1FD8900AB55B1 /* UIImage+Utils.m in Sources */, + 189DD52E26DE255300AB55B1 /* AppDelegate.m in Sources */, + 189DD56526DE465A00AB55B1 /* LoginViewController.m in Sources */, + 189DD73D26E21C3F00AB55B1 /* YYUtility+Device.m in Sources */, + 189DD74026E21C3F00AB55B1 /* YYUtility+App.m in Sources */, + 189DD74526E21CCC00AB55B1 /* YYReachability.m in Sources */, + 189DD75026E21D9000AB55B1 /* GCDHelper.m in Sources */, + 189DD75926E6003C00AB55B1 /* Api.m in Sources */, + 189DD53F26DE255600AB55B1 /* main.m in Sources */, + 189DD58F26DF97E700AB55B1 /* LoginPresenter.m in Sources */, + 189DD55026DE37F900AB55B1 /* MvpViewController.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 189DD53A26DE255600AB55B1 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 189DD53B26DE255600AB55B1 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 189DD54026DE255600AB55B1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.4; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + }; + name = Debug; + }; + 189DD54126DE255600AB55B1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.4; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 189DD54326DE255600AB55B1 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7DB00EC07F1D0ADFF900B38D /* Pods-xplan-ios.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 48UCG35Q9W; + GCC_PREFIX_HEADER = "xplan-ios/Base/Base.pch"; + INFOPLIST_FILE = "xplan-ios/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 3.1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.yinyou.enterprise.ios; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 189DD54426DE255600AB55B1 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = B66633E061B1B34177CD011C /* Pods-xplan-ios.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 48UCG35Q9W; + GCC_PREFIX_HEADER = "xplan-ios/Base/Base.pch"; + INFOPLIST_FILE = "xplan-ios/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 9.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 3.1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.yinyou.enterprise.ios; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 189DD52426DE255300AB55B1 /* Build configuration list for PBXProject "xplan-ios" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 189DD54026DE255600AB55B1 /* Debug */, + 189DD54126DE255600AB55B1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 189DD54226DE255600AB55B1 /* Build configuration list for PBXNativeTarget "xplan-ios" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 189DD54326DE255600AB55B1 /* Debug */, + 189DD54426DE255600AB55B1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 189DD52126DE255300AB55B1 /* Project object */; +} diff --git a/xplan-ios.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/xplan-ios.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..919434a6 --- /dev/null +++ b/xplan-ios.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/xplan-ios.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/xplan-ios.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/xplan-ios.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/xplan-ios.xcworkspace/contents.xcworkspacedata b/xplan-ios.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..9f98a206 --- /dev/null +++ b/xplan-ios.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/xplan-ios.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/xplan-ios.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/xplan-ios.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/xplan-ios.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/xplan-ios.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 00000000..f9b0d7c5 --- /dev/null +++ b/xplan-ios.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + PreviewsEnabled + + + diff --git a/xplan-ios/AppDelegate.h b/xplan-ios/AppDelegate.h new file mode 100644 index 00000000..dde3a8ab --- /dev/null +++ b/xplan-ios/AppDelegate.h @@ -0,0 +1,15 @@ +// +// AppDelegate.h +// xplan-ios +// +// Created by zu on 2021/8/31. +// + +#import + +@interface AppDelegate : UIResponder + +@property (strong, nonatomic) UIWindow *window; + +@end + diff --git a/xplan-ios/AppDelegate.m b/xplan-ios/AppDelegate.m new file mode 100644 index 00000000..59247fa1 --- /dev/null +++ b/xplan-ios/AppDelegate.m @@ -0,0 +1,26 @@ +// +// AppDelegate.m +// xplan-ios +// +// Created by zu on 2021/8/31. +// + +#import "AppDelegate.h" +#import "TabbarViewController.h" + +@interface AppDelegate () + +@end + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + TabbarViewController *vc = [[TabbarViewController alloc] init]; + UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:vc]; + self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; + self.window.rootViewController = nav; + [self.window makeKeyAndVisible]; + return YES; +} + +@end diff --git a/xplan-ios/Assets.xcassets/AccentColor.colorset/Contents.json b/xplan-ios/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 00000000..eb878970 --- /dev/null +++ b/xplan-ios/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/xplan-ios/Assets.xcassets/AppIcon.appiconset/APP Store 1024_1024 (2).png b/xplan-ios/Assets.xcassets/AppIcon.appiconset/APP Store 1024_1024 (2).png new file mode 100644 index 00000000..5352c523 Binary files /dev/null and b/xplan-ios/Assets.xcassets/AppIcon.appiconset/APP Store 1024_1024 (2).png differ diff --git a/xplan-ios/Assets.xcassets/AppIcon.appiconset/Contents.json b/xplan-ios/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..0ccc17c7 --- /dev/null +++ b/xplan-ios/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,62 @@ +{ + "images" : [ + { + "filename" : "Icon-Spotlight-40.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "filename" : "Icon-60.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "filename" : "Icon-Small@2x.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "filename" : "Icon-Small@3x.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "filename" : "Icon-Spotlight-40@2x.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "filename" : "Icon-60@2x-1.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "filename" : "Icon-60@2x.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "filename" : "Icon-60@3x.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "filename" : "APP Store 1024_1024 (2).png", + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/xplan-ios/Assets.xcassets/AppIcon.appiconset/Icon-60.png b/xplan-ios/Assets.xcassets/AppIcon.appiconset/Icon-60.png new file mode 100644 index 00000000..2200767f Binary files /dev/null and b/xplan-ios/Assets.xcassets/AppIcon.appiconset/Icon-60.png differ diff --git a/xplan-ios/Assets.xcassets/AppIcon.appiconset/Icon-60@2x-1.png b/xplan-ios/Assets.xcassets/AppIcon.appiconset/Icon-60@2x-1.png new file mode 100644 index 00000000..d83fce6a Binary files /dev/null and b/xplan-ios/Assets.xcassets/AppIcon.appiconset/Icon-60@2x-1.png differ diff --git a/xplan-ios/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png b/xplan-ios/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png new file mode 100644 index 00000000..d83fce6a Binary files /dev/null and b/xplan-ios/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png differ diff --git a/xplan-ios/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png b/xplan-ios/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png new file mode 100644 index 00000000..a9be82c5 Binary files /dev/null and b/xplan-ios/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png differ diff --git a/xplan-ios/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png b/xplan-ios/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png new file mode 100644 index 00000000..8a3d2cb1 Binary files /dev/null and b/xplan-ios/Assets.xcassets/AppIcon.appiconset/Icon-Small@2x.png differ diff --git a/xplan-ios/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png b/xplan-ios/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png new file mode 100644 index 00000000..e68ccabe Binary files /dev/null and b/xplan-ios/Assets.xcassets/AppIcon.appiconset/Icon-Small@3x.png differ diff --git a/xplan-ios/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40.png b/xplan-ios/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40.png new file mode 100644 index 00000000..a0c11ca7 Binary files /dev/null and b/xplan-ios/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40.png differ diff --git a/xplan-ios/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40@2x.png b/xplan-ios/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40@2x.png new file mode 100644 index 00000000..c198706f Binary files /dev/null and b/xplan-ios/Assets.xcassets/AppIcon.appiconset/Icon-Spotlight-40@2x.png differ diff --git a/xplan-ios/Assets.xcassets/Common/Contents.json b/xplan-ios/Assets.xcassets/Common/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/xplan-ios/Assets.xcassets/Common/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/xplan-ios/Assets.xcassets/Common/common_checkbox_checked.imageset/Contents.json b/xplan-ios/Assets.xcassets/Common/common_checkbox_checked.imageset/Contents.json new file mode 100644 index 00000000..4ece2b53 --- /dev/null +++ b/xplan-ios/Assets.xcassets/Common/common_checkbox_checked.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "common_checkbox_checked@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "common_checkbox_checked@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/xplan-ios/Assets.xcassets/Common/common_checkbox_checked.imageset/common_checkbox_checked@2x.png b/xplan-ios/Assets.xcassets/Common/common_checkbox_checked.imageset/common_checkbox_checked@2x.png new file mode 100644 index 00000000..6dfb0b2b Binary files /dev/null and b/xplan-ios/Assets.xcassets/Common/common_checkbox_checked.imageset/common_checkbox_checked@2x.png differ diff --git a/xplan-ios/Assets.xcassets/Common/common_checkbox_checked.imageset/common_checkbox_checked@3x.png b/xplan-ios/Assets.xcassets/Common/common_checkbox_checked.imageset/common_checkbox_checked@3x.png new file mode 100644 index 00000000..044cd5ef Binary files /dev/null and b/xplan-ios/Assets.xcassets/Common/common_checkbox_checked.imageset/common_checkbox_checked@3x.png differ diff --git a/xplan-ios/Assets.xcassets/Common/common_checkbox_uncheck.imageset/Contents.json b/xplan-ios/Assets.xcassets/Common/common_checkbox_uncheck.imageset/Contents.json new file mode 100644 index 00000000..a80db0e3 --- /dev/null +++ b/xplan-ios/Assets.xcassets/Common/common_checkbox_uncheck.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "common_checkbox_uncheck@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "common_checkbox_uncheck@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/xplan-ios/Assets.xcassets/Common/common_checkbox_uncheck.imageset/common_checkbox_uncheck@2x.png b/xplan-ios/Assets.xcassets/Common/common_checkbox_uncheck.imageset/common_checkbox_uncheck@2x.png new file mode 100644 index 00000000..ea14258a Binary files /dev/null and b/xplan-ios/Assets.xcassets/Common/common_checkbox_uncheck.imageset/common_checkbox_uncheck@2x.png differ diff --git a/xplan-ios/Assets.xcassets/Common/common_checkbox_uncheck.imageset/common_checkbox_uncheck@3x.png b/xplan-ios/Assets.xcassets/Common/common_checkbox_uncheck.imageset/common_checkbox_uncheck@3x.png new file mode 100644 index 00000000..922dff75 Binary files /dev/null and b/xplan-ios/Assets.xcassets/Common/common_checkbox_uncheck.imageset/common_checkbox_uncheck@3x.png differ diff --git a/xplan-ios/Assets.xcassets/Common/common_loading.imageset/Contents.json b/xplan-ios/Assets.xcassets/Common/common_loading.imageset/Contents.json new file mode 100644 index 00000000..9cb24442 --- /dev/null +++ b/xplan-ios/Assets.xcassets/Common/common_loading.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "loading@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "loading@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/xplan-ios/Assets.xcassets/Common/common_loading.imageset/loading@2x.png b/xplan-ios/Assets.xcassets/Common/common_loading.imageset/loading@2x.png new file mode 100644 index 00000000..5d4ebd40 Binary files /dev/null and b/xplan-ios/Assets.xcassets/Common/common_loading.imageset/loading@2x.png differ diff --git a/xplan-ios/Assets.xcassets/Common/common_loading.imageset/loading@3x.png b/xplan-ios/Assets.xcassets/Common/common_loading.imageset/loading@3x.png new file mode 100644 index 00000000..959d2a9c Binary files /dev/null and b/xplan-ios/Assets.xcassets/Common/common_loading.imageset/loading@3x.png differ diff --git a/xplan-ios/Assets.xcassets/Common/common_nav_back.imageset/Contents.json b/xplan-ios/Assets.xcassets/Common/common_nav_back.imageset/Contents.json new file mode 100644 index 00000000..608ee4a1 --- /dev/null +++ b/xplan-ios/Assets.xcassets/Common/common_nav_back.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "common_nav_back@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "common_nav_back@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/xplan-ios/Assets.xcassets/Common/common_nav_back.imageset/common_nav_back@2x.png b/xplan-ios/Assets.xcassets/Common/common_nav_back.imageset/common_nav_back@2x.png new file mode 100644 index 00000000..6254fcb9 Binary files /dev/null and b/xplan-ios/Assets.xcassets/Common/common_nav_back.imageset/common_nav_back@2x.png differ diff --git a/xplan-ios/Assets.xcassets/Common/common_nav_back.imageset/common_nav_back@3x.png b/xplan-ios/Assets.xcassets/Common/common_nav_back.imageset/common_nav_back@3x.png new file mode 100644 index 00000000..af8e2383 Binary files /dev/null and b/xplan-ios/Assets.xcassets/Common/common_nav_back.imageset/common_nav_back@3x.png differ diff --git a/xplan-ios/Assets.xcassets/Contents.json b/xplan-ios/Assets.xcassets/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/xplan-ios/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/xplan-ios/Assets.xcassets/Login/Contents.json b/xplan-ios/Assets.xcassets/Login/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/xplan-ios/Assets.xcassets/Login/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/xplan-ios/Assets.xcassets/Login/login_appIcon.imageset/Contents.json b/xplan-ios/Assets.xcassets/Login/login_appIcon.imageset/Contents.json new file mode 100644 index 00000000..1b785385 --- /dev/null +++ b/xplan-ios/Assets.xcassets/Login/login_appIcon.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "login_appIcon@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "login_appIcon@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/xplan-ios/Assets.xcassets/Login/login_appIcon.imageset/login_appIcon@2x.png b/xplan-ios/Assets.xcassets/Login/login_appIcon.imageset/login_appIcon@2x.png new file mode 100644 index 00000000..ab245c49 Binary files /dev/null and b/xplan-ios/Assets.xcassets/Login/login_appIcon.imageset/login_appIcon@2x.png differ diff --git a/xplan-ios/Assets.xcassets/Login/login_appIcon.imageset/login_appIcon@3x.png b/xplan-ios/Assets.xcassets/Login/login_appIcon.imageset/login_appIcon@3x.png new file mode 100644 index 00000000..a8f7cd8f Binary files /dev/null and b/xplan-ios/Assets.xcassets/Login/login_appIcon.imageset/login_appIcon@3x.png differ diff --git a/xplan-ios/Assets.xcassets/Login/login_auth_bubble.imageset/Contents.json b/xplan-ios/Assets.xcassets/Login/login_auth_bubble.imageset/Contents.json new file mode 100644 index 00000000..25035935 --- /dev/null +++ b/xplan-ios/Assets.xcassets/Login/login_auth_bubble.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "login_auth_bubble@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "login_auth_bubble@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/xplan-ios/Assets.xcassets/Login/login_auth_bubble.imageset/login_auth_bubble@2x.png b/xplan-ios/Assets.xcassets/Login/login_auth_bubble.imageset/login_auth_bubble@2x.png new file mode 100644 index 00000000..8ca49438 Binary files /dev/null and b/xplan-ios/Assets.xcassets/Login/login_auth_bubble.imageset/login_auth_bubble@2x.png differ diff --git a/xplan-ios/Assets.xcassets/Login/login_auth_bubble.imageset/login_auth_bubble@3x.png b/xplan-ios/Assets.xcassets/Login/login_auth_bubble.imageset/login_auth_bubble@3x.png new file mode 100644 index 00000000..a4ff25f1 Binary files /dev/null and b/xplan-ios/Assets.xcassets/Login/login_auth_bubble.imageset/login_auth_bubble@3x.png differ diff --git a/xplan-ios/Assets.xcassets/Login/login_china_mobile.imageset/ChinaMobile_icon@2x.png b/xplan-ios/Assets.xcassets/Login/login_china_mobile.imageset/ChinaMobile_icon@2x.png new file mode 100644 index 00000000..1bedf871 Binary files /dev/null and b/xplan-ios/Assets.xcassets/Login/login_china_mobile.imageset/ChinaMobile_icon@2x.png differ diff --git a/xplan-ios/Assets.xcassets/Login/login_china_mobile.imageset/ChinaMobile_icon@3x.png b/xplan-ios/Assets.xcassets/Login/login_china_mobile.imageset/ChinaMobile_icon@3x.png new file mode 100644 index 00000000..be2466e6 Binary files /dev/null and b/xplan-ios/Assets.xcassets/Login/login_china_mobile.imageset/ChinaMobile_icon@3x.png differ diff --git a/xplan-ios/Assets.xcassets/Login/login_china_mobile.imageset/Contents.json b/xplan-ios/Assets.xcassets/Login/login_china_mobile.imageset/Contents.json new file mode 100644 index 00000000..9887a543 --- /dev/null +++ b/xplan-ios/Assets.xcassets/Login/login_china_mobile.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "ChinaMobile_icon@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "ChinaMobile_icon@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/xplan-ios/Assets.xcassets/Login/login_ct_mobile.imageset/Contents.json b/xplan-ios/Assets.xcassets/Login/login_ct_mobile.imageset/Contents.json new file mode 100644 index 00000000..81068c12 --- /dev/null +++ b/xplan-ios/Assets.xcassets/Login/login_ct_mobile.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "ctmobile_icon@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "ctmobile_icon@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/xplan-ios/Assets.xcassets/Login/login_ct_mobile.imageset/ctmobile_icon@2x.png b/xplan-ios/Assets.xcassets/Login/login_ct_mobile.imageset/ctmobile_icon@2x.png new file mode 100644 index 00000000..ff8d8ee5 Binary files /dev/null and b/xplan-ios/Assets.xcassets/Login/login_ct_mobile.imageset/ctmobile_icon@2x.png differ diff --git a/xplan-ios/Assets.xcassets/Login/login_ct_mobile.imageset/ctmobile_icon@3x.png b/xplan-ios/Assets.xcassets/Login/login_ct_mobile.imageset/ctmobile_icon@3x.png new file mode 100644 index 00000000..de9eef63 Binary files /dev/null and b/xplan-ios/Assets.xcassets/Login/login_ct_mobile.imageset/ctmobile_icon@3x.png differ diff --git a/xplan-ios/Assets.xcassets/Login/login_phone.imageset/Contents.json b/xplan-ios/Assets.xcassets/Login/login_phone.imageset/Contents.json new file mode 100644 index 00000000..60a82c18 --- /dev/null +++ b/xplan-ios/Assets.xcassets/Login/login_phone.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "phone@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "phone@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/xplan-ios/Assets.xcassets/Login/login_phone.imageset/phone@2x.png b/xplan-ios/Assets.xcassets/Login/login_phone.imageset/phone@2x.png new file mode 100644 index 00000000..6aadb485 Binary files /dev/null and b/xplan-ios/Assets.xcassets/Login/login_phone.imageset/phone@2x.png differ diff --git a/xplan-ios/Assets.xcassets/Login/login_phone.imageset/phone@3x.png b/xplan-ios/Assets.xcassets/Login/login_phone.imageset/phone@3x.png new file mode 100644 index 00000000..a0308bd8 Binary files /dev/null and b/xplan-ios/Assets.xcassets/Login/login_phone.imageset/phone@3x.png differ diff --git a/xplan-ios/Assets.xcassets/Login/login_qq.imageset/Contents.json b/xplan-ios/Assets.xcassets/Login/login_qq.imageset/Contents.json new file mode 100644 index 00000000..625e041a --- /dev/null +++ b/xplan-ios/Assets.xcassets/Login/login_qq.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "qq@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "qq@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/xplan-ios/Assets.xcassets/Login/login_qq.imageset/qq@2x.png b/xplan-ios/Assets.xcassets/Login/login_qq.imageset/qq@2x.png new file mode 100644 index 00000000..337e4389 Binary files /dev/null and b/xplan-ios/Assets.xcassets/Login/login_qq.imageset/qq@2x.png differ diff --git a/xplan-ios/Assets.xcassets/Login/login_qq.imageset/qq@3x.png b/xplan-ios/Assets.xcassets/Login/login_qq.imageset/qq@3x.png new file mode 100644 index 00000000..df8325d5 Binary files /dev/null and b/xplan-ios/Assets.xcassets/Login/login_qq.imageset/qq@3x.png differ diff --git a/xplan-ios/Assets.xcassets/Login/login_unicom_mobile.imageset/Contents.json b/xplan-ios/Assets.xcassets/Login/login_unicom_mobile.imageset/Contents.json new file mode 100644 index 00000000..7aeb2387 --- /dev/null +++ b/xplan-ios/Assets.xcassets/Login/login_unicom_mobile.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "unicomMobile_icon@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "unicomMobile_icon@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/xplan-ios/Assets.xcassets/Login/login_unicom_mobile.imageset/unicomMobile_icon@2x.png b/xplan-ios/Assets.xcassets/Login/login_unicom_mobile.imageset/unicomMobile_icon@2x.png new file mode 100644 index 00000000..df028038 Binary files /dev/null and b/xplan-ios/Assets.xcassets/Login/login_unicom_mobile.imageset/unicomMobile_icon@2x.png differ diff --git a/xplan-ios/Assets.xcassets/Login/login_unicom_mobile.imageset/unicomMobile_icon@3x.png b/xplan-ios/Assets.xcassets/Login/login_unicom_mobile.imageset/unicomMobile_icon@3x.png new file mode 100644 index 00000000..bc163724 Binary files /dev/null and b/xplan-ios/Assets.xcassets/Login/login_unicom_mobile.imageset/unicomMobile_icon@3x.png differ diff --git a/xplan-ios/Assets.xcassets/Login/login_wechat.imageset/Contents.json b/xplan-ios/Assets.xcassets/Login/login_wechat.imageset/Contents.json new file mode 100644 index 00000000..cb4bb44c --- /dev/null +++ b/xplan-ios/Assets.xcassets/Login/login_wechat.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "wechat@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "wechat@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/xplan-ios/Assets.xcassets/Login/login_wechat.imageset/wechat@2x.png b/xplan-ios/Assets.xcassets/Login/login_wechat.imageset/wechat@2x.png new file mode 100644 index 00000000..67f80543 Binary files /dev/null and b/xplan-ios/Assets.xcassets/Login/login_wechat.imageset/wechat@2x.png differ diff --git a/xplan-ios/Assets.xcassets/Login/login_wechat.imageset/wechat@3x.png b/xplan-ios/Assets.xcassets/Login/login_wechat.imageset/wechat@3x.png new file mode 100644 index 00000000..b3417994 Binary files /dev/null and b/xplan-ios/Assets.xcassets/Login/login_wechat.imageset/wechat@3x.png differ diff --git a/xplan-ios/Base.lproj/LaunchScreen.storyboard b/xplan-ios/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..865e9329 --- /dev/null +++ b/xplan-ios/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/xplan-ios/Base/Base.pch b/xplan-ios/Base/Base.pch new file mode 100644 index 00000000..68008557 --- /dev/null +++ b/xplan-ios/Base/Base.pch @@ -0,0 +1,63 @@ +// +// Base.pch +// xplan-ios +// +// Created by zu on 2021/9/1. +// + +#ifndef Base_pch +#define Base_pch + +// Include any system framework and library headers here that should be included in all compilation units. +// You will also need to set the Prefix Header build setting of one or more of your targets to reference this file. +#import + +#define AppName ([[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleDisplayName"]) + +//iPhoneX系列设备(刘海屏设备) +#define iPhoneXSeries \ +({BOOL isPhoneXSeries = NO;\ +if (@available(iOS 11.0, *)) {\ +isPhoneXSeries = [[UIApplication sharedApplication] delegate].window.safeAreaInsets.bottom > 0.0;\ +}\ +(isPhoneXSeries);}) + +#define UIColorFromRGB(rgbValue) [UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0] + +/// 默认主题色 +#define ThemeDefaultColor UIColorFromRGB(0x248CFE) +/// 页面背景色 +#define ThemeBackgroundColor UIColorFromRGB(0x1C1B22) +/// 字体默认颜色 +#define ThemeTextColor UIColorFromRGB(0xC6C6E9) +/// 字体默认颜色 +#define ThemeTextSecondColor UIColorFromRGB(0x4F516A) +/// 按钮渐变色 start +#define ThemeButtonGradientStartColor UIColorFromRGB(0x218EFF) +/// 按钮渐变色 end +#define ThemeButtonGradientEndColor UIColorFromRGB(0x7727E4) + +#define KScreenWidth [[UIScreen mainScreen] bounds].size.width +#define KScreenHeight [[UIScreen mainScreen] bounds].size.height +#define statusbarHeight [[UIApplication sharedApplication] statusBarFrame].size.height +#define kStatusBarHeight statusbarHeight +#define kSafeAreaBottomHeight (iPhoneXSeries ? 34 : 0) +#define kSafeAreaTopHeight (iPhoneXSeries ? 24 : 0) +#define kNavigationHeight (kStatusBarHeight + 44) +#define kTabBarHeight (iPhoneXSeries ? 49.0+34.0 : 49.0) + +#ifdef DEBUG +#define NSLog(fmt,...) NSLog((@"%s [Line %d]" fmt),__PRETTY_FUNCTION__,__LINE__,##__VA_ARGS__) +#else +#define NSLog(...) +#endif + +#define isEnterprise \ +({BOOL isEnterprise = NO;\ +if (@available(iOS 11.0, *)) {\ + NSString *bundleID = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleIdentifier"];\ + isEnterprise = [bundleID isEqualToString:@"com.yinyou.enterprise.ios"];\ +}\ +(isEnterprise);}) + +#endif /* Base_pch */ diff --git a/xplan-ios/Base/Net/ApiHost.h b/xplan-ios/Base/Net/ApiHost.h new file mode 100644 index 00000000..88e58a49 --- /dev/null +++ b/xplan-ios/Base/Net/ApiHost.h @@ -0,0 +1,17 @@ +// +// ApiHost.h +// xplan-ios +// +// Created by zu on 2021/9/6. +// + +#ifndef ApiHost_h +#define ApiHost_h + +#ifdef DEBUG +#define API_HOST_URL @"http://api.uat.lecheng163.com" +#else +#define API_HOST_URL @"https://api.lecheng163.com" +#endif + +#endif /* ApiHost_h */ diff --git a/xplan-ios/Base/Net/HttpRequestHelper.h b/xplan-ios/Base/Net/HttpRequestHelper.h new file mode 100644 index 00000000..6fcfd512 --- /dev/null +++ b/xplan-ios/Base/Net/HttpRequestHelper.h @@ -0,0 +1,59 @@ +// +// HttpRequestHelper.h +// xplan-ios +// +// Created by zu on 2021/9/3. +// + +#import + +typedef NS_ENUM(NSUInteger, HttpRequestHelperMethod) { + HttpRequestHelperMethodPOST, + HttpRequestHelperMethodGET +}; + +static dispatch_once_t onceToken; + +typedef void(^HttpRequestHelperCompletion)(id _Nullable data, NSNumber * _Nullable code, NSString * _Nullable msg); + +NS_ASSUME_NONNULL_BEGIN + +@interface HttpRequestHelper : NSObject + ++ (void)GET:(NSString *)method + params:(NSDictionary *)params + success:(void (^)(id data))success + failure:(void (^)(NSNumber *resCode, NSString *message))failure; + ++ (void)GET:(NSString *)interfaceUrl + method:(NSString *)method + params:(NSDictionary *)params + success:(void (^)(id data))success + failure:(void (^)(NSNumber *resCode, NSString *message))failure; + + ++ (void)POST:(NSString *)method + params:(NSDictionary *)params + success:(void (^)(id data))success + failure:(void (^)(NSNumber *resCode, NSString *message))failure; + ++ (void)POST:(NSString *)interfaceUrl + method:(NSString *)method + params:(NSDictionary *)params + success:(void (^)(id data))success + failure:(void (^)(NSNumber *resCode, NSString *message))failure; + ++ (void)request:(NSString *)url + method:(HttpRequestHelperMethod)method + params:(NSDictionary *)params + success:(void (^)(id data))success + failure:(void (^)(NSNumber *resCode, NSString *message))failure; + ++ (void)request:(NSString *)path + method:(HttpRequestHelperMethod)method + params:(NSDictionary *)params + completion:(HttpRequestHelperCompletion)completion; + +@end + +NS_ASSUME_NONNULL_END diff --git a/xplan-ios/Base/Net/HttpRequestHelper.m b/xplan-ios/Base/Net/HttpRequestHelper.m new file mode 100644 index 00000000..03e50974 --- /dev/null +++ b/xplan-ios/Base/Net/HttpRequestHelper.m @@ -0,0 +1,170 @@ +// +// HttpRequestHelper.m +// xplan-ios +// +// Created by zu on 2021/9/3. +// + +#import "HttpRequestHelper.h" +#import "ApiHost.h" +#import "YYUtility.h" +#import "YYReachability.h" +#import + +@implementation HttpRequestHelper + ++(AFHTTPSessionManager *)requestManager +{ + static AFHTTPSessionManager *manager = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + NSURLSessionConfiguration * config = [NSURLSessionConfiguration defaultSessionConfiguration]; + manager = [[AFHTTPSessionManager alloc]initWithSessionConfiguration:config]; + manager.requestSerializer.timeoutInterval = 15; + manager.responseSerializer = [AFJSONResponseSerializer serializer]; + manager.requestSerializer.HTTPShouldHandleCookies = YES; + // 客户端是否信任非法证书 + manager.securityPolicy.allowInvalidCertificates = YES; + AFSecurityPolicy *securityPolicy= [AFSecurityPolicy defaultPolicy]; + manager.securityPolicy = securityPolicy; + // 是否在证书域字段中验证域名 + [manager.securityPolicy setValidatesDomainName:NO]; + manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript",@"text/html",@"text/plain",@"image/jpeg",@"image/png", nil]; + }); + return manager; +} + ++ (void)GET:(NSString *)method + params:(NSDictionary *)params + success:(void (^)(id data))success + failure:(void (^)(NSNumber *resCode, NSString *message))failure +{ + [self GET:API_HOST_URL method:method params:params success:success failure:failure]; + +} + ++ (void)GET:(NSString *)interfaceUrl method:(NSString *)method params:(NSDictionary *)params success:(void (^)(id))success failure:(void (^)(NSNumber *, NSString *))failure +{ + if ([AFNetworkReachabilityManager sharedManager].networkReachabilityStatus == 0) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.01 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + + }); + } + + AFHTTPSessionManager *manager = [HttpRequestHelper requestManager]; + + NSString *url = [NSString stringWithFormat:@"%@/%@", interfaceUrl, method]; + +#ifdef DEBUG + NSLog(@"url:%@, parameter:%@", url, params); +#endif + + [manager GET:url parameters:params headers:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { + + } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { + + }]; +} + ++ (void)POST:(NSString *)method + params:(NSDictionary *)params + success:(void (^)(id data))success + failure:(void (^)(NSNumber *resCode, NSString *message))failure +{ + [self POST:API_HOST_URL method:method params:params success:success failure:failure]; +} + ++ (void)POST:(NSString *)interfaceUrl + method:(NSString *)method + params:(NSDictionary *)params + success:(void (^)(id data))success + failure:(void (^)(NSNumber *resCode, NSString *message))failure +{ + if ([AFNetworkReachabilityManager sharedManager].networkReachabilityStatus == 0) { + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.01 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + + }); + } + + AFHTTPSessionManager *manager = [HttpRequestHelper requestManager]; + + NSString *url = [NSString stringWithFormat:@"%@/%@", interfaceUrl, method]; + params = [self configBaseParmars:params]; +#ifdef DEBUG + NSLog(@"\nurl:\n%@\nparameter:\n%@", url, params); +#endif + + [manager POST:url parameters:params headers:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { +#ifdef DEBUG + NSLog(@"\n%@", responseObject); +#endif + success(responseObject); + } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { +#ifdef DEBUG + NSLog(@"\n%@", error); +#endif + }]; +} + ++ (void)request:(NSString *)url + method:(HttpRequestHelperMethod)method + params:(NSDictionary *)params + success:(void (^)(id data))success + failure:(void (^)(NSNumber *resCode, NSString *message))failure +{ + + switch (method) { + case HttpRequestHelperMethodGET: { + [self GET:url params:params success:success failure:failure]; + } + break; + case HttpRequestHelperMethodPOST:{ + [self POST:url params:params success:success failure:failure]; + } + break; + } +} + ++ (void)request:(NSString *)path + method:(HttpRequestHelperMethod)method + params:(NSDictionary *)params + completion:(HttpRequestHelperCompletion)completion +{ + if ([path hasPrefix:@"/"]) { + path = [path substringFromIndex:1]; + } + [self request:path method:method params:params success:^(id data) { + if (completion) { + completion(data, nil, nil); + } + } failure:^(NSNumber *resCode, NSString *message) { + if (completion) { + completion(nil, resCode, message); + } + }]; +} + ++ (NSDictionary*)configBaseParmars:(NSDictionary *)parmars { + NSDictionary *defaultBasciParame = @{ + @"os" : @"iOS", + @"osVersion" : [YYUtility systemVersion], + @"netType" : ([YYUtility networkStatus] == ReachableViaWiFi) ? @2 : @1, + @"ispType" : @([YYUtility carrierIdentifier]), + @"channel" : [YYUtility getAppSource] ? : @"", + @"model" : [YYUtility modelName], + @"deviceId" : [YYUtility deviceUniqueIdentification], + @"appVersion" : [YYUtility appVersion], + @"app" : [YYUtility appName] + }; + if (!parmars||![parmars isKindOfClass:[NSDictionary class]]){ + NSMutableDictionary *dic = [NSMutableDictionary dictionaryWithDictionary:defaultBasciParame]; + return dic; + } + NSMutableDictionary * dic = [NSMutableDictionary dictionaryWithDictionary:parmars]; + for (NSString *parameKey in defaultBasciParame.allKeys) { + [dic setObject:defaultBasciParame[parameKey] forKey:parameKey]; + } + return dic; +} + +@end diff --git a/xplan-ios/Base/Tool/GCDHelper/GCDHelper.h b/xplan-ios/Base/Tool/GCDHelper/GCDHelper.h new file mode 100644 index 00000000..587e2cdb --- /dev/null +++ b/xplan-ios/Base/Tool/GCDHelper/GCDHelper.h @@ -0,0 +1,11 @@ +// +// GCDHelper.h +// YYMobileFramework +// +// Created by wuwei on 14/7/18. +// Copyright (c) 2014年 YY Inc. All rights reserved. +// + +#import + +void dispatch_main_sync_safe(dispatch_block_t block); \ No newline at end of file diff --git a/xplan-ios/Base/Tool/GCDHelper/GCDHelper.m b/xplan-ios/Base/Tool/GCDHelper/GCDHelper.m new file mode 100644 index 00000000..7d3bd056 --- /dev/null +++ b/xplan-ios/Base/Tool/GCDHelper/GCDHelper.m @@ -0,0 +1,16 @@ +// +// GCDHelper.m +// YYMobileFramework +// +// Created by wuwei on 14/7/18. +// Copyright (c) 2014年 YY Inc. All rights reserved. +// + +#import "GCDHelper.h" +void dispatch_main_sync_safe(dispatch_block_t block) { + if ([NSThread isMainThread]) { + block(); + }else { + dispatch_sync(dispatch_get_main_queue(), block); + } +} diff --git a/xplan-ios/Base/Tool/KeyChain/SSKeychain.h b/xplan-ios/Base/Tool/KeyChain/SSKeychain.h new file mode 100755 index 00000000..1460a34a --- /dev/null +++ b/xplan-ios/Base/Tool/KeyChain/SSKeychain.h @@ -0,0 +1,357 @@ +// +// SSKeychain.h +// SSToolkit +// +// Created by Sam Soffes on 5/19/10. +// Copyright (c) 2009-2011 Sam Soffes. All rights reserved. +// + +#import +#import + +/** Error codes that can be returned in NSError objects. */ +typedef enum { + /** No error. */ + SSKeychainErrorNone = noErr, + + /** Some of the arguments were invalid. */ + SSKeychainErrorBadArguments = -1001, + + /** There was no password. */ + SSKeychainErrorNoPassword = -1002, + + /** One or more parameters passed internally were not valid. */ + SSKeychainErrorInvalidParameter = errSecParam, + + /** Failed to allocate memory. */ + SSKeychainErrorFailedToAllocated = errSecAllocate, + + /** No trust results are available. */ + SSKeychainErrorNotAvailable = errSecNotAvailable, + + /** Authorization/Authentication failed. */ + SSKeychainErrorAuthorizationFailed = errSecAuthFailed, + + /** The item already exists. */ + SSKeychainErrorDuplicatedItem = errSecDuplicateItem, + + /** The item cannot be found.*/ + SSKeychainErrorNotFound = errSecItemNotFound, + + /** Interaction with the Security Server is not allowed. */ + SSKeychainErrorInteractionNotAllowed = errSecInteractionNotAllowed, + + /** Unable to decode the provided data. */ + SSKeychainErrorFailedToDecode = errSecDecode +} SSKeychainErrorCode; + +extern NSString *const kSSKeychainErrorDomain; + +/** Account name. */ +extern NSString *const kSSKeychainAccountKey; + +/** + Time the item was created. + + The value will be a string. + */ +extern NSString *const kSSKeychainCreatedAtKey; + +/** Item class. */ +extern NSString *const kSSKeychainClassKey; + +/** Item description. */ +extern NSString *const kSSKeychainDescriptionKey; + +/** Item label. */ +extern NSString *const kSSKeychainLabelKey; + +/** Time the item was last modified. + + The value will be a string. + */ +extern NSString *const kSSKeychainLastModifiedKey; + +/** Where the item was created. */ +extern NSString *const kSSKeychainWhereKey; + +/** + Simple wrapper for accessing accounts, getting passwords, setting passwords, and deleting passwords using the system + Keychain on Mac OS X and iOS. + + This was originally inspired by EMKeychain and SDKeychain (both of which are now gone). Thanks to the authors. + SSKeychain has since switched to a simpler implementation that was abstracted from [SSToolkit](http://sstoolk.it). + */ +@interface SSKeychain : NSObject + +///----------------------- +/// @name Getting Accounts +///----------------------- + +/** + Returns an array containing the Keychain's accounts, or `nil` if the Keychain has no accounts. + + See the `NSString` constants declared in SSKeychain.h for a list of keys that can be used when accessing the + dictionaries returned by this method. + + @return An array of dictionaries containing the Keychain's accounts, or `nil` if the Keychain doesn't have any + accounts. The order of the objects in the array isn't defined. + + @see allAccounts: + */ ++ (NSArray *)allAccounts; + +/** + Returns an array containing the Keychain's accounts, or `nil` if the Keychain doesn't have any + accounts. + + See the `NSString` constants declared in SSKeychain.h for a list of keys that can be used when accessing the + dictionaries returned by this method. + + @param error If accessing the accounts fails, upon return contains an error that describes the problem. + + @return An array of dictionaries containing the Keychain's accounts, or `nil` if the Keychain doesn't have any + accounts. The order of the objects in the array isn't defined. + + @see allAccounts + */ ++ (NSArray *)allAccounts:(NSError **)error; + +/** + Returns an array containing the Keychain's accounts for a given service, or `nil` if the Keychain doesn't have any + accounts for the given service. + + See the `NSString` constants declared in SSKeychain.h for a list of keys that can be used when accessing the + dictionaries returned by this method. + + @param serviceName The service for which to return the corresponding accounts. + + @return An array of dictionaries containing the Keychain's accountsfor a given `serviceName`, or `nil` if the Keychain + doesn't have any accounts for the given `serviceName`. The order of the objects in the array isn't defined. + + @see accountsForService:error: + */ ++ (NSArray *)accountsForService:(NSString *)serviceName; + +/** + Returns an array containing the Keychain's accounts for a given service, or `nil` if the Keychain doesn't have any + accounts for the given service. + + @param serviceName The service for which to return the corresponding accounts. + + @param error If accessing the accounts fails, upon return contains an error that describes the problem. + + @return An array of dictionaries containing the Keychain's accountsfor a given `serviceName`, or `nil` if the Keychain + doesn't have any accounts for the given `serviceName`. The order of the objects in the array isn't defined. + + @see accountsForService: + */ ++ (NSArray *)accountsForService:(NSString *)serviceName error:(NSError **)error; + + +///------------------------ +/// @name Getting Passwords +///------------------------ + +/** + Returns a string containing the password for a given account and service, or `nil` if the Keychain doesn't have a + password for the given parameters. + + @param serviceName The service for which to return the corresponding password. + + @param account The account for which to return the corresponding password. + + @return Returns a string containing the password for a given account and service, or `nil` if the Keychain doesn't + have a password for the given parameters. + + @see passwordForService:account:error: + */ ++ (NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account; + +/** + Returns a string containing the password for a given account and service, or `nil` if the Keychain doesn't have a + password for the given parameters. + + @param serviceName The service for which to return the corresponding password. + + @param account The account for which to return the corresponding password. + + @param error If accessing the password fails, upon return contains an error that describes the problem. + + @return Returns a string containing the password for a given account and service, or `nil` if the Keychain doesn't + have a password for the given parameters. + + @see passwordForService:account: + */ ++ (NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error; + +/** + Returns the password data for a given account and service, or `nil` if the Keychain doesn't have data + for the given parameters. + + @param serviceName The service for which to return the corresponding password. + + @param account The account for which to return the corresponding password. + + @param error If accessing the password fails, upon return contains an error that describes the problem. + + @return Returns a the password data for the given account and service, or `nil` if the Keychain doesn't + have data for the given parameters. + + @see passwordDataForService:account:error: + */ ++ (NSData *)passwordDataForService:(NSString *)serviceName account:(NSString *)account; + +/** + Returns the password data for a given account and service, or `nil` if the Keychain doesn't have data + for the given parameters. + + @param serviceName The service for which to return the corresponding password. + + @param account The account for which to return the corresponding password. + + @param error If accessing the password fails, upon return contains an error that describes the problem. + + @return Returns a the password data for the given account and service, or `nil` if the Keychain doesn't + have a password for the given parameters. + + @see passwordDataForService:account: + */ ++ (NSData *)passwordDataForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error; + + +///------------------------- +/// @name Deleting Passwords +///------------------------- + +/** + Deletes a password from the Keychain. + + @param serviceName The service for which to delete the corresponding password. + + @param account The account for which to delete the corresponding password. + + @return Returns `YES` on success, or `NO` on failure. + + @see deletePasswordForService:account:error: + */ ++ (BOOL)deletePasswordForService:(NSString *)serviceName account:(NSString *)account; + +/** + Deletes a password from the Keychain. + + @param serviceName The service for which to delete the corresponding password. + + @param account The account for which to delete the corresponding password. + + @param error If deleting the password fails, upon return contains an error that describes the problem. + + @return Returns `YES` on success, or `NO` on failure. + + @see deletePasswordForService:account: + */ ++ (BOOL)deletePasswordForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error; + + +///------------------------ +/// @name Setting Passwords +///------------------------ + +/** + Sets a password in the Keychain. + + @param password The password to store in the Keychain. + + @param serviceName The service for which to set the corresponding password. + + @param account The account for which to set the corresponding password. + + @return Returns `YES` on success, or `NO` on failure. + + @see setPassword:forService:account:error: + */ ++ (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account; + +/** + Sets a password in the Keychain. + + @param password The password to store in the Keychain. + + @param serviceName The service for which to set the corresponding password. + + @param account The account for which to set the corresponding password. + + @param error If setting the password fails, upon return contains an error that describes the problem. + + @return Returns `YES` on success, or `NO` on failure. + + @see setPassword:forService:account: + */ ++ (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error; + +/** + Sets arbirary data in the Keychain. + + @param password The data to store in the Keychain. + + @param serviceName The service for which to set the corresponding password. + + @param account The account for which to set the corresponding password. + + @param error If setting the password fails, upon return contains an error that describes the problem. + + @return Returns `YES` on success, or `NO` on failure. + + @see setPasswordData:forService:account:error: + */ ++ (BOOL)setPasswordData:(NSData *)password forService:(NSString *)serviceName account:(NSString *)account; + +/** + Sets arbirary data in the Keychain. + + @param password The data to store in the Keychain. + + @param serviceName The service for which to set the corresponding password. + + @param account The account for which to set the corresponding password. + + @param error If setting the password fails, upon return contains an error that describes the problem. + + @return Returns `YES` on success, or `NO` on failure. + + @see setPasswordData:forService:account: + */ ++ (BOOL)setPasswordData:(NSData *)password forService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error; + + +///-------------------- +/// @name Configuration +///-------------------- + +#if __IPHONE_4_0 && TARGET_OS_IPHONE +/** + Returns the accessibility type for all future passwords saved to the Keychain. + + @return Returns the accessibility type. + + The return value will be `NULL` or one of the "Keychain Item Accessibility Constants" used for determining when a + keychain item should be readable. + + @see accessibilityType + */ ++ (CFTypeRef)accessibilityType; + +/** + Sets the accessibility type for all future passwords saved to the Keychain. + + @param accessibilityType One of the "Keychain Item Accessibility Constants" used for determining when a keychain item + should be readable. + + If the value is `NULL` (the default), the Keychain default will be used. + + @see accessibilityType + */ ++ (void)setAccessibilityType:(CFTypeRef)accessibilityType; +#endif + +@end diff --git a/xplan-ios/Base/Tool/KeyChain/SSKeychain.m b/xplan-ios/Base/Tool/KeyChain/SSKeychain.m new file mode 100755 index 00000000..ff58301e --- /dev/null +++ b/xplan-ios/Base/Tool/KeyChain/SSKeychain.m @@ -0,0 +1,262 @@ +// +// SSKeychain.m +// SSToolkit +// +// Created by Sam Soffes on 5/19/10. +// Copyright (c) 2009-2011 Sam Soffes. All rights reserved. +// + +#import "SSKeychain.h" + +NSString *const kSSKeychainErrorDomain = @"com.samsoffes.sskeychain"; + +NSString *const kSSKeychainAccountKey = @"acct"; +NSString *const kSSKeychainCreatedAtKey = @"cdat"; +NSString *const kSSKeychainClassKey = @"labl"; +NSString *const kSSKeychainDescriptionKey = @"desc"; +NSString *const kSSKeychainLabelKey = @"labl"; +NSString *const kSSKeychainLastModifiedKey = @"mdat"; +NSString *const kSSKeychainWhereKey = @"svce"; + +#if __IPHONE_4_0 && TARGET_OS_IPHONE +CFTypeRef SSKeychainAccessibilityType = NULL; +#endif + +@interface SSKeychain () ++ (NSMutableDictionary *)_queryForService:(NSString *)service account:(NSString *)account; +@end + +@implementation SSKeychain + +#pragma mark - Getting Accounts + ++ (NSArray *)allAccounts { + return [self accountsForService:nil error:nil]; +} + + ++ (NSArray *)allAccounts:(NSError **)error { + return [self accountsForService:nil error:error]; +} + + ++ (NSArray *)accountsForService:(NSString *)service { + return [self accountsForService:service error:nil]; +} + + ++ (NSArray *)accountsForService:(NSString *)service error:(NSError **)error { + OSStatus status = SSKeychainErrorBadArguments; + NSMutableDictionary *query = [self _queryForService:service account:nil]; +#if __has_feature(objc_arc) + [query setObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id)kSecReturnAttributes]; + [query setObject:(__bridge id)kSecMatchLimitAll forKey:(__bridge id)kSecMatchLimit]; +#else + [query setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnAttributes]; + [query setObject:(id)kSecMatchLimitAll forKey:(id)kSecMatchLimit]; +#endif + + CFTypeRef result = NULL; +#if __has_feature(objc_arc) + status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result); +#else + status = SecItemCopyMatching((CFDictionaryRef)query, &result); +#endif + if (status != noErr && error != NULL) { + *error = [NSError errorWithDomain:kSSKeychainErrorDomain code:status userInfo:nil]; + return nil; + } + +#if __has_feature(objc_arc) + return (__bridge_transfer NSArray *)result; +#else + return [(NSArray *)result autorelease]; +#endif +} + + +#pragma mark - Getting Passwords + ++ (NSString *)passwordForService:(NSString *)service account:(NSString *)account { + return [self passwordForService:service account:account error:nil]; +} + + ++ (NSString *)passwordForService:(NSString *)service account:(NSString *)account error:(NSError **)error { + NSData *data = [self passwordDataForService:service account:account error:error]; + if (data.length > 0) { + NSString *string = [[NSString alloc] initWithData:(NSData *)data encoding:NSUTF8StringEncoding]; +#if !__has_feature(objc_arc) + [string autorelease]; +#endif + return string; + } + + return nil; +} + + ++ (NSData *)passwordDataForService:(NSString *)service account:(NSString *)account { + return [self passwordDataForService:service account:account error:nil]; +} + + ++ (NSData *)passwordDataForService:(NSString *)service account:(NSString *)account error:(NSError **)error { + OSStatus status = SSKeychainErrorBadArguments; + if (!service || !account) { + if (error) { + *error = [NSError errorWithDomain:kSSKeychainErrorDomain code:status userInfo:nil]; + } + return nil; + } + + CFTypeRef result = NULL; + NSMutableDictionary *query = [self _queryForService:service account:account]; +#if __has_feature(objc_arc) + [query setObject:(__bridge id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData]; + [query setObject:(__bridge id)kSecMatchLimitOne forKey:(__bridge id)kSecMatchLimit]; + status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result); +#else + [query setObject:(id)kCFBooleanTrue forKey:(id)kSecReturnData]; + [query setObject:(id)kSecMatchLimitOne forKey:(id)kSecMatchLimit]; + status = SecItemCopyMatching((CFDictionaryRef)query, &result); +#endif + + if (status != noErr && error != NULL) { + *error = [NSError errorWithDomain:kSSKeychainErrorDomain code:status userInfo:nil]; + return nil; + } + +#if __has_feature(objc_arc) + return (__bridge_transfer NSData *)result; +#else + return [(NSData *)result autorelease]; +#endif +} + + +#pragma mark - Deleting Passwords + ++ (BOOL)deletePasswordForService:(NSString *)service account:(NSString *)account { + return [self deletePasswordForService:service account:account error:nil]; +} + + ++ (BOOL)deletePasswordForService:(NSString *)service account:(NSString *)account error:(NSError **)error { + OSStatus status = SSKeychainErrorBadArguments; + if (service && account) { + NSMutableDictionary *query = [self _queryForService:service account:account]; +#if __has_feature(objc_arc) + status = SecItemDelete((__bridge CFDictionaryRef)query); +#else + status = SecItemDelete((CFDictionaryRef)query); +#endif + } + if (status != noErr && error != NULL) { + *error = [NSError errorWithDomain:kSSKeychainErrorDomain code:status userInfo:nil]; + } + return (status == noErr); + +} + + +#pragma mark - Setting Passwords + ++ (BOOL)setPassword:(NSString *)password forService:(NSString *)service account:(NSString *)account { + return [self setPassword:password forService:service account:account error:nil]; +} + + ++ (BOOL)setPassword:(NSString *)password forService:(NSString *)service account:(NSString *)account error:(NSError **)error { + NSData *data = [password dataUsingEncoding:NSUTF8StringEncoding]; + return [self setPasswordData:data forService:service account:account error:error]; +} + + ++ (BOOL)setPasswordData:(NSData *)password forService:(NSString *)service account:(NSString *)account { + return [self setPasswordData:password forService:service account:account error:nil]; +} + + ++ (BOOL)setPasswordData:(NSData *)password forService:(NSString *)service account:(NSString *)account error:(NSError **)error { + OSStatus status = SSKeychainErrorBadArguments; + if (password && service && account) { + [self deletePasswordForService:service account:account]; + NSMutableDictionary *query = [self _queryForService:service account:account]; +#if __has_feature(objc_arc) + [query setObject:password forKey:(__bridge id)kSecValueData]; +#else + [query setObject:password forKey:(id)kSecValueData]; +#endif + +#if __IPHONE_4_0 && TARGET_OS_IPHONE + if (SSKeychainAccessibilityType) { +#if __has_feature(objc_arc) + [query setObject:(id)[self accessibilityType] forKey:(__bridge id)kSecAttrAccessible]; +#else + [query setObject:(id)[self accessibilityType] forKey:(id)kSecAttrAccessible]; +#endif + } +#endif + +#if __has_feature(objc_arc) + status = SecItemAdd((__bridge CFDictionaryRef)query, NULL); +#else + status = SecItemAdd((CFDictionaryRef)query, NULL); +#endif + } + if (status != noErr && error != NULL) { + *error = [NSError errorWithDomain:kSSKeychainErrorDomain code:status userInfo:nil]; + } + return (status == noErr); +} + + +#pragma mark - Configuration + +#if __IPHONE_4_0 && TARGET_OS_IPHONE ++ (CFTypeRef)accessibilityType { + return SSKeychainAccessibilityType; +} + + ++ (void)setAccessibilityType:(CFTypeRef)accessibilityType { + CFRetain(accessibilityType); + if (SSKeychainAccessibilityType) { + CFRelease(SSKeychainAccessibilityType); + } + SSKeychainAccessibilityType = accessibilityType; +} +#endif + + +#pragma mark - Private + ++ (NSMutableDictionary *)_queryForService:(NSString *)service account:(NSString *)account { + NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:3]; +#if __has_feature(objc_arc) + [dictionary setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass]; +#else + [dictionary setObject:(id)kSecClassGenericPassword forKey:(id)kSecClass]; +#endif + + if (service) { +#if __has_feature(objc_arc) + [dictionary setObject:service forKey:(__bridge id)kSecAttrService]; +#else + [dictionary setObject:service forKey:(id)kSecAttrService]; +#endif + } + + if (account) { +#if __has_feature(objc_arc) + [dictionary setObject:account forKey:(__bridge id)kSecAttrAccount]; +#else + [dictionary setObject:account forKey:(id)kSecAttrAccount]; +#endif + } + + return dictionary; +} + +@end diff --git a/xplan-ios/Base/Tool/Reachability/YYReachability.h b/xplan-ios/Base/Tool/Reachability/YYReachability.h new file mode 100644 index 00000000..05910449 --- /dev/null +++ b/xplan-ios/Base/Tool/Reachability/YYReachability.h @@ -0,0 +1,109 @@ +/* + Copyright (c) 2011, Tony Million. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + */ + +#import +#import + +#import +#import +#import +#import +#import +#import + +/** + * Does ARC support GCD objects? + * It does if the minimum deployment target is iOS 6+ or Mac OS X 8+ + * + * @see http://opensource.apple.com/source/libdispatch/libdispatch-228.18/os/object.h + **/ +#if OS_OBJECT_USE_OBJC +#define NEEDS_DISPATCH_RETAIN_RELEASE 0 +#else +#define NEEDS_DISPATCH_RETAIN_RELEASE 1 +#endif + +/** + * Create NS_ENUM macro if it does not exist on the targeted version of iOS or OS X. + * + * @see http://nshipster.com/ns_enum-ns_options/ + **/ +#ifndef NS_ENUM +#define NS_ENUM(_type, _name) enum _name : _type _name; enum _name : _type +#endif + +extern NSString *const kYYReachabilityChangedNotification; + +typedef NS_ENUM(NSInteger, NetworkStatus) { + // Apple NetworkStatus Compatible Names. + NotReachable = 0, + ReachableViaWiFi = 2, + ReachableViaWWAN = 1 +}; + +@class YYReachability; + +typedef void (^NetworkReachable)(YYReachability * reachability); +typedef void (^NetworkUnreachable)(YYReachability * reachability); + +@interface YYReachability : NSObject + +@property (nonatomic, copy) NetworkReachable reachableBlock; +@property (nonatomic, copy) NetworkUnreachable unreachableBlock; + + +@property (nonatomic, assign) BOOL reachableOnWWAN; + ++(YYReachability*)reachabilityWithHostname:(NSString*)hostname; ++(YYReachability*)reachabilityForInternetConnection; ++(YYReachability*)reachabilityWithAddress:(const struct sockaddr_in*)hostAddress; ++(YYReachability*)reachabilityForLocalWiFi; + +-(YYReachability *)initWithReachabilityRef:(SCNetworkReachabilityRef)ref; + +-(BOOL)startNotifier; +-(void)stopNotifier; + +-(BOOL)isReachable; +-(BOOL)isReachableViaWWAN; +-(BOOL)isReachableViaWiFi; + +// WWAN may be available, but not active until a connection has been established. +// WiFi may require a connection for VPN on Demand. +-(BOOL)isConnectionRequired; // Identical DDG variant. +-(BOOL)connectionRequired; // Apple's routine. +// Dynamic, on demand connection? +-(BOOL)isConnectionOnDemand; +// Is user intervention required? +-(BOOL)isInterventionRequired; + +-(NetworkStatus)currentReachabilityStatus; +-(SCNetworkReachabilityFlags)reachabilityFlags; +-(NSString*)currentReachabilityString; +-(NSString*)currentReachabilityFlags; + +@end diff --git a/xplan-ios/Base/Tool/Reachability/YYReachability.m b/xplan-ios/Base/Tool/Reachability/YYReachability.m new file mode 100644 index 00000000..3bafa920 --- /dev/null +++ b/xplan-ios/Base/Tool/Reachability/YYReachability.m @@ -0,0 +1,527 @@ +/* + Copyright (c) 2011, Tony Million. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + */ + +#import "YYReachability.h" + + +NSString *const kYYReachabilityChangedNotification = @"kYYReachabilityChangedNotification"; + +@interface YYReachability () + +@property (nonatomic, assign) SCNetworkReachabilityRef reachabilityRef; + + +#if NEEDS_DISPATCH_RETAIN_RELEASE +@property (nonatomic, assign) dispatch_queue_t reachabilitySerialQueue; +#else +@property (nonatomic, strong) dispatch_queue_t reachabilitySerialQueue; +#endif + + +@property (nonatomic, strong) id reachabilityObject; + +-(void)reachabilityChanged:(SCNetworkReachabilityFlags)flags; +-(BOOL)isReachableWithFlags:(SCNetworkReachabilityFlags)flags; + +@end + +static NSString *reachabilityFlags(SCNetworkReachabilityFlags flags) +{ + return [NSString stringWithFormat:@"%c%c %c%c%c%c%c%c%c", +#if TARGET_OS_IPHONE + (flags & kSCNetworkReachabilityFlagsIsWWAN) ? 'W' : '-', +#else + 'X', +#endif + (flags & kSCNetworkReachabilityFlagsReachable) ? 'R' : '-', + (flags & kSCNetworkReachabilityFlagsConnectionRequired) ? 'c' : '-', + (flags & kSCNetworkReachabilityFlagsTransientConnection) ? 't' : '-', + (flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-', + (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) ? 'C' : '-', + (flags & kSCNetworkReachabilityFlagsConnectionOnDemand) ? 'D' : '-', + (flags & kSCNetworkReachabilityFlagsIsLocalAddress) ? 'l' : '-', + (flags & kSCNetworkReachabilityFlagsIsDirect) ? 'd' : '-']; +} + +// Start listening for reachability notifications on the current run loop +static void TMReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info) +{ +#pragma unused (target) +#if __has_feature(objc_arc) + YYReachability *reachability = ((__bridge YYReachability*)info); +#else + YYReachability *reachability = ((YYReachability*)info); +#endif + + // We probably don't need an autoreleasepool here, as GCD docs state each queue has its own autorelease pool, + // but what the heck eh? + @autoreleasepool + { + [reachability reachabilityChanged:flags]; + } +} + + +@implementation YYReachability + +@synthesize reachabilityRef; +@synthesize reachabilitySerialQueue; + +@synthesize reachableOnWWAN; + +@synthesize reachableBlock; +@synthesize unreachableBlock; + +@synthesize reachabilityObject; + +#pragma mark - Class Constructor Methods + ++(YYReachability*)reachabilityWithHostName:(NSString*)hostname +{ + return [YYReachability reachabilityWithHostname:hostname]; +} + ++(YYReachability*)reachabilityWithHostname:(NSString*)hostname +{ + SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithName(NULL, [hostname UTF8String]); + if (ref) + { + id reachability = [[self alloc] initWithReachabilityRef:ref]; + +#if __has_feature(objc_arc) + return reachability; +#else + return [reachability autorelease]; +#endif + + } + + return nil; +} + ++(YYReachability *)reachabilityWithAddress:(const struct sockaddr_in *)hostAddress +{ + SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)hostAddress); + if (ref) + { + id reachability = [[self alloc] initWithReachabilityRef:ref]; + +#if __has_feature(objc_arc) + return reachability; +#else + return [reachability autorelease]; +#endif + } + + return nil; +} + ++(YYReachability *)reachabilityForInternetConnection +{ + struct sockaddr_in zeroAddress; + bzero(&zeroAddress, sizeof(zeroAddress)); + zeroAddress.sin_len = sizeof(zeroAddress); + zeroAddress.sin_family = AF_INET; + + return [self reachabilityWithAddress:&zeroAddress]; +} + ++(YYReachability*)reachabilityForLocalWiFi +{ + struct sockaddr_in localWifiAddress; + bzero(&localWifiAddress, sizeof(localWifiAddress)); + localWifiAddress.sin_len = sizeof(localWifiAddress); + localWifiAddress.sin_family = AF_INET; + // IN_LINKLOCALNETNUM is defined in as 169.254.0.0 + localWifiAddress.sin_addr.s_addr = htonl(IN_LINKLOCALNETNUM); + + return [self reachabilityWithAddress:&localWifiAddress]; +} + + +// Initialization methods + +-(YYReachability *)initWithReachabilityRef:(SCNetworkReachabilityRef)ref +{ + self = [super init]; + if (self != nil) + { + self.reachableOnWWAN = YES; + self.reachabilityRef = ref; + } + + return self; +} + +-(void)dealloc +{ + [self stopNotifier]; + + if(self.reachabilityRef) + { + CFRelease(self.reachabilityRef); + self.reachabilityRef = nil; + } + + self.reachableBlock = nil; + self.unreachableBlock = nil; + +#if !(__has_feature(objc_arc)) + [super dealloc]; +#endif + + +} + +#pragma mark - Notifier Methods + +// Notifier +// NOTE: This uses GCD to trigger the blocks - they *WILL NOT* be called on THE MAIN THREAD +// - In other words DO NOT DO ANY UI UPDATES IN THE BLOCKS. +// INSTEAD USE dispatch_async(dispatch_get_main_queue(), ^{UISTUFF}) (or dispatch_sync if you want) + +-(BOOL)startNotifier +{ + SCNetworkReachabilityContext context = { 0, NULL, NULL, NULL, NULL }; + + // this should do a retain on ourself, so as long as we're in notifier mode we shouldn't disappear out from under ourselves + // woah + self.reachabilityObject = self; + + + + // First, we need to create a serial queue. + // We allocate this once for the lifetime of the notifier. + self.reachabilitySerialQueue = dispatch_queue_create("com.tonymillion.reachability", NULL); + if(!self.reachabilitySerialQueue) + { + return NO; + } + +#if __has_feature(objc_arc) + context.info = (__bridge void *)self; +#else + context.info = (void *)self; +#endif + + if (!SCNetworkReachabilitySetCallback(self.reachabilityRef, TMReachabilityCallback, &context)) + { +#ifdef DEBUG + NSLog(@"SCNetworkReachabilitySetCallback() failed: %s", SCErrorString(SCError())); +#endif + + // Clear out the dispatch queue + if(self.reachabilitySerialQueue) + { +#if NEEDS_DISPATCH_RETAIN_RELEASE + dispatch_release(self.reachabilitySerialQueue); +#endif + self.reachabilitySerialQueue = nil; + } + + self.reachabilityObject = nil; + + return NO; + } + + // Set it as our reachability queue, which will retain the queue + if(!SCNetworkReachabilitySetDispatchQueue(self.reachabilityRef, self.reachabilitySerialQueue)) + { +#ifdef DEBUG + NSLog(@"SCNetworkReachabilitySetDispatchQueue() failed: %s", SCErrorString(SCError())); +#endif + + // UH OH - FAILURE! + + // First stop, any callbacks! + SCNetworkReachabilitySetCallback(self.reachabilityRef, NULL, NULL); + + // Then clear out the dispatch queue. + if(self.reachabilitySerialQueue) + { +#if NEEDS_DISPATCH_RETAIN_RELEASE + dispatch_release(self.reachabilitySerialQueue); +#endif + self.reachabilitySerialQueue = nil; + } + + self.reachabilityObject = nil; + + return NO; + } + + return YES; +} + +-(void)stopNotifier +{ + // First stop, any callbacks! + SCNetworkReachabilitySetCallback(self.reachabilityRef, NULL, NULL); + + // Unregister target from the GCD serial dispatch queue. + SCNetworkReachabilitySetDispatchQueue(self.reachabilityRef, NULL); + + if(self.reachabilitySerialQueue) + { +#if NEEDS_DISPATCH_RETAIN_RELEASE + dispatch_release(self.reachabilitySerialQueue); +#endif + self.reachabilitySerialQueue = nil; + } + + self.reachabilityObject = nil; +} + +#pragma mark - reachability tests + +// This is for the case where you flick the airplane mode; +// you end up getting something like this: +//Reachability: WR ct----- +//Reachability: -- ------- +//Reachability: WR ct----- +//Reachability: -- ------- +// We treat this as 4 UNREACHABLE triggers - really apple should do better than this + +#define testcase (kSCNetworkReachabilityFlagsConnectionRequired | kSCNetworkReachabilityFlagsTransientConnection) + +-(BOOL)isReachableWithFlags:(SCNetworkReachabilityFlags)flags +{ + BOOL connectionUP = YES; + + if(!(flags & kSCNetworkReachabilityFlagsReachable)) + connectionUP = NO; + + if( (flags & testcase) == testcase ) + connectionUP = NO; + +#if TARGET_OS_IPHONE + if(flags & kSCNetworkReachabilityFlagsIsWWAN) + { + // We're on 3G. + if(!self.reachableOnWWAN) + { + // We don't want to connect when on 3G. + connectionUP = NO; + } + } +#endif + + return connectionUP; +} + +-(BOOL)isReachable +{ + SCNetworkReachabilityFlags flags; + + if(!SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags)) + return NO; + + return [self isReachableWithFlags:flags]; +} + +-(BOOL)isReachableViaWWAN +{ +#if TARGET_OS_IPHONE + + SCNetworkReachabilityFlags flags = 0; + + if(SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) + { + // Check we're REACHABLE + if(flags & kSCNetworkReachabilityFlagsReachable) + { + // Now, check we're on WWAN + if(flags & kSCNetworkReachabilityFlagsIsWWAN) + { + return YES; + } + } + } +#endif + + return NO; +} + +-(BOOL)isReachableViaWiFi +{ + SCNetworkReachabilityFlags flags = 0; + + if(SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) + { + // Check we're reachable + if((flags & kSCNetworkReachabilityFlagsReachable)) + { +#if TARGET_OS_IPHONE + // Check we're NOT on WWAN + if((flags & kSCNetworkReachabilityFlagsIsWWAN)) + { + return NO; + } +#endif + return YES; + } + } + + return NO; +} + + +// WWAN may be available, but not active until a connection has been established. +// WiFi may require a connection for VPN on Demand. +-(BOOL)isConnectionRequired +{ + return [self connectionRequired]; +} + +-(BOOL)connectionRequired +{ + SCNetworkReachabilityFlags flags; + + if(SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) + { + return (flags & kSCNetworkReachabilityFlagsConnectionRequired); + } + + return NO; +} + +// Dynamic, on demand connection? +-(BOOL)isConnectionOnDemand +{ + SCNetworkReachabilityFlags flags; + + if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) + { + return ((flags & kSCNetworkReachabilityFlagsConnectionRequired) && + (flags & (kSCNetworkReachabilityFlagsConnectionOnTraffic | kSCNetworkReachabilityFlagsConnectionOnDemand))); + } + + return NO; +} + +// Is user intervention required? +-(BOOL)isInterventionRequired +{ + SCNetworkReachabilityFlags flags; + + if (SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) + { + return ((flags & kSCNetworkReachabilityFlagsConnectionRequired) && + (flags & kSCNetworkReachabilityFlagsInterventionRequired)); + } + + return NO; +} + + +#pragma mark - reachability status stuff + +-(NetworkStatus)currentReachabilityStatus +{ + if([self isReachable]) + { + if([self isReachableViaWiFi]) + return ReachableViaWiFi; + +#if TARGET_OS_IPHONE + return ReachableViaWWAN; +#endif + } + + return NotReachable; +} + +-(SCNetworkReachabilityFlags)reachabilityFlags +{ + SCNetworkReachabilityFlags flags = 0; + + if(SCNetworkReachabilityGetFlags(reachabilityRef, &flags)) + { + return flags; + } + + return 0; +} + +-(NSString*)currentReachabilityString +{ + NetworkStatus temp = [self currentReachabilityStatus]; + + if(temp == reachableOnWWAN) + { + // Updated for the fact that we have CDMA phones now! + return NSLocalizedString(@"Cellular", @""); + } + if (temp == ReachableViaWiFi) + { + return NSLocalizedString(@"WiFi", @""); + } + + return NSLocalizedString(@"No Connection", @""); +} + +-(NSString*)currentReachabilityFlags +{ + return reachabilityFlags([self reachabilityFlags]); +} + +#pragma mark - Callback function calls this method + +-(void)reachabilityChanged:(SCNetworkReachabilityFlags)flags +{ + if([self isReachableWithFlags:flags]) + { + if(self.reachableBlock) + { + self.reachableBlock(self); + } + } + else + { + if(self.unreachableBlock) + { + self.unreachableBlock(self); + } + } + + // this makes sure the change notification happens on the MAIN THREAD + dispatch_async(dispatch_get_main_queue(), ^{ + [[NSNotificationCenter defaultCenter] postNotificationName:kYYReachabilityChangedNotification + object:self]; + }); +} + +#pragma mark - Debug Description + +- (NSString *) description +{ + NSString *description = [NSString stringWithFormat:@"<%@: %#x>", + NSStringFromClass([self class]), (unsigned int) self]; + return description; +} + +@end diff --git a/xplan-ios/Base/Tool/YYUtility/CarrierIdentifier.h b/xplan-ios/Base/Tool/YYUtility/CarrierIdentifier.h new file mode 100644 index 00000000..94c5bdb2 --- /dev/null +++ b/xplan-ios/Base/Tool/YYUtility/CarrierIdentifier.h @@ -0,0 +1,21 @@ +// +// CarrierIdentifier.h +// YYMobileFramework +// +// Created by wuwei on 14-5-30. +// Copyright (c) 2014年 YY Inc. All rights reserved. +// + +#import + +// 运营商类型 +typedef NS_ENUM(NSUInteger, CarrierIdentifier) +{ + CarrierIdentifier_Unknown = 0, // 未知, 网络不可用(未插SIM卡/无信号/飞行模式) + + CarrierIdentifier_ChinaMobile = 1, // 中国移动 + CarrierIdentifier_ChinaUnicom = 2, // 中国联通 + CarrierIdentifier_ChinaTelecom = 3, // 中国电信 + + CarrierIdentifier_Otherwise = 0x0000FFFF, // 其他运营商 +}; \ No newline at end of file diff --git a/xplan-ios/Base/Tool/YYUtility/YYUtility+App.m b/xplan-ios/Base/Tool/YYUtility/YYUtility+App.m new file mode 100644 index 00000000..2ccd39bc --- /dev/null +++ b/xplan-ios/Base/Tool/YYUtility/YYUtility+App.m @@ -0,0 +1,141 @@ +// +// YYUtility+App.m +// YYMobileFramework +// +// Created by wuwei on 14-5-30. +// Copyright (c) 2014年 YY Inc. All rights reserved. +// + +#import "YYUtility.h" + +@implementation YYUtility (App) + ++ (id)valueInPlistForKey:(NSString *)key{ + NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary]; + return [infoDictionary objectForKey:key]; +} + ++ (NSString *)appVersion{ + static NSString *appVersion = nil; + if (!appVersion) { + appVersion = [self valueInPlistForKey:@"CFBundleShortVersionString"]; + } + return appVersion; +} + ++ (NSString *)appName { + if (isEnterprise) { + return @"yinyouEnterprise"; // 企业包名字 区分一键登录使用 + }else { + return @"yinyou"; + } +} + ++ (NSString *)appBuild{ + static NSString *appBuild = nil; + if (!appBuild) { + appBuild = [self valueInPlistForKey:(NSString *)kCFBundleVersionKey]; + } + return appBuild; +} + ++ (NSString *)appBundleId{ + static NSString *appBundleId = nil; + if (!appBundleId) { + appBundleId = [self valueInPlistForKey:(NSString *)kCFBundleIdentifierKey]; + } + return appBundleId; +} + + ++ (NSString *)svnVersion{ + static NSString *svnVersion = nil; + if (!svnVersion) { + svnVersion = [self valueInPlistForKey:@"SvnBuildVersion"]; + } + return svnVersion; +} + +static NSString * const kMobileFrameworkResourceBundleName = @"YYMobileFrameworkRes"; + ++ (NSURL *)URLForMobileFrameworkResourceBundle{ + return [[NSBundle mainBundle] URLForResource:kMobileFrameworkResourceBundleName + withExtension:@"bundle"]; +} + ++ (NSString *)pathForMobileFrameworkResourceBundle{ + return [[NSBundle mainBundle] pathForResource:kMobileFrameworkResourceBundleName + ofType:@"bundle"]; +} + ++ (NSString *)buildType{ +#ifdef DEBUG + return @"DEBUG"; +#else + return @"RELEASE"; +#endif +} + ++ (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)fileURL{ + if (![[NSFileManager defaultManager] fileExistsAtPath:[fileURL path]]) { + NSLog(@"File %@ dosen't exist!", fileURL); + return NO; + } + + NSError *error = nil; + BOOL result = [fileURL setResourceValue:[NSNumber numberWithBool:YES] + forKey:NSURLIsExcludedFromBackupKey + error:&error]; + if (!result) { + NSLog(@"Error excluding '%@' from backup, error: %@.", fileURL, error); + } + + return result; +} + +static NSString *_from = nil; + ++ (NSString *)getAppSource{ + if (_from == nil) { + NSString *path = [[NSBundle mainBundle] pathForResource:@"sourceid.dat" ofType:nil]; + + NSError *error; + NSString *from = [NSString stringWithContentsOfFile:path encoding:NSUTF8StringEncoding error:&error]; + + // 去除换行及空格 + if (from) { + from = [self removeSpaceAndNewline:from]; + } + // 如果读到有值且不为0 + if (!error + && ![from isEqualToString:@"0"] + && ![from isEqualToString:@""]) { + _from = from; + } else { + if (isEnterprise) { + _from = @"yinyou_Enterprise"; // 企业包 + }else { + _from = @"yinyou_appstore"; // App Store包 + } + } + } + + return _from; +} + ++ (BOOL)isFromAppStore{ + if([[[YYUtility getAppSource] lowercaseString] isEqualToString:@"appstore"]){ + return YES; + } + return NO; +} + + ++ (NSString *)removeSpaceAndNewline:(NSString *)str{ + NSString *temp = [str stringByReplacingOccurrencesOfString:@" " withString:@""]; + temp = [temp stringByReplacingOccurrencesOfString:@"\r" withString:@""]; + temp = [temp stringByReplacingOccurrencesOfString:@"\n" withString:@""]; + return temp; +} + +@end diff --git a/xplan-ios/Base/Tool/YYUtility/YYUtility+Carrier.m b/xplan-ios/Base/Tool/YYUtility/YYUtility+Carrier.m new file mode 100644 index 00000000..7e4f1c71 --- /dev/null +++ b/xplan-ios/Base/Tool/YYUtility/YYUtility+Carrier.m @@ -0,0 +1,88 @@ +// +// YYUtility+Carrier.m +// YYFoundation +// +// Created by wuwei on 14-5-30. +// Copyright (c) 2014年 YY Inc. All rights reserved. +// + +#import "YYUtility.h" +#import +#import +#import "CarrierIdentifier.h" + +/* + * The most update-to-date list of MNC&MCC is fetched + * from the website below: http://www.mcc-mnc.com + */ +// MCCs +static NSString * const kMobileCountryCode_China = @"460"; // 中国 + +// MNCs +static NSSet * kMobileNetworkCodes_ChinaMobile; // 移动 +static NSSet * kMobileNetworkCodes_ChinaUnicom; // 联通 +static NSSet * kMobileNetworkCodes_ChinaTelecom; // 电信 + +@implementation YYUtility (Carrier) + ++ (void)load +{ + if (self == [YYUtility self]) { + kMobileNetworkCodes_ChinaMobile = [NSSet setWithObjects:@"00", @"02", @"07", nil]; // 中国移动 + kMobileNetworkCodes_ChinaUnicom = [NSSet setWithObjects:@"01", @"06", nil]; // 中国联通 + kMobileNetworkCodes_ChinaTelecom = [NSSet setWithObjects:@"03", @"05", nil]; // 中国电信 + } +} + ++ (NSString *)carrierName +{ + return [self carrier].carrierName; +} + ++ (NSInteger)carrierIdentifier +{ + return [self identifierOfCarrier:[self carrier]]; +} + ++ (CTCarrier *)carrier +{ + return [[CTTelephonyNetworkInfo alloc] init].subscriberCellularProvider; +} + ++ (NSInteger)identifierOfCarrier:(CTCarrier *)carrier +{ + CarrierIdentifier identifier = CarrierIdentifier_Unknown; + do { + if (carrier.mobileCountryCode == nil || carrier.mobileNetworkCode == nil) + { + identifier = CarrierIdentifier_Unknown; + break; + } + + if ([carrier.mobileCountryCode isEqualToString:kMobileCountryCode_China]) + { + if ([kMobileNetworkCodes_ChinaMobile containsObject:carrier.mobileNetworkCode]) + { + identifier = CarrierIdentifier_ChinaMobile; + break; + } + else if ([kMobileNetworkCodes_ChinaUnicom containsObject:carrier.mobileNetworkCode]) + { + identifier = CarrierIdentifier_ChinaUnicom; + break; + } + else if ([kMobileNetworkCodes_ChinaTelecom containsObject:carrier.mobileNetworkCode]) + { + identifier = CarrierIdentifier_ChinaTelecom; + break; + } + } + + identifier = CarrierIdentifier_Otherwise; + + } while (0); + + return identifier; +} + +@end diff --git a/xplan-ios/Base/Tool/YYUtility/YYUtility+Device.m b/xplan-ios/Base/Tool/YYUtility/YYUtility+Device.m new file mode 100644 index 00000000..c94c2bae --- /dev/null +++ b/xplan-ios/Base/Tool/YYUtility/YYUtility+Device.m @@ -0,0 +1,538 @@ +// +// YYUtility+Device.m +// YYMobileFramework +// +// Created by wuwei on 14-5-30. +// Copyright (c) 2014年 YY Inc. All rights reserved. +// + +#import +#import +#import + +#import "YYUtility.h" +#import +#import +#import +#import +#import "YYReachability.h" +#import + +#import +#import +#import +#import "SSKeychain.h" +#import "GCDHelper.h" + + +#if !defined(IFT_ETHER) +#define IFT_ETHER 0x6 +#endif + +#define kIOSCellular @"pdp_ip0" +#define kIOSWifi @"en0" +#define kIPAddrV4 @"ipv4" +#define kIPAddrV6 @"ipv6" +#define kAppName @"yym51ip" + +@implementation YYUtility (Device) + ++ (NSString *)modelName +{ + static NSString *modelName = nil; + if (!modelName) { + struct utsname systemInfo; + uname(&systemInfo); + modelName = [NSString stringWithCString:systemInfo.machine encoding:NSUTF8StringEncoding]; + } + return modelName; +} + ++ (NSString *)systemVersion +{ + // 调用非常频繁,主要在cleanSpecialText中 + static NSString* _systemVersion = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _systemVersion = [UIDevice currentDevice].systemVersion;; + }); + + if (_systemVersion) { + return _systemVersion; + } + + return [UIDevice currentDevice].systemVersion; +} + ++ (NSString *)identifierForVendor +{ + static NSString *idfv = nil; + if (!idfv) { + idfv = [[[UIDevice currentDevice] identifierForVendor] UUIDString]; + } + return idfv; +} + ++ (NSString *)deviceID +{ + NSString *deviceID = @""; + if (!deviceID || deviceID.length == 0) { + deviceID = [YYUtility identifierForVendor]; + } + + if (!deviceID) { + deviceID = @""; + } + + return deviceID; +} + ++ (NSInteger)networkStatus +{ + return (self.reachability.currentReachabilityStatus); +} + ++ (YYReachability *)reachability +{ + static YYReachability *reachability = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + reachability = [YYReachability reachabilityForInternetConnection]; + }); + return reachability; +} + + ++ (void)checkCameraAvailable:(void (^)(void))available denied:(void(^)(void))denied restriction:(void(^)(void))restriction +{ + available = available ? : ^{}; + denied = denied ? : ^{}; + restriction = restriction ? : ^{}; + + if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) + { + // iOS7下,需要检查iPhone的隐私和访问限制项 + AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo]; + switch (authStatus) { + case AVAuthorizationStatusAuthorized: + { + available(); + break; + } + case AVAuthorizationStatusDenied: + { + // [设置->隐私->相机]中禁止了YY访问相机 + denied(); + break; + } + case AVAuthorizationStatusRestricted: + { + // NOTE: 这个跟[设置-通用-访问限制]似乎没有关系 + restriction(); + break; + } + case AVAuthorizationStatusNotDetermined: + { + [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) { + + dispatch_main_sync_safe(^{ + if (granted) + { + available(); + } + else + { + denied(); + } + + }); + + }]; + } + + default: + break; + } + } + else + { + restriction(); + } +} + ++ (void)checkAssetsLibrayAvailable:(void (^)(void))available denied:(void(^)(void))denied restriction:(void(^)(void))restriction +{ + available = available ? : ^{}; + denied = denied ? : ^{}; + restriction = restriction ? : ^{}; + + if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypePhotoLibrary] || + [UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeSavedPhotosAlbum]) { + ALAuthorizationStatus author = [ALAssetsLibrary authorizationStatus]; + if (author == ALAuthorizationStatusNotDetermined) { + ALAssetsLibrary *assetLibrary = [[ALAssetsLibrary alloc] init]; + [assetLibrary enumerateGroupsWithTypes:ALAssetsGroupAll usingBlock:^(ALAssetsGroup *group, BOOL *stop) { + if (*stop) { + available(); + return; + } + *stop = TRUE; + } failureBlock:^(NSError *error) { + denied(); + }]; + }else if (author == ALAuthorizationStatusDenied) { + denied(); + }else if(author == ALAuthorizationStatusRestricted) { + restriction(); + }else if(author == ALAuthorizationStatusAuthorized) { + available(); + } + }else { + restriction(); + } +} + ++ (void)checkMicPrivacy:(void(^)(BOOL succeed))resultBlock +{ + [[AVAudioSession sharedInstance] requestRecordPermission:^(BOOL granted) { + if (resultBlock) { + dispatch_async(dispatch_get_main_queue(), ^{ + resultBlock(granted); + }); + } + }]; +} + ++ (NSString *)deviceUniqueIdentification { + // SSKeychain + NSString * currentDeviceUUIDStr = [SSKeychain passwordForService:@" " account:@"uuid"]; + if (currentDeviceUUIDStr == nil || [currentDeviceUUIDStr isEqualToString:@""]) + { + NSUUID * currentDeviceUUID = [UIDevice currentDevice].identifierForVendor; + currentDeviceUUIDStr = currentDeviceUUID.UUIDString; + currentDeviceUUIDStr = [currentDeviceUUIDStr stringByReplacingOccurrencesOfString:@"-" withString:@""]; + currentDeviceUUIDStr = [currentDeviceUUIDStr lowercaseString]; + [SSKeychain setPassword: currentDeviceUUIDStr forService:@" "account:@"uuid"]; + } + return currentDeviceUUIDStr; +} + ++ (NSString *)ipAddress +{ + return [self ipAddress:YES]; +} + ++ (NSString *)ipAddress:(BOOL)preferIPv4 +{ + NSArray *searchArray = preferIPv4 ? + @[ kIOSWifi @"/" kIPAddrV4, kIOSWifi @"/" kIPAddrV6, kIOSCellular @"/" kIPAddrV4, kIOSCellular @"/" kIPAddrV6 ] : + @[ kIOSWifi @"/" kIPAddrV6, kIOSWifi @"/" kIPAddrV4, kIOSCellular @"/" kIPAddrV6, kIOSCellular @"/" kIPAddrV4 ] ; + + NSDictionary *addresses = [self getIpAddresses]; + + __block NSString *addr; + [searchArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { + addr = addresses[obj]; + if (addr) { + *stop = YES; + } + }]; + return addr ? : @"0.0.0.0"; +} + ++ (NSDictionary *)getIpAddresses +{ + NSMutableDictionary *addresses = [NSMutableDictionary dictionary]; + + // retrieve the current interfaces - return 0 on success + struct ifaddrs *interfaces; + if (!getifaddrs(&interfaces)) { + // Loop through linked list of interfaces + struct ifaddrs *interface; + for (interface = interfaces; interface; interface = interface->ifa_next) { + if (!(interface -> ifa_flags & IFF_UP)) { + continue; + } + const struct sockaddr_in *addr = (const struct sockaddr_in *)interface->ifa_addr; + char addrBuf[MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) + 2]; + if (addr && (addr->sin_family == AF_INET || addr->sin_family == AF_INET6)) { + NSString *ifaName = [NSString stringWithUTF8String:interface->ifa_name]; + NSString *ifaType; + if (addr->sin_family == AF_INET) { + if (inet_ntop(AF_INET, &addr->sin_addr, addrBuf, INET_ADDRSTRLEN)) { + ifaType = kIPAddrV4; + } + } else { + const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6 *)interface->ifa_addr; + if (inet_ntop(AF_INET6, &addr6->sin6_addr, addrBuf, INET6_ADDRSTRLEN)) { + ifaType = kIPAddrV6; + } + } + if (ifaType) { + NSString *key = [NSString stringWithFormat:@"%@/%@", ifaName, ifaType]; + addresses[key] = [NSString stringWithUTF8String:addrBuf]; + } + } + } + } + // free memory + freeifaddrs(interfaces); + return addresses; +} + ++ (NSString *)macAddresss +{ + static NSMutableString *macAddress = nil; + + if ([macAddress length] > 0) { + return macAddress; + } + + do + { + struct ifaddrs* addrs; + if ( getifaddrs( &addrs ) ) + break; + + const struct ifaddrs *cursor = addrs; + while ( cursor ) + { + if ( ( cursor->ifa_addr->sa_family == AF_LINK ) + && strcmp( "en0", cursor->ifa_name ) == 0 + && ( ( ( const struct sockaddr_dl * )cursor->ifa_addr)->sdl_type == IFT_ETHER ) ) + { + const struct sockaddr_dl *dlAddr = ( const struct sockaddr_dl * )cursor->ifa_addr; + const uint8_t *base = ( const uint8_t * )&dlAddr->sdl_data[dlAddr->sdl_nlen]; + + macAddress = [[NSMutableString alloc] initWithCapacity:64]; + + for ( int i = 0; i < dlAddr->sdl_alen; i++ ) + { + if (i > 0) { + [macAddress appendFormat:@":%02X", base[i]]; + } + else + { + [macAddress appendFormat:@"%02X", base[i]]; + } + } + + break; + } + cursor = cursor->ifa_next; + } + freeifaddrs(addrs); + } while (NO); + + if (macAddress == nil) { + macAddress = [NSMutableString stringWithString:@""]; + } + + return macAddress; +} + ++ (NSString *)idfa +{ + static NSString *idfa = nil; + if (!idfa) { + if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 6.0) { + idfa = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString]; + } else { + idfa = @""; + } + } + return idfa; +} + + ++ (NSString*)modelType { + + struct utsname systemInfo; + + uname(&systemInfo); + + NSString*platform = [NSString stringWithCString: systemInfo.machine encoding:NSASCIIStringEncoding]; + + if([platform isEqualToString:@"iPhone1,1"])return@"iPhone 2G"; + + if([platform isEqualToString:@"iPhone1,2"])return@"iPhone 3G"; + + if([platform isEqualToString:@"iPhone2,1"])return@"iPhone 3GS"; + + if([platform isEqualToString:@"iPhone3,1"])return@"iPhone 4"; + + if([platform isEqualToString:@"iPhone3,2"])return@"iPhone 4"; + + if([platform isEqualToString:@"iPhone3,3"])return@"iPhone 4"; + + if([platform isEqualToString:@"iPhone4,1"])return@"iPhone 4S"; + + if([platform isEqualToString:@"iPhone5,1"])return@"iPhone 5"; + + if([platform isEqualToString:@"iPhone5,2"])return@"iPhone 5"; + + if([platform isEqualToString:@"iPhone5,3"])return@"iPhone 5c"; + + if([platform isEqualToString:@"iPhone5,4"])return@"iPhone 5c"; + + if([platform isEqualToString:@"iPhone6,1"])return@"iPhone 5s"; + + if([platform isEqualToString:@"iPhone6,2"])return@"iPhone 5s"; + + if([platform isEqualToString:@"iPhone7,1"])return@"iPhone 6 Plus"; + + if([platform isEqualToString:@"iPhone7,2"])return@"iPhone 6"; + + if([platform isEqualToString:@"iPhone8,1"])return@"iPhone 6s"; + + if([platform isEqualToString:@"iPhone8,2"])return@"iPhone 6s Plus"; + + if([platform isEqualToString:@"iPhone8,4"])return@"iPhone SE"; + + if([platform isEqualToString:@"iPhone9,1"])return@"iPhone 7"; + + if([platform isEqualToString:@"iPhone9,2"])return@"iPhone 7 Plus"; + + if([platform isEqualToString:@"iPhone10,1"])return@"iPhone 8"; + + if([platform isEqualToString:@"iPhone10,4"])return@"iPhone 8"; + + if([platform isEqualToString:@"iPhone10,2"])return@"iPhone 8 Plus"; + + if([platform isEqualToString:@"iPhone10,5"])return@"iPhone 8 Plus"; + + if([platform isEqualToString:@"iPhone10,3"])return@"iPhone X"; + + if([platform isEqualToString:@"iPhone10,6"])return@"iPhone X"; + + if([platform isEqualToString:@"iPod1,1"])return@"iPod Touch 1G"; + + if([platform isEqualToString:@"iPod2,1"])return@"iPod Touch 2G"; + + if([platform isEqualToString:@"iPod3,1"])return@"iPod Touch 3G"; + + if([platform isEqualToString:@"iPod4,1"])return@"iPod Touch 4G"; + + if([platform isEqualToString:@"iPod5,1"])return@"iPod Touch 5G"; + + if([platform isEqualToString:@"iPad1,1"])return@"iPad 1G"; + + if([platform isEqualToString:@"iPad2,1"])return@"iPad 2"; + + if([platform isEqualToString:@"iPad2,2"])return@"iPad 2"; + + if([platform isEqualToString:@"iPad2,3"])return@"iPad 2"; + + if([platform isEqualToString:@"iPad2,4"])return@"iPad 2"; + + if([platform isEqualToString:@"iPad2,5"])return@"iPad Mini 1G"; + + if([platform isEqualToString:@"iPad2,6"])return@"iPad Mini 1G"; + + if([platform isEqualToString:@"iPad2,7"])return@"iPad Mini 1G"; + + if([platform isEqualToString:@"iPad3,1"])return@"iPad 3"; + + if([platform isEqualToString:@"iPad3,2"])return@"iPad 3"; + + if([platform isEqualToString:@"iPad3,3"])return@"iPad 3"; + + if([platform isEqualToString:@"iPad3,4"])return@"iPad 4"; + + if([platform isEqualToString:@"iPad3,5"])return@"iPad 4"; + + if([platform isEqualToString:@"iPad3,6"])return@"iPad 4"; + + if([platform isEqualToString:@"iPad4,1"])return@"iPad Air"; + + if([platform isEqualToString:@"iPad4,2"])return@"iPad Air"; + + if([platform isEqualToString:@"iPad4,3"])return@"iPad Air"; + + if([platform isEqualToString:@"iPad4,4"])return@"iPad Mini 2G"; + + if([platform isEqualToString:@"iPad4,5"])return@"iPad Mini 2G"; + + if([platform isEqualToString:@"iPad4,6"])return@"iPad Mini 2G"; + + if([platform isEqualToString:@"iPad4,7"])return@"iPad Mini 3"; + + if([platform isEqualToString:@"iPad4,8"])return@"iPad Mini 3"; + + if([platform isEqualToString:@"iPad4,9"])return@"iPad Mini 3"; + + if([platform isEqualToString:@"iPad5,1"])return@"iPad Mini 4"; + + if([platform isEqualToString:@"iPad5,2"])return@"iPad Mini 4"; + + if([platform isEqualToString:@"iPad5,3"])return@"iPad Air 2"; + + if([platform isEqualToString:@"iPad5,4"])return@"iPad Air 2"; + + if([platform isEqualToString:@"iPad6,3"])return@"iPad Pro 9.7"; + + if([platform isEqualToString:@"iPad6,4"])return@"iPad Pro 9.7"; + + if([platform isEqualToString:@"iPad6,7"])return@"iPad Pro 12.9"; + + if([platform isEqualToString:@"iPad6,8"])return@"iPad Pro 12.9"; + + if([platform isEqualToString:@"i386"])return@"iPhone Simulator"; + + if([platform isEqualToString:@"x86_64"])return@"iPhone Simulator"; + + return platform; + +} + +// 当前设备是否低于, 等于 iPhone6 ++ (BOOL)isIphone6AndLow { + struct utsname systemInfo; + + uname(&systemInfo); + + NSString*platform = [NSString stringWithCString: systemInfo.machine encoding:NSASCIIStringEncoding]; + if([platform isEqualToString:@"iPhone1,1"])return YES; + + if([platform isEqualToString:@"iPhone1,2"])return YES; + + if([platform isEqualToString:@"iPhone2,1"])return YES; + + if([platform isEqualToString:@"iPhone3,1"])return YES; + + if([platform isEqualToString:@"iPhone3,2"])return YES; + + if([platform isEqualToString:@"iPhone3,3"])return YES; + + if([platform isEqualToString:@"iPhone4,1"])return YES; + + if([platform isEqualToString:@"iPhone5,1"])return YES; + + if([platform isEqualToString:@"iPhone5,2"])return YES; + + if([platform isEqualToString:@"iPhone5,3"])return YES; + + if([platform isEqualToString:@"iPhone5,4"])return YES; + + if([platform isEqualToString:@"iPhone6,1"])return YES; + + if([platform isEqualToString:@"iPhone6,2"])return YES; + + if([platform isEqualToString:@"iPhone7,1"])return YES; + + if([platform isEqualToString:@"iPhone7,2"])return YES; + + if ([platform isEqualToString:@"iPhone8,1"]) return YES; + + if ([platform isEqualToString:@"iPhone8,3"]) return YES; + + if ([platform isEqualToString:@"iPhone8,4"]) return YES; + + if ([platform isEqualToString:@"iPhone9,1"]) return YES; + + if ([platform isEqualToString:@"iPhone9,3"]) return YES; + + return NO; +} + +@end diff --git a/xplan-ios/Base/Tool/YYUtility/YYUtility.h b/xplan-ios/Base/Tool/YYUtility/YYUtility.h new file mode 100644 index 00000000..2ae4cb10 --- /dev/null +++ b/xplan-ios/Base/Tool/YYUtility/YYUtility.h @@ -0,0 +1,225 @@ +// +// YYUtility.h +// YYMobileFramework +// +// Created by wuwei on 14/6/11. +// Copyright (c) 2014年 YY.inc. All rights reserved. +// + +#import + +@class CTCarrier; + +@interface YYUtility : NSObject + +@end + +/*==============================*/ +/* App Utilities */ +/*==============================*/ +@interface YYUtility (App) + +/** + * 从YYMobile-Info.plist中读取字段 + * + * @param key 键 + * + * @return 值 + */ ++ (id)valueInPlistForKey:(NSString *)key; + +/** + * 获取App版本号, 从plist从读取CFBundleShortVersion + */ ++ (NSString *)appVersion; + +/** + * 获取AppBuild号, 从plist中读取CFBundleVersion + */ ++ (NSString *)appBuild; + +/** + * 当前构建出的版本在svn中的版本号 + */ ++ (NSString *)svnVersion; + +/** + 获取appName + + @return app的名称 + */ ++ (NSString *)appName; + +/** + * 获取bundle id + * + * @return bundle id + */ ++ (NSString *)appBundleId; + +/** + * 获取YYMobileFrameworkRes.bundle的URL + */ ++ (NSURL *)URLForMobileFrameworkResourceBundle; + +/** + * 获取YYMobileFrameworkRes.bundle的路径 + */ ++ (NSString *)pathForMobileFrameworkResourceBundle; + ++ (BOOL)addSkipBackupAttributeToItemAtURL:(NSURL *)fileURL; + +/** + * 获取当前的构建类型(DEBUG/RELEASE) + * + * @return 构建类型 + */ ++ (NSString *)buildType; + +/** + * 获取平台渠道 + */ ++ (NSString *)getAppSource; + +/** + * 是否来自appstore + */ ++ (BOOL)isFromAppStore; + +@end + +/*==============================*/ +/* Carrier Utilities */ +/*==============================*/ +@interface YYUtility (Carrier) + + +/** + 获取设备唯一标识 + + @return 唯一标识 + */ ++ (NSString *)deviceUniqueIdentification; + +/** + * 获取运营商 + */ ++ (CTCarrier *)carrier; + +/** + * 获取运营商类型 + */ ++ (NSInteger)carrierIdentifier; + +/** + * 获取运营商名称 + */ ++ (NSString *)carrierName; + +/** + * 从CTCarrier对象获取网络类型 + * @param carrier - CTCarrier对象 + * @return CarrierType + */ ++ (NSInteger)identifierOfCarrier:(CTCarrier *)carrier; + +@end + +/*==============================*/ +/* Device Utilities */ +/*==============================*/ +@interface YYUtility (Device) + +/** + * 获取modelName, 如iPhone5,2 + */ ++ (NSString *)modelName; + + +/** + 获取设备类型 + + @return 设备类型 + */ ++ (NSString*)modelType; + +/** + * 获取系统版本 + */ ++ (NSString *)systemVersion; + +/** + * 获取当前设备的 IDFV,IDFV 在某些情况下会变,不建议将其作为设备标识 + */ ++ (NSString *)identifierForVendor NS_AVAILABLE_IOS(6_0); + +/** + * 获取当前的设备标识,会使用海度 SDK 中的 OpenUDID + * + * @return 海度 SDK 缓存的 OpenUDID + */ ++ (NSString *)deviceID; + +/** + * 获取当前网络状态 + */ ++ (NSInteger)networkStatus; + +/** + * 获取当前IP地址 + */ ++ (NSString *)ipAddress; + +/** + * 获取当前IP地址 + * + * @param preferIPv4 优先取IPv4的地址 + */ ++ (NSString *)ipAddress:(BOOL)preferIPv4; + +/** + * 检查Camera是否可用, 可用则调用available; 若隐私设置中禁用了本app对相机 + * 的访问, 则调用denied; 否则视为被限制, 调用restriction + * + * @param available 可用 + * @param denied 不可用 + * @param restriction 受限制 + */ ++ (void)checkCameraAvailable:(void (^)(void))available denied:(void(^)(void))denied restriction:(void(^)(void))restriction; + +/** + * 检查相册是否可用, 可用则调用available; 若隐私设置中禁用了本app对相册 + * 的访问, 则调用denied; 否则视为被限制, 调用restriction + * + * @param available 可用 + * @param denied 不可用 + * @param restriction 受限制 + */ ++ (void)checkAssetsLibrayAvailable:(void (^)(void))available denied:(void(^)(void))denied restriction:(void(^)(void))restriction; + +/** + 检查麦克风权限 + + @param resultBlock 结果处理block + */ ++ (void)checkMicPrivacy:(void(^)(BOOL succeed))resultBlock; + ++ (NSString *)macAddresss; ++ (NSString *)idfa; + +/** + * 初始化信令sdk,imsdk所用到的appName + * + * @return app name + */ ++ (NSString *)appName; + + +/** + 当前设备是否低于, 等于 iPhone6 + + @return 当前设备是否低于, 等于 iPhone6 + */ ++ (BOOL)isIphone6AndLow; + +@end diff --git a/xplan-ios/Base/Tool/YYUtility/YYUtility.m b/xplan-ios/Base/Tool/YYUtility/YYUtility.m new file mode 100644 index 00000000..e95021f0 --- /dev/null +++ b/xplan-ios/Base/Tool/YYUtility/YYUtility.m @@ -0,0 +1,13 @@ +// +// YYUtility.m +// YYMobileFramework +// +// Created by wuwei on 14-5-30. +// Copyright (c) 2014年 YY Inc. All rights reserved. +// + +#import "YYUtility.h" + +@implementation YYUtility + +@end diff --git a/xplan-ios/Base/UI/BaseViewController.h b/xplan-ios/Base/UI/BaseViewController.h new file mode 100644 index 00000000..d845c7fd --- /dev/null +++ b/xplan-ios/Base/UI/BaseViewController.h @@ -0,0 +1,38 @@ +// +// BaseViewController.h +// xplan-ios +// +// Created by zu on 2021/8/31. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface BaseViewController : UIViewController + +/** + 显示/隐藏导航 + */ +- (void)hideNavigationBar; + +/** + 隐藏导航栏 + */ +- (void)showNavigationBar; + + +/** + 显示/隐藏状态栏 + */ +- (void)showStatusBar; + + +/** + 隐藏状态栏 + */ +- (void)hideStatusBar; + +@end + +NS_ASSUME_NONNULL_END diff --git a/xplan-ios/Base/UI/BaseViewController.m b/xplan-ios/Base/UI/BaseViewController.m new file mode 100644 index 00000000..646da5b7 --- /dev/null +++ b/xplan-ios/Base/UI/BaseViewController.m @@ -0,0 +1,70 @@ +// +// BaseViewController.m +// xplan-ios +// +// Created by zu on 2021/8/31. +// + +#import "BaseViewController.h" + +@interface BaseViewController () + +@end + +@implementation BaseViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + [self themeConfig]; +} + +- (void)themeConfig { + self.view.backgroundColor = ThemeBackgroundColor; + + [UIApplication sharedApplication].statusBarStyle = UIStatusBarStyleLightContent; + self.navigationController.navigationBar.shadowImage = [[UIImage alloc] init]; + self.navigationController.navigationBar.barTintColor = ThemeBackgroundColor; + self.navigationController.navigationBar.tintColor = [UIColor whiteColor]; + self.navigationController.navigationBar.translucent = NO; + + UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithTitle:@"" style:UIBarButtonItemStylePlain target:nil action:nil]; + self.navigationItem.backBarButtonItem = item; + + [self.navigationController.navigationBar setTitleTextAttributes:@{ + NSFontAttributeName:[UIFont systemFontOfSize:18], + NSForegroundColorAttributeName:UIColor.whiteColor + }]; + self.navigationItem.title = @"音游"; +} + +/** + 隐藏导航条 + */ +- (void)hideNavigationBar { + [self.navigationController setNavigationBarHidden:YES]; +} + + +/** + 显示导航条 + */ +- (void)showNavigationBar { + [self.navigationController setNavigationBarHidden:NO]; +} + +/** + 显示状态栏 + */ +- (void)showStatusBar { + [UIApplication sharedApplication].statusBarHidden = NO; +} + +/** + 隐藏状态栏 + */ +- (void)hideStatusBar { + [UIApplication sharedApplication].statusBarHidden = YES; +} + + +@end diff --git a/xplan-ios/Base/UI/UIImage+Utils.h b/xplan-ios/Base/UI/UIImage+Utils.h new file mode 100644 index 00000000..54928b16 --- /dev/null +++ b/xplan-ios/Base/UI/UIImage+Utils.h @@ -0,0 +1,43 @@ +// +// UIImage+Utils.h +// YYMobileFramework +// +// Created by wuwei on 14/6/20. +// Copyright (c) 2014年 YY Inc. All rights reserved. +// + +#import + +typedef NS_ENUM(NSUInteger, GradientType) { + GradientTypeTopToBottom = 0,//从上到小 + GradientTypeLeftToRight = 1,//从左到右 + GradientTypeUpleftToLowright = 2,//左上到右下 + GradientTypeUprightToLowleft = 3,//右上到左下 +}; + +@interface UIImage (Utils) + +- (UIImage *)grayscaleImage; + +- (UIImage *)imageBlendInGray; + +- (UIImage *)imageWithBlendMode:(CGBlendMode)blendMode; + ++ (UIImage *)imageWithColor:(UIColor *)color; + ++ (UIImage *)imageWithColor:(UIColor *)color size:(CGSize)size; + ++ (UIImage *)fixOrientation:(UIImage *)aImage; + +- (UIImage *)imageWithColor:(UIColor *)color; + +- (UIImage *)setCornerWithRadius:(CGFloat)radius andSize:(CGSize)size; + + +//异步生成纯色圆角图片 +- (void)imageWithSize:(CGSize)size radius:(CGFloat)radius backColor:(UIColor *)backColor completion:(void(^)(UIImage *image))completion; +/** + 返回指定大小,颜色,渐变模式的渐变色图片 + */ ++ (UIImage *)gradientColorImageFromColors:(NSArray*)colors gradientType:(GradientType)gradientType imgSize:(CGSize)imgSize; +@end diff --git a/xplan-ios/Base/UI/UIImage+Utils.m b/xplan-ios/Base/UI/UIImage+Utils.m new file mode 100644 index 00000000..425526da --- /dev/null +++ b/xplan-ios/Base/UI/UIImage+Utils.m @@ -0,0 +1,301 @@ +// +// UIImage+Utils.m +// YYMobileFramework +// +// Created by wuwei on 14/6/20. +// Copyright (c) 2014年 YY Inc. All rights reserved. +// + +#import "UIImage+Utils.h" +#import + +@implementation UIImage (Utils) +- (UIImage *)grayscaleImage +{ + CGFloat width = self.size.width; + CGFloat height = self.size.height; + + CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray(); + + CGContextRef context = CGBitmapContextCreate(nil, + width, + height, + 8, + 0, + colorSpace, + kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedLast); + CGColorSpaceRelease(colorSpace); + + if (context == NULL) { + return nil; + } + + CGContextDrawImage(context, CGRectMake(0, 0, width, height), self.CGImage); + CGImageRef imageRef = CGBitmapContextCreateImage(context); + UIImage *grayscaleImage = [UIImage imageWithCGImage:imageRef]; + CGImageRelease(imageRef); + CGContextRelease(context); + + return grayscaleImage; +} + + +- (UIImage *)imageBlendInGray { + + UIGraphicsBeginImageContext(self.size); + CGRect bounds = CGRectMake(0, 0, self.size.width, self.size.height); + CGContextRef context = UIGraphicsGetCurrentContext(); + CGContextFillRect(context, bounds); + [self drawInRect:bounds blendMode:kCGBlendModeLuminosity alpha:1.0f]; + [self drawInRect:bounds blendMode:kCGBlendModeDestinationIn alpha:1.0f]; + UIImage *newImg = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + return newImg; +} + + + +- (UIImage *)imageWithBlendMode:(CGBlendMode)blendMode { + + UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0f); + CGRect bounds = CGRectMake(0, 0, self.size.width, self.size.height); + UIRectFill(bounds); + + [self drawInRect:bounds blendMode:blendMode alpha:1.0f]; + + if (blendMode != kCGBlendModeDestinationIn) { + [self drawInRect:bounds blendMode:kCGBlendModeDestinationIn alpha:1.0f]; + } + UIImage *newImg = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + return newImg; +} + ++ (UIImage *)imageWithColor:(UIColor *)color +{ + return [self imageWithColor:color size:CGSizeMake(1, 1)]; +} + ++ (UIImage *)imageWithColor:(UIColor *)color size:(CGSize)size { + if (!color || size.width <= 0 || size.height <= 0) return nil; + CGRect rect = CGRectMake(0.0f, 0.0f, size.width + 1, size.height); + UIGraphicsBeginImageContextWithOptions(rect.size, NO, 0); + CGContextRef context = UIGraphicsGetCurrentContext(); + CGContextSetFillColorWithColor(context, color.CGColor); + CGContextFillRect(context, rect); + UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + return image; +} + ++ (UIImage *)fixOrientation:(UIImage *)aImage { + + // No-op if the orientation is already correct + if (aImage.imageOrientation == UIImageOrientationUp) + return aImage; + + // We need to calculate the proper transformation to make the image upright. + // We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored. + CGAffineTransform transform = CGAffineTransformIdentity; + + switch (aImage.imageOrientation) { + case UIImageOrientationDown: + case UIImageOrientationDownMirrored: + transform = CGAffineTransformTranslate(transform, aImage.size.width, aImage.size.height); + transform = CGAffineTransformRotate(transform, M_PI); + break; + + case UIImageOrientationLeft: + case UIImageOrientationLeftMirrored: + transform = CGAffineTransformTranslate(transform, aImage.size.width, 0); + transform = CGAffineTransformRotate(transform, M_PI_2); + break; + + case UIImageOrientationRight: + case UIImageOrientationRightMirrored: + transform = CGAffineTransformTranslate(transform, 0, aImage.size.height); + transform = CGAffineTransformRotate(transform, -M_PI_2); + break; + default: + break; + } + + switch (aImage.imageOrientation) { + case UIImageOrientationUpMirrored: + case UIImageOrientationDownMirrored: + transform = CGAffineTransformTranslate(transform, aImage.size.width, 0); + transform = CGAffineTransformScale(transform, -1, 1); + break; + + case UIImageOrientationLeftMirrored: + case UIImageOrientationRightMirrored: + transform = CGAffineTransformTranslate(transform, aImage.size.height, 0); + transform = CGAffineTransformScale(transform, -1, 1); + break; + default: + break; + } + + // Now we draw the underlying CGImage into a new context, applying the transform + // calculated above. + CGContextRef ctx = CGBitmapContextCreate(NULL, aImage.size.width, aImage.size.height, + CGImageGetBitsPerComponent(aImage.CGImage), 0, + CGImageGetColorSpace(aImage.CGImage), + CGImageGetBitmapInfo(aImage.CGImage)); + CGContextConcatCTM(ctx, transform); + switch (aImage.imageOrientation) { + case UIImageOrientationLeft: + case UIImageOrientationLeftMirrored: + case UIImageOrientationRight: + case UIImageOrientationRightMirrored: + // Grr... + CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.height,aImage.size.width), aImage.CGImage); + break; + + default: + CGContextDrawImage(ctx, CGRectMake(0,0,aImage.size.width,aImage.size.height), aImage.CGImage); + break; + } + + // And now we just create a new UIImage from the drawing context + CGImageRef cgimg = CGBitmapContextCreateImage(ctx); + UIImage *img = [UIImage imageWithCGImage:cgimg]; + CGContextRelease(ctx); + CGImageRelease(cgimg); + return img; +} + +- (UIImage *)imageWithColor:(UIColor *)color { + CGRect rect = CGRectMake(0.0f, 0.0f, 1.0f, 1.0f); + UIGraphicsBeginImageContext(rect.size); + CGContextRef context = UIGraphicsGetCurrentContext(); + + CGContextSetFillColorWithColor(context, [color CGColor]); + CGContextFillRect(context, rect); + + UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); + UIGraphicsEndImageContext(); + + return image; + +} + +//异步生成纯色圆角图片 +- (void)imageWithSize:(CGSize)size radius:(CGFloat)radius backColor:(UIColor *)backColor completion:(void(^)(UIImage *image))completion { + // 异步绘制裁切 + dispatch_async(dispatch_get_global_queue(0, 0), ^{ + // 利用绘图建立上下文 + UIGraphicsBeginImageContextWithOptions(size, true, 0); + CGRect rect = CGRectMake(0, 0, size.width, size.height); + // 填充颜色 + [backColor setFill]; + UIRectFill(rect); + // // 贝塞尔裁切 + // UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:radius]; + // [path addClip]; + // [self drawInRect:rect]; + + // 获取结果 + UIImage *resultImage = [UIGraphicsGetImageFromCurrentImageContext() circularImage]; + // 关闭上下文 + UIGraphicsEndImageContext(); + // 主队列回调 + dispatch_async(dispatch_get_main_queue(), ^{ + completion(resultImage); + }); + }); + +} + +- (UIImage *)circularImage { + // 1. 开启图形上下文 + UIGraphicsBeginImageContextWithOptions(self.size, NO, 0); + + // 2. 描述路径 + + UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, self.size.width, self.size.height) byRoundingCorners:UIRectCornerTopLeft|UIRectCornerTopRight | UIRectCornerBottomLeft | UIRectCornerBottomRight cornerRadii:CGSizeMake(self.size.width, self.size.height)]; + // UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, self.size.width, self.size.height)]; + + // 3. 添加裁减区域 + [path addClip]; + + // 4. 绘制图片 + [self drawAtPoint:CGPointZero]; + + // 5. 从上下文获取图片 + UIImage * image = UIGraphicsGetImageFromCurrentImageContext(); + + // 6. 关闭上下文 + UIGraphicsEndImageContext(); + + // 7. 设置图片 + return image; +} + ++ (UIImage *)gradientColorImageFromColors:(NSArray *)colors gradientType:(GradientType)gradientType imgSize:(CGSize)imgSize{ + + NSMutableArray *ar = [NSMutableArray array]; + for(UIColor *c in colors) { + [ar addObject:(id)c.CGColor]; + } + UIGraphicsBeginImageContextWithOptions(imgSize, YES, 1); + CGContextRef context = UIGraphicsGetCurrentContext(); + CGContextSaveGState(context); + CGColorSpaceRef colorSpace = CGColorGetColorSpace([[colors lastObject] CGColor]); + CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (CFArrayRef)ar, NULL); + CGPoint start; + CGPoint end; + switch (gradientType) { + case GradientTypeTopToBottom: + start = CGPointMake(0.0, 0.0); + end = CGPointMake(0.0, imgSize.height); + break; + case GradientTypeLeftToRight: + start = CGPointMake(0.0, 0.0); + end = CGPointMake(imgSize.width, 0.0); + break; + case GradientTypeUpleftToLowright: + start = CGPointMake(0.0, 0.0); + end = CGPointMake(imgSize.width, imgSize.height); + break; + case GradientTypeUprightToLowleft: + start = CGPointMake(imgSize.width, 0.0); + end = CGPointMake(0.0, imgSize.height); + break; + default: + break; + } + CGContextDrawLinearGradient(context, gradient, start, end, kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation); + UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); + CGGradientRelease(gradient); + CGContextRestoreGState(context); + CGColorSpaceRelease(colorSpace); + UIGraphicsEndImageContext(); + + return image; +} + +- (UIImage *)setCornerWithRadius:(CGFloat)radius andSize:(CGSize)size { + //开启图形上下文 + UIGraphicsBeginImageContext(size); + //绘制圆角矩形 + CGRect rect = CGRectMake(0, 0, size.width, size.height); + UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rect byRoundingCorners:UIRectCornerAllCorners cornerRadii:CGSizeMake(radius, radius)]; + //将Path添加到上下文中 + CGContextAddPath(UIGraphicsGetCurrentContext(), path.CGPath); + //裁剪上下文 + CGContextClip(UIGraphicsGetCurrentContext()); + //将图片绘制到上下文中 + [self drawInRect:rect]; + //设置绘制模式 + CGContextDrawPath(UIGraphicsGetCurrentContext(), kCGPathStroke); + //获取图片 + UIImage *output = UIGraphicsGetImageFromCurrentImageContext(); + //关闭上下文 + UIGraphicsEndImageContext(); + //返回裁剪好的图片 + return output; +} + +@end diff --git a/xplan-ios/Base/UI/XCHUDTool.h b/xplan-ios/Base/UI/XCHUDTool.h new file mode 100644 index 00000000..185eefb3 --- /dev/null +++ b/xplan-ios/Base/UI/XCHUDTool.h @@ -0,0 +1,134 @@ +// +// XCHUDTool.h +// TTPlay +// +// Created by Macx on 2019/5/15. +// Copyright © 2019 YiZhuan. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +/** + showGIFLoading使用注意: + 1.谁负责showLoading, 谁负责hideHUD + 2.showLoading是指定了加载在那个View, hideHUD时请指定hide那个view的hud + */ + +@interface XCHUDTool : NSObject +/** + 隐藏HUD + */ ++ (void)hideHUD; + +/** + 隐藏HUD, 如果view为nil, 则默认隐藏主窗口的HUD + + @param view view + */ ++ (void)hideHUDInView:(nullable UIView *)view; + +/** + 显示成功message, 默认显示在窗口上, 2.5s后消失, 默认不拦截点击事件 + + @param message 文字 + */ ++ (void)showSuccessWithMessage:(NSString *)message; + +/** + 显示成功message, 2.5s后消失, 默认不拦截点击事件 + + @param message 文字 + @param view 显示在哪个view上 + */ ++ (void)showSuccessWithMessage:(NSString *)message inView:(nullable UIView *)view; + +/** + 显示成功message + + @param message 文字 + @param view 显示在哪个view上 + @param afterDelay 延迟消失时间 + @param enabled 是否可以拦截事件 no:不拦截 yes:拦截 + */ ++ (void)showSuccessWithMessage:(NSString *)message inView:(nullable UIView *)view delay:(NSTimeInterval)afterDelay enabled:(BOOL)enabled; + +/** + 显示错误message, 默认显示在窗口上, 2.5s后消失, 默认不拦截点击事件 + + @param message 文字 + */ ++ (void)showErrorWithMessage:(NSString *)message; + +/** + 显示错误message, 2.5s后消失, 默认不拦截点击事件 + + @param message 文字 + @param view 显示在哪个view上 + */ ++ (void)showErrorWithMessage:(NSString *)message inView:(nullable UIView *)view; + +/** + 显示错误message + + @param message 文字 + @param view 显示在哪个view上 + @param afterDelay 延迟消失时间 + @param enabled 是否可以拦截事件 no:不拦截 yes:拦截 + */ ++ (void)showErrorWithMessage:(NSString *)message inView:(nullable UIView *)view delay:(NSTimeInterval)afterDelay enabled:(BOOL)enabled; + +/** + 在窗口上显示自定义GIFLoading, 背景默认黑色0.35透明度, 默认拦截点击事件 + */ ++ (void)showGIFLoading; + +/** + 在指定的view上显示自定义GIFLoading, 背景默认黑色0.35透明度, 默认拦截点击事件 + + @param view 显示在哪个view上 + */ ++ (void)showGIFLoadingInView:(nullable UIView *)view; + +/** + 在指定的view上显示自定义GIFLoading + + @param view 显示在哪个view上 + @param bgColor 背景颜色, 遮盖 + @param enabled 是否可以拦截事件 no:不拦截 yes:拦截 + */ ++ (void)showGIFLoadingInView:(nullable UIView *)view bgColor:(nullable UIColor *)bgColor enabled:(BOOL)enabled; + +/** + 在窗口上显示菊花 + */ ++ (void)showLoading; + +/** + 在view上显示菊花 + */ ++ (void)showLoadingInView:(nullable UIView *)view; + +/** + 在view上显示菊花 + */ ++ (void)showLoadingInView:(nullable UIView *)view enabled:(BOOL)enabled; + +/** + 在窗口上显示菊花+文字 + */ ++ (void)showLoadingWithMessage:(NSString *)message; + +/** + 在view上显示菊花+文字 + */ ++ (void)showLoadingWithMessage:(NSString *)message inView:(nullable UIView *)view; + +/** + 在view上显示菊花+文字 + */ ++ (void)showLoadingWithMessage:(NSString *)message inView:(nullable UIView *)view enabled:(BOOL)enabled; +@end + +NS_ASSUME_NONNULL_END diff --git a/xplan-ios/Base/UI/XCHUDTool.m b/xplan-ios/Base/UI/XCHUDTool.m new file mode 100644 index 00000000..df25a048 --- /dev/null +++ b/xplan-ios/Base/UI/XCHUDTool.m @@ -0,0 +1,284 @@ +// +// XCHUDTool.m +// TTPlay +// +// Created by Macx on 2019/5/15. +// Copyright © 2019 YiZhuan. All rights reserved. +// + +#import "XCHUDTool.h" +#import "GCDHelper.h" +#import + +#define kDelayTime 2.5 + +@implementation XCHUDTool +/** + 隐藏HUD, 如果view为nil, 则默认隐藏主窗口的HUD + + @param view view + */ ++ (void)hideHUDInView:(nullable UIView *)view { + dispatch_main_sync_safe(^{ + if (view) { + [MBProgressHUD hideHUDForView:view animated:NO]; + [MBProgressHUD hideHUDForView:[UIApplication sharedApplication].keyWindow animated:NO]; + } else { + [MBProgressHUD hideHUDForView:[UIApplication sharedApplication].keyWindow animated:NO]; + } + }); +} +/** + 隐藏HUD + */ ++ (void)hideHUD { + [self hideHUDInView:nil]; +} + +/** + 显示成功message, 默认显示在窗口上, 2.5s后消失, 默认不拦截点击事件 + + @param message 文字 + */ ++ (void)showSuccessWithMessage:(NSString *)message { + [self showSuccessWithMessage:message inView:[UIApplication sharedApplication].keyWindow]; +} + +/** + 显示成功message, 2.5s后消失, 默认不拦截点击事件 + + @param message 文字 + @param view 显示在哪个view上 + */ ++ (void)showSuccessWithMessage:(NSString *)message inView:(nullable UIView *)view { + [self showSuccessWithMessage:message inView:view delay:kDelayTime enabled:NO]; +} + +/** + 显示成功message + @param message 文字 + @param view 显示在哪个view上 + @param afterDelay 延迟消失时间 + @param enabled 是否可以拦截事件 no:不拦截 yes:拦截 + */ ++ (void)showSuccessWithMessage:(NSString *)message inView:(nullable UIView *)view delay:(NSTimeInterval)afterDelay enabled:(BOOL)enabled { + + if (message.length == 0) { return; } + __block UIView *inView = view; + + dispatch_main_sync_safe(^{ + if (!inView) { + inView = [UIApplication sharedApplication].keyWindow; + } + [self hideHUDInView:view]; // 先隐藏 + MBProgressHUD *hud = [self normalProgressHUD:view]; + hud.userInteractionEnabled = enabled; + hud.mode = MBProgressHUDModeText; + hud.bezelView.style = MBProgressHUDBackgroundStyleSolidColor; + hud.margin = 8; + // 方框背景颜色 + hud.bezelView.color = [[UIColor blackColor] colorWithAlphaComponent:0.8]; + hud.label.text = message; + hud.label.numberOfLines = 0; + hud.label.textColor = [UIColor whiteColor]; + hud.label.font = [UIFont systemFontOfSize:14]; + [hud hideAnimated:YES afterDelay:afterDelay]; + }); +} + +/** + 显示错误message, 默认显示在窗口上, 2.5s后消失, 默认不拦截点击事件 + + @param message 文字 + */ ++ (void)showErrorWithMessage:(NSString *)message { + [self showErrorWithMessage:message inView:[UIApplication sharedApplication].keyWindow]; +} + +/** + 显示错误message, 2.5s后消失, 默认不拦截点击事件 + + @param message 文字 + @param view 显示在哪个view上 + */ ++ (void)showErrorWithMessage:(NSString *)message inView:(nullable UIView *)view { + [self showErrorWithMessage:message inView:view delay:kDelayTime enabled:NO]; +} + +/** + 显示错误message + + @param message 文字 + @param view 显示在哪个view上 + @param afterDelay 延迟消失时间 + @param enabled 是否可以拦截事件 no:不拦截 yes:拦截 + */ ++ (void)showErrorWithMessage:(NSString *)message inView:(nullable UIView *)view delay:(NSTimeInterval)afterDelay enabled:(BOOL)enabled { + if (message.length == 0) { return; } + if (!view) { + view = [UIApplication sharedApplication].keyWindow; + } + + [self hideHUDInView:view]; // 先隐藏 + + dispatch_main_sync_safe(^{ + MBProgressHUD *hud = [self normalProgressHUD:view]; + hud.userInteractionEnabled = enabled; + hud.mode = MBProgressHUDModeText; + hud.bezelView.style = MBProgressHUDBackgroundStyleSolidColor; + hud.margin = 8; + // 方框背景颜色 + hud.bezelView.color = [[UIColor blackColor] colorWithAlphaComponent:0.8]; + hud.label.text = message; + hud.label.numberOfLines = 0; + hud.label.textColor = [UIColor whiteColor]; + hud.label.font = [UIFont systemFontOfSize:14]; + [hud hideAnimated:YES afterDelay:afterDelay]; + }); +} + +/** + * 在窗口上显示菊花 + */ ++ (void)showLoading { + [self showLoadingInView:[UIApplication sharedApplication].keyWindow]; +} + +/** + * 在view上显示菊花 + */ ++ (void)showLoadingInView:(nullable UIView *)view { + [self showLoadingInView:view enabled:YES]; +} + +/** + * 在view上显示菊花 + */ ++ (void)showLoadingInView:(nullable UIView *)view enabled:(BOOL)enabled { + [self showLoadingWithMessage:@"" inView:view enabled:enabled]; +} + +/** + * 在窗口上显示菊花+文字 + */ ++ (void)showLoadingWithMessage:(NSString *)message { + [self showLoadingWithMessage:message inView:[UIApplication sharedApplication].keyWindow]; +} + +/** + * 在view上显示菊花+文字 + */ ++ (void)showLoadingWithMessage:(NSString *)message inView:(nullable UIView *)view { + [self showLoadingWithMessage:message inView:view enabled:YES]; +} + +/** + * 在view上显示菊花+文字 + */ ++ (void)showLoadingWithMessage:(NSString *)message inView:(nullable UIView *)view enabled:(BOOL)enabled { + if (!view) { + view = [UIApplication sharedApplication].keyWindow; + } + [self hideHUDInView:view]; + + dispatch_main_sync_safe(^{ + MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:view animated:YES]; + hud.userInteractionEnabled = enabled; + hud.bezelView.color = [[UIColor whiteColor] colorWithAlphaComponent:0.8]; + hud.removeFromSuperViewOnHide = YES; + if (message.length) { + hud.label.text = message; + hud.label.numberOfLines = 0; + hud.label.textColor = [UIColor blackColor]; + hud.label.font = [UIFont systemFontOfSize:14]; + } + }); + +} + +/** + 在窗口上显示自定义GIFLoading, 背景默认黑色0.35透明度, 默认拦截点击事件 + */ ++ (void)showGIFLoading { + [self showGIFLoadingInView:[UIApplication sharedApplication].keyWindow]; +} + +/** + 在指定的view上显示自定义GIFLoading, 背景默认黑色0.35透明度, 默认拦截点击事件 + + @param view 显示在哪个view上 + */ ++ (void)showGIFLoadingInView:(nullable UIView *)view { + [self showGIFLoadingInView:view bgColor:[UIColor colorWithRed:0 green:0 blue:0 alpha:0.35] enabled:YES]; +} + +/** + 在指定的view上显示自定义GIFLoading + @param view 显示在哪个view上 + @param bgColor 背景颜色, 遮盖 + @param enabled 是否可以拦截事件 no:不拦截 yes:拦截 + */ ++ (void)showGIFLoadingInView:(nullable UIView *)view bgColor:(nullable UIColor *)bgColor enabled:(BOOL)enabled { + if (!view) { + view = [UIApplication sharedApplication].keyWindow; + } + [self hideHUDInView:view]; + dispatch_main_sync_safe(^{ + MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:view animated:YES]; + hud.bezelView.style = MBProgressHUDBackgroundStyleSolidColor; + hud.minSize = CGSizeMake(168, 133); + hud.userInteractionEnabled = enabled; + hud.mode = MBProgressHUDModeCustomView; + hud.minSize = CGSizeMake(150, 150); + [hud.bezelView addSubview:[self loadingView]]; + hud.backgroundColor = bgColor; + hud.bezelView.color = [UIColor clearColor]; + hud.removeFromSuperViewOnHide = YES; + }); +} + +#pragma mark - private ++ (MBProgressHUD *)normalProgressHUD:(UIView *)view { + MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:view animated:YES]; + hud.removeFromSuperViewOnHide = YES; + return hud; +} + + + ++ (UIView *)loadingView { + UIView *loadingBGView = [[UIView alloc] init]; + loadingBGView.layer.cornerRadius = 20; + loadingBGView.backgroundColor = [UIColor whiteColor]; + UIImageView *loadingImageView = [[UIImageView alloc] init]; + + UILabel *loadingTitleLabel = [[UILabel alloc] init]; + loadingTitleLabel = [[UILabel alloc] init]; + loadingTitleLabel.textColor = [UIColor colorWithRed:(153.0)/255.0f green:(153.0)/255.0f blue:(153.0)/255.0f alpha:1]; + loadingTitleLabel.font = [UIFont systemFontOfSize:14]; + loadingTitleLabel.text = @"加载中…"; + loadingTitleLabel.textAlignment = NSTextAlignmentCenter; + + [loadingBGView addSubview:loadingImageView]; + [loadingBGView addSubview:loadingTitleLabel]; + + loadingBGView.frame = CGRectMake(0,0,130,130); + loadingImageView.frame = CGRectMake((CGRectGetWidth(loadingBGView.frame)-40)/ 2, (CGRectGetHeight(loadingBGView.frame) - 40 -15-15) / 2, 40, 40); + loadingTitleLabel.frame = CGRectMake(0, CGRectGetMaxY(loadingImageView.frame) + 15, loadingBGView.frame.size.width, 15); + + loadingImageView.image = [UIImage imageNamed:@"common_loading"]; + + CABasicAnimation * animage = [CABasicAnimation animationWithKeyPath:@"transform.rotation"]; + animage.fromValue = 0; + animage.toValue = @(2 * M_PI); + animage.duration = 1; + animage.repeatCount = CGFLOAT_MAX; + animage.autoreverses = NO; + animage.removedOnCompletion = NO; + [loadingImageView.layer addAnimation:animage forKey:@"animation"]; + return loadingBGView; +} + + + +@end diff --git a/xplan-ios/Info.plist b/xplan-ios/Info.plist new file mode 100644 index 00000000..8a09f676 --- /dev/null +++ b/xplan-ios/Info.plist @@ -0,0 +1,54 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + 音游 + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + $(MARKETING_VERSION) + CFBundleVersion + 1 + LSRequiresIPhoneOS + + NSAppTransportSecurity + + NSAllowsArbitraryLoads + + + UIApplicationSupportsIndirectInputEvents + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/xplan-ios/Main/Login/Api/Api+Login.h b/xplan-ios/Main/Login/Api/Api+Login.h new file mode 100644 index 00000000..9cb5a4f4 --- /dev/null +++ b/xplan-ios/Main/Login/Api/Api+Login.h @@ -0,0 +1,18 @@ +// +// Api+Login.h +// xplan-ios +// +// Created by zu on 2021/9/6. +// + +#import "Api.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface Api (Login) + ++ (void)phoneQuickLogin:(HttpRequestHelperCompletion)completion accessToken:(NSString *)accessToken token:(NSString *)token; + +@end + +NS_ASSUME_NONNULL_END diff --git a/xplan-ios/Main/Login/Api/Api+Login.m b/xplan-ios/Main/Login/Api/Api+Login.m new file mode 100644 index 00000000..0abc8258 --- /dev/null +++ b/xplan-ios/Main/Login/Api/Api+Login.m @@ -0,0 +1,18 @@ +// +// Api+Login.m +// xplan-ios +// +// Created by zu on 2021/9/6. +// + +#import "Api+Login.h" + +@implementation Api (Login) + ++ (void)phoneQuickLogin:(HttpRequestHelperCompletion)completion accessToken:(NSString *)accessToken token:(NSString *)token { + [self makeRequest:@"acc/oneclick/login" method:HttpRequestHelperMethodPOST completion:^(id _Nullable data, NSNumber * _Nonnull code, NSString * _Nullable msg) { + completion(data, code, msg); + }, __FUNCTION__, accessToken, token, nil]; +} + +@end diff --git a/xplan-ios/Main/Login/Presenter/LoginPresenter.h b/xplan-ios/Main/Login/Presenter/LoginPresenter.h new file mode 100644 index 00000000..e385d156 --- /dev/null +++ b/xplan-ios/Main/Login/Presenter/LoginPresenter.h @@ -0,0 +1,18 @@ +// +// LoginPresenter.h +// xplan-ios +// +// Created by zu on 2021/9/1. +// + +#import "BaseMvpPresenter.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface LoginPresenter : BaseMvpPresenter + +- (void)phoneQuickLogin:(NSString *)accessToken token:(NSString*) token; + +@end + +NS_ASSUME_NONNULL_END diff --git a/xplan-ios/Main/Login/Presenter/LoginPresenter.m b/xplan-ios/Main/Login/Presenter/LoginPresenter.m new file mode 100644 index 00000000..34bba0a7 --- /dev/null +++ b/xplan-ios/Main/Login/Presenter/LoginPresenter.m @@ -0,0 +1,26 @@ +// +// LoginPresenter.m +// xplan-ios +// +// Created by zu on 2021/9/1. +// + +#import "LoginPresenter.h" +#import "LoginProtocol.h" +#import "Api+Login.h" + +@implementation LoginPresenter + +- (id)getView { + return ((id) [super getView]); +} + +- (void)phoneQuickLogin:(NSString *)accessToken token:(NSString *)token { + [Api phoneQuickLogin:[self createHttpCompletion:^(id _Nonnull data) { + [[self getView] phoneQuickLoginSuccess]; + } fail:^(NSNumber * _Nonnull code, NSString * _Nullable msg) { + // todo fail + } showLoading:YES errorToast:YES] accessToken:accessToken token:token]; +} + +@end diff --git a/xplan-ios/Main/Login/Protocol/LoginProtocol.h b/xplan-ios/Main/Login/Protocol/LoginProtocol.h new file mode 100644 index 00000000..7e501c7b --- /dev/null +++ b/xplan-ios/Main/Login/Protocol/LoginProtocol.h @@ -0,0 +1,18 @@ +// +// LoginProtocol.h +// xplan-ios +// +// Created by zu on 2021/9/1. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@protocol LoginProtocol + +- (void)phoneQuickLoginSuccess; + +@end + +NS_ASSUME_NONNULL_END diff --git a/xplan-ios/Main/Login/View/LoginViewController.h b/xplan-ios/Main/Login/View/LoginViewController.h new file mode 100644 index 00000000..35a3a96f --- /dev/null +++ b/xplan-ios/Main/Login/View/LoginViewController.h @@ -0,0 +1,16 @@ +// +// LoginViewController.h +// xplan-ios +// +// Created by zu on 2021/8/31. +// + +#import "MvpViewController.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface LoginViewController : MvpViewController + +@end + +NS_ASSUME_NONNULL_END diff --git a/xplan-ios/Main/Login/View/LoginViewController.m b/xplan-ios/Main/Login/View/LoginViewController.m new file mode 100644 index 00000000..e107cf3a --- /dev/null +++ b/xplan-ios/Main/Login/View/LoginViewController.m @@ -0,0 +1,578 @@ +// +// LoginViewController.m +// xplan-ios +// +// Created by zu on 2021/8/31. +// + +#import "LoginViewController.h" +#import "LoginPresenter.h" +#import "LoginProtocol.h" +#import "UIImage+Utils.h" +#import "GCDHelper.h" +#import "XCHUDTool.h" +#import +#import +#import +#import + +// 易盾 本机一键登录 +#ifdef DEBUG +NSString *businessID = @"09c1214706c34f4798d3f05d86148608"; +#else +NSString *businessID = @"09c1214706c34f4798d3f05d86148608"; +#endif + +typedef NS_ENUM(NSUInteger, XYLoginType) { + XYLoginTypeUnknow = 0, //未知 + XYLoginTypeTelecom = 1, //电信 + XYLoginTypeChinaMobile = 2, //移动 + XYLoginTypeUnicom = 3 //联通 +}; + + +typedef NS_ENUM(NSInteger,XYThirdLoginType) { + XYThirdLoginWechat = 1, // 微信登录 + XYThirdLoginQQ = 2, // QQ登录 + XYThirdLoginPhoneNum = 3, // 手机号登录 + XYThirdLoginApple = 5, // 苹果id登录 +}; + +@interface LLButtonView : UIView + +//icon +@property (nonatomic,strong) UIImageView *logoImageView; +//title +@property (nonatomic,strong) UILabel *titleLabel; +@end + +@implementation LLButtonView + +- (instancetype)initWithFrame:(CGRect)frame { + if (self = [super initWithFrame:frame]) { + [self initView]; + [self initContrations]; + } + return self; +} + + +- (void)initView { + [self addSubview:self.logoImageView]; + [self addSubview:self.titleLabel]; +} + +- (void)initContrations { + [self.logoImageView mas_makeConstraints:^(MASConstraintMaker *make) { + make.size.mas_equalTo(CGSizeMake(36, 36)); + make.centerX.left.mas_equalTo(self); + }]; + + [self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) { + make.left.right.mas_equalTo(self); + make.top.mas_equalTo(self.logoImageView.mas_bottom).offset(15); + }]; +} + +- (UIImageView *)logoImageView { + if (!_logoImageView) { + _logoImageView = [[UIImageView alloc] init]; + _logoImageView.userInteractionEnabled = YES; + } + return _logoImageView; +} + +- (UILabel *)titleLabel { + if (!_titleLabel) { + _titleLabel = [[UILabel alloc] init]; + _titleLabel.font = [UIFont systemFontOfSize:11]; + _titleLabel.textColor = ThemeTextColor; + _titleLabel.textAlignment = NSTextAlignmentCenter; + } + return _titleLabel; +} + +@end + +@interface LoginViewController () + +@property (nonatomic, strong) UIImageView *appIcon; +/** 标题 */ +@property (nonatomic, strong) UILabel *titleLabel; +/** 副标题 */ +@property (nonatomic, strong) UILabel *subTitleLabel; + +@property (nonatomic, strong) UIView *contentView; +/** 登录按钮*/ +@property (nonatomic, strong) UIButton *loginButton; + +@property (nonatomic,strong) UIStackView *stackView; +///手机 +@property (nonatomic,strong) LLButtonView *qqButtonView; +///wx +@property (nonatomic,strong) LLButtonView *wxButtonView; +///qq +@property (nonatomic,strong) LLButtonView *phoneButtonView; + +/** 同意勾选按钮*/ +@property (nonatomic, strong) UIButton *agreeButton; +/** 同意即可登录 */ +@property (nonatomic, strong) YYLabel *agreeLabel; +/** 勾选隐私政策提示泡泡 */ +@property (nonatomic, strong) UIImageView *authBubbleView; +/** 泡泡提示内容 */ +@property (nonatomic, strong) UILabel *authBubbleLabel; + +@end + +@implementation LoginViewController + +- (LoginPresenter *)createPresenter { + return [[LoginPresenter alloc] init]; +} + +- (void)phoneQuickLoginSuccess { + [XCHUDTool showSuccessWithMessage:@"一键登录成功。"]; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + [self initView]; + [self setUpConstraints]; + [self setEvents]; +} + +- (void)viewWillAppear:(BOOL)animated { + [super viewWillAppear:animated]; + [self hideNavigationBar]; +} + +- (void)viewWillDisappear:(BOOL)animated { + [super viewWillDisappear:animated]; + [self showNavigationBar]; +} + +- (void)initView { + [self.view addSubview:self.appIcon]; + [self.view addSubview:self.titleLabel]; + [self.view addSubview:self.subTitleLabel]; + + [self.view addSubview:self.contentView]; + [self.contentView addSubview:self.loginButton]; + [self.contentView addSubview:self.stackView]; + [self.view addSubview:self.agreeButton]; + [self.view addSubview:self.agreeLabel]; + [self.view addSubview:self.authBubbleView]; + [self.authBubbleView addSubview:self.authBubbleLabel]; + + [self.stackView addArrangedSubview:self.qqButtonView]; + [self.stackView addArrangedSubview:self.phoneButtonView]; + [self.stackView addArrangedSubview:self.wxButtonView]; +} + + +- (void)setUpConstraints { + [self.appIcon mas_makeConstraints:^(MASConstraintMaker *make) { + make.top.mas_equalTo(110 + kSafeAreaTopHeight); + make.left.mas_equalTo(52); + make.width.mas_equalTo(100); + make.height.mas_equalTo(100); + }]; + + [self.titleLabel mas_makeConstraints:^(MASConstraintMaker *make) { + make.top.mas_equalTo(self.appIcon.mas_top).offset(28.5); + make.left.mas_equalTo(self.appIcon.mas_right).offset(7.5); + make.height.mas_equalTo(20); + make.right.mas_equalTo(-20); + }]; + + [self.subTitleLabel mas_makeConstraints:^(MASConstraintMaker *make) { + make.top.mas_equalTo(self.titleLabel.mas_bottom).offset(13); + make.left.mas_equalTo(self.titleLabel.mas_left); + make.height.mas_equalTo(13); + make.right.mas_equalTo(-20); + }]; + + + [self.contentView mas_makeConstraints:^(MASConstraintMaker *make) { + make.top.mas_equalTo(self.view).offset(iPhoneXSeries ? 300 : 228); + make.left.right.bottom.mas_equalTo(0); + }]; + [self.loginButton mas_makeConstraints:^(MASConstraintMaker *make) { + make.left.right.mas_equalTo(self.view).inset(52); + make.top.mas_equalTo(70); + make.height.mas_equalTo(45); + }]; + + + [self.stackView mas_makeConstraints:^(MASConstraintMaker *make) { + make.bottom.mas_equalTo(self.view).offset(-kSafeAreaBottomHeight - 60); + make.centerX.mas_equalTo(self.view); + make.height.mas_equalTo(65); + }]; + + + [self.agreeLabel mas_makeConstraints:^(MASConstraintMaker *make) { + make.top.mas_equalTo(self.loginButton.mas_bottom).offset(20); + make.height.mas_equalTo(40); + make.width.mas_equalTo(270); + make.centerX.mas_equalTo(self.view).mas_offset(15 - 6); + }]; + + [self.agreeButton mas_makeConstraints:^(MASConstraintMaker *make) { + make.right.mas_equalTo(self.agreeLabel.mas_left).mas_offset(6); + make.centerY.mas_equalTo(self.agreeLabel.mas_centerY); + make.width.height.mas_equalTo(30); + }]; + + [self.authBubbleView mas_makeConstraints:^(MASConstraintMaker *make) { + make.left.mas_equalTo(self.agreeButton).mas_offset((-16)); + make.top.mas_equalTo(self.agreeButton.mas_bottom); + make.width.mas_equalTo(235); + make.height.mas_equalTo(29); + }]; + + [self.authBubbleLabel mas_makeConstraints:^(MASConstraintMaker *make) { + make.left.mas_equalTo(8); + make.bottom.mas_equalTo(self.authBubbleView).mas_offset(-6); + }]; +} + +- (void)setEvents { + // 手机登陆按钮点击 + @weakify(self) + UITapGestureRecognizer *phoneTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didClickRecognizer:)]; + [self.phoneButtonView addGestureRecognizer:phoneTap]; + UITapGestureRecognizer * qqTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didClickRecognizer:)]; + [self.qqButtonView addGestureRecognizer:qqTap]; + UITapGestureRecognizer * wxTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(didClickRecognizer:)]; + [self.wxButtonView addGestureRecognizer:wxTap]; + + [[self.agreeButton rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) { + self.agreeButton.selected = !self.agreeButton.selected; + if (self.agreeButton.isSelected) { + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + [defaults setObject:@"hadAgree" forKey:@"kHadAgreePrivacy"]; + [defaults synchronize]; + [UIView animateWithDuration:0.5 animations:^{ + self.authBubbleView.alpha = 0.0; + }]; + } + }]; + + // 一键登录 + [[self.loginButton rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) { + @strongify(self) + if (self.agreeButton.isSelected) { + [self phoneQuickLogin]; + } else { + [UIView animateWithDuration:0.5 animations:^{ + self.authBubbleView.alpha = 1.0; + }]; + } + }]; +} + +- (void)didClickRecognizer:(UITapGestureRecognizer *)tap { + UIView * view = tap.view; + if (self.agreeButton.isSelected) { + if ([view isEqual:self.phoneButtonView]) { + // InputPhoneLoginViewController *inputPhoneVC = [[InputPhoneLoginViewController alloc] init]; + // [self.navigationController pushViewController:inputPhoneVC animated:YES]; + } else if([view isEqual:self.qqButtonView]) { + [XCHUDTool showLoading]; + // [GetCore(AuthCore) thirdLoginPlatform:SSDKPlatformTypeQQ]; + } else if([view isEqual:self.wxButtonView]) { + [XCHUDTool showLoading]; + // [GetCore(AuthCore) thirdLoginPlatform:SSDKPlatformTypeWechat]; + } + } else { + [UIView animateWithDuration:0.5 animations:^{ + self.authBubbleView.alpha = 1.0; + }]; + } +} + +- (void)phoneQuickLogin { + [XCHUDTool showLoading]; + // 在使用一键登录之前,请先调用shouldQuickLogin方法,判断当前上网卡的网络环境和运营商是否可以一键登录 + @weakify(self) + NTESQuickLoginManager *qlManager = [NTESQuickLoginManager sharedInstance]; + BOOL shouldQL = [qlManager shouldQuickLogin]; + if (!shouldQL) { + [self phoneQuickLoginFail]; + return; + } + + [qlManager registerWithBusinessID:businessID timeout:3*1000 configURL:nil extData:nil completion:^(NSDictionary * _Nullable params, BOOL regSuccess) { + @strongify(self) + if (!regSuccess) { + dispatch_main_sync_safe(^{ + [self phoneQuickLoginFail]; + }); + return; + } + + NSString *token = [params objectForKey:@"token"]; + [qlManager getPhoneNumberCompletion:^(NSDictionary * _Nonnull resultDic) { + @strongify(self) + NSNumber *boolNum = [resultDic objectForKey:@"success"]; + BOOL getPhoneNumberSuccess = [boolNum boolValue]; + if (!getPhoneNumberSuccess) { + dispatch_main_sync_safe(^{ + [self phoneQuickLoginFail]; + }); + return; + } + + [self configQuickLogin]; + [qlManager CUCMCTAuthorizeLoginCompletion:^(NSDictionary * _Nonnull resultDic) { + @strongify(self) + NSNumber *boolNum = [resultDic objectForKey:@"success"]; + BOOL authSuccess = [boolNum boolValue]; + if (!authSuccess) { + dispatch_main_sync_safe(^{ + NSString *resultCode = [resultDic objectForKey:@"resultCode"]; + //取消一键登录 + if ([resultCode isEqualToString:@"200020"] || + [resultCode isEqualToString:@"10104"]) { + [XCHUDTool hideHUD]; + } else { + [self phoneQuickLoginFail]; + } + }); + return; + } + + dispatch_main_sync_safe(^{ + @strongify(self) + // [TTStatisticsService trackEvent:@"one_click_login_succeed" eventDescribe:@"一键登录成功"]; + // // 取号成功,获取acessToken + [XCHUDTool hideHUD]; + [self dismissViewControllerAnimated:YES completion:nil]; + [self.presenter phoneQuickLogin:resultDic[@"accessToken"] token:token]; + }); + }]; + }]; + + }]; +} + +- (void)phoneQuickLoginFail { + [XCHUDTool showErrorWithMessage:@"一键登录失败,请检查手机网络状态。"]; +} + +- (void)configQuickLogin { + // 获取当前上网卡的运营商,0:未知 1:电信 2.移动 3.联通 + NSInteger currentCarrier = [[NTESQuickLoginManager sharedInstance] getCarrier]; + NTESQuickLoginModel *CMModel = [[NTESQuickLoginModel alloc] init]; + CMModel.currentVC = self; + CMModel.presentDirectionType = NTESPresentDirectionPresent; + if (currentCarrier == XYLoginTypeUnicom) { // 联通 + CMModel.logoImg = [UIImage imageNamed:@"login_unicom_mobile"]; + }else if (currentCarrier == XYLoginTypeChinaMobile) { //移动 + CMModel.logoImg = [UIImage imageNamed:@"login_china_mobile"]; + }else { //电信 + CMModel.logoImg = [UIImage imageNamed:@"login_ct_mobile"]; + } + CMModel.backgroundColor = ThemeBackgroundColor; + + CMModel.logoWidth = 95; + CMModel.logoHeight = 95; + CMModel.logoOffsetTopY = 30; + CMModel.navText = @"一键登录"; + CMModel.navTextColor = UIColor.whiteColor; + CMModel.navTextFont = [UIFont boldSystemFontOfSize:18]; + + CMModel.navReturnImg = [UIImage imageNamed:@"common_nav_back"]; + CMModel.navBgColor = ThemeBackgroundColor; + + CMModel.logBtnHeight = 45; + CMModel.logBtnRadius = 45.f / 2; + CMModel.logBtnOffsetTopY= 260; + CMModel.logBtnOriginLeft = 52; + CMModel.logBtnOriginRight = 52; + CMModel.logBtnUsableBGColor = ThemeDefaultColor; + CMModel.logBtnText = @"本机号码一键登录"; + CMModel.logBtnTextColor = UIColor.whiteColor; + CMModel.logBtnTextFont = [UIFont systemFontOfSize:18 weight:UIFontWeightMedium]; + + CMModel.numberOffsetTopY = 170; + CMModel.numberColor = UIColor.whiteColor; + CMModel.numberFont = [UIFont boldSystemFontOfSize:20]; + CMModel.brandColor = UIColor.whiteColor; + + CMModel.uncheckedImg = [UIImage imageNamed:@"common_checkbox_uncheck"]; + CMModel.checkedImg = [UIImage imageNamed:@"common_checkbox_checked"]; + CMModel.checkboxWH = 20; + + CMModel.privacyColor = ThemeTextColor; + CMModel.appPrivacyText = @"登录即代表同意《默认》,并授权音游获取本机号码。"; + CMModel.privacyState = YES; + CMModel.privacyFont = [UIFont systemFontOfSize:12]; + CMModel.protocolColor = ThemeDefaultColor; + + CMModel.backActionBlock = ^{ //点击了返回按钮 + [XCHUDTool hideHUD]; + }; + dispatch_main_sync_safe(^{ + [[NTESQuickLoginManager sharedInstance] setupModel:CMModel]; + }); +} + +- (UIImageView *)appIcon { + if (!_appIcon) { + _appIcon = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"login_appIcon"]]; + } + return _appIcon; +} + +- (UILabel *)titleLabel{ + if (!_titleLabel) { + _titleLabel = [[UILabel alloc] init]; + _titleLabel.font = [UIFont fontWithName:@"PingFang-SC-Medium" size:18]; + _titleLabel.text = AppName; + _titleLabel.textColor = ThemeTextColor; + } + return _titleLabel; +} + +- (UILabel *)subTitleLabel { + if (!_subTitleLabel) { + _subTitleLabel = [[UILabel alloc] init]; + _subTitleLabel.font = [UIFont systemFontOfSize:13]; + _subTitleLabel.text = @"来音游打游戏,争夺更多赏金!"; + _subTitleLabel.textColor = ThemeTextColor; + _subTitleLabel.textAlignment = NSTextAlignmentLeft; + } + return _subTitleLabel; +} + +- (UIButton *)loginButton{ + if (!_loginButton) { + _loginButton = [UIButton buttonWithType:UIButtonTypeCustom]; + _loginButton.layer.masksToBounds = YES; + _loginButton.layer.cornerRadius = 45/2.f; + [_loginButton setTitle:@"本机号码一键登录" forState:UIControlStateNormal]; + [_loginButton setTitle:@"本机号码一键登录" forState:UIControlStateSelected]; + _loginButton.titleLabel.font = [UIFont systemFontOfSize:18 weight:UIFontWeightMedium]; + [_loginButton setTitleColor:UIColor.whiteColor forState:UIControlStateNormal]; + UIImage *image = [UIImage gradientColorImageFromColors:@[ThemeButtonGradientStartColor,ThemeButtonGradientEndColor] gradientType:GradientTypeLeftToRight imgSize:CGSizeMake(KScreenWidth - 52 * 2, 45)]; + [_loginButton setBackgroundImage:image forState:UIControlStateNormal]; + } + return _loginButton; +} + +- (UIButton *)agreeButton { + if (!_agreeButton) { + _agreeButton = [UIButton buttonWithType:UIButtonTypeCustom]; + [_agreeButton setImage:[UIImage imageNamed:@"common_checkbox_uncheck"] forState:UIControlStateNormal]; + [_agreeButton setImage:[UIImage imageNamed:@"common_checkbox_checked"] forState:UIControlStateSelected]; + } + return _agreeButton; +} + +- (YYLabel *)agreeLabel { + if (!_agreeLabel) { + _agreeLabel = [[YYLabel alloc] init]; + _agreeLabel.font = [UIFont systemFontOfSize:12]; + _agreeLabel.numberOfLines = 0; + _agreeLabel.attributedText = [self privacyAttributedStr]; + _agreeLabel.textAlignment = NSTextAlignmentCenter; + [_agreeLabel sizeToFit]; + } + return _agreeLabel; +} + +- (NSMutableAttributedString *)privacyAttributedStr{ + NSMutableAttributedString *attString = [[NSMutableAttributedString alloc] initWithString:@"登录即代表同意"]; + attString.yy_color = ThemeTextColor; + NSString *userString = @"《用户服务协议》"; + NSMutableAttributedString *userAttString = [[NSMutableAttributedString alloc] initWithString:userString attributes:@{NSForegroundColorAttributeName:ThemeDefaultColor}]; + @weakify(self) + [userAttString yy_setTextHighlightRange:NSMakeRange(0, userAttString.length) color:nil backgroundColor:nil userInfo:nil tapAction:^(UIView * _Nonnull containerView, NSAttributedString * _Nonnull text, NSRange range, CGRect rect) { + @strongify(self); + // 跳转用户协议 + // [self goToWebview:HtmlUrlKey(kUserProtocalURL)]; + } longPressAction:nil]; + + NSMutableAttributedString *andString = [[NSMutableAttributedString alloc] initWithString:@"和"]; + andString.yy_color = ThemeTextColor; + NSString *protocolString = @"《隐私政策》"; + NSMutableAttributedString *privateString = [[NSMutableAttributedString alloc] initWithString:protocolString attributes:@{NSForegroundColorAttributeName:ThemeDefaultColor}]; + [privateString yy_setTextHighlightRange:NSMakeRange(0, privateString.length) color:nil backgroundColor:nil userInfo:nil tapAction:^(UIView * _Nonnull containerView, NSAttributedString * _Nonnull text, NSRange range, CGRect rect) { + @strongify(self); + // 跳转隐私政策 + // [self goToWebview:HtmlUrlKey(kPrivacyURL)]; + } longPressAction:nil]; + [attString appendAttributedString:userAttString]; + [attString appendAttributedString:andString]; + [attString appendAttributedString:privateString]; + return attString; +} + +- (UIImageView *)authBubbleView { + if (!_authBubbleView) { + _authBubbleView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"login_auth_bubble"]]; + _authBubbleView.contentMode = UIViewContentModeScaleAspectFit; + _authBubbleView.alpha = 0; + } + return _authBubbleView; +} + +- (UILabel *)authBubbleLabel { + if (!_authBubbleLabel) { + _authBubbleLabel = [[UILabel alloc] init]; + _authBubbleLabel.text = @"同意隐私政策和用户协议后,才可以注册登录哦~"; + _authBubbleLabel.font = [UIFont systemFontOfSize:10]; + _authBubbleLabel.textColor = UIColor.whiteColor; + } + return _authBubbleLabel; +} + +- (UIView *)contentView { + if (!_contentView) { + _contentView = [[UIView alloc] init]; + } + return _contentView; +} + +- (UIStackView *)stackView { + if (!_stackView) { + _stackView = [[UIStackView alloc] init]; + _stackView.distribution = UIStackViewDistributionEqualSpacing; + _stackView.spacing = 43; + _stackView.axis = UILayoutConstraintAxisHorizontal; + _stackView.alignment = UIStackViewAlignmentFill; + } + return _stackView; +} + +- (LLButtonView *)phoneButtonView { + if (!_phoneButtonView) { + _phoneButtonView = [[LLButtonView alloc] init]; + _phoneButtonView.logoImageView.image = [UIImage imageNamed:@"login_phone"]; + _phoneButtonView.titleLabel.text = @"手机"; + } + return _phoneButtonView; +} + +- (LLButtonView *)qqButtonView { + if (!_qqButtonView) { + _qqButtonView = [[LLButtonView alloc] init]; + _qqButtonView.logoImageView.image = [UIImage imageNamed:@"login_qq"]; + _qqButtonView.titleLabel.text = @"QQ"; + } + return _qqButtonView; +} + +- (LLButtonView *)wxButtonView { + if (!_wxButtonView) { + _wxButtonView = [[LLButtonView alloc] init]; + _wxButtonView.logoImageView.image = [UIImage imageNamed:@"login_wechat"]; + _wxButtonView.titleLabel.text = @"微信"; + } + return _wxButtonView; +} + +@end diff --git a/xplan-ios/Main/MVP/Api/Api.h b/xplan-ios/Main/MVP/Api/Api.h new file mode 100644 index 00000000..ab7f6768 --- /dev/null +++ b/xplan-ios/Main/MVP/Api/Api.h @@ -0,0 +1,25 @@ +// +// Api.h +// xplan-ios +// +// Created by zu on 2021/9/6. +// + +#import +#import "HttpRequestHelper.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface Api : NSObject + +/** + route : 接口路径 + method : http 请求方法(POST、GET等) + completion : 网络请求完成的回调 + ... : 可变参数,第一个为 __FUNCTION__ ,接下来依次为该接口的协议参数 + */ ++ (void)makeRequest:(NSString *)route method:(HttpRequestHelperMethod)method completion:(HttpRequestHelperCompletion)completion, ...; + +@end + +NS_ASSUME_NONNULL_END diff --git a/xplan-ios/Main/MVP/Api/Api.m b/xplan-ios/Main/MVP/Api/Api.m new file mode 100644 index 00000000..92dcc41f --- /dev/null +++ b/xplan-ios/Main/MVP/Api/Api.m @@ -0,0 +1,39 @@ +// +// Api.m +// xplan-ios +// +// Created by zu on 2021/9/6. +// + +#import "Api.h" + +@implementation Api + ++ (void)makeRequest:(NSString *)route method:(HttpRequestHelperMethod)method completion:(HttpRequestHelperCompletion)completion, ... { + va_list arg_lists; + va_start(arg_lists, completion); + + // 获取第一个参数 __FUNCTION__ ,然后解析出来 key 。 + const char *functionName = va_arg(arg_lists, const char *); + NSString *fn = [[NSString alloc] initWithUTF8String:functionName]; + // NSLog 一下 __FUNCTION__ 就知道为什么这么截取了。 + NSRange blankRange = [fn rangeOfString:@":"]; + NSUInteger start = blankRange.location + 1; + NSUInteger length = fn.length - start - 2; + NSString *fromatParamKeys = [fn substringWithRange:NSMakeRange(start, length)]; + + // 构造请求的 NSMutableDictionary *params 。 + NSMutableDictionary *params = [NSMutableDictionary dictionary]; + + NSArray *paramKeys = [fromatParamKeys componentsSeparatedByString:@":"]; + NSEnumerator *enumerator = [paramKeys objectEnumerator]; + NSString *value = nil; + while((value = va_arg(arg_lists, NSString*))){ + [params setValue:value forKey:enumerator.nextObject]; + }; + va_end(arg_lists); + + [HttpRequestHelper request:route method:method params:params completion:completion]; +} + +@end diff --git a/xplan-ios/Main/MVP/Presenter/BaseMvpPresenter.h b/xplan-ios/Main/MVP/Presenter/BaseMvpPresenter.h new file mode 100644 index 00000000..69307d88 --- /dev/null +++ b/xplan-ios/Main/MVP/Presenter/BaseMvpPresenter.h @@ -0,0 +1,26 @@ +// +// BaseMvpPresenter.h +// xplan-ios +// +// Created by zu on 2021/8/31. +// + +#import +#import "BaseMvpProtocol.h" +#import "HttpRequestHelper.h" + +NS_ASSUME_NONNULL_BEGIN + +typedef void(^HttpSuccess)(id _Nonnull data); +typedef void(^HttpFail)(NSNumber * _Nonnull code, NSString * _Nullable msg); + +@interface BaseMvpPresenter : NSObject + +- (void)attatchView:(id)view; +- (id)getView; +- (HttpRequestHelperCompletion)createHttpCompletion:(HttpSuccess)success fail:(HttpFail)fail showLoading:(BOOL)loading errorToast:(BOOL)toast; +- (void)detatchView; + +@end + +NS_ASSUME_NONNULL_END diff --git a/xplan-ios/Main/MVP/Presenter/BaseMvpPresenter.m b/xplan-ios/Main/MVP/Presenter/BaseMvpPresenter.m new file mode 100644 index 00000000..974593e3 --- /dev/null +++ b/xplan-ios/Main/MVP/Presenter/BaseMvpPresenter.m @@ -0,0 +1,60 @@ +// +// BaseMvpPresenter.m +// xplan-ios +// +// Created by zu on 2021/8/31. +// + +#import "BaseMvpPresenter.h" +#import "XCHUDTool.h" + +@interface BaseMvpPresenter() + +@property (nonatomic, weak) id view; + +@end + +@implementation BaseMvpPresenter + +- (void)attatchView:(id)view { + self.view = view; +} + +- (id)getView { + return self.view; +} + +- (HttpRequestHelperCompletion)createHttpCompletion:(HttpSuccess)success fail:(HttpFail)fail showLoading:(BOOL)loading errorToast:(BOOL)toast { + if (loading) { + [XCHUDTool showLoading]; + } + return ^(id _Nullable data, NSNumber * _Nonnull code, NSString * _Nullable msg) { + if (data != nil) { + NSString *message = [data valueForKey:@"message"]; + NSNumber *resCode = [data valueForKey:@"code"]; + switch (resCode.intValue) { + case 200: + success(data); + return; + + default: + if (toast) { + [XCHUDTool showErrorWithMessage:msg]; + } + fail(resCode, message); + return; + } + } + + if (toast) { + [XCHUDTool showErrorWithMessage:msg]; + } + fail(code, msg); + }; +} + +- (void)detatchView { + +} + +@end diff --git a/xplan-ios/Main/MVP/Protocol/BaseMvpProtocol.h b/xplan-ios/Main/MVP/Protocol/BaseMvpProtocol.h new file mode 100644 index 00000000..89707a97 --- /dev/null +++ b/xplan-ios/Main/MVP/Protocol/BaseMvpProtocol.h @@ -0,0 +1,19 @@ +// +// BaseMvpProtocol.h +// xplan-ios +// +// Created by zu on 2021/8/31. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@protocol BaseMvpProtocol + +- (void)tokenInvalid; +- (void)completeUserInfo; + +@end + +NS_ASSUME_NONNULL_END diff --git a/xplan-ios/Main/MVP/View/MvpViewController.h b/xplan-ios/Main/MVP/View/MvpViewController.h new file mode 100644 index 00000000..27a404cc --- /dev/null +++ b/xplan-ios/Main/MVP/View/MvpViewController.h @@ -0,0 +1,21 @@ +// +// MvpViewController.h +// xplan-ios +// +// Created by zu on 2021/8/31. +// + +#import "BaseViewController.h" +#import "BaseMvpPresenter.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface MvpViewController : BaseViewController + +@property (nonatomic,strong) __kindof T presenter; + +- (__kindof T)createPresenter; + +@end + +NS_ASSUME_NONNULL_END diff --git a/xplan-ios/Main/MVP/View/MvpViewController.m b/xplan-ios/Main/MVP/View/MvpViewController.m new file mode 100644 index 00000000..39a6314b --- /dev/null +++ b/xplan-ios/Main/MVP/View/MvpViewController.m @@ -0,0 +1,45 @@ +// +// MvpViewController.m +// xplan-ios +// +// Created by zu on 2021/8/31. +// + +#import "MvpViewController.h" +#import "BaseMvpProtocol.h" +#import "BaseMvpPresenter.h" +#import "LoginViewController.h" + +@interface MvpViewController () + +@end + +@implementation MvpViewController + +- (__kindof id)presenter { + if (_presenter == nil) { + _presenter = [self createPresenter]; + [_presenter attatchView:self]; + } + return _presenter; +} + +- (__kindof id)createPresenter { + return [[BaseMvpPresenter alloc] init]; +} + +- (void)viewDidLoad { + [super viewDidLoad]; +} + +- (void)tokenInvalid { + LoginViewController *loginVC = [[LoginViewController alloc] init]; + [self.navigationController pushViewController:loginVC animated:NO]; +} + +- (void)completeUserInfo { + // TODO:完善用户信息 +} + + +@end diff --git a/xplan-ios/Main/TabbarViewController.h b/xplan-ios/Main/TabbarViewController.h new file mode 100644 index 00000000..e9c9b71a --- /dev/null +++ b/xplan-ios/Main/TabbarViewController.h @@ -0,0 +1,13 @@ +// +// ViewController.h +// xplan-ios +// +// Created by zu on 2021/8/31. +// + +#import "MvpViewController.h" + +@interface TabbarViewController : MvpViewController + +@end + diff --git a/xplan-ios/Main/TabbarViewController.m b/xplan-ios/Main/TabbarViewController.m new file mode 100644 index 00000000..0824294e --- /dev/null +++ b/xplan-ios/Main/TabbarViewController.m @@ -0,0 +1,22 @@ +// +// ViewController.m +// xplan-ios +// +// Created by zu on 2021/8/31. +// + +#import "TabbarViewController.h" +#import "LoginViewController.h" + +@interface TabbarViewController () + +@end + +@implementation TabbarViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + [self tokenInvalid]; +} + +@end diff --git a/xplan-ios/main.m b/xplan-ios/main.m new file mode 100644 index 00000000..e3a77ad5 --- /dev/null +++ b/xplan-ios/main.m @@ -0,0 +1,18 @@ +// +// main.m +// xplan-ios +// +// Created by apple on 2021/8/31. +// + +#import +#import "AppDelegate.h" + +int main(int argc, char * argv[]) { + NSString * appDelegateClassName; + @autoreleasepool { + // Setup code that might create autoreleased objects goes here. + appDelegateClassName = NSStringFromClass([AppDelegate class]); + } + return UIApplicationMain(argc, argv, nil, appDelegateClassName); +}