Browse Source

fix: add dependency

main
AnranYus 1 month ago
parent
commit
d3cecca27b
  1. 41
      iosApp/iosApp.xcodeproj/project.pbxproj
  2. 15
      iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
  3. 35
      iosApp/iosApp/Libs/DateTools/DTConstants.h
  4. 33
      iosApp/iosApp/Libs/DateTools/DTConstants.m
  5. 38
      iosApp/iosApp/Libs/DateTools/DTError.h
  6. 72
      iosApp/iosApp/Libs/DateTools/DTError.m
  7. 123
      iosApp/iosApp/Libs/DateTools/DTTimePeriod.h
  8. 642
      iosApp/iosApp/Libs/DateTools/DTTimePeriod.m
  9. 49
      iosApp/iosApp/Libs/DateTools/DTTimePeriodChain.h
  10. 218
      iosApp/iosApp/Libs/DateTools/DTTimePeriodChain.m
  11. 56
      iosApp/iosApp/Libs/DateTools/DTTimePeriodCollection.h
  12. 370
      iosApp/iosApp/Libs/DateTools/DTTimePeriodCollection.m
  13. 62
      iosApp/iosApp/Libs/DateTools/DTTimePeriodGroup.h
  14. 234
      iosApp/iosApp/Libs/DateTools/DTTimePeriodGroup.m
  15. 29
      iosApp/iosApp/Libs/DateTools/DateTools.h
  16. 194
      iosApp/iosApp/Libs/DateTools/NSDate+DateTools.h
  17. 1743
      iosApp/iosApp/Libs/DateTools/NSDate+DateTools.m
  18. 27
      iosApp/iosApp/Libs/MJExtension/MJExtension.h
  19. 111
      iosApp/iosApp/Libs/MJExtension/MJExtensionConst.h
  20. 27
      iosApp/iosApp/Libs/MJExtension/MJExtensionConst.m
  21. 16
      iosApp/iosApp/Libs/MJExtension/MJFoundation.h
  22. 70
      iosApp/iosApp/Libs/MJExtension/MJFoundation.m
  23. 53
      iosApp/iosApp/Libs/MJExtension/MJProperty.h
  24. 211
      iosApp/iosApp/Libs/MJExtension/MJProperty.m
  25. 30
      iosApp/iosApp/Libs/MJExtension/MJPropertyKey.h
  26. 25
      iosApp/iosApp/Libs/MJExtension/MJPropertyKey.m
  27. 39
      iosApp/iosApp/Libs/MJExtension/MJPropertyType.h
  28. 71
      iosApp/iosApp/Libs/MJExtension/MJPropertyType.m
  29. 90
      iosApp/iosApp/Libs/MJExtension/NSObject+MJClass.h
  30. 174
      iosApp/iosApp/Libs/MJExtension/NSObject+MJClass.m
  31. 66
      iosApp/iosApp/Libs/MJExtension/NSObject+MJCoding.h
  32. 59
      iosApp/iosApp/Libs/MJExtension/NSObject+MJCoding.m
  33. 194
      iosApp/iosApp/Libs/MJExtension/NSObject+MJKeyValue.h
  34. 524
      iosApp/iosApp/Libs/MJExtension/NSObject+MJKeyValue.m
  35. 70
      iosApp/iosApp/Libs/MJExtension/NSObject+MJProperty.h
  36. 240
      iosApp/iosApp/Libs/MJExtension/NSObject+MJProperty.m
  37. 33
      iosApp/iosApp/Libs/MJExtension/NSString+MJExtension.h
  38. 80
      iosApp/iosApp/Libs/MJExtension/NSString+MJExtension.m

41
iosApp/iosApp.xcodeproj/project.pbxproj

@ -7,11 +7,13 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
203834332DEEF65A004401C8 /* FMDB in Frameworks */ = {isa = PBXBuildFile; productRef = 203834322DEEF65A004401C8 /* FMDB */; };
2063EF612DEE6B790080FEBC /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2063EF602DEE6B790080FEBC /* Foundation.framework */; }; 2063EF612DEE6B790080FEBC /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2063EF602DEE6B790080FEBC /* Foundation.framework */; };
2063EF632DEE6B830080FEBC /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2063EF622DEE6B830080FEBC /* Security.framework */; }; 2063EF632DEE6B830080FEBC /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2063EF622DEE6B830080FEBC /* Security.framework */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
2038342F2DEEF5CC004401C8 /* libsqlite3.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libsqlite3.tbd; path = usr/lib/libsqlite3.tbd; sourceTree = SDKROOT; };
2063EF602DEE6B790080FEBC /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; 2063EF602DEE6B790080FEBC /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
2063EF622DEE6B830080FEBC /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; 2063EF622DEE6B830080FEBC /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
B9DA97B12DC1472C00A4DA20 /* RingAppKMP.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RingAppKMP.app; sourceTree = BUILT_PRODUCTS_DIR; }; B9DA97B12DC1472C00A4DA20 /* RingAppKMP.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RingAppKMP.app; sourceTree = BUILT_PRODUCTS_DIR; };
@ -48,6 +50,7 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
203834332DEEF65A004401C8 /* FMDB in Frameworks */,
2063EF632DEE6B830080FEBC /* Security.framework in Frameworks */, 2063EF632DEE6B830080FEBC /* Security.framework in Frameworks */,
2063EF612DEE6B790080FEBC /* Foundation.framework in Frameworks */, 2063EF612DEE6B790080FEBC /* Foundation.framework in Frameworks */,
); );
@ -59,6 +62,7 @@
2063EF5F2DEE6B790080FEBC /* Frameworks */ = { 2063EF5F2DEE6B790080FEBC /* Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
2038342F2DEEF5CC004401C8 /* libsqlite3.tbd */,
2063EF622DEE6B830080FEBC /* Security.framework */, 2063EF622DEE6B830080FEBC /* Security.framework */,
2063EF602DEE6B790080FEBC /* Foundation.framework */, 2063EF602DEE6B790080FEBC /* Foundation.framework */,
); );
@ -104,6 +108,7 @@
); );
name = iosApp; name = iosApp;
packageProductDependencies = ( packageProductDependencies = (
203834322DEEF65A004401C8 /* FMDB */,
); );
productName = iosApp; productName = iosApp;
productReference = B9DA97B12DC1472C00A4DA20 /* RingAppKMP.app */; productReference = B9DA97B12DC1472C00A4DA20 /* RingAppKMP.app */;
@ -133,6 +138,9 @@
); );
mainGroup = B9DA97A82DC1472C00A4DA20; mainGroup = B9DA97A82DC1472C00A4DA20;
minimizedProjectReferenceProxies = 1; minimizedProjectReferenceProxies = 1;
packageReferences = (
203834312DEEF65A004401C8 /* XCRemoteSwiftPackageReference "fmdb" */,
);
preferredProjectObjectVersion = 77; preferredProjectObjectVersion = 77;
productRefGroup = B9DA97B22DC1472C00A4DA20 /* Products */; productRefGroup = B9DA97B22DC1472C00A4DA20 /* Products */;
projectDirPath = ""; projectDirPath = "";
@ -315,7 +323,7 @@
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\"";
DEVELOPMENT_TEAM = "${TEAM_ID}"; DEVELOPMENT_TEAM = NLHG3FFX4L;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
@ -342,8 +350,9 @@
"$(PROJECT_DIR)/iosApp/Libs", "$(PROJECT_DIR)/iosApp/Libs",
); );
OTHER_CFLAGS = ( OTHER_CFLAGS = (
"-DSQLITE_HAS_CODEC-DSQLITE_TEMP_STORE=3-DSQLCIPHER_CRYPTO_CC-DN", "-DSQLITE_TEMP_STORE=3",
DEBUG, "-DSQLCIPHER_CRYPTO_CC",
"-DNDEBUG",
); );
OTHER_LDFLAGS = "-Objc"; OTHER_LDFLAGS = "-Objc";
SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_EMIT_LOC_STRINGS = YES;
@ -362,7 +371,7 @@
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\"";
DEVELOPMENT_TEAM = "${TEAM_ID}"; DEVELOPMENT_TEAM = NLHG3FFX4L;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
@ -384,8 +393,9 @@
"$(PROJECT_DIR)/iosApp/Libs", "$(PROJECT_DIR)/iosApp/Libs",
); );
OTHER_CFLAGS = ( OTHER_CFLAGS = (
"-DSQLITE_HAS_CODEC-DSQLITE_TEMP_STORE=3-DSQLCIPHER_CRYPTO_CC-DN", "-DSQLITE_TEMP_STORE=3",
DEBUG, "-DSQLCIPHER_CRYPTO_CC",
"-DNDEBUG",
); );
OTHER_LDFLAGS = "-Objc"; OTHER_LDFLAGS = "-Objc";
SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_EMIT_LOC_STRINGS = YES;
@ -416,6 +426,25 @@
defaultConfigurationName = Release; defaultConfigurationName = Release;
}; };
/* End XCConfigurationList section */ /* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */
203834312DEEF65A004401C8 /* XCRemoteSwiftPackageReference "fmdb" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/ccgus/fmdb";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 2.7.12;
};
};
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
203834322DEEF65A004401C8 /* FMDB */ = {
isa = XCSwiftPackageProductDependency;
package = 203834312DEEF65A004401C8 /* XCRemoteSwiftPackageReference "fmdb" */;
productName = FMDB;
};
/* End XCSwiftPackageProductDependency section */
}; };
rootObject = B9DA97A92DC1472C00A4DA20 /* Project object */; rootObject = B9DA97A92DC1472C00A4DA20 /* Project object */;
} }

15
iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved

@ -0,0 +1,15 @@
{
"originHash" : "edf512815619ed6bb2e67b3bdb38a134d5e8f6b3f3f79c05028ab9cbffc0efd2",
"pins" : [
{
"identity" : "fmdb",
"kind" : "remoteSourceControl",
"location" : "https://github.com/ccgus/fmdb",
"state" : {
"revision" : "1227a3fa2b9916bfd75fe380eb45cd210e69e251",
"version" : "2.7.12"
}
}
],
"version" : 3
}

35
iosApp/iosApp/Libs/DateTools/DTConstants.h

@ -0,0 +1,35 @@
// Copyright (C) 2014 by Matthew York
//
// Permission is hereby granted, free of charge, to any
// person obtaining a copy of this software and
// associated documentation files (the "Software"), to
// deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the
// Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall
// be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
FOUNDATION_EXPORT const long long SECONDS_IN_YEAR;
FOUNDATION_EXPORT const NSInteger SECONDS_IN_MONTH_28;
FOUNDATION_EXPORT const NSInteger SECONDS_IN_MONTH_29;
FOUNDATION_EXPORT const NSInteger SECONDS_IN_MONTH_30;
FOUNDATION_EXPORT const NSInteger SECONDS_IN_MONTH_31;
FOUNDATION_EXPORT const NSInteger SECONDS_IN_WEEK;
FOUNDATION_EXPORT const NSInteger SECONDS_IN_DAY;
FOUNDATION_EXPORT const NSInteger SECONDS_IN_HOUR;
FOUNDATION_EXPORT const NSInteger SECONDS_IN_MINUTE;
FOUNDATION_EXPORT const NSInteger MILLISECONDS_IN_DAY;
#import "DTError.h"

33
iosApp/iosApp/Libs/DateTools/DTConstants.m

@ -0,0 +1,33 @@
// Copyright (C) 2014 by Matthew York
//
// Permission is hereby granted, free of charge, to any
// person obtaining a copy of this software and
// associated documentation files (the "Software"), to
// deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the
// Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall
// be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "DTConstants.h"
const long long SECONDS_IN_YEAR = 31556900;
const NSInteger SECONDS_IN_MONTH_28 = 2419200;
const NSInteger SECONDS_IN_MONTH_29 = 2505600;
const NSInteger SECONDS_IN_MONTH_30 = 2592000;
const NSInteger SECONDS_IN_MONTH_31 = 2678400;
const NSInteger SECONDS_IN_WEEK = 604800;
const NSInteger SECONDS_IN_DAY = 86400;
const NSInteger SECONDS_IN_HOUR = 3600;
const NSInteger SECONDS_IN_MINUTE = 60;
const NSInteger MILLISECONDS_IN_DAY = 86400000;

38
iosApp/iosApp/Libs/DateTools/DTError.h

@ -0,0 +1,38 @@
// Copyright (C) 2014 by Matthew York
//
// Permission is hereby granted, free of charge, to any
// person obtaining a copy of this software and
// associated documentation files (the "Software"), to
// deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the
// Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall
// be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
#pragma mark - Domain
extern NSString *const DTErrorDomain;
#pragma mark - Status Codes
static const NSUInteger DTInsertOutOfBoundsException = 0;
static const NSUInteger DTRemoveOutOfBoundsException = 1;
static const NSUInteger DTBadTypeException = 2;
@interface DTError : NSObject
+(void)throwInsertOutOfBoundsException:(NSInteger)index array:(NSArray *)array;
+(void)throwRemoveOutOfBoundsException:(NSInteger)index array:(NSArray *)array;
+(void)throwBadTypeException:(id)obj expectedClass:(Class)classType;
@end

72
iosApp/iosApp/Libs/DateTools/DTError.m

@ -0,0 +1,72 @@
// Copyright (C) 2014 by Matthew York
//
// Permission is hereby granted, free of charge, to any
// person obtaining a copy of this software and
// associated documentation files (the "Software"), to
// deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the
// Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall
// be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "DTError.h"
#pragma mark - Domain
NSString *const DTErrorDomain = @"com.mattyork.dateTools";
@implementation DTError
+(void)throwInsertOutOfBoundsException:(NSInteger)index array:(NSArray *)array{
//Handle possible zero bounds
NSInteger arrayUpperBound = (array.count == 0)? 0:array.count;
//Create info for error
NSDictionary *userInfo = @{NSLocalizedDescriptionKey: NSLocalizedString(@"Operation was unsuccessful.", nil), NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"Attempted to insert DTTimePeriod at index %ld but the group is of size [0...%ld].", (long)index, (long)arrayUpperBound],NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(@"Please try an index within the bounds or the group.", nil)};
//Handle Error
NSError *error = [NSError errorWithDomain:DTErrorDomain code:DTInsertOutOfBoundsException userInfo:userInfo];
[self printErrorWithCallStack:error];
}
+(void)throwRemoveOutOfBoundsException:(NSInteger)index array:(NSArray *)array{
//Handle possible zero bounds
NSInteger arrayUpperBound = (array.count == 0)? 0:array.count;
//Create info for error
NSDictionary *userInfo = @{NSLocalizedDescriptionKey: NSLocalizedString(@"Operation was unsuccessful.", nil), NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"Attempted to remove DTTimePeriod at index %ld but the group is of size [0...%ld].", (long)index, (long)arrayUpperBound],NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(@"Please try an index within the bounds of the group.", nil)};
//Handle Error
NSError *error = [NSError errorWithDomain:DTErrorDomain code:DTRemoveOutOfBoundsException userInfo:userInfo];
[self printErrorWithCallStack:error];
}
+(void)throwBadTypeException:(id)obj expectedClass:(Class)classType{
//Create info for error
NSDictionary *userInfo = @{NSLocalizedDescriptionKey: NSLocalizedString(@"Operation was unsuccessful.", nil), NSLocalizedFailureReasonErrorKey: [NSString stringWithFormat:@"Attempted to insert object of class %@ when expecting object of class %@.", NSStringFromClass([obj class]), NSStringFromClass(classType)],NSLocalizedRecoverySuggestionErrorKey: NSLocalizedString(@"Please try again by inserting a DTTimePeriod object.", nil)};
//Handle Error
NSError *error = [NSError errorWithDomain:DTErrorDomain code:DTBadTypeException userInfo:userInfo];
[self printErrorWithCallStack:error];
}
+(void)printErrorWithCallStack:(NSError *)error{
//Print error
NSLog(@"%@", error);
//Print call stack
for (NSString *symbol in [NSThread callStackSymbols]) {
NSLog(@"\n\n %@", symbol);
}
}
@end

123
iosApp/iosApp/Libs/DateTools/DTTimePeriod.h

@ -0,0 +1,123 @@
// Copyright (C) 2014 by Matthew York
//
// Permission is hereby granted, free of charge, to any
// person obtaining a copy of this software and
// associated documentation files (the "Software"), to
// deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the
// Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall
// be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
typedef NS_ENUM(NSUInteger, DTTimePeriodRelation){
DTTimePeriodRelationAfter,
DTTimePeriodRelationStartTouching,
DTTimePeriodRelationStartInside,
DTTimePeriodRelationInsideStartTouching,
DTTimePeriodRelationEnclosingStartTouching,
DTTimePeriodRelationEnclosing,
DTTimePeriodRelationEnclosingEndTouching,
DTTimePeriodRelationExactMatch,
DTTimePeriodRelationInside,
DTTimePeriodRelationInsideEndTouching,
DTTimePeriodRelationEndInside,
DTTimePeriodRelationEndTouching,
DTTimePeriodRelationBefore,
DTTimePeriodRelationNone //One or more of the dates does not exist
};
typedef NS_ENUM(NSUInteger, DTTimePeriodSize) {
DTTimePeriodSizeSecond,
DTTimePeriodSizeMinute,
DTTimePeriodSizeHour,
DTTimePeriodSizeDay,
DTTimePeriodSizeWeek,
DTTimePeriodSizeMonth,
DTTimePeriodSizeYear
};
typedef NS_ENUM(NSUInteger, DTTimePeriodInterval) {
DTTimePeriodIntervalOpen,
DTTimePeriodIntervalClosed
};
typedef NS_ENUM(NSUInteger, DTTimePeriodAnchor) {
DTTimePeriodAnchorStart,
DTTimePeriodAnchorCenter,
DTTimePeriodAnchorEnd
};
@interface DTTimePeriod : NSObject
/**
* The start date for a DTTimePeriod representing the starting boundary of the time period
*/
@property (nonatomic,strong) NSDate *StartDate;
/**
* The end date for a DTTimePeriod representing the ending boundary of the time period
*/
@property (nonatomic,strong) NSDate *EndDate;
#pragma mark - Custom Init / Factory Methods
-(instancetype)initWithStartDate:(NSDate *)startDate endDate:(NSDate *)endDate;
+(instancetype)timePeriodWithStartDate:(NSDate *)startDate endDate:(NSDate *)endDate;
+(instancetype)timePeriodWithSize:(DTTimePeriodSize)size startingAt:(NSDate *)date;
+(instancetype)timePeriodWithSize:(DTTimePeriodSize)size amount:(NSInteger)amount startingAt:(NSDate *)date;
+(instancetype)timePeriodWithSize:(DTTimePeriodSize)size endingAt:(NSDate *)date;
+(instancetype)timePeriodWithSize:(DTTimePeriodSize)size amount:(NSInteger)amount endingAt:(NSDate *)date;
+(instancetype)timePeriodWithAllTime;
#pragma mark - Time Period Information
-(BOOL)hasStartDate;
-(BOOL)hasEndDate;
-(BOOL)isMoment;
-(double)durationInYears;
-(double)durationInWeeks;
-(double)durationInDays;
-(double)durationInHours;
-(double)durationInMinutes;
-(double)durationInSeconds;
#pragma mark - Time Period Relationship
-(BOOL)isEqualToPeriod:(DTTimePeriod *)period;
-(BOOL)isInside:(DTTimePeriod *)period;
-(BOOL)contains:(DTTimePeriod *)period;
-(BOOL)overlapsWith:(DTTimePeriod *)period;
-(BOOL)intersects:(DTTimePeriod *)period;
-(DTTimePeriodRelation)relationToPeriod:(DTTimePeriod *)period;
-(NSTimeInterval)gapBetween:(DTTimePeriod *)period;
#pragma mark - Date Relationships
-(BOOL)containsDate:(NSDate *)date interval:(DTTimePeriodInterval)interval;
#pragma mark - Period Manipulation
#pragma mark Shifts
-(void)shiftEarlierWithSize:(DTTimePeriodSize)size;
-(void)shiftEarlierWithSize:(DTTimePeriodSize)size amount:(NSInteger)amount;
-(void)shiftLaterWithSize:(DTTimePeriodSize)size;
-(void)shiftLaterWithSize:(DTTimePeriodSize)size amount:(NSInteger)amount;
#pragma mark Lengthen / Shorten
-(void)lengthenWithAnchorDate:(DTTimePeriodAnchor)anchor size:(DTTimePeriodSize)size;
-(void)lengthenWithAnchorDate:(DTTimePeriodAnchor)anchor size:(DTTimePeriodSize)size amount:(NSInteger)amount;
-(void)shortenWithAnchorDate:(DTTimePeriodAnchor)anchor size:(DTTimePeriodSize)size;
-(void)shortenWithAnchorDate:(DTTimePeriodAnchor)anchor size:(DTTimePeriodSize)size amount:(NSInteger)amount;
#pragma mark - Helper Methods
-(DTTimePeriod *)copy;
@end

642
iosApp/iosApp/Libs/DateTools/DTTimePeriod.m

@ -0,0 +1,642 @@
// Copyright (C) 2014 by Matthew York
//
// Permission is hereby granted, free of charge, to any
// person obtaining a copy of this software and
// associated documentation files (the "Software"), to
// deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the
// Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall
// be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "DTTimePeriod.h"
#import "NSDate+DateTools.h"
@interface DTTimePeriod ()
@end
@implementation DTTimePeriod
#pragma mark - Custom Init / Factory Methods
/**
* Initializes an instance of DTTimePeriod from a given start and end date
*
* @param startDate NSDate - Desired start date
* @param endDate NSDate - Desired end date
*
* @return DTTimePeriod - new instance
*/
-(instancetype)initWithStartDate:(NSDate *)startDate endDate:(NSDate *)endDate{
if (self = [super init]) {
self.StartDate = startDate;
self.EndDate = endDate;
}
return self;
}
/**
* Returns a new instance of DTTimePeriod from a given start and end date
*
* @param startDate NSDate - Desired start date
* @param endDate NSDate - Desired end date
*
* @return DTTimePeriod - new instance
*/
+(instancetype)timePeriodWithStartDate:(NSDate *)startDate endDate:(NSDate *)endDate{
return [[self.class alloc] initWithStartDate:startDate endDate:endDate];
}
/**
* Returns a new instance of DTTimePeriod that starts on the provided start date
* and is of the size provided
*
* @param size DTTimePeriodSize - Desired size of the new time period
* @param date NSDate - Desired start date of the new time period
*
* @return DTTimePeriod - new instance
*/
+(instancetype)timePeriodWithSize:(DTTimePeriodSize)size startingAt:(NSDate *)date{
return [[self.class alloc] initWithStartDate:date endDate:[DTTimePeriod dateWithAddedTime:size amount:1 baseDate:date]];
}
/**
* Returns a new instance of DTTimePeriod that starts on the provided start date
* and is of the size provided. The amount represents a multipler to the size (e.g. "2 weeks" or "4 years")
*
* @param size DTTimePeriodSize - Desired size of the new time period
* @param amount NSInteger - Desired multiplier of the size provided
* @param date NSDate - Desired start date of the new time period
*
* @return DTTimePeriod - new instance
*/
+(instancetype)timePeriodWithSize:(DTTimePeriodSize)size amount:(NSInteger)amount startingAt:(NSDate *)date{
return [[self.class alloc] initWithStartDate:date endDate:[DTTimePeriod dateWithAddedTime:size amount:amount baseDate:date]];
}
/**
* Returns a new instance of DTTimePeriod that ends on the provided end date
* and is of the size provided
*
* @param size DTTimePeriodSize - Desired size of the new time period
* @param date NSDate - Desired end date of the new time period
*
* @return DTTimePeriod - new instance
*/
+(instancetype)timePeriodWithSize:(DTTimePeriodSize)size endingAt:(NSDate *)date{
return [[self.class alloc] initWithStartDate:[DTTimePeriod dateWithSubtractedTime:size amount:1 baseDate:date] endDate:date];
}
/**
* Returns a new instance of DTTimePeriod that ends on the provided end date
* and is of the size provided. The amount represents a multipler to the size (e.g. "2 weeks" or "4 years")
*
* @param size DTTimePeriodSize - Desired size of the new time period
* @param amount NSInteger - Desired multiplier of the size provided
* @param date NSDate - Desired end date of the new time period
*
* @return DTTimePeriod - new instance
*/
+(instancetype)timePeriodWithSize:(DTTimePeriodSize)size amount:(NSInteger)amount endingAt:(NSDate *)date{
return [[self.class alloc] initWithStartDate:[DTTimePeriod dateWithSubtractedTime:size amount:amount baseDate:date] endDate:date];
}
/**
* Returns a new instance of DTTimePeriod that represents the largest time period available.
* The start date is in the distant past and the end date is in the distant future.
*
* @return DTTimePeriod - new instance
*/
+(instancetype)timePeriodWithAllTime{
return [[self.class alloc] initWithStartDate:[NSDate distantPast] endDate:[NSDate distantFuture]];
}
/**
* Method serving the various factory methods as well as a few others.
* Returns a date with time added to a given base date. Includes multiplier amount.
*
* @param size DTTimePeriodSize - Desired size of the new time period
* @param amount NSInteger - Desired multiplier of the size provided
* @param date NSDate - Desired end date of the new time period
*
* @return NSDate - new instance
*/
+(NSDate *)dateWithAddedTime:(DTTimePeriodSize)size amount:(NSInteger)amount baseDate:(NSDate *)date{
switch (size) {
case DTTimePeriodSizeSecond:
return [date dateByAddingSeconds:amount];
break;
case DTTimePeriodSizeMinute:
return [date dateByAddingMinutes:amount];
break;
case DTTimePeriodSizeHour:
return [date dateByAddingHours:amount];
break;
case DTTimePeriodSizeDay:
return [date dateByAddingDays:amount];
break;
case DTTimePeriodSizeWeek:
return [date dateByAddingWeeks:amount];
break;
case DTTimePeriodSizeMonth:
return [date dateByAddingMonths:amount];
break;
case DTTimePeriodSizeYear:
return [date dateByAddingYears:amount];
break;
default:
break;
}
return date;
}
/**
* Method serving the various factory methods as well as a few others.
* Returns a date with time subtracted from a given base date. Includes multiplier amount.
*
* @param size DTTimePeriodSize - Desired size of the new time period
* @param amount NSInteger - Desired multiplier of the size provided
* @param date NSDate - Desired end date of the new time period
*
* @return NSDate - new instance
*/
+(NSDate *)dateWithSubtractedTime:(DTTimePeriodSize)size amount:(NSInteger)amount baseDate:(NSDate *)date{
switch (size) {
case DTTimePeriodSizeSecond:
return [date dateBySubtractingSeconds:amount];
break;
case DTTimePeriodSizeMinute:
return [date dateBySubtractingMinutes:amount];
break;
case DTTimePeriodSizeHour:
return [date dateBySubtractingHours:amount];
break;
case DTTimePeriodSizeDay:
return [date dateBySubtractingDays:amount];
break;
case DTTimePeriodSizeWeek:
return [date dateBySubtractingWeeks:amount];
break;
case DTTimePeriodSizeMonth:
return [date dateBySubtractingMonths:amount];
break;
case DTTimePeriodSizeYear:
return [date dateBySubtractingYears:amount];
break;
default:
break;
}
return date;
}
#pragma mark - Time Period Information
/**
* Returns a boolean representing whether the receiver's StartDate exists
* Returns YES if StartDate is not nil, otherwise NO
*
* @return BOOL
*/
-(BOOL)hasStartDate {
return (self.StartDate)? YES:NO;
}
/**
* Returns a boolean representing whether the receiver's EndDate exists
* Returns YES if EndDate is not nil, otherwise NO
*
* @return BOOL
*/
-(BOOL)hasEndDate {
return (self.EndDate)? YES:NO;
}
/**
* Returns a boolean representing whether the receiver is a "moment", that is the start and end dates are the same.
* Returns YES if receiver is a moment, otherwise NO
*
* @return BOOL
*/
-(BOOL)isMoment{
if (self.StartDate && self.EndDate) {
if ([self.StartDate isEqualToDate:self.EndDate]) {
return YES;
}
}
return NO;
}
/**
* Returns the duration of the receiver in years
*
* @return NSInteger
*/
-(double)durationInYears {
if (self.StartDate && self.EndDate) {
return [self.StartDate yearsEarlierThan:self.EndDate];
}
return 0;
}
/**
* Returns the duration of the receiver in weeks
*
* @return double
*/
-(double)durationInWeeks {
if (self.StartDate && self.EndDate) {
return [self.StartDate weeksEarlierThan:self.EndDate];
}
return 0;
}
/**
* Returns the duration of the receiver in days
*
* @return double
*/
-(double)durationInDays {
if (self.StartDate && self.EndDate) {
return [self.StartDate daysEarlierThan:self.EndDate];
}
return 0;
}
/**
* Returns the duration of the receiver in hours
*
* @return double
*/
-(double)durationInHours {
if (self.StartDate && self.EndDate) {
return [self.StartDate hoursEarlierThan:self.EndDate];
}
return 0;
}
/**
* Returns the duration of the receiver in minutes
*
* @return double
*/
-(double)durationInMinutes {
if (self.StartDate && self.EndDate) {
return [self.StartDate minutesEarlierThan:self.EndDate];
}
return 0;
}
/**
* Returns the duration of the receiver in seconds
*
* @return double
*/
-(double)durationInSeconds {
if (self.StartDate && self.EndDate) {
return [self.StartDate secondsEarlierThan:self.EndDate];
}
return 0;
}
#pragma mark - Time Period Relationship
/**
* Returns a BOOL representing whether the receiver's start and end dates exatcly match a given time period
* Returns YES if the two periods are the same, otherwise NO
*
* @param period DTTimePeriod - Time period to compare to receiver
*
* @return BOOL
*/
-(BOOL)isEqualToPeriod:(DTTimePeriod *)period{
if ([self.StartDate isEqualToDate:period.StartDate] && [self.EndDate isEqualToDate:period.EndDate]) {
return YES;
}
return NO;
}
/**
* Returns a BOOL representing whether the receiver's start and end dates exatcly match a given time period or is contained within them
* Returns YES if the receiver is inside the given time period, otherwise NO
*
* @param period DTTimePeriod - Time period to compare to receiver
*
* @return BOOL
*/
-(BOOL)isInside:(DTTimePeriod *)period{
if ([period.StartDate isEarlierThanOrEqualTo:self.StartDate] && [period.EndDate isLaterThanOrEqualTo:self.EndDate]) {
return YES;
}
return NO;
}
/**
* Returns a BOOL representing whether the given time period's start and end dates exatcly match the receivers' or is contained within them
* Returns YES if the receiver is inside the given time period, otherwise NO
*
* @param period DTTimePeriod - Time period to compare to receiver
*
* @return BOOL
*/
-(BOOL)contains:(DTTimePeriod *)period{
if ([self.StartDate isEarlierThanOrEqualTo:period.StartDate] && [self.EndDate isLaterThanOrEqualTo:period.EndDate]) {
return YES;
}
return NO;
}
/**
* Returns a BOOL representing whether the receiver and the given time period overlap.
* This covers all space they share, minus instantaneous space (i.e. one's start date equals another's end date)
* Returns YES if they overlap, otherwise NO
*
* @param period DTTimePeriod - Time period to compare to receiver
*
* @return BOOL
*/
-(BOOL)overlapsWith:(DTTimePeriod *)period{
//Outside -> Inside
if ([period.StartDate isEarlierThan:self.StartDate] && [period.EndDate isLaterThan:self.StartDate]) {
return YES;
}
//Enclosing
else if ([period.StartDate isLaterThanOrEqualTo:self.StartDate] && [period.EndDate isEarlierThanOrEqualTo:self.EndDate]){
return YES;
}
//Inside -> Out
else if([period.StartDate isEarlierThan:self.EndDate] && [period.EndDate isLaterThan:self.EndDate]){
return YES;
}
return NO;
}
/**
* Returns a BOOL representing whether the receiver and the given time period overlap.
* This covers all space they share, including instantaneous space (i.e. one's start date equals another's end date)
* Returns YES if they overlap, otherwise NO
*
* @param period DTTimePeriod - Time period to compare to receiver
*
* @return BOOL
*/
-(BOOL)intersects:(DTTimePeriod *)period{
//Outside -> Inside
if ([period.StartDate isEarlierThan:self.StartDate] && [period.EndDate isLaterThanOrEqualTo:self.StartDate]) {
return YES;
}
//Enclosing
else if ([period.StartDate isLaterThanOrEqualTo:self.StartDate] && [period.EndDate isEarlierThanOrEqualTo:self.EndDate]){
return YES;
}
//Inside -> Out
else if([period.StartDate isEarlierThanOrEqualTo:self.EndDate] && [period.EndDate isLaterThan:self.EndDate]){
return YES;
}
return NO;
}
/**
* Returns the relationship of the receiver to a given time period
*
* @param period DTTimePeriod - Time period to compare to receiver
*
* @return DTTimePeriodRelation
*/
-(DTTimePeriodRelation)relationToPeriod:(DTTimePeriod *)period{
//Make sure that all start and end points exist for comparison
if (self.StartDate && self.EndDate && period.StartDate && period.EndDate) {
//Make sure time periods are of positive durations
if ([self.StartDate isEarlierThan:self.EndDate] && [period.StartDate isEarlierThan:period.EndDate]) {
//Make comparisons
if ([period.EndDate isEarlierThan:self.StartDate]) {
return DTTimePeriodRelationAfter;
}
else if ([period.EndDate isEqualToDate:self.StartDate]){
return DTTimePeriodRelationStartTouching;
}
else if ([period.StartDate isEarlierThan:self.StartDate] && [period.EndDate isEarlierThan:self.EndDate]){
return DTTimePeriodRelationStartInside;
}
else if ([period.StartDate isEqualToDate:self.StartDate] && [period.EndDate isLaterThan:self.EndDate]){
return DTTimePeriodRelationInsideStartTouching;
}
else if ([period.StartDate isEqualToDate:self.StartDate] && [period.EndDate isEarlierThan:self.EndDate]){
return DTTimePeriodRelationEnclosingStartTouching;
}
else if ([period.StartDate isLaterThan:self.StartDate] && [period.EndDate isEarlierThan:self.EndDate]){
return DTTimePeriodRelationEnclosing;
}
else if ([period.StartDate isLaterThan:self.StartDate] && [period.EndDate isEqualToDate:self.EndDate]){
return DTTimePeriodRelationEnclosingEndTouching;
}
else if ([period.StartDate isEqualToDate:self.StartDate] && [period.EndDate isEqualToDate:self.EndDate]){
return DTTimePeriodRelationExactMatch;
}
else if ([period.StartDate isEarlierThan:self.StartDate] && [period.EndDate isLaterThan:self.EndDate]){
return DTTimePeriodRelationInside;
}
else if ([period.StartDate isEarlierThan:self.StartDate] && [period.EndDate isEqualToDate:self.EndDate]){
return DTTimePeriodRelationInsideEndTouching;
}
else if ([period.StartDate isEarlierThan:self.EndDate] && [period.EndDate isLaterThan:self.EndDate]){
return DTTimePeriodRelationEndInside;
}
else if ([period.StartDate isEqualToDate:self.EndDate] && [period.EndDate isLaterThan:self.EndDate]){
return DTTimePeriodRelationEndTouching;
}
else if ([period.StartDate isLaterThan:self.EndDate]){
return DTTimePeriodRelationBefore;
}
}
}
return DTTimePeriodRelationNone;
}
/**
* Returns the gap in seconds between the receiver and provided time period
* Returns 0 if the time periods intersect, otherwise returns the gap between.
*
* @param period <#period description#>
*
* @return <#return value description#>
*/
-(NSTimeInterval)gapBetween:(DTTimePeriod *)period{
if ([self.EndDate isEarlierThan:period.StartDate]) {
return ABS([self.EndDate timeIntervalSinceDate:period.StartDate]);
}
else if ([period.EndDate isEarlierThan:self.StartDate]){
return ABS([period.EndDate timeIntervalSinceDate:self.StartDate]);
}
return 0;
}
#pragma mark - Date Relationships
/**
* Returns a BOOL representing whether the provided date is contained in the receiver.
*
* @param date NSDate - Date to evaluate
* @param interval DTTimePeriodInterval representing evaluation type (Closed includes StartDate and EndDate in evaluation, Open does not)
*
* @return <#return value description#>
*/
-(BOOL)containsDate:(NSDate *)date interval:(DTTimePeriodInterval)interval{
if (interval == DTTimePeriodIntervalOpen) {
if ([self.StartDate isEarlierThan:date] && [self.EndDate isLaterThan:date]) {
return YES;
}
else {
return NO;
}
}
else if (interval == DTTimePeriodIntervalClosed){
if ([self.StartDate isEarlierThanOrEqualTo:date] && [self.EndDate isLaterThanOrEqualTo:date]) {
return YES;
}
else {
return NO;
}
}
return NO;
}
#pragma mark - Period Manipulation
/**
* Shifts the StartDate and EndDate earlier by a given size amount
*
* @param size DTTimePeriodSize - Desired shift size
*/
-(void)shiftEarlierWithSize:(DTTimePeriodSize)size{
[self shiftEarlierWithSize:size amount:1];
}
/**
* Shifts the StartDate and EndDate earlier by a given size amount. Amount multiplies size.
*
* @param size DTTimePeriodSize - Desired shift size
* @param amount NSInteger - Multiplier of size (i.e. "2 weeks" or "4 years")
*/
-(void)shiftEarlierWithSize:(DTTimePeriodSize)size amount:(NSInteger)amount{
self.StartDate = [DTTimePeriod dateWithSubtractedTime:size amount:amount baseDate:self.StartDate];
self.EndDate = [DTTimePeriod dateWithSubtractedTime:size amount:amount baseDate:self.EndDate];
}
/**
* Shifts the StartDate and EndDate later by a given size amount
*
* @param size DTTimePeriodSize - Desired shift size
*/
-(void)shiftLaterWithSize:(DTTimePeriodSize)size{
[self shiftLaterWithSize:size amount:1];
}
/**
* Shifts the StartDate and EndDate later by a given size amount. Amount multiplies size.
*
* @param size DTTimePeriodSize - Desired shift size
* @param amount NSInteger - Multiplier of size (i.e. "2 weeks" or "4 years")
*/
-(void)shiftLaterWithSize:(DTTimePeriodSize)size amount:(NSInteger)amount{
self.StartDate = [DTTimePeriod dateWithAddedTime:size amount:amount baseDate:self.StartDate];
self.EndDate = [DTTimePeriod dateWithAddedTime:size amount:amount baseDate:self.EndDate];
}
#pragma mark Lengthen / Shorten
/**
* Lengthens the receiver by a given amount, anchored by a provided point
*
* @param anchor DTTimePeriodAnchor - Anchor point for the lengthen (the date that stays the same)
* @param size DTTimePeriodSize - Desired lenghtening size
*/
-(void)lengthenWithAnchorDate:(DTTimePeriodAnchor)anchor size:(DTTimePeriodSize)size{
[self lengthenWithAnchorDate:anchor size:size amount:1];
}
/**
* Lengthens the receiver by a given amount, anchored by a provided point. Amount multiplies size.
*
* @param anchor DTTimePeriodAnchor - Anchor point for the lengthen (the date that stays the same)
* @param size DTTimePeriodSize - Desired lenghtening size
* @param amount NSInteger - Multiplier of size (i.e. "2 weeks" or "4 years")
*/
-(void)lengthenWithAnchorDate:(DTTimePeriodAnchor)anchor size:(DTTimePeriodSize)size amount:(NSInteger)amount{
switch (anchor) {
case DTTimePeriodAnchorStart:
self.EndDate = [DTTimePeriod dateWithAddedTime:size amount:amount baseDate:self.EndDate];
break;
case DTTimePeriodAnchorCenter:
self.StartDate = [DTTimePeriod dateWithSubtractedTime:size amount:amount/2 baseDate:self.StartDate];
self.EndDate = [DTTimePeriod dateWithAddedTime:size amount:amount/2 baseDate:self.EndDate];
break;
case DTTimePeriodAnchorEnd:
self.StartDate = [DTTimePeriod dateWithSubtractedTime:size amount:amount baseDate:self.StartDate];
break;
default:
break;
}
}
/**
* Shortens the receiver by a given amount, anchored by a provided point
*
* @param anchor DTTimePeriodAnchor - Anchor point for the shorten (the date that stays the same)
* @param size DTTimePeriodSize - Desired shortening size
*/
-(void)shortenWithAnchorDate:(DTTimePeriodAnchor)anchor size:(DTTimePeriodSize)size{
[self shortenWithAnchorDate:anchor size:size amount:1];
}
/**
* Shortens the receiver by a given amount, anchored by a provided point. Amount multiplies size.
*
* @param anchor DTTimePeriodAnchor - Anchor point for the shorten (the date that stays the same)
* @param size DTTimePeriodSize - Desired shortening size
* @param amount NSInteger - Multiplier of size (i.e. "2 weeks" or "4 years")
*/
-(void)shortenWithAnchorDate:(DTTimePeriodAnchor)anchor size:(DTTimePeriodSize)size amount:(NSInteger)amount{
switch (anchor) {
case DTTimePeriodAnchorStart:
self.EndDate = [DTTimePeriod dateWithSubtractedTime:size amount:amount baseDate:self.EndDate];
break;
case DTTimePeriodAnchorCenter:
self.StartDate = [DTTimePeriod dateWithAddedTime:size amount:amount/2 baseDate:self.StartDate];
self.EndDate = [DTTimePeriod dateWithSubtractedTime:size amount:amount/2 baseDate:self.EndDate];
break;
case DTTimePeriodAnchorEnd:
self.StartDate = [DTTimePeriod dateWithAddedTime:size amount:amount baseDate:self.StartDate];
break;
default:
break;
}
}
#pragma mark - Helper Methods
-(DTTimePeriod *)copy{
DTTimePeriod *period = [DTTimePeriod timePeriodWithStartDate:[NSDate dateWithTimeIntervalSince1970:self.StartDate.timeIntervalSince1970] endDate:[NSDate dateWithTimeIntervalSince1970:self.EndDate.timeIntervalSince1970]];
return period;
}
@end

49
iosApp/iosApp/Libs/DateTools/DTTimePeriodChain.h

@ -0,0 +1,49 @@
// Copyright (C) 2014 by Matthew York
//
// Permission is hereby granted, free of charge, to any
// person obtaining a copy of this software and
// associated documentation files (the "Software"), to
// deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the
// Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall
// be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
#import "DTTimePeriodGroup.h"
@interface DTTimePeriodChain : DTTimePeriodGroup {
DTTimePeriod *First;
DTTimePeriod *Last;
}
@property (nonatomic, readonly) DTTimePeriod *First;
@property (nonatomic, readonly) DTTimePeriod *Last;
#pragma mark - Custom Init / Factory Chain
+(DTTimePeriodChain *)chain;
#pragma mark - Chain Existence Manipulation
-(void)addTimePeriod:(DTTimePeriod *)period;
-(void)insertTimePeriod:(DTTimePeriod *)period atInedx:(NSInteger)index;
-(void)removeTimePeriodAtIndex:(NSInteger)index;
-(void)removeLatestTimePeriod;
-(void)removeEarliestTimePeriod;
#pragma mark - Chain Relationship
-(BOOL)isEqualToChain:(DTTimePeriodChain *)chain;
#pragma mark - Updates
-(void)updateVariables;
@end

218
iosApp/iosApp/Libs/DateTools/DTTimePeriodChain.m

@ -0,0 +1,218 @@
// Copyright (C) 2014 by Matthew York
//
// Permission is hereby granted, free of charge, to any
// person obtaining a copy of this software and
// associated documentation files (the "Software"), to
// deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the
// Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall
// be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "DTTimePeriodChain.h"
#import "DTError.h"
@interface DTTimePeriodChain ()
@end
@implementation DTTimePeriodChain
#pragma mark - Custom Init / Factory Chain
+(DTTimePeriodChain *)chain{
return [[DTTimePeriodChain alloc] init];
}
#pragma mark - Chain Existence Manipulation
-(void)addTimePeriod:(DTTimePeriod *)period{
if ([period class] != [DTTimePeriod class]) {
[DTError throwBadTypeException:period expectedClass:[DTTimePeriod class]];
return;
}
if (periods) {
if (periods.count > 0) {
//Create a modified period to be added based on size of passed in period
DTTimePeriod *modifiedPeriod = [DTTimePeriod timePeriodWithSize:DTTimePeriodSizeSecond amount:period.durationInSeconds startingAt:[periods[periods.count - 1] EndDate]];
//Add object to periods array
[periods addObject:modifiedPeriod];
}
else {
//Add object to periods array
[periods addObject:period];
}
}
else {
//Create new periods array
periods = [NSMutableArray array];
//Add object to periods array
[periods addObject:period];
}
//Set object's variables with updated array values
[self updateVariables];
}
-(void)insertTimePeriod:(DTTimePeriod *)period atInedx:(NSInteger)index{
if ([period class] != [DTTimePeriod class]) {
[DTError throwBadTypeException:period expectedClass:[DTTimePeriod class]];
return;
}
//Make sure the index is within the operable bounds of the periods array
if (index == 0) {
//Update bounds of period to make it fit in chain
DTTimePeriod *modifiedPeriod = [DTTimePeriod timePeriodWithSize:DTTimePeriodSizeSecond amount:period.durationInSeconds endingAt:[periods[0] EndDate]];
//Insert the updated object at the beginning of the periods array
[periods insertObject:modifiedPeriod atIndex:0];
}
else if (index > 0 && index < periods.count) {
//Shift time periods later if they fall after new period
[periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
//Shift later
if (idx >= index) {
[((DTTimePeriod *) obj) shiftLaterWithSize:DTTimePeriodSizeSecond amount:period.durationInSeconds];
}
}];
//Update bounds of period to make it fit in chain
DTTimePeriod *modifiedPeriod = [DTTimePeriod timePeriodWithSize:DTTimePeriodSizeSecond amount:period.durationInSeconds startingAt:[periods[index - 1] EndDate]];
//Insert the updated object at the beginning of the periods array
[periods insertObject:modifiedPeriod atIndex:index];
//Set object's variables with updated array values
[self updateVariables];
}
else {
[DTError throwInsertOutOfBoundsException:index array:periods];
}
}
-(void)removeTimePeriodAtIndex:(NSInteger)index{
//Make sure the index is within the operable bounds of the periods array
if (index >= 0 && index < periods.count) {
DTTimePeriod *period = periods[index];
//Shift time periods later if they fall after new period
[periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
//Shift earlier
if (idx > index) {
[((DTTimePeriod *) obj) shiftEarlierWithSize:DTTimePeriodSizeSecond amount:period.durationInSeconds];
}
}];
//Remove object
[periods removeObjectAtIndex:index];
//Set object's variables with updated array values
[self updateVariables];
}
else {
[DTError throwRemoveOutOfBoundsException:index array:periods];
}
}
-(void)removeLatestTimePeriod{
if (periods.count > 0) {
[periods removeLastObject];
//Update the object variables
if (periods.count > 0) {
//Set object's variables with updated array values
[self updateVariables];
}
else {
[self setVariablesNil];
}
}
}
-(void)removeEarliestTimePeriod{
if (periods > 0) {
//Shift time periods earlier
[periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
//Shift earlier to account for removal of first element in periods array
[((DTTimePeriod *) obj) shiftEarlierWithSize:DTTimePeriodSizeSecond amount:[periods[0] durationInSeconds]];
}];
//Remove first period
[periods removeObjectAtIndex:0];
//Update the object variables
if (periods.count > 0) {
//Set object's variables with updated array values
[self updateVariables];
}
else {
[self setVariablesNil];
}
}
}
#pragma mark - Chain Relationship
-(BOOL)isEqualToChain:(DTTimePeriodChain *)chain{
//Check class
if ([chain class] != [DTTimePeriodChain class]) {
[DTError throwBadTypeException:chain expectedClass:[DTTimePeriodChain class]];
return NO;
}
//Check group level characteristics for speed
if (![self hasSameCharacteristicsAs:chain]) {
return NO;
}
//Check whole chain
__block BOOL isEqual = YES;
[periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if (![chain[idx] isEqualToPeriod:obj]) {
isEqual = NO;
*stop = YES;
}
}];
return isEqual;
}
#pragma mark - Getters
-(DTTimePeriod *)First{
return First;
}
-(DTTimePeriod *)Last{
return Last;
}
#pragma mark - Helper Methods
-(void)updateVariables{
//Set helper variables
StartDate = [periods[0] StartDate];
EndDate = [periods[periods.count - 1] EndDate];
First = periods[0];
Last = periods[periods.count -1];
}
-(void)setVariablesNil{
//Set helper variables
StartDate = nil;
EndDate = nil;
First = nil;
Last = nil;
}
@end

56
iosApp/iosApp/Libs/DateTools/DTTimePeriodCollection.h

@ -0,0 +1,56 @@
// Copyright (C) 2014 by Matthew York
//
// Permission is hereby granted, free of charge, to any
// person obtaining a copy of this software and
// associated documentation files (the "Software"), to
// deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the
// Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall
// be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
#import "DTTimePeriodGroup.h"
@interface DTTimePeriodCollection : DTTimePeriodGroup
#pragma mark - Custom Init / Factory Methods
+(DTTimePeriodCollection *)collection;
#pragma mark - Collection Manipulation
-(void)addTimePeriod:(DTTimePeriod *)period;
-(void)insertTimePeriod:(DTTimePeriod *)period atIndex:(NSInteger)index;
-(void)removeTimePeriodAtIndex:(NSInteger)index;
#pragma mark - Sorting
-(void)sortByStartAscending;
-(void)sortByStartDescending;
-(void)sortByEndAscending;
-(void)sortByEndDescending;
-(void)sortByDurationAscending;
-(void)sortByDurationDescending;
#pragma mark - Collection Relationship
-(DTTimePeriodCollection *)periodsInside:(DTTimePeriod *)period;
-(DTTimePeriodCollection *)periodsIntersectedByDate:(NSDate *)date;
-(DTTimePeriodCollection *)periodsIntersectedByPeriod:(DTTimePeriod *)period;
-(DTTimePeriodCollection *)periodsOverlappedByPeriod:(DTTimePeriod *)period;
-(BOOL)isEqualToCollection:(DTTimePeriodCollection *)collection considerOrder:(BOOL)considerOrder;
#pragma mark - Helper Methods
-(DTTimePeriodCollection *)copy;
#pragma mark - Updates
-(void)updateVariables;
@end

370
iosApp/iosApp/Libs/DateTools/DTTimePeriodCollection.m

@ -0,0 +1,370 @@
// Copyright (C) 2014 by Matthew York
//
// Permission is hereby granted, free of charge, to any
// person obtaining a copy of this software and
// associated documentation files (the "Software"), to
// deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the
// Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall
// be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "DTTimePeriodCollection.h"
#import "DTError.h"
#import "NSDate+DateTools.h"
@implementation DTTimePeriodCollection
#pragma mark - Custom Init / Factory Methods
/**
* Initializes a new instance of DTTimePeriodCollection
*
* @return DTTimePeriodCollection
*/
+(DTTimePeriodCollection *)collection{
return [[DTTimePeriodCollection alloc] init];
}
#pragma mark - Collection Manipulation
/**
* Adds a time period to the reciever.
*
* @param period DTTimePeriod - The time period to add to the collection
*/
-(void)addTimePeriod:(DTTimePeriod *)period{
if ([period isKindOfClass:[DTTimePeriod class]]) {
[periods addObject:period];
//Set object's variables with updated array values
[self updateVariables];
}
else {
[DTError throwBadTypeException:period expectedClass:[DTTimePeriod class]];
}
}
/**
* Inserts a time period to the receiver at a given index.
*
* @param period DTTimePeriod - The time period to insert into the collection
* @param index NSInteger - The index in the collection the time period is to be added at
*/
-(void)insertTimePeriod:(DTTimePeriod *)period atIndex:(NSInteger)index{
if ([period class] != [DTTimePeriod class]) {
[DTError throwBadTypeException:period expectedClass:[DTTimePeriod class]];
return;
}
if (index >= 0 && index < periods.count) {
[periods insertObject:period atIndex:index];
//Set object's variables with updated array values
[self updateVariables];
}
else {
[DTError throwInsertOutOfBoundsException:index array:periods];
}
}
/**
* Removes the time period at a given index from the collection
*
* @param index NSInteger - The index in the collection the time period is to be removed from
*/
-(void)removeTimePeriodAtIndex:(NSInteger)index{
if (index >= 0 && index < periods.count) {
[periods removeObjectAtIndex:index];
//Update the object variables
if (periods.count > 0) {
//Set object's variables with updated array values
[self updateVariables];
}
else {
[self setVariablesNil];
}
}
else {
[DTError throwRemoveOutOfBoundsException:index array:periods];
}
}
#pragma mark - Sorting
/**
* Sorts the time periods in the collection by earliest start date to latest start date.
*/
-(void)sortByStartAscending{
[periods sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
return [((DTTimePeriod *) obj1).StartDate compare:((DTTimePeriod *) obj2).StartDate];
}];
}
/**
* Sorts the time periods in the collection by latest start date to earliest start date.
*/
-(void)sortByStartDescending{
[periods sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
return [((DTTimePeriod *) obj2).StartDate compare:((DTTimePeriod *) obj1).StartDate];
}];
}
/**
* Sorts the time periods in the collection by earliest end date to latest end date.
*/
-(void)sortByEndAscending{
[periods sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
return [((DTTimePeriod *) obj1).EndDate compare:((DTTimePeriod *) obj2).EndDate];
}];
}
/**
* Sorts the time periods in the collection by latest end date to earliest end date.
*/
-(void)sortByEndDescending{
[periods sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
return [((DTTimePeriod *) obj2).EndDate compare:((DTTimePeriod *) obj1).EndDate];
}];
}
/**
* Sorts the time periods in the collection by how much time they span. Sorts smallest durations to longest.
*/
-(void)sortByDurationAscending{
[periods sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
if (((DTTimePeriod *) obj1).durationInSeconds < ((DTTimePeriod *) obj2).durationInSeconds) {
return NSOrderedAscending;
}
else {
return NSOrderedDescending;
}
return NSOrderedSame;
}];
}
/**
* Sorts the time periods in the collection by how much time they span. Sorts longest durations to smallest.
*/
-(void)sortByDurationDescending{
[periods sortUsingComparator:^NSComparisonResult(id obj1, id obj2) {
if (((DTTimePeriod *) obj1).durationInSeconds > ((DTTimePeriod *) obj2).durationInSeconds) {
return NSOrderedAscending;
}
else {
return NSOrderedDescending;
}
return NSOrderedSame;
}];
}
#pragma mark - Collection Relationship
/**
* Returns an instance of DTTimePeriodCollection with all the time periods in the receiver that fall inside a given time period.
* Time periods of the receiver must have a start date and end date within the closed interval of the period provided to be included.
*
* @param period DTTimePeriod - The time period to check against the receiver's time periods.
*
* @return DTTimePeriodCollection
*/
-(DTTimePeriodCollection *)periodsInside:(DTTimePeriod *)period{
DTTimePeriodCollection *collection = [[DTTimePeriodCollection alloc] init];
if ([period isKindOfClass:[DTTimePeriod class]]) {
[periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([((DTTimePeriod *) obj) isInside:period]) {
[collection addTimePeriod:obj];
}
}];
}
else {
[DTError throwBadTypeException:period expectedClass:[DTTimePeriod class]];
}
return collection;
}
/**
* Returns an instance of DTTimePeriodCollection with all the time periods in the receiver that intersect a given date.
* Time periods of the receiver must have a start date earlier than or equal to the comparison date and an end date later than or equal to the comparison date to be included
*
* @param date NSDate - The date to check against the receiver's time periods
*
* @return DTTimePeriodCollection
*/
-(DTTimePeriodCollection *)periodsIntersectedByDate:(NSDate *)date{
DTTimePeriodCollection *collection = [[DTTimePeriodCollection alloc] init];
if ([date isKindOfClass:[NSDate class]]) {
[periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([((DTTimePeriod *) obj) containsDate:date interval:DTTimePeriodIntervalClosed]) {
[collection addTimePeriod:obj];
}
}];
}
else {
[DTError throwBadTypeException:date expectedClass:[NSDate class]];
}
return collection;
}
/**
* Returns an instance of DTTimePeriodCollection with all the time periods in the receiver that intersect a given time period.
* Intersection with the given time period includes other time periods that simply touch it. (i.e. one's start date is equal to another's end date)
*
* @param period DTTimePeriod - The time period to check against the receiver's time periods.
*
* @return DTTimePeriodCollection
*/
-(DTTimePeriodCollection *)periodsIntersectedByPeriod:(DTTimePeriod *)period{
DTTimePeriodCollection *collection = [[DTTimePeriodCollection alloc] init];
if ([period isKindOfClass:[DTTimePeriod class]]) {
[periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([((DTTimePeriod *) obj) intersects:period]) {
[collection addTimePeriod:obj];
}
}];
}
else {
[DTError throwBadTypeException:period expectedClass:[DTTimePeriod class]];
}
return collection;
}
/**
* Returns an instance of DTTimePeriodCollection with all the time periods in the receiver that overlap a given time period.
* Overlap with the given time period does NOT include other time periods that simply touch it. (i.e. one's start date is equal to another's end date)
*
* @param period DTTimePeriod - The time period to check against the receiver's time periods.
*
* @return DTTimePeriodCollection
*/
-(DTTimePeriodCollection *)periodsOverlappedByPeriod:(DTTimePeriod *)period{
DTTimePeriodCollection *collection = [[DTTimePeriodCollection alloc] init];
[periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([((DTTimePeriod *) obj) overlapsWith:period]) {
[collection addTimePeriod:obj];
}
}];
return collection;
}
/**
* Returns a BOOL representing whether the receiver is equal to a given DTTimePeriodCollection. Equality requires the start and end dates to be the same, and all time periods to be the same.
*
* If you would like to take the order of the time periods in two collections into consideration, you may do so with the considerOrder BOOL
*
* @param collection DTTimePeriodCollection - The collection to compare with the receiver
* @param considerOrder BOOL - Option for whether to account for the time periods order in the test for equality. YES considers order, NO does not.
*
* @return BOOL
*/
-(BOOL)isEqualToCollection:(DTTimePeriodCollection *)collection considerOrder:(BOOL)considerOrder{
//Check class
if ([collection class] != [DTTimePeriodCollection class]) {
[DTError throwBadTypeException:collection expectedClass:[DTTimePeriodCollection class]];
return NO;
}
//Check group level characteristics for speed
if (![self hasSameCharacteristicsAs:collection]) {
return NO;
}
//Default to equality and look for inequality
__block BOOL isEqual = YES;
if (considerOrder) {
[periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if (![collection[idx] isEqualToPeriod:obj]) {
isEqual = NO;
*stop = YES;
}
}];
}
else {
__block DTTimePeriodCollection *collectionCopy = [collection copy];
[periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
__block BOOL innerMatch = NO;
__block NSInteger matchIndex = 0; //We will remove matches to account for duplicates and to help speed
for (int ii = 0; ii < collectionCopy.count; ii++) {
if ([obj isEqualToPeriod:collectionCopy[ii]]) {
innerMatch = YES;
matchIndex = ii;
break;
}
}
//If there was a match found, stop
if (!innerMatch) {
isEqual = NO;
*stop = YES;
}
else {
[collectionCopy removeTimePeriodAtIndex:matchIndex];
}
}];
}
return isEqual;
}
#pragma mark - Helper Methods
-(void)updateVariables{
//Set helper variables
__block NSDate *startDate = [NSDate distantFuture];
__block NSDate *endDate = [NSDate distantPast];
[periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
if ([((DTTimePeriod *) obj).StartDate isEarlierThan:startDate]) {
startDate = ((DTTimePeriod *) obj).StartDate;
}
if ([((DTTimePeriod *) obj).EndDate isLaterThan:endDate]) {
endDate = ((DTTimePeriod *) obj).EndDate;
}
}];
//Make assignments after evaluation
StartDate = startDate;
EndDate = endDate;
}
-(void)setVariablesNil{
//Set helper variables
StartDate = nil;
EndDate = nil;
}
/**
* Returns a new instance of DTTimePeriodCollection that is an exact copy of the receiver, but with differnt memory references, etc.
*
* @return DTTimePeriodCollection
*/
-(DTTimePeriodCollection *)copy{
DTTimePeriodCollection *collection = [DTTimePeriodCollection collection];
[periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[collection addTimePeriod:[obj copy]];
}];
return collection;
}
@end

62
iosApp/iosApp/Libs/DateTools/DTTimePeriodGroup.h

@ -0,0 +1,62 @@
// Copyright (C) 2014 by Matthew York
//
// Permission is hereby granted, free of charge, to any
// person obtaining a copy of this software and
// associated documentation files (the "Software"), to
// deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the
// Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall
// be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import <Foundation/Foundation.h>
#import "DTTimePeriod.h"
@interface DTTimePeriodGroup : NSObject {
@protected
NSMutableArray *periods;
NSDate *StartDate;
NSDate *EndDate;
}
@property (nonatomic, readonly) NSDate *StartDate;
@property (nonatomic, readonly) NSDate *EndDate;
//Here we will use object subscripting to help create the illusion of an array
- (id)objectAtIndexedSubscript:(NSUInteger)index; //getter
- (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)index; //setter
#pragma mark - Group Info
-(double)durationInYears;
-(double)durationInWeeks;
-(double)durationInDays;
-(double)durationInHours;
-(double)durationInMinutes;
-(double)durationInSeconds;
-(NSDate *)StartDate;
-(NSDate *)EndDate;
-(NSInteger)count;
#pragma mark - Chain Time Manipulation
-(void)shiftEarlierWithSize:(DTTimePeriodSize)size;
-(void)shiftEarlierWithSize:(DTTimePeriodSize)size amount:(NSInteger)amount;
-(void)shiftLaterWithSize:(DTTimePeriodSize)size;
-(void)shiftLaterWithSize:(DTTimePeriodSize)size amount:(NSInteger)amount;
#pragma mark - Comparison
-(BOOL)hasSameCharacteristicsAs:(DTTimePeriodGroup *)group;
#pragma mark - Updates
-(void)updateVariables;
@end

234
iosApp/iosApp/Libs/DateTools/DTTimePeriodGroup.m

@ -0,0 +1,234 @@
// Copyright (C) 2014 by Matthew York
//
// Permission is hereby granted, free of charge, to any
// person obtaining a copy of this software and
// associated documentation files (the "Software"), to
// deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the
// Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall
// be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "DTTimePeriodGroup.h"
#import "NSDate+DateTools.h"
@interface DTTimePeriodGroup ()
@end
@implementation DTTimePeriodGroup
-(id) init
{
if (self = [super init]) {
periods = [[NSMutableArray alloc] init];
}
return self;
}
- (id)objectAtIndexedSubscript:(NSUInteger)index
{
return periods[index];
}
- (void)setObject:(id)obj atIndexedSubscript:(NSUInteger)index {
periods[index] = obj;
}
#pragma mark - Group Info
/**
* Returns the duration of the receiver in years
*
* @return NSInteger
*/
-(double)durationInYears {
if (self.StartDate && self.EndDate) {
return [self.StartDate yearsEarlierThan:self.EndDate];
}
return 0;
}
/**
* Returns the duration of the receiver in weeks
*
* @return double
*/
-(double)durationInWeeks {
if (self.StartDate && self.EndDate) {
return [self.StartDate weeksEarlierThan:self.EndDate];
}
return 0;
}
/**
* Returns the duration of the receiver in days
*
* @return double
*/
-(double)durationInDays {
if (self.StartDate && self.EndDate) {
return [self.StartDate daysEarlierThan:self.EndDate];
}
return 0;
}
/**
* Returns the duration of the receiver in hours
*
* @return double
*/
-(double)durationInHours {
if (self.StartDate && self.EndDate) {
return [self.StartDate hoursEarlierThan:self.EndDate];
}
return 0;
}
/**
* Returns the duration of the receiver in minutes
*
* @return double
*/
-(double)durationInMinutes {
if (self.StartDate && self.EndDate) {
return [self.StartDate minutesEarlierThan:self.EndDate];
}
return 0;
}
/**
* Returns the duration of the receiver in seconds
*
* @return double
*/
-(double)durationInSeconds {
if (self.StartDate && self.EndDate) {
return [self.StartDate secondsEarlierThan:self.EndDate];
}
return 0;
}
/**
* Returns the NSDate representing the earliest date in the DTTimePeriodGroup (or subclass)
*
* @return NSDate
*/
-(NSDate *)StartDate{
return StartDate;
}
/**
* Returns the NSDate representing the latest date in the DTTimePeriodGroup (or subclass)
*
* @return NSDate
*/
-(NSDate *)EndDate{
return EndDate;
}
/**
* The total number of DTTimePeriods in the group
*
* @return NSInteger
*/
-(NSInteger)count{
return periods.count;
}
/**
* Returns a BOOL if the receiver and the comparison group have the same metadata (i.e. number of periods, start & end date, etc.)
* Returns YES if they share the same characteristics, otherwise NO
*
* @param group The group to compare with the receiver
*
* @return BOOL
*/
-(BOOL)hasSameCharacteristicsAs:(DTTimePeriodGroup *)group{
//Check characteristics first for speed
if (group.count != self.count) {
return NO;
}
else if (!group.StartDate && !group.EndDate && !self.StartDate && !self.EndDate){
return YES;
}
else if (![group.StartDate isEqualToDate:self.StartDate] || ![group.EndDate isEqualToDate:self.EndDate]){
return NO;
}
return YES;
}
#pragma mark - Chain Time Manipulation
/**
* Shifts all the time periods in the collection to an earlier date by the given size
*
* @param size DTTimePeriodSize - The desired size of the shift
*/
-(void)shiftEarlierWithSize:(DTTimePeriodSize)size{
[self shiftEarlierWithSize:size amount:1];
}
/**
* Shifts all the time periods in the collection to an earlier date by the given size and amount.
* The amount acts as a multiplier to the size (i.e. "2 weeks" or "4 years")
*
* @param size DTTimePeriodSize - The desired size of the shift
* @param amount NSInteger - Multiplier for the size
*/
-(void)shiftEarlierWithSize:(DTTimePeriodSize)size amount:(NSInteger)amount{
if (periods) {
[periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[((DTTimePeriod *)obj) shiftEarlierWithSize:size amount:amount];
}];
[self updateVariables];
}
}
/**
* Shifts all the time periods in the collection to a later date by the given size
*
* @param size DTTimePeriodSize - The desired size of the shift
*/
-(void)shiftLaterWithSize:(DTTimePeriodSize)size{
[self shiftLaterWithSize:size amount:1];
}
/**
* Shifts all the time periods in the collection to an later date by the given size and amount.
* The amount acts as a multiplier to the size (i.e. "2 weeks" or "4 years")
*
* @param size DTTimePeriodSize - The desired size of the shift
* @param amount NSInteger - Multiplier for the size
*/
-(void)shiftLaterWithSize:(DTTimePeriodSize)size amount:(NSInteger)amount{
if (periods) {
[periods enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
[((DTTimePeriod *)obj) shiftLaterWithSize:size amount:amount];
}];
[self updateVariables];
}
}
#pragma mark - Updates
-(void)updateVariables{}
@end

29
iosApp/iosApp/Libs/DateTools/DateTools.h

@ -0,0 +1,29 @@
// Copyright (C) 2014 by Matthew York
//
// Permission is hereby granted, free of charge, to any
// person obtaining a copy of this software and
// associated documentation files (the "Software"), to
// deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the
// Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall
// be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#import "DTConstants.h"
#import "DTError.h"
#import "NSDate+DateTools.h"
#import "DTTimePeriod.h"
#import "DTTimePeriodGroup.h"
#import "DTTimePeriodCollection.h"
#import "DTTimePeriodChain.h"

194
iosApp/iosApp/Libs/DateTools/NSDate+DateTools.h

@ -0,0 +1,194 @@
// Copyright (C) 2014 by Matthew York
//
// 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.
#ifndef DateToolsLocalizedStrings
#ifdef SPM
#define DateToolsLocalizedStrings(key) \
NSLocalizedStringFromTableInBundle(key, @"DateTools", [NSBundle bundleWithPath:[[SWIFTPM_MODULE_BUNDLE resourcePath] stringByAppendingPathComponent:@"DateTools.bundle"]], nil)
#else
#define DateToolsLocalizedStrings(key) \
NSLocalizedStringFromTableInBundle(key, @"DateTools", [NSBundle bundleWithPath:[[[NSBundle bundleForClass:[DTError class]] resourcePath] stringByAppendingPathComponent:@"DateTools.bundle"]], nil)
#endif
#endif
#import <Foundation/Foundation.h>
#import "DTConstants.h"
@interface NSDate (DateTools)
#pragma mark - Time Ago
+ (NSString*)timeAgoSinceDate:(NSDate*)date;
+ (NSString*)shortTimeAgoSinceDate:(NSDate*)date;
+ (NSString *)weekTimeAgoSinceDate:(NSDate *)date;
- (NSString*)timeAgoSinceNow;
- (NSString *)shortTimeAgoSinceNow;
- (NSString *)weekTimeAgoSinceNow;
- (NSString *)timeAgoSinceDate:(NSDate *)date;
- (NSString *)timeAgoSinceDate:(NSDate *)date numericDates:(BOOL)useNumericDates;
- (NSString *)timeAgoSinceDate:(NSDate *)date numericDates:(BOOL)useNumericDates numericTimes:(BOOL)useNumericTimes;
- (NSString *)shortTimeAgoSinceDate:(NSDate *)date;
- (NSString *)weekTimeAgoSinceDate:(NSDate *)date;
#pragma mark - Date Components Without Calendar
- (NSInteger)era;
- (NSInteger)year;
- (NSInteger)month;
- (NSInteger)day;
- (NSInteger)hour;
- (NSInteger)minute;
- (NSInteger)second;
- (NSInteger)weekday;
- (NSInteger)weekdayOrdinal;
- (NSInteger)quarter;
- (NSInteger)weekOfMonth;
- (NSInteger)weekOfYear;
- (NSInteger)yearForWeekOfYear;
- (NSInteger)daysInMonth;
- (NSInteger)dayOfYear;
-(NSInteger)daysInYear;
-(BOOL)isInLeapYear;
- (BOOL)isToday;
- (BOOL)isTomorrow;
-(BOOL)isYesterday;
- (BOOL)isWeekend;
-(BOOL)isSameDay:(NSDate *)date;
+ (BOOL)isSameDay:(NSDate *)date asDate:(NSDate *)compareDate;
#pragma mark - Date Components With Calendar
- (NSInteger)eraWithCalendar:(NSCalendar *)calendar;
- (NSInteger)yearWithCalendar:(NSCalendar *)calendar;
- (NSInteger)monthWithCalendar:(NSCalendar *)calendar;
- (NSInteger)dayWithCalendar:(NSCalendar *)calendar;
- (NSInteger)hourWithCalendar:(NSCalendar *)calendar;
- (NSInteger)minuteWithCalendar:(NSCalendar *)calendar;
- (NSInteger)secondWithCalendar:(NSCalendar *)calendar;
- (NSInteger)weekdayWithCalendar:(NSCalendar *)calendar;
- (NSInteger)weekdayOrdinalWithCalendar:(NSCalendar *)calendar;
- (NSInteger)quarterWithCalendar:(NSCalendar *)calendar;
- (NSInteger)weekOfMonthWithCalendar:(NSCalendar *)calendar;
- (NSInteger)weekOfYearWithCalendar:(NSCalendar *)calendar;
- (NSInteger)yearForWeekOfYearWithCalendar:(NSCalendar *)calendar;
#pragma mark - Date Creating
+ (NSDate *)dateWithYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day;
+ (NSDate *)dateWithYear:(NSInteger)year month:(NSInteger)month day:(NSInteger)day hour:(NSInteger)hour minute:(NSInteger)minute second:(NSInteger)second;
+ (NSDate *)dateWithString:(NSString *)dateString formatString:(NSString *)formatString;
+ (NSDate *)dateWithString:(NSString *)dateString formatString:(NSString *)formatString timeZone:(NSTimeZone *)timeZone;
#pragma mark - Date Editing
#pragma mark Date By Adding
- (NSDate *)dateByAddingYears:(NSInteger)years;
- (NSDate *)dateByAddingMonths:(NSInteger)months;
- (NSDate *)dateByAddingWeeks:(NSInteger)weeks;
- (NSDate *)dateByAddingDays:(NSInteger)days;
- (NSDate *)dateByAddingHours:(NSInteger)hours;
- (NSDate *)dateByAddingMinutes:(NSInteger)minutes;
- (NSDate *)dateByAddingSeconds:(NSInteger)seconds;
#pragma mark Date By Subtracting
- (NSDate *)dateBySubtractingYears:(NSInteger)years;
- (NSDate *)dateBySubtractingMonths:(NSInteger)months;
- (NSDate *)dateBySubtractingWeeks:(NSInteger)weeks;
- (NSDate *)dateBySubtractingDays:(NSInteger)days;
- (NSDate *)dateBySubtractingHours:(NSInteger)hours;
- (NSDate *)dateBySubtractingMinutes:(NSInteger)minutes;
- (NSDate *)dateBySubtractingSeconds:(NSInteger)seconds;
#pragma mark - Date Comparison
#pragma mark Time From
-(NSInteger)yearsFrom:(NSDate *)date;
-(NSInteger)monthsFrom:(NSDate *)date;
-(NSInteger)weeksFrom:(NSDate *)date;
-(NSInteger)daysFrom:(NSDate *)date;
-(double)hoursFrom:(NSDate *)date;
-(double)minutesFrom:(NSDate *)date;
-(double)secondsFrom:(NSDate *)date;
#pragma mark Time From With Calendar
-(NSInteger)yearsFrom:(NSDate *)date calendar:(NSCalendar *)calendar;
-(NSInteger)monthsFrom:(NSDate *)date calendar:(NSCalendar *)calendar;
-(NSInteger)weeksFrom:(NSDate *)date calendar:(NSCalendar *)calendar;
-(NSInteger)daysFrom:(NSDate *)date calendar:(NSCalendar *)calendar;
#pragma mark Time Until
-(NSInteger)yearsUntil;
-(NSInteger)monthsUntil;
-(NSInteger)weeksUntil;
-(NSInteger)daysUntil;
-(double)hoursUntil;
-(double)minutesUntil;
-(double)secondsUntil;
#pragma mark Time Ago
-(NSInteger)yearsAgo;
-(NSInteger)monthsAgo;
-(NSInteger)weeksAgo;
-(NSInteger)daysAgo;
-(double)hoursAgo;
-(double)minutesAgo;
-(double)secondsAgo;
#pragma mark Earlier Than
-(NSInteger)yearsEarlierThan:(NSDate *)date;
-(NSInteger)monthsEarlierThan:(NSDate *)date;
-(NSInteger)weeksEarlierThan:(NSDate *)date;
-(NSInteger)daysEarlierThan:(NSDate *)date;
-(double)hoursEarlierThan:(NSDate *)date;
-(double)minutesEarlierThan:(NSDate *)date;
-(double)secondsEarlierThan:(NSDate *)date;
#pragma mark Later Than
-(NSInteger)yearsLaterThan:(NSDate *)date;
-(NSInteger)monthsLaterThan:(NSDate *)date;
-(NSInteger)weeksLaterThan:(NSDate *)date;
-(NSInteger)daysLaterThan:(NSDate *)date;
-(double)hoursLaterThan:(NSDate *)date;
-(double)minutesLaterThan:(NSDate *)date;
-(double)secondsLaterThan:(NSDate *)date;
#pragma mark Comparators
-(BOOL)isEarlierThan:(NSDate *)date;
-(BOOL)isLaterThan:(NSDate *)date;
-(BOOL)isEarlierThanOrEqualTo:(NSDate *)date;
-(BOOL)isLaterThanOrEqualTo:(NSDate *)date;
#pragma mark - Formatted Dates
#pragma mark Formatted With Style
-(NSString *)formattedDateWithStyle:(NSDateFormatterStyle)style;
-(NSString *)formattedDateWithStyle:(NSDateFormatterStyle)style timeZone:(NSTimeZone *)timeZone;
-(NSString *)formattedDateWithStyle:(NSDateFormatterStyle)style locale:(NSLocale *)locale;
-(NSString *)formattedDateWithStyle:(NSDateFormatterStyle)style timeZone:(NSTimeZone *)timeZone locale:(NSLocale *)locale;
#pragma mark Formatted With Format
-(NSString *)formattedDateWithFormat:(NSString *)format;
-(NSString *)formattedDateWithFormat:(NSString *)format timeZone:(NSTimeZone *)timeZone;
-(NSString *)formattedDateWithFormat:(NSString *)format locale:(NSLocale *)locale;
-(NSString *)formattedDateWithFormat:(NSString *)format timeZone:(NSTimeZone *)timeZone locale:(NSLocale *)locale;
#pragma mark - Helpers
+(NSString *)defaultCalendarIdentifier;
+ (void)setDefaultCalendarIdentifier:(NSString *)identifier;
@end

1743
iosApp/iosApp/Libs/DateTools/NSDate+DateTools.m

File diff suppressed because it is too large

27
iosApp/iosApp/Libs/MJExtension/MJExtension.h

@ -0,0 +1,27 @@
//
// MJExtension.h
// MJExtension
//
// Created by mj on 14-1-15.
// Copyright (c) 2014年 小码哥. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "NSObject+MJCoding.h"
#import "NSObject+MJProperty.h"
#import "NSObject+MJClass.h"
#import "NSObject+MJKeyValue.h"
#import "NSString+MJExtension.h"
#import "MJExtensionConst.h"
#import "MJFoundation.h"
//! Project version number for MJExtension.
FOUNDATION_EXPORT double MJExtensionVersionNumber;
//! Project version string for MJExtension.
FOUNDATION_EXPORT const unsigned char MJExtensionVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <MJExtension/PublicHeader.h>

111
iosApp/iosApp/Libs/MJExtension/MJExtensionConst.h

@ -0,0 +1,111 @@
#ifndef __MJExtensionConst__H__
#define __MJExtensionConst__H__
#import <Foundation/Foundation.h>
#ifndef MJ_LOCK
#define MJ_LOCK(lock) dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
#endif
#ifndef MJ_UNLOCK
#define MJ_UNLOCK(lock) dispatch_semaphore_signal(lock);
#endif
// 信号量
#define MJExtensionSemaphoreCreate \
extern dispatch_semaphore_t mje_signalSemaphore; \
extern dispatch_once_t mje_onceTokenSemaphore; \
dispatch_once(&mje_onceTokenSemaphore, ^{ \
mje_signalSemaphore = dispatch_semaphore_create(1); \
});
// 过期
#define MJExtensionDeprecated(instead) NS_DEPRECATED(2_0, 2_0, 2_0, 2_0, instead)
// 构建错误
#define MJExtensionBuildError(clazz, msg) \
NSError *error = [NSError errorWithDomain:msg code:250 userInfo:nil]; \
[clazz setMj_error:error];
// 日志输出
#ifdef DEBUG
#define MJExtensionLog(...) NSLog(__VA_ARGS__)
#else
#define MJExtensionLog(...)
#endif
/**
*
* @param condition
* @param returnValue
*/
#define MJExtensionAssertError(condition, returnValue, clazz, msg) \
[clazz setMj_error:nil]; \
if ((condition) == NO) { \
MJExtensionBuildError(clazz, msg); \
return returnValue;\
}
#define MJExtensionAssert2(condition, returnValue) \
if ((condition) == NO) return returnValue;
/**
*
* @param condition
*/
#define MJExtensionAssert(condition) MJExtensionAssert2(condition, )
/**
*
* @param param
* @param returnValue
*/
#define MJExtensionAssertParamNotNil2(param, returnValue) \
MJExtensionAssert2((param) != nil, returnValue)
/**
*
* @param param
*/
#define MJExtensionAssertParamNotNil(param) MJExtensionAssertParamNotNil2(param, )
/**
*
*/
#define MJLogAllIvars \
- (NSString *)description \
{ \
return [self mj_keyValues].description; \
}
#define MJExtensionLogAllProperties MJLogAllIvars
/** 仅在 Debugger 展示所有的属性 */
#define MJImplementDebugDescription \
- (NSString *)debugDescription \
{ \
return [self mj_keyValues].debugDescription; \
}
/**
*
*/
FOUNDATION_EXPORT NSString *const MJPropertyTypeInt;
FOUNDATION_EXPORT NSString *const MJPropertyTypeShort;
FOUNDATION_EXPORT NSString *const MJPropertyTypeFloat;
FOUNDATION_EXPORT NSString *const MJPropertyTypeDouble;
FOUNDATION_EXPORT NSString *const MJPropertyTypeLong;
FOUNDATION_EXPORT NSString *const MJPropertyTypeLongLong;
FOUNDATION_EXPORT NSString *const MJPropertyTypeChar;
FOUNDATION_EXPORT NSString *const MJPropertyTypeBOOL1;
FOUNDATION_EXPORT NSString *const MJPropertyTypeBOOL2;
FOUNDATION_EXPORT NSString *const MJPropertyTypePointer;
FOUNDATION_EXPORT NSString *const MJPropertyTypeIvar;
FOUNDATION_EXPORT NSString *const MJPropertyTypeMethod;
FOUNDATION_EXPORT NSString *const MJPropertyTypeBlock;
FOUNDATION_EXPORT NSString *const MJPropertyTypeClass;
FOUNDATION_EXPORT NSString *const MJPropertyTypeSEL;
FOUNDATION_EXPORT NSString *const MJPropertyTypeId;
#endif

27
iosApp/iosApp/Libs/MJExtension/MJExtensionConst.m

@ -0,0 +1,27 @@
#ifndef __MJExtensionConst__M__
#define __MJExtensionConst__M__
#import <Foundation/Foundation.h>
/**
* 成员变量类型(属性类型)
*/
NSString *const MJPropertyTypeInt = @"i";
NSString *const MJPropertyTypeShort = @"s";
NSString *const MJPropertyTypeFloat = @"f";
NSString *const MJPropertyTypeDouble = @"d";
NSString *const MJPropertyTypeLong = @"l";
NSString *const MJPropertyTypeLongLong = @"q";
NSString *const MJPropertyTypeChar = @"c";
NSString *const MJPropertyTypeBOOL1 = @"c";
NSString *const MJPropertyTypeBOOL2 = @"b";
NSString *const MJPropertyTypePointer = @"*";
NSString *const MJPropertyTypeIvar = @"^{objc_ivar=}";
NSString *const MJPropertyTypeMethod = @"^{objc_method=}";
NSString *const MJPropertyTypeBlock = @"@?";
NSString *const MJPropertyTypeClass = @"#";
NSString *const MJPropertyTypeSEL = @":";
NSString *const MJPropertyTypeId = @"@";
#endif

16
iosApp/iosApp/Libs/MJExtension/MJFoundation.h

@ -0,0 +1,16 @@
//
// MJFoundation.h
// MJExtensionExample
//
// Created by MJ Lee on 14/7/16.
// Copyright (c) 2014年 小码哥. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface MJFoundation : NSObject
+ (BOOL)isClassFromFoundation:(Class)c;
+ (BOOL)isFromNSObjectProtocolProperty:(NSString *)propertyName;
@end

70
iosApp/iosApp/Libs/MJExtension/MJFoundation.m

@ -0,0 +1,70 @@
//
// MJFoundation.m
// MJExtensionExample
//
// Created by MJ Lee on 14/7/16.
// Copyright (c) 2014年 小码哥. All rights reserved.
//
#import "MJFoundation.h"
#import "MJExtensionConst.h"
#import <CoreData/CoreData.h>
#import "objc/runtime.h"
@implementation MJFoundation
+ (BOOL)isClassFromFoundation:(Class)c
{
if (c == [NSObject class] || c == [NSManagedObject class]) return YES;
static NSSet *foundationClasses;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 集合中没有NSObject,因为几乎所有的类都是继承自NSObject,具体是不是NSObject需要特殊判断
foundationClasses = [NSSet setWithObjects:
[NSURL class],
[NSDate class],
[NSValue class],
[NSData class],
[NSError class],
[NSArray class],
[NSDictionary class],
[NSString class],
[NSAttributedString class], nil];
});
__block BOOL result = NO;
[foundationClasses enumerateObjectsUsingBlock:^(Class foundationClass, BOOL *stop) {
if ([c isSubclassOfClass:foundationClass]) {
result = YES;
*stop = YES;
}
}];
return result;
}
+ (BOOL)isFromNSObjectProtocolProperty:(NSString *)propertyName
{
if (!propertyName) return NO;
static NSSet<NSString *> *objectProtocolPropertyNames;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
unsigned int count = 0;
objc_property_t *propertyList = protocol_copyPropertyList(@protocol(NSObject), &count);
NSMutableSet *propertyNames = [NSMutableSet setWithCapacity:count];
for (int i = 0; i < count; i++) {
objc_property_t property = propertyList[i];
NSString *propertyName = [NSString stringWithCString:property_getName(property) encoding:NSUTF8StringEncoding];
if (propertyName) {
[propertyNames addObject:propertyName];
}
}
objectProtocolPropertyNames = [propertyNames copy];
free(propertyList);
});
return [objectProtocolPropertyNames containsObject:propertyName];
}
@end

53
iosApp/iosApp/Libs/MJExtension/MJProperty.h

@ -0,0 +1,53 @@
//
// MJProperty.h
// MJExtensionExample
//
// Created by MJ Lee on 15/4/17.
// Copyright (c) 2015年 小码哥. All rights reserved.
// 包装一个成员属性
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import "MJPropertyType.h"
#import "MJPropertyKey.h"
/**
*
*/
@interface MJProperty : NSObject
/** 成员属性 */
@property (nonatomic, assign) objc_property_t property;
/** 成员属性的名字 */
@property (nonatomic, readonly) NSString *name;
/** 成员属性的类型 */
@property (nonatomic, readonly) MJPropertyType *type;
/** 成员属性来源于哪个类(可能是父类) */
@property (nonatomic, assign) Class srcClass;
/**** 同一个成员属性 - 父类和子类的行为可能不一致(originKey、propertyKeys、objectClassInArray) ****/
/** 设置最原始的key */
- (void)setOriginKey:(id)originKey forClass:(Class)c;
/** 对应着字典中的多级key(里面存放的数组,数组里面都是MJPropertyKey对象) */
- (NSArray *)propertyKeysForClass:(Class)c;
/** 模型数组中的模型类型 */
- (void)setObjectClassInArray:(Class)objectClass forClass:(Class)c;
- (Class)objectClassInArrayForClass:(Class)c;
/**** 同一个成员变量 - 父类和子类的行为可能不一致(key、keys、objectClassInArray) ****/
/**
* object的成员变量值
*/
- (void)setValue:(id)value forObject:(id)object;
/**
* object的成员属性值
*/
- (id)valueForObject:(id)object;
/**
*
*/
+ (instancetype)cachedPropertyWithProperty:(objc_property_t)property;
@end

211
iosApp/iosApp/Libs/MJExtension/MJProperty.m

@ -0,0 +1,211 @@
//
// MJProperty.m
// MJExtensionExample
//
// Created by MJ Lee on 15/4/17.
// Copyright (c) 2015年 小码哥. All rights reserved.
//
#import "MJProperty.h"
#import "MJFoundation.h"
#import "MJExtensionConst.h"
#import <objc/message.h>
#include "TargetConditionals.h"
@interface MJProperty()
@property (strong, nonatomic) NSMutableDictionary *propertyKeysDict;
@property (strong, nonatomic) NSMutableDictionary *objectClassInArrayDict;
@property (strong, nonatomic) dispatch_semaphore_t propertyKeysLock;
@property (strong, nonatomic) dispatch_semaphore_t objectClassInArrayLock;
@end
@implementation MJProperty
#pragma mark - 初始化
- (instancetype)init
{
if (self = [super init]) {
_propertyKeysDict = [NSMutableDictionary dictionary];
_objectClassInArrayDict = [NSMutableDictionary dictionary];
_propertyKeysLock = dispatch_semaphore_create(1);
_objectClassInArrayLock = dispatch_semaphore_create(1);
}
return self;
}
#pragma mark - 缓存
+ (instancetype)cachedPropertyWithProperty:(objc_property_t)property
{
MJProperty *propertyObj = objc_getAssociatedObject(self, property);
if (propertyObj == nil) {
propertyObj = [[self alloc] init];
propertyObj.property = property;
objc_setAssociatedObject(self, property, propertyObj, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return propertyObj;
}
#pragma mark - 公共方法
- (void)setProperty:(objc_property_t)property
{
_property = property;
MJExtensionAssertParamNotNil(property);
// 1.属性名
_name = @(property_getName(property));
// 2.成员类型
NSString *attrs = @(property_getAttributes(property));
NSUInteger dotLoc = [attrs rangeOfString:@","].location;
NSString *code = nil;
NSUInteger loc = 1;
if (dotLoc == NSNotFound) { // 没有,
code = [attrs substringFromIndex:loc];
} else {
code = [attrs substringWithRange:NSMakeRange(loc, dotLoc - loc)];
}
_type = [MJPropertyType cachedTypeWithCode:code];
}
/**
* 获得成员变量的值
*/
- (id)valueForObject:(id)object
{
if (self.type.KVCDisabled) return [NSNull null];
id value = [object valueForKey:self.name];
// 32BOOL类型转换json后成Int类型
/** https://github.com/CoderMJLee/MJExtension/issues/545 */
// 32 bit device OR 32 bit Simulator
#if defined(__arm__) || (TARGET_OS_SIMULATOR && !__LP64__)
if (self.type.isBoolType) {
value = @([(NSNumber *)value boolValue]);
}
#endif
return value;
}
/**
* 设置成员变量的值
*/
- (void)setValue:(id)value forObject:(id)object
{
if (self.type.KVCDisabled || value == nil) return;
[object setValue:value forKey:self.name];
}
/**
* 通过字符串key创建对应的keys
*/
- (NSArray *)propertyKeysWithStringKey:(NSString *)stringKey
{
if (stringKey.length == 0) return nil;
NSMutableArray *propertyKeys = [NSMutableArray array];
// 如果有多级映射
NSArray *oldKeys = [stringKey componentsSeparatedByString:@"."];
for (NSString *oldKey in oldKeys) {
NSUInteger start = [oldKey rangeOfString:@"["].location;
if (start != NSNotFound) { // 有索引的key
NSString *prefixKey = [oldKey substringToIndex:start];
NSString *indexKey = prefixKey;
if (prefixKey.length) {
MJPropertyKey *propertyKey = [[MJPropertyKey alloc] init];
propertyKey.name = prefixKey;
[propertyKeys addObject:propertyKey];
indexKey = [oldKey stringByReplacingOccurrencesOfString:prefixKey withString:@""];
}
/** 解析索引 **/
// 元素
NSArray *cmps = [[indexKey stringByReplacingOccurrencesOfString:@"[" withString:@""] componentsSeparatedByString:@"]"];
for (NSInteger i = 0; i<cmps.count - 1; i++) {
MJPropertyKey *subPropertyKey = [[MJPropertyKey alloc] init];
subPropertyKey.type = MJPropertyKeyTypeArray;
subPropertyKey.name = cmps[i];
[propertyKeys addObject:subPropertyKey];
}
} else { // 没有索引的key
MJPropertyKey *propertyKey = [[MJPropertyKey alloc] init];
propertyKey.name = oldKey;
[propertyKeys addObject:propertyKey];
}
}
return propertyKeys;
}
/** 对应着字典中的key */
- (void)setOriginKey:(id)originKey forClass:(Class)c
{
if ([originKey isKindOfClass:[NSString class]]) { // 字符串类型的key
NSArray *propertyKeys = [self propertyKeysWithStringKey:originKey];
if (propertyKeys.count) {
[self setPropertyKeys:@[propertyKeys] forClass:c];
}
} else if ([originKey isKindOfClass:[NSArray class]]) {
NSMutableArray *keyses = [NSMutableArray array];
for (NSString *stringKey in originKey) {
NSArray *propertyKeys = [self propertyKeysWithStringKey:stringKey];
if (propertyKeys.count) {
[keyses addObject:propertyKeys];
}
}
if (keyses.count) {
[self setPropertyKeys:keyses forClass:c];
}
}
}
/** 对应着字典中的多级key */
- (void)setPropertyKeys:(NSArray *)propertyKeys forClass:(Class)c
{
if (propertyKeys.count == 0) return;
NSString *key = NSStringFromClass(c);
if (!key) return;
MJ_LOCK(self.propertyKeysLock);
self.propertyKeysDict[key] = propertyKeys;
MJ_UNLOCK(self.propertyKeysLock);
}
- (NSArray *)propertyKeysForClass:(Class)c
{
NSString *key = NSStringFromClass(c);
if (!key) return nil;
MJ_LOCK(self.propertyKeysLock);
NSArray *propertyKeys = self.propertyKeysDict[key];
MJ_UNLOCK(self.propertyKeysLock);
return propertyKeys;
}
/** 模型数组中的模型类型 */
- (void)setObjectClassInArray:(Class)objectClass forClass:(Class)c
{
if (!objectClass) return;
NSString *key = NSStringFromClass(c);
if (!key) return;
MJ_LOCK(self.objectClassInArrayLock);
self.objectClassInArrayDict[key] = objectClass;
MJ_UNLOCK(self.objectClassInArrayLock);
}
- (Class)objectClassInArrayForClass:(Class)c
{
NSString *key = NSStringFromClass(c);
if (!key) return nil;
MJ_LOCK(self.objectClassInArrayLock);
Class objectClass = self.objectClassInArrayDict[key];
MJ_UNLOCK(self.objectClassInArrayLock);
return objectClass;
}
@end

30
iosApp/iosApp/Libs/MJExtension/MJPropertyKey.h

@ -0,0 +1,30 @@
//
// MJPropertyKey.h
// MJExtensionExample
//
// Created by MJ Lee on 15/8/11.
// Copyright (c) 2015年 小码哥. All rights reserved.
//
#import <Foundation/Foundation.h>
typedef enum {
MJPropertyKeyTypeDictionary = 0, // 字典的key
MJPropertyKeyTypeArray // 数组的key
} MJPropertyKeyType;
/**
* key
*/
@interface MJPropertyKey : NSObject
/** key的名字 */
@property (copy, nonatomic) NSString *name;
/** key的种类,可能是@"10",可能是@"age" */
@property (assign, nonatomic) MJPropertyKeyType type;
/**
* keynameobject
*/
- (id)valueInObject:(id)object;
@end

25
iosApp/iosApp/Libs/MJExtension/MJPropertyKey.m

@ -0,0 +1,25 @@
//
// MJPropertyKey.m
// MJExtensionExample
//
// Created by MJ Lee on 15/8/11.
// Copyright (c) 2015年 小码哥. All rights reserved.
//
#import "MJPropertyKey.h"
@implementation MJPropertyKey
- (id)valueInObject:(id)object
{
if ([object isKindOfClass:[NSDictionary class]] && self.type == MJPropertyKeyTypeDictionary) {
return object[self.name];
} else if ([object isKindOfClass:[NSArray class]] && self.type == MJPropertyKeyTypeArray) {
NSArray *array = object;
NSUInteger index = self.name.intValue;
if (index < array.count) return array[index];
return nil;
}
return nil;
}
@end

39
iosApp/iosApp/Libs/MJExtension/MJPropertyType.h

@ -0,0 +1,39 @@
//
// MJPropertyType.h
// MJExtension
//
// Created by mj on 14-1-15.
// Copyright (c) 2014年 小码哥. All rights reserved.
// 包装一种类型
#import <Foundation/Foundation.h>
/**
*
*/
@interface MJPropertyType : NSObject
/** 类型标识符 */
@property (nonatomic, copy) NSString *code;
/** 是否为id类型 */
@property (nonatomic, readonly, getter=isIdType) BOOL idType;
/** 是否为基本数字类型:int、float等 */
@property (nonatomic, readonly, getter=isNumberType) BOOL numberType;
/** 是否为BOOL类型 */
@property (nonatomic, readonly, getter=isBoolType) BOOL boolType;
/** 对象类型(如果是基本数据类型,此值为nil) */
@property (nonatomic, readonly) Class typeClass;
/** 类型是否来自于Foundation框架,比如NSString、NSArray */
@property (nonatomic, readonly, getter = isFromFoundation) BOOL fromFoundation;
/** 类型是否不支持KVC */
@property (nonatomic, readonly, getter = isKVCDisabled) BOOL KVCDisabled;
/**
*
*/
+ (instancetype)cachedTypeWithCode:(NSString *)code;
@end

71
iosApp/iosApp/Libs/MJExtension/MJPropertyType.m

@ -0,0 +1,71 @@
//
// MJPropertyType.m
// MJExtension
//
// Created by mj on 14-1-15.
// Copyright (c) 2014年 小码哥. All rights reserved.
//
#import "MJPropertyType.h"
#import "MJExtension.h"
#import "MJFoundation.h"
#import "MJExtensionConst.h"
@implementation MJPropertyType
+ (instancetype)cachedTypeWithCode:(NSString *)code
{
MJExtensionAssertParamNotNil2(code, nil);
static NSMutableDictionary *types;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
types = [NSMutableDictionary dictionary];
});
MJPropertyType *type = types[code];
if (type == nil) {
type = [[self alloc] init];
type.code = code;
types[code] = type;
}
return type;
}
#pragma mark - 公共方法
- (void)setCode:(NSString *)code
{
_code = code;
MJExtensionAssertParamNotNil(code);
if ([code isEqualToString:MJPropertyTypeId]) {
_idType = YES;
} else if (code.length == 0) {
_KVCDisabled = YES;
} else if (code.length > 3 && [code hasPrefix:@"@\""]) {
// 去掉@"和",截取中间的类型名称
_code = [code substringWithRange:NSMakeRange(2, code.length - 3)];
_typeClass = NSClassFromString(_code);
_fromFoundation = [MJFoundation isClassFromFoundation:_typeClass];
_numberType = [_typeClass isSubclassOfClass:[NSNumber class]];
} else if ([code isEqualToString:MJPropertyTypeSEL] ||
[code isEqualToString:MJPropertyTypeIvar] ||
[code isEqualToString:MJPropertyTypeMethod]) {
_KVCDisabled = YES;
}
// 是否为数字类型
NSString *lowerCode = _code.lowercaseString;
NSArray *numberTypes = @[MJPropertyTypeInt, MJPropertyTypeShort, MJPropertyTypeBOOL1, MJPropertyTypeBOOL2, MJPropertyTypeFloat, MJPropertyTypeDouble, MJPropertyTypeLong, MJPropertyTypeLongLong, MJPropertyTypeChar];
if ([numberTypes containsObject:lowerCode]) {
_numberType = YES;
if ([lowerCode isEqualToString:MJPropertyTypeBOOL1]
|| [lowerCode isEqualToString:MJPropertyTypeBOOL2]) {
_boolType = YES;
}
}
}
@end

90
iosApp/iosApp/Libs/MJExtension/NSObject+MJClass.h

@ -0,0 +1,90 @@
//
// NSObject+MJClass.h
// MJExtensionExample
//
// Created by MJ Lee on 15/8/11.
// Copyright (c) 2015年 小码哥. All rights reserved.
//
#import <Foundation/Foundation.h>
/**
* block
*/
typedef void (^MJClassesEnumeration)(Class c, BOOL *stop);
/** 这个数组中的属性名才会进行字典和模型的转换 */
typedef NSArray * (^MJAllowedPropertyNames)(void);
/** 这个数组中的属性名才会进行归档 */
typedef NSArray * (^MJAllowedCodingPropertyNames)(void);
/** 这个数组中的属性名将会被忽略:不进行字典和模型的转换 */
typedef NSArray * (^MJIgnoredPropertyNames)(void);
/** 这个数组中的属性名将会被忽略:不进行归档 */
typedef NSArray * (^MJIgnoredCodingPropertyNames)(void);
/**
*
*/
@interface NSObject (MJClass)
/**
*
*/
+ (void)mj_enumerateClasses:(MJClassesEnumeration)enumeration;
+ (void)mj_enumerateAllClasses:(MJClassesEnumeration)enumeration;
#pragma mark - 属性白名单配置
/**
*
*
* @param allowedPropertyNames
*/
+ (void)mj_setupAllowedPropertyNames:(MJAllowedPropertyNames)allowedPropertyNames;
/**
*
*/
+ (NSMutableArray *)mj_totalAllowedPropertyNames;
#pragma mark - 属性黑名单配置
/**
*
*
* @param ignoredPropertyNames
*/
+ (void)mj_setupIgnoredPropertyNames:(MJIgnoredPropertyNames)ignoredPropertyNames;
/**
*
*/
+ (NSMutableArray *)mj_totalIgnoredPropertyNames;
#pragma mark - 归档属性白名单配置
/**
*
*
* @param allowedCodingPropertyNames
*/
+ (void)mj_setupAllowedCodingPropertyNames:(MJAllowedCodingPropertyNames)allowedCodingPropertyNames;
/**
*
*/
+ (NSMutableArray *)mj_totalAllowedCodingPropertyNames;
#pragma mark - 归档属性黑名单配置
/**
*
*
* @param ignoredCodingPropertyNames
*/
+ (void)mj_setupIgnoredCodingPropertyNames:(MJIgnoredCodingPropertyNames)ignoredCodingPropertyNames;
/**
*
*/
+ (NSMutableArray *)mj_totalIgnoredCodingPropertyNames;
#pragma mark - 内部使用
+ (void)mj_setupBlockReturnValue:(id (^)(void))block key:(const char *)key;
@end

174
iosApp/iosApp/Libs/MJExtension/NSObject+MJClass.m

@ -0,0 +1,174 @@
//
// NSObject+MJClass.m
// MJExtensionExample
//
// Created by MJ Lee on 15/8/11.
// Copyright (c) 2015年 小码哥. All rights reserved.
//
#import "NSObject+MJClass.h"
#import "NSObject+MJCoding.h"
#import "NSObject+MJKeyValue.h"
#import "MJFoundation.h"
#import <objc/runtime.h>
static const char MJAllowedPropertyNamesKey = '\0';
static const char MJIgnoredPropertyNamesKey = '\0';
static const char MJAllowedCodingPropertyNamesKey = '\0';
static const char MJIgnoredCodingPropertyNamesKey = '\0';
@implementation NSObject (MJClass)
+ (NSMutableDictionary *)mj_classDictForKey:(const void *)key
{
static NSMutableDictionary *allowedPropertyNamesDict;
static NSMutableDictionary *ignoredPropertyNamesDict;
static NSMutableDictionary *allowedCodingPropertyNamesDict;
static NSMutableDictionary *ignoredCodingPropertyNamesDict;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
allowedPropertyNamesDict = [NSMutableDictionary dictionary];
ignoredPropertyNamesDict = [NSMutableDictionary dictionary];
allowedCodingPropertyNamesDict = [NSMutableDictionary dictionary];
ignoredCodingPropertyNamesDict = [NSMutableDictionary dictionary];
});
if (key == &MJAllowedPropertyNamesKey) return allowedPropertyNamesDict;
if (key == &MJIgnoredPropertyNamesKey) return ignoredPropertyNamesDict;
if (key == &MJAllowedCodingPropertyNamesKey) return allowedCodingPropertyNamesDict;
if (key == &MJIgnoredCodingPropertyNamesKey) return ignoredCodingPropertyNamesDict;
return nil;
}
+ (void)mj_enumerateClasses:(MJClassesEnumeration)enumeration
{
// 1.没有block就直接返回
if (enumeration == nil) return;
// 2.停止遍历的标记
BOOL stop = NO;
// 3.当前正在遍历的类
Class c = self;
// 4.开始遍历每一个类
while (c && !stop) {
// 4.1.执行操作
enumeration(c, &stop);
// 4.2.获得父类
c = class_getSuperclass(c);
if ([MJFoundation isClassFromFoundation:c]) break;
}
}
+ (void)mj_enumerateAllClasses:(MJClassesEnumeration)enumeration
{
// 1.没有block就直接返回
if (enumeration == nil) return;
// 2.停止遍历的标记
BOOL stop = NO;
// 3.当前正在遍历的类
Class c = self;
// 4.开始遍历每一个类
while (c && !stop) {
// 4.1.执行操作
enumeration(c, &stop);
// 4.2.获得父类
c = class_getSuperclass(c);
}
}
#pragma mark - 属性黑名单配置
+ (void)mj_setupIgnoredPropertyNames:(MJIgnoredPropertyNames)ignoredPropertyNames
{
[self mj_setupBlockReturnValue:ignoredPropertyNames key:&MJIgnoredPropertyNamesKey];
}
+ (NSMutableArray *)mj_totalIgnoredPropertyNames
{
return [self mj_totalObjectsWithSelector:@selector(mj_ignoredPropertyNames) key:&MJIgnoredPropertyNamesKey];
}
#pragma mark - 归档属性黑名单配置
+ (void)mj_setupIgnoredCodingPropertyNames:(MJIgnoredCodingPropertyNames)ignoredCodingPropertyNames
{
[self mj_setupBlockReturnValue:ignoredCodingPropertyNames key:&MJIgnoredCodingPropertyNamesKey];
}
+ (NSMutableArray *)mj_totalIgnoredCodingPropertyNames
{
return [self mj_totalObjectsWithSelector:@selector(mj_ignoredCodingPropertyNames) key:&MJIgnoredCodingPropertyNamesKey];
}
#pragma mark - 属性白名单配置
+ (void)mj_setupAllowedPropertyNames:(MJAllowedPropertyNames)allowedPropertyNames;
{
[self mj_setupBlockReturnValue:allowedPropertyNames key:&MJAllowedPropertyNamesKey];
}
+ (NSMutableArray *)mj_totalAllowedPropertyNames
{
return [self mj_totalObjectsWithSelector:@selector(mj_allowedPropertyNames) key:&MJAllowedPropertyNamesKey];
}
#pragma mark - 归档属性白名单配置
+ (void)mj_setupAllowedCodingPropertyNames:(MJAllowedCodingPropertyNames)allowedCodingPropertyNames
{
[self mj_setupBlockReturnValue:allowedCodingPropertyNames key:&MJAllowedCodingPropertyNamesKey];
}
+ (NSMutableArray *)mj_totalAllowedCodingPropertyNames
{
return [self mj_totalObjectsWithSelector:@selector(mj_allowedCodingPropertyNames) key:&MJAllowedCodingPropertyNamesKey];
}
#pragma mark - block和方法处理:存储block的返回值
+ (void)mj_setupBlockReturnValue:(id (^)(void))block key:(const char *)key {
MJExtensionSemaphoreCreate
MJ_LOCK(mje_signalSemaphore);
if (block) {
objc_setAssociatedObject(self, key, block(), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
} else {
objc_setAssociatedObject(self, key, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
// 清空数据
[[self mj_classDictForKey:key] removeAllObjects];
MJ_UNLOCK(mje_signalSemaphore);
}
+ (NSMutableArray *)mj_totalObjectsWithSelector:(SEL)selector key:(const char *)key
{
MJExtensionSemaphoreCreate
MJ_LOCK(mje_signalSemaphore);
NSMutableArray *array = [self mj_classDictForKey:key][NSStringFromClass(self)];
if (array == nil) {
// 创建、存储
[self mj_classDictForKey:key][NSStringFromClass(self)] = array = [NSMutableArray array];
if ([self respondsToSelector:selector]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
NSArray *subArray = [self performSelector:selector];
#pragma clang diagnostic pop
if (subArray) {
[array addObjectsFromArray:subArray];
}
}
[self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) {
NSArray *subArray = objc_getAssociatedObject(c, key);
[array addObjectsFromArray:subArray];
}];
}
MJ_UNLOCK(mje_signalSemaphore);
return array;
}
@end

66
iosApp/iosApp/Libs/MJExtension/NSObject+MJCoding.h

@ -0,0 +1,66 @@
//
// NSObject+MJCoding.h
// MJExtension
//
// Created by mj on 14-1-15.
// Copyright (c) 2014年 小码哥. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "MJExtensionConst.h"
/**
* Codeing协议
*/
@protocol MJCoding <NSObject>
@optional
/**
*
*/
+ (NSArray *)mj_allowedCodingPropertyNames;
/**
*
*/
+ (NSArray *)mj_ignoredCodingPropertyNames;
@end
@interface NSObject (MJCoding) <MJCoding>
/**
*
*/
- (void)mj_decode:(NSCoder *)decoder;
/**
*
*/
- (void)mj_encode:(NSCoder *)encoder;
@end
/**
*/
#define MJCodingImplementation \
- (id)initWithCoder:(NSCoder *)decoder \
{ \
if (self = [super init]) { \
[self mj_decode:decoder]; \
} \
return self; \
} \
\
- (void)encodeWithCoder:(NSCoder *)encoder \
{ \
[self mj_encode:encoder]; \
}\
#define MJExtensionCodingImplementation MJCodingImplementation
#define MJSecureCodingImplementation(CLASS, FLAG) \
@interface CLASS (MJSecureCoding) <NSSecureCoding> \
@end \
@implementation CLASS (MJSecureCoding) \
MJCodingImplementation \
+ (BOOL)supportsSecureCoding { \
return FLAG; \
} \
@end \

59
iosApp/iosApp/Libs/MJExtension/NSObject+MJCoding.m

@ -0,0 +1,59 @@
//
// NSObject+MJCoding.m
// MJExtension
//
// Created by mj on 14-1-15.
// Copyright (c) 2014年 小码哥. All rights reserved.
//
#import "NSObject+MJCoding.h"
#import "NSObject+MJClass.h"
#import "NSObject+MJProperty.h"
#import "MJProperty.h"
@implementation NSObject (MJCoding)
- (void)mj_encode:(NSCoder *)encoder
{
Class clazz = [self class];
NSArray *allowedCodingPropertyNames = [clazz mj_totalAllowedCodingPropertyNames];
NSArray *ignoredCodingPropertyNames = [clazz mj_totalIgnoredCodingPropertyNames];
[clazz mj_enumerateProperties:^(MJProperty *property, BOOL *stop) {
// 检测是否被忽略
if (allowedCodingPropertyNames.count && ![allowedCodingPropertyNames containsObject:property.name]) return;
if ([ignoredCodingPropertyNames containsObject:property.name]) return;
id value = [property valueForObject:self];
if (value == nil) return;
[encoder encodeObject:value forKey:property.name];
}];
}
- (void)mj_decode:(NSCoder *)decoder
{
Class clazz = [self class];
NSArray *allowedCodingPropertyNames = [clazz mj_totalAllowedCodingPropertyNames];
NSArray *ignoredCodingPropertyNames = [clazz mj_totalIgnoredCodingPropertyNames];
[clazz mj_enumerateProperties:^(MJProperty *property, BOOL *stop) {
// 检测是否被忽略
if (allowedCodingPropertyNames.count && ![allowedCodingPropertyNames containsObject:property.name]) return;
if ([ignoredCodingPropertyNames containsObject:property.name]) return;
// fixed `-[NSKeyedUnarchiver validateAllowedClass:forKey:] allowed unarchiving safe plist type ''NSNumber'(This will be disallowed in the future.)` warning.
Class genericClass = [property objectClassInArrayForClass:property.srcClass];
// If genericClass exists, property.type.typeClass would be a collection type(Array, Set, Dictionary). This scenario([obj, nil, obj, nil]) would not happened.
NSSet *classes = [NSSet setWithObjects:NSNumber.class,
property.type.typeClass, genericClass, nil];
id value = [decoder decodeObjectOfClasses:classes forKey:property.name];
if (value == nil) { // 兼容以前的MJExtension版本
value = [decoder decodeObjectForKey:[@"_" stringByAppendingString:property.name]];
}
if (value == nil) return;
[property setValue:value forObject:self];
}];
}
@end

194
iosApp/iosApp/Libs/MJExtension/NSObject+MJKeyValue.h

@ -0,0 +1,194 @@
//
// NSObject+MJKeyValue.h
// MJExtension
//
// Created by mj on 13-8-24.
// Copyright (c) 2013年 小码哥. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "MJExtensionConst.h"
#import <CoreData/CoreData.h>
#import "MJProperty.h"
/**
* KeyValue协议
*/
@protocol MJKeyValue <NSObject>
@optional
/**
*
*/
+ (NSArray *)mj_allowedPropertyNames;
/**
*
*/
+ (NSArray *)mj_ignoredPropertyNames;
/**
* key去字典中取值
*
* @return key是属性名value是从字典中取值用的key
*/
+ (NSDictionary *)mj_replacedKeyFromPropertyName;
/**
* key去字典中取值
*
* @return key
*/
+ (id)mj_replacedKeyFromPropertyName121:(NSString *)propertyName;
/**
*
*
* @return key是数组属性名value是数组中存放模型的ClassClass类型或者NSString类型
*/
+ (NSDictionary *)mj_objectClassInArray;
/** 特殊地区在字符串格式化数字时使用 */
+ (NSLocale *)mj_numberLocale;
/**
*
*
* @param oldValue
*
* @return
*/
- (id)mj_newValueFromOldValue:(id)oldValue property:(MJProperty *)property;
/**
*
*/
- (void)mj_keyValuesDidFinishConvertingToObject MJExtensionDeprecated("请使用`mj_didConvertToObjectWithKeyValues:`替代");
- (void)mj_keyValuesDidFinishConvertingToObject:(NSDictionary *)keyValues MJExtensionDeprecated("请使用`mj_didConvertToObjectWithKeyValues:`替代");
- (void)mj_didConvertToObjectWithKeyValues:(NSDictionary *)keyValues;
/**
*
*/
- (void)mj_objectDidFinishConvertingToKeyValues MJExtensionDeprecated("请使用`mj_objectDidConvertToKeyValues:`替代");
- (void)mj_objectDidConvertToKeyValues:(NSMutableDictionary *)keyValues;
@end
@interface NSObject (MJKeyValue) <MJKeyValue>
#pragma mark - 类方法
/**
*
*/
+ (NSError *)mj_error;
/**
* key是否参考replacedKeyFromPropertyName等方法
*/
+ (void)mj_referenceReplacedKeyWhenCreatingKeyValues:(BOOL)reference;
#pragma mark - 对象方法
/**
*
* @param keyValues (NSDictionaryNSDataNSString)
*/
- (instancetype)mj_setKeyValues:(id)keyValues;
/**
*
* @param keyValues (NSDictionaryNSDataNSString)
* @param context CoreData上下文
*/
- (instancetype)mj_setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context;
/**
*
* @return
*/
- (NSMutableDictionary *)mj_keyValues;
- (NSMutableDictionary *)mj_keyValuesWithKeys:(NSArray *)keys;
- (NSMutableDictionary *)mj_keyValuesWithIgnoredKeys:(NSArray *)ignoredKeys;
/**
*
* @param objectArray
* @return
*/
+ (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray;
+ (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray keys:(NSArray *)keys;
+ (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray ignoredKeys:(NSArray *)ignoredKeys;
#pragma mark - 字典转模型
/**
*
* @param keyValues (NSDictionaryNSDataNSString)
* @return
*/
+ (instancetype)mj_objectWithKeyValues:(id)keyValues;
/**
* CoreData模型
* @param keyValues (NSDictionaryNSDataNSString)
* @param context CoreData上下文
* @return
*/
+ (instancetype)mj_objectWithKeyValues:(id)keyValues context:(NSManagedObjectContext *)context;
/**
* plist来创建一个模型
* @param filename (mainBundle中的文件)
* @return
*/
+ (instancetype)mj_objectWithFilename:(NSString *)filename;
/**
* plist来创建一个模型
* @param file
* @return
*/
+ (instancetype)mj_objectWithFile:(NSString *)file;
#pragma mark - 字典数组转模型数组
/**
*
* @param keyValuesArray (NSDictionaryNSDataNSString)
* @return
*/
+ (NSMutableArray *)mj_objectArrayWithKeyValuesArray:(id)keyValuesArray;
/**
*
* @param keyValuesArray (NSDictionaryNSDataNSString)
* @param context CoreData上下文
* @return
*/
+ (NSMutableArray *)mj_objectArrayWithKeyValuesArray:(id)keyValuesArray context:(NSManagedObjectContext *)context;
/**
* plist来创建一个模型数组
* @param filename (mainBundle中的文件)
* @return
*/
+ (NSMutableArray *)mj_objectArrayWithFilename:(NSString *)filename;
/**
* plist来创建一个模型数组
* @param file
* @return
*/
+ (NSMutableArray *)mj_objectArrayWithFile:(NSString *)file;
#pragma mark - 转换为JSON
/**
* JSON Data
*/
- (NSData *)mj_JSONData;
/**
*
*/
- (id)mj_JSONObject;
/**
* JSON
*/
- (NSString *)mj_JSONString;
@end

524
iosApp/iosApp/Libs/MJExtension/NSObject+MJKeyValue.m

@ -0,0 +1,524 @@
//
// NSObject+MJKeyValue.m
// MJExtension
//
// Created by mj on 13-8-24.
// Copyright (c) 2013年 小码哥. All rights reserved.
//
#import "NSObject+MJKeyValue.h"
#import "NSObject+MJProperty.h"
#import "NSString+MJExtension.h"
#import "MJProperty.h"
#import "MJPropertyType.h"
#import "MJExtensionConst.h"
#import "MJFoundation.h"
#import "NSString+MJExtension.h"
#import "NSObject+MJClass.h"
@implementation NSDecimalNumber(MJKeyValue)
- (id)mj_standardValueWithTypeCode:(NSString *)typeCode {
// 由于这里涉及到编译器问题, 暂时保留 Long, 实际上在 64 位系统上,2 个精度范围相同,
// 32 位略有不同, 其余都可使用 Double 进行强转不丢失精度
if ([typeCode isEqualToString:MJPropertyTypeLongLong]) {
return @(self.longLongValue);
} else if ([typeCode isEqualToString:MJPropertyTypeLongLong.uppercaseString]) {
return @(self.unsignedLongLongValue);
} else if ([typeCode isEqualToString:MJPropertyTypeLong]) {
return @(self.longValue);
} else if ([typeCode isEqualToString:MJPropertyTypeLong.uppercaseString]) {
return @(self.unsignedLongValue);
} else {
return @(self.doubleValue);
}
}
@end
@implementation NSObject (MJKeyValue)
#pragma mark - 错误
static const char MJErrorKey = '\0';
+ (NSError *)mj_error
{
return objc_getAssociatedObject(self, &MJErrorKey);
}
+ (void)setMj_error:(NSError *)error
{
objc_setAssociatedObject(self, &MJErrorKey, error, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
#pragma mark - 模型 -> 字典时的参考
/** 模型转字典时,字典的key是否参考replacedKeyFromPropertyName等方法(父类设置了,子类也会继承下来) */
static const char MJReferenceReplacedKeyWhenCreatingKeyValuesKey = '\0';
+ (void)mj_referenceReplacedKeyWhenCreatingKeyValues:(BOOL)reference
{
objc_setAssociatedObject(self, &MJReferenceReplacedKeyWhenCreatingKeyValuesKey, @(reference), OBJC_ASSOCIATION_ASSIGN);
}
+ (BOOL)mj_isReferenceReplacedKeyWhenCreatingKeyValues
{
__block id value = objc_getAssociatedObject(self, &MJReferenceReplacedKeyWhenCreatingKeyValuesKey);
if (!value) {
[self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) {
value = objc_getAssociatedObject(c, &MJReferenceReplacedKeyWhenCreatingKeyValuesKey);
if (value) *stop = YES;
}];
}
return [value boolValue];
}
#pragma mark - --常用的对象--
+ (void)load
{
// 默认设置
[self mj_referenceReplacedKeyWhenCreatingKeyValues:YES];
}
#pragma mark - --公共方法--
#pragma mark - 字典 -> 模型
- (instancetype)mj_setKeyValues:(id)keyValues
{
return [self mj_setKeyValues:keyValues context:nil];
}
/**
核心代码:
*/
- (instancetype)mj_setKeyValues:(id)keyValues context:(NSManagedObjectContext *)context
{
// 获得JSON对象
keyValues = [keyValues mj_JSONObject];
MJExtensionAssertError([keyValues isKindOfClass:[NSDictionary class]], self, [self class], @"keyValues参数不是一个字典");
Class clazz = [self class];
NSArray *allowedPropertyNames = [clazz mj_totalAllowedPropertyNames];
NSArray *ignoredPropertyNames = [clazz mj_totalIgnoredPropertyNames];
NSLocale *numberLocale = nil;
if ([self.class respondsToSelector:@selector(mj_numberLocale)]) {
numberLocale = self.class.mj_numberLocale;
}
//通过封装的方法回调一个通过运行时编写的,用于返回属性列表的方法。
[clazz mj_enumerateProperties:^(MJProperty *property, BOOL *stop) {
@try {
// 0.检测是否被忽略
if (allowedPropertyNames.count && ![allowedPropertyNames containsObject:property.name]) return;
if ([ignoredPropertyNames containsObject:property.name]) return;
// 1.取出属性值
id value;
NSArray *propertyKeyses = [property propertyKeysForClass:clazz];
for (NSArray *propertyKeys in propertyKeyses) {
value = keyValues;
for (MJPropertyKey *propertyKey in propertyKeys) {
value = [propertyKey valueInObject:value];
}
if (value) break;
}
// 值的过滤
id newValue = [clazz mj_getNewValueFromObject:self oldValue:value property:property];
if (newValue != value) { // 有过滤后的新值
[property setValue:newValue forObject:self];
return;
}
// 如果没有值,就直接返回
if (!value || value == [NSNull null]) return;
// 2.复杂处理
MJPropertyType *type = property.type;
Class propertyClass = type.typeClass;
Class objectClass = [property objectClassInArrayForClass:[self class]];
// 不可变 -> 可变处理
if (propertyClass == [NSMutableArray class] && [value isKindOfClass:[NSArray class]]) {
value = [NSMutableArray arrayWithArray:value];
} else if (propertyClass == [NSMutableDictionary class] && [value isKindOfClass:[NSDictionary class]]) {
value = [NSMutableDictionary dictionaryWithDictionary:value];
} else if (propertyClass == [NSMutableString class] && [value isKindOfClass:[NSString class]]) {
value = [NSMutableString stringWithString:value];
} else if (propertyClass == [NSMutableData class] && [value isKindOfClass:[NSData class]]) {
value = [NSMutableData dataWithData:value];
}
if (!type.isFromFoundation && propertyClass) { // 模型属性
value = [propertyClass mj_objectWithKeyValues:value context:context];
} else if (objectClass) {
if (objectClass == [NSURL class] && [value isKindOfClass:[NSArray class]]) {
// string array -> url array
NSMutableArray *urlArray = [NSMutableArray array];
for (NSString *string in value) {
if (![string isKindOfClass:[NSString class]]) continue;
[urlArray addObject:string.mj_url];
}
value = urlArray;
} else { // 字典数组-->模型数组
value = [objectClass mj_objectArrayWithKeyValuesArray:value context:context];
}
} else if (propertyClass == [NSString class]) {
if ([value isKindOfClass:[NSNumber class]]) {
// NSNumber -> NSString
value = [value description];
} else if ([value isKindOfClass:[NSURL class]]) {
// NSURL -> NSString
value = [value absoluteString];
}
} else if ([value isKindOfClass:[NSString class]]) {
if (propertyClass == [NSURL class]) {
// NSString -> NSURL
// 字符串转码
value = [value mj_url];
} else if (type.isNumberType) {
NSString *oldValue = value;
// NSString -> NSDecimalNumber, 使用 DecimalNumber 来转换数字, 避免丢失精度以及溢出
NSDecimalNumber *decimalValue = [NSDecimalNumber decimalNumberWithString:oldValue
locale:numberLocale];
// 检查特殊情况
if (decimalValue == NSDecimalNumber.notANumber) {
value = @(0);
}else if (propertyClass != [NSDecimalNumber class]) {
value = [decimalValue mj_standardValueWithTypeCode:type.code];
} else {
value = decimalValue;
}
// 如果是BOOL
if (type.isBoolType) {
// 字符串转BOOL(字符串没有charValue方法
// 系统会调用字符串的charValue转为BOOL类型
NSString *lower = [oldValue lowercaseString];
if ([lower isEqualToString:@"yes"] || [lower isEqualToString:@"true"]) {
value = @YES;
} else if ([lower isEqualToString:@"no"] || [lower isEqualToString:@"false"]) {
value = @NO;
}
}
}
} else if ([value isKindOfClass:[NSNumber class]] && propertyClass == [NSDecimalNumber class]){
// 过滤 NSDecimalNumber类型
if (![value isKindOfClass:[NSDecimalNumber class]]) {
value = [NSDecimalNumber decimalNumberWithDecimal:[((NSNumber *)value) decimalValue]];
}
}
// 经过转换后, 最终检查 valueproperty 是否匹配
if (propertyClass && ![value isKindOfClass:propertyClass]) {
value = nil;
}
// 3.赋值
[property setValue:value forObject:self];
} @catch (NSException *exception) {
MJExtensionBuildError([self class], exception.reason);
MJExtensionLog(@"%@", exception);
#ifdef DEBUG
[exception raise];
#endif
}
}];
// 转换完毕
if ([self respondsToSelector:@selector(mj_didConvertToObjectWithKeyValues:)]) {
[self mj_didConvertToObjectWithKeyValues:keyValues];
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored"-Wdeprecated-declarations"
if ([self respondsToSelector:@selector(mj_keyValuesDidFinishConvertingToObject)]) {
[self mj_keyValuesDidFinishConvertingToObject];
}
if ([self respondsToSelector:@selector(mj_keyValuesDidFinishConvertingToObject:)]) {
[self mj_keyValuesDidFinishConvertingToObject:keyValues];
}
#pragma clang diagnostic pop
return self;
}
+ (instancetype)mj_objectWithKeyValues:(id)keyValues
{
return [self mj_objectWithKeyValues:keyValues context:nil];
}
+ (instancetype)mj_objectWithKeyValues:(id)keyValues context:(NSManagedObjectContext *)context
{
// 获得JSON对象
keyValues = [keyValues mj_JSONObject];
MJExtensionAssertError([keyValues isKindOfClass:[NSDictionary class]], nil, [self class], @"keyValues参数不是一个字典");
if ([self isSubclassOfClass:[NSManagedObject class]] && context) {
NSString *entityName = [(NSManagedObject *)self entity].name;
return [[NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:context] mj_setKeyValues:keyValues context:context];
}
return [[[self alloc] init] mj_setKeyValues:keyValues];
}
+ (instancetype)mj_objectWithFilename:(NSString *)filename
{
MJExtensionAssertError(filename != nil, nil, [self class], @"filename参数为nil");
return [self mj_objectWithFile:[[NSBundle mainBundle] pathForResource:filename ofType:nil]];
}
+ (instancetype)mj_objectWithFile:(NSString *)file
{
MJExtensionAssertError(file != nil, nil, [self class], @"file参数为nil");
return [self mj_objectWithKeyValues:[NSDictionary dictionaryWithContentsOfFile:file]];
}
#pragma mark - 字典数组 -> 模型数组
+ (NSMutableArray *)mj_objectArrayWithKeyValuesArray:(NSArray *)keyValuesArray
{
return [self mj_objectArrayWithKeyValuesArray:keyValuesArray context:nil];
}
+ (NSMutableArray *)mj_objectArrayWithKeyValuesArray:(id)keyValuesArray context:(NSManagedObjectContext *)context
{
// 如果是JSON字符串
keyValuesArray = [keyValuesArray mj_JSONObject];
// 1.判断真实性
MJExtensionAssertError([keyValuesArray isKindOfClass:[NSArray class]], nil, [self class], @"keyValuesArray参数不是一个数组");
// 如果数组里面放的是NSStringNSNumber等数据
if ([MJFoundation isClassFromFoundation:self]) return [NSMutableArray arrayWithArray:keyValuesArray];
// 2.创建数组
NSMutableArray *modelArray = [NSMutableArray array];
// 3.遍历
for (NSDictionary *keyValues in keyValuesArray) {
if ([keyValues isKindOfClass:[NSArray class]]){
[modelArray addObject:[self mj_objectArrayWithKeyValuesArray:keyValues context:context]];
} else {
id model = [self mj_objectWithKeyValues:keyValues context:context];
if (model) [modelArray addObject:model];
}
}
return modelArray;
}
+ (NSMutableArray *)mj_objectArrayWithFilename:(NSString *)filename
{
MJExtensionAssertError(filename != nil, nil, [self class], @"filename参数为nil");
return [self mj_objectArrayWithFile:[[NSBundle mainBundle] pathForResource:filename ofType:nil]];
}
+ (NSMutableArray *)mj_objectArrayWithFile:(NSString *)file
{
MJExtensionAssertError(file != nil, nil, [self class], @"file参数为nil");
return [self mj_objectArrayWithKeyValuesArray:[NSArray arrayWithContentsOfFile:file]];
}
#pragma mark - 模型 -> 字典
- (NSMutableDictionary *)mj_keyValues
{
return [self mj_keyValuesWithKeys:nil ignoredKeys:nil];
}
- (NSMutableDictionary *)mj_keyValuesWithKeys:(NSArray *)keys
{
return [self mj_keyValuesWithKeys:keys ignoredKeys:nil];
}
- (NSMutableDictionary *)mj_keyValuesWithIgnoredKeys:(NSArray *)ignoredKeys
{
return [self mj_keyValuesWithKeys:nil ignoredKeys:ignoredKeys];
}
- (NSMutableDictionary *)mj_keyValuesWithKeys:(NSArray *)keys ignoredKeys:(NSArray *)ignoredKeys
{
// 如果自己不是模型类, 那就返回自己
// 模型类过滤掉 NSNull
// 唯一一个不返回自己的
if ([self isMemberOfClass:NSNull.class]) { return nil; }
// 这里虽然返回了自己, 但是其实是有报错信息的.
// TODO: 报错机制不好, 需要重做
MJExtensionAssertError(![MJFoundation isClassFromFoundation:[self class]], (NSMutableDictionary *)self, [self class], @"不是自定义的模型类")
id keyValues = [NSMutableDictionary dictionary];
Class clazz = [self class];
NSArray *allowedPropertyNames = [clazz mj_totalAllowedPropertyNames];
NSArray *ignoredPropertyNames = [clazz mj_totalIgnoredPropertyNames];
[clazz mj_enumerateProperties:^(MJProperty *property, BOOL *stop) {
@try {
// 0.检测是否被忽略
if (allowedPropertyNames.count && ![allowedPropertyNames containsObject:property.name]) return;
if ([ignoredPropertyNames containsObject:property.name]) return;
if (keys.count && ![keys containsObject:property.name]) return;
if ([ignoredKeys containsObject:property.name]) return;
// 1.取出属性值
id value = [property valueForObject:self];
if (!value) return;
// 2.如果是模型属性
MJPropertyType *type = property.type;
Class propertyClass = type.typeClass;
if (!type.isFromFoundation && propertyClass) {
value = [value mj_keyValues];
} else if ([value isKindOfClass:[NSArray class]]) {
// 3.处理数组里面有模型的情况
value = [NSObject mj_keyValuesArrayWithObjectArray:value];
} else if (propertyClass == [NSURL class]) {
value = [value absoluteString];
}
// 4.赋值
if ([clazz mj_isReferenceReplacedKeyWhenCreatingKeyValues]) {
NSArray *propertyKeys = [[property propertyKeysForClass:clazz] firstObject];
NSUInteger keyCount = propertyKeys.count;
// 创建字典
__block id innerContainer = keyValues;
[propertyKeys enumerateObjectsUsingBlock:^(MJPropertyKey *propertyKey, NSUInteger idx, BOOL *stop) {
// 下一个属性
MJPropertyKey *nextPropertyKey = nil;
if (idx != keyCount - 1) {
nextPropertyKey = propertyKeys[idx + 1];
}
if (nextPropertyKey) { // 不是最后一个key
// 当前propertyKey对应的字典或者数组
id tempInnerContainer = [propertyKey valueInObject:innerContainer];
if (tempInnerContainer == nil || [tempInnerContainer isKindOfClass:[NSNull class]]) {
if (nextPropertyKey.type == MJPropertyKeyTypeDictionary) {
tempInnerContainer = [NSMutableDictionary dictionary];
} else {
tempInnerContainer = [NSMutableArray array];
}
if (propertyKey.type == MJPropertyKeyTypeDictionary) {
innerContainer[propertyKey.name] = tempInnerContainer;
} else {
innerContainer[propertyKey.name.intValue] = tempInnerContainer;
}
}
if ([tempInnerContainer isKindOfClass:[NSMutableArray class]]) {
NSMutableArray *tempInnerContainerArray = tempInnerContainer;
int index = nextPropertyKey.name.intValue;
while (tempInnerContainerArray.count < index + 1) {
[tempInnerContainerArray addObject:[NSNull null]];
}
}
innerContainer = tempInnerContainer;
} else { // 最后一个key
if (propertyKey.type == MJPropertyKeyTypeDictionary) {
innerContainer[propertyKey.name] = value;
} else {
innerContainer[propertyKey.name.intValue] = value;
}
}
}];
} else {
keyValues[property.name] = value;
}
} @catch (NSException *exception) {
MJExtensionBuildError([self class], exception.reason);
MJExtensionLog(@"%@", exception);
#ifdef DEBUG
[exception raise];
#endif
}
}];
// 转换完毕
if ([self respondsToSelector:@selector(mj_objectDidConvertToKeyValues:)]) {
[self mj_objectDidConvertToKeyValues:keyValues];
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored"-Wdeprecated-declarations"
if ([self respondsToSelector:@selector(mj_objectDidFinishConvertingToKeyValues)]) {
[self mj_objectDidFinishConvertingToKeyValues];
}
#pragma clang diagnostic pop
return keyValues;
}
#pragma mark - 模型数组 -> 字典数组
+ (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray
{
return [self mj_keyValuesArrayWithObjectArray:objectArray keys:nil ignoredKeys:nil];
}
+ (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray keys:(NSArray *)keys
{
return [self mj_keyValuesArrayWithObjectArray:objectArray keys:keys ignoredKeys:nil];
}
+ (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray ignoredKeys:(NSArray *)ignoredKeys
{
return [self mj_keyValuesArrayWithObjectArray:objectArray keys:nil ignoredKeys:ignoredKeys];
}
+ (NSMutableArray *)mj_keyValuesArrayWithObjectArray:(NSArray *)objectArray keys:(NSArray *)keys ignoredKeys:(NSArray *)ignoredKeys
{
// 0.判断真实性
MJExtensionAssertError([objectArray isKindOfClass:[NSArray class]], nil, [self class], @"objectArray参数不是一个数组");
// 1.创建数组
NSMutableArray *keyValuesArray = [NSMutableArray array];
for (id object in objectArray) {
if (keys) {
id convertedObj = [object mj_keyValuesWithKeys:keys];
if (!convertedObj) { continue; }
[keyValuesArray addObject:convertedObj];
} else {
id convertedObj = [object mj_keyValuesWithIgnoredKeys:ignoredKeys];
if (!convertedObj) { continue; }
[keyValuesArray addObject:convertedObj];
}
}
return keyValuesArray;
}
#pragma mark - 转换为JSON
- (NSData *)mj_JSONData
{
if ([self isKindOfClass:[NSString class]]) {
return [((NSString *)self) dataUsingEncoding:NSUTF8StringEncoding];
} else if ([self isKindOfClass:[NSData class]]) {
return (NSData *)self;
}
return [NSJSONSerialization dataWithJSONObject:[self mj_JSONObject] options:kNilOptions error:nil];
}
- (id)mj_JSONObject
{
if ([self isKindOfClass:[NSString class]]) {
return [NSJSONSerialization JSONObjectWithData:[((NSString *)self) dataUsingEncoding:NSUTF8StringEncoding] options:kNilOptions error:nil];
} else if ([self isKindOfClass:[NSData class]]) {
return [NSJSONSerialization JSONObjectWithData:(NSData *)self options:kNilOptions error:nil];
}
return self.mj_keyValues;
}
- (NSString *)mj_JSONString
{
if ([self isKindOfClass:[NSString class]]) {
return (NSString *)self;
} else if ([self isKindOfClass:[NSData class]]) {
return [[NSString alloc] initWithData:(NSData *)self encoding:NSUTF8StringEncoding];
}
return [[NSString alloc] initWithData:[self mj_JSONData] encoding:NSUTF8StringEncoding];
}
@end

70
iosApp/iosApp/Libs/MJExtension/NSObject+MJProperty.h

@ -0,0 +1,70 @@
//
// NSObject+MJProperty.h
// MJExtensionExample
//
// Created by MJ Lee on 15/4/17.
// Copyright (c) 2015年 小码哥. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "MJExtensionConst.h"
@class MJProperty;
/**
* block
*
* @param property
* @param stop YES代表停止遍历NO代表继续遍历
*/
typedef void (^MJPropertiesEnumeration)(MJProperty *property, BOOL *stop);
/** 将属性名换为其他key去字典中取值 */
typedef NSDictionary * (^MJReplacedKeyFromPropertyName)(void);
typedef id (^MJReplacedKeyFromPropertyName121)(NSString *propertyName);
/** 数组中需要转换的模型类 */
typedef NSDictionary * (^MJObjectClassInArray)(void);
/** 用于过滤字典中的值 */
typedef id (^MJNewValueFromOldValue)(id object, id oldValue, MJProperty *property);
/**
*
*/
@interface NSObject (MJProperty)
#pragma mark - 遍历
/**
*
*/
+ (void)mj_enumerateProperties:(MJPropertiesEnumeration)enumeration;
#pragma mark - 新值配置
/**
*
*
* @param newValueFormOldValue
*/
+ (void)mj_setupNewValueFromOldValue:(MJNewValueFromOldValue)newValueFormOldValue;
+ (id)mj_getNewValueFromObject:(__unsafe_unretained id)object oldValue:(__unsafe_unretained id)oldValue property:(__unsafe_unretained MJProperty *)property;
#pragma mark - key配置
/**
* key去字典中取值
*
* @param replacedKeyFromPropertyName key去字典中取值
*/
+ (void)mj_setupReplacedKeyFromPropertyName:(MJReplacedKeyFromPropertyName)replacedKeyFromPropertyName;
/**
* key去字典中取值
*
* @param replacedKeyFromPropertyName121 key去字典中取值
*/
+ (void)mj_setupReplacedKeyFromPropertyName121:(MJReplacedKeyFromPropertyName121)replacedKeyFromPropertyName121;
#pragma mark - array model class配置
/**
*
*
* @param objectClassInArray
*/
+ (void)mj_setupObjectClassInArray:(MJObjectClassInArray)objectClassInArray;
@end

240
iosApp/iosApp/Libs/MJExtension/NSObject+MJProperty.m

@ -0,0 +1,240 @@
//
// NSObject+MJProperty.m
// MJExtensionExample
//
// Created by MJ Lee on 15/4/17.
// Copyright (c) 2015年 小码哥. All rights reserved.
//
#import "NSObject+MJProperty.h"
#import "NSObject+MJKeyValue.h"
#import "NSObject+MJCoding.h"
#import "NSObject+MJClass.h"
#import "MJProperty.h"
#import "MJFoundation.h"
#import <objc/runtime.h>
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
static const char MJReplacedKeyFromPropertyNameKey = '\0';
static const char MJReplacedKeyFromPropertyName121Key = '\0';
static const char MJNewValueFromOldValueKey = '\0';
static const char MJObjectClassInArrayKey = '\0';
static const char MJCachedPropertiesKey = '\0';
dispatch_semaphore_t mje_signalSemaphore;
dispatch_once_t mje_onceTokenSemaphore;
@implementation NSObject (Property)
+ (NSMutableDictionary *)mj_propertyDictForKey:(const void *)key
{
static NSMutableDictionary *replacedKeyFromPropertyNameDict;
static NSMutableDictionary *replacedKeyFromPropertyName121Dict;
static NSMutableDictionary *newValueFromOldValueDict;
static NSMutableDictionary *objectClassInArrayDict;
static NSMutableDictionary *cachedPropertiesDict;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
replacedKeyFromPropertyNameDict = [NSMutableDictionary dictionary];
replacedKeyFromPropertyName121Dict = [NSMutableDictionary dictionary];
newValueFromOldValueDict = [NSMutableDictionary dictionary];
objectClassInArrayDict = [NSMutableDictionary dictionary];
cachedPropertiesDict = [NSMutableDictionary dictionary];
});
if (key == &MJReplacedKeyFromPropertyNameKey) return replacedKeyFromPropertyNameDict;
if (key == &MJReplacedKeyFromPropertyName121Key) return replacedKeyFromPropertyName121Dict;
if (key == &MJNewValueFromOldValueKey) return newValueFromOldValueDict;
if (key == &MJObjectClassInArrayKey) return objectClassInArrayDict;
if (key == &MJCachedPropertiesKey) return cachedPropertiesDict;
return nil;
}
#pragma mark - --私有方法--
+ (id)mj_propertyKey:(NSString *)propertyName
{
MJExtensionAssertParamNotNil2(propertyName, nil);
__block id key = nil;
// 查看有没有需要替换的key
if ([self respondsToSelector:@selector(mj_replacedKeyFromPropertyName121:)]) {
key = [self mj_replacedKeyFromPropertyName121:propertyName];
}
// 调用block
if (!key) {
[self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) {
MJReplacedKeyFromPropertyName121 block = objc_getAssociatedObject(c, &MJReplacedKeyFromPropertyName121Key);
if (block) {
key = block(propertyName);
}
if (key) *stop = YES;
}];
}
// 查看有没有需要替换的key
if ((!key || [key isEqual:propertyName]) && [self respondsToSelector:@selector(mj_replacedKeyFromPropertyName)]) {
key = [self mj_replacedKeyFromPropertyName][propertyName];
}
if (!key || [key isEqual:propertyName]) {
[self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) {
NSDictionary *dict = objc_getAssociatedObject(c, &MJReplacedKeyFromPropertyNameKey);
if (dict) {
key = dict[propertyName];
}
if (key && ![key isEqual:propertyName]) *stop = YES;
}];
}
// 2.用属性名作为key
if (!key) key = propertyName;
return key;
}
+ (Class)mj_propertyObjectClassInArray:(NSString *)propertyName
{
__block id clazz = nil;
if ([self respondsToSelector:@selector(mj_objectClassInArray)]) {
clazz = [self mj_objectClassInArray][propertyName];
}
if (!clazz) {
[self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) {
NSDictionary *dict = objc_getAssociatedObject(c, &MJObjectClassInArrayKey);
if (dict) {
clazz = dict[propertyName];
}
if (clazz) *stop = YES;
}];
}
// 如果是NSString类型
if ([clazz isKindOfClass:[NSString class]]) {
clazz = NSClassFromString(clazz);
}
return clazz;
}
#pragma mark - --公共方法--
+ (void)mj_enumerateProperties:(MJPropertiesEnumeration)enumeration
{
// 获得成员变量
NSArray *cachedProperties = [self mj_properties];
// 遍历成员变量
BOOL stop = NO;
for (MJProperty *property in cachedProperties) {
enumeration(property, &stop);
if (stop) break;
}
}
#pragma mark - 公共方法
+ (NSArray *)mj_properties
{
MJExtensionSemaphoreCreate
MJ_LOCK(mje_signalSemaphore);
NSMutableDictionary *cachedInfo = [self mj_propertyDictForKey:&MJCachedPropertiesKey];
NSMutableArray *cachedProperties = cachedInfo[NSStringFromClass(self)];
if (cachedProperties == nil) {
cachedProperties = [NSMutableArray array];
[self mj_enumerateClasses:^(__unsafe_unretained Class c, BOOL *stop) {
// 1.获得所有的成员变量
unsigned int outCount = 0;
objc_property_t *properties = class_copyPropertyList(c, &outCount);
// 2.遍历每一个成员变量
for (unsigned int i = 0; i<outCount; i++) {
MJProperty *property = [MJProperty cachedPropertyWithProperty:properties[i]];
// 过滤掉Foundation框架类里面的属性
if ([MJFoundation isClassFromFoundation:property.srcClass]) continue;
// 过滤掉`hash`, `superclass`, `description`, `debugDescription`
if ([MJFoundation isFromNSObjectProtocolProperty:property.name]) continue;
property.srcClass = c;
[property setOriginKey:[self mj_propertyKey:property.name] forClass:self];
[property setObjectClassInArray:[self mj_propertyObjectClassInArray:property.name] forClass:self];
[cachedProperties addObject:property];
}
// 3.释放内存
free(properties);
}];
cachedInfo[NSStringFromClass(self)] = cachedProperties;
}
NSArray *properties = [cachedProperties copy];
MJ_UNLOCK(mje_signalSemaphore);
return properties;
}
#pragma mark - 新值配置
+ (void)mj_setupNewValueFromOldValue:(MJNewValueFromOldValue)newValueFormOldValue {
MJExtensionSemaphoreCreate
MJ_LOCK(mje_signalSemaphore);
objc_setAssociatedObject(self, &MJNewValueFromOldValueKey, newValueFormOldValue, OBJC_ASSOCIATION_COPY_NONATOMIC);
MJ_UNLOCK(mje_signalSemaphore);
}
+ (id)mj_getNewValueFromObject:(__unsafe_unretained id)object oldValue:(__unsafe_unretained id)oldValue property:(MJProperty *__unsafe_unretained)property{
// 如果有实现方法
if ([object respondsToSelector:@selector(mj_newValueFromOldValue:property:)]) {
return [object mj_newValueFromOldValue:oldValue property:property];
}
MJExtensionSemaphoreCreate
MJ_LOCK(mje_signalSemaphore);
// 查看静态设置
__block id newValue = oldValue;
[self mj_enumerateAllClasses:^(__unsafe_unretained Class c, BOOL *stop) {
MJNewValueFromOldValue block = objc_getAssociatedObject(c, &MJNewValueFromOldValueKey);
if (block) {
newValue = block(object, oldValue, property);
*stop = YES;
}
}];
MJ_UNLOCK(mje_signalSemaphore);
return newValue;
}
+ (void)mj_removeCachedProperties {
MJExtensionSemaphoreCreate
MJ_LOCK(mje_signalSemaphore);
[[self mj_propertyDictForKey:&MJCachedPropertiesKey] removeAllObjects];
MJ_UNLOCK(mje_signalSemaphore);
}
#pragma mark - array model class配置
+ (void)mj_setupObjectClassInArray:(MJObjectClassInArray)objectClassInArray
{
[self mj_setupBlockReturnValue:objectClassInArray key:&MJObjectClassInArrayKey];
[self mj_removeCachedProperties];
}
#pragma mark - key配置
+ (void)mj_setupReplacedKeyFromPropertyName:(MJReplacedKeyFromPropertyName)replacedKeyFromPropertyName {
[self mj_setupBlockReturnValue:replacedKeyFromPropertyName key:&MJReplacedKeyFromPropertyNameKey];
[self mj_removeCachedProperties];
}
+ (void)mj_setupReplacedKeyFromPropertyName121:(MJReplacedKeyFromPropertyName121)replacedKeyFromPropertyName121 {
MJExtensionSemaphoreCreate
MJ_LOCK(mje_signalSemaphore);
objc_setAssociatedObject(self, &MJReplacedKeyFromPropertyName121Key, replacedKeyFromPropertyName121, OBJC_ASSOCIATION_COPY_NONATOMIC);
[[self mj_propertyDictForKey:&MJCachedPropertiesKey] removeAllObjects];
MJ_UNLOCK(mje_signalSemaphore);
}
@end
#pragma clang diagnostic pop

33
iosApp/iosApp/Libs/MJExtension/NSString+MJExtension.h

@ -0,0 +1,33 @@
//
// NSString+MJExtension.h
// MJExtensionExample
//
// Created by MJ Lee on 15/6/7.
// Copyright (c) 2015年 小码哥. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "MJExtensionConst.h"
@interface NSString (MJExtension)
/**
* 线loveYou -> love_you
*/
- (NSString *)mj_underlineFromCamel;
/**
* 线love_you -> loveYou
*/
- (NSString *)mj_camelFromUnderline;
/**
*
*/
- (NSString *)mj_firstCharUpper;
/**
*
*/
- (NSString *)mj_firstCharLower;
- (BOOL)mj_isPureInt;
- (NSURL *)mj_url;
@end

80
iosApp/iosApp/Libs/MJExtension/NSString+MJExtension.m

@ -0,0 +1,80 @@
//
// NSString+MJExtension.m
// MJExtensionExample
//
// Created by MJ Lee on 15/6/7.
// Copyright (c) 2015年 小码哥. All rights reserved.
//
#import "NSString+MJExtension.h"
@implementation NSString (MJExtension)
- (NSString *)mj_underlineFromCamel
{
if (self.length == 0) return self;
NSMutableString *string = [NSMutableString string];
for (NSUInteger i = 0; i<self.length; i++) {
unichar c = [self characterAtIndex:i];
NSString *cString = [NSString stringWithFormat:@"%c", c];
NSString *cStringLower = [cString lowercaseString];
if ([cString isEqualToString:cStringLower]) {
[string appendString:cStringLower];
} else {
[string appendString:@"_"];
[string appendString:cStringLower];
}
}
return string;
}
- (NSString *)mj_camelFromUnderline
{
if (self.length == 0) return self;
NSMutableString *string = [NSMutableString string];
NSArray *cmps = [self componentsSeparatedByString:@"_"];
for (NSUInteger i = 0; i<cmps.count; i++) {
NSString *cmp = cmps[i];
if (i && cmp.length) {
[string appendString:[NSString stringWithFormat:@"%c", [cmp characterAtIndex:0]].uppercaseString];
if (cmp.length >= 2) [string appendString:[cmp substringFromIndex:1]];
} else {
[string appendString:cmp];
}
}
return string;
}
- (NSString *)mj_firstCharLower
{
if (self.length == 0) return self;
NSMutableString *string = [NSMutableString string];
[string appendString:[NSString stringWithFormat:@"%c", [self characterAtIndex:0]].lowercaseString];
if (self.length >= 2) [string appendString:[self substringFromIndex:1]];
return string;
}
- (NSString *)mj_firstCharUpper
{
if (self.length == 0) return self;
NSMutableString *string = [NSMutableString string];
[string appendString:[NSString stringWithFormat:@"%c", [self characterAtIndex:0]].uppercaseString];
if (self.length >= 2) [string appendString:[self substringFromIndex:1]];
return string;
}
- (BOOL)mj_isPureInt
{
NSScanner *scan = [NSScanner scannerWithString:self];
int val;
return [scan scanInt:&val] && [scan isAtEnd];
}
- (NSURL *)mj_url
{
// [self stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet characterSetWithCharactersInString:@"!$&'()*+,-./:;=?@_~%#[]"]];
#pragma clang diagnostic push
#pragma clang diagnostic ignored"-Wdeprecated-declarations"
return [NSURL URLWithString:(NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)self, (CFStringRef)@"!$&'()*+,-./:;=?@_~%#[]", NULL,kCFStringEncodingUTF8))];
#pragma clang diagnostic pop
}
@end
Loading…
Cancel
Save