Commit 20a47128 cgx

微信登录接口、账户与资料UI

1 个父辈 9ada19e9
正在显示 67 个修改的文件 包含 4507 行增加40 行删除
...@@ -34,6 +34,9 @@ ...@@ -34,6 +34,9 @@
D0506B1828054ECD00229278 /* SafeHelperCollectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = D0506B1728054ECD00229278 /* SafeHelperCollectionView.m */; }; D0506B1828054ECD00229278 /* SafeHelperCollectionView.m in Sources */ = {isa = PBXBuildFile; fileRef = D0506B1728054ECD00229278 /* SafeHelperCollectionView.m */; };
D0506B1B2805500E00229278 /* SafeHelperCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = D0506B1A2805500E00229278 /* SafeHelperCollectionViewCell.m */; }; D0506B1B2805500E00229278 /* SafeHelperCollectionViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = D0506B1A2805500E00229278 /* SafeHelperCollectionViewCell.m */; };
D070509A28071BAF006D72E1 /* DSNetworkTool.m in Sources */ = {isa = PBXBuildFile; fileRef = D070509928071BAF006D72E1 /* DSNetworkTool.m */; }; D070509A28071BAF006D72E1 /* DSNetworkTool.m in Sources */ = {isa = PBXBuildFile; fileRef = D070509928071BAF006D72E1 /* DSNetworkTool.m */; };
D07A4B27280E9BAA00BA0EC0 /* AccountController.m in Sources */ = {isa = PBXBuildFile; fileRef = D07A4B26280E9BAA00BA0EC0 /* AccountController.m */; };
D07A4B2A280EA6B600BA0EC0 /* UserInfoTableView.m in Sources */ = {isa = PBXBuildFile; fileRef = D07A4B29280EA6B600BA0EC0 /* UserInfoTableView.m */; };
D07A4B2D280EA73B00BA0EC0 /* UserInfoView.m in Sources */ = {isa = PBXBuildFile; fileRef = D07A4B2C280EA73B00BA0EC0 /* UserInfoView.m */; };
D07F9DE927F4683B0036372F /* DKColorTable.txt in Resources */ = {isa = PBXBuildFile; fileRef = D07F9DE827F4683B0036372F /* DKColorTable.txt */; }; D07F9DE927F4683B0036372F /* DKColorTable.txt in Resources */ = {isa = PBXBuildFile; fileRef = D07F9DE827F4683B0036372F /* DKColorTable.txt */; };
D0878F48280BD18F005F1B7F /* LoginView.m in Sources */ = {isa = PBXBuildFile; fileRef = D0878F47280BD18F005F1B7F /* LoginView.m */; }; D0878F48280BD18F005F1B7F /* LoginView.m in Sources */ = {isa = PBXBuildFile; fileRef = D0878F47280BD18F005F1B7F /* LoginView.m */; };
D0878F4C280BEFF9005F1B7F /* UILabel+YBAttributeTextTapAction.m in Sources */ = {isa = PBXBuildFile; fileRef = D0878F4B280BEFF9005F1B7F /* UILabel+YBAttributeTextTapAction.m */; }; D0878F4C280BEFF9005F1B7F /* UILabel+YBAttributeTextTapAction.m in Sources */ = {isa = PBXBuildFile; fileRef = D0878F4B280BEFF9005F1B7F /* UILabel+YBAttributeTextTapAction.m */; };
...@@ -43,6 +46,9 @@ ...@@ -43,6 +46,9 @@
D0930F122801124E006B497A /* BaseNaviController.m in Sources */ = {isa = PBXBuildFile; fileRef = D0930F112801124E006B497A /* BaseNaviController.m */; }; D0930F122801124E006B497A /* BaseNaviController.m in Sources */ = {isa = PBXBuildFile; fileRef = D0930F112801124E006B497A /* BaseNaviController.m */; };
D0930F1A2801874B006B497A /* UIViewController+Swizzling.m in Sources */ = {isa = PBXBuildFile; fileRef = D0930F152801874B006B497A /* UIViewController+Swizzling.m */; }; D0930F1A2801874B006B497A /* UIViewController+Swizzling.m in Sources */ = {isa = PBXBuildFile; fileRef = D0930F152801874B006B497A /* UIViewController+Swizzling.m */; };
D0930F1B2801874B006B497A /* UINavigationController+Pop.m in Sources */ = {isa = PBXBuildFile; fileRef = D0930F182801874B006B497A /* UINavigationController+Pop.m */; }; D0930F1B2801874B006B497A /* UINavigationController+Pop.m in Sources */ = {isa = PBXBuildFile; fileRef = D0930F182801874B006B497A /* UINavigationController+Pop.m */; };
D09BF331280E570D00E5F06C /* UserModel.m in Sources */ = {isa = PBXBuildFile; fileRef = D09BF330280E570D00E5F06C /* UserModel.m */; };
D09BF337280E605900E5F06C /* NSObject+Extras.m in Sources */ = {isa = PBXBuildFile; fileRef = D09BF336280E605900E5F06C /* NSObject+Extras.m */; };
D09BF33A280E94E300E5F06C /* LoginUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = D09BF339280E94E300E5F06C /* LoginUtils.m */; };
D09D0E96280D3FE9008DEDAB /* NSDate+Extras.m in Sources */ = {isa = PBXBuildFile; fileRef = D09D0E95280D3FE9008DEDAB /* NSDate+Extras.m */; }; D09D0E96280D3FE9008DEDAB /* NSDate+Extras.m in Sources */ = {isa = PBXBuildFile; fileRef = D09D0E95280D3FE9008DEDAB /* NSDate+Extras.m */; };
D09D0E9A280D507F008DEDAB /* ProfileAlertView.m in Sources */ = {isa = PBXBuildFile; fileRef = D09D0E99280D507F008DEDAB /* ProfileAlertView.m */; }; D09D0E9A280D507F008DEDAB /* ProfileAlertView.m in Sources */ = {isa = PBXBuildFile; fileRef = D09D0E99280D507F008DEDAB /* ProfileAlertView.m */; };
D09D0E9D280D73B6008DEDAB /* InviteController.m in Sources */ = {isa = PBXBuildFile; fileRef = D09D0E9C280D73B6008DEDAB /* InviteController.m */; }; D09D0E9D280D73B6008DEDAB /* InviteController.m in Sources */ = {isa = PBXBuildFile; fileRef = D09D0E9C280D73B6008DEDAB /* InviteController.m */; };
...@@ -136,6 +142,12 @@ ...@@ -136,6 +142,12 @@
D0506B1A2805500E00229278 /* SafeHelperCollectionViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SafeHelperCollectionViewCell.m; sourceTree = "<group>"; }; D0506B1A2805500E00229278 /* SafeHelperCollectionViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SafeHelperCollectionViewCell.m; sourceTree = "<group>"; };
D070509828071BAF006D72E1 /* DSNetworkTool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DSNetworkTool.h; sourceTree = "<group>"; }; D070509828071BAF006D72E1 /* DSNetworkTool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DSNetworkTool.h; sourceTree = "<group>"; };
D070509928071BAF006D72E1 /* DSNetworkTool.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DSNetworkTool.m; sourceTree = "<group>"; }; D070509928071BAF006D72E1 /* DSNetworkTool.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DSNetworkTool.m; sourceTree = "<group>"; };
D07A4B25280E9BAA00BA0EC0 /* AccountController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AccountController.h; sourceTree = "<group>"; };
D07A4B26280E9BAA00BA0EC0 /* AccountController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AccountController.m; sourceTree = "<group>"; };
D07A4B28280EA6B600BA0EC0 /* UserInfoTableView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UserInfoTableView.h; sourceTree = "<group>"; };
D07A4B29280EA6B600BA0EC0 /* UserInfoTableView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UserInfoTableView.m; sourceTree = "<group>"; };
D07A4B2B280EA73B00BA0EC0 /* UserInfoView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UserInfoView.h; sourceTree = "<group>"; };
D07A4B2C280EA73B00BA0EC0 /* UserInfoView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UserInfoView.m; sourceTree = "<group>"; };
D07F9D0627F45CB20036372F /* DKNightVersion.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DKNightVersion.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D07F9D0627F45CB20036372F /* DKNightVersion.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = DKNightVersion.framework; sourceTree = BUILT_PRODUCTS_DIR; };
D07F9DE827F4683B0036372F /* DKColorTable.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = DKColorTable.txt; sourceTree = "<group>"; }; D07F9DE827F4683B0036372F /* DKColorTable.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = DKColorTable.txt; sourceTree = "<group>"; };
D0878F46280BD18F005F1B7F /* LoginView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LoginView.h; sourceTree = "<group>"; }; D0878F46280BD18F005F1B7F /* LoginView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LoginView.h; sourceTree = "<group>"; };
...@@ -156,6 +168,12 @@ ...@@ -156,6 +168,12 @@
D0930F172801874B006B497A /* UIViewController+Swizzling.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIViewController+Swizzling.h"; sourceTree = "<group>"; }; D0930F172801874B006B497A /* UIViewController+Swizzling.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIViewController+Swizzling.h"; sourceTree = "<group>"; };
D0930F182801874B006B497A /* UINavigationController+Pop.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UINavigationController+Pop.m"; sourceTree = "<group>"; }; D0930F182801874B006B497A /* UINavigationController+Pop.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UINavigationController+Pop.m"; sourceTree = "<group>"; };
D0930F192801874B006B497A /* NaviBarHandlerProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NaviBarHandlerProtocol.h; sourceTree = "<group>"; }; D0930F192801874B006B497A /* NaviBarHandlerProtocol.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NaviBarHandlerProtocol.h; sourceTree = "<group>"; };
D09BF32F280E570D00E5F06C /* UserModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = UserModel.h; sourceTree = "<group>"; };
D09BF330280E570D00E5F06C /* UserModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = UserModel.m; sourceTree = "<group>"; };
D09BF335280E605900E5F06C /* NSObject+Extras.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSObject+Extras.h"; sourceTree = "<group>"; };
D09BF336280E605900E5F06C /* NSObject+Extras.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSObject+Extras.m"; sourceTree = "<group>"; };
D09BF338280E94E300E5F06C /* LoginUtils.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LoginUtils.h; sourceTree = "<group>"; };
D09BF339280E94E300E5F06C /* LoginUtils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LoginUtils.m; sourceTree = "<group>"; };
D09D0E94280D3FE9008DEDAB /* NSDate+Extras.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSDate+Extras.h"; sourceTree = "<group>"; }; D09D0E94280D3FE9008DEDAB /* NSDate+Extras.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSDate+Extras.h"; sourceTree = "<group>"; };
D09D0E95280D3FE9008DEDAB /* NSDate+Extras.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSDate+Extras.m"; sourceTree = "<group>"; }; D09D0E95280D3FE9008DEDAB /* NSDate+Extras.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSDate+Extras.m"; sourceTree = "<group>"; };
D09D0E98280D507F008DEDAB /* ProfileAlertView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ProfileAlertView.h; sourceTree = "<group>"; }; D09D0E98280D507F008DEDAB /* ProfileAlertView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ProfileAlertView.h; sourceTree = "<group>"; };
...@@ -312,6 +330,8 @@ ...@@ -312,6 +330,8 @@
D091BBBA2809079600487A50 /* NSString+Extras.m */, D091BBBA2809079600487A50 /* NSString+Extras.m */,
D09D0E94280D3FE9008DEDAB /* NSDate+Extras.h */, D09D0E94280D3FE9008DEDAB /* NSDate+Extras.h */,
D09D0E95280D3FE9008DEDAB /* NSDate+Extras.m */, D09D0E95280D3FE9008DEDAB /* NSDate+Extras.m */,
D09BF335280E605900E5F06C /* NSObject+Extras.h */,
D09BF336280E605900E5F06C /* NSObject+Extras.m */,
); );
path = Category; path = Category;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -395,6 +415,12 @@ ...@@ -395,6 +415,12 @@
D09D0E99280D507F008DEDAB /* ProfileAlertView.m */, D09D0E99280D507F008DEDAB /* ProfileAlertView.m */,
D09D0E9B280D73B6008DEDAB /* InviteController.h */, D09D0E9B280D73B6008DEDAB /* InviteController.h */,
D09D0E9C280D73B6008DEDAB /* InviteController.m */, D09D0E9C280D73B6008DEDAB /* InviteController.m */,
D07A4B25280E9BAA00BA0EC0 /* AccountController.h */,
D07A4B26280E9BAA00BA0EC0 /* AccountController.m */,
D07A4B2B280EA73B00BA0EC0 /* UserInfoView.h */,
D07A4B2C280EA73B00BA0EC0 /* UserInfoView.m */,
D07A4B28280EA6B600BA0EC0 /* UserInfoTableView.h */,
D07A4B29280EA6B600BA0EC0 /* UserInfoTableView.m */,
); );
path = Profile; path = Profile;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -511,8 +537,12 @@ ...@@ -511,8 +537,12 @@
D0CE3D80280A532D00D8B02D /* UserLogin */ = { D0CE3D80280A532D00D8B02D /* UserLogin */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
D09BF338280E94E300E5F06C /* LoginUtils.h */,
D09BF339280E94E300E5F06C /* LoginUtils.m */,
D0878F46280BD18F005F1B7F /* LoginView.h */, D0878F46280BD18F005F1B7F /* LoginView.h */,
D0878F47280BD18F005F1B7F /* LoginView.m */, D0878F47280BD18F005F1B7F /* LoginView.m */,
D09BF32F280E570D00E5F06C /* UserModel.h */,
D09BF330280E570D00E5F06C /* UserModel.m */,
D0878F4D280C087E005F1B7F /* LoginController.h */, D0878F4D280C087E005F1B7F /* LoginController.h */,
D0878F4E280C087E005F1B7F /* LoginController.m */, D0878F4E280C087E005F1B7F /* LoginController.m */,
); );
...@@ -791,6 +821,7 @@ ...@@ -791,6 +821,7 @@
D0C09EE028007E5F00709D4C /* BreatheController.m in Sources */, D0C09EE028007E5F00709D4C /* BreatheController.m in Sources */,
D0C50B4627FD66FB00DC68F0 /* DSConstUtil.m in Sources */, D0C50B4627FD66FB00DC68F0 /* DSConstUtil.m in Sources */,
D01814D227FFCBAF00583D4E /* CWFlowLayout.m in Sources */, D01814D227FFCBAF00583D4E /* CWFlowLayout.m in Sources */,
D07A4B2D280EA73B00BA0EC0 /* UserInfoView.m in Sources */,
D0878F48280BD18F005F1B7F /* LoginView.m in Sources */, D0878F48280BD18F005F1B7F /* LoginView.m in Sources */,
D0E660052807D02C006562F2 /* UserRequestModel.m in Sources */, D0E660052807D02C006562F2 /* UserRequestModel.m in Sources */,
D0F82286280C44D10039F586 /* SetTableView.m in Sources */, D0F82286280C44D10039F586 /* SetTableView.m in Sources */,
...@@ -801,23 +832,28 @@ ...@@ -801,23 +832,28 @@
D0878F4C280BEFF9005F1B7F /* UILabel+YBAttributeTextTapAction.m in Sources */, D0878F4C280BEFF9005F1B7F /* UILabel+YBAttributeTextTapAction.m in Sources */,
D0F80907280431100097899F /* UILabel+Extras.m in Sources */, D0F80907280431100097899F /* UILabel+Extras.m in Sources */,
D04B3DAC27F6EEB50022F8DF /* DSTabBarController.m in Sources */, D04B3DAC27F6EEB50022F8DF /* DSTabBarController.m in Sources */,
D09BF331280E570D00E5F06C /* UserModel.m in Sources */,
D0C50B4F27FD832300DC68F0 /* LeadView.m in Sources */, D0C50B4F27FD832300DC68F0 /* LeadView.m in Sources */,
D091BBBB2809079600487A50 /* NSString+Extras.m in Sources */, D091BBBB2809079600487A50 /* NSString+Extras.m in Sources */,
D01814EB28002A8200583D4E /* DsMiddleView.m in Sources */, D01814EB28002A8200583D4E /* DsMiddleView.m in Sources */,
D0C50B4227FD39C800DC68F0 /* UIViewController+FullScreenModal.m in Sources */, D0C50B4227FD39C800DC68F0 /* UIViewController+FullScreenModal.m in Sources */,
D09BF337280E605900E5F06C /* NSObject+Extras.m in Sources */,
D027EE2927FB51D0004BBA61 /* DSGifHeader.m in Sources */, D027EE2927FB51D0004BBA61 /* DSGifHeader.m in Sources */,
D01814EE28002DC700583D4E /* HeaderDataModel.m in Sources */, D01814EE28002DC700583D4E /* HeaderDataModel.m in Sources */,
D0506B1B2805500E00229278 /* SafeHelperCollectionViewCell.m in Sources */, D0506B1B2805500E00229278 /* SafeHelperCollectionViewCell.m in Sources */,
D01814DC27FFD92200583D4E /* DSDataSource.m in Sources */, D01814DC27FFD92200583D4E /* DSDataSource.m in Sources */,
D0B5ECC827F2E97A003EDFE3 /* MacroFuncUtil.m in Sources */, D0B5ECC827F2E97A003EDFE3 /* MacroFuncUtil.m in Sources */,
D01814E227FFDBB800583D4E /* HomeHeaderView.m in Sources */, D01814E227FFDBB800583D4E /* HomeHeaderView.m in Sources */,
D09BF33A280E94E300E5F06C /* LoginUtils.m in Sources */,
D0F808F52803D4E70097899F /* Track.m in Sources */, D0F808F52803D4E70097899F /* Track.m in Sources */,
D0E65FFC2807A654006562F2 /* NSSet+HYBUnicodeReadable.m in Sources */, D0E65FFC2807A654006562F2 /* NSSet+HYBUnicodeReadable.m in Sources */,
D0C50B3C27FD2EFD00DC68F0 /* PrivacyViewController.m in Sources */, D0C50B3C27FD2EFD00DC68F0 /* PrivacyViewController.m in Sources */,
D07A4B2A280EA6B600BA0EC0 /* UserInfoTableView.m in Sources */,
D0930F122801124E006B497A /* BaseNaviController.m in Sources */, D0930F122801124E006B497A /* BaseNaviController.m in Sources */,
D0878F4F280C087E005F1B7F /* LoginController.m in Sources */, D0878F4F280C087E005F1B7F /* LoginController.m in Sources */,
D0930F1A2801874B006B497A /* UIViewController+Swizzling.m in Sources */, D0930F1A2801874B006B497A /* UIViewController+Swizzling.m in Sources */,
D0F808FE280421C20097899F /* ThreeMinuteController.m in Sources */, D0F808FE280421C20097899F /* ThreeMinuteController.m in Sources */,
D07A4B27280E9BAA00BA0EC0 /* AccountController.m in Sources */,
D01814E8280020F900583D4E /* CWPageControl.m in Sources */, D01814E8280020F900583D4E /* CWPageControl.m in Sources */,
D027EE3027FB52DA004BBA61 /* UIImage+Extras.m in Sources */, D027EE3027FB52DA004BBA61 /* UIImage+Extras.m in Sources */,
D0506B1828054ECD00229278 /* SafeHelperCollectionView.m in Sources */, D0506B1828054ECD00229278 /* SafeHelperCollectionView.m in Sources */,
...@@ -884,6 +920,7 @@ ...@@ -884,6 +920,7 @@
DEBUG_INFORMATION_FORMAT = dwarf; DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES; ENABLE_TESTABILITY = YES;
EXCLUDED_ARCHS = "";
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO; GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES; GCC_NO_COMMON_BLOCKS = YES;
...@@ -901,7 +938,7 @@ ...@@ -901,7 +938,7 @@
IPHONEOS_DEPLOYMENT_TARGET = 11.0; IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
}; };
name = Debug; name = Debug;
...@@ -943,6 +980,7 @@ ...@@ -943,6 +980,7 @@
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO; ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
EXCLUDED_ARCHS = "";
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES; GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
...@@ -954,7 +992,7 @@ ...@@ -954,7 +992,7 @@
IPHONEOS_DEPLOYMENT_TARGET = 11.0; IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
VALIDATE_PRODUCT = YES; VALIDATE_PRODUCT = YES;
}; };
...@@ -1094,6 +1132,7 @@ ...@@ -1094,6 +1132,7 @@
DEBUG_INFORMATION_FORMAT = dwarf; DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES; ENABLE_TESTABILITY = YES;
EXCLUDED_ARCHS = "";
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO; GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES; GCC_NO_COMMON_BLOCKS = YES;
...@@ -1111,7 +1150,7 @@ ...@@ -1111,7 +1150,7 @@
IPHONEOS_DEPLOYMENT_TARGET = 11.0; IPHONEOS_DEPLOYMENT_TARGET = 11.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = NO;
SDKROOT = iphoneos; SDKROOT = iphoneos;
}; };
name = Beta; name = Beta;
...@@ -1125,8 +1164,8 @@ ...@@ -1125,8 +1164,8 @@
ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO; ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS = NO;
Bundle_Display_Name = "小梦睡眠-Beta"; Bundle_Display_Name = "小梦睡眠-Beta";
CODE_SIGN_ENTITLEMENTS = DreamSleep/DreamSleepBeta.entitlements; CODE_SIGN_ENTITLEMENTS = DreamSleep/DreamSleepBeta.entitlements;
CODE_SIGN_IDENTITY = "iPhone Distribution"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Manual; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 4NDZ6UX8PW; DEVELOPMENT_TEAM = 4NDZ6UX8PW;
GCC_PREFIX_HEADER = "${SRCROOT}/DreamSleep/Utils/PrefixHeader.pch"; GCC_PREFIX_HEADER = "${SRCROOT}/DreamSleep/Utils/PrefixHeader.pch";
...@@ -1159,7 +1198,7 @@ ...@@ -1159,7 +1198,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.aolei.sleep; PRODUCT_BUNDLE_IDENTIFIER = com.aolei.sleep;
PRODUCT_MODULE_NAME = DreamSleepBeta; PRODUCT_MODULE_NAME = DreamSleepBeta;
PRODUCT_NAME = "$(TARGET_NAME)Beta"; PRODUCT_NAME = "$(TARGET_NAME)Beta";
PROVISIONING_PROFILE_SPECIFIER = xiaomengSleep_adhot_profile; PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_EMIT_LOC_STRINGS = YES;
TARGETED_DEVICE_FAMILY = 1; TARGETED_DEVICE_FAMILY = 1;
}; };
......
//
// NSObject+Extras.h
// DreamSleep
//
// Created by peter on 2022/4/19.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface NSObject (Extras)
@end
NS_ASSUME_NONNULL_END
//
// NSObject+Extras.m
// DreamSleep
//
// Created by peter on 2022/4/19.
//
#import "NSObject+Extras.h"
@implementation NSObject (Extras)
// 重写debugDescription, 而不是description
- (NSString *)debugDescription {
// 判断是否时NSArray 或者NSDictionary NSNumber 如果是的话直接返回 debugDescription
if ([self isKindOfClass:[NSArray class]] || [self isKindOfClass:[NSDictionary class]] || [self isKindOfClass:[NSString class]] || [self isKindOfClass:[NSNumber class]]) {
return [self debugDescription];
}
// 声明一个字典
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
// 得到当前class的所有属性
uint count;
objc_property_t *properties = class_copyPropertyList([self class], &count);
// 循环并用KVC得到每个属性的值
for (int i = 0; i<count; i++) {
objc_property_t property = properties[i];
NSString *name = @(property_getName(property));
id value = [self valueForKey:name]?:@"nil"; // 默认值为nil字符串
[dictionary setObject:value forKey:name]; // 装载到字典里
}
// 释放
free(properties);
//return
return [NSString stringWithFormat:@"<%@: %p> -- %@", [self class], self,dictionary];
}
@end
...@@ -38,6 +38,8 @@ NS_ASSUME_NONNULL_BEGIN ...@@ -38,6 +38,8 @@ NS_ASSUME_NONNULL_BEGIN
+ (instancetype)btnWithTitle:(NSString *)title titleColor:(UIColor *)titleColor font:(UIFont *)font bgColor:(UIColor *)bgColor; + (instancetype)btnWithTitle:(NSString *)title titleColor:(UIColor *)titleColor font:(UIFont *)font bgColor:(UIColor *)bgColor;
+ (instancetype)btnWithTitle:(NSString *)title font:(UIFont *)font; + (instancetype)btnWithTitle:(NSString *)title font:(UIFont *)font;
+ (instancetype)btnWithTitle:(NSString *)title titleColor:(UIColor *)titleColor font:(UIFont *)font;
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END
...@@ -80,4 +80,12 @@ ...@@ -80,4 +80,12 @@
return btn; return btn;
} }
+ (instancetype)btnWithTitle:(NSString *)title titleColor:(UIColor *)titleColor font:(UIFont *)font {
UIButton *btn = [UIButton new];
[btn setTitle:title forState:UIControlStateNormal];
[btn setTitleColor:titleColor forState:UIControlStateNormal];
[btn.titleLabel setFont:font];
return btn;
}
@end @end
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
DSLog(@"获取舒眠课程、助眠音乐列表数据接口dataDic:%@", responseObj); DSLog(@"获取舒眠课程、助眠音乐列表数据接口dataDic:%@", responseObj);
completion(requestModel); completion(requestModel);
} failure:^(id _Nonnull failureInfo) { } failure:^(id _Nonnull failureInfo) {
requestModel.errCode = DSErrCodeNetFail; requestModel.resCode = DSResCodeNetFail;
requestModel.errorInfo = failureInfo; requestModel.errorInfo = failureInfo;
completion(requestModel); completion(requestModel);
}]; }];
......
...@@ -90,8 +90,9 @@ ...@@ -90,8 +90,9 @@
// 授权登录的类 // 授权登录的类
if ([resp isKindOfClass:[SendAuthResp class]]) { if ([resp isKindOfClass:[SendAuthResp class]]) {
if (resp.errCode == 0) { if (resp.errCode == 0) {
SendAuthResp *resp2 = (SendAuthResp *)resp; SendAuthResp *authResp = (SendAuthResp *)resp;
DSLog(@"resp2.code:%@", resp2.code); DSLog(@"authResp.code:%@", authResp.code);
[[NSNotificationCenter defaultCenter] postNotificationName:WXLoginAuthNoti object:authResp.code];
} else { } else {
DSLog(@"error:%@", resp.errStr); DSLog(@"error:%@", resp.errStr);
} }
......
...@@ -5,17 +5,31 @@ ...@@ -5,17 +5,31 @@
// Created by peter on 2022/4/13. // Created by peter on 2022/4/13.
// //
typedef NS_ENUM(NSInteger, DSErrCode) { //typedef NS_ENUM(NSInteger, DSErrCode) {
// /** 网络故障 */
// DSErrCodeNetFail = -9,
// /** 业务逻辑成功 */
// DSErrCodeSuccess = -1,
// /** token验证失败 */
// DSErrCodeTokenFail = 401,
// /** 数据异常 */
// DSErrCodeDataWrong = -666,
// /** 异常数据错误,需要额外处理异常 */
// DSErrCodeNeedExtraDeal = 400022
//};
// 响应码
typedef NS_ENUM(NSInteger, DSResCode) {
/** 网络故障 */ /** 网络故障 */
DSErrCodeNetFail = -9, DSResCodeNetFail = -9,
/** 业务逻辑成功 */ /** 业务逻辑成功 */
DSErrCodeSuccess = -1, DSResCodeSuccess = 1,
/** token验证失败 */ /** 业务逻辑失败 */
DSErrCodeTokenFail = 401, DSResCodeFail = -1,
/** 数据异常 */ /** 数据异常 */
DSErrCodeDataWrong = -666, DSResCodeDataWrong = -666,
/** 异常数据错误,需要额外处理异常 */ /** 异常数据错误,需要额外处理异常 */
DSErrCodeNeedExtraDeal = 400022 DSResCodeNeedExtraDeal = 400022
}; };
#import <AFNetworking/AFNetworking-umbrella.h> #import <AFNetworking/AFNetworking-umbrella.h>
...@@ -23,8 +37,10 @@ typedef NS_ENUM(NSInteger, DSErrCode) { ...@@ -23,8 +37,10 @@ typedef NS_ENUM(NSInteger, DSErrCode) {
/// 网络请求二次封装基类 /// 网络请求二次封装基类
@interface DSNetworkTool : AFHTTPSessionManager @interface DSNetworkTool : AFHTTPSessionManager
/** 响应状态码 */
@property (nonatomic, assign) DSResCode resCode;
/** 错误状态码 */ /** 错误状态码 */
@property (nonatomic, assign) DSErrCode errCode; //@property (nonatomic, assign) DSErrCode errCode;
///** 返回的错误信息 */ ///** 返回的错误信息 */
@property (nonatomic, copy) NSString *errorInfo; @property (nonatomic, copy) NSString *errorInfo;
...@@ -43,6 +59,23 @@ typedef NS_ENUM(NSInteger, DSErrCode) { ...@@ -43,6 +59,23 @@ typedef NS_ENUM(NSInteger, DSErrCode) {
hasNetActivity:(BOOL)hasNetActivity hasNetActivity:(BOOL)hasNetActivity
loadingInfo:(NSString *)loadingInfo loadingInfo:(NSString *)loadingInfo
hasFailInfo:(BOOL)hasFailInfo hasFailInfo:(BOOL)hasFailInfo
success:(void (^)(id responseObj))success success:(void (^)(NSDictionary *responseObj))success
failure:(void (^)(id failureInfo))failure; failure:(void (^)(id failureInfo))failure;
/// @param api 请求接口
/// @param params get、post请求统一使用post body
/// @param view view
/// @param hasNetActivity 是否显示网络状态指示器
/// @param loadingInfo 网络加载信息
/// @param hasFailInfo 是否有失败信息
/// @param success success
/// @param failure failure
+ (__kindof NSURLSessionDataTask *)httpPostBodyRequestWithAPI:(NSString *)api
params:(id)params
view:(UIView *)view
hasNetActivity:(BOOL)hasNetActivity
loadingInfo:(NSString *)loadingInfo
hasFailInfo:(BOOL)hasFailInfo
success:(void (^)(NSDictionary *apiDic))success
failure:(void (^)(id failure))failure;
@end @end
...@@ -34,7 +34,7 @@ NSString * const NetworkUnableError = @"网络不给力,请检查您的网络 ...@@ -34,7 +34,7 @@ NSString * const NetworkUnableError = @"网络不给力,请检查您的网络
hasNetActivity:(BOOL)hasNetActivity hasNetActivity:(BOOL)hasNetActivity
loadingInfo:(NSString *)loadingInfo loadingInfo:(NSString *)loadingInfo
hasFailInfo:(BOOL)hasFailInfo hasFailInfo:(BOOL)hasFailInfo
success:(void (^)(id responseObj))success success:(void (^)(NSDictionary *responseObj))success
failure:(void (^)(id failureInfo))failure failure:(void (^)(id failureInfo))failure
{ {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:hasNetActivity]; [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:hasNetActivity];
...@@ -76,4 +76,57 @@ NSString * const NetworkUnableError = @"网络不给力,请检查您的网络 ...@@ -76,4 +76,57 @@ NSString * const NetworkUnableError = @"网络不给力,请检查您的网络
return dataTask; return dataTask;
} }
+ (__kindof NSURLSessionDataTask *)httpPostBodyRequestWithAPI:(NSString *)api
params:(id)params
view:(UIView *)view
hasNetActivity:(BOOL)hasNetActivity
loadingInfo:(NSString *)loadingInfo
hasFailInfo:(BOOL)hasFailInfo
success:(void (^)(NSDictionary *apiDic))success
failure:(void (^)(id failure))failure
{
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:hasNetActivity];
if (loadingInfo) {
view ? [DSProgressHUD showProgressHUDWithInfo:loadingInfo inView:view] : [DSProgressHUD showProgressHUDWithInfo:loadingInfo];
}
NSString *urlString = [ServerURL stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet URLQueryAllowedCharacterSet]];
DSLog(@"请求链接:%@,业务参数:%@", urlString, params);
NSMutableURLRequest *request = [[AFJSONRequestSerializer serializer] requestWithMethod:@"POST" URLString:urlString parameters:nil error:nil];
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:params options:NSJSONWritingPrettyPrinted error:nil];
[request setHTTPBody:jsonData];
[request addValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
NSURLSessionDataTask *dataTask = [[DSNetworkTool sharedManager] dataTaskWithRequest:request uploadProgress:nil downloadProgress:nil completionHandler:^(NSURLResponse * _Nonnull response, id _Nullable responseObject, NSError * _Nullable error) {
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
if (loadingInfo) { [DSProgressHUD dissmissProgressHUD]; }
if (responseObject) {
NSDictionary *responseDic = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingMutableContainers error:nil];
// DSLog(@"服务器返回的原始Json数据:%@", responseDic);
// 接口业务数据
NSString *apiStr = (api && [api isKindOfClass:[NSString class]]) ? api : @"";
NSDictionary *apiDataDic = responseDic[@"data"][apiStr];
if ([apiDataDic[@"res_code"] intValue] == DSResCodeSuccess) {
success(apiDataDic);
} else {
[DSProgressHUD showToast:apiDataDic[@"error"]];
return;
}
} else {
// 网络故障
DSLog(@"失败错误信息:%@", error);
if (hasFailInfo) {
NSString *alertStr = [NSString stringWithFormat:@"%ld", error.code];
view ? [DSProgressHUD showDetailInfo:alertStr inView:view] : [DSProgressHUD showDetailInfo:alertStr];
}
failure(error);
}
}];
[dataTask resume];
return dataTask;
}
@end @end
//
// AccountController.h
// DreamSleep
//
// Created by peter on 2022/4/19.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
/// 账户与资料页面
@interface AccountController : UIViewController
@end
NS_ASSUME_NONNULL_END
//
// AccountController.m
// DreamSleep
//
// Created by peter on 2022/4/19.
//
#import "AccountController.h"
#import "UserInfoView.h"
@interface AccountController ()
@property (nonatomic, strong) UserInfoView *userInfoView;
@end
@implementation AccountController
- (void)loadView {
self.view = self.userInfoView;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.navigationItem.title = @"账户与资料";
self.view.dk_backgroundColorPicker = DKColorPickerWithKey(VCViewBG);
UIButton *closeBtn = [UIButton btnWithTitle:@"注销账号" titleColor:DSWhite font:SysFont(15)];
[closeBtn addTarget:self action:@selector(closeUserAction) forControlEvents:UIControlEventTouchUpInside];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:closeBtn];
}
- (void)closeUserAction {
}
#pragma mark - 导航栏日间、黑夜模式
- (NaviStyle)navigationBarStyle {
return NaviStyleDefault;
}
- (UserInfoView *)userInfoView {
if (!_userInfoView) {
_userInfoView = [[UserInfoView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, kScreenHeight - kTopHeight(0))];
}
return _userInfoView;
}
@end
...@@ -6,13 +6,14 @@ ...@@ -6,13 +6,14 @@
// //
#import "ProfileController.h" #import "ProfileController.h"
#import "AccountController.h"
#import "SystemSetController.h" #import "SystemSetController.h"
#import "LoginController.h"
#import "InviteController.h" #import "InviteController.h"
#import "PrivacyViewController.h" #import "PrivacyViewController.h"
#import "ProfileAlertView.h" #import "ProfileAlertView.h"
@interface ProfileController () @interface ProfileController ()
@property (nonatomic, strong) UIView *userInfoView;
@property (nonatomic, strong) NSArray *tmpDatas; @property (nonatomic, strong) NSArray *tmpDatas;
@end @end
...@@ -23,7 +24,8 @@ ...@@ -23,7 +24,8 @@
self.navigationItem.title = @"我的"; self.navigationItem.title = @"我的";
self.tableView.dk_backgroundColorPicker = DKColorPickerWithKey(VCViewBG); self.tableView.dk_backgroundColorPicker = DKColorPickerWithKey(VCViewBG);
self.tmpDatas = @[@"注册登录信息", @"意见反馈", @"系统设置", @"邀请好友", @"关于我们", @"前往小程序", @"关注公众号", @"添加客服微信", @"失眠的认知行为疗法"]; self.tmpDatas = @[@"模拟用户登录", @"意见反馈", @"系统设置", @"邀请好友", @"关于我们", @"前往小程序", @"关注公众号", @"添加客服微信", @"失眠的认知行为疗法"];
self.tableView.tableHeaderView = self.userInfoView;
} }
#pragma mark - 导航栏日间、黑夜模式 #pragma mark - 导航栏日间、黑夜模式
...@@ -54,10 +56,9 @@ ...@@ -54,10 +56,9 @@
[tableView deselectRowAtIndexPath:indexPath animated:YES]; [tableView deselectRowAtIndexPath:indexPath animated:YES];
switch (indexPath.row) { switch (indexPath.row) {
case 0: // 跳转到登录页面 case 0: // 模拟跳转到登录页面
{ {
BaseNaviController *navi = [[BaseNaviController alloc] initWithRootViewController:[[LoginController alloc] init]]; [LoginUtils jumpToLoginControllerWithTarget:self];
[self presentViewController:navi animated:YES completion:nil];
} }
break; break;
case 2: // 系统设置入口 case 2: // 系统设置入口
...@@ -104,4 +105,28 @@ ...@@ -104,4 +105,28 @@
} }
} }
- (void)modifyAction {
// 判断是否登录成功
if ([LoginUtils getUserLoginData]) {
// 进入修改页面
AccountController *accountVC = [[AccountController alloc] init];
[self.navigationController pushViewController:accountVC animated:YES];
} else {
// 跳转到登录页面
[LoginUtils jumpToLoginControllerWithTarget:self];
}
}
- (UIView *)userInfoView {
if (!_userInfoView) {
_userInfoView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, kScreenWidth, 100)];
UIButton *btn = [UIButton btnWithTitle:@"点击修改信息" titleColor:BrandColor font:SysFont(16) bgColor:DSClearColor];
btn.frame = CGRectMake(0, 0, 150, 40);
btn.center = _userInfoView.center;
[btn addTarget:self action:@selector(modifyAction) forControlEvents:UIControlEventTouchUpInside];
[_userInfoView addSubview:btn];
}
return _userInfoView;
}
@end @end
...@@ -20,7 +20,7 @@ ...@@ -20,7 +20,7 @@
NSString *version = [NSString stringWithFormat:@"当前版本%@", [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"]]; NSString *version = [NSString stringWithFormat:@"当前版本%@", [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"]];
NSArray *details = @[version, @"", @"", @"10.0M"]; NSArray *details = @[version, @"", @"", @"10.0M"];
NSMutableArray *tmpArr = [NSMutableArray array]; NSMutableArray *tmpArr = [NSMutableArray array];
for (int i = 0; i < 4; i++) { for (int i = 0; i < titles.count; i++) {
SetModel *m = [SetModel new]; SetModel *m = [SetModel new];
m.title = titles[i]; m.title = titles[i];
m.detail = details[i]; m.detail = details[i];
...@@ -42,7 +42,7 @@ ...@@ -42,7 +42,7 @@
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) { if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
self.dk_backgroundColorPicker = DKColorPickerWithColors(DSWhite, ColorFromHex(0x1F263F), DSWhite); self.dk_backgroundColorPicker = DKColorPickerWithColors(DSWhite, CornerViewDarkColor, DSWhite);
self.accessoryView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"moreIcon"]]; self.accessoryView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"moreIcon"]];
self.titleLab = [UILabel dkLabWithFont:SysFont(15.0)]; self.titleLab = [UILabel dkLabWithFont:SysFont(15.0)];
...@@ -94,7 +94,7 @@ ...@@ -94,7 +94,7 @@
self.setDelegate = delegate; self.setDelegate = delegate;
[self cornerRadius:10]; [self cornerRadius:10];
self.scrollEnabled = NO; self.scrollEnabled = NO;
self.dk_backgroundColorPicker = DKColorPickerWithColors(DSWhite, ColorFromHex(0x1F263F), DSWhite); self.dk_backgroundColorPicker = DKColorPickerWithColors(DSWhite, CornerViewDarkColor, DSWhite);
self.separatorStyle = UITableViewCellSeparatorStyleNone; self.separatorStyle = UITableViewCellSeparatorStyleNone;
[self.setDataSource addDataArray:self.dataArr]; [self.setDataSource addDataArray:self.dataArr];
self.tableFooterView = self.footView; self.tableFooterView = self.footView;
......
//
// UserInfoTableView.h
// DreamSleep
//
// Created by peter on 2022/4/19.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@protocol UserInfoTableViewDelegate <NSObject>
- (void)didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
@end
@interface UserInfoTableView : UITableView
@property (nonatomic, weak) id<UserInfoTableViewDelegate> setDelegate;
- (instancetype)initWithFrame:(CGRect)frame delegate:(id<UserInfoTableViewDelegate>)delegate;
@end
NS_ASSUME_NONNULL_END
//
// UserInfoTableView.m
// DreamSleep
//
// Created by peter on 2022/4/19.
//
#import "UserInfoTableView.h"
#import "UserModel.h"
/// 设置自定义Cell
@interface UserCell : UITableViewCell
@property (nonatomic, strong) UILabel *titleLab;
@property (nonatomic, strong) UILabel *detailLab;
@property (nonatomic, strong) UIImageView *rightIcon;
- (void)updateUIWithModel:(UserModel *)model;
@end
@implementation UserCell
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
self.dk_backgroundColorPicker = DKColorPickerWithColors(DSWhite, CornerViewDarkColor, DSWhite);
self.accessoryView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"moreIcon"]];
self.titleLab = [UILabel dkLabWithFont:SysFont(15.0)];
[self addSubview:self.titleLab];
self.detailLab = [UILabel labWithFont:SysFont(14.0)];
self.detailLab.dk_textColorPicker = DKColorPickerWithColors(SubTitleColor, ColorFromHex(0x8C8F9D), DSWhite);
[self addSubview:self.detailLab];
}
return self;
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
self.selectedBackgroundView = [[UIView alloc] initWithFrame:self.frame];
self.selectedBackgroundView.backgroundColor = DSClearColor;
[super setSelected:selected animated:animated];
}
- (void)updateUIWithModel:(UserModel *)model {
self.titleLab.text = model.title;
self.detailLab.text = @"";
[self.titleLab sizeToFit];
[self.detailLab sizeToFit];
[self.titleLab mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self).offset(15);
make.centerY.equalTo(self);
}];
[self.detailLab mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(self.accessoryView.mas_left).offset(-8);
make.centerY.equalTo(self);
}];
}
@end
@interface UserInfoTableView () <UITableViewDelegate>
@property (nonatomic, strong) DSDataSource *infoDataSource;
@property (nonatomic, strong) NSArray *dataArr;
@end
@implementation UserInfoTableView
- (instancetype)initWithFrame:(CGRect)frame delegate:(id<UserInfoTableViewDelegate>)delegate {
if (self = [super initWithFrame:frame style:UITableViewStylePlain]) {
self.dataArr = [UserModel getAllUserDatas];
self.setDelegate = delegate;
[self cornerRadius:10];
self.scrollEnabled = NO;
self.dk_backgroundColorPicker = DKColorPickerWithColors(DSWhite, CornerViewDarkColor, DSWhite);
self.separatorStyle = UITableViewCellSeparatorStyleNone;
[self.infoDataSource addDataArray:self.dataArr];
}
return self;
}
- (DSDataSource *)infoDataSource {
if (!_infoDataSource) {
CellConfigureBlock cellBlock = ^(UserCell * cell, UserModel * model, NSIndexPath * indexPath) {
[cell updateUIWithModel:model];
};
NSString * const userCell = @"UserCell";
_infoDataSource = [[DSDataSource alloc] initWithIdentifier:userCell datas:@[] isSection:NO configureBlock:cellBlock];
self.dataSource = _infoDataSource;
self.delegate = self;
[self registerClass:[UserCell class] forCellReuseIdentifier:userCell];
}
return _infoDataSource;
}
#pragma mark - UITableViewDelegate
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 50;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
DSLog(@"indexPath:%ld", indexPath.row);
}
@end
//
// UserInfoView.h
// DreamSleep
//
// Created by peter on 2022/4/19.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface UserInfoView : UIView
@end
NS_ASSUME_NONNULL_END
//
// UserInfoView.m
// DreamSleep
//
// Created by peter on 2022/4/19.
//
#import "UserInfoView.h"
#import "UserInfoTableView.h"
#import <YYWebImage/YYWebImage.h>
@interface UserInfoView () <UserInfoTableViewDelegate>
@property (nonatomic, strong) UIButton *portraitBtn;
@property (nonatomic, strong) UserInfoTableView *userInfoTV;
@property (nonatomic, strong) UIButton *layoutBtn;
@end
@implementation UserInfoView
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self.portraitBtn yy_setImageWithURL:[NSURL URLWithString:@"https://img2.ydniu.com/sleep_ssmain/face_img/1648892614555_QhXhJX.jpg"] forState:UIControlStateNormal placeholder:[UIImage imageNamed:@"portrait"]];
[self addSubview:self.userInfoTV];
[self addSubview:self.layoutBtn];
[self.layoutBtn mas_makeConstraints:^(MASConstraintMaker *make) {
make.centerX.equalTo(self);
make.width.equalTo(@(kScreenWidth - 30));
make.height.equalTo(@40);
make.top.equalTo(self.userInfoTV.mas_bottom).offset(100);
}];
}
return self;
}
- (void)updatePortraitAction {
}
- (void)exitAction {
}
#pragma mark - UserInfoTableViewDelegate
- (void)didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
}
- (UIButton *)portraitBtn {
if (!_portraitBtn) {
_portraitBtn = [[UIButton alloc] initWithFrame:CGRectMake(0, 83, 145, 145)];
_portraitBtn.centerX = self.centerX;
[_portraitBtn addTarget:self action:@selector(updatePortraitAction) forControlEvents:UIControlEventTouchUpInside];
_portraitBtn.imageView.contentMode = UIViewContentModeScaleAspectFit;
_portraitBtn.contentHorizontalAlignment = UIControlContentHorizontalAlignmentFill;
_portraitBtn.contentVerticalAlignment = UIControlContentVerticalAlignmentFill;
[_portraitBtn cornerRadius:145/2.0];
[self addSubview:_portraitBtn];
UIImageView *cameraIV = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"cameraIcon"]];
[self addSubview:cameraIV];
[cameraIV mas_makeConstraints:^(MASConstraintMaker *make) {
make.right.equalTo(_portraitBtn).offset(-6);
make.bottom.equalTo(_portraitBtn).offset(-6);
}];
}
return _portraitBtn;
}
- (UserInfoTableView *)userInfoTV {
if (!_userInfoTV) {
_userInfoTV = [[UserInfoTableView alloc] initWithFrame:CGRectMake(15, CGRectGetMaxY(self.portraitBtn.frame) + 60, kScreenWidth - 30, 150) delegate:self];
}
return _userInfoTV;
}
- (UIButton *)layoutBtn {
if (!_layoutBtn) {
_layoutBtn = [UIButton btnWithTitle:@"退出登录" titleColor:BrandColor font:SysFont(15)];
[_layoutBtn addTarget:self action:@selector(exitAction) forControlEvents:UIControlEventTouchUpInside];
[_layoutBtn cornerRadius:10];
_layoutBtn.dk_backgroundColorPicker = DKColorPickerWithColors(DSWhite, CornerViewDarkColor, DSWhite);
}
return _layoutBtn;
}
@end
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#import "LoginView.h" #import "LoginView.h"
#import "PrivacyViewController.h" #import "PrivacyViewController.h"
#import <AuthenticationServices/AuthenticationServices.h> #import <AuthenticationServices/AuthenticationServices.h>
#import "UserRequestModel.h"
@interface LoginController () <LoginViewDelegate, ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding> @interface LoginController () <LoginViewDelegate, ASAuthorizationControllerDelegate, ASAuthorizationControllerPresentationContextProviding>
@property (strong, nonatomic) LoginView *loginView; @property (strong, nonatomic) LoginView *loginView;
...@@ -23,6 +24,7 @@ ...@@ -23,6 +24,7 @@
- (void)viewDidLoad { - (void)viewDidLoad {
[super viewDidLoad]; [super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(wxLoginAuth:) name:WXLoginAuthNoti object:nil];
} }
- (void)viewWillAppear:(BOOL)animated { - (void)viewWillAppear:(BOOL)animated {
...@@ -35,6 +37,21 @@ ...@@ -35,6 +37,21 @@
return [self.dk_manager.themeVersion isEqualToString:DKThemeVersionNormal] ? UIStatusBarStyleDefault : UIStatusBarStyleLightContent; return [self.dk_manager.themeVersion isEqualToString:DKThemeVersionNormal] ? UIStatusBarStyleDefault : UIStatusBarStyleLightContent;
} }
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self name:WXLoginAuthNoti object:nil];
}
- (void)wxLoginAuth:(NSNotification *)noti {
NSString *code = noti.object;
[UserRequestModel wxLoginWithCode:code completion:^(UserRequestModel * _Nonnull requestModel) {
if (requestModel.resCode == DSResCodeSuccess) {
[self dismissViewControllerAnimated:YES completion:nil];
} else {
[DSProgressHUD showToast:requestModel.errorInfo];
}
}];
}
#pragma mark - LoginViewDelegate #pragma mark - LoginViewDelegate
- (void)sendClickAction:(NSInteger)tag { - (void)sendClickAction:(NSInteger)tag {
switch (tag) { switch (tag) {
...@@ -170,6 +187,9 @@ ...@@ -170,6 +187,9 @@
case ASAuthorizationErrorUnknown: case ASAuthorizationErrorUnknown:
errorMsg = @"授权请求失败未知原因"; errorMsg = @"授权请求失败未知原因";
break; break;
case ASAuthorizationErrorNotInteractive:
errorMsg = @"ASAuthorizationErrorNotInteractive";
break;
} }
NSLog(@"controller requests:%@, errorMsg:%@", controller.authorizationRequests, errorMsg); NSLog(@"controller requests:%@, errorMsg:%@", controller.authorizationRequests, errorMsg);
} }
......
//
// LoginUtils.h
// DreamSleep
//
// Created by peter on 2022/4/19.
//
#import <Foundation/Foundation.h>
@class UserModel;
NS_ASSUME_NONNULL_BEGIN
/// 登录模块工具类
@interface LoginUtils : NSObject
/// 获取用户登录数据
+ (UserModel *)getUserLoginData;
+ (int)getUserID;
/// 保存用户登录数据
+ (void)saveUserLoginData:(UserModel *)model;
+ (void)jumpToLoginControllerWithTarget:(UIViewController *)target;
@end
NS_ASSUME_NONNULL_END
//
// LoginUtils.m
// DreamSleep
//
// Created by peter on 2022/4/19.
//
#import "LoginUtils.h"
#import "UserModel.h"
#import "LoginController.h"
@implementation LoginUtils
+ (UserModel *)getUserLoginData {
UserModel *userModel = [NSKeyedUnarchiver unarchiveObjectWithData:kGetUserDefaultsObj(UserBasicInfo)];
return userModel;
}
+ (int)getUserID {
UserModel *userModel = [self getUserLoginData];
return userModel.user_id;
}
+ (void)saveUserLoginData:(UserModel *)model {
NSData *userData = [NSKeyedArchiver archivedDataWithRootObject:model];
kSetUserDefaultsObj(userData, UserBasicInfo);
kUserDefaultsSynchronize;
}
+ (void)jumpToLoginControllerWithTarget:(UIViewController *)target {
if (target && [target isKindOfClass:[UIViewController class]]) {
BaseNaviController *navi = [[BaseNaviController alloc] initWithRootViewController:[[LoginController alloc] init]];
[target presentViewController:navi animated:YES completion:nil];
}
}
@end
//
// UserModel.h
// DreamSleep
//
// Created by peter on 2022/4/19.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/// 用户数据model
@interface UserModel : NSObject <NSCoding>
@property (nonatomic, copy) NSString *nick_name;
@property (nonatomic, assign) BOOL gender;
@property (nonatomic, copy) NSString *birthday;
@property (nonatomic, assign) int user_id;
@property (nonatomic, copy) NSString *token;
@property (nonatomic, copy) NSString *sid;
@property (nonatomic, copy) NSString *user_name;
@property (nonatomic, copy) NSString *face_img;
@property (nonatomic, assign) int is_access;
@property (nonatomic, copy) NSString *title;
+ (NSArray *)getAllUserDatas;
@end
NS_ASSUME_NONNULL_END
//
// UserModel.m
// DreamSleep
//
// Created by peter on 2022/4/19.
//
#import "UserModel.h"
@implementation UserModel
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:_nick_name forKey:@"nick_name"];
[aCoder encodeObject:@(_gender) forKey:@"gender"];
[aCoder encodeObject:_birthday forKey:@"birthday"];
[aCoder encodeObject:@(_user_id) forKey:@"user_id"];
[aCoder encodeObject:_token forKey:@"token"];
[aCoder encodeObject:_face_img forKey:@"face_img"];
}
- (id)initWithCoder:(NSCoder *)aDecoder {
_nick_name = [aDecoder decodeObjectForKey:@"nick_name"] ;
_gender = [[aDecoder decodeObjectForKey:@"gender"] boolValue];
_birthday = [aDecoder decodeObjectForKey:@"birthday"];
_user_id = [[aDecoder decodeObjectForKey:@"user_id"] intValue];
_token = [aDecoder decodeObjectForKey:@"token"];
_face_img = [aDecoder decodeObjectForKey:@"face_img"];
return self;
}
+ (NSArray *)getAllUserDatas {
NSArray *titles = @[@"昵称", @"性别", @"生日"];
NSMutableArray *tmpArr = [NSMutableArray array];
for (int i = 0; i < titles.count; i++) {
UserModel *m = [UserModel new];
m.title = titles[i];
[tmpArr addObject:m];
}
return [tmpArr copy];
}
@end
...@@ -6,11 +6,28 @@ ...@@ -6,11 +6,28 @@
// //
#import "DSNetworkTool.h" #import "DSNetworkTool.h"
#import "UserModel.h"
NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_BEGIN
/// 用户相关请求
@interface UserRequestModel : DSNetworkTool @interface UserRequestModel : DSNetworkTool
@property (nonatomic, strong) UserModel *userModel;
/// 微信登录请求
/// @param code 客户端微信授权用户code
/// @param completion completion
+ (NSURLSessionDataTask *)wxLoginWithCode:(NSString *)code completion:(void (^)(UserRequestModel *requestModel))completion;
/// 用户退出登录接口
/// @param completion completion
+ (NSURLSessionDataTask *)layoutRequestWithCompletion:(void (^)(UserRequestModel *requestModel))completion;
/// 用户注销账号接口
/// @param completion completion
+ (NSURLSessionDataTask *)closeUserRequestWithCompletion:(void (^)(UserRequestModel *requestModel))completion;
@end @end
NS_ASSUME_NONNULL_END NS_ASSUME_NONNULL_END
...@@ -6,9 +6,63 @@ ...@@ -6,9 +6,63 @@
// //
#import "UserRequestModel.h" #import "UserRequestModel.h"
#import <YYModel/YYModel.h>
@implementation UserRequestModel @implementation UserRequestModel
+ (NSURLSessionDataTask *)wxLoginWithCode:(NSString *)code completion:(void (^)(UserRequestModel *requestModel))completion {
UserRequestModel * requestModel = [[UserRequestModel alloc] init];
NSString *api = @"wxLogin";
NSString *argStr = [NSString stringWithFormat:@"mutation{%@(code:\"%@\")}", api, code];
return [self httpPostBodyRequestWithAPI:api params:@{@"query" : argStr} view:nil hasNetActivity:YES loadingInfo:nil hasFailInfo:YES success:^(NSDictionary * _Nonnull apiDic) {
DSLog(@"微信登录接口apiDic:%@", apiDic);
requestModel.resCode = DSResCodeSuccess;
NSDictionary *resultDic = apiDic[@"result"];
UserModel *userModel = [UserModel yy_modelWithJSON:resultDic];
DSLog(@"userModel:%@", userModel.debugDescription);
// 保存用户信息
[LoginUtils saveUserLoginData:userModel];
completion(requestModel);
} failure:^(id _Nonnull failureInfo) {
requestModel.resCode = DSResCodeNetFail;
requestModel.errorInfo = failureInfo;
completion(requestModel);
}];
}
+ (NSURLSessionDataTask *)layoutRequestWithCompletion:(void (^)(UserRequestModel *requestModel))completion
{
UserRequestModel * requestModel = [[UserRequestModel alloc] init];
NSString *api = @"user_logout";
NSString *argStr = [NSString stringWithFormat:@"mutation{%@}", api];
return [self httpPostBodyRequestWithAPI:api params:@{@"query" : argStr} view:nil hasNetActivity:YES loadingInfo:nil hasFailInfo:YES success:^(NSDictionary * _Nonnull apiDic) {
DSLog(@"用户退出登录接口apiDic:%@", apiDic);
requestModel.resCode = DSResCodeSuccess;
completion(requestModel);
} failure:^(id _Nonnull failureInfo) {
requestModel.resCode = DSResCodeNetFail;
requestModel.errorInfo = failureInfo;
completion(requestModel);
}];
}
+ (NSURLSessionDataTask *)closeUserRequestWithCompletion:(void (^)(UserRequestModel *requestModel))completion
{
UserRequestModel * requestModel = [[UserRequestModel alloc] init];
NSString *api = @"close_user";
int user_id = [LoginUtils getUserID];
NSString *argStr = [NSString stringWithFormat:@"mutation{%@(user_id:%d)}", api, user_id];
return [self httpPostBodyRequestWithAPI:api params:@{@"query" : argStr} view:nil hasNetActivity:YES loadingInfo:nil hasFailInfo:YES success:^(NSDictionary * _Nonnull apiDic) {
DSLog(@"用户用户注销账户接口apiDic:%@", apiDic);
requestModel.resCode = DSResCodeSuccess;
completion(requestModel);
} failure:^(id _Nonnull failureInfo) {
requestModel.resCode = DSResCodeNetFail;
requestModel.errorInfo = failureInfo;
completion(requestModel);
}];
}
// 修改用户信息 // 修改用户信息
+ (NSURLSessionDataTask *)updateUserInfoWithCompletion:(void (^)(UserRequestModel *requestModel))completion { + (NSURLSessionDataTask *)updateUserInfoWithCompletion:(void (^)(UserRequestModel *requestModel))completion {
UserRequestModel * requestModel = [[UserRequestModel alloc] init]; UserRequestModel * requestModel = [[UserRequestModel alloc] init];
...@@ -17,9 +71,10 @@ ...@@ -17,9 +71,10 @@
return [self httpPostBodyRequestWithParam:@{@"query" : argStr} view:nil hasNetActivity:YES loadingInfo:nil hasFailInfo:YES success:^(id _Nonnull responseObj) { return [self httpPostBodyRequestWithParam:@{@"query" : argStr} view:nil hasNetActivity:YES loadingInfo:nil hasFailInfo:YES success:^(id _Nonnull responseObj) {
DSLog(@"修改用户接口dataDic:%@", responseObj); DSLog(@"修改用户接口dataDic:%@", responseObj);
requestModel.resCode = DSResCodeSuccess;
completion(requestModel); completion(requestModel);
} failure:^(id _Nonnull failureInfo) { } failure:^(id _Nonnull failureInfo) {
requestModel.errCode = DSErrCodeNetFail; requestModel.resCode = DSResCodeNetFail;
requestModel.errorInfo = failureInfo; requestModel.errorInfo = failureInfo;
completion(requestModel); completion(requestModel);
}]; }];
......
{
"images" : [
{
"filename" : "cameraIcon.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "cameraIcon@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "cameraIcon@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"filename" : "portrait.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "portrait@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "portrait@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
...@@ -64,6 +64,8 @@ ...@@ -64,6 +64,8 @@
#define BGColor ColorFromHex(0xF5F7FA) #define BGColor ColorFromHex(0xF5F7FA)
// 弹框背景黑夜深色 // 弹框背景黑夜深色
#define AlertDarkColor ColorFromHex(0x131724) #define AlertDarkColor ColorFromHex(0x131724)
// 常用圆角视图黑夜深色
#define CornerViewDarkColor ColorFromHex(0x1F263F)
/** 副文案颜色 */ /** 副文案颜色 */
#define SubTextColor [UIColor colorWithHexString:@"#A0A0A0"] #define SubTextColor [UIColor colorWithHexString:@"#A0A0A0"]
......
...@@ -30,6 +30,12 @@ FOUNDATION_EXTERN NSString * const ExpireTime1; ...@@ -30,6 +30,12 @@ FOUNDATION_EXTERN NSString * const ExpireTime1;
FOUNDATION_EXTERN NSString * const StartTime2; FOUNDATION_EXTERN NSString * const StartTime2;
FOUNDATION_EXTERN NSString * const ExpireTime2; FOUNDATION_EXTERN NSString * const ExpireTime2;
// 通知相关
FOUNDATION_EXTERN NSString * const WXLoginAuthNoti;
// 用户基础信息
FOUNDATION_EXTERN NSString * const UserBasicInfo;
#pragma mark - 第三方平台配置 #pragma mark - 第三方平台配置
/** 微信开放平台AppID */ /** 微信开放平台AppID */
FOUNDATION_EXTERN NSString * const WeChatAppID; FOUNDATION_EXTERN NSString * const WeChatAppID;
......
...@@ -23,6 +23,10 @@ NSString * const ExpireTime1 = @"23:59:59"; ...@@ -23,6 +23,10 @@ NSString * const ExpireTime1 = @"23:59:59";
NSString * const StartTime2 = @"00:00:00"; NSString * const StartTime2 = @"00:00:00";
NSString * const ExpireTime2 = @"06:00:00"; NSString * const ExpireTime2 = @"06:00:00";
NSString * const WXLoginAuthNoti = @"wxLoginAuthNoti";
NSString * const UserBasicInfo = @"UserBasicInfo";
NSString * const WeChatAppID = @"wx4cdd4760092cdfdf"; NSString * const WeChatAppID = @"wx4cdd4760092cdfdf";
NSString * const WeChatAppSecret = @""; NSString * const WeChatAppSecret = @"";
NSString * const UNIVERSAL_LINK = @"https://www.cbti.cn/sleep_apple/"; NSString * const UNIVERSAL_LINK = @"https://www.cbti.cn/sleep_apple/";
......
...@@ -29,6 +29,9 @@ ...@@ -29,6 +29,9 @@
#endif #endif
#pragma mark - UserDefaults #pragma mark - UserDefaults
// OBJ
#define kSetUserDefaultsObj(obj, key) [[NSUserDefaults standardUserDefaults] setObject:obj forKey:key]
#define kGetUserDefaultsObj(key) [[NSUserDefaults standardUserDefaults] objectForKey:key]
// BOOL // BOOL
#define kSetUserDefaultsBOOL(boolValue, key) [[NSUserDefaults standardUserDefaults] setBool:boolValue forKey:key] #define kSetUserDefaultsBOOL(boolValue, key) [[NSUserDefaults standardUserDefaults] setBool:boolValue forKey:key]
#define kGetUserDefaultsBOOL(key) [[NSUserDefaults standardUserDefaults] boolForKey:key] #define kGetUserDefaultsBOOL(key) [[NSUserDefaults standardUserDefaults] boolForKey:key]
......
...@@ -30,4 +30,6 @@ ...@@ -30,4 +30,6 @@
#import "DSProgressHUD.h" #import "DSProgressHUD.h"
#import "LoginUtils.h"
#endif /* PrefixHeader_pch */ #endif /* PrefixHeader_pch */
...@@ -11,6 +11,7 @@ target 'DreamSleep' do ...@@ -11,6 +11,7 @@ target 'DreamSleep' do
pod 'MBProgressHUD', '~> 1.2.0' pod 'MBProgressHUD', '~> 1.2.0'
pod 'YYWebImage', '~> 1.0.5' pod 'YYWebImage', '~> 1.0.5'
pod 'YYImage/WebP' pod 'YYImage/WebP'
pod 'YYModel', '~> 1.0.4'
end end
# AFNetworking (4.0.1) # AFNetworking (4.0.1)
...@@ -22,4 +23,5 @@ end ...@@ -22,4 +23,5 @@ end
# DOUAudioStreamer (0.2.16) # DOUAudioStreamer (0.2.16)
# MBProgressHUD (1.2.0) # MBProgressHUD (1.2.0)
# YYWebImage (1.0.5) # YYWebImage (1.0.5)
# YYImage/WebP # YYImage/WebP(模拟器上目前无法运行)
# YYModel (1.0.4)
...@@ -32,6 +32,7 @@ PODS: ...@@ -32,6 +32,7 @@ PODS:
- YYImage/Core (1.0.4) - YYImage/Core (1.0.4)
- YYImage/WebP (1.0.4): - YYImage/WebP (1.0.4):
- YYImage/Core - YYImage/Core
- YYModel (1.0.4)
- YYWebImage (1.0.5): - YYWebImage (1.0.5):
- YYCache - YYCache
- YYImage - YYImage
...@@ -45,6 +46,7 @@ DEPENDENCIES: ...@@ -45,6 +46,7 @@ DEPENDENCIES:
- MJRefresh (~> 3.7.5) - MJRefresh (~> 3.7.5)
- YTKNetwork (~> 3.0.6) - YTKNetwork (~> 3.0.6)
- YYImage/WebP - YYImage/WebP
- YYModel (~> 1.0.4)
- YYWebImage (~> 1.0.5) - YYWebImage (~> 1.0.5)
SPEC REPOS: SPEC REPOS:
...@@ -59,6 +61,7 @@ SPEC REPOS: ...@@ -59,6 +61,7 @@ SPEC REPOS:
- YTKNetwork - YTKNetwork
- YYCache - YYCache
- YYImage - YYImage
- YYModel
- YYWebImage - YYWebImage
SPEC CHECKSUMS: SPEC CHECKSUMS:
...@@ -72,8 +75,9 @@ SPEC CHECKSUMS: ...@@ -72,8 +75,9 @@ SPEC CHECKSUMS:
YTKNetwork: c16be90b06be003de9e9cd0d3b187cc8eaf35c04 YTKNetwork: c16be90b06be003de9e9cd0d3b187cc8eaf35c04
YYCache: 8105b6638f5e849296c71f331ff83891a4942952 YYCache: 8105b6638f5e849296c71f331ff83891a4942952
YYImage: 1e1b62a9997399593e4b9c4ecfbbabbf1d3f3b54 YYImage: 1e1b62a9997399593e4b9c4ecfbbabbf1d3f3b54
YYModel: 2a7fdd96aaa4b86a824e26d0c517de8928c04b30
YYWebImage: 5f7f36aee2ae293f016d418c7d6ba05c4863e928 YYWebImage: 5f7f36aee2ae293f016d418c7d6ba05c4863e928
PODFILE CHECKSUM: 462801539eac1a9c9c847595e3687fd361bff7ed PODFILE CHECKSUM: b3d9e0300e1732ba6a625a3cac34c1a05fcefc19
COCOAPODS: 1.11.3 COCOAPODS: 1.11.3
...@@ -32,6 +32,7 @@ PODS: ...@@ -32,6 +32,7 @@ PODS:
- YYImage/Core (1.0.4) - YYImage/Core (1.0.4)
- YYImage/WebP (1.0.4): - YYImage/WebP (1.0.4):
- YYImage/Core - YYImage/Core
- YYModel (1.0.4)
- YYWebImage (1.0.5): - YYWebImage (1.0.5):
- YYCache - YYCache
- YYImage - YYImage
...@@ -45,6 +46,7 @@ DEPENDENCIES: ...@@ -45,6 +46,7 @@ DEPENDENCIES:
- MJRefresh (~> 3.7.5) - MJRefresh (~> 3.7.5)
- YTKNetwork (~> 3.0.6) - YTKNetwork (~> 3.0.6)
- YYImage/WebP - YYImage/WebP
- YYModel (~> 1.0.4)
- YYWebImage (~> 1.0.5) - YYWebImage (~> 1.0.5)
SPEC REPOS: SPEC REPOS:
...@@ -59,6 +61,7 @@ SPEC REPOS: ...@@ -59,6 +61,7 @@ SPEC REPOS:
- YTKNetwork - YTKNetwork
- YYCache - YYCache
- YYImage - YYImage
- YYModel
- YYWebImage - YYWebImage
SPEC CHECKSUMS: SPEC CHECKSUMS:
...@@ -72,8 +75,9 @@ SPEC CHECKSUMS: ...@@ -72,8 +75,9 @@ SPEC CHECKSUMS:
YTKNetwork: c16be90b06be003de9e9cd0d3b187cc8eaf35c04 YTKNetwork: c16be90b06be003de9e9cd0d3b187cc8eaf35c04
YYCache: 8105b6638f5e849296c71f331ff83891a4942952 YYCache: 8105b6638f5e849296c71f331ff83891a4942952
YYImage: 1e1b62a9997399593e4b9c4ecfbbabbf1d3f3b54 YYImage: 1e1b62a9997399593e4b9c4ecfbbabbf1d3f3b54
YYModel: 2a7fdd96aaa4b86a824e26d0c517de8928c04b30
YYWebImage: 5f7f36aee2ae293f016d418c7d6ba05c4863e928 YYWebImage: 5f7f36aee2ae293f016d418c7d6ba05c4863e928
PODFILE CHECKSUM: 462801539eac1a9c9c847595e3687fd361bff7ed PODFILE CHECKSUM: b3d9e0300e1732ba6a625a3cac34c1a05fcefc19
COCOAPODS: 1.11.3 COCOAPODS: 1.11.3
...@@ -227,6 +227,32 @@ SOFTWARE. ...@@ -227,6 +227,32 @@ SOFTWARE.
## YYModel
The MIT License (MIT)
Copyright (c) 2015 ibireme <ibireme@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
## YYWebImage ## YYWebImage
The MIT License (MIT) The MIT License (MIT)
......
...@@ -320,6 +320,38 @@ SOFTWARE. ...@@ -320,6 +320,38 @@ SOFTWARE.
<key>License</key> <key>License</key>
<string>MIT</string> <string>MIT</string>
<key>Title</key> <key>Title</key>
<string>YYModel</string>
<key>Type</key>
<string>PSGroupSpecifier</string>
</dict>
<dict>
<key>FooterText</key>
<string>The MIT License (MIT)
Copyright (c) 2015 ibireme &lt;ibireme@gmail.com&gt;
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
</string>
<key>License</key>
<string>MIT</string>
<key>Title</key>
<string>YYWebImage</string> <string>YYWebImage</string>
<key>Type</key> <key>Type</key>
<string>PSGroupSpecifier</string> <string>PSGroupSpecifier</string>
......
...@@ -8,5 +8,6 @@ ${BUILT_PRODUCTS_DIR}/Masonry/Masonry.framework ...@@ -8,5 +8,6 @@ ${BUILT_PRODUCTS_DIR}/Masonry/Masonry.framework
${BUILT_PRODUCTS_DIR}/YTKNetwork/YTKNetwork.framework ${BUILT_PRODUCTS_DIR}/YTKNetwork/YTKNetwork.framework
${BUILT_PRODUCTS_DIR}/YYCache/YYCache.framework ${BUILT_PRODUCTS_DIR}/YYCache/YYCache.framework
${BUILT_PRODUCTS_DIR}/YYImage/YYImage.framework ${BUILT_PRODUCTS_DIR}/YYImage/YYImage.framework
${BUILT_PRODUCTS_DIR}/YYModel/YYModel.framework
${BUILT_PRODUCTS_DIR}/YYWebImage/YYWebImage.framework ${BUILT_PRODUCTS_DIR}/YYWebImage/YYWebImage.framework
${BUILT_PRODUCTS_DIR}/lottie-ios/Lottie.framework ${BUILT_PRODUCTS_DIR}/lottie-ios/Lottie.framework
\ No newline at end of file \ No newline at end of file
...@@ -7,5 +7,6 @@ ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Masonry.framework ...@@ -7,5 +7,6 @@ ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Masonry.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YTKNetwork.framework ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YTKNetwork.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYCache.framework ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYCache.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYImage.framework ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYImage.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYModel.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYWebImage.framework ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYWebImage.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Lottie.framework ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Lottie.framework
\ No newline at end of file \ No newline at end of file
...@@ -8,5 +8,6 @@ ${BUILT_PRODUCTS_DIR}/Masonry/Masonry.framework ...@@ -8,5 +8,6 @@ ${BUILT_PRODUCTS_DIR}/Masonry/Masonry.framework
${BUILT_PRODUCTS_DIR}/YTKNetwork/YTKNetwork.framework ${BUILT_PRODUCTS_DIR}/YTKNetwork/YTKNetwork.framework
${BUILT_PRODUCTS_DIR}/YYCache/YYCache.framework ${BUILT_PRODUCTS_DIR}/YYCache/YYCache.framework
${BUILT_PRODUCTS_DIR}/YYImage/YYImage.framework ${BUILT_PRODUCTS_DIR}/YYImage/YYImage.framework
${BUILT_PRODUCTS_DIR}/YYModel/YYModel.framework
${BUILT_PRODUCTS_DIR}/YYWebImage/YYWebImage.framework ${BUILT_PRODUCTS_DIR}/YYWebImage/YYWebImage.framework
${BUILT_PRODUCTS_DIR}/lottie-ios/Lottie.framework ${BUILT_PRODUCTS_DIR}/lottie-ios/Lottie.framework
\ No newline at end of file \ No newline at end of file
...@@ -7,5 +7,6 @@ ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Masonry.framework ...@@ -7,5 +7,6 @@ ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Masonry.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YTKNetwork.framework ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YTKNetwork.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYCache.framework ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYCache.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYImage.framework ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYImage.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYModel.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYWebImage.framework ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYWebImage.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Lottie.framework ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Lottie.framework
\ No newline at end of file \ No newline at end of file
...@@ -8,5 +8,6 @@ ${BUILT_PRODUCTS_DIR}/Masonry/Masonry.framework ...@@ -8,5 +8,6 @@ ${BUILT_PRODUCTS_DIR}/Masonry/Masonry.framework
${BUILT_PRODUCTS_DIR}/YTKNetwork/YTKNetwork.framework ${BUILT_PRODUCTS_DIR}/YTKNetwork/YTKNetwork.framework
${BUILT_PRODUCTS_DIR}/YYCache/YYCache.framework ${BUILT_PRODUCTS_DIR}/YYCache/YYCache.framework
${BUILT_PRODUCTS_DIR}/YYImage/YYImage.framework ${BUILT_PRODUCTS_DIR}/YYImage/YYImage.framework
${BUILT_PRODUCTS_DIR}/YYModel/YYModel.framework
${BUILT_PRODUCTS_DIR}/YYWebImage/YYWebImage.framework ${BUILT_PRODUCTS_DIR}/YYWebImage/YYWebImage.framework
${BUILT_PRODUCTS_DIR}/lottie-ios/Lottie.framework ${BUILT_PRODUCTS_DIR}/lottie-ios/Lottie.framework
\ No newline at end of file \ No newline at end of file
...@@ -7,5 +7,6 @@ ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Masonry.framework ...@@ -7,5 +7,6 @@ ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Masonry.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YTKNetwork.framework ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YTKNetwork.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYCache.framework ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYCache.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYImage.framework ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYImage.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYModel.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYWebImage.framework ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/YYWebImage.framework
${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Lottie.framework ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Lottie.framework
\ No newline at end of file \ No newline at end of file
...@@ -185,6 +185,7 @@ if [[ "$CONFIGURATION" == "Beta" ]]; then ...@@ -185,6 +185,7 @@ if [[ "$CONFIGURATION" == "Beta" ]]; then
install_framework "${BUILT_PRODUCTS_DIR}/YTKNetwork/YTKNetwork.framework" install_framework "${BUILT_PRODUCTS_DIR}/YTKNetwork/YTKNetwork.framework"
install_framework "${BUILT_PRODUCTS_DIR}/YYCache/YYCache.framework" install_framework "${BUILT_PRODUCTS_DIR}/YYCache/YYCache.framework"
install_framework "${BUILT_PRODUCTS_DIR}/YYImage/YYImage.framework" install_framework "${BUILT_PRODUCTS_DIR}/YYImage/YYImage.framework"
install_framework "${BUILT_PRODUCTS_DIR}/YYModel/YYModel.framework"
install_framework "${BUILT_PRODUCTS_DIR}/YYWebImage/YYWebImage.framework" install_framework "${BUILT_PRODUCTS_DIR}/YYWebImage/YYWebImage.framework"
install_framework "${BUILT_PRODUCTS_DIR}/lottie-ios/Lottie.framework" install_framework "${BUILT_PRODUCTS_DIR}/lottie-ios/Lottie.framework"
fi fi
...@@ -198,6 +199,7 @@ if [[ "$CONFIGURATION" == "Debug" ]]; then ...@@ -198,6 +199,7 @@ if [[ "$CONFIGURATION" == "Debug" ]]; then
install_framework "${BUILT_PRODUCTS_DIR}/YTKNetwork/YTKNetwork.framework" install_framework "${BUILT_PRODUCTS_DIR}/YTKNetwork/YTKNetwork.framework"
install_framework "${BUILT_PRODUCTS_DIR}/YYCache/YYCache.framework" install_framework "${BUILT_PRODUCTS_DIR}/YYCache/YYCache.framework"
install_framework "${BUILT_PRODUCTS_DIR}/YYImage/YYImage.framework" install_framework "${BUILT_PRODUCTS_DIR}/YYImage/YYImage.framework"
install_framework "${BUILT_PRODUCTS_DIR}/YYModel/YYModel.framework"
install_framework "${BUILT_PRODUCTS_DIR}/YYWebImage/YYWebImage.framework" install_framework "${BUILT_PRODUCTS_DIR}/YYWebImage/YYWebImage.framework"
install_framework "${BUILT_PRODUCTS_DIR}/lottie-ios/Lottie.framework" install_framework "${BUILT_PRODUCTS_DIR}/lottie-ios/Lottie.framework"
fi fi
...@@ -211,6 +213,7 @@ if [[ "$CONFIGURATION" == "Release" ]]; then ...@@ -211,6 +213,7 @@ if [[ "$CONFIGURATION" == "Release" ]]; then
install_framework "${BUILT_PRODUCTS_DIR}/YTKNetwork/YTKNetwork.framework" install_framework "${BUILT_PRODUCTS_DIR}/YTKNetwork/YTKNetwork.framework"
install_framework "${BUILT_PRODUCTS_DIR}/YYCache/YYCache.framework" install_framework "${BUILT_PRODUCTS_DIR}/YYCache/YYCache.framework"
install_framework "${BUILT_PRODUCTS_DIR}/YYImage/YYImage.framework" install_framework "${BUILT_PRODUCTS_DIR}/YYImage/YYImage.framework"
install_framework "${BUILT_PRODUCTS_DIR}/YYModel/YYModel.framework"
install_framework "${BUILT_PRODUCTS_DIR}/YYWebImage/YYWebImage.framework" install_framework "${BUILT_PRODUCTS_DIR}/YYWebImage/YYWebImage.framework"
install_framework "${BUILT_PRODUCTS_DIR}/lottie-ios/Lottie.framework" install_framework "${BUILT_PRODUCTS_DIR}/lottie-ios/Lottie.framework"
fi fi
......
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking" "${PODS_CONFIGURATION_BUILD_DIR}/DKNightVersion" "${PODS_CONFIGURATION_BUILD_DIR}/DOUAudioStreamer" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "${PODS_CONFIGURATION_BUILD_DIR}/YTKNetwork" "${PODS_CONFIGURATION_BUILD_DIR}/YYCache" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage" "${PODS_CONFIGURATION_BUILD_DIR}/YYWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios" "${PODS_ROOT}/YYImage/Vendor" FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking" "${PODS_CONFIGURATION_BUILD_DIR}/DKNightVersion" "${PODS_CONFIGURATION_BUILD_DIR}/DOUAudioStreamer" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "${PODS_CONFIGURATION_BUILD_DIR}/YTKNetwork" "${PODS_CONFIGURATION_BUILD_DIR}/YYCache" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage" "${PODS_CONFIGURATION_BUILD_DIR}/YYModel" "${PODS_CONFIGURATION_BUILD_DIR}/YYWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios" "${PODS_ROOT}/YYImage/Vendor"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking/AFNetworking.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DKNightVersion/DKNightVersion.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DOUAudioStreamer/DOUAudioStreamer.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh/MJRefresh.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry/Masonry.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YTKNetwork/YTKNetwork.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYCache/YYCache.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage/YYImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYWebImage/YYWebImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios/Lottie.framework/Headers" HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking/AFNetworking.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DKNightVersion/DKNightVersion.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DOUAudioStreamer/DOUAudioStreamer.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh/MJRefresh.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry/Masonry.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YTKNetwork/YTKNetwork.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYCache/YYCache.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage/YYImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYModel/YYModel.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYWebImage/YYWebImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios/Lottie.framework/Headers"
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
OTHER_LDFLAGS = $(inherited) -ObjC -l"sqlite3" -l"z" -framework "AFNetworking" -framework "AVFoundation" -framework "Accelerate" -framework "AssetsLibrary" -framework "AudioToolbox" -framework "CFNetwork" -framework "CoreAudio" -framework "CoreFoundation" -framework "CoreGraphics" -framework "DKNightVersion" -framework "DOUAudioStreamer" -framework "Foundation" -framework "ImageIO" -framework "Lottie" -framework "MBProgressHUD" -framework "MJRefresh" -framework "Masonry" -framework "MediaPlayer" -framework "MobileCoreServices" -framework "OpenGLES" -framework "QuartzCore" -framework "UIKit" -framework "YTKNetwork" -framework "YYCache" -framework "YYImage" -framework "YYWebImage" OTHER_LDFLAGS = $(inherited) -ObjC -l"sqlite3" -l"z" -framework "AFNetworking" -framework "AVFoundation" -framework "Accelerate" -framework "AssetsLibrary" -framework "AudioToolbox" -framework "CFNetwork" -framework "CoreAudio" -framework "CoreFoundation" -framework "CoreGraphics" -framework "DKNightVersion" -framework "DOUAudioStreamer" -framework "Foundation" -framework "ImageIO" -framework "Lottie" -framework "MBProgressHUD" -framework "MJRefresh" -framework "Masonry" -framework "MediaPlayer" -framework "MobileCoreServices" -framework "OpenGLES" -framework "QuartzCore" -framework "UIKit" -framework "YTKNetwork" -framework "YYCache" -framework "YYImage" -framework "YYModel" -framework "YYWebImage"
PODS_BUILD_DIR = ${BUILD_DIR} PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_PODFILE_DIR_PATH = ${SRCROOT}/. PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
......
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking" "${PODS_CONFIGURATION_BUILD_DIR}/DKNightVersion" "${PODS_CONFIGURATION_BUILD_DIR}/DOUAudioStreamer" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "${PODS_CONFIGURATION_BUILD_DIR}/YTKNetwork" "${PODS_CONFIGURATION_BUILD_DIR}/YYCache" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage" "${PODS_CONFIGURATION_BUILD_DIR}/YYWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios" "${PODS_ROOT}/YYImage/Vendor" FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking" "${PODS_CONFIGURATION_BUILD_DIR}/DKNightVersion" "${PODS_CONFIGURATION_BUILD_DIR}/DOUAudioStreamer" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "${PODS_CONFIGURATION_BUILD_DIR}/YTKNetwork" "${PODS_CONFIGURATION_BUILD_DIR}/YYCache" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage" "${PODS_CONFIGURATION_BUILD_DIR}/YYModel" "${PODS_CONFIGURATION_BUILD_DIR}/YYWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios" "${PODS_ROOT}/YYImage/Vendor"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking/AFNetworking.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DKNightVersion/DKNightVersion.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DOUAudioStreamer/DOUAudioStreamer.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh/MJRefresh.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry/Masonry.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YTKNetwork/YTKNetwork.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYCache/YYCache.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage/YYImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYWebImage/YYWebImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios/Lottie.framework/Headers" HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking/AFNetworking.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DKNightVersion/DKNightVersion.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DOUAudioStreamer/DOUAudioStreamer.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh/MJRefresh.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry/Masonry.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YTKNetwork/YTKNetwork.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYCache/YYCache.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage/YYImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYModel/YYModel.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYWebImage/YYWebImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios/Lottie.framework/Headers"
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
OTHER_LDFLAGS = $(inherited) -ObjC -l"sqlite3" -l"z" -framework "AFNetworking" -framework "AVFoundation" -framework "Accelerate" -framework "AssetsLibrary" -framework "AudioToolbox" -framework "CFNetwork" -framework "CoreAudio" -framework "CoreFoundation" -framework "CoreGraphics" -framework "DKNightVersion" -framework "DOUAudioStreamer" -framework "Foundation" -framework "ImageIO" -framework "Lottie" -framework "MBProgressHUD" -framework "MJRefresh" -framework "Masonry" -framework "MediaPlayer" -framework "MobileCoreServices" -framework "OpenGLES" -framework "QuartzCore" -framework "UIKit" -framework "YTKNetwork" -framework "YYCache" -framework "YYImage" -framework "YYWebImage" OTHER_LDFLAGS = $(inherited) -ObjC -l"sqlite3" -l"z" -framework "AFNetworking" -framework "AVFoundation" -framework "Accelerate" -framework "AssetsLibrary" -framework "AudioToolbox" -framework "CFNetwork" -framework "CoreAudio" -framework "CoreFoundation" -framework "CoreGraphics" -framework "DKNightVersion" -framework "DOUAudioStreamer" -framework "Foundation" -framework "ImageIO" -framework "Lottie" -framework "MBProgressHUD" -framework "MJRefresh" -framework "Masonry" -framework "MediaPlayer" -framework "MobileCoreServices" -framework "OpenGLES" -framework "QuartzCore" -framework "UIKit" -framework "YTKNetwork" -framework "YYCache" -framework "YYImage" -framework "YYModel" -framework "YYWebImage"
PODS_BUILD_DIR = ${BUILD_DIR} PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_PODFILE_DIR_PATH = ${SRCROOT}/. PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
......
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking" "${PODS_CONFIGURATION_BUILD_DIR}/DKNightVersion" "${PODS_CONFIGURATION_BUILD_DIR}/DOUAudioStreamer" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "${PODS_CONFIGURATION_BUILD_DIR}/YTKNetwork" "${PODS_CONFIGURATION_BUILD_DIR}/YYCache" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage" "${PODS_CONFIGURATION_BUILD_DIR}/YYWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios" "${PODS_ROOT}/YYImage/Vendor" FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking" "${PODS_CONFIGURATION_BUILD_DIR}/DKNightVersion" "${PODS_CONFIGURATION_BUILD_DIR}/DOUAudioStreamer" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry" "${PODS_CONFIGURATION_BUILD_DIR}/YTKNetwork" "${PODS_CONFIGURATION_BUILD_DIR}/YYCache" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage" "${PODS_CONFIGURATION_BUILD_DIR}/YYModel" "${PODS_CONFIGURATION_BUILD_DIR}/YYWebImage" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios" "${PODS_ROOT}/YYImage/Vendor"
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking/AFNetworking.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DKNightVersion/DKNightVersion.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DOUAudioStreamer/DOUAudioStreamer.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh/MJRefresh.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry/Masonry.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YTKNetwork/YTKNetwork.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYCache/YYCache.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage/YYImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYWebImage/YYWebImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios/Lottie.framework/Headers" HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/AFNetworking/AFNetworking.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DKNightVersion/DKNightVersion.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/DOUAudioStreamer/DOUAudioStreamer.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/MJRefresh/MJRefresh.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/Masonry/Masonry.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YTKNetwork/YTKNetwork.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYCache/YYCache.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYImage/YYImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYModel/YYModel.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/YYWebImage/YYWebImage.framework/Headers" "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios/Lottie.framework/Headers"
LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks'
OTHER_LDFLAGS = $(inherited) -ObjC -l"sqlite3" -l"z" -framework "AFNetworking" -framework "AVFoundation" -framework "Accelerate" -framework "AssetsLibrary" -framework "AudioToolbox" -framework "CFNetwork" -framework "CoreAudio" -framework "CoreFoundation" -framework "CoreGraphics" -framework "DKNightVersion" -framework "DOUAudioStreamer" -framework "Foundation" -framework "ImageIO" -framework "Lottie" -framework "MBProgressHUD" -framework "MJRefresh" -framework "Masonry" -framework "MediaPlayer" -framework "MobileCoreServices" -framework "OpenGLES" -framework "QuartzCore" -framework "UIKit" -framework "YTKNetwork" -framework "YYCache" -framework "YYImage" -framework "YYWebImage" OTHER_LDFLAGS = $(inherited) -ObjC -l"sqlite3" -l"z" -framework "AFNetworking" -framework "AVFoundation" -framework "Accelerate" -framework "AssetsLibrary" -framework "AudioToolbox" -framework "CFNetwork" -framework "CoreAudio" -framework "CoreFoundation" -framework "CoreGraphics" -framework "DKNightVersion" -framework "DOUAudioStreamer" -framework "Foundation" -framework "ImageIO" -framework "Lottie" -framework "MBProgressHUD" -framework "MJRefresh" -framework "Masonry" -framework "MediaPlayer" -framework "MobileCoreServices" -framework "OpenGLES" -framework "QuartzCore" -framework "UIKit" -framework "YTKNetwork" -framework "YYCache" -framework "YYImage" -framework "YYModel" -framework "YYWebImage"
PODS_BUILD_DIR = ${BUILD_DIR} PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_PODFILE_DIR_PATH = ${SRCROOT}/. PODS_PODFILE_DIR_PATH = ${SRCROOT}/.
......
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>${PRODUCT_NAME}</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0.4</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>${CURRENT_PROJECT_VERSION}</string>
<key>NSPrincipalClass</key>
<string></string>
</dict>
</plist>
#import <Foundation/Foundation.h>
@interface PodsDummy_YYModel : NSObject
@end
@implementation PodsDummy_YYModel
@end
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#else
#ifndef FOUNDATION_EXPORT
#if defined(__cplusplus)
#define FOUNDATION_EXPORT extern "C"
#else
#define FOUNDATION_EXPORT extern
#endif
#endif
#endif
#ifdef __OBJC__
#import <UIKit/UIKit.h>
#else
#ifndef FOUNDATION_EXPORT
#if defined(__cplusplus)
#define FOUNDATION_EXPORT extern "C"
#else
#define FOUNDATION_EXPORT extern
#endif
#endif
#endif
#import "NSObject+YYModel.h"
#import "YYClassInfo.h"
#import "YYModel.h"
FOUNDATION_EXPORT double YYModelVersionNumber;
FOUNDATION_EXPORT const unsigned char YYModelVersionString[];
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/YYModel
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
OTHER_LDFLAGS = $(inherited) -framework "CoreFoundation" -framework "Foundation"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/YYModel
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
framework module YYModel {
umbrella header "YYModel-umbrella.h"
export *
module * { export * }
}
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = NO
CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/YYModel
GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1
OTHER_LDFLAGS = $(inherited) -framework "CoreFoundation" -framework "Foundation"
PODS_BUILD_DIR = ${BUILD_DIR}
PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)
PODS_ROOT = ${SRCROOT}
PODS_TARGET_SRCROOT = ${PODS_ROOT}/YYModel
PODS_XCFRAMEWORKS_BUILD_DIR = $(PODS_CONFIGURATION_BUILD_DIR)/XCFrameworkIntermediates
PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier}
SKIP_INSTALL = YES
USE_RECURSIVE_SCRIPT_INPUTS_IN_SCRIPT_PHASES = YES
The MIT License (MIT)
Copyright (c) 2015 ibireme <ibireme@gmail.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
YYModel
==============
[![License MIT](https://img.shields.io/badge/license-MIT-green.svg?style=flat)](https://raw.githubusercontent.com/ibireme/YYModel/master/LICENSE)&nbsp;
[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)&nbsp;
[![CocoaPods](http://img.shields.io/cocoapods/v/YYModel.svg?style=flat)](http://cocoapods.org/?q= YYModel)&nbsp;
[![CocoaPods](http://img.shields.io/cocoapods/p/YYModel.svg?style=flat)](http://cocoapods.org/?q= YYModel)&nbsp;
[![Build Status](https://travis-ci.org/ibireme/YYModel.svg?branch=master)](https://travis-ci.org/ibireme/YYModel)&nbsp;
[![codecov.io](https://codecov.io/github/ibireme/YYModel/coverage.svg?branch=master)](https://codecov.io/github/ibireme/YYModel?branch=master)
High performance model framework for iOS/OSX.<br/>
(It's a component of [YYKit](https://github.com/ibireme/YYKit))
Performance
==============
Time cost (process GithubUser 10000 times on iPhone 6):
![Benchmark result](https://raw.github.com/ibireme/YYModel/master/Benchmark/Result.png
)
See `Benchmark/ModelBenchmark.xcodeproj` for more benchmark case.
Features
==============
- **High performance**: The conversion performance is close to handwriting code.
- **Automatic type conversion**: The object types can be automatically converted.
- **Type Safe**: All data types will be verified to ensure type-safe during the conversion process.
- **Non-intrusive**: There is no need to make the model class inherit from other base class.
- **Lightwight**: This library contains only 5 files.
- **Docs and unit testing**: 100% docs coverage, 99.6% code coverage.
Usage
==============
###Simple model json convert
// JSON:
{
"uid":123456,
"name":"Harry",
"created":"1965-07-31T00:00:00+0000"
}
// Model:
@interface User : NSObject
@property UInt64 uid;
@property NSString *name;
@property NSDate *created;
@end
@implementation User
@end
// Convert json to model:
User *user = [User yy_modelWithJSON:json];
// Convert model to json:
NSDictionary *json = [user yy_modelToJSONObject];
If the type of an object in JSON/Dictionary cannot be matched to the property of the model, the following automatic conversion is performed. If the automatic conversion failed, the value will be ignored.
<table>
<thead>
<tr>
<th>JSON/Dictionary</th>
<th>Model</th>
</tr>
</thead>
<tbody>
<tr>
<td>NSString</td>
<td>NSNumber,NSURL,SEL,Class</td>
</tr>
<tr>
<td>NSNumber</td>
<td>NSString</td>
</tr>
<tr>
<td>NSString/NSNumber</td>
<td>C number (BOOL,int,float,NSUInteger,UInt64,...)<br/>
NaN and Inf will be ignored</td>
</tr>
<tr>
<td>NSString</td>
<td>NSDate parsed with these formats:<br/>
yyyy-MM-dd<br/>
yyyy-MM-dd HH:mm:ss<br/>
yyyy-MM-dd'T'HH:mm:ss<br/>
yyyy-MM-dd'T'HH:mm:ssZ<br/>
EEE MMM dd HH:mm:ss Z yyyy
</td>
</tr>
<tr>
<td>NSDate</td>
<td>NSString formatted with ISO8601:<br/>
"YYYY-MM-dd'T'HH:mm:ssZ"</td>
</tr>
<tr>
<td>NSValue</td>
<td>struct (CGRect,CGSize,...)</td>
</tr>
<tr>
<td>NSNull</td>
<td>nil,0</td>
</tr>
<tr>
<td>"no","false",...</td>
<td>@(NO),0</td>
</tr>
<tr>
<td>"yes","true",...</td>
<td>@(YES),1</td>
</tr>
</tbody>
</table>
###Match model property to different JSON key
// JSON:
{
"n":"Harry Pottery",
"p": 256,
"ext" : {
"desc" : "A book written by J.K.Rowing."
},
"ID" : 100010
}
// Model:
@interface Book : NSObject
@property NSString *name;
@property NSInteger page;
@property NSString *desc;
@property NSString *bookID;
@end
@implementation Book
+ (NSDictionary *)modelCustomPropertyMapper {
return @{@"name" : @"n",
@"page" : @"p",
@"desc" : @"ext.desc",
@"bookID" : @[@"id",@"ID",@"book_id"]};
}
@end
You can map a json key (key path) or an array of json key (key path) to one or multiple property name. If there's no mapper for a property, it will use the property's name as default.
###Nested model
// JSON
{
"author":{
"name":"J.K.Rowling",
"birthday":"1965-07-31T00:00:00+0000"
},
"name":"Harry Potter",
"pages":256
}
// Model: (no need to do anything)
@interface Author : NSObject
@property NSString *name;
@property NSDate *birthday;
@end
@implementation Author
@end
@interface Book : NSObject
@property NSString *name;
@property NSUInteger pages;
@property Author *author;
@end
@implementation Book
@end
### Container property
@class Shadow, Border, Attachment;
@interface Attributes
@property NSString *name;
@property NSArray *shadows; //Array<Shadow>
@property NSSet *borders; //Set<Border>
@property NSMutableDictionary *attachments; //Dict<NSString,Attachment>
@end
@implementation Attributes
+ (NSDictionary *)modelContainerPropertyGenericClass {
// value should be Class or Class name.
return @{@"shadows" : [Shadow class],
@"borders" : Border.class,
@"attachments" : @"Attachment" };
}
@end
### Whitelist and blacklist
@interface User
@property NSString *name;
@property NSUInteger age;
@end
@implementation Attributes
+ (NSArray *)modelPropertyBlacklist {
return @[@"test1", @"test2"];
}
+ (NSArray *)modelPropertyWhitelist {
return @[@"name"];
}
@end
###Data validate and custom transform
// JSON:
{
"name":"Harry",
"timestamp" : 1445534567
}
// Model:
@interface User
@property NSString *name;
@property NSDate *createdAt;
@end
@implementation User
- (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic {
NSNumber *timestamp = dic[@"timestamp"];
if (![timestamp isKindOfClass:[NSNumber class]]) return NO;
_createdAt = [NSDate dateWithTimeIntervalSince1970:timestamp.floatValue];
return YES;
}
- (BOOL)modelCustomTransformToDictionary:(NSMutableDictionary *)dic {
if (!_createdAt) return NO;
dic[@"timestamp"] = @(n.timeIntervalSince1970);
return YES;
}
@end
###Coding/Copying/hash/equal/description
@interface YYShadow :NSObject <NSCoding, NSCopying>
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) CGSize size;
@end
@implementation YYShadow
- (void)encodeWithCoder:(NSCoder *)aCoder { [self yy_modelEncodeWithCoder:aCoder]; }
- (id)initWithCoder:(NSCoder *)aDecoder { self = [super init]; return [self yy_modelInitWithCoder:aDecoder]; }
- (id)copyWithZone:(NSZone *)zone { return [self yy_modelCopy]; }
- (NSUInteger)hash { return [self yy_modelHash]; }
- (BOOL)isEqual:(id)object { return [self yy_modelIsEqual:object]; }
- (NSString *)description { return [self yy_modelDescription]; }
@end
Installation
==============
### CocoaPods
1. Add `pod 'YYModel'` to your Podfile.
2. Run `pod install` or `pod update`.
3. Import \<YYModel/YYModel.h\>.
### Carthage
1. Add `github "ibireme/YYModel"` to your Cartfile.
2. Run `carthage update --platform ios` and add the framework to your project.
3. Import \<YYModel/YYModel.h\>.
### Manually
1. Download all the files in the YYModel subdirectory.
2. Add the source files to your Xcode project.
3. Import `YYModel.h`.
Documentation
==============
Full API documentation is available on [CocoaDocs](http://cocoadocs.org/docsets/YYModel/).<br/>
You can also install documentation locally using [appledoc](https://github.com/tomaz/appledoc).
Requirements
==============
This library requires `iOS 6.0+` and `Xcode 7.0+`.
License
==============
YYModel is provided under the MIT license. See LICENSE file for details.
<br/><br/>
---
中文介绍
==============
高性能 iOS/OSX 模型转换框架。<br/>
(该项目是 [YYKit](https://github.com/ibireme/YYKit) 组件之一)
性能
==============
处理 GithubUser 数据 10000 次耗时统计 (iPhone 6):
![Benchmark result](https://raw.github.com/ibireme/YYModel/master/Benchmark/Result.png
)
更多测试代码和用例见 `Benchmark/ModelBenchmark.xcodeproj`
特性
==============
- **高性能**: 模型转换性能接近手写解析代码。
- **自动类型转换**: 对象类型可以自动转换,详情见下方表格。
- **类型安全**: 转换过程中,所有的数据类型都会被检测一遍,以保证类型安全,避免崩溃问题。
- **无侵入性**: 模型无需继承自其他基类。
- **轻量**: 该框架只有 5 个文件 (包括.h文件)。
- **文档和单元测试**: 文档覆盖率100%, 代码覆盖率99.6%。
使用方法
==============
###简单的 Model 与 JSON 相互转换
// JSON:
{
"uid":123456,
"name":"Harry",
"created":"1965-07-31T00:00:00+0000"
}
// Model:
@interface User : NSObject
@property UInt64 uid;
@property NSString *name;
@property NSDate *created;
@end
@implementation User
@end
// 将 JSON (NSData,NSString,NSDictionary) 转换为 Model:
User *user = [User yy_modelWithJSON:json];
// 将 Model 转换为 JSON 对象:
NSDictionary *json = [user yy_modelToJSONObject];
当 JSON/Dictionary 中的对象类型与 Model 属性不一致时,YYModel 将会进行如下自动转换。自动转换不支持的值将会被忽略,以避免各种潜在的崩溃问题。
<table>
<thead>
<tr>
<th>JSON/Dictionary</th>
<th>Model</th>
</tr>
</thead>
<tbody>
<tr>
<td>NSString</td>
<td>NSNumber,NSURL,SEL,Class</td>
</tr>
<tr>
<td>NSNumber</td>
<td>NSString</td>
</tr>
<tr>
<td>NSString/NSNumber</td>
<td>基础类型 (BOOL,int,float,NSUInteger,UInt64,...)<br/>
NaN 和 Inf 会被忽略</td>
</tr>
<tr>
<td>NSString</td>
<td>NSDate 以下列格式解析:<br/>
yyyy-MM-dd<br/>
yyyy-MM-dd HH:mm:ss<br/>
yyyy-MM-dd'T'HH:mm:ss<br/>
yyyy-MM-dd'T'HH:mm:ssZ<br/>
EEE MMM dd HH:mm:ss Z yyyy
</td>
</tr>
<tr>
<td>NSDate</td>
<td>NSString 格式化为 ISO8601:<br/>
"YYYY-MM-dd'T'HH:mm:ssZ"</td>
</tr>
<tr>
<td>NSValue</td>
<td>struct (CGRect,CGSize,...)</td>
</tr>
<tr>
<td>NSNull</td>
<td>nil,0</td>
</tr>
<tr>
<td>"no","false",...</td>
<td>@(NO),0</td>
</tr>
<tr>
<td>"yes","true",...</td>
<td>@(YES),1</td>
</tr>
</tbody>
</table>
###Model 属性名和 JSON 中的 Key 不相同
// JSON:
{
"n":"Harry Pottery",
"p": 256,
"ext" : {
"desc" : "A book written by J.K.Rowing."
},
"ID" : 100010
}
// Model:
@interface Book : NSObject
@property NSString *name;
@property NSInteger page;
@property NSString *desc;
@property NSString *bookID;
@end
@implementation Book
//返回一个 Dict,将 Model 属性名对映射到 JSON 的 Key。
+ (NSDictionary *)modelCustomPropertyMapper {
return @{@"name" : @"n",
@"page" : @"p",
@"desc" : @"ext.desc",
@"bookID" : @[@"id",@"ID",@"book_id"]};
}
@end
你可以把一个或一组 json key (key path) 映射到一个或多个属性。如果一个属性没有映射关系,那默认会使用相同属性名作为映射。
在 json->model 的过程中:如果一个属性对应了多个 json key,那么转换过程会按顺序查找,并使用第一个不为空的值。
在 model->json 的过程中:如果一个属性对应了多个 json key (key path),那么转换过程仅会处理第一个 json key (key path);如果多个属性对应了同一个 json key,则转换过过程会使用其中任意一个不为空的值。
###Model 包含其他 Model
// JSON
{
"author":{
"name":"J.K.Rowling",
"birthday":"1965-07-31T00:00:00+0000"
},
"name":"Harry Potter",
"pages":256
}
// Model: 什么都不用做,转换会自动完成
@interface Author : NSObject
@property NSString *name;
@property NSDate *birthday;
@end
@implementation Author
@end
@interface Book : NSObject
@property NSString *name;
@property NSUInteger pages;
@property Author *author; //Book 包含 Author 属性
@end
@implementation Book
@end
###容器类属性
@class Shadow, Border, Attachment;
@interface Attributes
@property NSString *name;
@property NSArray *shadows; //Array<Shadow>
@property NSSet *borders; //Set<Border>
@property NSMutableDictionary *attachments; //Dict<NSString,Attachment>
@end
@implementation Attributes
// 返回容器类中的所需要存放的数据类型 (以 Class 或 Class Name 的形式)。
+ (NSDictionary *)modelContainerPropertyGenericClass {
return @{@"shadows" : [Shadow class],
@"borders" : Border.class,
@"attachments" : @"Attachment" };
}
@end
###黑名单与白名单
@interface User
@property NSString *name;
@property NSUInteger age;
@end
@implementation Attributes
// 如果实现了该方法,则处理过程中会忽略该列表内的所有属性
+ (NSArray *)modelPropertyBlacklist {
return @[@"test1", @"test2"];
}
// 如果实现了该方法,则处理过程中不会处理该列表外的属性。
+ (NSArray *)modelPropertyWhitelist {
return @[@"name"];
}
@end
###数据校验与自定义转换
// JSON:
{
"name":"Harry",
"timestamp" : 1445534567
}
// Model:
@interface User
@property NSString *name;
@property NSDate *createdAt;
@end
@implementation User
// 当 JSON 转为 Model 完成后,该方法会被调用。
// 你可以在这里对数据进行校验,如果校验不通过,可以返回 NO,则该 Model 会被忽略。
// 你也可以在这里做一些自动转换不能完成的工作。
- (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic {
NSNumber *timestamp = dic[@"timestamp"];
if (![timestamp isKindOfClass:[NSNumber class]]) return NO;
_createdAt = [NSDate dateWithTimeIntervalSince1970:timestamp.floatValue];
return YES;
}
// 当 Model 转为 JSON 完成后,该方法会被调用。
// 你可以在这里对数据进行校验,如果校验不通过,可以返回 NO,则该 Model 会被忽略。
// 你也可以在这里做一些自动转换不能完成的工作。
- (BOOL)modelCustomTransformToDictionary:(NSMutableDictionary *)dic {
if (!_createdAt) return NO;
dic[@"timestamp"] = @(n.timeIntervalSince1970);
return YES;
}
@end
###Coding/Copying/hash/equal/description
@interface YYShadow :NSObject <NSCoding, NSCopying>
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) CGSize size;
@end
@implementation YYShadow
// 直接添加以下代码即可自动完成
- (void)encodeWithCoder:(NSCoder *)aCoder { [self yy_modelEncodeWithCoder:aCoder]; }
- (id)initWithCoder:(NSCoder *)aDecoder { self = [super init]; return [self yy_modelInitWithCoder:aDecoder]; }
- (id)copyWithZone:(NSZone *)zone { return [self yy_modelCopy]; }
- (NSUInteger)hash { return [self yy_modelHash]; }
- (BOOL)isEqual:(id)object { return [self yy_modelIsEqual:object]; }
- (NSString *)description { return [self yy_modelDescription]; }
@end
安装
==============
### CocoaPods
1. 在 Podfile 中添加 `pod 'YYModel'`
2. 执行 `pod install``pod update`
3. 导入 \<YYModel/YYModel.h\>
### Carthage
1. 在 Cartfile 中添加 `github "ibireme/YYModel"`
2. 执行 `carthage update --platform ios` 并将生成的 framework 添加到你的工程。
3. 导入 \<YYModel/YYModel.h\>
### 手动安装
1. 下载 YYModel 文件夹内的所有内容。
2. 将 YYModel 内的源文件添加(拖放)到你的工程。
3. 导入 `YYModel.h`
文档
==============
你可以在 [CocoaDocs](http://cocoadocs.org/docsets/YYModel/) 查看在线 API 文档,也可以用 [appledoc](https://github.com/tomaz/appledoc) 本地生成文档。
系统要求
==============
该项目最低支持 `iOS 6.0``Xcode 7.0`
许可证
==============
YYModel 使用 MIT 许可证,详情见 LICENSE 文件。
相关链接
==============
[iOS JSON 模型转换库评测](http://blog.ibireme.com/2015/10/23/ios_model_framework_benchmark/)
//
// NSObject+YYModel.h
// YYModel <https://github.com/ibireme/YYModel>
//
// Created by ibireme on 15/5/10.
// Copyright (c) 2015 ibireme.
//
// This source code is licensed under the MIT-style license found in the
// LICENSE file in the root directory of this source tree.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/**
Provide some data-model method:
* Convert json to any object, or convert any object to json.
* Set object properties with a key-value dictionary (like KVC).
* Implementations of `NSCoding`, `NSCopying`, `-hash` and `-isEqual:`.
See `YYModel` protocol for custom methods.
Sample Code:
********************** json convertor *********************
@interface YYAuthor : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) NSDate *birthday;
@end
@implementation YYAuthor
@end
@interface YYBook : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSUInteger pages;
@property (nonatomic, strong) YYAuthor *author;
@end
@implementation YYBook
@end
int main() {
// create model from json
YYBook *book = [YYBook yy_modelWithJSON:@"{\"name\": \"Harry Potter\", \"pages\": 256, \"author\": {\"name\": \"J.K.Rowling\", \"birthday\": \"1965-07-31\" }}"];
// convert model to json
NSString *json = [book yy_modelToJSONString];
// {"author":{"name":"J.K.Rowling","birthday":"1965-07-31T00:00:00+0000"},"name":"Harry Potter","pages":256}
}
********************** Coding/Copying/hash/equal *********************
@interface YYShadow :NSObject <NSCoding, NSCopying>
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) CGSize size;
@end
@implementation YYShadow
- (void)encodeWithCoder:(NSCoder *)aCoder { [self yy_modelEncodeWithCoder:aCoder]; }
- (id)initWithCoder:(NSCoder *)aDecoder { self = [super init]; return [self yy_modelInitWithCoder:aDecoder]; }
- (id)copyWithZone:(NSZone *)zone { return [self yy_modelCopy]; }
- (NSUInteger)hash { return [self yy_modelHash]; }
- (BOOL)isEqual:(id)object { return [self yy_modelIsEqual:object]; }
@end
*/
@interface NSObject (YYModel)
/**
Creates and returns a new instance of the receiver from a json.
This method is thread-safe.
@param json A json object in `NSDictionary`, `NSString` or `NSData`.
@return A new instance created from the json, or nil if an error occurs.
*/
+ (nullable instancetype)yy_modelWithJSON:(id)json;
/**
Creates and returns a new instance of the receiver from a key-value dictionary.
This method is thread-safe.
@param dictionary A key-value dictionary mapped to the instance's properties.
Any invalid key-value pair in dictionary will be ignored.
@return A new instance created from the dictionary, or nil if an error occurs.
@discussion The key in `dictionary` will mapped to the reciever's property name,
and the value will set to the property. If the value's type does not match the
property, this method will try to convert the value based on these rules:
`NSString` or `NSNumber` -> c number, such as BOOL, int, long, float, NSUInteger...
`NSString` -> NSDate, parsed with format "yyyy-MM-dd'T'HH:mm:ssZ", "yyyy-MM-dd HH:mm:ss" or "yyyy-MM-dd".
`NSString` -> NSURL.
`NSValue` -> struct or union, such as CGRect, CGSize, ...
`NSString` -> SEL, Class.
*/
+ (nullable instancetype)yy_modelWithDictionary:(NSDictionary *)dictionary;
/**
Set the receiver's properties with a json object.
@discussion Any invalid data in json will be ignored.
@param json A json object of `NSDictionary`, `NSString` or `NSData`, mapped to the
receiver's properties.
@return Whether succeed.
*/
- (BOOL)yy_modelSetWithJSON:(id)json;
/**
Set the receiver's properties with a key-value dictionary.
@param dic A key-value dictionary mapped to the receiver's properties.
Any invalid key-value pair in dictionary will be ignored.
@discussion The key in `dictionary` will mapped to the reciever's property name,
and the value will set to the property. If the value's type doesn't match the
property, this method will try to convert the value based on these rules:
`NSString`, `NSNumber` -> c number, such as BOOL, int, long, float, NSUInteger...
`NSString` -> NSDate, parsed with format "yyyy-MM-dd'T'HH:mm:ssZ", "yyyy-MM-dd HH:mm:ss" or "yyyy-MM-dd".
`NSString` -> NSURL.
`NSValue` -> struct or union, such as CGRect, CGSize, ...
`NSString` -> SEL, Class.
@return Whether succeed.
*/
- (BOOL)yy_modelSetWithDictionary:(NSDictionary *)dic;
/**
Generate a json object from the receiver's properties.
@return A json object in `NSDictionary` or `NSArray`, or nil if an error occurs.
See [NSJSONSerialization isValidJSONObject] for more information.
@discussion Any of the invalid property is ignored.
If the reciver is `NSArray`, `NSDictionary` or `NSSet`, it just convert
the inner object to json object.
*/
- (nullable id)yy_modelToJSONObject;
/**
Generate a json string's data from the receiver's properties.
@return A json string's data, or nil if an error occurs.
@discussion Any of the invalid property is ignored.
If the reciver is `NSArray`, `NSDictionary` or `NSSet`, it will also convert the
inner object to json string.
*/
- (nullable NSData *)yy_modelToJSONData;
/**
Generate a json string from the receiver's properties.
@return A json string, or nil if an error occurs.
@discussion Any of the invalid property is ignored.
If the reciver is `NSArray`, `NSDictionary` or `NSSet`, it will also convert the
inner object to json string.
*/
- (nullable NSString *)yy_modelToJSONString;
/**
Copy a instance with the receiver's properties.
@return A copied instance, or nil if an error occurs.
*/
- (nullable id)yy_modelCopy;
/**
Encode the receiver's properties to a coder.
@param aCoder An archiver object.
*/
- (void)yy_modelEncodeWithCoder:(NSCoder *)aCoder;
/**
Decode the receiver's properties from a decoder.
@param aDecoder An archiver object.
@return self
*/
- (id)yy_modelInitWithCoder:(NSCoder *)aDecoder;
/**
Get a hash code with the receiver's properties.
@return Hash code.
*/
- (NSUInteger)yy_modelHash;
/**
Compares the receiver with another object for equality, based on properties.
@param model Another object.
@return `YES` if the reciever is equal to the object, otherwise `NO`.
*/
- (BOOL)yy_modelIsEqual:(id)model;
/**
Description method for debugging purposes based on properties.
@return A string that describes the contents of the receiver.
*/
- (NSString *)yy_modelDescription;
@end
/**
Provide some data-model method for NSArray.
*/
@interface NSArray (YYModel)
/**
Creates and returns an array from a json-array.
This method is thread-safe.
@param cls The instance's class in array.
@param json A json array of `NSArray`, `NSString` or `NSData`.
Example: [{"name","Mary"},{name:"Joe"}]
@return A array, or nil if an error occurs.
*/
+ (nullable NSArray *)yy_modelArrayWithClass:(Class)cls json:(id)json;
@end
/**
Provide some data-model method for NSDictionary.
*/
@interface NSDictionary (YYModel)
/**
Creates and returns a dictionary from a json.
This method is thread-safe.
@param cls The value instance's class in dictionary.
@param json A json dictionary of `NSDictionary`, `NSString` or `NSData`.
Example: {"user1":{"name","Mary"}, "user2": {name:"Joe"}}
@return A dictionary, or nil if an error occurs.
*/
+ (nullable NSDictionary *)yy_modelDictionaryWithClass:(Class)cls json:(id)json;
@end
/**
If the default model transform does not fit to your model class, implement one or
more method in this protocol to change the default key-value transform process.
There's no need to add '<YYModel>' to your class header.
*/
@protocol YYModel <NSObject>
@optional
/**
Custom property mapper.
@discussion If the key in JSON/Dictionary does not match to the model's property name,
implements this method and returns the additional mapper.
Example:
json:
{
"n":"Harry Pottery",
"p": 256,
"ext" : {
"desc" : "A book written by J.K.Rowling."
},
"ID" : 100010
}
model:
@interface YYBook : NSObject
@property NSString *name;
@property NSInteger page;
@property NSString *desc;
@property NSString *bookID;
@end
@implementation YYBook
+ (NSDictionary *)modelCustomPropertyMapper {
return @{@"name" : @"n",
@"page" : @"p",
@"desc" : @"ext.desc",
@"bookID": @[@"id", @"ID", @"book_id"]};
}
@end
@return A custom mapper for properties.
*/
+ (nullable NSDictionary<NSString *, id> *)modelCustomPropertyMapper;
/**
The generic class mapper for container properties.
@discussion If the property is a container object, such as NSArray/NSSet/NSDictionary,
implements this method and returns a property->class mapper, tells which kind of
object will be add to the array/set/dictionary.
Example:
@class YYShadow, YYBorder, YYAttachment;
@interface YYAttributes
@property NSString *name;
@property NSArray *shadows;
@property NSSet *borders;
@property NSDictionary *attachments;
@end
@implementation YYAttributes
+ (NSDictionary *)modelContainerPropertyGenericClass {
return @{@"shadows" : [YYShadow class],
@"borders" : YYBorder.class,
@"attachments" : @"YYAttachment" };
}
@end
@return A class mapper.
*/
+ (nullable NSDictionary<NSString *, id> *)modelContainerPropertyGenericClass;
/**
If you need to create instances of different classes during json->object transform,
use the method to choose custom class based on dictionary data.
@discussion If the model implements this method, it will be called to determine resulting class
during `+modelWithJSON:`, `+modelWithDictionary:`, conveting object of properties of parent objects
(both singular and containers via `+modelContainerPropertyGenericClass`).
Example:
@class YYCircle, YYRectangle, YYLine;
@implementation YYShape
+ (Class)modelCustomClassForDictionary:(NSDictionary*)dictionary {
if (dictionary[@"radius"] != nil) {
return [YYCircle class];
} else if (dictionary[@"width"] != nil) {
return [YYRectangle class];
} else if (dictionary[@"y2"] != nil) {
return [YYLine class];
} else {
return [self class];
}
}
@end
@param dictionary The json/kv dictionary.
@return Class to create from this dictionary, `nil` to use current class.
*/
+ (nullable Class)modelCustomClassForDictionary:(NSDictionary *)dictionary;
/**
All the properties in blacklist will be ignored in model transform process.
Returns nil to ignore this feature.
@return An array of property's name.
*/
+ (nullable NSArray<NSString *> *)modelPropertyBlacklist;
/**
If a property is not in the whitelist, it will be ignored in model transform process.
Returns nil to ignore this feature.
@return An array of property's name.
*/
+ (nullable NSArray<NSString *> *)modelPropertyWhitelist;
/**
This method's behavior is similar to `- (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic;`,
but be called before the model transform.
@discussion If the model implements this method, it will be called before
`+modelWithJSON:`, `+modelWithDictionary:`, `-modelSetWithJSON:` and `-modelSetWithDictionary:`.
If this method returns nil, the transform process will ignore this model.
@param dic The json/kv dictionary.
@return Returns the modified dictionary, or nil to ignore this model.
*/
- (NSDictionary *)modelCustomWillTransformFromDictionary:(NSDictionary *)dic;
/**
If the default json-to-model transform does not fit to your model object, implement
this method to do additional process. You can also use this method to validate the
model's properties.
@discussion If the model implements this method, it will be called at the end of
`+modelWithJSON:`, `+modelWithDictionary:`, `-modelSetWithJSON:` and `-modelSetWithDictionary:`.
If this method returns NO, the transform process will ignore this model.
@param dic The json/kv dictionary.
@return Returns YES if the model is valid, or NO to ignore this model.
*/
- (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic;
/**
If the default model-to-json transform does not fit to your model class, implement
this method to do additional process. You can also use this method to validate the
json dictionary.
@discussion If the model implements this method, it will be called at the end of
`-modelToJSONObject` and `-modelToJSONString`.
If this method returns NO, the transform process will ignore this json dictionary.
@param dic The json dictionary.
@return Returns YES if the model is valid, or NO to ignore this model.
*/
- (BOOL)modelCustomTransformToDictionary:(NSMutableDictionary *)dic;
@end
NS_ASSUME_NONNULL_END
//
// NSObject+YYModel.m
// YYModel <https://github.com/ibireme/YYModel>
//
// Created by ibireme on 15/5/10.
// Copyright (c) 2015 ibireme.
//
// This source code is licensed under the MIT-style license found in the
// LICENSE file in the root directory of this source tree.
//
#import "NSObject+YYModel.h"
#import "YYClassInfo.h"
#import <objc/message.h>
#define force_inline __inline__ __attribute__((always_inline))
/// Foundation Class Type
typedef NS_ENUM (NSUInteger, YYEncodingNSType) {
YYEncodingTypeNSUnknown = 0,
YYEncodingTypeNSString,
YYEncodingTypeNSMutableString,
YYEncodingTypeNSValue,
YYEncodingTypeNSNumber,
YYEncodingTypeNSDecimalNumber,
YYEncodingTypeNSData,
YYEncodingTypeNSMutableData,
YYEncodingTypeNSDate,
YYEncodingTypeNSURL,
YYEncodingTypeNSArray,
YYEncodingTypeNSMutableArray,
YYEncodingTypeNSDictionary,
YYEncodingTypeNSMutableDictionary,
YYEncodingTypeNSSet,
YYEncodingTypeNSMutableSet,
};
/// Get the Foundation class type from property info.
static force_inline YYEncodingNSType YYClassGetNSType(Class cls) {
if (!cls) return YYEncodingTypeNSUnknown;
if ([cls isSubclassOfClass:[NSMutableString class]]) return YYEncodingTypeNSMutableString;
if ([cls isSubclassOfClass:[NSString class]]) return YYEncodingTypeNSString;
if ([cls isSubclassOfClass:[NSDecimalNumber class]]) return YYEncodingTypeNSDecimalNumber;
if ([cls isSubclassOfClass:[NSNumber class]]) return YYEncodingTypeNSNumber;
if ([cls isSubclassOfClass:[NSValue class]]) return YYEncodingTypeNSValue;
if ([cls isSubclassOfClass:[NSMutableData class]]) return YYEncodingTypeNSMutableData;
if ([cls isSubclassOfClass:[NSData class]]) return YYEncodingTypeNSData;
if ([cls isSubclassOfClass:[NSDate class]]) return YYEncodingTypeNSDate;
if ([cls isSubclassOfClass:[NSURL class]]) return YYEncodingTypeNSURL;
if ([cls isSubclassOfClass:[NSMutableArray class]]) return YYEncodingTypeNSMutableArray;
if ([cls isSubclassOfClass:[NSArray class]]) return YYEncodingTypeNSArray;
if ([cls isSubclassOfClass:[NSMutableDictionary class]]) return YYEncodingTypeNSMutableDictionary;
if ([cls isSubclassOfClass:[NSDictionary class]]) return YYEncodingTypeNSDictionary;
if ([cls isSubclassOfClass:[NSMutableSet class]]) return YYEncodingTypeNSMutableSet;
if ([cls isSubclassOfClass:[NSSet class]]) return YYEncodingTypeNSSet;
return YYEncodingTypeNSUnknown;
}
/// Whether the type is c number.
static force_inline BOOL YYEncodingTypeIsCNumber(YYEncodingType type) {
switch (type & YYEncodingTypeMask) {
case YYEncodingTypeBool:
case YYEncodingTypeInt8:
case YYEncodingTypeUInt8:
case YYEncodingTypeInt16:
case YYEncodingTypeUInt16:
case YYEncodingTypeInt32:
case YYEncodingTypeUInt32:
case YYEncodingTypeInt64:
case YYEncodingTypeUInt64:
case YYEncodingTypeFloat:
case YYEncodingTypeDouble:
case YYEncodingTypeLongDouble: return YES;
default: return NO;
}
}
/// Parse a number value from 'id'.
static force_inline NSNumber *YYNSNumberCreateFromID(__unsafe_unretained id value) {
static NSCharacterSet *dot;
static NSDictionary *dic;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
dot = [NSCharacterSet characterSetWithRange:NSMakeRange('.', 1)];
dic = @{@"TRUE" : @(YES),
@"True" : @(YES),
@"true" : @(YES),
@"FALSE" : @(NO),
@"False" : @(NO),
@"false" : @(NO),
@"YES" : @(YES),
@"Yes" : @(YES),
@"yes" : @(YES),
@"NO" : @(NO),
@"No" : @(NO),
@"no" : @(NO),
@"NIL" : (id)kCFNull,
@"Nil" : (id)kCFNull,
@"nil" : (id)kCFNull,
@"NULL" : (id)kCFNull,
@"Null" : (id)kCFNull,
@"null" : (id)kCFNull,
@"(NULL)" : (id)kCFNull,
@"(Null)" : (id)kCFNull,
@"(null)" : (id)kCFNull,
@"<NULL>" : (id)kCFNull,
@"<Null>" : (id)kCFNull,
@"<null>" : (id)kCFNull};
});
if (!value || value == (id)kCFNull) return nil;
if ([value isKindOfClass:[NSNumber class]]) return value;
if ([value isKindOfClass:[NSString class]]) {
NSNumber *num = dic[value];
if (num) {
if (num == (id)kCFNull) return nil;
return num;
}
if ([(NSString *)value rangeOfCharacterFromSet:dot].location != NSNotFound) {
const char *cstring = ((NSString *)value).UTF8String;
if (!cstring) return nil;
double num = atof(cstring);
if (isnan(num) || isinf(num)) return nil;
return @(num);
} else {
const char *cstring = ((NSString *)value).UTF8String;
if (!cstring) return nil;
return @(atoll(cstring));
}
}
return nil;
}
/// Parse string to date.
static force_inline NSDate *YYNSDateFromString(__unsafe_unretained NSString *string) {
typedef NSDate* (^YYNSDateParseBlock)(NSString *string);
#define kParserNum 34
static YYNSDateParseBlock blocks[kParserNum + 1] = {0};
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
{
/*
2014-01-20 // Google
*/
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
formatter.dateFormat = @"yyyy-MM-dd";
blocks[10] = ^(NSString *string) { return [formatter dateFromString:string]; };
}
{
/*
2014-01-20 12:24:48
2014-01-20T12:24:48 // Google
2014-01-20 12:24:48.000
2014-01-20T12:24:48.000
*/
NSDateFormatter *formatter1 = [[NSDateFormatter alloc] init];
formatter1.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter1.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
formatter1.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss";
NSDateFormatter *formatter2 = [[NSDateFormatter alloc] init];
formatter2.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter2.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
formatter2.dateFormat = @"yyyy-MM-dd HH:mm:ss";
NSDateFormatter *formatter3 = [[NSDateFormatter alloc] init];
formatter3.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter3.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
formatter3.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSS";
NSDateFormatter *formatter4 = [[NSDateFormatter alloc] init];
formatter4.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter4.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
formatter4.dateFormat = @"yyyy-MM-dd HH:mm:ss.SSS";
blocks[19] = ^(NSString *string) {
if ([string characterAtIndex:10] == 'T') {
return [formatter1 dateFromString:string];
} else {
return [formatter2 dateFromString:string];
}
};
blocks[23] = ^(NSString *string) {
if ([string characterAtIndex:10] == 'T') {
return [formatter3 dateFromString:string];
} else {
return [formatter4 dateFromString:string];
}
};
}
{
/*
2014-01-20T12:24:48Z // Github, Apple
2014-01-20T12:24:48+0800 // Facebook
2014-01-20T12:24:48+12:00 // Google
2014-01-20T12:24:48.000Z
2014-01-20T12:24:48.000+0800
2014-01-20T12:24:48.000+12:00
*/
NSDateFormatter *formatter = [NSDateFormatter new];
formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ";
NSDateFormatter *formatter2 = [NSDateFormatter new];
formatter2.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter2.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSSZ";
blocks[20] = ^(NSString *string) { return [formatter dateFromString:string]; };
blocks[24] = ^(NSString *string) { return [formatter dateFromString:string]?: [formatter2 dateFromString:string]; };
blocks[25] = ^(NSString *string) { return [formatter dateFromString:string]; };
blocks[28] = ^(NSString *string) { return [formatter2 dateFromString:string]; };
blocks[29] = ^(NSString *string) { return [formatter2 dateFromString:string]; };
}
{
/*
Fri Sep 04 00:12:21 +0800 2015 // Weibo, Twitter
Fri Sep 04 00:12:21.000 +0800 2015
*/
NSDateFormatter *formatter = [NSDateFormatter new];
formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter.dateFormat = @"EEE MMM dd HH:mm:ss Z yyyy";
NSDateFormatter *formatter2 = [NSDateFormatter new];
formatter2.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter2.dateFormat = @"EEE MMM dd HH:mm:ss.SSS Z yyyy";
blocks[30] = ^(NSString *string) { return [formatter dateFromString:string]; };
blocks[34] = ^(NSString *string) { return [formatter2 dateFromString:string]; };
}
});
if (!string) return nil;
if (string.length > kParserNum) return nil;
YYNSDateParseBlock parser = blocks[string.length];
if (!parser) return nil;
return parser(string);
#undef kParserNum
}
/// Get the 'NSBlock' class.
static force_inline Class YYNSBlockClass() {
static Class cls;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
void (^block)(void) = ^{};
cls = ((NSObject *)block).class;
while (class_getSuperclass(cls) != [NSObject class]) {
cls = class_getSuperclass(cls);
}
});
return cls; // current is "NSBlock"
}
/**
Get the ISO date formatter.
ISO8601 format example:
2010-07-09T16:13:30+12:00
2011-01-11T11:11:11+0000
2011-01-26T19:06:43Z
length: 20/24/25
*/
static force_inline NSDateFormatter *YYISODateFormatter() {
static NSDateFormatter *formatter = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
formatter = [[NSDateFormatter alloc] init];
formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ";
});
return formatter;
}
/// Get the value with key paths from dictionary
/// The dic should be NSDictionary, and the keyPath should not be nil.
static force_inline id YYValueForKeyPath(__unsafe_unretained NSDictionary *dic, __unsafe_unretained NSArray *keyPaths) {
id value = nil;
for (NSUInteger i = 0, max = keyPaths.count; i < max; i++) {
value = dic[keyPaths[i]];
if (i + 1 < max) {
if ([value isKindOfClass:[NSDictionary class]]) {
dic = value;
} else {
return nil;
}
}
}
return value;
}
/// Get the value with multi key (or key path) from dictionary
/// The dic should be NSDictionary
static force_inline id YYValueForMultiKeys(__unsafe_unretained NSDictionary *dic, __unsafe_unretained NSArray *multiKeys) {
id value = nil;
for (NSString *key in multiKeys) {
if ([key isKindOfClass:[NSString class]]) {
value = dic[key];
if (value) break;
} else {
value = YYValueForKeyPath(dic, (NSArray *)key);
if (value) break;
}
}
return value;
}
/// A property info in object model.
@interface _YYModelPropertyMeta : NSObject {
@package
NSString *_name; ///< property's name
YYEncodingType _type; ///< property's type
YYEncodingNSType _nsType; ///< property's Foundation type
BOOL _isCNumber; ///< is c number type
Class _cls; ///< property's class, or nil
Class _genericCls; ///< container's generic class, or nil if threr's no generic class
SEL _getter; ///< getter, or nil if the instances cannot respond
SEL _setter; ///< setter, or nil if the instances cannot respond
BOOL _isKVCCompatible; ///< YES if it can access with key-value coding
BOOL _isStructAvailableForKeyedArchiver; ///< YES if the struct can encoded with keyed archiver/unarchiver
BOOL _hasCustomClassFromDictionary; ///< class/generic class implements +modelCustomClassForDictionary:
/*
property->key: _mappedToKey:key _mappedToKeyPath:nil _mappedToKeyArray:nil
property->keyPath: _mappedToKey:keyPath _mappedToKeyPath:keyPath(array) _mappedToKeyArray:nil
property->keys: _mappedToKey:keys[0] _mappedToKeyPath:nil/keyPath _mappedToKeyArray:keys(array)
*/
NSString *_mappedToKey; ///< the key mapped to
NSArray *_mappedToKeyPath; ///< the key path mapped to (nil if the name is not key path)
NSArray *_mappedToKeyArray; ///< the key(NSString) or keyPath(NSArray) array (nil if not mapped to multiple keys)
YYClassPropertyInfo *_info; ///< property's info
_YYModelPropertyMeta *_next; ///< next meta if there are multiple properties mapped to the same key.
}
@end
@implementation _YYModelPropertyMeta
+ (instancetype)metaWithClassInfo:(YYClassInfo *)classInfo propertyInfo:(YYClassPropertyInfo *)propertyInfo generic:(Class)generic {
// support pseudo generic class with protocol name
if (!generic && propertyInfo.protocols) {
for (NSString *protocol in propertyInfo.protocols) {
Class cls = objc_getClass(protocol.UTF8String);
if (cls) {
generic = cls;
break;
}
}
}
_YYModelPropertyMeta *meta = [self new];
meta->_name = propertyInfo.name;
meta->_type = propertyInfo.type;
meta->_info = propertyInfo;
meta->_genericCls = generic;
if ((meta->_type & YYEncodingTypeMask) == YYEncodingTypeObject) {
meta->_nsType = YYClassGetNSType(propertyInfo.cls);
} else {
meta->_isCNumber = YYEncodingTypeIsCNumber(meta->_type);
}
if ((meta->_type & YYEncodingTypeMask) == YYEncodingTypeStruct) {
/*
It seems that NSKeyedUnarchiver cannot decode NSValue except these structs:
*/
static NSSet *types = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSMutableSet *set = [NSMutableSet new];
// 32 bit
[set addObject:@"{CGSize=ff}"];
[set addObject:@"{CGPoint=ff}"];
[set addObject:@"{CGRect={CGPoint=ff}{CGSize=ff}}"];
[set addObject:@"{CGAffineTransform=ffffff}"];
[set addObject:@"{UIEdgeInsets=ffff}"];
[set addObject:@"{UIOffset=ff}"];
// 64 bit
[set addObject:@"{CGSize=dd}"];
[set addObject:@"{CGPoint=dd}"];
[set addObject:@"{CGRect={CGPoint=dd}{CGSize=dd}}"];
[set addObject:@"{CGAffineTransform=dddddd}"];
[set addObject:@"{UIEdgeInsets=dddd}"];
[set addObject:@"{UIOffset=dd}"];
types = set;
});
if ([types containsObject:propertyInfo.typeEncoding]) {
meta->_isStructAvailableForKeyedArchiver = YES;
}
}
meta->_cls = propertyInfo.cls;
if (generic) {
meta->_hasCustomClassFromDictionary = [generic respondsToSelector:@selector(modelCustomClassForDictionary:)];
} else if (meta->_cls && meta->_nsType == YYEncodingTypeNSUnknown) {
meta->_hasCustomClassFromDictionary = [meta->_cls respondsToSelector:@selector(modelCustomClassForDictionary:)];
}
if (propertyInfo.getter) {
if ([classInfo.cls instancesRespondToSelector:propertyInfo.getter]) {
meta->_getter = propertyInfo.getter;
}
}
if (propertyInfo.setter) {
if ([classInfo.cls instancesRespondToSelector:propertyInfo.setter]) {
meta->_setter = propertyInfo.setter;
}
}
if (meta->_getter && meta->_setter) {
/*
KVC invalid type:
long double
pointer (such as SEL/CoreFoundation object)
*/
switch (meta->_type & YYEncodingTypeMask) {
case YYEncodingTypeBool:
case YYEncodingTypeInt8:
case YYEncodingTypeUInt8:
case YYEncodingTypeInt16:
case YYEncodingTypeUInt16:
case YYEncodingTypeInt32:
case YYEncodingTypeUInt32:
case YYEncodingTypeInt64:
case YYEncodingTypeUInt64:
case YYEncodingTypeFloat:
case YYEncodingTypeDouble:
case YYEncodingTypeObject:
case YYEncodingTypeClass:
case YYEncodingTypeBlock:
case YYEncodingTypeStruct:
case YYEncodingTypeUnion: {
meta->_isKVCCompatible = YES;
} break;
default: break;
}
}
return meta;
}
@end
/// A class info in object model.
@interface _YYModelMeta : NSObject {
@package
YYClassInfo *_classInfo;
/// Key:mapped key and key path, Value:_YYModelPropertyMeta.
NSDictionary *_mapper;
/// Array<_YYModelPropertyMeta>, all property meta of this model.
NSArray *_allPropertyMetas;
/// Array<_YYModelPropertyMeta>, property meta which is mapped to a key path.
NSArray *_keyPathPropertyMetas;
/// Array<_YYModelPropertyMeta>, property meta which is mapped to multi keys.
NSArray *_multiKeysPropertyMetas;
/// The number of mapped key (and key path), same to _mapper.count.
NSUInteger _keyMappedCount;
/// Model class type.
YYEncodingNSType _nsType;
BOOL _hasCustomWillTransformFromDictionary;
BOOL _hasCustomTransformFromDictionary;
BOOL _hasCustomTransformToDictionary;
BOOL _hasCustomClassFromDictionary;
}
@end
@implementation _YYModelMeta
- (instancetype)initWithClass:(Class)cls {
YYClassInfo *classInfo = [YYClassInfo classInfoWithClass:cls];
if (!classInfo) return nil;
self = [super init];
// Get black list
NSSet *blacklist = nil;
if ([cls respondsToSelector:@selector(modelPropertyBlacklist)]) {
NSArray *properties = [(id<YYModel>)cls modelPropertyBlacklist];
if (properties) {
blacklist = [NSSet setWithArray:properties];
}
}
// Get white list
NSSet *whitelist = nil;
if ([cls respondsToSelector:@selector(modelPropertyWhitelist)]) {
NSArray *properties = [(id<YYModel>)cls modelPropertyWhitelist];
if (properties) {
whitelist = [NSSet setWithArray:properties];
}
}
// Get container property's generic class
NSDictionary *genericMapper = nil;
if ([cls respondsToSelector:@selector(modelContainerPropertyGenericClass)]) {
genericMapper = [(id<YYModel>)cls modelContainerPropertyGenericClass];
if (genericMapper) {
NSMutableDictionary *tmp = [NSMutableDictionary new];
[genericMapper enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
if (![key isKindOfClass:[NSString class]]) return;
Class meta = object_getClass(obj);
if (!meta) return;
if (class_isMetaClass(meta)) {
tmp[key] = obj;
} else if ([obj isKindOfClass:[NSString class]]) {
Class cls = NSClassFromString(obj);
if (cls) {
tmp[key] = cls;
}
}
}];
genericMapper = tmp;
}
}
// Create all property metas.
NSMutableDictionary *allPropertyMetas = [NSMutableDictionary new];
YYClassInfo *curClassInfo = classInfo;
while (curClassInfo && curClassInfo.superCls != nil) { // recursive parse super class, but ignore root class (NSObject/NSProxy)
for (YYClassPropertyInfo *propertyInfo in curClassInfo.propertyInfos.allValues) {
if (!propertyInfo.name) continue;
if (blacklist && [blacklist containsObject:propertyInfo.name]) continue;
if (whitelist && ![whitelist containsObject:propertyInfo.name]) continue;
_YYModelPropertyMeta *meta = [_YYModelPropertyMeta metaWithClassInfo:classInfo
propertyInfo:propertyInfo
generic:genericMapper[propertyInfo.name]];
if (!meta || !meta->_name) continue;
if (!meta->_getter || !meta->_setter) continue;
if (allPropertyMetas[meta->_name]) continue;
allPropertyMetas[meta->_name] = meta;
}
curClassInfo = curClassInfo.superClassInfo;
}
if (allPropertyMetas.count) _allPropertyMetas = allPropertyMetas.allValues.copy;
// create mapper
NSMutableDictionary *mapper = [NSMutableDictionary new];
NSMutableArray *keyPathPropertyMetas = [NSMutableArray new];
NSMutableArray *multiKeysPropertyMetas = [NSMutableArray new];
if ([cls respondsToSelector:@selector(modelCustomPropertyMapper)]) {
NSDictionary *customMapper = [(id <YYModel>)cls modelCustomPropertyMapper];
[customMapper enumerateKeysAndObjectsUsingBlock:^(NSString *propertyName, NSString *mappedToKey, BOOL *stop) {
_YYModelPropertyMeta *propertyMeta = allPropertyMetas[propertyName];
if (!propertyMeta) return;
[allPropertyMetas removeObjectForKey:propertyName];
if ([mappedToKey isKindOfClass:[NSString class]]) {
if (mappedToKey.length == 0) return;
propertyMeta->_mappedToKey = mappedToKey;
NSArray *keyPath = [mappedToKey componentsSeparatedByString:@"."];
for (NSString *onePath in keyPath) {
if (onePath.length == 0) {
NSMutableArray *tmp = keyPath.mutableCopy;
[tmp removeObject:@""];
keyPath = tmp;
break;
}
}
if (keyPath.count > 1) {
propertyMeta->_mappedToKeyPath = keyPath;
[keyPathPropertyMetas addObject:propertyMeta];
}
propertyMeta->_next = mapper[mappedToKey] ?: nil;
mapper[mappedToKey] = propertyMeta;
} else if ([mappedToKey isKindOfClass:[NSArray class]]) {
NSMutableArray *mappedToKeyArray = [NSMutableArray new];
for (NSString *oneKey in ((NSArray *)mappedToKey)) {
if (![oneKey isKindOfClass:[NSString class]]) continue;
if (oneKey.length == 0) continue;
NSArray *keyPath = [oneKey componentsSeparatedByString:@"."];
if (keyPath.count > 1) {
[mappedToKeyArray addObject:keyPath];
} else {
[mappedToKeyArray addObject:oneKey];
}
if (!propertyMeta->_mappedToKey) {
propertyMeta->_mappedToKey = oneKey;
propertyMeta->_mappedToKeyPath = keyPath.count > 1 ? keyPath : nil;
}
}
if (!propertyMeta->_mappedToKey) return;
propertyMeta->_mappedToKeyArray = mappedToKeyArray;
[multiKeysPropertyMetas addObject:propertyMeta];
propertyMeta->_next = mapper[mappedToKey] ?: nil;
mapper[mappedToKey] = propertyMeta;
}
}];
}
[allPropertyMetas enumerateKeysAndObjectsUsingBlock:^(NSString *name, _YYModelPropertyMeta *propertyMeta, BOOL *stop) {
propertyMeta->_mappedToKey = name;
propertyMeta->_next = mapper[name] ?: nil;
mapper[name] = propertyMeta;
}];
if (mapper.count) _mapper = mapper;
if (keyPathPropertyMetas) _keyPathPropertyMetas = keyPathPropertyMetas;
if (multiKeysPropertyMetas) _multiKeysPropertyMetas = multiKeysPropertyMetas;
_classInfo = classInfo;
_keyMappedCount = _allPropertyMetas.count;
_nsType = YYClassGetNSType(cls);
_hasCustomWillTransformFromDictionary = ([cls instancesRespondToSelector:@selector(modelCustomWillTransformFromDictionary:)]);
_hasCustomTransformFromDictionary = ([cls instancesRespondToSelector:@selector(modelCustomTransformFromDictionary:)]);
_hasCustomTransformToDictionary = ([cls instancesRespondToSelector:@selector(modelCustomTransformToDictionary:)]);
_hasCustomClassFromDictionary = ([cls respondsToSelector:@selector(modelCustomClassForDictionary:)]);
return self;
}
/// Returns the cached model class meta
+ (instancetype)metaWithClass:(Class)cls {
if (!cls) return nil;
static CFMutableDictionaryRef cache;
static dispatch_once_t onceToken;
static dispatch_semaphore_t lock;
dispatch_once(&onceToken, ^{
cache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
lock = dispatch_semaphore_create(1);
});
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
_YYModelMeta *meta = CFDictionaryGetValue(cache, (__bridge const void *)(cls));
dispatch_semaphore_signal(lock);
if (!meta || meta->_classInfo.needUpdate) {
meta = [[_YYModelMeta alloc] initWithClass:cls];
if (meta) {
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
CFDictionarySetValue(cache, (__bridge const void *)(cls), (__bridge const void *)(meta));
dispatch_semaphore_signal(lock);
}
}
return meta;
}
@end
/**
Get number from property.
@discussion Caller should hold strong reference to the parameters before this function returns.
@param model Should not be nil.
@param meta Should not be nil, meta.isCNumber should be YES, meta.getter should not be nil.
@return A number object, or nil if failed.
*/
static force_inline NSNumber *ModelCreateNumberFromProperty(__unsafe_unretained id model,
__unsafe_unretained _YYModelPropertyMeta *meta) {
switch (meta->_type & YYEncodingTypeMask) {
case YYEncodingTypeBool: {
return @(((bool (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
}
case YYEncodingTypeInt8: {
return @(((int8_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
}
case YYEncodingTypeUInt8: {
return @(((uint8_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
}
case YYEncodingTypeInt16: {
return @(((int16_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
}
case YYEncodingTypeUInt16: {
return @(((uint16_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
}
case YYEncodingTypeInt32: {
return @(((int32_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
}
case YYEncodingTypeUInt32: {
return @(((uint32_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
}
case YYEncodingTypeInt64: {
return @(((int64_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
}
case YYEncodingTypeUInt64: {
return @(((uint64_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
}
case YYEncodingTypeFloat: {
float num = ((float (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter);
if (isnan(num) || isinf(num)) return nil;
return @(num);
}
case YYEncodingTypeDouble: {
double num = ((double (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter);
if (isnan(num) || isinf(num)) return nil;
return @(num);
}
case YYEncodingTypeLongDouble: {
double num = ((long double (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter);
if (isnan(num) || isinf(num)) return nil;
return @(num);
}
default: return nil;
}
}
/**
Set number to property.
@discussion Caller should hold strong reference to the parameters before this function returns.
@param model Should not be nil.
@param num Can be nil.
@param meta Should not be nil, meta.isCNumber should be YES, meta.setter should not be nil.
*/
static force_inline void ModelSetNumberToProperty(__unsafe_unretained id model,
__unsafe_unretained NSNumber *num,
__unsafe_unretained _YYModelPropertyMeta *meta) {
switch (meta->_type & YYEncodingTypeMask) {
case YYEncodingTypeBool: {
((void (*)(id, SEL, bool))(void *) objc_msgSend)((id)model, meta->_setter, num.boolValue);
} break;
case YYEncodingTypeInt8: {
((void (*)(id, SEL, int8_t))(void *) objc_msgSend)((id)model, meta->_setter, (int8_t)num.charValue);
} break;
case YYEncodingTypeUInt8: {
((void (*)(id, SEL, uint8_t))(void *) objc_msgSend)((id)model, meta->_setter, (uint8_t)num.unsignedCharValue);
} break;
case YYEncodingTypeInt16: {
((void (*)(id, SEL, int16_t))(void *) objc_msgSend)((id)model, meta->_setter, (int16_t)num.shortValue);
} break;
case YYEncodingTypeUInt16: {
((void (*)(id, SEL, uint16_t))(void *) objc_msgSend)((id)model, meta->_setter, (uint16_t)num.unsignedShortValue);
} break;
case YYEncodingTypeInt32: {
((void (*)(id, SEL, int32_t))(void *) objc_msgSend)((id)model, meta->_setter, (int32_t)num.intValue);
}
case YYEncodingTypeUInt32: {
((void (*)(id, SEL, uint32_t))(void *) objc_msgSend)((id)model, meta->_setter, (uint32_t)num.unsignedIntValue);
} break;
case YYEncodingTypeInt64: {
if ([num isKindOfClass:[NSDecimalNumber class]]) {
((void (*)(id, SEL, int64_t))(void *) objc_msgSend)((id)model, meta->_setter, (int64_t)num.stringValue.longLongValue);
} else {
((void (*)(id, SEL, uint64_t))(void *) objc_msgSend)((id)model, meta->_setter, (uint64_t)num.longLongValue);
}
} break;
case YYEncodingTypeUInt64: {
if ([num isKindOfClass:[NSDecimalNumber class]]) {
((void (*)(id, SEL, int64_t))(void *) objc_msgSend)((id)model, meta->_setter, (int64_t)num.stringValue.longLongValue);
} else {
((void (*)(id, SEL, uint64_t))(void *) objc_msgSend)((id)model, meta->_setter, (uint64_t)num.unsignedLongLongValue);
}
} break;
case YYEncodingTypeFloat: {
float f = num.floatValue;
if (isnan(f) || isinf(f)) f = 0;
((void (*)(id, SEL, float))(void *) objc_msgSend)((id)model, meta->_setter, f);
} break;
case YYEncodingTypeDouble: {
double d = num.doubleValue;
if (isnan(d) || isinf(d)) d = 0;
((void (*)(id, SEL, double))(void *) objc_msgSend)((id)model, meta->_setter, d);
} break;
case YYEncodingTypeLongDouble: {
long double d = num.doubleValue;
if (isnan(d) || isinf(d)) d = 0;
((void (*)(id, SEL, long double))(void *) objc_msgSend)((id)model, meta->_setter, (long double)d);
} // break; commented for code coverage in next line
default: break;
}
}
/**
Set value to model with a property meta.
@discussion Caller should hold strong reference to the parameters before this function returns.
@param model Should not be nil.
@param value Should not be nil, but can be NSNull.
@param meta Should not be nil, and meta->_setter should not be nil.
*/
static void ModelSetValueForProperty(__unsafe_unretained id model,
__unsafe_unretained id value,
__unsafe_unretained _YYModelPropertyMeta *meta) {
if (meta->_isCNumber) {
NSNumber *num = YYNSNumberCreateFromID(value);
ModelSetNumberToProperty(model, num, meta);
if (num) [num class]; // hold the number
} else if (meta->_nsType) {
if (value == (id)kCFNull) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)nil);
} else {
switch (meta->_nsType) {
case YYEncodingTypeNSString:
case YYEncodingTypeNSMutableString: {
if ([value isKindOfClass:[NSString class]]) {
if (meta->_nsType == YYEncodingTypeNSString) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);
} else {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, ((NSString *)value).mutableCopy);
}
} else if ([value isKindOfClass:[NSNumber class]]) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,
meta->_setter,
(meta->_nsType == YYEncodingTypeNSString) ?
((NSNumber *)value).stringValue :
((NSNumber *)value).stringValue.mutableCopy);
} else if ([value isKindOfClass:[NSData class]]) {
NSMutableString *string = [[NSMutableString alloc] initWithData:value encoding:NSUTF8StringEncoding];
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, string);
} else if ([value isKindOfClass:[NSURL class]]) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,
meta->_setter,
(meta->_nsType == YYEncodingTypeNSString) ?
((NSURL *)value).absoluteString :
((NSURL *)value).absoluteString.mutableCopy);
} else if ([value isKindOfClass:[NSAttributedString class]]) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,
meta->_setter,
(meta->_nsType == YYEncodingTypeNSString) ?
((NSAttributedString *)value).string :
((NSAttributedString *)value).string.mutableCopy);
}
} break;
case YYEncodingTypeNSValue:
case YYEncodingTypeNSNumber:
case YYEncodingTypeNSDecimalNumber: {
if (meta->_nsType == YYEncodingTypeNSNumber) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, YYNSNumberCreateFromID(value));
} else if (meta->_nsType == YYEncodingTypeNSDecimalNumber) {
if ([value isKindOfClass:[NSDecimalNumber class]]) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);
} else if ([value isKindOfClass:[NSNumber class]]) {
NSDecimalNumber *decNum = [NSDecimalNumber decimalNumberWithDecimal:[((NSNumber *)value) decimalValue]];
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, decNum);
} else if ([value isKindOfClass:[NSString class]]) {
NSDecimalNumber *decNum = [NSDecimalNumber decimalNumberWithString:value];
NSDecimal dec = decNum.decimalValue;
if (dec._length == 0 && dec._isNegative) {
decNum = nil; // NaN
}
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, decNum);
}
} else { // YYEncodingTypeNSValue
if ([value isKindOfClass:[NSValue class]]) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);
}
}
} break;
case YYEncodingTypeNSData:
case YYEncodingTypeNSMutableData: {
if ([value isKindOfClass:[NSData class]]) {
if (meta->_nsType == YYEncodingTypeNSData) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);
} else {
NSMutableData *data = ((NSData *)value).mutableCopy;
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, data);
}
} else if ([value isKindOfClass:[NSString class]]) {
NSData *data = [(NSString *)value dataUsingEncoding:NSUTF8StringEncoding];
if (meta->_nsType == YYEncodingTypeNSMutableData) {
data = ((NSData *)data).mutableCopy;
}
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, data);
}
} break;
case YYEncodingTypeNSDate: {
if ([value isKindOfClass:[NSDate class]]) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);
} else if ([value isKindOfClass:[NSString class]]) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, YYNSDateFromString(value));
}
} break;
case YYEncodingTypeNSURL: {
if ([value isKindOfClass:[NSURL class]]) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);
} else if ([value isKindOfClass:[NSString class]]) {
NSCharacterSet *set = [NSCharacterSet whitespaceAndNewlineCharacterSet];
NSString *str = [value stringByTrimmingCharactersInSet:set];
if (str.length == 0) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, nil);
} else {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, [[NSURL alloc] initWithString:str]);
}
}
} break;
case YYEncodingTypeNSArray:
case YYEncodingTypeNSMutableArray: {
if (meta->_genericCls) {
NSArray *valueArr = nil;
if ([value isKindOfClass:[NSArray class]]) valueArr = value;
else if ([value isKindOfClass:[NSSet class]]) valueArr = ((NSSet *)value).allObjects;
if (valueArr) {
NSMutableArray *objectArr = [NSMutableArray new];
for (id one in valueArr) {
if ([one isKindOfClass:meta->_genericCls]) {
[objectArr addObject:one];
} else if ([one isKindOfClass:[NSDictionary class]]) {
Class cls = meta->_genericCls;
if (meta->_hasCustomClassFromDictionary) {
cls = [cls modelCustomClassForDictionary:one];
if (!cls) cls = meta->_genericCls; // for xcode code coverage
}
NSObject *newOne = [cls new];
[newOne yy_modelSetWithDictionary:one];
if (newOne) [objectArr addObject:newOne];
}
}
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, objectArr);
}
} else {
if ([value isKindOfClass:[NSArray class]]) {
if (meta->_nsType == YYEncodingTypeNSArray) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);
} else {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,
meta->_setter,
((NSArray *)value).mutableCopy);
}
} else if ([value isKindOfClass:[NSSet class]]) {
if (meta->_nsType == YYEncodingTypeNSArray) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, ((NSSet *)value).allObjects);
} else {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,
meta->_setter,
((NSSet *)value).allObjects.mutableCopy);
}
}
}
} break;
case YYEncodingTypeNSDictionary:
case YYEncodingTypeNSMutableDictionary: {
if ([value isKindOfClass:[NSDictionary class]]) {
if (meta->_genericCls) {
NSMutableDictionary *dic = [NSMutableDictionary new];
[((NSDictionary *)value) enumerateKeysAndObjectsUsingBlock:^(NSString *oneKey, id oneValue, BOOL *stop) {
if ([oneValue isKindOfClass:[NSDictionary class]]) {
Class cls = meta->_genericCls;
if (meta->_hasCustomClassFromDictionary) {
cls = [cls modelCustomClassForDictionary:oneValue];
if (!cls) cls = meta->_genericCls; // for xcode code coverage
}
NSObject *newOne = [cls new];
[newOne yy_modelSetWithDictionary:(id)oneValue];
if (newOne) dic[oneKey] = newOne;
}
}];
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, dic);
} else {
if (meta->_nsType == YYEncodingTypeNSDictionary) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);
} else {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,
meta->_setter,
((NSDictionary *)value).mutableCopy);
}
}
}
} break;
case YYEncodingTypeNSSet:
case YYEncodingTypeNSMutableSet: {
NSSet *valueSet = nil;
if ([value isKindOfClass:[NSArray class]]) valueSet = [NSMutableSet setWithArray:value];
else if ([value isKindOfClass:[NSSet class]]) valueSet = ((NSSet *)value);
if (meta->_genericCls) {
NSMutableSet *set = [NSMutableSet new];
for (id one in valueSet) {
if ([one isKindOfClass:meta->_genericCls]) {
[set addObject:one];
} else if ([one isKindOfClass:[NSDictionary class]]) {
Class cls = meta->_genericCls;
if (meta->_hasCustomClassFromDictionary) {
cls = [cls modelCustomClassForDictionary:one];
if (!cls) cls = meta->_genericCls; // for xcode code coverage
}
NSObject *newOne = [cls new];
[newOne yy_modelSetWithDictionary:one];
if (newOne) [set addObject:newOne];
}
}
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, set);
} else {
if (meta->_nsType == YYEncodingTypeNSSet) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, valueSet);
} else {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,
meta->_setter,
((NSSet *)valueSet).mutableCopy);
}
}
} // break; commented for code coverage in next line
default: break;
}
}
} else {
BOOL isNull = (value == (id)kCFNull);
switch (meta->_type & YYEncodingTypeMask) {
case YYEncodingTypeObject: {
if (isNull) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)nil);
} else if ([value isKindOfClass:meta->_cls] || !meta->_cls) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)value);
} else if ([value isKindOfClass:[NSDictionary class]]) {
NSObject *one = nil;
if (meta->_getter) {
one = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter);
}
if (one) {
[one yy_modelSetWithDictionary:value];
} else {
Class cls = meta->_cls;
if (meta->_hasCustomClassFromDictionary) {
cls = [cls modelCustomClassForDictionary:value];
if (!cls) cls = meta->_genericCls; // for xcode code coverage
}
one = [cls new];
[one yy_modelSetWithDictionary:value];
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)one);
}
}
} break;
case YYEncodingTypeClass: {
if (isNull) {
((void (*)(id, SEL, Class))(void *) objc_msgSend)((id)model, meta->_setter, (Class)NULL);
} else {
Class cls = nil;
if ([value isKindOfClass:[NSString class]]) {
cls = NSClassFromString(value);
if (cls) {
((void (*)(id, SEL, Class))(void *) objc_msgSend)((id)model, meta->_setter, (Class)cls);
}
} else {
cls = object_getClass(value);
if (cls) {
if (class_isMetaClass(cls)) {
((void (*)(id, SEL, Class))(void *) objc_msgSend)((id)model, meta->_setter, (Class)value);
}
}
}
}
} break;
case YYEncodingTypeSEL: {
if (isNull) {
((void (*)(id, SEL, SEL))(void *) objc_msgSend)((id)model, meta->_setter, (SEL)NULL);
} else if ([value isKindOfClass:[NSString class]]) {
SEL sel = NSSelectorFromString(value);
if (sel) ((void (*)(id, SEL, SEL))(void *) objc_msgSend)((id)model, meta->_setter, (SEL)sel);
}
} break;
case YYEncodingTypeBlock: {
if (isNull) {
((void (*)(id, SEL, void (^)()))(void *) objc_msgSend)((id)model, meta->_setter, (void (^)())NULL);
} else if ([value isKindOfClass:YYNSBlockClass()]) {
((void (*)(id, SEL, void (^)()))(void *) objc_msgSend)((id)model, meta->_setter, (void (^)())value);
}
} break;
case YYEncodingTypeStruct:
case YYEncodingTypeUnion:
case YYEncodingTypeCArray: {
if ([value isKindOfClass:[NSValue class]]) {
const char *valueType = ((NSValue *)value).objCType;
const char *metaType = meta->_info.typeEncoding.UTF8String;
if (valueType && metaType && strcmp(valueType, metaType) == 0) {
[model setValue:value forKey:meta->_name];
}
}
} break;
case YYEncodingTypePointer:
case YYEncodingTypeCString: {
if (isNull) {
((void (*)(id, SEL, void *))(void *) objc_msgSend)((id)model, meta->_setter, (void *)NULL);
} else if ([value isKindOfClass:[NSValue class]]) {
NSValue *nsValue = value;
if (nsValue.objCType && strcmp(nsValue.objCType, "^v") == 0) {
((void (*)(id, SEL, void *))(void *) objc_msgSend)((id)model, meta->_setter, nsValue.pointerValue);
}
}
} // break; commented for code coverage in next line
default: break;
}
}
}
typedef struct {
void *modelMeta; ///< _YYModelMeta
void *model; ///< id (self)
void *dictionary; ///< NSDictionary (json)
} ModelSetContext;
/**
Apply function for dictionary, to set the key-value pair to model.
@param _key should not be nil, NSString.
@param _value should not be nil.
@param _context _context.modelMeta and _context.model should not be nil.
*/
static void ModelSetWithDictionaryFunction(const void *_key, const void *_value, void *_context) {
ModelSetContext *context = _context;
__unsafe_unretained _YYModelMeta *meta = (__bridge _YYModelMeta *)(context->modelMeta);
__unsafe_unretained _YYModelPropertyMeta *propertyMeta = [meta->_mapper objectForKey:(__bridge id)(_key)];
__unsafe_unretained id model = (__bridge id)(context->model);
while (propertyMeta) {
if (propertyMeta->_setter) {
ModelSetValueForProperty(model, (__bridge __unsafe_unretained id)_value, propertyMeta);
}
propertyMeta = propertyMeta->_next;
};
}
/**
Apply function for model property meta, to set dictionary to model.
@param _propertyMeta should not be nil, _YYModelPropertyMeta.
@param _context _context.model and _context.dictionary should not be nil.
*/
static void ModelSetWithPropertyMetaArrayFunction(const void *_propertyMeta, void *_context) {
ModelSetContext *context = _context;
__unsafe_unretained NSDictionary *dictionary = (__bridge NSDictionary *)(context->dictionary);
__unsafe_unretained _YYModelPropertyMeta *propertyMeta = (__bridge _YYModelPropertyMeta *)(_propertyMeta);
if (!propertyMeta->_setter) return;
id value = nil;
if (propertyMeta->_mappedToKeyArray) {
value = YYValueForMultiKeys(dictionary, propertyMeta->_mappedToKeyArray);
} else if (propertyMeta->_mappedToKeyPath) {
value = YYValueForKeyPath(dictionary, propertyMeta->_mappedToKeyPath);
} else {
value = [dictionary objectForKey:propertyMeta->_mappedToKey];
}
if (value) {
__unsafe_unretained id model = (__bridge id)(context->model);
ModelSetValueForProperty(model, value, propertyMeta);
}
}
/**
Returns a valid JSON object (NSArray/NSDictionary/NSString/NSNumber/NSNull),
or nil if an error occurs.
@param model Model, can be nil.
@return JSON object, nil if an error occurs.
*/
static id ModelToJSONObjectRecursive(NSObject *model) {
if (!model || model == (id)kCFNull) return model;
if ([model isKindOfClass:[NSString class]]) return model;
if ([model isKindOfClass:[NSNumber class]]) return model;
if ([model isKindOfClass:[NSDictionary class]]) {
if ([NSJSONSerialization isValidJSONObject:model]) return model;
NSMutableDictionary *newDic = [NSMutableDictionary new];
[((NSDictionary *)model) enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
NSString *stringKey = [key isKindOfClass:[NSString class]] ? key : key.description;
if (!stringKey) return;
id jsonObj = ModelToJSONObjectRecursive(obj);
if (!jsonObj) jsonObj = (id)kCFNull;
newDic[stringKey] = jsonObj;
}];
return newDic;
}
if ([model isKindOfClass:[NSSet class]]) {
NSArray *array = ((NSSet *)model).allObjects;
if ([NSJSONSerialization isValidJSONObject:array]) return array;
NSMutableArray *newArray = [NSMutableArray new];
for (id obj in array) {
if ([obj isKindOfClass:[NSString class]] || [obj isKindOfClass:[NSNumber class]]) {
[newArray addObject:obj];
} else {
id jsonObj = ModelToJSONObjectRecursive(obj);
if (jsonObj && jsonObj != (id)kCFNull) [newArray addObject:jsonObj];
}
}
return newArray;
}
if ([model isKindOfClass:[NSArray class]]) {
if ([NSJSONSerialization isValidJSONObject:model]) return model;
NSMutableArray *newArray = [NSMutableArray new];
for (id obj in (NSArray *)model) {
if ([obj isKindOfClass:[NSString class]] || [obj isKindOfClass:[NSNumber class]]) {
[newArray addObject:obj];
} else {
id jsonObj = ModelToJSONObjectRecursive(obj);
if (jsonObj && jsonObj != (id)kCFNull) [newArray addObject:jsonObj];
}
}
return newArray;
}
if ([model isKindOfClass:[NSURL class]]) return ((NSURL *)model).absoluteString;
if ([model isKindOfClass:[NSAttributedString class]]) return ((NSAttributedString *)model).string;
if ([model isKindOfClass:[NSDate class]]) return [YYISODateFormatter() stringFromDate:(id)model];
if ([model isKindOfClass:[NSData class]]) return nil;
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:[model class]];
if (!modelMeta || modelMeta->_keyMappedCount == 0) return nil;
NSMutableDictionary *result = [[NSMutableDictionary alloc] initWithCapacity:64];
__unsafe_unretained NSMutableDictionary *dic = result; // avoid retain and release in block
[modelMeta->_mapper enumerateKeysAndObjectsUsingBlock:^(NSString *propertyMappedKey, _YYModelPropertyMeta *propertyMeta, BOOL *stop) {
if (!propertyMeta->_getter) return;
id value = nil;
if (propertyMeta->_isCNumber) {
value = ModelCreateNumberFromProperty(model, propertyMeta);
} else if (propertyMeta->_nsType) {
id v = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);
value = ModelToJSONObjectRecursive(v);
} else {
switch (propertyMeta->_type & YYEncodingTypeMask) {
case YYEncodingTypeObject: {
id v = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);
value = ModelToJSONObjectRecursive(v);
if (value == (id)kCFNull) value = nil;
} break;
case YYEncodingTypeClass: {
Class v = ((Class (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);
value = v ? NSStringFromClass(v) : nil;
} break;
case YYEncodingTypeSEL: {
SEL v = ((SEL (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);
value = v ? NSStringFromSelector(v) : nil;
} break;
default: break;
}
}
if (!value) return;
if (propertyMeta->_mappedToKeyPath) {
NSMutableDictionary *superDic = dic;
NSMutableDictionary *subDic = nil;
for (NSUInteger i = 0, max = propertyMeta->_mappedToKeyPath.count; i < max; i++) {
NSString *key = propertyMeta->_mappedToKeyPath[i];
if (i + 1 == max) { // end
if (!superDic[key]) superDic[key] = value;
break;
}
subDic = superDic[key];
if (subDic) {
if ([subDic isKindOfClass:[NSDictionary class]]) {
subDic = subDic.mutableCopy;
superDic[key] = subDic;
} else {
break;
}
} else {
subDic = [NSMutableDictionary new];
superDic[key] = subDic;
}
superDic = subDic;
subDic = nil;
}
} else {
if (!dic[propertyMeta->_mappedToKey]) {
dic[propertyMeta->_mappedToKey] = value;
}
}
}];
if (modelMeta->_hasCustomTransformToDictionary) {
BOOL suc = [((id<YYModel>)model) modelCustomTransformToDictionary:dic];
if (!suc) return nil;
}
return result;
}
/// Add indent to string (exclude first line)
static NSMutableString *ModelDescriptionAddIndent(NSMutableString *desc, NSUInteger indent) {
for (NSUInteger i = 0, max = desc.length; i < max; i++) {
unichar c = [desc characterAtIndex:i];
if (c == '\n') {
for (NSUInteger j = 0; j < indent; j++) {
[desc insertString:@" " atIndex:i + 1];
}
i += indent * 4;
max += indent * 4;
}
}
return desc;
}
/// Generaate a description string
static NSString *ModelDescription(NSObject *model) {
static const int kDescMaxLength = 100;
if (!model) return @"<nil>";
if (model == (id)kCFNull) return @"<null>";
if (![model isKindOfClass:[NSObject class]]) return [NSString stringWithFormat:@"%@",model];
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:model.class];
switch (modelMeta->_nsType) {
case YYEncodingTypeNSString: case YYEncodingTypeNSMutableString: {
return [NSString stringWithFormat:@"\"%@\"",model];
}
case YYEncodingTypeNSValue:
case YYEncodingTypeNSData: case YYEncodingTypeNSMutableData: {
NSString *tmp = model.description;
if (tmp.length > kDescMaxLength) {
tmp = [tmp substringToIndex:kDescMaxLength];
tmp = [tmp stringByAppendingString:@"..."];
}
return tmp;
}
case YYEncodingTypeNSNumber:
case YYEncodingTypeNSDecimalNumber:
case YYEncodingTypeNSDate:
case YYEncodingTypeNSURL: {
return [NSString stringWithFormat:@"%@",model];
}
case YYEncodingTypeNSSet: case YYEncodingTypeNSMutableSet: {
model = ((NSSet *)model).allObjects;
} // no break
case YYEncodingTypeNSArray: case YYEncodingTypeNSMutableArray: {
NSArray *array = (id)model;
NSMutableString *desc = [NSMutableString new];
if (array.count == 0) {
return [desc stringByAppendingString:@"[]"];
} else {
[desc appendFormat:@"[\n"];
for (NSUInteger i = 0, max = array.count; i < max; i++) {
NSObject *obj = array[i];
[desc appendString:@" "];
[desc appendString:ModelDescriptionAddIndent(ModelDescription(obj).mutableCopy, 1)];
[desc appendString:(i + 1 == max) ? @"\n" : @";\n"];
}
[desc appendString:@"]"];
return desc;
}
}
case YYEncodingTypeNSDictionary: case YYEncodingTypeNSMutableDictionary: {
NSDictionary *dic = (id)model;
NSMutableString *desc = [NSMutableString new];
if (dic.count == 0) {
return [desc stringByAppendingString:@"{}"];
} else {
NSArray *keys = dic.allKeys;
[desc appendFormat:@"{\n"];
for (NSUInteger i = 0, max = keys.count; i < max; i++) {
NSString *key = keys[i];
NSObject *value = dic[key];
[desc appendString:@" "];
[desc appendFormat:@"%@ = %@",key, ModelDescriptionAddIndent(ModelDescription(value).mutableCopy, 1)];
[desc appendString:(i + 1 == max) ? @"\n" : @";\n"];
}
[desc appendString:@"}"];
}
return desc;
}
default: {
NSMutableString *desc = [NSMutableString new];
[desc appendFormat:@"<%@: %p>", model.class, model];
if (modelMeta->_allPropertyMetas.count == 0) return desc;
// sort property names
NSArray *properties = [modelMeta->_allPropertyMetas
sortedArrayUsingComparator:^NSComparisonResult(_YYModelPropertyMeta *p1, _YYModelPropertyMeta *p2) {
return [p1->_name compare:p2->_name];
}];
[desc appendFormat:@" {\n"];
for (NSUInteger i = 0, max = properties.count; i < max; i++) {
_YYModelPropertyMeta *property = properties[i];
NSString *propertyDesc;
if (property->_isCNumber) {
NSNumber *num = ModelCreateNumberFromProperty(model, property);
propertyDesc = num.stringValue;
} else {
switch (property->_type & YYEncodingTypeMask) {
case YYEncodingTypeObject: {
id v = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, property->_getter);
propertyDesc = ModelDescription(v);
if (!propertyDesc) propertyDesc = @"<nil>";
} break;
case YYEncodingTypeClass: {
id v = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, property->_getter);
propertyDesc = ((NSObject *)v).description;
if (!propertyDesc) propertyDesc = @"<nil>";
} break;
case YYEncodingTypeSEL: {
SEL sel = ((SEL (*)(id, SEL))(void *) objc_msgSend)((id)model, property->_getter);
if (sel) propertyDesc = NSStringFromSelector(sel);
else propertyDesc = @"<NULL>";
} break;
case YYEncodingTypeBlock: {
id block = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, property->_getter);
propertyDesc = block ? ((NSObject *)block).description : @"<nil>";
} break;
case YYEncodingTypeCArray: case YYEncodingTypeCString: case YYEncodingTypePointer: {
void *pointer = ((void* (*)(id, SEL))(void *) objc_msgSend)((id)model, property->_getter);
propertyDesc = [NSString stringWithFormat:@"%p",pointer];
} break;
case YYEncodingTypeStruct: case YYEncodingTypeUnion: {
NSValue *value = [model valueForKey:property->_name];
propertyDesc = value ? value.description : @"{unknown}";
} break;
default: propertyDesc = @"<unknown>";
}
}
propertyDesc = ModelDescriptionAddIndent(propertyDesc.mutableCopy, 1);
[desc appendFormat:@" %@ = %@",property->_name, propertyDesc];
[desc appendString:(i + 1 == max) ? @"\n" : @";\n"];
}
[desc appendFormat:@"}"];
return desc;
}
}
}
@implementation NSObject (YYModel)
+ (NSDictionary *)_yy_dictionaryWithJSON:(id)json {
if (!json || json == (id)kCFNull) return nil;
NSDictionary *dic = nil;
NSData *jsonData = nil;
if ([json isKindOfClass:[NSDictionary class]]) {
dic = json;
} else if ([json isKindOfClass:[NSString class]]) {
jsonData = [(NSString *)json dataUsingEncoding : NSUTF8StringEncoding];
} else if ([json isKindOfClass:[NSData class]]) {
jsonData = json;
}
if (jsonData) {
dic = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:NULL];
if (![dic isKindOfClass:[NSDictionary class]]) dic = nil;
}
return dic;
}
+ (instancetype)yy_modelWithJSON:(id)json {
NSDictionary *dic = [self _yy_dictionaryWithJSON:json];
return [self yy_modelWithDictionary:dic];
}
+ (instancetype)yy_modelWithDictionary:(NSDictionary *)dictionary {
if (!dictionary || dictionary == (id)kCFNull) return nil;
if (![dictionary isKindOfClass:[NSDictionary class]]) return nil;
Class cls = [self class];
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:cls];
if (modelMeta->_hasCustomClassFromDictionary) {
cls = [cls modelCustomClassForDictionary:dictionary] ?: cls;
}
NSObject *one = [cls new];
if ([one yy_modelSetWithDictionary:dictionary]) return one;
return nil;
}
- (BOOL)yy_modelSetWithJSON:(id)json {
NSDictionary *dic = [NSObject _yy_dictionaryWithJSON:json];
return [self yy_modelSetWithDictionary:dic];
}
- (BOOL)yy_modelSetWithDictionary:(NSDictionary *)dic {
if (!dic || dic == (id)kCFNull) return NO;
if (![dic isKindOfClass:[NSDictionary class]]) return NO;
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:object_getClass(self)];
if (modelMeta->_keyMappedCount == 0) return NO;
if (modelMeta->_hasCustomWillTransformFromDictionary) {
dic = [((id<YYModel>)self) modelCustomWillTransformFromDictionary:dic];
if (![dic isKindOfClass:[NSDictionary class]]) return NO;
}
ModelSetContext context = {0};
context.modelMeta = (__bridge void *)(modelMeta);
context.model = (__bridge void *)(self);
context.dictionary = (__bridge void *)(dic);
if (modelMeta->_keyMappedCount >= CFDictionaryGetCount((CFDictionaryRef)dic)) {
CFDictionaryApplyFunction((CFDictionaryRef)dic, ModelSetWithDictionaryFunction, &context);
if (modelMeta->_keyPathPropertyMetas) {
CFArrayApplyFunction((CFArrayRef)modelMeta->_keyPathPropertyMetas,
CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_keyPathPropertyMetas)),
ModelSetWithPropertyMetaArrayFunction,
&context);
}
if (modelMeta->_multiKeysPropertyMetas) {
CFArrayApplyFunction((CFArrayRef)modelMeta->_multiKeysPropertyMetas,
CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_multiKeysPropertyMetas)),
ModelSetWithPropertyMetaArrayFunction,
&context);
}
} else {
CFArrayApplyFunction((CFArrayRef)modelMeta->_allPropertyMetas,
CFRangeMake(0, modelMeta->_keyMappedCount),
ModelSetWithPropertyMetaArrayFunction,
&context);
}
if (modelMeta->_hasCustomTransformFromDictionary) {
return [((id<YYModel>)self) modelCustomTransformFromDictionary:dic];
}
return YES;
}
- (id)yy_modelToJSONObject {
/*
Apple said:
The top level object is an NSArray or NSDictionary.
All objects are instances of NSString, NSNumber, NSArray, NSDictionary, or NSNull.
All dictionary keys are instances of NSString.
Numbers are not NaN or infinity.
*/
id jsonObject = ModelToJSONObjectRecursive(self);
if ([jsonObject isKindOfClass:[NSArray class]]) return jsonObject;
if ([jsonObject isKindOfClass:[NSDictionary class]]) return jsonObject;
return nil;
}
- (NSData *)yy_modelToJSONData {
id jsonObject = [self yy_modelToJSONObject];
if (!jsonObject) return nil;
return [NSJSONSerialization dataWithJSONObject:jsonObject options:0 error:NULL];
}
- (NSString *)yy_modelToJSONString {
NSData *jsonData = [self yy_modelToJSONData];
if (jsonData.length == 0) return nil;
return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}
- (id)yy_modelCopy{
if (self == (id)kCFNull) return self;
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:self.class];
if (modelMeta->_nsType) return [self copy];
NSObject *one = [self.class new];
for (_YYModelPropertyMeta *propertyMeta in modelMeta->_allPropertyMetas) {
if (!propertyMeta->_getter || !propertyMeta->_setter) continue;
if (propertyMeta->_isCNumber) {
switch (propertyMeta->_type & YYEncodingTypeMask) {
case YYEncodingTypeBool: {
bool num = ((bool (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter);
((void (*)(id, SEL, bool))(void *) objc_msgSend)((id)one, propertyMeta->_setter, num);
} break;
case YYEncodingTypeInt8:
case YYEncodingTypeUInt8: {
uint8_t num = ((bool (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter);
((void (*)(id, SEL, uint8_t))(void *) objc_msgSend)((id)one, propertyMeta->_setter, num);
} break;
case YYEncodingTypeInt16:
case YYEncodingTypeUInt16: {
uint16_t num = ((uint16_t (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter);
((void (*)(id, SEL, uint16_t))(void *) objc_msgSend)((id)one, propertyMeta->_setter, num);
} break;
case YYEncodingTypeInt32:
case YYEncodingTypeUInt32: {
uint32_t num = ((uint32_t (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter);
((void (*)(id, SEL, uint32_t))(void *) objc_msgSend)((id)one, propertyMeta->_setter, num);
} break;
case YYEncodingTypeInt64:
case YYEncodingTypeUInt64: {
uint64_t num = ((uint64_t (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter);
((void (*)(id, SEL, uint64_t))(void *) objc_msgSend)((id)one, propertyMeta->_setter, num);
} break;
case YYEncodingTypeFloat: {
float num = ((float (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter);
((void (*)(id, SEL, float))(void *) objc_msgSend)((id)one, propertyMeta->_setter, num);
} break;
case YYEncodingTypeDouble: {
double num = ((double (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter);
((void (*)(id, SEL, double))(void *) objc_msgSend)((id)one, propertyMeta->_setter, num);
} break;
case YYEncodingTypeLongDouble: {
long double num = ((long double (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter);
((void (*)(id, SEL, long double))(void *) objc_msgSend)((id)one, propertyMeta->_setter, num);
} // break; commented for code coverage in next line
default: break;
}
} else {
switch (propertyMeta->_type & YYEncodingTypeMask) {
case YYEncodingTypeObject:
case YYEncodingTypeClass:
case YYEncodingTypeBlock: {
id value = ((id (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter);
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)one, propertyMeta->_setter, value);
} break;
case YYEncodingTypeSEL:
case YYEncodingTypePointer:
case YYEncodingTypeCString: {
size_t value = ((size_t (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter);
((void (*)(id, SEL, size_t))(void *) objc_msgSend)((id)one, propertyMeta->_setter, value);
} break;
case YYEncodingTypeStruct:
case YYEncodingTypeUnion: {
@try {
NSValue *value = [self valueForKey:NSStringFromSelector(propertyMeta->_getter)];
if (value) {
[one setValue:value forKey:propertyMeta->_name];
}
} @catch (NSException *exception) {}
} // break; commented for code coverage in next line
default: break;
}
}
}
return one;
}
- (void)yy_modelEncodeWithCoder:(NSCoder *)aCoder {
if (!aCoder) return;
if (self == (id)kCFNull) {
[((id<NSCoding>)self)encodeWithCoder:aCoder];
return;
}
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:self.class];
if (modelMeta->_nsType) {
[((id<NSCoding>)self)encodeWithCoder:aCoder];
return;
}
for (_YYModelPropertyMeta *propertyMeta in modelMeta->_allPropertyMetas) {
if (!propertyMeta->_getter) return;
if (propertyMeta->_isCNumber) {
NSNumber *value = ModelCreateNumberFromProperty(self, propertyMeta);
if (value) [aCoder encodeObject:value forKey:propertyMeta->_name];
} else {
switch (propertyMeta->_type & YYEncodingTypeMask) {
case YYEncodingTypeObject: {
id value = ((id (*)(id, SEL))(void *)objc_msgSend)((id)self, propertyMeta->_getter);
if (value && (propertyMeta->_nsType || [value respondsToSelector:@selector(encodeWithCoder:)])) {
if ([value isKindOfClass:[NSValue class]]) {
if ([value isKindOfClass:[NSNumber class]]) {
[aCoder encodeObject:value forKey:propertyMeta->_name];
}
} else {
[aCoder encodeObject:value forKey:propertyMeta->_name];
}
}
} break;
case YYEncodingTypeSEL: {
SEL value = ((SEL (*)(id, SEL))(void *)objc_msgSend)((id)self, propertyMeta->_getter);
if (value) {
NSString *str = NSStringFromSelector(value);
[aCoder encodeObject:str forKey:propertyMeta->_name];
}
} break;
case YYEncodingTypeStruct:
case YYEncodingTypeUnion: {
if (propertyMeta->_isKVCCompatible && propertyMeta->_isStructAvailableForKeyedArchiver) {
@try {
NSValue *value = [self valueForKey:NSStringFromSelector(propertyMeta->_getter)];
[aCoder encodeObject:value forKey:propertyMeta->_name];
} @catch (NSException *exception) {}
}
} break;
default:
break;
}
}
}
}
- (id)yy_modelInitWithCoder:(NSCoder *)aDecoder {
if (!aDecoder) return self;
if (self == (id)kCFNull) return self;
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:self.class];
if (modelMeta->_nsType) return self;
for (_YYModelPropertyMeta *propertyMeta in modelMeta->_allPropertyMetas) {
if (!propertyMeta->_setter) continue;
if (propertyMeta->_isCNumber) {
NSNumber *value = [aDecoder decodeObjectForKey:propertyMeta->_name];
if ([value isKindOfClass:[NSNumber class]]) {
ModelSetNumberToProperty(self, value, propertyMeta);
[value class];
}
} else {
YYEncodingType type = propertyMeta->_type & YYEncodingTypeMask;
switch (type) {
case YYEncodingTypeObject: {
id value = [aDecoder decodeObjectForKey:propertyMeta->_name];
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)self, propertyMeta->_setter, value);
} break;
case YYEncodingTypeSEL: {
NSString *str = [aDecoder decodeObjectForKey:propertyMeta->_name];
if ([str isKindOfClass:[NSString class]]) {
SEL sel = NSSelectorFromString(str);
((void (*)(id, SEL, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_setter, sel);
}
} break;
case YYEncodingTypeStruct:
case YYEncodingTypeUnion: {
if (propertyMeta->_isKVCCompatible) {
@try {
NSValue *value = [aDecoder decodeObjectForKey:propertyMeta->_name];
if (value) [self setValue:value forKey:propertyMeta->_name];
} @catch (NSException *exception) {}
}
} break;
default:
break;
}
}
}
return self;
}
- (NSUInteger)yy_modelHash {
if (self == (id)kCFNull) return [self hash];
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:self.class];
if (modelMeta->_nsType) return [self hash];
NSUInteger value = 0;
NSUInteger count = 0;
for (_YYModelPropertyMeta *propertyMeta in modelMeta->_allPropertyMetas) {
if (!propertyMeta->_isKVCCompatible) continue;
value ^= [[self valueForKey:NSStringFromSelector(propertyMeta->_getter)] hash];
count++;
}
if (count == 0) value = (long)((__bridge void *)self);
return value;
}
- (BOOL)yy_modelIsEqual:(id)model {
if (self == model) return YES;
if (![model isMemberOfClass:self.class]) return NO;
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:self.class];
if (modelMeta->_nsType) return [self isEqual:model];
if ([self hash] != [model hash]) return NO;
for (_YYModelPropertyMeta *propertyMeta in modelMeta->_allPropertyMetas) {
if (!propertyMeta->_isKVCCompatible) continue;
id this = [self valueForKey:NSStringFromSelector(propertyMeta->_getter)];
id that = [model valueForKey:NSStringFromSelector(propertyMeta->_getter)];
if (this == that) continue;
if (this == nil || that == nil) return NO;
if (![this isEqual:that]) return NO;
}
return YES;
}
- (NSString *)yy_modelDescription {
return ModelDescription(self);
}
@end
@implementation NSArray (YYModel)
+ (NSArray *)yy_modelArrayWithClass:(Class)cls json:(id)json {
if (!json) return nil;
NSArray *arr = nil;
NSData *jsonData = nil;
if ([json isKindOfClass:[NSArray class]]) {
arr = json;
} else if ([json isKindOfClass:[NSString class]]) {
jsonData = [(NSString *)json dataUsingEncoding : NSUTF8StringEncoding];
} else if ([json isKindOfClass:[NSData class]]) {
jsonData = json;
}
if (jsonData) {
arr = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:NULL];
if (![arr isKindOfClass:[NSArray class]]) arr = nil;
}
return [self yy_modelArrayWithClass:cls array:arr];
}
+ (NSArray *)yy_modelArrayWithClass:(Class)cls array:(NSArray *)arr {
if (!cls || !arr) return nil;
NSMutableArray *result = [NSMutableArray new];
for (NSDictionary *dic in arr) {
if (![dic isKindOfClass:[NSDictionary class]]) continue;
NSObject *obj = [cls yy_modelWithDictionary:dic];
if (obj) [result addObject:obj];
}
return result;
}
@end
@implementation NSDictionary (YYModel)
+ (NSDictionary *)yy_modelDictionaryWithClass:(Class)cls json:(id)json {
if (!json) return nil;
NSDictionary *dic = nil;
NSData *jsonData = nil;
if ([json isKindOfClass:[NSDictionary class]]) {
dic = json;
} else if ([json isKindOfClass:[NSString class]]) {
jsonData = [(NSString *)json dataUsingEncoding : NSUTF8StringEncoding];
} else if ([json isKindOfClass:[NSData class]]) {
jsonData = json;
}
if (jsonData) {
dic = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:NULL];
if (![dic isKindOfClass:[NSDictionary class]]) dic = nil;
}
return [self yy_modelDictionaryWithClass:cls dictionary:dic];
}
+ (NSDictionary *)yy_modelDictionaryWithClass:(Class)cls dictionary:(NSDictionary *)dic {
if (!cls || !dic) return nil;
NSMutableDictionary *result = [NSMutableDictionary new];
for (NSString *key in dic.allKeys) {
if (![key isKindOfClass:[NSString class]]) continue;
NSObject *obj = [cls yy_modelWithDictionary:dic[key]];
if (obj) result[key] = obj;
}
return result;
}
@end
//
// YYClassInfo.h
// YYModel <https://github.com/ibireme/YYModel>
//
// Created by ibireme on 15/5/9.
// Copyright (c) 2015 ibireme.
//
// This source code is licensed under the MIT-style license found in the
// LICENSE file in the root directory of this source tree.
//
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
NS_ASSUME_NONNULL_BEGIN
/**
Type encoding's type.
*/
typedef NS_OPTIONS(NSUInteger, YYEncodingType) {
YYEncodingTypeMask = 0xFF, ///< mask of type value
YYEncodingTypeUnknown = 0, ///< unknown
YYEncodingTypeVoid = 1, ///< void
YYEncodingTypeBool = 2, ///< bool
YYEncodingTypeInt8 = 3, ///< char / BOOL
YYEncodingTypeUInt8 = 4, ///< unsigned char
YYEncodingTypeInt16 = 5, ///< short
YYEncodingTypeUInt16 = 6, ///< unsigned short
YYEncodingTypeInt32 = 7, ///< int
YYEncodingTypeUInt32 = 8, ///< unsigned int
YYEncodingTypeInt64 = 9, ///< long long
YYEncodingTypeUInt64 = 10, ///< unsigned long long
YYEncodingTypeFloat = 11, ///< float
YYEncodingTypeDouble = 12, ///< double
YYEncodingTypeLongDouble = 13, ///< long double
YYEncodingTypeObject = 14, ///< id
YYEncodingTypeClass = 15, ///< Class
YYEncodingTypeSEL = 16, ///< SEL
YYEncodingTypeBlock = 17, ///< block
YYEncodingTypePointer = 18, ///< void*
YYEncodingTypeStruct = 19, ///< struct
YYEncodingTypeUnion = 20, ///< union
YYEncodingTypeCString = 21, ///< char*
YYEncodingTypeCArray = 22, ///< char[10] (for example)
YYEncodingTypeQualifierMask = 0xFF00, ///< mask of qualifier
YYEncodingTypeQualifierConst = 1 << 8, ///< const
YYEncodingTypeQualifierIn = 1 << 9, ///< in
YYEncodingTypeQualifierInout = 1 << 10, ///< inout
YYEncodingTypeQualifierOut = 1 << 11, ///< out
YYEncodingTypeQualifierBycopy = 1 << 12, ///< bycopy
YYEncodingTypeQualifierByref = 1 << 13, ///< byref
YYEncodingTypeQualifierOneway = 1 << 14, ///< oneway
YYEncodingTypePropertyMask = 0xFF0000, ///< mask of property
YYEncodingTypePropertyReadonly = 1 << 16, ///< readonly
YYEncodingTypePropertyCopy = 1 << 17, ///< copy
YYEncodingTypePropertyRetain = 1 << 18, ///< retain
YYEncodingTypePropertyNonatomic = 1 << 19, ///< nonatomic
YYEncodingTypePropertyWeak = 1 << 20, ///< weak
YYEncodingTypePropertyCustomGetter = 1 << 21, ///< getter=
YYEncodingTypePropertyCustomSetter = 1 << 22, ///< setter=
YYEncodingTypePropertyDynamic = 1 << 23, ///< @dynamic
};
/**
Get the type from a Type-Encoding string.
@discussion See also:
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html
@param typeEncoding A Type-Encoding string.
@return The encoding type.
*/
YYEncodingType YYEncodingGetType(const char *typeEncoding);
/**
Instance variable information.
*/
@interface YYClassIvarInfo : NSObject
@property (nonatomic, assign, readonly) Ivar ivar; ///< ivar opaque struct
@property (nonatomic, strong, readonly) NSString *name; ///< Ivar's name
@property (nonatomic, assign, readonly) ptrdiff_t offset; ///< Ivar's offset
@property (nonatomic, strong, readonly) NSString *typeEncoding; ///< Ivar's type encoding
@property (nonatomic, assign, readonly) YYEncodingType type; ///< Ivar's type
/**
Creates and returns an ivar info object.
@param ivar ivar opaque struct
@return A new object, or nil if an error occurs.
*/
- (instancetype)initWithIvar:(Ivar)ivar;
@end
/**
Method information.
*/
@interface YYClassMethodInfo : NSObject
@property (nonatomic, assign, readonly) Method method; ///< method opaque struct
@property (nonatomic, strong, readonly) NSString *name; ///< method name
@property (nonatomic, assign, readonly) SEL sel; ///< method's selector
@property (nonatomic, assign, readonly) IMP imp; ///< method's implementation
@property (nonatomic, strong, readonly) NSString *typeEncoding; ///< method's parameter and return types
@property (nonatomic, strong, readonly) NSString *returnTypeEncoding; ///< return value's type
@property (nullable, nonatomic, strong, readonly) NSArray<NSString *> *argumentTypeEncodings; ///< array of arguments' type
/**
Creates and returns a method info object.
@param method method opaque struct
@return A new object, or nil if an error occurs.
*/
- (instancetype)initWithMethod:(Method)method;
@end
/**
Property information.
*/
@interface YYClassPropertyInfo : NSObject
@property (nonatomic, assign, readonly) objc_property_t property; ///< property's opaque struct
@property (nonatomic, strong, readonly) NSString *name; ///< property's name
@property (nonatomic, assign, readonly) YYEncodingType type; ///< property's type
@property (nonatomic, strong, readonly) NSString *typeEncoding; ///< property's encoding value
@property (nonatomic, strong, readonly) NSString *ivarName; ///< property's ivar name
@property (nullable, nonatomic, assign, readonly) Class cls; ///< may be nil
@property (nullable, nonatomic, strong, readonly) NSArray<NSString *> *protocols; ///< may nil
@property (nonatomic, assign, readonly) SEL getter; ///< getter (nonnull)
@property (nonatomic, assign, readonly) SEL setter; ///< setter (nonnull)
/**
Creates and returns a property info object.
@param property property opaque struct
@return A new object, or nil if an error occurs.
*/
- (instancetype)initWithProperty:(objc_property_t)property;
@end
/**
Class information for a class.
*/
@interface YYClassInfo : NSObject
@property (nonatomic, assign, readonly) Class cls; ///< class object
@property (nullable, nonatomic, assign, readonly) Class superCls; ///< super class object
@property (nullable, nonatomic, assign, readonly) Class metaCls; ///< class's meta class object
@property (nonatomic, readonly) BOOL isMeta; ///< whether this class is meta class
@property (nonatomic, strong, readonly) NSString *name; ///< class name
@property (nullable, nonatomic, strong, readonly) YYClassInfo *superClassInfo; ///< super class's class info
@property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, YYClassIvarInfo *> *ivarInfos; ///< ivars
@property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, YYClassMethodInfo *> *methodInfos; ///< methods
@property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, YYClassPropertyInfo *> *propertyInfos; ///< properties
/**
If the class is changed (for example: you add a method to this class with
'class_addMethod()'), you should call this method to refresh the class info cache.
After called this method, `needUpdate` will returns `YES`, and you should call
'classInfoWithClass' or 'classInfoWithClassName' to get the updated class info.
*/
- (void)setNeedUpdate;
/**
If this method returns `YES`, you should stop using this instance and call
`classInfoWithClass` or `classInfoWithClassName` to get the updated class info.
@return Whether this class info need update.
*/
- (BOOL)needUpdate;
/**
Get the class info of a specified Class.
@discussion This method will cache the class info and super-class info
at the first access to the Class. This method is thread-safe.
@param cls A class.
@return A class info, or nil if an error occurs.
*/
+ (nullable instancetype)classInfoWithClass:(Class)cls;
/**
Get the class info of a specified Class.
@discussion This method will cache the class info and super-class info
at the first access to the Class. This method is thread-safe.
@param className A class name.
@return A class info, or nil if an error occurs.
*/
+ (nullable instancetype)classInfoWithClassName:(NSString *)className;
@end
NS_ASSUME_NONNULL_END
//
// YYClassInfo.m
// YYModel <https://github.com/ibireme/YYModel>
//
// Created by ibireme on 15/5/9.
// Copyright (c) 2015 ibireme.
//
// This source code is licensed under the MIT-style license found in the
// LICENSE file in the root directory of this source tree.
//
#import "YYClassInfo.h"
#import <objc/runtime.h>
YYEncodingType YYEncodingGetType(const char *typeEncoding) {
char *type = (char *)typeEncoding;
if (!type) return YYEncodingTypeUnknown;
size_t len = strlen(type);
if (len == 0) return YYEncodingTypeUnknown;
YYEncodingType qualifier = 0;
bool prefix = true;
while (prefix) {
switch (*type) {
case 'r': {
qualifier |= YYEncodingTypeQualifierConst;
type++;
} break;
case 'n': {
qualifier |= YYEncodingTypeQualifierIn;
type++;
} break;
case 'N': {
qualifier |= YYEncodingTypeQualifierInout;
type++;
} break;
case 'o': {
qualifier |= YYEncodingTypeQualifierOut;
type++;
} break;
case 'O': {
qualifier |= YYEncodingTypeQualifierBycopy;
type++;
} break;
case 'R': {
qualifier |= YYEncodingTypeQualifierByref;
type++;
} break;
case 'V': {
qualifier |= YYEncodingTypeQualifierOneway;
type++;
} break;
default: { prefix = false; } break;
}
}
len = strlen(type);
if (len == 0) return YYEncodingTypeUnknown | qualifier;
switch (*type) {
case 'v': return YYEncodingTypeVoid | qualifier;
case 'B': return YYEncodingTypeBool | qualifier;
case 'c': return YYEncodingTypeInt8 | qualifier;
case 'C': return YYEncodingTypeUInt8 | qualifier;
case 's': return YYEncodingTypeInt16 | qualifier;
case 'S': return YYEncodingTypeUInt16 | qualifier;
case 'i': return YYEncodingTypeInt32 | qualifier;
case 'I': return YYEncodingTypeUInt32 | qualifier;
case 'l': return YYEncodingTypeInt32 | qualifier;
case 'L': return YYEncodingTypeUInt32 | qualifier;
case 'q': return YYEncodingTypeInt64 | qualifier;
case 'Q': return YYEncodingTypeUInt64 | qualifier;
case 'f': return YYEncodingTypeFloat | qualifier;
case 'd': return YYEncodingTypeDouble | qualifier;
case 'D': return YYEncodingTypeLongDouble | qualifier;
case '#': return YYEncodingTypeClass | qualifier;
case ':': return YYEncodingTypeSEL | qualifier;
case '*': return YYEncodingTypeCString | qualifier;
case '^': return YYEncodingTypePointer | qualifier;
case '[': return YYEncodingTypeCArray | qualifier;
case '(': return YYEncodingTypeUnion | qualifier;
case '{': return YYEncodingTypeStruct | qualifier;
case '@': {
if (len == 2 && *(type + 1) == '?')
return YYEncodingTypeBlock | qualifier;
else
return YYEncodingTypeObject | qualifier;
}
default: return YYEncodingTypeUnknown | qualifier;
}
}
@implementation YYClassIvarInfo
- (instancetype)initWithIvar:(Ivar)ivar {
if (!ivar) return nil;
self = [super init];
_ivar = ivar;
const char *name = ivar_getName(ivar);
if (name) {
_name = [NSString stringWithUTF8String:name];
}
_offset = ivar_getOffset(ivar);
const char *typeEncoding = ivar_getTypeEncoding(ivar);
if (typeEncoding) {
_typeEncoding = [NSString stringWithUTF8String:typeEncoding];
_type = YYEncodingGetType(typeEncoding);
}
return self;
}
@end
@implementation YYClassMethodInfo
- (instancetype)initWithMethod:(Method)method {
if (!method) return nil;
self = [super init];
_method = method;
_sel = method_getName(method);
_imp = method_getImplementation(method);
const char *name = sel_getName(_sel);
if (name) {
_name = [NSString stringWithUTF8String:name];
}
const char *typeEncoding = method_getTypeEncoding(method);
if (typeEncoding) {
_typeEncoding = [NSString stringWithUTF8String:typeEncoding];
}
char *returnType = method_copyReturnType(method);
if (returnType) {
_returnTypeEncoding = [NSString stringWithUTF8String:returnType];
free(returnType);
}
unsigned int argumentCount = method_getNumberOfArguments(method);
if (argumentCount > 0) {
NSMutableArray *argumentTypes = [NSMutableArray new];
for (unsigned int i = 0; i < argumentCount; i++) {
char *argumentType = method_copyArgumentType(method, i);
NSString *type = argumentType ? [NSString stringWithUTF8String:argumentType] : nil;
[argumentTypes addObject:type ? type : @""];
if (argumentType) free(argumentType);
}
_argumentTypeEncodings = argumentTypes;
}
return self;
}
@end
@implementation YYClassPropertyInfo
- (instancetype)initWithProperty:(objc_property_t)property {
if (!property) return nil;
self = [super init];
_property = property;
const char *name = property_getName(property);
if (name) {
_name = [NSString stringWithUTF8String:name];
}
YYEncodingType type = 0;
unsigned int attrCount;
objc_property_attribute_t *attrs = property_copyAttributeList(property, &attrCount);
for (unsigned int i = 0; i < attrCount; i++) {
switch (attrs[i].name[0]) {
case 'T': { // Type encoding
if (attrs[i].value) {
_typeEncoding = [NSString stringWithUTF8String:attrs[i].value];
type = YYEncodingGetType(attrs[i].value);
if ((type & YYEncodingTypeMask) == YYEncodingTypeObject && _typeEncoding.length) {
NSScanner *scanner = [NSScanner scannerWithString:_typeEncoding];
if (![scanner scanString:@"@\"" intoString:NULL]) continue;
NSString *clsName = nil;
if ([scanner scanUpToCharactersFromSet: [NSCharacterSet characterSetWithCharactersInString:@"\"<"] intoString:&clsName]) {
if (clsName.length) _cls = objc_getClass(clsName.UTF8String);
}
NSMutableArray *protocols = nil;
while ([scanner scanString:@"<" intoString:NULL]) {
NSString* protocol = nil;
if ([scanner scanUpToString:@">" intoString: &protocol]) {
if (protocol.length) {
if (!protocols) protocols = [NSMutableArray new];
[protocols addObject:protocol];
}
}
[scanner scanString:@">" intoString:NULL];
}
_protocols = protocols;
}
}
} break;
case 'V': { // Instance variable
if (attrs[i].value) {
_ivarName = [NSString stringWithUTF8String:attrs[i].value];
}
} break;
case 'R': {
type |= YYEncodingTypePropertyReadonly;
} break;
case 'C': {
type |= YYEncodingTypePropertyCopy;
} break;
case '&': {
type |= YYEncodingTypePropertyRetain;
} break;
case 'N': {
type |= YYEncodingTypePropertyNonatomic;
} break;
case 'D': {
type |= YYEncodingTypePropertyDynamic;
} break;
case 'W': {
type |= YYEncodingTypePropertyWeak;
} break;
case 'G': {
type |= YYEncodingTypePropertyCustomGetter;
if (attrs[i].value) {
_getter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]);
}
} break;
case 'S': {
type |= YYEncodingTypePropertyCustomSetter;
if (attrs[i].value) {
_setter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]);
}
} // break; commented for code coverage in next line
default: break;
}
}
if (attrs) {
free(attrs);
attrs = NULL;
}
_type = type;
if (_name.length) {
if (!_getter) {
_getter = NSSelectorFromString(_name);
}
if (!_setter) {
_setter = NSSelectorFromString([NSString stringWithFormat:@"set%@%@:", [_name substringToIndex:1].uppercaseString, [_name substringFromIndex:1]]);
}
}
return self;
}
@end
@implementation YYClassInfo {
BOOL _needUpdate;
}
- (instancetype)initWithClass:(Class)cls {
if (!cls) return nil;
self = [super init];
_cls = cls;
_superCls = class_getSuperclass(cls);
_isMeta = class_isMetaClass(cls);
if (!_isMeta) {
_metaCls = objc_getMetaClass(class_getName(cls));
}
_name = NSStringFromClass(cls);
[self _update];
_superClassInfo = [self.class classInfoWithClass:_superCls];
return self;
}
- (void)_update {
_ivarInfos = nil;
_methodInfos = nil;
_propertyInfos = nil;
Class cls = self.cls;
unsigned int methodCount = 0;
Method *methods = class_copyMethodList(cls, &methodCount);
if (methods) {
NSMutableDictionary *methodInfos = [NSMutableDictionary new];
_methodInfos = methodInfos;
for (unsigned int i = 0; i < methodCount; i++) {
YYClassMethodInfo *info = [[YYClassMethodInfo alloc] initWithMethod:methods[i]];
if (info.name) methodInfos[info.name] = info;
}
free(methods);
}
unsigned int propertyCount = 0;
objc_property_t *properties = class_copyPropertyList(cls, &propertyCount);
if (properties) {
NSMutableDictionary *propertyInfos = [NSMutableDictionary new];
_propertyInfos = propertyInfos;
for (unsigned int i = 0; i < propertyCount; i++) {
YYClassPropertyInfo *info = [[YYClassPropertyInfo alloc] initWithProperty:properties[i]];
if (info.name) propertyInfos[info.name] = info;
}
free(properties);
}
unsigned int ivarCount = 0;
Ivar *ivars = class_copyIvarList(cls, &ivarCount);
if (ivars) {
NSMutableDictionary *ivarInfos = [NSMutableDictionary new];
_ivarInfos = ivarInfos;
for (unsigned int i = 0; i < ivarCount; i++) {
YYClassIvarInfo *info = [[YYClassIvarInfo alloc] initWithIvar:ivars[i]];
if (info.name) ivarInfos[info.name] = info;
}
free(ivars);
}
if (!_ivarInfos) _ivarInfos = @{};
if (!_methodInfos) _methodInfos = @{};
if (!_propertyInfos) _propertyInfos = @{};
_needUpdate = NO;
}
- (void)setNeedUpdate {
_needUpdate = YES;
}
- (BOOL)needUpdate {
return _needUpdate;
}
+ (instancetype)classInfoWithClass:(Class)cls {
if (!cls) return nil;
static CFMutableDictionaryRef classCache;
static CFMutableDictionaryRef metaCache;
static dispatch_once_t onceToken;
static dispatch_semaphore_t lock;
dispatch_once(&onceToken, ^{
classCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
metaCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
lock = dispatch_semaphore_create(1);
});
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
YYClassInfo *info = CFDictionaryGetValue(class_isMetaClass(cls) ? metaCache : classCache, (__bridge const void *)(cls));
if (info && info->_needUpdate) {
[info _update];
}
dispatch_semaphore_signal(lock);
if (!info) {
info = [[YYClassInfo alloc] initWithClass:cls];
if (info) {
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
CFDictionarySetValue(info.isMeta ? metaCache : classCache, (__bridge const void *)(cls), (__bridge const void *)(info));
dispatch_semaphore_signal(lock);
}
}
return info;
}
+ (instancetype)classInfoWithClassName:(NSString *)className {
Class cls = NSClassFromString(className);
return [self classInfoWithClass:cls];
}
@end
//
// YYModel.h
// YYModel <https://github.com/ibireme/YYModel>
//
// Created by ibireme on 15/5/10.
// Copyright (c) 2015 ibireme.
//
// This source code is licensed under the MIT-style license found in the
// LICENSE file in the root directory of this source tree.
//
#import <Foundation/Foundation.h>
#if __has_include(<YYModel/YYModel.h>)
FOUNDATION_EXPORT double YYModelVersionNumber;
FOUNDATION_EXPORT const unsigned char YYModelVersionString[];
#import <YYModel/NSObject+YYModel.h>
#import <YYModel/YYClassInfo.h>
#else
#import "NSObject+YYModel.h"
#import "YYClassInfo.h"
#endif
支持 Markdown 格式
你添加了 0 到此讨论。请谨慎行事。
Finish editing this message first!