Commit 77806b6f authored by “Icebear”'s avatar “Icebear”

完成密码登录接口调试

parent 13d972ff
......@@ -42,6 +42,11 @@
33E979A1251C4B7900437015 /* BaseUrlConfig.json in Resources */ = {isa = PBXBuildFile; fileRef = 33E979A0251C4B7900437015 /* BaseUrlConfig.json */; };
33E979A6251C659800437015 /* NSMutableArray+Safe.m in Sources */ = {isa = PBXBuildFile; fileRef = 33E979A5251C659800437015 /* NSMutableArray+Safe.m */; };
33E979BB251C757A00437015 /* BaseHttpModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 33E979B9251C757A00437015 /* BaseHttpModel.m */; };
33E979C1251CA2E100437015 /* NRLoginPublicKeyRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = 33E979C0251CA2E100437015 /* NRLoginPublicKeyRequest.m */; };
33E979D3251CAAC800437015 /* UIDevice+NRDevice.m in Sources */ = {isa = PBXBuildFile; fileRef = 33E979D2251CAAC800437015 /* UIDevice+NRDevice.m */; };
33E97ACE251CAFBA00437015 /* SFHFKeychainUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 33E97ACD251CAFBA00437015 /* SFHFKeychainUtils.m */; settings = {COMPILER_FLAGS = "-fno-objc-arc"; }; };
33E97AED251CB37200437015 /* NSString+NRRSA.m in Sources */ = {isa = PBXBuildFile; fileRef = 33E97AEC251CB37200437015 /* NSString+NRRSA.m */; };
33E97AF7251D8A3B00437015 /* NRUserModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 33E97AF6251D8A3B00437015 /* NRUserModel.m */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
......@@ -107,6 +112,16 @@
33E979A5251C659800437015 /* NSMutableArray+Safe.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSMutableArray+Safe.m"; sourceTree = "<group>"; };
33E979B9251C757A00437015 /* BaseHttpModel.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BaseHttpModel.m; sourceTree = "<group>"; };
33E979BA251C757A00437015 /* BaseHttpModel.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BaseHttpModel.h; sourceTree = "<group>"; };
33E979BF251CA2E100437015 /* NRLoginPublicKeyRequest.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NRLoginPublicKeyRequest.h; sourceTree = "<group>"; };
33E979C0251CA2E100437015 /* NRLoginPublicKeyRequest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NRLoginPublicKeyRequest.m; sourceTree = "<group>"; };
33E979D1251CAAC800437015 /* UIDevice+NRDevice.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "UIDevice+NRDevice.h"; sourceTree = "<group>"; };
33E979D2251CAAC800437015 /* UIDevice+NRDevice.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "UIDevice+NRDevice.m"; sourceTree = "<group>"; };
33E97ACC251CAFBA00437015 /* SFHFKeychainUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SFHFKeychainUtils.h; sourceTree = "<group>"; };
33E97ACD251CAFBA00437015 /* SFHFKeychainUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SFHFKeychainUtils.m; sourceTree = "<group>"; };
33E97AEB251CB37200437015 /* NSString+NRRSA.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSString+NRRSA.h"; sourceTree = "<group>"; };
33E97AEC251CB37200437015 /* NSString+NRRSA.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSString+NRRSA.m"; sourceTree = "<group>"; };
33E97AF5251D8A3B00437015 /* NRUserModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NRUserModel.h; sourceTree = "<group>"; };
33E97AF6251D8A3B00437015 /* NRUserModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NRUserModel.m; sourceTree = "<group>"; };
49664D4BB9E25240F6B2F0A9 /* Pods-NetrainFrame.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NetrainFrame.release.xcconfig"; path = "Target Support Files/Pods-NetrainFrame/Pods-NetrainFrame.release.xcconfig"; sourceTree = "<group>"; };
AFAD26382CE323D58F08BF66 /* Pods_NetrainFrame.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_NetrainFrame.framework; sourceTree = BUILT_PRODUCTS_DIR; };
DCF21838657E8CF2B8988B38 /* Pods-NetrainFrame.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-NetrainFrame.debug.xcconfig"; path = "Target Support Files/Pods-NetrainFrame/Pods-NetrainFrame.debug.xcconfig"; sourceTree = "<group>"; };
......@@ -174,6 +189,8 @@
33E97974251B618700437015 /* NRVerifyCodeLoginRequest.m */,
33E9796A251B5FA500437015 /* NRLoginKeyRequest.h */,
33E9796B251B5FA500437015 /* NRLoginKeyRequest.m */,
33E979BF251CA2E100437015 /* NRLoginPublicKeyRequest.h */,
33E979C0251CA2E100437015 /* NRLoginPublicKeyRequest.m */,
33E97966251B5E8B00437015 /* NRServerTimeRequest.h */,
33E97967251B5E8B00437015 /* NRServerTimeRequest.m */,
33E9795E251B5D8100437015 /* NRVerifyCodeRequest.h */,
......@@ -300,7 +317,7 @@
335F6AC4251895660083A571 /* Utils */ = {
isa = PBXGroup;
children = (
33E979A4251C658900437015 /* SafeArray */,
33E979A4251C658900437015 /* Categories */,
33A1D2872519E50C00824CB8 /* QMUIConfigurationTemplate */,
);
path = Utils;
......@@ -370,6 +387,7 @@
33A1D27C2519C11E00824CB8 /* Login */ = {
isa = PBXGroup;
children = (
33E97AF4251D8A1F00437015 /* Model */,
335609FC251B59D400628080 /* Service */,
335609FB251B554200628080 /* Logic */,
335609B4251B23D100628080 /* Coordinator */,
......@@ -410,12 +428,27 @@
path = BaseModelAgent;
sourceTree = "<group>";
};
33E979A4251C658900437015 /* SafeArray */ = {
33E979A4251C658900437015 /* Categories */ = {
isa = PBXGroup;
children = (
33E97ACC251CAFBA00437015 /* SFHFKeychainUtils.h */,
33E97ACD251CAFBA00437015 /* SFHFKeychainUtils.m */,
33E979A5251C659800437015 /* NSMutableArray+Safe.m */,
33E979D1251CAAC800437015 /* UIDevice+NRDevice.h */,
33E979D2251CAAC800437015 /* UIDevice+NRDevice.m */,
33E97AEB251CB37200437015 /* NSString+NRRSA.h */,
33E97AEC251CB37200437015 /* NSString+NRRSA.m */,
);
path = SafeArray;
path = Categories;
sourceTree = "<group>";
};
33E97AF4251D8A1F00437015 /* Model */ = {
isa = PBXGroup;
children = (
33E97AF5251D8A3B00437015 /* NRUserModel.h */,
33E97AF6251D8A3B00437015 /* NRUserModel.m */,
);
path = Model;
sourceTree = "<group>";
};
AF101A8647B6D432ED4BF085 /* Pods */ = {
......@@ -560,10 +593,15 @@
files = (
33E97964251B5D9500437015 /* NRRegisterRequest.m in Sources */,
335609B7251B23E500628080 /* Target_LoginCoordinator.m in Sources */,
33E97AED251CB37200437015 /* NSString+NRRSA.m in Sources */,
335F6AD125189B130083A571 /* AppDelegate+AppService.m in Sources */,
33E979C1251CA2E100437015 /* NRLoginPublicKeyRequest.m in Sources */,
33E97993251C476300437015 /* NRBaseModelAgent.m in Sources */,
335609EA251B4D9900628080 /* CTMediator+MainCoordinatorActions.m in Sources */,
33A1D28A2519E50C00824CB8 /* QMUIConfigurationTemplate.m in Sources */,
33E979D3251CAAC800437015 /* UIDevice+NRDevice.m in Sources */,
33E97ACE251CAFBA00437015 /* SFHFKeychainUtils.m in Sources */,
33E97AF7251D8A3B00437015 /* NRUserModel.m in Sources */,
33E97968251B5E8B00437015 /* NRServerTimeRequest.m in Sources */,
33560A01251B5AAF00628080 /* NRPasswordLoginRequest.m in Sources */,
33A1D2942519EB5100824CB8 /* NRCommonViewController.m in Sources */,
......
......@@ -17,6 +17,21 @@ NS_ASSUME_NONNULL_BEGIN
* @return instance of JHModelAgent
*/
+ (instancetype)agent;
/**
* Write Model instance to File
*
* @param modelClass Should be derived from NSObject.
*
*/
- (BOOL)writeModel:(NSObject *)model;
/**
* Read Model instance for Class
*
* @param class .
*
*/
- (id)readModelForClass:(Class)class1;
/**
* Create Model instance from Json Data
*
......
//
// NRBaseModelAgent.m
// NRNSObjectAgent.m
// NetrainFrame
//
// Created by Gin on 2020/9/24.
......@@ -7,10 +7,120 @@
#import "NRBaseModelAgent.h"
#define kModelFileExt (@"model")
#define kModelFileDir (@"YYModel")
#define PATH_DOCUMENT [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject]
@implementation NRBaseModelAgent
+ (instancetype)agent {
return [[NRBaseModelAgent alloc] init];
}
#pragma mark - read/write/delete
- (BOOL)writeModel:(NSObject *)model {
if (model) {
NSString *fileName = [NSString stringWithUTF8String:class_getName([model class])];
return [self writeModel:model toFileWithName:fileName];
} else {
NSLog(@"model is null");
return NO;
}
}
- (BOOL)writeModel:(NSObject *)model toFileWithName:(NSString *)fileName {
if (fileName.isNotBlank) {
return [self writeModel:model toDisk:[self filePathWithName:fileName]];
} else {
NSLog(@"fileName is null");
return NO;
}
}
- (BOOL)writeModel:(NSObject *)model toDisk:(NSString *)path {
BOOL result = NO;
if (path.isNotBlank) {
path = [path stringByAppendingPathExtension:kModelFileExt];
result = [NSKeyedArchiver archiveRootObject:model toFile:path];
if (!result) {
NSLog(@"Failed to archive. Path :%@", path);
}
} else {
NSLog(@"Path is null");
}
return result;
}
- (id)readModelForClass:(Class)class1 {
return [self readModelFromFileWithName:[NSString stringWithUTF8String:class_getName(class1)]];
}
- (id)readModelFromFileWithName:(NSString *)fileName {
if (fileName.isNotBlank) {
return [self readModelFromDisk:[self filePathWithName:fileName]];
} else {
NSLog(@"fileName is nil");
return nil;
}
}
- (id)readModelFromDisk:(NSString *)path {
NSObject *result = nil;
if (path.isNotBlank) {
path = [path stringByAppendingPathExtension:kModelFileExt];
id data = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
if ([data isKindOfClass:NSObject.class]) {
result = data;
} else if (data) {
NSLog(@"Class Type is wrong. %@", [data class]);
}
} else {
NSLog(@"Path is null");
}
return result;
}
- (BOOL)removeModel:(NSObject *)model toFileWithName:(NSString *)fileName {
if (fileName.isNotBlank) {
return [self deleteModel:model toFilePath:[self filePathWithName:fileName]];
} else {
NSLog(@"fileName is null");
return NO;
}
}
- (BOOL)deleteModel:(NSObject *)model toFilePath:(NSString *)filePath {
BOOL result = NO;
if (filePath.isNotBlank) {
filePath = [filePath stringByAppendingPathExtension:kModelFileExt];
NSFileManager *fileManager = [NSFileManager defaultManager];
if ([fileManager isDeletableFileAtPath:filePath]) {
result = [fileManager removeItemAtPath:filePath error:nil];
if (!result) {
NSLog(@"Failed to archive. Path :%@", filePath);
}
} else {
result = NO;
}
} else {
NSLog(@"Path is null");
}
return result;
}
- (NSString *)filePathWithName:(NSString *)name {
NSString *dir = [PATH_DOCUMENT stringByAppendingPathComponent:kModelFileDir];
if (![[NSFileManager defaultManager] fileExistsAtPath:dir]) {
[[NSFileManager defaultManager] createDirectoryAtPath:dir withIntermediateDirectories:YES attributes:nil error:nil];
NSURL *URL = [NSURL fileURLWithPath:dir isDirectory:NO];
[URL setResourceValue:@(YES) forKey:NSURLIsExcludedFromBackupKey error:nil];
}
return [dir stringByAppendingPathComponent:name];
}
#pragma mark - json to model
- (NSArray *)createModel:(Class)modelClass fromJson:(id)json {
......
......@@ -81,6 +81,11 @@ typedef NS_ENUM(NSUInteger, UserSelectLoginMode) {
self.phoneNumberField.delegate = self;
self.passwdField.delegate = self;
[self.phoneNumberField addTarget:self action:@selector(textFieldDidChange)
forControlEvents:UIControlEventEditingChanged];
[self.passwdField addTarget:self action:@selector(textFieldDidChange)
forControlEvents:UIControlEventEditingChanged];
}
......@@ -93,9 +98,8 @@ typedef NS_ENUM(NSUInteger, UserSelectLoginMode) {
}
}
- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{
- (void)textFieldDidChange{
[self checkLoginStatus];
return YES;
}
-(void)textFieldDidEndEditing:(UITextField *)textField{
......@@ -106,6 +110,16 @@ typedef NS_ENUM(NSUInteger, UserSelectLoginMode) {
}
}
- (BOOL)textFieldShouldClear:(UITextField *)textField{
if(textField == self.phoneNumberField){
self.phoneNumberField.text = @"";
}else{
self.passwdField.text = @"";
}
[self checkLoginStatus];
return YES;
}
#pragma mark -- NRLoginLogicDelegate
-(void)requestDataCompleted:(BaseHttpModel *)httpModel{
[QMUITips hideAllTips];
......@@ -126,20 +140,17 @@ typedef NS_ENUM(NSUInteger, UserSelectLoginMode) {
///点击登录
- (IBAction)loginBtnClick:(id)sender {
[QMUITips showLoadingInView:self.view];
if(self.loginMode == verificationLogin){
[self.loginLogic verifyCodeLoginAction];
}else{
[self.loginLogic passwordLoginAction];
[self.loginLogic passwordLoginWithPhoneNumber:self.phoneNumberField.text password:self.passwdField.text];
}
}
///获取验证码
- (IBAction)getVerification:(id)sender {
if([self.phoneNumberField.text qmui_trimAllWhiteSpace].length != 11){
[QMUITips showError:@"请输入正确的手机号"];
return;
}
[QMUITips showLoading:@"获取中" inView:self.view];
[self.loginLogic getVerifyCodeAction:self.phoneNumberField.text];
[QMUITips showLoadingInView:self.view];
[self.loginLogic getVerifyCodeWithPhoneNumber:self.phoneNumberField.text];
}
///密码登录
......@@ -199,6 +210,13 @@ typedef NS_ENUM(NSUInteger, UserSelectLoginMode) {
}
}
}
if(self.phoneNumberField.text.length == kAccountMaxLength) {
self.verificationBtn.enabled = YES;
}else{
if(self.verificationBtn.isEnabled){
self.verificationBtn.enabled = NO;
}
}
}
-(NRLoginLogic *)loginLogic{
......
......@@ -9,14 +9,11 @@
#import "NRPasswordLoginRequest.h"
#import "NRServerTimeRequest.h"
#import "NRVerifyCodeRequest.h"
#import "NRLoginKeyRequest.h"
#import "NRLoginPublicKeyRequest.h"
@protocol NRLoginLogicDelegate <NSObject>
@optional
/**
数据加载开始
*/
-(void)requestDataStarted;
/**
数据加载完成
*/
......@@ -30,11 +27,11 @@ NS_ASSUME_NONNULL_BEGIN
@property(nonatomic,weak) id<NRLoginLogicDelegate> logicDelegate;
- (void)getVerifyCodeAction:(NSString *)phoneNumber;
- (void)getVerifyCodeWithPhoneNumber:(NSString *)phoneNumber;
- (void)verifyCodeLoginAction;
- (void)passwordLoginAction;
- (void)passwordLoginWithPhoneNumber:(NSString *)phoneNumber password:(NSString *)password;
@end
......
......@@ -6,40 +6,103 @@
//
#import "NRLoginLogic.h"
#import "UIDevice+NRDevice.h"
#import "NSString+NRRSA.h"
#import "NRUserModel.h"
@implementation NRLoginLogic
- (void)getVerifyCodeAction:(NSString *)phoneNumber{
- (void)getVerifyCodeWithPhoneNumber:(NSString *)phoneNumber{
NRServerTimeRequest *servertimeRequest = [[NRServerTimeRequest alloc] init];
[servertimeRequest startWithCompletionBlock:^(NRBaseRequest * _Nonnull request, NSString * _Nonnull error) {
if (!error) {
NSString *tvalue = [servertimeRequest.httpModel.data objectAtIndex:0];
NRVerifyCodeRequest *verifyCodeRequest = [[NRVerifyCodeRequest alloc] init];
verifyCodeRequest.phoneNumber = phoneNumber;
verifyCodeRequest.actionType = @"6";
verifyCodeRequest.tvalue = tvalue ? tvalue : @"";
[verifyCodeRequest startWithCompletionBlock:^(NRBaseRequest * _Nonnull request, NSString * _Nonnull error) {
if (!error) {
}
if (self.logicDelegate && [self.logicDelegate respondsToSelector:@selector(requestDataCompleted:)]) {
[self.logicDelegate requestDataCompleted:request.httpModel];
}
}];
[self sendVerifyCodeRequest:tvalue phoneNumber:phoneNumber];
}else{
if (self.logicDelegate && [self.logicDelegate respondsToSelector:@selector(requestDataCompleted:)]) {
[self.logicDelegate requestDataCompleted:request.httpModel];
}
}
}];
}
- (void)sendVerifyCodeRequest:(NSString *)tvalue phoneNumber:(NSString *)phoneNumber{
NRVerifyCodeRequest *verifyCodeRequest = [[NRVerifyCodeRequest alloc] init];
verifyCodeRequest.phoneNumber = phoneNumber;
verifyCodeRequest.actionType = @"6";
verifyCodeRequest.tvalue = tvalue ? tvalue : @"";
[verifyCodeRequest startWithCompletionBlock:^(NRBaseRequest * _Nonnull request, NSString * _Nonnull error) {
if (!error) {
}
if (self.logicDelegate && [self.logicDelegate respondsToSelector:@selector(requestDataCompleted:)]) {
[self.logicDelegate requestDataCompleted:request.httpModel];
}
}];
}
- (void)verifyCodeLoginAction{
}
- (void)passwordLoginAction{
- (void)passwordLoginWithPhoneNumber:(NSString *)phoneNumber password:(NSString *)password{
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
NRLoginKeyRequest *loginKeyRequest = [[NRLoginKeyRequest alloc] init];
loginKeyRequest.phoneNumber = phoneNumber;
[loginKeyRequest startWithCompletionBlock:^(NRBaseRequest * _Nonnull request, NSString * _Nonnull error) {
if(!error){
NSDictionary *loginKeyDict = [loginKeyRequest.httpModel.data objectAtIndex:0];
[params setObject:[loginKeyDict objectForKey:@"loginKey"] forKey:@"loginKey"];
[params setObject:[loginKeyDict objectForKey:@"loginId"] forKey:@"loginId"];
[params setObject:phoneNumber forKey:@"phoneNum"];
[params setObject:password forKey:@"password"];
[self getPublicKeyRequest:params];
}else{
if (self.logicDelegate && [self.logicDelegate respondsToSelector:@selector(requestDataCompleted:)]) {
[self.logicDelegate requestDataCompleted:request.httpModel];
}
}
}];
}
-(void)getPublicKeyRequest:(NSMutableDictionary *)params{
NRLoginPublicKeyRequest *publicKeyRequest = [[NRLoginPublicKeyRequest alloc] init];
publicKeyRequest.phoneNumber = [params objectForKey:@"phoneNum"];
[publicKeyRequest startWithCompletionBlock:^(NRBaseRequest * _Nonnull request, NSString * _Nonnull error) {
if(!error){
NSString* pubKey = [[publicKeyRequest.httpModel.data objectAtIndex:0] objectForKey:@"publicKey"];
NSString *publicKey=[NSString formatPulicKeys:pubKey password:[params objectForKey:@"password"]];
// if(!publicKey.isNotBlank){
// [ToastView showMessageCaption:@"登录失败,请重新登录"];
// return ;
// }
[params setObject:publicKey forKey:@"password"];
[self passwordLoginRequest:params];
}else{
if (self.logicDelegate && [self.logicDelegate respondsToSelector:@selector(requestDataCompleted:)]) {
[self.logicDelegate requestDataCompleted:request.httpModel];
}
}
}];
}
-(void)passwordLoginRequest:(NSMutableDictionary *)params{
NRPasswordLoginRequest *pwdLoginRequest = [[NRPasswordLoginRequest alloc] init];
NSString *deviceIdentifier = [UIDevice deviceIdentifier];
if (!deviceIdentifier.isNotBlank) {
deviceIdentifier = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
}
if(deviceIdentifier.isNotBlank){
[params setObject:deviceIdentifier forKey:@"deviceSN"];
}
pwdLoginRequest.params = params;
[pwdLoginRequest startWithCompletionBlock:^(NRBaseRequest * _Nonnull request, NSString * _Nonnull error) {
if(!error){
[pwdLoginRequest saveModel];
}
if (self.logicDelegate && [self.logicDelegate respondsToSelector:@selector(requestDataCompleted:)]) {
[self.logicDelegate requestDataCompleted:request.httpModel];
}
}];
}
@end
//
// NRUserModel.h
// NetrainFrame
//
// Created by Gin on 2020/9/25.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface NRUserModel : NSObject
@property(nonatomic, copy) NSString *doctorId;
@property(nonatomic, copy) NSString *firstFlag;
@property(nonatomic, copy) NSString *token;
@property(nonatomic, copy) NSString *status;
@property(nonatomic, copy) NSString *deviceFlag;
@property(nonatomic, copy) NSString *phoneNum;
+ (instancetype)getModel;
@end
NS_ASSUME_NONNULL_END
//
// NRUserModel.m
// NetrainFrame
//
// Created by Gin on 2020/9/25.
//
#import "NRUserModel.h"
#import "NRBaseModelAgent.h"
@implementation NRUserModel
+ (instancetype)getModel
{
static NRUserModel *model = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
model = [[NRBaseModelAgent agent] readModelForClass:[self class]];
if (!model) {
model = [NRUserModel new];
}
});
return model;
}
@end
......@@ -11,6 +11,8 @@ NS_ASSUME_NONNULL_BEGIN
@interface NRLoginKeyRequest : NRBaseRequest
@property(nonatomic, copy) NSString *phoneNumber;
@end
NS_ASSUME_NONNULL_END
......@@ -10,6 +10,12 @@
@implementation NRLoginKeyRequest
-(NSString *)requestUrl{
return @"/login/genLoginKey";
return @"login/genLoginKey";
}
-(id)requestArgument{
return @{
@"phoneNum" : self.phoneNumber,
};
}
@end
//
// NRLoginPublicKeyRequest.h
// NetrainFrame
//
// Created by Gin on 2020/9/24.
//
#import "NRBaseRequest.h"
NS_ASSUME_NONNULL_BEGIN
@interface NRLoginPublicKeyRequest : NRBaseRequest
@property(nonatomic, copy) NSString *phoneNumber;
@end
NS_ASSUME_NONNULL_END
//
// NRLoginPublicKeyRequest.m
// NetrainFrame
//
// Created by Gin on 2020/9/24.
//
#import "NRLoginPublicKeyRequest.h"
@implementation NRLoginPublicKeyRequest
-(NSString *)requestUrl{
return @"login/getPublicKey";
}
-(id)requestArgument{
return @{
@"phoneNum" : self.phoneNumber,
};
}
@end
......@@ -10,6 +10,10 @@ NS_ASSUME_NONNULL_BEGIN
@interface NRPasswordLoginRequest : NRBaseRequest
@property(copy, nonatomic) NSDictionary *params;
-(void)saveModel;
@end
NS_ASSUME_NONNULL_END
......@@ -6,11 +6,22 @@
//
#import "NRPasswordLoginRequest.h"
#import "NRUserModel.h"
@implementation NRPasswordLoginRequest
-(NSString *)requestUrl{
return @"/login";
return @"login";
}
-(id)requestArgument{
return self.params;
}
-(void)saveModel{
NSArray *arr = [[NRBaseModelAgent agent] createModel:NRUserModel.class fromJson:self.httpModel.data];
NRUserModel *userModel = [arr objectAtIndex:0];
userModel.phoneNum = [self.params objectForKey:@"phoneNum"];
[[NRBaseModelAgent agent] writeModel:userModel];
}
@end
......@@ -10,7 +10,7 @@
@implementation NRVerifyCodeLoginRequest
-(NSString *)requestUrl{
return @"/login/validLogin";
return @"login/validLogin";
}
-(id)requestArgument{
......
......@@ -20,5 +20,5 @@
#import <YYModel.h>
#import <SDWebImage/UIImageView+WebCache.h>
#import <QMUIKit/QMUIKit.h>
#import <YYCategories.h>
#endif /* PrefixHeader_pch */
//
// NSObject+NRRSA.h
// NetrainFrame
//
// Created by Gin on 2020/9/24.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface NSString (NRRSA)
+(NSString*)formatPulicKeys:(NSString*)publicKey password:(NSString*)password;
@end
NS_ASSUME_NONNULL_END
//
// NSString+NRRSA.m
// NetrainFrame
//
// Created by Gin on 2020/9/24.
//
#import "NSString+NRRSA.h"
#import "BDRSACryptor.h"
#import "BDRSACryptorKeyPair.h"
#define X509PublicHeader @"-----BEGIN PUBLIC KEY-----"
#define X509PublicFooter @"-----END PUBLIC KEY-----"
@implementation NSString (NRRSA)
#pragma mark-导入公钥
+(NSString*)formatPulicKeys:(NSString*)publicKey password:(NSString*)password{
NSString *tempStr = [[[publicKey stringByReplacingOccurrencesOfString:@"\n"
withString:@""] stringByReplacingOccurrencesOfString:@" " withString:@""]stringByReplacingOccurrencesOfString:@"\r" withString:@""];
tempStr=[[NSString alloc] initWithFormat:@"%@\n%@\n%@",X509PublicHeader,tempStr,X509PublicFooter];
NSLog(@"加密前公钥=====%@",tempStr);
BDError *error = [[BDError alloc] init];
BDRSACryptor *RSACryptor = [[BDRSACryptor alloc] init];
NSString *cipherText =
[RSACryptor encrypt:password
key:tempStr
error:error];
NSLog(@"加密后公钥=====%@",cipherText);
return cipherText;
}
@end
//
// SFHFKeychainUtils.h
//
// Created by Buzz Andersen on 10/20/08.
// Based partly on code by Jonathan Wight, Jon Crosby, and Mike Malone.
// Copyright 2008 Sci-Fi Hi-Fi. All rights reserved.
//
// 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 <UIKit/UIKit.h>
@interface SFHFKeychainUtils : NSObject {
}
/**
* @brief 从Keychain里获取用户密码
*
* @param username 用户名
* @param serviceName App identifier
* @param error error
*
* @return 用户名对应的密码
*/
+ (NSString *)getPasswordForUsername:(NSString *)username andServiceName:(NSString *)serviceName error:(NSError **)error;
/**
* @brief 把用户的密码保存到Keychain里
*
* @param username 用户名
* @param password 要保存的密码
* @param serviceName App identifier
* @param updateExisting 是否覆盖已经写入到KeyChain里面值
* @param error error
*
* @return 是否存储成功
*/
+ (BOOL)storeUsername:(NSString *)username andPassword:(NSString *)password forServiceName:(NSString *)serviceName updateExisting:(BOOL)updateExisting error:(NSError **)error;
/**
* @brief 删除某个用户信息
*
* @param username 用户名
* @param serviceName App identifier
* @param error error
*
* @return 是否删除成功
*/
+ (BOOL)deleteItemForUsername:(NSString *)username andServiceName:(NSString *)serviceName error:(NSError **)error;
///**
// * @brief 查询所有的KeyChain 信息
// */
//+ (void) selectAllKeyChainInfo;
@end
\ No newline at end of file
//
// SFHFKeychainUtils.m
//
// Created by Buzz Andersen on 10/20/08.
// Based partly on code by Jonathan Wight, Jon Crosby, and Mike Malone.
// Copyright 2008 Sci-Fi Hi-Fi. All rights reserved.
//
// 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 "SFHFKeychainUtils.h"
#import <Security/Security.h>
static NSString *SFHFKeychainUtilsErrorDomain = @"SFHFKeychainUtilsErrorDomain";
#if __IPHONE_OS_VERSION_MIN_REQUIRED < 30000 && TARGET_IPHONE_SIMULATOR
@interface SFHFKeychainUtils (PrivateMethods)
+ (SecKeychainItemRef) getKeychainItemReferenceForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error;
@end
#endif
@implementation SFHFKeychainUtils
//+ (void)selectAllKeyChainInfo
//{
// NSDictionary* query = [NSDictionary dictionaryWithObjectsAndKeys:kSecClassGenericPassword,kSecClass,
// kSecMatchLimitAll,kSecMatchLimit,
// kCFBooleanTrue,kSecReturnAttributes,nil];
// CFTypeRef result = nil;
// OSStatus s = SecItemCopyMatching((CFDictionaryRef)query, &result);
// QLKDLog(@"se;ect all : %d",s);
// QLKDLog(@"%@",result);
//
//}
#if __IPHONE_OS_VERSION_MIN_REQUIRED < 30000 && TARGET_IPHONE_SIMULATOR
+ (NSString *) getPasswordForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error {
if (!username || !serviceName) {
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil];
return nil;
}
SecKeychainItemRef item = [SFHFKeychainUtils getKeychainItemReferenceForUsername: username andServiceName: serviceName error: error];
if (*error || !item) {
return nil;
}
// from Advanced Mac OS X Programming, ch. 16
UInt32 length;
char *password;
SecKeychainAttribute attributes[8];
SecKeychainAttributeList list;
attributes[0].tag = kSecAccountItemAttr;
attributes[1].tag = kSecDescriptionItemAttr;
attributes[2].tag = kSecLabelItemAttr;
attributes[3].tag = kSecModDateItemAttr;
list.count = 4;
list.attr = attributes;
OSStatus status = SecKeychainItemCopyContent(item, NULL, &list, &length, (void **)&password);
if (status != noErr) {
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
return nil;
}
NSString *passwordString = nil;
if (password != NULL) {
char passwordBuffer[1024];
if (length > 1023) {
length = 1023;
}
strncpy(passwordBuffer, password, length);
passwordBuffer[length] = '\0';
passwordString = [NSString stringWithCString:passwordBuffer];
}
SecKeychainItemFreeContent(&list, password);
CFRelease(item);
return passwordString;
}
+ (void) storeUsername: (NSString *) username andPassword: (NSString *) password forServiceName: (NSString *) serviceName updateExisting: (BOOL) updateExisting error: (NSError **) error {
if (!username || !password || !serviceName) {
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil];
return;
}
OSStatus status = noErr;
SecKeychainItemRef item = [SFHFKeychainUtils getKeychainItemReferenceForUsername: username andServiceName: serviceName error: error];
if (*error && [*error code] != noErr) {
return;
}
*error = nil;
if (item) {
status = SecKeychainItemModifyAttributesAndData(item,
NULL,
strlen([password UTF8String]),
[password UTF8String]);
CFRelease(item);
}
else {
status = SecKeychainAddGenericPassword(NULL,
strlen([serviceName UTF8String]),
[serviceName UTF8String],
strlen([username UTF8String]),
[username UTF8String],
strlen([password UTF8String]),
[password UTF8String],
NULL);
}
if (status != noErr) {
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
}
}
+ (void) deleteItemForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error {
if (!username || !serviceName) {
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: 2000 userInfo: nil];
return;
}
*error = nil;
SecKeychainItemRef item = [SFHFKeychainUtils getKeychainItemReferenceForUsername: username andServiceName: serviceName error: error];
if (*error && [*error code] != noErr) {
return;
}
OSStatus status;
if (item) {
status = SecKeychainItemDelete(item);
CFRelease(item);
}
if (status != noErr) {
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
}
}
+ (SecKeychainItemRef) getKeychainItemReferenceForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error {
if (!username || !serviceName) {
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil];
return nil;
}
*error = nil;
SecKeychainItemRef item;
OSStatus status = SecKeychainFindGenericPassword(NULL,
strlen([serviceName UTF8String]),
[serviceName UTF8String],
strlen([username UTF8String]),
[username UTF8String],
NULL,
NULL,
&item);
if (status != noErr) {
if (status != errSecItemNotFound) {
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
}
return nil;
}
return item;
}
#else
+ (NSString *) getPasswordForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error {
if (!username || !serviceName) {
if (error != nil) {
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil];
}
return nil;
}
if (error != nil) {
*error = nil;
}
// Set up a query dictionary with the base query attributes: item type (generic), username, and service
NSArray *keys = [[[NSArray alloc] initWithObjects: (NSString *) kSecClass, kSecAttrAccount, kSecAttrService, nil] autorelease];
NSArray *objects = [[[NSArray alloc] initWithObjects: (NSString *) kSecClassGenericPassword, username, serviceName, nil] autorelease];
NSMutableDictionary *query = [[[NSMutableDictionary alloc] initWithObjects: objects forKeys: keys] autorelease];
// First do a query for attributes, in case we already have a Keychain item with no password data set.
// One likely way such an incorrect item could have come about is due to the previous (incorrect)
// version of this code (which set the password as a generic attribute instead of password data).
NSDictionary *attributeResult = NULL;
NSMutableDictionary *attributeQuery = [query mutableCopy];
[attributeQuery setObject: (id) kCFBooleanTrue forKey:(id) kSecReturnAttributes];
OSStatus status = SecItemCopyMatching((CFDictionaryRef) attributeQuery, (CFTypeRef *) &attributeResult);
[attributeResult release];
[attributeQuery release];
if (status != noErr) {
// No existing item found--simply return nil for the password
if (error != nil && status != errSecItemNotFound) {
//Only return an error if a real exception happened--not simply for "not found."
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
}
return nil;
}
// We have an existing item, now query for the password data associated with it.
NSData *resultData = nil;
NSMutableDictionary *passwordQuery = [query mutableCopy];
[passwordQuery setObject: (id) kCFBooleanTrue forKey: (id) kSecReturnData];
status = SecItemCopyMatching((CFDictionaryRef) passwordQuery, (CFTypeRef *) &resultData);
[resultData autorelease];
[passwordQuery release];
if (status != noErr) {
if (status == errSecItemNotFound) {
// We found attributes for the item previously, but no password now, so return a special error.
// Users of this API will probably want to detect this error and prompt the user to
// re-enter their credentials. When you attempt to store the re-entered credentials
// using storeUsername:andPassword:forServiceName:updateExisting:error
// the old, incorrect entry will be deleted and a new one with a properly encrypted
// password will be added.
if (error != nil) {
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -1999 userInfo: nil];
}
}
else {
// Something else went wrong. Simply return the normal Keychain API error code.
if (error != nil) {
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
}
}
return nil;
}
NSString *password = nil;
if (resultData) {
password = [[NSString alloc] initWithData: resultData encoding: NSUTF8StringEncoding];
}
else {
// There is an existing item, but we weren't able to get password data for it for some reason,
// Possibly as a result of an item being incorrectly entered by the previous code.
// Set the -1999 error so the code above us can prompt the user again.
if (error != nil) {
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -1999 userInfo: nil];
}
}
return [password autorelease];
}
+ (BOOL) storeUsername: (NSString *) username andPassword: (NSString *) password forServiceName: (NSString *) serviceName updateExisting: (BOOL) updateExisting error: (NSError **) error
{
if (!username || !password || !serviceName)
{
if (error != nil)
{
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil];
}
return NO;
}
// See if we already have a password entered for these credentials.
NSError *getError = nil;
NSString *existingPassword = [SFHFKeychainUtils getPasswordForUsername: username andServiceName: serviceName error:&getError];
if ([getError code] == -1999)
{
// There is an existing entry without a password properly stored (possibly as a result of the previous incorrect version of this code.
// Delete the existing item before moving on entering a correct one.
getError = nil;
[self deleteItemForUsername: username andServiceName: serviceName error: &getError];
if ([getError code] != noErr)
{
if (error != nil)
{
*error = getError;
}
return NO;
}
}
else if ([getError code] != noErr)
{
if (error != nil)
{
*error = getError;
}
return NO;
}
if (error != nil)
{
*error = nil;
}
OSStatus status = noErr;
if (existingPassword)
{
// We have an existing, properly entered item with a password.
// Update the existing item.
if (![existingPassword isEqualToString:password] && updateExisting)
{
//Only update if we're allowed to update existing. If not, simply do nothing.
NSArray *keys = [[[NSArray alloc] initWithObjects: (NSString *) kSecClass,
kSecAttrService,
kSecAttrLabel,
kSecAttrAccount,
kSecAttrAccessible,
nil] autorelease];
NSArray *objects = [[[NSArray alloc] initWithObjects: (NSString *) kSecClassGenericPassword,
serviceName,
serviceName,
username,
kSecAttrAccessibleAfterFirstUnlock,
nil] autorelease];
NSDictionary *query = [[[NSDictionary alloc] initWithObjects: objects forKeys: keys] autorelease];
status = SecItemUpdate((CFDictionaryRef) query, (CFDictionaryRef) [NSDictionary dictionaryWithObject: [password dataUsingEncoding: NSUTF8StringEncoding] forKey: (NSString *) kSecValueData]);
}
}
else
{
// No existing entry (or an existing, improperly entered, and therefore now
// deleted, entry). Create a new entry.
NSArray *keys = [[[NSArray alloc] initWithObjects: (NSString *) kSecClass,
kSecAttrService,
kSecAttrLabel,
kSecAttrAccount,
kSecAttrAccessible,
kSecValueData,
nil] autorelease];
NSArray *objects = [[[NSArray alloc] initWithObjects: (NSString *) kSecClassGenericPassword,
serviceName,
serviceName,
username,
kSecAttrAccessibleAfterFirstUnlock,
[password dataUsingEncoding: NSUTF8StringEncoding],
nil] autorelease];
NSDictionary *query = [[[NSDictionary alloc] initWithObjects: objects forKeys: keys] autorelease];
status = SecItemAdd((CFDictionaryRef) query, NULL);
}
if (error != nil && status != noErr)
{
// Something went wrong with adding the new item. Return the Keychain error code.
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
return NO;
}
return YES;
}
+ (BOOL) deleteItemForUsername: (NSString *) username andServiceName: (NSString *) serviceName error: (NSError **) error
{
if (!username || !serviceName)
{
if (error != nil)
{
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: -2000 userInfo: nil];
}
return NO;
}
if (error != nil)
{
*error = nil;
}
NSArray *keys = [[[NSArray alloc] initWithObjects: (NSString *) kSecClass, kSecAttrAccount, kSecAttrService, kSecReturnAttributes, nil] autorelease];
NSArray *objects = [[[NSArray alloc] initWithObjects: (NSString *) kSecClassGenericPassword, username, serviceName, kCFBooleanTrue, nil] autorelease];
NSDictionary *query = [[[NSDictionary alloc] initWithObjects: objects forKeys: keys] autorelease];
OSStatus status = SecItemDelete((CFDictionaryRef) query);
if (error != nil && status != noErr)
{
*error = [NSError errorWithDomain: SFHFKeychainUtilsErrorDomain code: status userInfo: nil];
return NO;
}
return YES;
}
#endif
@end
\ No newline at end of file
//
// UIDevice+NRDevice.h
// NetrainFrame
//
// Created by Gin on 2020/9/24.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface UIDevice (NRDevice)
/**
* 同步唯一设备标识 (生成并保存唯一设备标识,如已存在则不进行任何处理)
*
* @return 是否成功
*/
+(BOOL)syncDeviceIdentifier;
/**
* 返回唯一设备标识
*
* @return 设备标识
*/
+(NSString*)deviceIdentifier;
/**
* 本应用是第一次安装
*
* @return 是否是第一次安装
*/
+(BOOL)isFirstInstall;
@end
NS_ASSUME_NONNULL_END
//
// UIDevice+NRDevice.m
// NetrainFrame
//
// Created by Gin on 2020/9/24.
//
#import "UIDevice+NRDevice.h"
#import "SFHFKeychainUtils.h"
#define bundleIdentifier [[NSBundle mainBundle]bundleIdentifier]
@implementation UIDevice (NRDevice)
/**
* 同步唯一设备标识 (生成并保存唯一设备标识,如已存在则不进行任何处理)
*
* @return 是否成功
*/
+(BOOL)syncDeviceIdentifier{
/**
* 获取应用的UUID标识
* (
* identifierForVendor返回本应用的UUID, 卸载重装后会变.所以要存入钥匙串
* 此处可用 [[NSUUID UUID]UUIDString] 代替, [NSUUID UUID]方法每次调用都会生成一个不同的UUID
* 但是identifierForVendor可以用来验证是不是第一次安装
* )
*/
NSString *myUUIDStr = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
/**
* 保存UUID到钥匙串Keychain, 如果已存在则不保存
* storeUsername:键
* Password:值
* ServiceName:组名
* updateExisting:更新已存在的
*/
BOOL f = [SFHFKeychainUtils storeUsername:@"deviceIdentifier" andPassword:myUUIDStr forServiceName:bundleIdentifier updateExisting:NO error:nil];
return f;
}
/**
* 返回唯一设备标识
*
* @return 设备标识
*/
+(NSString*)deviceIdentifier{
//先同步一下, 防止设备标识还未存在的情况
[self syncDeviceIdentifier];
//从钥匙串中获取唯一设备标识
NSString * deviceIdentifier = [SFHFKeychainUtils getPasswordForUsername:@"deviceIdentifier" andServiceName:bundleIdentifier error:nil];
return deviceIdentifier;
}
/**
* 本应用是第一次安装
*
* @return 是否是第一次安装
*/
+(BOOL)isFirstInstall{
NSString * deviceIdentifier = [UIDevice deviceIdentifier];
NSString * identifierForVendor = [[[UIDevice currentDevice] identifierForVendor] UUIDString];
/**
* 如果钥匙串中存的deviceIdentifier(设备标识)不存在 或者 等于deviceIdentifier(本应用的UUID) , 则为第一次安装
*/
if ( !deviceIdentifier || [deviceIdentifier isEqualToString:identifierForVendor]) {
return YES;
}else{
return NO;
}
}
@end
......@@ -12,7 +12,8 @@ pod 'MJRefresh','3.4.3'
pod 'YTKNetwork','3.0.2'
pod 'QMUIKit','4.2.0'
pod "CTMediator",'44'
pod 'YYCategories','1.0.4'
pod 'RSA', '1.0.1'
end
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment