This commit is contained in:
Yao
2024-12-20 17:49:45 +08:00
parent 86b0363ce1
commit 654d456c7d
7011 changed files with 1705926 additions and 7 deletions

26
ShenQi/AppDelegate.h Normal file
View File

@@ -0,0 +1,26 @@
//
// AppDelegate.h
// ShenQi
//
// Created by Yao on 2023/4/4.
//
#import <UIKit/UIKit.h>
#import "ConfigData.h"
@interface AppDelegate : UIResponder <UIApplicationDelegate>
@property (nonatomic, strong) UIWindow *window;
@property (nonatomic, strong) ConfigData *data;
@property (nonatomic, assign) BOOL isRefresh;
@property (nonatomic, assign) BOOL forceUpdate;
@property (nonatomic, strong) NSString *downloadUrl;
@property (nonatomic, strong) NSString *inviteCode;
-(void)applicationDidBecomeActive:(UIApplication *)application;
@end

478
ShenQi/AppDelegate.m Normal file
View File

@@ -0,0 +1,478 @@
//
// AppDelegate.m
// ShenQi
//
// Created by Yao on 2023/4/4.
//
#import "AppDelegate.h"
#import <UserNotifications/UserNotifications.h>
#import <FirebaseCore.h>
#import <FirebaseMessaging.h>
#import <Contacts/Contacts.h>
#import <AdSupport/AdSupport.h>
#import <AppTrackingTransparency/AppTrackingTransparency.h>
#import <SVProgressHUD.h>
#import "MacroDefine.h"
//#import "ViewController.h"
#import "MainOldViewController.h"
#import "LaunchViewController.h"
@interface AppDelegate () <FIRMessagingDelegate,UNUserNotificationCenterDelegate>
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window.overrideUserInterfaceStyle = UIUserInterfaceStyleDark;
[UIApplication sharedApplication].applicationIconBadgeNumber = 0;
[FIRApp configure];
[FIRMessaging messaging].delegate = self;
[SVProgressHUD setMaximumDismissTimeInterval:1.5];
[SVProgressHUD setMinimumDismissTimeInterval:1.5];
[SVProgressHUD setHapticsEnabled:NO];
[SVProgressHUD setDefaultMaskType:(SVProgressHUDMaskTypeBlack)];
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
center.delegate = self;
[center requestAuthorizationWithOptions:(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge) completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (!error && granted) {
//
dispatch_async(dispatch_get_main_queue(), ^{
[[UIApplication sharedApplication] registerForRemoteNotifications];
});
}else{
//
}
}];
//
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:AppUseURL]];
request.HTTPMethod = @"PUT";
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
NSDictionary *parmas = @{@"userId":[NSNumber numberWithInteger:[PPN88UserID integerValue]]};
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:parmas options:NSJSONWritingPrettyPrinted error:nil];
request.HTTPBody = jsonData;
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
}];
[dataTask resume];
return YES;
}
-(void)applicationDidBecomeActive:(UIApplication *)application
{
//
if (self.data.contactApplyMode == 1) {
CNAuthorizationStatus status = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts];
if (status == CNAuthorizationStatusNotDetermined) {
CNContactStore *store = [[CNContactStore alloc] init];
[store requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError* _Nullable error) {
if (granted == NO) {
[self setupAlertContact];
}
}];
}else if (status == CNAuthorizationStatusDenied) {
[self setupAlertContact];
}
}
//
if (self.data.noticeApplyMode == 1) {
[[UNUserNotificationCenter currentNotificationCenter] getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
//
if (settings.authorizationStatus == UNAuthorizationStatusNotDetermined) {
[self setupNotificationStatus];
}else if (settings.authorizationStatus != UNAuthorizationStatusAuthorized) {
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertController *alertview = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Tips", nil) message:NSLocalizedString(@"Need to enable push permission", nil) preferredStyle:(UIAlertControllerStyleAlert)];
UIAlertAction *action = [UIAlertAction actionWithTitle:NSLocalizedString(@"SETTING", nil) style:(UIAlertActionStyleCancel) handler:^(UIAlertAction * _Nonnull action) {
self.isRefresh = YES;
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString] options:@{} completionHandler:^(BOOL success) {}];
}];
[alertview addAction:action];
[self.window.rootViewController presentViewController:alertview animated:YES completion:^{}];
});
}
}];
}
if (self.forceUpdate == YES) {
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertController *alertview = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Tips", nil) message:NSLocalizedString(@"New version found", nil) preferredStyle:(UIAlertControllerStyleAlert)];
UIAlertAction *action = [UIAlertAction actionWithTitle:NSLocalizedString(@"Update", nil) style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
self.isRefresh = NO;
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:self.downloadUrl] options:@{} completionHandler:^(BOOL success) {}];
}];
[alertview addAction:action];
[self.window.rootViewController presentViewController:alertview animated:YES completion:^{}];
});
}
//
// NSUserDefaults *userfault = [NSUserDefaults standardUserDefaults];
// BOOL install = [userfault objectForKey:@"INSTALL"];
// if (install == NO) {
// [self setupSendInvite];
// }else{
// if (self.isRefresh == YES) {
// [[NSNotificationCenter defaultCenter] postNotificationName:@"NOTI_INTO" object:nil];
// }
// }
}
#pragma mark -
-(void)setupNotificationStatus
{
[[UNUserNotificationCenter currentNotificationCenter] getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
//
if (settings.authorizationStatus == UNAuthorizationStatusNotDetermined) {
[self setupNotificationStatus];
}else if (settings.authorizationStatus != UNAuthorizationStatusAuthorized) {
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertController *alertview = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Tips", nil) message:NSLocalizedString(@"Need to enable push permission", nil) preferredStyle:(UIAlertControllerStyleAlert)];
UIAlertAction *action = [UIAlertAction actionWithTitle:NSLocalizedString(@"SETTING", nil) style:(UIAlertActionStyleCancel) handler:^(UIAlertAction * _Nonnull action) {
self.isRefresh = YES;
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString] options:@{} completionHandler:^(BOOL success) {}];
}];
[alertview addAction:action];
[self.window.rootViewController presentViewController:alertview animated:YES completion:^{}];
});
}
}];
}
#pragma mark -
-(void)setupAlertContact
{
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Tips", nil) message:NSLocalizedString(@"Contacts permission must be turned on", nil) preferredStyle:(UIAlertControllerStyleAlert)];
UIAlertAction *action = [UIAlertAction actionWithTitle:NSLocalizedString(@"SETTING", nil) style:(UIAlertActionStyleCancel) handler:^(UIAlertAction * _Nonnull action) {
self.isRefresh = YES;
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString] options:@{} completionHandler:^(BOOL success) {}];
}];
[alertController addAction:action];
[self.window.rootViewController presentViewController:alertController animated:YES completion:^{}];
});
}
/*
-(void)refreshPermissionAll
{
[SVProgressHUD dismiss];
//
[[UNUserNotificationCenter currentNotificationCenter] getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
//
if (settings.authorizationStatus == UNAuthorizationStatusNotDetermined) {
[self setupNotificationStatus];
}else if (settings.authorizationStatus != UNAuthorizationStatusAuthorized) {
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertController *alertview = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Tips", nil) message:NSLocalizedString(@"Need to enable push permission", nil) preferredStyle:(UIAlertControllerStyleAlert)];
UIAlertAction *action = [UIAlertAction actionWithTitle:NSLocalizedString(@"SETTING", nil) style:(UIAlertActionStyleCancel) handler:^(UIAlertAction * _Nonnull action) {
self.isRefresh = YES;
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString] options:@{} completionHandler:^(BOOL success) {}];
}];
[alertview addAction:action];
[self.window.rootViewController presentViewController:alertview animated:YES completion:^{}];
});
}
}];
//
CNAuthorizationStatus status = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts];
if (status == CNAuthorizationStatusNotDetermined) {
CNContactStore *store = [[CNContactStore alloc] init];
[store requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError* _Nullable error) {
if (granted == NO) {
[self setupAlertContact];
}
}];
}else if (status == CNAuthorizationStatusDenied) {
[self setupAlertContact];
}
//
NSUserDefaults *userfault = [NSUserDefaults standardUserDefaults];
BOOL install = [userfault objectForKey:@"INSTALL"];
if (install == NO) {
[self setupSendInvite];
}else{
[[NSNotificationCenter defaultCenter] postNotificationName:@"NOTI_INTO" object:nil];
}
}
#pragma mark -
-(void)setupSendInvite
{
//
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertController *inputCodeAlertController = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Please enter the invitation code", nil) message:nil preferredStyle:(UIAlertControllerStyleAlert)];
[inputCodeAlertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
textField.placeholder = NSLocalizedString(@"Please enter the invitation code", nil);
}];
UIAlertAction *inputCodeAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil) style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
UITextField *codeTextField = inputCodeAlertController.textFields.firstObject;
if (!ISNULLSTR(codeTextField.text)) {
NSString *advertisingId = codeTextField.text;
[self setupInviteSend:advertisingId];
}else{
[self setupSendInvite];
}
}];
[inputCodeAlertController addAction:inputCodeAction];
[self.window.rootViewController presentViewController:inputCodeAlertController animated:YES completion:^{}];
});
}
-(void)setupInviteSend:(NSString *)advertisingId
{
__block NSString *uuidString = @"";
if (@available(iOS 14, *)) {
[ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus status) {
uuidString = [[ASIdentifierManager sharedManager] advertisingIdentifier].UUIDString;
[self setupInviteSendWithUUIDString:uuidString advertisingId:advertisingId];
}];
} else {
uuidString = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
[self setupInviteSendWithUUIDString:uuidString advertisingId:advertisingId];
}
}
-(void)setupInviteSendWithUUIDString:(NSString *)uuidString advertisingId:(NSString *)advertisingId
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:InviteSendURL]];
request.HTTPMethod = @"PUT";
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
NSDictionary *parmas = @{@"code":advertisingId,@"deviceCode":uuidString,@"userId":[NSNumber numberWithInteger:[VV88AUUserID integerValue]]};
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:parmas options:NSJSONWritingPrettyPrinted error:nil];
request.HTTPBody = jsonData;
[SVProgressHUD show];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error == nil) {
[SVProgressHUD dismiss];
NSError *error;
NSMutableDictionary *responseObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
if (error == nil) {
NSInteger code = [[responseObject objectForKey:@"code"] integerValue];
if (ISNULL(error)) {
if (code == 40001) {
NSString *errmsg = [responseObject objectForKey:@"error"];
[SVProgressHUD showInfoWithStatus:errmsg];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSDictionary *datadic = [responseObject objectForKey:@"data"];
NSString *inviteCode = [datadic objectForKey:@"inviteCode"];
NSUserDefaults *userfault = [NSUserDefaults standardUserDefaults];
[userfault setObject:[NSNumber numberWithBool:YES] forKey:@"INSTALL"];
[userfault setObject:inviteCode forKey:@"INVITE_CODE"];
[userfault synchronize];
//
[self refreshPermissionAll];
});
}else if (code == 1) {
NSUserDefaults *userfault = [NSUserDefaults standardUserDefaults];
[userfault setObject:[NSNumber numberWithBool:YES] forKey:@"INSTALL"];
[userfault setObject:advertisingId forKey:@"INVITE_CODE"];
[userfault synchronize];
//
[self refreshPermissionAll];
}else{
NSString *errmsg = [responseObject objectForKey:@"error"];
[SVProgressHUD showInfoWithStatus:errmsg];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self setupSendInvite];
});
}
}else{
NSString *errmsg = [responseObject objectForKey:@"message"];
[SVProgressHUD showInfoWithStatus:errmsg];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self setupSendInvite];
});
}
}
else{
[SVProgressHUD showInfoWithStatus:error.localizedDescription];
[self setupSendInvite];
}
}
else{
[SVProgressHUD showInfoWithStatus:error.localizedDescription];
[self setupSendInvite];
}
});
}];
[dataTask resume];
}
*/
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
if (deviceToken != nil) {
[[FIRMessaging messaging] setAPNSToken:deviceToken];
[[FIRMessaging messaging] subscribeToTopic:@"demo"
completion:^(NSError * _Nullable error) {
NSLog(@"Subscribed to demo topic");
}];
NSLog(@"APNSToken = %@",[FIRMessaging messaging].APNSToken);
}
}
-(UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window
{
return UIInterfaceOrientationMaskAll;
}
- (void)messaging:(FIRMessaging *)messaging didReceiveRegistrationToken:(NSString *)fcmToken
{
NSLog(@"FCM registration token: %@", fcmToken);
[[FIRMessaging messaging] tokenWithCompletion:^(NSString *token, NSError *error) {
if (error != nil) {
NSLog(@"Error getting FCM registration token: %@", error);
} else {
NSLog(@"FCM registration token: %@", token);
}
}];
}
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
{
completionHandler(UNNotificationPresentationOptionSound|UNNotificationPresentationOptionAlert);
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
[self processWithNoti:userInfo];
}
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler
{
[self processWithNoti:response.notification.request.content.userInfo];
}
/*
-(void)receiveNOTIProcessWithNoti:(NSDictionary *)userInfo
{
[[FIRMessaging messaging] appDidReceiveMessage:userInfo];
if (userInfo) {
NSDictionary *apnsDic = [userInfo objectForKey:@"aps"];
NSDictionary *alertDic = [apnsDic objectForKey:@"alert"];
NSString *title = [alertDic objectForKey:@"body"];
NSString *content = [alertDic objectForKey:@"title"];
[self scheduleLocalNotification:userInfo title:title content:content];
}
}
- (void)scheduleLocalNotification:(NSDictionary *)userInfo title:(NSString *)title content:(NSString *)content
{
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
center.delegate = self;
UNMutableNotificationContent *notificationContent = [[UNMutableNotificationContent alloc] init];
notificationContent.title = title;
notificationContent.subtitle = content;
notificationContent.sound = [UNNotificationSound defaultSound];
UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:@"LOCAL_NOTI" content:notificationContent trigger:nil];
[center addNotificationRequest:request withCompletionHandler:^(NSError *_Nullable error) {
if (!error) {
[self processWithNoti:userInfo];
}
}];
}
*/
-(void)processWithNoti:(NSDictionary *)userInfo
{
NSString *message = [userInfo objectForKey:@"message"];
NSData *jsonData = [message dataUsingEncoding:NSUTF8StringEncoding];
NSDictionary *messageNotiDic = nil;
if (!ISNULL(jsonData)) {
NSError *err;
messageNotiDic = [NSJSONSerialization JSONObjectWithData:jsonData
options:NSJSONReadingMutableContainers
error:&err];
if (!ISNULL(messageNotiDic)) {
NSInteger type = [[messageNotiDic objectForKey:@"type"] integerValue];// 1 2 3
NSString *image = [messageNotiDic objectForKey:@"image"];
NSString *jumpUrl = [messageNotiDic objectForKey:@"jumpUrl"];
NSInteger pushId = [[messageNotiDic objectForKey:@"pushId"] longValue];
// pushId
if (pushId > 0) {
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:PushStatisticsURL]];
request.HTTPMethod = @"POST";
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
NSDictionary *parmas = @{@"pushId":[NSNumber numberWithLong:pushId]};
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:parmas options:NSJSONWritingPrettyPrinted error:nil];
request.HTTPBody = jsonData;
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
}];
[dataTask resume];
}
if (type == 2 && !ISNULLSTR(image)) {
dispatch_async(dispatch_get_main_queue(), ^{
NSURL *url = [NSURL URLWithString:image];
NSData *data = [NSData dataWithContentsOfURL:url];
UIView *bgView = [[UIView alloc] initWithFrame:self.window.bounds];
bgView.backgroundColor = [UIColor colorWithWhite:0 alpha:0.5];
UIImageView *imageview = [[UIImageView alloc] initWithFrame:CGRectMake(20, 80, bgView.frame.size.width-40, bgView.frame.size.height-160)];
imageview.image = [UIImage imageWithData:data];
imageview.contentMode = UIViewContentModeScaleAspectFill;
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapImageView:)];
[imageview addGestureRecognizer:tap];
[self.window addSubview:bgView];
[bgView addSubview:imageview];
});
}else if (type == 3 && !ISNULLSTR(jumpUrl)) {
dispatch_async(dispatch_get_main_queue(), ^{
MainOldViewController *view = [[MainOldViewController alloc] init];
view.gameURLString = jumpUrl;
view.isPresent = YES;
view.modalPresentationStyle = UIModalPresentationFullScreen;
[self.window.rootViewController presentViewController:view animated:YES completion:^{}];
});
}
}
}
}
-(void)tapImageView:(UITapGestureRecognizer *)tap
{
[tap.view removeFromSuperview];
}
@end

View File

@@ -0,0 +1,18 @@
//
// AreaCodeTableViewCell.h
// ShenQi
//
// Created by Yao on 2024/11/9.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface AreaCodeTableViewCell : UITableViewCell
@property (weak, nonatomic) IBOutlet UILabel *titleLabel;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,25 @@
//
// AreaCodeTableViewCell.m
// ShenQi
//
// Created by Yao on 2024/11/9.
//
#import "AreaCodeTableViewCell.h"
@implementation AreaCodeTableViewCell
- (void)awakeFromNib {
[super awakeFromNib];
// Initialization code
[self setSelectionStyle:(UITableViewCellSelectionStyleNone)];
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
@end

View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="22155" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22131"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="KGk-i7-Jjw" customClass="AreaCodeTableViewCell">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="TLN-RP-qir">
<rect key="frame" x="20" y="11.666666666666664" width="42" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="1" green="0.65490196079999996" blue="0.1333333333" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" red="0.17254901959999999" green="0.17254901959999999" blue="0.18039215689999999" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstItem="TLN-RP-qir" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="20" id="C0V-Hb-LWQ"/>
<constraint firstItem="TLN-RP-qir" firstAttribute="centerY" secondItem="H2p-sc-9uM" secondAttribute="centerY" id="ThY-bs-ntC"/>
</constraints>
</tableViewCellContentView>
<viewLayoutGuide key="safeArea" id="aW0-zy-SZf"/>
<color key="backgroundColor" red="0.17254901959999999" green="0.17254901959999999" blue="0.18039215689999999" alpha="1" colorSpace="calibratedRGB"/>
<connections>
<outlet property="titleLabel" destination="TLN-RP-qir" id="L2J-gI-TeQ"/>
</connections>
<point key="canvasLocation" x="139" y="20"/>
</tableViewCell>
</objects>
</document>

View File

@@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,14 @@
{
"images" : [
{
"filename" : "PPN88.png",
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 382 KiB

View File

@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "PPN88.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 382 KiB

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "caidan.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "candy916.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "facebook.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "goback.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "line.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "menu@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 459 KiB

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "shuaxin.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "telegram.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "weibiaoti.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "whatsapp.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "winway.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 406 KiB

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "winwaymenu.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "xialasanjiao.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 893 B

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "zhongxindakai.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

38
ShenQi/BankView.h Normal file
View File

@@ -0,0 +1,38 @@
//
// BankView.h
// ShenQi
//
// Created by Yao on 2024/11/9.
//
#import <UIKit/UIKit.h>
#import "DataModels.h"
typedef NS_ENUM(NSInteger, ChooseType) {
ChooseBankCodeType, //
ChooseBankNameType, //
};
NS_ASSUME_NONNULL_BEGIN
typedef void(^SelectedCompleteBlock)(id code);
@interface BankView : UIView <UITableViewDelegate,UITableViewDataSource>
@property (weak, nonatomic) IBOutlet UIView *mainView;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *bottomConstraint;
@property (weak, nonatomic) IBOutlet UILabel *titleLabel;
@property (weak, nonatomic) IBOutlet UITableView *bankTableView;
@property (nonatomic, copy) SelectedCompleteBlock selectedCompleteBlock;
@property (nonatomic, assign) ChooseType chooseType;
@property (nonatomic, strong) NSString *currentBankCode;
@property (nonatomic, strong) NSDictionary *bankdic;
@property (nonatomic, strong) NSMutableArray *bankArray;
@end
NS_ASSUME_NONNULL_END

113
ShenQi/BankView.m Normal file
View File

@@ -0,0 +1,113 @@
//
// BankView.m
// ShenQi
//
// Created by Yao on 2024/11/9.
//
#import "BankView.h"
#import "MacroDefine.h"
#import "AreaCodeTableViewCell.h"
static NSString *identifier = @"AreaCodeTableViewCell";
@implementation BankView
-(void)awakeFromNib
{
[super awakeFromNib];
self.titleLabel.text = [NSString stringWithFormat:@"%@%@",NSLocalizedString(@"PLS", nil),NSLocalizedString(@"Choose", nil)];
self.bottomConstraint.constant = 0;
[UIView animateWithDuration:0.25 animations:^{
self.backgroundColor = [UIColor colorWithWhite:0 alpha:0.5];
[self layoutIfNeeded];
}];
UIBezierPath *maskPath = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0, SCREEN_WIDTH, 400) byRoundingCorners:UIRectCornerTopLeft | UIRectCornerTopRight cornerRadii:CGSizeMake(12,12)];
CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
maskLayer.frame = CGRectMake(0, 0, SCREEN_WIDTH, 400);
maskLayer.path = maskPath.CGPath;
self.mainView.layer.mask = maskLayer;
self.bankTableView.delegate = self;
self.bankTableView.dataSource = self;
self.bankTableView.rowHeight = 50;
[self.bankTableView registerNib:[UINib nibWithNibName:@"AreaCodeTableViewCell" bundle:nil] forCellReuseIdentifier:identifier];
[self.bankTableView setSeparatorStyle:(UITableViewCellSeparatorStyleSingleLine)];
[self.bankTableView setSeparatorInset:UIEdgeInsetsMake(0, 0, 0, 0)];
self.bankTableView.tableFooterView = [UIView new];
}
- (IBAction)dismissAction:(id)sender
{
self.bottomConstraint.constant = -400;
[UIView animateWithDuration:0.25 animations:^{
[self layoutIfNeeded];
} completion:^(BOOL finished) {
[self removeFromSuperview];
}];
}
-(void)setBankdic:(NSDictionary *)bankdic
{
_bankdic = bankdic;
if (self.chooseType == ChooseBankCodeType) {
self.bankArray = @[].mutableCopy;
[self.bankArray addObjectsFromArray:self.bankdic.allKeys];
[self.bankTableView reloadData];
}
}
-(void)setCurrentBankCode:(NSString *)currentBankCode
{
_currentBankCode = currentBankCode;
if (self.chooseType == ChooseBankNameType && !ISNULLSTR(self.currentBankCode) && !ISNULL(self.bankdic)) {
self.bankArray = @[].mutableCopy;
NSArray *bankArray = [self.bankdic objectForKey:self.currentBankCode];
for (NSDictionary *bankdic in bankArray) {
BankModelData *bank = [[BankModelData alloc] initWithDictionary:bankdic];
[self.bankArray addObject:bank];
}
[self.bankTableView reloadData];
}
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.bankArray.count;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
AreaCodeTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (self.chooseType == ChooseBankNameType) {
BankModelData *data = [self.bankArray objectAtIndex:indexPath.row];
cell.titleLabel.text = data.bankName;
}else{
cell.titleLabel.text = [self.bankArray objectAtIndex:indexPath.row];
}
return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (self.chooseType == ChooseBankNameType) {
BankModelData *data = [self.bankArray objectAtIndex:indexPath.row];
if (self.selectedCompleteBlock) {
self.selectedCompleteBlock(data);
}
}else{
NSString *code = [self.bankArray objectAtIndex:indexPath.row];
if (self.selectedCompleteBlock) {
self.selectedCompleteBlock(code);
}
}
[self dismissAction:nil];
}
@end

71
ShenQi/BankView.xib Normal file
View File

@@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="22155" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22131"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB" customClass="BankView">
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="t8f-BG-Vzb">
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal">
<color key="titleColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</state>
<connections>
<action selector="dismissAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="7MX-iT-sAu"/>
</connections>
</button>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="RSo-3d-7oa">
<rect key="frame" x="0.0" y="852" width="393" height="400"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="请选择国家地区" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Ick-K1-kEm">
<rect key="frame" x="136" y="20" width="121.33333333333331" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/>
</label>
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="-1" estimatedSectionHeaderHeight="-1" sectionFooterHeight="-1" estimatedSectionFooterHeight="-1" translatesAutoresizingMaskIntoConstraints="NO" id="r0P-rW-aqh">
<rect key="frame" x="0.0" y="61" width="393" height="339"/>
<color key="backgroundColor" red="0.17254901959999999" green="0.17254901959999999" blue="0.18039215689999999" alpha="1" colorSpace="calibratedRGB"/>
</tableView>
</subviews>
<color key="backgroundColor" red="0.17254901959999999" green="0.17254901959999999" blue="0.18039215689999999" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstItem="Ick-K1-kEm" firstAttribute="centerX" secondItem="RSo-3d-7oa" secondAttribute="centerX" id="HJR-vh-rDi"/>
<constraint firstAttribute="bottom" secondItem="r0P-rW-aqh" secondAttribute="bottom" id="HOS-gx-zBk"/>
<constraint firstItem="Ick-K1-kEm" firstAttribute="top" secondItem="RSo-3d-7oa" secondAttribute="top" constant="20" id="ZNl-9z-z2E"/>
<constraint firstItem="r0P-rW-aqh" firstAttribute="top" secondItem="Ick-K1-kEm" secondAttribute="bottom" constant="20" id="cb6-0J-zjE"/>
<constraint firstItem="r0P-rW-aqh" firstAttribute="width" secondItem="RSo-3d-7oa" secondAttribute="width" id="gxp-ZS-n2H"/>
<constraint firstAttribute="height" constant="400" id="hM4-Tb-ODH"/>
<constraint firstItem="r0P-rW-aqh" firstAttribute="centerX" secondItem="RSo-3d-7oa" secondAttribute="centerX" id="xcg-zW-sKs"/>
</constraints>
</view>
</subviews>
<constraints>
<constraint firstItem="RSo-3d-7oa" firstAttribute="width" secondItem="iN0-l3-epB" secondAttribute="width" id="Gji-Wf-AeP"/>
<constraint firstItem="t8f-BG-Vzb" firstAttribute="centerX" secondItem="iN0-l3-epB" secondAttribute="centerX" id="Pgp-eJ-elg"/>
<constraint firstItem="t8f-BG-Vzb" firstAttribute="height" secondItem="iN0-l3-epB" secondAttribute="height" id="Rdv-af-t2d"/>
<constraint firstItem="RSo-3d-7oa" firstAttribute="centerX" secondItem="iN0-l3-epB" secondAttribute="centerX" id="YoS-ht-Y4q"/>
<constraint firstItem="t8f-BG-Vzb" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="centerY" id="bvq-I6-DuQ"/>
<constraint firstAttribute="bottom" secondItem="RSo-3d-7oa" secondAttribute="bottom" constant="-400" id="kJH-o8-SaK"/>
<constraint firstItem="t8f-BG-Vzb" firstAttribute="width" secondItem="iN0-l3-epB" secondAttribute="width" id="tq0-lr-KrS"/>
</constraints>
<connections>
<outlet property="bankTableView" destination="r0P-rW-aqh" id="7ay-2d-uRe"/>
<outlet property="bottomConstraint" destination="kJH-o8-SaK" id="vlb-Md-tpg"/>
<outlet property="mainView" destination="RSo-3d-7oa" id="XA7-jR-B1T"/>
<outlet property="titleLabel" destination="Ick-K1-kEm" id="qX5-R1-9dI"/>
</connections>
<point key="canvasLocation" x="140" y="20"/>
</view>
</objects>
</document>

View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="22155" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22131"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="PPN88" translatesAutoresizingMaskIntoConstraints="NO" id="wWz-RR-gyr">
<rect key="frame" x="96.666666666666686" y="298.66666666666669" width="200" height="200"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" secondItem="wWz-RR-gyr" secondAttribute="height" multiplier="1:1" id="N0J-q8-ZKK"/>
<constraint firstAttribute="width" constant="200" id="gfW-ba-a5X"/>
</constraints>
</imageView>
</subviews>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
<color key="backgroundColor" red="0.98781532049999998" green="1" blue="0.99589437250000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="wWz-RR-gyr" firstAttribute="centerY" secondItem="6Tk-OE-BBY" secondAttribute="centerY" constant="-40" id="CqV-4e-F3I"/>
<constraint firstItem="wWz-RR-gyr" firstAttribute="centerX" secondItem="6Tk-OE-BBY" secondAttribute="centerX" id="FHK-Fd-BPL"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="PPN88" width="1024" height="1024"/>
</resources>
</document>

View File

@@ -0,0 +1,153 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="22155" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="CLX-jd-tRb">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22131"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController storyboardIdentifier="ViewController" useStoryboardIdentifierAsRestorationIdentifier="YES" id="BYZ-38-t0r" customClass="ViewController" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="9sI-nt-sPs">
<rect key="frame" x="150.66666666666666" y="399" width="92" height="54.333333333333314"/>
<state key="normal" title="Button"/>
<buttonConfiguration key="configuration" style="tinted" image="shuaxin" imagePlacement="top" title="进入App"/>
<connections>
<action selector="intoGameAction:" destination="BYZ-38-t0r" eventType="touchUpInside" id="g2E-1A-Uc1"/>
</connections>
</button>
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" animating="YES" style="medium" translatesAutoresizingMaskIntoConstraints="NO" id="wDT-DT-yPP">
<rect key="frame" x="186.66666666666666" y="416" width="20" height="20"/>
</activityIndicatorView>
</subviews>
<viewLayoutGuide key="safeArea" id="s8A-Hx-U8O"/>
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="9sI-nt-sPs" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="OwP-N9-T7E"/>
<constraint firstItem="wDT-DT-yPP" firstAttribute="centerY" secondItem="8bC-Xf-vdC" secondAttribute="centerY" id="ccY-HQ-XLn"/>
<constraint firstItem="wDT-DT-yPP" firstAttribute="centerX" secondItem="8bC-Xf-vdC" secondAttribute="centerX" id="gOD-bI-qCF"/>
<constraint firstItem="9sI-nt-sPs" firstAttribute="centerY" secondItem="8bC-Xf-vdC" secondAttribute="centerY" id="gkX-0j-pN6"/>
</constraints>
</view>
<connections>
<outlet property="activityIndicatorView" destination="wDT-DT-yPP" id="Tm5-SJ-0lI"/>
<outlet property="goIntoBtn" destination="9sI-nt-sPs" id="FJ7-H6-iJC"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="133.58778625954199" y="3.5211267605633805"/>
</scene>
<!--Main Old View Controller-->
<scene sceneID="Hwl-qq-Q04">
<objects>
<viewController storyboardIdentifier="MainOldViewController" useStoryboardIdentifierAsRestorationIdentifier="YES" id="IMS-fv-0OO" customClass="MainOldViewController" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ys6-Sp-lCM">
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="aS0-9b-Vdi">
<rect key="frame" x="150.66666666666666" y="399" width="92" height="54.333333333333314"/>
<state key="normal" title="Button"/>
<buttonConfiguration key="configuration" style="tinted" image="shuaxin" imagePlacement="top" title="进入App"/>
<connections>
<action selector="intoGameAction:" destination="IMS-fv-0OO" eventType="touchUpInside" id="eFh-NV-HoP"/>
</connections>
</button>
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" animating="YES" style="medium" translatesAutoresizingMaskIntoConstraints="NO" id="kot-ab-erv">
<rect key="frame" x="186.66666666666666" y="416" width="20" height="20"/>
</activityIndicatorView>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="jWy-uW-8Li" customClass="DSWebDragView">
<rect key="frame" x="0.0" y="271.66666666666669" width="60" height="60"/>
<constraints>
<constraint firstAttribute="height" constant="60" id="XG1-uZ-BDG"/>
<constraint firstAttribute="width" secondItem="jWy-uW-8Li" secondAttribute="height" multiplier="1:1" id="yzH-EX-jDa"/>
</constraints>
</view>
<collectionView hidden="YES" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" dataMode="prototypes" translatesAutoresizingMaskIntoConstraints="NO" id="rsS-Qz-Fg1" customClass="DSWebMenuCollectionView">
<rect key="frame" x="0.0" y="271.66666666666669" width="60" height="334.00000000000006"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstAttribute="width" constant="60" id="gU5-UR-beg"/>
<constraint firstAttribute="height" constant="334" id="seP-m5-Lwg"/>
</constraints>
<collectionViewFlowLayout key="collectionViewLayout" minimumLineSpacing="10" minimumInteritemSpacing="10" id="8Dp-df-goj">
<size key="itemSize" width="128" height="128"/>
<size key="headerReferenceSize" width="0.0" height="0.0"/>
<size key="footerReferenceSize" width="0.0" height="0.0"/>
<inset key="sectionInset" minX="0.0" minY="0.0" maxX="0.0" maxY="0.0"/>
</collectionViewFlowLayout>
</collectionView>
</subviews>
<viewLayoutGuide key="safeArea" id="fdh-Xv-lcd"/>
<color key="backgroundColor" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="rsS-Qz-Fg1" firstAttribute="leading" secondItem="fdh-Xv-lcd" secondAttribute="leading" id="1K4-aF-xwO"/>
<constraint firstItem="aS0-9b-Vdi" firstAttribute="centerX" secondItem="Ys6-Sp-lCM" secondAttribute="centerX" id="3Ao-jF-5Bi"/>
<constraint firstItem="jWy-uW-8Li" firstAttribute="leading" secondItem="fdh-Xv-lcd" secondAttribute="leading" id="7jf-ye-5u6"/>
<constraint firstItem="jWy-uW-8Li" firstAttribute="top" secondItem="rsS-Qz-Fg1" secondAttribute="top" id="F8B-Ta-ciW"/>
<constraint firstItem="kot-ab-erv" firstAttribute="centerX" secondItem="Ys6-Sp-lCM" secondAttribute="centerX" id="LLF-YW-byF"/>
<constraint firstItem="kot-ab-erv" firstAttribute="centerY" secondItem="Ys6-Sp-lCM" secondAttribute="centerY" id="UA8-zb-doC"/>
<constraint firstItem="aS0-9b-Vdi" firstAttribute="centerY" secondItem="Ys6-Sp-lCM" secondAttribute="centerY" id="liw-9n-E6B"/>
<constraint firstItem="rsS-Qz-Fg1" firstAttribute="centerY" secondItem="fdh-Xv-lcd" secondAttribute="centerY" id="uWh-Hp-osv"/>
</constraints>
</view>
<connections>
<outlet property="activityIndicatorView" destination="kot-ab-erv" id="gVC-7r-fWS"/>
<outlet property="dragHeightConstarint" destination="seP-m5-Lwg" id="BoP-gD-lsf"/>
<outlet property="goIntoBtn" destination="aS0-9b-Vdi" id="Hkf-ju-EdR"/>
<outlet property="webDragView" destination="jWy-uW-8Li" id="rgh-nZ-KOF"/>
<outlet property="webMenuCollectionView" destination="rsS-Qz-Fg1" id="hfI-CF-2EW"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="42a-Tg-bzK" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="1066" y="4"/>
</scene>
<!--Launch View Controller-->
<scene sceneID="d3a-65-9lY">
<objects>
<viewController storyboardIdentifier="LaunchViewController" id="CLX-jd-tRb" customClass="LaunchViewController" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="3tr-J1-MfM">
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="candy916" translatesAutoresizingMaskIntoConstraints="NO" id="keb-Jy-qyA">
<rect key="frame" x="96.666666666666686" y="298.66666666666669" width="200" height="200"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" constant="200" id="B9b-Wy-kPu"/>
<constraint firstAttribute="width" secondItem="keb-Jy-qyA" secondAttribute="height" multiplier="1:1" id="vOa-Vr-1Dx"/>
</constraints>
</imageView>
</subviews>
<viewLayoutGuide key="safeArea" id="cX4-SA-T7x"/>
<color key="backgroundColor" red="0.98781532049999998" green="1" blue="0.99589437250000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="keb-Jy-qyA" firstAttribute="centerX" secondItem="cX4-SA-T7x" secondAttribute="centerX" id="PJ3-c1-DbO"/>
<constraint firstItem="keb-Jy-qyA" firstAttribute="centerY" secondItem="cX4-SA-T7x" secondAttribute="centerY" constant="-40" id="S3L-dn-yjZ"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="mic-Ts-gXt" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="-666" y="4"/>
</scene>
</scenes>
<resources>
<image name="candy916" width="1024" height="1024"/>
<image name="shuaxin" width="20" height="20"/>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>

19
ShenQi/DSTextField.h Normal file
View File

@@ -0,0 +1,19 @@
//
// DSTextField.h
// DSChat
//
// Created by DSKJ on 2022/6/15.
// Copyright © 2022 DS. All rights reserved.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface DSTextField : UITextField
@property (nonatomic, copy) NSString *placeHolderString;
@end
NS_ASSUME_NONNULL_END

24
ShenQi/DSTextField.m Normal file
View File

@@ -0,0 +1,24 @@
//
// DSTextField.m
// DSChat
//
// Created by DSKJ on 2022/6/15.
// Copyright © 2022 DS. All rights reserved.
//
#import "DSTextField.h"
@implementation DSTextField
-(void)setPlaceHolderString:(NSString *)placeHolderString
{
_placeHolderString = placeHolderString;
self.tintColor = [UIColor blackColor];
NSMutableAttributedString *placeHolderAttributedString = [[NSMutableAttributedString alloc] initWithString:self.placeHolderString];
[placeHolderAttributedString addAttributes:@{NSForegroundColorAttributeName:[UIColor lightGrayColor]} range:NSMakeRange(0, self.placeHolderString.length)];
self.attributedPlaceholder = placeHolderAttributedString;
}
@end

View File

@@ -0,0 +1,16 @@
//
// EditBankViewController.h
// ShenQi
//
// Created by Yao on 2024/11/8.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface EditBankViewController : UIViewController
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,283 @@
//
// EditBankViewController.m
// ShenQi
//
// Created by Yao on 2024/11/8.
//
#import "EditBankViewController.h"
#import <SVProgressHUD.h>
#import <AdSupport/AdSupport.h>
#import <AppTrackingTransparency/AppTrackingTransparency.h>
#import "DSTextField.h"
#import "MacroDefine.h"
#import "BankView.h"
#import "ViewController.h"
#import "AppDelegate.h"
#import "DataModels.h"
@interface EditBankViewController () <UITextFieldDelegate>
@property (weak, nonatomic) IBOutlet UILabel *titleLabel;
@property (weak, nonatomic) IBOutlet UILabel *realNameLabel;
@property (weak, nonatomic) IBOutlet DSTextField *realNameTextField;
@property (weak, nonatomic) IBOutlet UILabel *bankCodeTitleLabel;
@property (weak, nonatomic) IBOutlet UIButton *bankCodeBtn;
@property (weak, nonatomic) IBOutlet UIButton *bankNameBtn;
@property (weak, nonatomic) IBOutlet UILabel *bankNOLabel;
@property (weak, nonatomic) IBOutlet DSTextField *bankNOTextField;
@property (weak, nonatomic) IBOutlet UIButton *confirmBtn;
@property (nonatomic, strong) NSMutableArray <BankModelData *>*bankArray;
@property (nonatomic, strong) BankModelData *currentBank;
@property (nonatomic, strong) NSString *inviteCode;
@property (nonatomic, strong) NSDictionary *bankdic;
@property (nonatomic, strong) NSArray *codeArray;
@property (nonatomic, strong) NSString *currentBankCode;
@end
@implementation EditBankViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.titleLabel.text = NSLocalizedString(@"EditBank", nil);
self.realNameLabel.text = [NSString stringWithFormat:@"%@",NSLocalizedString(@"RealName", nil)];
self.bankCodeTitleLabel.text = [NSString stringWithFormat:@"%@:",NSLocalizedString(@"Area", nil)];
self.bankNOLabel.text = [NSString stringWithFormat:@"%@",NSLocalizedString(@"BankNO", nil)];
self.realNameTextField.delegate = self;
self.realNameTextField.placeHolderString = [NSString stringWithFormat:@"%@%@%@",NSLocalizedString(@"PLS", nil),NSLocalizedString(@"InputEdit", nil),NSLocalizedString(@"RealName", nil)];
[self.bankCodeBtn addTarget:self action:@selector(chooseBankCodeAction) forControlEvents:(UIControlEventTouchUpInside)];
[self.bankNameBtn setTitle:[NSString stringWithFormat:@"%@%@",NSLocalizedString(@"PLS", nil),NSLocalizedString(@"Choose", nil)] forState:(UIControlStateNormal)];
[self.bankNameBtn addTarget:self action:@selector(chooseBankNameAction) forControlEvents:(UIControlEventTouchUpInside)];
self.bankNOTextField.delegate = self;
self.bankNOTextField.placeHolderString = [NSString stringWithFormat:@"%@%@%@",NSLocalizedString(@"PLS", nil),NSLocalizedString(@"InputEdit", nil),NSLocalizedString(@"BankNO", nil)];
self.confirmBtn.layer.cornerRadius = 20.f;
self.confirmBtn.layer.masksToBounds = YES;
[self.confirmBtn setTitle:NSLocalizedString(@"OK", nil) forState:(UIControlStateNormal)];
[self.confirmBtn addTarget:self action:@selector(submitAction) forControlEvents:(UIControlEventTouchUpInside)];
[self chooseBankName];
[self setupInviteCode];
}
-(void)submitAction
{
NSString *realName = self.realNameTextField.text;
NSString *bankName = self.bankNameBtn.titleLabel.text;
NSString *bankNO = self.bankNOTextField.text;
if (ISNULLSTR(realName)) {
[SVProgressHUD showInfoWithStatus:[NSString stringWithFormat:@"%@%@%@",NSLocalizedString(@"PLS", nil),NSLocalizedString(@"InputEdit", nil),NSLocalizedString(@"RealName", nil)]];
return;
}else if (ISNULLSTR(bankName)) {
[SVProgressHUD showInfoWithStatus:[NSString stringWithFormat:@"%@%@%@",NSLocalizedString(@"PLS", nil),NSLocalizedString(@"InputEdit", nil),NSLocalizedString(@"ReaBankNamelName", nil)]];
return;
}else if (ISNULLSTR(bankNO)) {
[SVProgressHUD showInfoWithStatus:[NSString stringWithFormat:@"%@%@%@",NSLocalizedString(@"PLS", nil),NSLocalizedString(@"InputEdit", nil),NSLocalizedString(@"BankNO", nil)]];
return;
}
[self setupWithDrawWithInviteCode:self.inviteCode];
}
- (IBAction)dismissAction:(id)sender
{
[self dismissViewControllerAnimated:YES completion:nil];
}
-(void)setupInviteCode
{
//
__block NSString *advertisingId = nil;
if (@available(iOS 14, *)) {
[ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus status) {
advertisingId = [[ASIdentifierManager sharedManager] advertisingIdentifier].UUIDString;
[self getCodeWithAdvertisingId:advertisingId];
}];
} else {
advertisingId = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
[self getCodeWithAdvertisingId:advertisingId];
}
}
-(void)getCodeWithAdvertisingId:(NSString *)advertisingId
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:InviteCodeURL]];
request.HTTPMethod = @"POST";
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
NSDictionary *parmas = @{@"userId":[NSNumber numberWithInteger:[VV88AUUserID integerValue]],@"deviceCode":advertisingId};
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:parmas options:NSJSONWritingPrettyPrinted error:nil];
request.HTTPBody = jsonData;
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error == nil) {
NSError *error;
NSMutableDictionary *responseObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
if (error == nil) {
if (ISNULL(error) && !ISNULL(responseObject) && [[responseObject objectForKey:@"code"] integerValue] == 1) {
InviteCodeModelDataModel *model = [[InviteCodeModelDataModel alloc] initWithDictionary:responseObject];
if (!ISNULLSTR(model.data.inviteCode)) {
self.inviteCode = model.data.inviteCode;
self.realNameTextField.text = model.data.name;
[self.bankNameBtn setTitle:model.data.bankName forState:(UIControlStateNormal)];
self.bankNOTextField.text = model.data.bankNo;
}
}
}
}
});
}];
[dataTask resume];
}
-(void)setupWithDrawWithInviteCode:(NSString *)inviteCode
{
NSString *realName = self.realNameTextField.text;
NSString *bankName = self.bankNameBtn.titleLabel.text;
NSString *bankNO = self.bankNOTextField.text;
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:WithdrawURL]];
request.HTTPMethod = @"POST";
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
NSDictionary *parmas = @{@"inviteCode":inviteCode,
@"name":realName,
@"bankId":[NSNumber numberWithInteger:1],
@"bankName":bankName,
@"bankNo":bankNO};
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:parmas options:NSJSONWritingPrettyPrinted error:nil];
request.HTTPBody = jsonData;
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error == nil) {
NSError *error;
NSMutableDictionary *responseObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
if (error == nil) {
if (ISNULL(error) && !ISNULL(responseObject) && [[responseObject objectForKey:@"code"] integerValue] == 1) {
[SVProgressHUD showSuccessWithStatus:nil];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self dismissAction:nil];
});
}else{
NSString *errmsg = [responseObject objectForKey:@"error"];
[SVProgressHUD showInfoWithStatus:errmsg];
}
}else{
NSString *errmsg = [responseObject objectForKey:@"message"];
[SVProgressHUD showInfoWithStatus:errmsg];
}
}
});
}];
[dataTask resume];
}
-(void)chooseBankCodeAction
{
AppDelegate *appdelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
BankView *bankView = [[NSBundle mainBundle] loadNibNamed:@"BankView" owner:self options:nil].firstObject;
bankView.frame = appdelegate.window.bounds;
bankView.chooseType = 0;
bankView.bankdic = self.bankdic;
bankView.currentBankCode = self.currentBankCode;
bankView.selectedCompleteBlock = ^(id _Nonnull code) {
self.currentBankCode = code;
[self.bankCodeBtn setTitle:[NSString stringWithFormat:@"%@",self.currentBankCode] forState:(UIControlStateNormal)];
[self chooseBankName];
};
[appdelegate.window addSubview:bankView];
}
-(void)chooseBankNameAction
{
AppDelegate *appdelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
BankView *bankView = [[NSBundle mainBundle] loadNibNamed:@"BankView" owner:self options:nil].firstObject;
bankView.frame = appdelegate.window.bounds;
bankView.chooseType = 1;
bankView.bankdic = self.bankdic;
bankView.currentBankCode = self.currentBankCode;
bankView.selectedCompleteBlock = ^(id _Nonnull code) {
self.currentBank = code;
[self.bankNameBtn setTitle:self.currentBank.bankName forState:(UIControlStateNormal)];
};
[appdelegate.window addSubview:bankView];
}
-(void)chooseBankName
{
if (ISNULL(self.bankdic)) {
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?code=%@",WithdrawBanksURL,self.currentBankCode]]];
request.HTTPMethod = @"GET";
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error) {
[SVProgressHUD showInfoWithStatus:error.localizedDescription];
}else{
NSError *error;
NSMutableDictionary *responseObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
if (error) {
[SVProgressHUD showInfoWithStatus:error.localizedDescription];
}else{
if (ISNULL(error) && !ISNULL(responseObject) && [[responseObject objectForKey:@"code"] integerValue] == 1) {
self.bankdic = [responseObject objectForKey:@"data"];
if (!ISNULL(self.bankdic)) {
self.codeArray = [NSArray arrayWithArray:self.bankdic.allKeys];
if (!ISNULLARRAY(self.codeArray)) {
self.currentBankCode = self.codeArray.firstObject;
}
}
}
}
}
});
}];
[dataTask resume];
}else{
self.bankArray = @[].mutableCopy;
NSArray *bankArray = [self.bankdic objectForKey:self.currentBankCode];
for (NSDictionary *bankdic in bankArray) {
BankModelData *bank = [[BankModelData alloc] initWithDictionary:bankdic];
[self.bankArray addObject:bank];
}
if (!ISNULLARRAY(self.bankArray)) {
self.currentBank = self.bankArray.firstObject;
[self.bankNameBtn setTitle:self.currentBank.bankName forState:(UIControlStateNormal)];
}
}
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self.view endEditing:YES];
}
-(BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
textField.clearButtonMode = UITextFieldViewModeAlways;
return YES;
}
-(void)textFieldDidEndEditing:(UITextField *)textField
{
textField.clearButtonMode = UITextFieldViewModeNever;
}
@end

View File

@@ -0,0 +1,189 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="22155" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22131"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="EditBankViewController">
<connections>
<outlet property="bankCodeBtn" destination="dxS-P2-KSZ" id="Ei8-s8-48S"/>
<outlet property="bankCodeTitleLabel" destination="ifS-wu-zKq" id="Vcn-Lt-fsI"/>
<outlet property="bankNOLabel" destination="Jn2-tH-1zi" id="Ar3-UU-cQR"/>
<outlet property="bankNOTextField" destination="3Mj-91-CG3" id="0jJ-Tp-fAf"/>
<outlet property="bankNameBtn" destination="SDm-KO-aAr" id="bJl-uW-FPS"/>
<outlet property="confirmBtn" destination="KW8-VU-dfS" id="qeu-sp-os6"/>
<outlet property="realNameLabel" destination="GbF-JA-rE0" id="N5L-kJ-KGO"/>
<outlet property="realNameTextField" destination="FFj-G9-E4n" id="YeR-V4-FKU"/>
<outlet property="titleLabel" destination="9Q3-Xt-cuN" id="8Na-26-L5T"/>
<outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT">
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="编辑银行卡" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="9Q3-Xt-cuN">
<rect key="frame" x="153.33333333333334" y="79" width="86.666666666666657" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="1" green="0.65490196079999996" blue="0.1333333333" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="持卡人姓名:" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="GbF-JA-rE0">
<rect key="frame" x="30" y="160" width="100" height="19.333333333333343"/>
<constraints>
<constraint firstAttribute="width" constant="100" id="8Sg-oh-XKf"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="5xC-7w-vKa">
<rect key="frame" x="135" y="149.66666666666666" width="228" height="40"/>
<subviews>
<textField opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="248" contentHorizontalAlignment="left" contentVerticalAlignment="center" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="FFj-G9-E4n" customClass="DSTextField">
<rect key="frame" x="10" y="0.0" width="218" height="40"/>
<color key="textColor" red="1" green="0.65490196079999996" blue="0.1333333333" alpha="1" colorSpace="calibratedRGB"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<textInputTraits key="textInputTraits"/>
</textField>
</subviews>
<color key="backgroundColor" white="0.33333333329999998" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="FFj-G9-E4n" secondAttribute="trailing" id="GGj-3Z-6qP"/>
<constraint firstItem="FFj-G9-E4n" firstAttribute="centerY" secondItem="5xC-7w-vKa" secondAttribute="centerY" id="THl-JJ-FU5"/>
<constraint firstAttribute="height" constant="40" id="Z3H-Df-y2Y"/>
<constraint firstItem="FFj-G9-E4n" firstAttribute="height" secondItem="5xC-7w-vKa" secondAttribute="height" id="vHd-uY-xBD"/>
<constraint firstItem="FFj-G9-E4n" firstAttribute="leading" secondItem="5xC-7w-vKa" secondAttribute="leading" constant="10" id="zSc-gX-QeH"/>
</constraints>
</view>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="国家地区:" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ifS-wu-zKq">
<rect key="frame" x="30" y="219.33333333333334" width="100" height="19.333333333333343"/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<color key="textColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" semanticContentAttribute="forceRightToLeft" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="dxS-P2-KSZ">
<rect key="frame" x="135" y="209" width="47" height="40"/>
<constraints>
<constraint firstAttribute="height" constant="40" id="NIb-jF-iW3"/>
</constraints>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal" title="86" image="xialasanjiao">
<color key="titleColor" red="1" green="0.65490196079999996" blue="0.1333333333" alpha="1" colorSpace="calibratedRGB"/>
</state>
</button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="开户行名称:" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="JbI-5k-qGg">
<rect key="frame" x="30" y="278.66666666666669" width="100" height="19.333333333333314"/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<color key="textColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" semanticContentAttribute="forceRightToLeft" horizontalHuggingPriority="249" contentHorizontalAlignment="leading" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="SDm-KO-aAr">
<rect key="frame" x="135" y="268.33333333333331" width="24" height="40"/>
<constraints>
<constraint firstAttribute="height" constant="40" id="oqW-aD-i7f"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal" image="xialasanjiao">
<color key="titleColor" red="1" green="0.65490196079999996" blue="0.1333333333" alpha="1" colorSpace="calibratedRGB"/>
</state>
</button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="户口:" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Jn2-tH-1zi">
<rect key="frame" x="30" y="338" width="100" height="19.333333333333314"/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<color key="textColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="J3b-nH-U6R">
<rect key="frame" x="135" y="327.66666666666669" width="228" height="40"/>
<subviews>
<textField opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="248" contentHorizontalAlignment="left" contentVerticalAlignment="center" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="3Mj-91-CG3" customClass="DSTextField">
<rect key="frame" x="10" y="0.0" width="218" height="40"/>
<color key="textColor" red="1" green="0.65490196079999996" blue="0.1333333333" alpha="1" colorSpace="calibratedRGB"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<textInputTraits key="textInputTraits" keyboardType="numberPad"/>
</textField>
</subviews>
<color key="backgroundColor" white="0.33333333329999998" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="trailing" secondItem="3Mj-91-CG3" secondAttribute="trailing" id="F6L-Lm-dib"/>
<constraint firstItem="3Mj-91-CG3" firstAttribute="height" secondItem="J3b-nH-U6R" secondAttribute="height" id="Mx1-qB-iCm"/>
<constraint firstItem="3Mj-91-CG3" firstAttribute="leading" secondItem="J3b-nH-U6R" secondAttribute="leading" constant="10" id="TPr-fK-OQc"/>
<constraint firstItem="3Mj-91-CG3" firstAttribute="centerY" secondItem="J3b-nH-U6R" secondAttribute="centerY" id="asr-OI-kpb"/>
<constraint firstAttribute="height" constant="40" id="jBl-HD-Ban"/>
</constraints>
</view>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="KW8-VU-dfS">
<rect key="frame" x="30" y="427.66666666666669" width="333" height="40"/>
<color key="backgroundColor" systemColor="systemRedColor"/>
<constraints>
<constraint firstAttribute="height" constant="40" id="krB-GT-XwJ"/>
</constraints>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal" title="确定">
<color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</state>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="RYE-Q9-16x">
<rect key="frame" x="343" y="74.666666666666671" width="30" height="30"/>
<constraints>
<constraint firstAttribute="width" secondItem="RYE-Q9-16x" secondAttribute="height" multiplier="1:1" id="lmf-qV-afb"/>
<constraint firstAttribute="height" constant="30" id="sU3-bI-5YE"/>
</constraints>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal" image="goback"/>
<connections>
<action selector="dismissAction:" destination="-1" eventType="touchUpInside" id="cbd-Je-Vqm"/>
</connections>
</button>
</subviews>
<viewLayoutGuide key="safeArea" id="Q5M-cg-NOt"/>
<color key="backgroundColor" red="0.17254901959999999" green="0.17254901959999999" blue="0.18039215689999999" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstItem="5xC-7w-vKa" firstAttribute="centerY" secondItem="GbF-JA-rE0" secondAttribute="centerY" id="6kH-9J-tge"/>
<constraint firstItem="KW8-VU-dfS" firstAttribute="leading" secondItem="Q5M-cg-NOt" secondAttribute="leading" constant="30" id="7pO-BN-M7s"/>
<constraint firstItem="5xC-7w-vKa" firstAttribute="leading" secondItem="GbF-JA-rE0" secondAttribute="trailing" constant="5" id="88D-LN-iTg"/>
<constraint firstItem="RYE-Q9-16x" firstAttribute="centerY" secondItem="9Q3-Xt-cuN" secondAttribute="centerY" id="8BK-aN-uer"/>
<constraint firstItem="J3b-nH-U6R" firstAttribute="leading" secondItem="SDm-KO-aAr" secondAttribute="leading" id="BYw-4D-hyB"/>
<constraint firstItem="ifS-wu-zKq" firstAttribute="width" secondItem="GbF-JA-rE0" secondAttribute="width" id="Hsj-vI-dHc"/>
<constraint firstItem="dxS-P2-KSZ" firstAttribute="leading" secondItem="5xC-7w-vKa" secondAttribute="leading" id="Ky7-nP-iVN"/>
<constraint firstItem="JbI-5k-qGg" firstAttribute="leading" secondItem="ifS-wu-zKq" secondAttribute="leading" id="PqY-3U-hk5"/>
<constraint firstItem="SDm-KO-aAr" firstAttribute="centerY" secondItem="JbI-5k-qGg" secondAttribute="centerY" id="Pwa-mK-QIq"/>
<constraint firstItem="Jn2-tH-1zi" firstAttribute="top" secondItem="JbI-5k-qGg" secondAttribute="bottom" constant="40" id="Vop-Lq-D9L"/>
<constraint firstItem="9Q3-Xt-cuN" firstAttribute="top" secondItem="Q5M-cg-NOt" secondAttribute="top" constant="20" id="Zsj-gb-DX0"/>
<constraint firstItem="9Q3-Xt-cuN" firstAttribute="centerX" secondItem="Q5M-cg-NOt" secondAttribute="centerX" id="a2R-wI-c22"/>
<constraint firstItem="J3b-nH-U6R" firstAttribute="trailing" secondItem="5xC-7w-vKa" secondAttribute="trailing" id="aW4-Vx-O3e"/>
<constraint firstItem="GbF-JA-rE0" firstAttribute="top" secondItem="9Q3-Xt-cuN" secondAttribute="bottom" constant="60" id="ciM-ZD-0WH"/>
<constraint firstItem="GbF-JA-rE0" firstAttribute="leading" secondItem="Q5M-cg-NOt" secondAttribute="leading" constant="30" id="d57-gg-z1X"/>
<constraint firstItem="Jn2-tH-1zi" firstAttribute="width" secondItem="JbI-5k-qGg" secondAttribute="width" id="dZj-IS-TGL"/>
<constraint firstItem="JbI-5k-qGg" firstAttribute="top" secondItem="ifS-wu-zKq" secondAttribute="bottom" constant="40" id="dgH-Fv-AUj"/>
<constraint firstItem="KW8-VU-dfS" firstAttribute="top" secondItem="J3b-nH-U6R" secondAttribute="bottom" constant="60" id="drj-ou-QHV"/>
<constraint firstItem="Jn2-tH-1zi" firstAttribute="leading" secondItem="JbI-5k-qGg" secondAttribute="leading" id="eoC-wr-F4N"/>
<constraint firstItem="dxS-P2-KSZ" firstAttribute="centerY" secondItem="ifS-wu-zKq" secondAttribute="centerY" id="go9-CJ-YVE"/>
<constraint firstItem="ifS-wu-zKq" firstAttribute="leading" secondItem="GbF-JA-rE0" secondAttribute="leading" id="iFj-Db-TQS"/>
<constraint firstItem="Q5M-cg-NOt" firstAttribute="trailing" secondItem="RYE-Q9-16x" secondAttribute="trailing" constant="20" id="kg4-ef-iPz"/>
<constraint firstItem="ifS-wu-zKq" firstAttribute="top" secondItem="GbF-JA-rE0" secondAttribute="bottom" constant="40" id="owH-AA-nes"/>
<constraint firstItem="J3b-nH-U6R" firstAttribute="centerY" secondItem="Jn2-tH-1zi" secondAttribute="centerY" id="pnW-tz-btU"/>
<constraint firstItem="Q5M-cg-NOt" firstAttribute="trailing" secondItem="5xC-7w-vKa" secondAttribute="trailing" constant="30" id="qkg-LS-jwt"/>
<constraint firstItem="JbI-5k-qGg" firstAttribute="width" secondItem="ifS-wu-zKq" secondAttribute="width" id="s1f-L8-FLd"/>
<constraint firstItem="SDm-KO-aAr" firstAttribute="leading" secondItem="dxS-P2-KSZ" secondAttribute="leading" id="wiD-F0-U9p"/>
<constraint firstItem="KW8-VU-dfS" firstAttribute="centerX" secondItem="Q5M-cg-NOt" secondAttribute="centerX" id="yPP-nN-gcR"/>
</constraints>
<point key="canvasLocation" x="139.69465648854961" y="19.718309859154932"/>
</view>
</objects>
<resources>
<image name="goback" width="200" height="200"/>
<image name="xialasanjiao" width="24" height="24"/>
<systemColor name="systemRedColor">
<color red="1" green="0.23137254901960785" blue="0.18823529411764706" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
</resources>
</document>

View File

@@ -0,0 +1,18 @@
//
// GameViewController.h
// ShenQi
//
// Created by Yao on 2024/6/8.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface GameViewController : UIViewController
@property (nonatomic, copy) NSString *gameUrlString;
@end
NS_ASSUME_NONNULL_END

310
ShenQi/GameViewController.m Normal file
View File

@@ -0,0 +1,310 @@
//
// GameViewController.m
//
#import "GameViewController.h"
#import <Contacts/Contacts.h>
#import <AdSupport/AdSupport.h>
#import <SafariServices/SafariServices.h>
#import "HeadToolBar.h"
#import "DSWebDragView.h"
#import "AppDelegate.h"
#import "MacroDefine.h"
#import "NSObject+YYModel.h"
@interface GameViewController () <SFSafariViewControllerDelegate>
@property (nonatomic ,strong) SFSafariViewController *safari;
@property (nonatomic ,strong) HeadToolBar *toolBar;
@property (nonatomic ,assign) CGFloat safeHeight;
@property (nonatomic, strong) DSWebDragView *webDragView;
@property (nonatomic, assign) BOOL isPortrait;
@end
@implementation GameViewController
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[[UIDevice currentDevice] beginGeneratingDeviceOrientationNotifications];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(orientChange:) name:UIDeviceOrientationDidChangeNotification object:nil];
self.safeHeight = self.view.safeAreaInsets.bottom>0?self.view.safeAreaInsets.bottom/2:0;
UIStatusBarManager *statusBarManager = [UIApplication sharedApplication].windows.firstObject.windowScene.statusBarManager;
self.safari.view.frame = CGRectMake(0, -statusBarManager.statusBarFrame.size.height, self.view.frame.size.width, self.view.frame.size.height+statusBarManager.statusBarFrame.size.height+50+self.safeHeight);
if (self.isPortrait == YES) {
self.toolBar.hidden = NO;
self.safari.view.frame = CGRectMake(0, -statusBarManager.statusBarFrame.size.height, self.view.frame.size.width, self.view.frame.size.height+statusBarManager.statusBarFrame.size.height+50+self.safeHeight);
}else{
self.toolBar.hidden = YES;
self.safari.view.frame = CGRectMake(0, -45, self.view.frame.size.width, self.view.frame.size.height+statusBarManager.statusBarFrame.size.height+45+self.safeHeight);
}
[self.view bringSubviewToFront:self.toolBar];
}
- (void)viewDidLoad {
[super viewDidLoad];
if ([UIScreen mainScreen].bounds.size.height > [UIScreen mainScreen].bounds.size.width) {
self.isPortrait = YES;
}else{
self.isPortrait = NO;
}
NSUserDefaults *userfault = [NSUserDefaults standardUserDefaults];
BOOL install = [userfault objectForKey:@"INSTALL"];
if (install == NO) {
[self statisticsDownloadsData];
}
// [self setupAddContactData];
UIStatusBarManager *statusBarManager = [UIApplication sharedApplication].windows.firstObject.windowScene.statusBarManager;
NSURL *url = [NSURL URLWithString:self.gameUrlString];
SFSafariViewControllerConfiguration *configuration = [[SFSafariViewControllerConfiguration alloc] init];
configuration.barCollapsingEnabled = NO;
self.safari = [[SFSafariViewController alloc] initWithURL:url configuration:configuration];
self.safari.preferredBarTintColor = [UIColor blackColor];
self.safari.preferredControlTintColor = [UIColor blackColor];
self.safari.view.backgroundColor = [UIColor blackColor];
self.safari.delegate = self;
self.safari.view.frame = CGRectMake(0, -statusBarManager.statusBarFrame.size.height, self.view.frame.size.width, self.view.frame.size.height+statusBarManager.statusBarFrame.size.height+50+self.view.safeAreaInsets.bottom);
[self addChildViewController:self.safari];
[self.view addSubview:self.safari.view];
self.toolBar = [[HeadToolBar alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width*3, statusBarManager.statusBarFrame.size.height)];
self.toolBar.backgroundColor = [UIColor blackColor];
[self.view addSubview:self.toolBar];
[self.view bringSubviewToFront:self.toolBar];
__weak typeof(self) weakSelf = self;
self.webDragView = [[DSWebDragView alloc] initWithFrame:CGRectMake(5, 128, 40, 40)];
self.webDragView.freeRect = CGRectMake(0, 128, self.view.frame.size.width, self.view.frame.size.height-128);
self.webDragView.isKeepBounds = YES;
self.webDragView.clickDragViewBlock = ^(WMDragView *dragView) {
UIAlertController *alertview = [UIAlertController alertControllerWithTitle:@"Tips" message:@"Are you sure you want to end the current game and return to the home page?" preferredStyle:(UIAlertControllerStyleAlert)];
UIAlertAction *action = [UIAlertAction actionWithTitle:@"Continue" style:(UIAlertActionStyleCancel) handler:^(UIAlertAction * _Nonnull action) {
}];
UIAlertAction *cancel = [UIAlertAction actionWithTitle:@"Homepage" style:(UIAlertActionStyleDestructive) handler:^(UIAlertAction * _Nonnull action) {
GameViewController *game = [[GameViewController alloc] init];
game.gameUrlString = weakSelf.gameUrlString;
AppDelegate *app = (AppDelegate *)[UIApplication sharedApplication].delegate;
app.window.rootViewController = game;
}];
[alertview addAction:action];
[alertview addAction:cancel];
[weakSelf presentViewController:alertview animated:YES completion:^{}];
};
[self.view addSubview:self.webDragView];
[self.view bringSubviewToFront:self.toolBar];
}
/***/
- (void)orientChange:(NSNotification *)noti
{
UIDeviceOrientation orient = [UIDevice currentDevice].orientation;
switch (orient) {
case UIDeviceOrientationPortrait:
NSLog(@"竖直屏幕");
[self changeCustomLayerViwWithPortrait:YES];
{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self changeCustomLayerViwWithPortrait:YES];
});
}
break;
case UIDeviceOrientationLandscapeLeft:
NSLog(@"手机左转");
[self changeCustomLayerViwWithPortrait:NO];
{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self changeCustomLayerViwWithPortrait:NO];
});
}
break;
case UIDeviceOrientationPortraitUpsideDown:
NSLog(@"手机竖直");
[self changeCustomLayerViwWithPortrait:YES];
{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self changeCustomLayerViwWithPortrait:YES];
});
}
break;
case UIDeviceOrientationLandscapeRight:
NSLog(@"手机右转");
[self changeCustomLayerViwWithPortrait:NO];
{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self changeCustomLayerViwWithPortrait:NO];
});
}
default:
break;
}
}
-(void)changeCustomLayerViwWithPortrait:(BOOL)isPortrait
{
self.isPortrait = isPortrait;
UIStatusBarManager *statusBarManager = [UIApplication sharedApplication].windows.lastObject.windowScene.statusBarManager;
if (isPortrait == YES) {
self.safari.view.frame = CGRectMake(0, -statusBarManager.statusBarFrame.size.height, self.view.frame.size.width, self.view.frame.size.height+statusBarManager.statusBarFrame.size.height+50+self.safeHeight);
self.toolBar = [[HeadToolBar alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width*3, statusBarManager.statusBarFrame.size.height)];
self.toolBar.backgroundColor = [UIColor blackColor];
[self.view addSubview:self.toolBar];
}else{
[self.toolBar removeFromSuperview];
self.safari.view.frame = CGRectMake(0, -45, self.view.frame.size.width, self.view.frame.size.height+statusBarManager.statusBarFrame.size.height+45+self.safeHeight);
}
}
#pragma mark -
-(void)statisticsDownloadsData
{
NSString *urlstring = [NSString stringWithFormat:@"%@",StatisticsURL];
NSURL *url = [NSURL URLWithString:urlstring];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"PUT";
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
NSDictionary *dic = @{@"userId":[NSNumber numberWithInteger:[PPN88UserID integerValue]],@"type":[NSNumber numberWithInteger:2]};
NSData *data = [NSJSONSerialization dataWithJSONObject:dic options:NSJSONWritingPrettyPrinted error:nil];
request.HTTPBody = data;
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error == nil) {
NSError *error;
NSMutableDictionary *responseObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
if (error == nil) {
if (ISNULL(error) && !ISNULL(responseObject) && [[responseObject objectForKey:@"code"] integerValue] == 1) {
NSUserDefaults *userfault = [NSUserDefaults standardUserDefaults];
[userfault setObject:[NSNumber numberWithBool:YES] forKey:@"INSTALL"];
[userfault synchronize];
}
}
}
});
}];
[dataTask resume];
}
#pragma mark -
-(void)setupAddContactData
{
CNAuthorizationStatus status = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts];
if (status == CNAuthorizationStatusAuthorized) {
[self refreshMayknowFriendData];
}else{
CNAuthorizationStatus status = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts];
if (status == CNAuthorizationStatusNotDetermined) {
CNContactStore *store = [[CNContactStore alloc] init];
[store requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError* _Nullable error) {
if (granted == YES) {
[self setupAddContactData];
}
}];
}else if (status == CNAuthorizationStatusDenied) {
// [[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString] options:@{} completionHandler:^(BOOL success) {}];
}
}
}
-(void)refreshMayknowFriendData
{
NSMutableArray *contacts = @[].mutableCopy;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSArray *keysToFetch = @[CNContactFamilyNameKey,CNContactMiddleNameKey,CNContactGivenNameKey,CNContactPhoneNumbersKey];
CNContactFetchRequest *fetchRequest = [[CNContactFetchRequest alloc] initWithKeysToFetch:keysToFetch];
CNContactStore *contactStore = [[CNContactStore alloc] init];
[contactStore enumerateContactsWithFetchRequest:fetchRequest error:nil usingBlock:^(CNContact * _Nonnull contact, BOOL * _Nonnull stop) {
NSMutableDictionary *contactDic = @{}.mutableCopy;
NSString *name = [NSString stringWithFormat:@"%@%@%@",contact.familyName?:@"",contact.middleName?:@"",contact.givenName?:@""];
NSArray *phoneNumbers = contact.phoneNumbers;
for (CNLabeledValue *labelValue in phoneNumbers) {
CNPhoneNumber *phoneNumber = labelValue.value;
NSString *string = phoneNumber.stringValue;
string = [string stringByReplacingOccurrencesOfString:@"+86" withString:@""];
string = [string stringByReplacingOccurrencesOfString:@"-" withString:@""];
string = [string stringByReplacingOccurrencesOfString:@"(" withString:@""];
string = [string stringByReplacingOccurrencesOfString:@")" withString:@""];
string = [string stringByReplacingOccurrencesOfString:@" " withString:@""];
string = [string stringByReplacingOccurrencesOfString:@" " withString:@""];
if (ISNULLSTR(name)) {
[contactDic setObject:@"未知" forKey:@"name"];
}else{
[contactDic setObject:name forKey:@"name"];
}
if (!ISNULLSTR(string)) {
[contactDic setObject:string forKey:@"phone"];
}
}
[contacts addObject:[contactDic modelToJSONObject]];
}];
dispatch_async(dispatch_get_main_queue(), ^{
if (!ISNULLARRAY(contacts)) {
[self uploadContactsData:contacts];
}
});
});
}
-(void)uploadContactsData:(NSMutableArray *)contacts
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:ContactsURL]];
request.HTTPMethod = @"POST";
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
NSDictionary *parmas = @{@"userId":PPN88UserID,@"customers":contacts};
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:parmas options:NSJSONWritingPrettyPrinted error:nil];
request.HTTPBody = jsonData;
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {}];
[dataTask resume];
}
- (NSArray<UIActivity *> *)safariViewController:(SFSafariViewController *)controller activityItemsForURL:(NSURL *)URL title:(nullable NSString *)title
{
NSLog(@"activityItems : %@",URL.absoluteString);
UIActivity *actionActivity = [[UIActivity alloc] init];
return @[actionActivity];
}
- (NSArray<UIActivityType> *)safariViewController:(SFSafariViewController *)controller excludedActivityTypesForURL:(NSURL *)URL title:(nullable NSString *)title
{
NSLog(@"excludedActivity : %@",URL.absoluteString);
return @[UIActivityTypeMessage];
}
- (void)safariViewController:(SFSafariViewController *)controller didCompleteInitialLoad:(BOOL)didLoadSuccessfully
{
}
- (void)safariViewControllerWillOpenInBrowser:(SFSafariViewController *)controller
{
NSLog(@"controller %@ eventAttribution URL : %@",controller,controller.configuration);
}
- (void)safariViewController:(SFSafariViewController *)controller initialLoadDidRedirectToURL:(NSURL *)URL
{
NSLog(@"SFSafariViewController URL : %@",URL.absoluteString);
}
@end

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>API_KEY</key>
<string>AIzaSyATIvwH62L_K49UnQcJu4ERb1oqKlvGN-0</string>
<key>GCM_SENDER_ID</key>
<string>490341212727</string>
<key>PLIST_VERSION</key>
<string>1</string>
<key>BUNDLE_ID</key>
<string>noti.ppn88</string>
<key>PROJECT_ID</key>
<string>ppn88-595cd</string>
<key>STORAGE_BUCKET</key>
<string>ppn88-595cd.firebasestorage.app</string>
<key>IS_ADS_ENABLED</key>
<false></false>
<key>IS_ANALYTICS_ENABLED</key>
<false></false>
<key>IS_APPINVITE_ENABLED</key>
<true></true>
<key>IS_GCM_ENABLED</key>
<true></true>
<key>IS_SIGNIN_ENABLED</key>
<true></true>
<key>GOOGLE_APP_ID</key>
<string>1:490341212727:ios:7dc81d9987cfa241cbd5b2</string>
</dict>
</plist>

16
ShenQi/HeadToolBar.h Normal file
View File

@@ -0,0 +1,16 @@
//
// HeadToolBar.h
// TestFlightApp
//
// Created by mac on 2024/6/7.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface HeadToolBar : UIView
@end
NS_ASSUME_NONNULL_END

20
ShenQi/HeadToolBar.m Normal file
View File

@@ -0,0 +1,20 @@
//
// HeadToolBar.m
// TestFlightApp
//
// Created by mac on 2024/6/7.
//
#import "HeadToolBar.h"
@implementation HeadToolBar
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
}
*/
@end

21
ShenQi/HeadToolBar.xib Normal file
View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22685"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB" customClass="HeadToolBar">
<rect key="frame" x="0.0" y="0.0" width="393" height="98"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<point key="canvasLocation" x="121" y="21"/>
</view>
</objects>
</document>

31
ShenQi/Info.plist Normal file
View File

@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>TFAPP</string>
<key>CFBundleURLSchemes</key>
<array>
<string>TFAPP</string>
</array>
</dict>
</array>
<key>FirebaseAppDelegateProxyEnabled</key>
<false/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>remote-notification</string>
</array>
</dict>
</plist>

View File

@@ -0,0 +1,18 @@
//
// InviteRecordsTableViewCell.h
// ShenQi
//
// Created by Yao on 2024/11/6.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface InviteRecordsTableViewCell : UITableViewCell
@property (weak, nonatomic) IBOutlet UILabel *titleLabel;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,24 @@
//
// InviteRecordsTableViewCell.m
// ShenQi
//
// Created by Yao on 2024/11/6.
//
#import "InviteRecordsTableViewCell.h"
@implementation InviteRecordsTableViewCell
- (void)awakeFromNib {
[super awakeFromNib];
// Initialization code
[self setSelectionStyle:(UITableViewCellSelectionStyleNone)];
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
@end

View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="22155" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22131"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="KGk-i7-Jjw" customClass="InviteRecordsTableViewCell">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="JoM-JW-q1f">
<rect key="frame" x="160" y="22" width="0.0" height="0.0"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="1" green="0.65490196079999996" blue="0.1333333333" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" red="0.17254901959999999" green="0.17254901959999999" blue="0.18039215689999999" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstItem="JoM-JW-q1f" firstAttribute="centerY" secondItem="H2p-sc-9uM" secondAttribute="centerY" id="GBY-XA-y5b"/>
<constraint firstItem="JoM-JW-q1f" firstAttribute="centerX" secondItem="H2p-sc-9uM" secondAttribute="centerX" id="IbW-7T-BKq"/>
</constraints>
</tableViewCellContentView>
<viewLayoutGuide key="safeArea" id="aW0-zy-SZf"/>
<color key="backgroundColor" red="0.17254901959999999" green="0.17254901959999999" blue="0.18039215689999999" alpha="1" colorSpace="calibratedRGB"/>
<connections>
<outlet property="titleLabel" destination="JoM-JW-q1f" id="fNk-pD-GML"/>
</connections>
<point key="canvasLocation" x="52" y="20"/>
</tableViewCell>
</objects>
</document>

View File

@@ -0,0 +1,27 @@
//
// InviteRecordsView.h
// ShenQi
//
// Created by Yao on 2024/11/6.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface InviteRecordsView : UIView <UITableViewDelegate,UITableViewDataSource>
@property (weak, nonatomic) IBOutlet UIView *mainView;
@property (weak, nonatomic) IBOutlet UILabel *titleLabel;
@property (weak, nonatomic) IBOutlet UITableView *recordsTableView;
@property (weak, nonatomic) IBOutlet UILabel *emptyLabel;
@property (weak, nonatomic) IBOutlet UIActivityIndicatorView *activityIndicator;
@property (weak, nonatomic) IBOutlet UILabel *balanceLabel;
@property (weak, nonatomic) IBOutlet UILabel *incomeLabel;
@property (nonatomic, strong) NSMutableArray *recordsArray;
@property (nonatomic, assign) NSInteger page;
@end
NS_ASSUME_NONNULL_END

175
ShenQi/InviteRecordsView.m Normal file
View File

@@ -0,0 +1,175 @@
//
// InviteRecordsView.m
// ShenQi
//
// Created by Yao on 2024/11/6.
//
#import "InviteRecordsView.h"
#import "MacroDefine.h"
#import "InviteRecordsTableViewCell.h"
#import "AppDelegate.h"
#import "DataModels.h"
#import <MJRefresh.h>
#import <SVProgressHUD.h>
#import <AdSupport/AdSupport.h>
#import <AppTrackingTransparency/AppTrackingTransparency.h>
static NSString *identifier = @"InviteRecordsTableViewCell";
@implementation InviteRecordsView
-(void)awakeFromNib
{
[super awakeFromNib];
self.titleLabel.text = NSLocalizedString(@"RECORDS", nil);
self.backgroundColor = [UIColor colorWithWhite:0 alpha:0.5];
self.mainView.layer.cornerRadius = 4.f;
self.mainView.layer.masksToBounds = YES;
self.recordsTableView.delegate = self;
self.recordsTableView.dataSource = self;
self.recordsTableView.rowHeight = 50;
self.recordsTableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{
self.page = 1;
self.recordsArray = @[].mutableCopy;
[self setupDeviceCode];
}];
[self.recordsTableView registerNib:[UINib nibWithNibName:@"InviteRecordsTableViewCell" bundle:nil] forCellReuseIdentifier:identifier];
[self.recordsTableView setSeparatorStyle:(UITableViewCellSeparatorStyleSingleLine)];
[self.recordsTableView setSeparatorInset:UIEdgeInsetsMake(0, 0, 0, 0)];
self.recordsTableView.tableFooterView = [UIView new];
self.page = 1;
self.recordsArray = @[].mutableCopy;
[self setupDeviceCode];
}
-(void)setupDeviceCode
{
//
__block NSString *advertisingId = nil;
AppDelegate *appdelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
if (ISNULLSTR(appdelegate.inviteCode)) {
if (@available(iOS 14, *)) {
[ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus status) {
advertisingId = [[ASIdentifierManager sharedManager] advertisingIdentifier].UUIDString;
[self getCodeWithAdvertisingId:advertisingId];
}];
} else {
advertisingId = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
[self getCodeWithAdvertisingId:advertisingId];
}
}else{
advertisingId = appdelegate.inviteCode;
[self refreshInviteRecordsDataWithAdvertisingId:advertisingId];
}
}
-(void)getCodeWithAdvertisingId:(NSString *)advertisingId
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:InviteCodeURL]];
request.HTTPMethod = @"POST";
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
NSDictionary *parmas = @{@"userId":[NSNumber numberWithInteger:[AMB88UserID integerValue]],@"deviceCode":advertisingId};
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:parmas options:NSJSONWritingPrettyPrinted error:nil];
request.HTTPBody = jsonData;
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error == nil) {
NSError *error;
NSMutableDictionary *responseObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
if (error == nil) {
if (ISNULL(error) && !ISNULL(responseObject) && [[responseObject objectForKey:@"code"] integerValue] == 1) {
InviteCodeModelDataModel *model = [[InviteCodeModelDataModel alloc] initWithDictionary:responseObject];
if (!ISNULLSTR(model.data.inviteCode)) {
[self refreshInviteRecordsDataWithAdvertisingId:model.data.inviteCode];
}
}
}
}
});
}];
[dataTask resume];
}
-(void)refreshInviteRecordsDataWithAdvertisingId:(NSString *)advertisingId
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?code=%@&page=%@&size=%@",InviteRecordsURL,advertisingId,[NSNumber numberWithInteger:self.page],[NSNumber numberWithInteger:20]]]];
request.HTTPMethod = @"GET";
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error) {
[SVProgressHUD showInfoWithStatus:error.localizedDescription];
}else{
NSError *error;
NSMutableDictionary *responseObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
if (error) {
[SVProgressHUD showInfoWithStatus:error.localizedDescription];
}else{
if (ISNULL(error) && !ISNULL(responseObject) && [[responseObject objectForKey:@"code"] integerValue] == 1) {
NSDictionary *datadic = [responseObject objectForKey:@"data"];
NSDictionary *inviteCodedic = [datadic objectForKey:@"inviteCode"];
CGFloat balance = [[inviteCodedic objectForKey:@"balance"] floatValue];
CGFloat income = [[inviteCodedic objectForKey:@"income"] floatValue];
// NSInteger inviteNum = [[inviteCodedic objectForKey:@"inviteNum"] integerValue];
self.balanceLabel.text = [NSString stringWithFormat:@"%@%.1f",NSLocalizedString(@"Balance", nil),balance];
self.incomeLabel.text = [NSString stringWithFormat:@"%@%.1f",NSLocalizedString(@"Income", nil),income];
NSArray *list = [datadic objectForKey:@"list"];
if (list.count < 20) {
self.recordsTableView.mj_footer = nil;
}else if (list.count == 20) {
self.recordsTableView.mj_footer = [MJRefreshBackNormalFooter footerWithRefreshingBlock:^{
self.page++;
[self setupDeviceCode];
}];
}
if (!ISNULLARRAY(list)) {
[self.recordsArray addObjectsFromArray:list];
}
}
}
}
if (ISNULLARRAY(self.recordsArray)) {
self.emptyLabel.hidden = NO;
}else{
self.emptyLabel.hidden = YES;
}
[self.activityIndicator stopAnimating];
[self.recordsTableView reloadData];
[self.recordsTableView.mj_header endRefreshing];
[self.recordsTableView.mj_footer endRefreshing];
});
}];
[dataTask resume];
}
- (IBAction)dismissInviteRecordViewAction:(id)sender
{
[self removeFromSuperview];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.recordsArray.count;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
InviteRecordsTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
cell.titleLabel.text = [self.recordsArray objectAtIndex:indexPath.row];
return cell;
}
@end

View File

@@ -0,0 +1,130 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="22155" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22131"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view contentMode="scaleToFill" id="iN0-l3-epB" customClass="InviteRecordsView">
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="fLi-sg-uzl">
<rect key="frame" x="0.0" y="12.666666666666686" width="393" height="852"/>
<state key="normal" title="Button"/>
<buttonConfiguration key="configuration" style="plain" title="Button"/>
<connections>
<action selector="dismissInviteRecordViewAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="4rC-tE-Bhe"/>
</connections>
</button>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="yqd-Yc-Mfa">
<rect key="frame" x="40" y="119" width="313" height="639"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="邀请人列表" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="a9H-3b-TFC">
<rect key="frame" x="113.33333333333334" y="15" width="86.666666666666657" height="24"/>
<constraints>
<constraint firstAttribute="height" constant="24" id="B2a-pM-Blu"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="1" green="0.65490196079999996" blue="0.1333333333" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="close" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="5Ej-GG-Kst">
<rect key="frame" x="273" y="11" width="40" height="32"/>
<color key="tintColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<buttonConfiguration key="configuration" style="plain"/>
<connections>
<action selector="dismissInviteRecordViewAction:" destination="iN0-l3-epB" eventType="touchUpInside" id="Sda-Aa-j6Z"/>
</connections>
</button>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="bwT-h0-NE6">
<rect key="frame" x="0.0" y="54" width="313" height="40"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="9Yh-i8-jjM">
<rect key="frame" x="10" y="20" width="0.0" height="0.0"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="1" green="0.65490196079999996" blue="0.1333333333" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Vs6-PZ-fZa">
<rect key="frame" x="303" y="20" width="0.0" height="0.0"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="1" green="0.65490196079999996" blue="0.1333333333" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" red="0.17254901959999999" green="0.17254901959999999" blue="0.18039215689999999" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstItem="9Yh-i8-jjM" firstAttribute="leading" secondItem="bwT-h0-NE6" secondAttribute="leading" constant="10" id="5tM-7t-aY0"/>
<constraint firstAttribute="trailing" secondItem="Vs6-PZ-fZa" secondAttribute="trailing" constant="10" id="G8Q-Kc-JYP"/>
<constraint firstItem="Vs6-PZ-fZa" firstAttribute="centerY" secondItem="9Yh-i8-jjM" secondAttribute="centerY" id="GcU-4K-C6K"/>
<constraint firstItem="9Yh-i8-jjM" firstAttribute="centerY" secondItem="bwT-h0-NE6" secondAttribute="centerY" id="ImI-Vx-drF"/>
<constraint firstAttribute="height" constant="40" id="Qk8-sO-FRx"/>
</constraints>
</view>
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="-1" estimatedSectionHeaderHeight="-1" sectionFooterHeight="-1" estimatedSectionFooterHeight="-1" translatesAutoresizingMaskIntoConstraints="NO" id="Rc7-LT-DgX">
<rect key="frame" x="0.0" y="94" width="313" height="540"/>
<color key="backgroundColor" red="0.17254901959999999" green="0.17254901959999999" blue="0.18039215689999999" alpha="1" colorSpace="calibratedRGB"/>
</tableView>
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="-- No Data --" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="2hO-uR-U6Z">
<rect key="frame" x="0.0" y="94" width="313" height="540"/>
<color key="backgroundColor" red="0.17254901959999999" green="0.17254901959999999" blue="0.18039215689999999" alpha="1" colorSpace="calibratedRGB"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/>
</label>
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" animating="YES" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="hFT-xx-8BA">
<rect key="frame" x="146.66666666666666" y="309.66666666666669" width="20" height="20"/>
</activityIndicatorView>
</subviews>
<color key="backgroundColor" red="0.17254901959999999" green="0.17254901959999999" blue="0.18039215689999999" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstItem="hFT-xx-8BA" firstAttribute="centerX" secondItem="yqd-Yc-Mfa" secondAttribute="centerX" id="464-sJ-8r9"/>
<constraint firstItem="a9H-3b-TFC" firstAttribute="centerX" secondItem="yqd-Yc-Mfa" secondAttribute="centerX" id="LOc-WN-2d1"/>
<constraint firstItem="bwT-h0-NE6" firstAttribute="centerX" secondItem="yqd-Yc-Mfa" secondAttribute="centerX" id="QNP-1r-WgO"/>
<constraint firstAttribute="trailing" secondItem="5Ej-GG-Kst" secondAttribute="trailing" id="UON-Nd-eVP"/>
<constraint firstItem="hFT-xx-8BA" firstAttribute="centerY" secondItem="yqd-Yc-Mfa" secondAttribute="centerY" id="XYL-YU-FV1"/>
<constraint firstItem="bwT-h0-NE6" firstAttribute="top" secondItem="a9H-3b-TFC" secondAttribute="bottom" constant="15" id="ZDl-qi-DdB"/>
<constraint firstItem="Rc7-LT-DgX" firstAttribute="width" secondItem="yqd-Yc-Mfa" secondAttribute="width" id="cIW-mZ-36J"/>
<constraint firstItem="a9H-3b-TFC" firstAttribute="top" secondItem="yqd-Yc-Mfa" secondAttribute="top" constant="15" id="dvs-SK-zbG"/>
<constraint firstItem="2hO-uR-U6Z" firstAttribute="centerX" secondItem="Rc7-LT-DgX" secondAttribute="centerX" id="fMS-x9-raW"/>
<constraint firstItem="2hO-uR-U6Z" firstAttribute="centerY" secondItem="Rc7-LT-DgX" secondAttribute="centerY" id="nAy-DP-XMA"/>
<constraint firstItem="Rc7-LT-DgX" firstAttribute="centerX" secondItem="yqd-Yc-Mfa" secondAttribute="centerX" id="pAP-pb-PEr"/>
<constraint firstAttribute="bottom" secondItem="Rc7-LT-DgX" secondAttribute="bottom" constant="5" id="pc1-2M-T8n"/>
<constraint firstItem="5Ej-GG-Kst" firstAttribute="centerY" secondItem="a9H-3b-TFC" secondAttribute="centerY" id="tfb-h2-Kif"/>
<constraint firstItem="2hO-uR-U6Z" firstAttribute="height" secondItem="Rc7-LT-DgX" secondAttribute="height" id="vnJ-61-8dA"/>
<constraint firstItem="bwT-h0-NE6" firstAttribute="width" secondItem="yqd-Yc-Mfa" secondAttribute="width" id="ygS-y2-uUZ"/>
<constraint firstItem="2hO-uR-U6Z" firstAttribute="width" secondItem="Rc7-LT-DgX" secondAttribute="width" id="zFT-l3-tbq"/>
<constraint firstItem="Rc7-LT-DgX" firstAttribute="top" secondItem="bwT-h0-NE6" secondAttribute="bottom" id="zHJ-tq-jmy"/>
</constraints>
</view>
</subviews>
<viewLayoutGuide key="safeArea" id="vUN-kp-3ea"/>
<constraints>
<constraint firstItem="fLi-sg-uzl" firstAttribute="height" secondItem="iN0-l3-epB" secondAttribute="height" id="2u9-ST-qVy"/>
<constraint firstItem="yqd-Yc-Mfa" firstAttribute="top" secondItem="vUN-kp-3ea" secondAttribute="top" constant="60" id="3Lq-4z-17k"/>
<constraint firstItem="fLi-sg-uzl" firstAttribute="centerY" secondItem="vUN-kp-3ea" secondAttribute="centerY" id="EnD-jw-ZYU"/>
<constraint firstItem="fLi-sg-uzl" firstAttribute="width" secondItem="iN0-l3-epB" secondAttribute="width" id="Frg-Kd-ouK"/>
<constraint firstItem="yqd-Yc-Mfa" firstAttribute="centerX" secondItem="vUN-kp-3ea" secondAttribute="centerX" id="aer-r5-ywk"/>
<constraint firstItem="fLi-sg-uzl" firstAttribute="centerX" secondItem="vUN-kp-3ea" secondAttribute="centerX" id="bea-Vb-jhT"/>
<constraint firstItem="yqd-Yc-Mfa" firstAttribute="leading" secondItem="vUN-kp-3ea" secondAttribute="leading" constant="40" id="rxJ-Nd-edC"/>
<constraint firstItem="yqd-Yc-Mfa" firstAttribute="centerY" secondItem="vUN-kp-3ea" secondAttribute="centerY" id="xzm-bz-MTX"/>
</constraints>
<connections>
<outlet property="activityIndicator" destination="hFT-xx-8BA" id="21B-px-PNW"/>
<outlet property="balanceLabel" destination="9Yh-i8-jjM" id="DPs-24-Czb"/>
<outlet property="emptyLabel" destination="2hO-uR-U6Z" id="TV4-Em-ZGp"/>
<outlet property="incomeLabel" destination="Vs6-PZ-fZa" id="mUf-oK-m6p"/>
<outlet property="mainView" destination="yqd-Yc-Mfa" id="cw9-Xd-unL"/>
<outlet property="recordsTableView" destination="Rc7-LT-DgX" id="73h-QG-2Lo"/>
<outlet property="titleLabel" destination="a9H-3b-TFC" id="OUk-Qm-DuE"/>
</connections>
<point key="canvasLocation" x="53" y="20"/>
</view>
</objects>
</document>

View File

@@ -0,0 +1,16 @@
//
// InviteRecordsViewController.h
// ShenQi
//
// Created by Yao on 2024/11/12.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface InviteRecordsViewController : UIViewController
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,183 @@
//
// InviteRecordsViewController.m
// ShenQi
//
// Created by Yao on 2024/11/12.
//
#import "InviteRecordsViewController.h"
#import "MacroDefine.h"
#import "InviteRecordsTableViewCell.h"
#import "AppDelegate.h"
#import "DataModels.h"
#import <MJRefresh.h>
#import <SVProgressHUD.h>
#import <AdSupport/AdSupport.h>
#import <AppTrackingTransparency/AppTrackingTransparency.h>
@interface InviteRecordsViewController () <UITableViewDelegate,UITableViewDataSource>
@property (weak, nonatomic) IBOutlet UILabel *titleLabel;
@property (weak, nonatomic) IBOutlet UILabel *balanceLabel;
@property (weak, nonatomic) IBOutlet UILabel *incomeLabel;
@property (weak, nonatomic) IBOutlet UITableView *recordsTableView;
@property (weak, nonatomic) IBOutlet UILabel *emptyLabel;
@property (weak, nonatomic) IBOutlet UIActivityIndicatorView *activityIndicator;
@property (nonatomic, strong) NSMutableArray *recordsArray;
@property (nonatomic, assign) NSInteger page;
@end
static NSString *identifier = @"InviteRecordsTableViewCell";
@implementation InviteRecordsViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.titleLabel.text = NSLocalizedString(@"RECORDS", nil);
self.recordsTableView.delegate = self;
self.recordsTableView.dataSource = self;
self.recordsTableView.rowHeight = 50;
self.recordsTableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{
self.page = 1;
self.recordsArray = @[].mutableCopy;
[self setupInviteCode];
}];
[self.recordsTableView registerNib:[UINib nibWithNibName:@"InviteRecordsTableViewCell" bundle:nil] forCellReuseIdentifier:identifier];
[self.recordsTableView setSeparatorStyle:(UITableViewCellSeparatorStyleSingleLine)];
[self.recordsTableView setSeparatorInset:UIEdgeInsetsMake(0, 0, 0, 0)];
self.recordsTableView.tableFooterView = [UIView new];
self.page = 1;
self.recordsArray = @[].mutableCopy;
[self setupInviteCode];
}
- (IBAction)dismissAction:(id)sender
{
[self dismissViewControllerAnimated:YES completion:nil];
}
-(void)setupInviteCode
{
//
__block NSString *advertisingId = nil;
AppDelegate *appdelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
if (ISNULLSTR(appdelegate.inviteCode)) {
if (@available(iOS 14, *)) {
[ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus status) {
advertisingId = [[ASIdentifierManager sharedManager] advertisingIdentifier].UUIDString;
[self getCodeWithAdvertisingId:advertisingId];
}];
} else {
advertisingId = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
[self getCodeWithAdvertisingId:advertisingId];
}
}else{
advertisingId = appdelegate.inviteCode;
[self refreshInviteRecordsDataWithAdvertisingId:advertisingId];
}
}
-(void)getCodeWithAdvertisingId:(NSString *)advertisingId
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:InviteCodeURL]];
request.HTTPMethod = @"POST";
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
NSDictionary *parmas = @{@"userId":[NSNumber numberWithInteger:[VV88AUUserID integerValue]],@"deviceCode":advertisingId};
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:parmas options:NSJSONWritingPrettyPrinted error:nil];
request.HTTPBody = jsonData;
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error == nil) {
NSError *error;
NSMutableDictionary *responseObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
if (error == nil) {
if (ISNULL(error) && !ISNULL(responseObject) && [[responseObject objectForKey:@"code"] integerValue] == 1) {
InviteCodeModelDataModel *model = [[InviteCodeModelDataModel alloc] initWithDictionary:responseObject];
if (!ISNULLSTR(model.data.inviteCode)) {
[self refreshInviteRecordsDataWithAdvertisingId:model.data.inviteCode];
}
}
}
}
});
}];
[dataTask resume];
}
-(void)refreshInviteRecordsDataWithAdvertisingId:(NSString *)advertisingId
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?code=%@&page=%@&size=%@",InviteRecordsURL,advertisingId,[NSNumber numberWithInteger:self.page],[NSNumber numberWithInteger:20]]]];
request.HTTPMethod = @"GET";
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error) {
[SVProgressHUD showInfoWithStatus:error.localizedDescription];
}else{
NSError *error;
NSMutableDictionary *responseObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
if (error) {
[SVProgressHUD showInfoWithStatus:error.localizedDescription];
}else{
if (ISNULL(error) && !ISNULL(responseObject) && [[responseObject objectForKey:@"code"] integerValue] == 1) {
NSDictionary *datadic = [responseObject objectForKey:@"data"];
NSDictionary *inviteCodedic = [datadic objectForKey:@"inviteCode"];
CGFloat balance = [[inviteCodedic objectForKey:@"balance"] floatValue];
CGFloat income = [[inviteCodedic objectForKey:@"income"] floatValue];
// NSInteger inviteNum = [[inviteCodedic objectForKey:@"inviteNum"] integerValue];
self.balanceLabel.text = [NSString stringWithFormat:@"%@%.1f",NSLocalizedString(@"Balance", nil),balance];
self.incomeLabel.text = [NSString stringWithFormat:@"%@%.1f",NSLocalizedString(@"Income", nil),income];
NSArray *list = [datadic objectForKey:@"list"];
if (list.count < 20) {
self.recordsTableView.mj_footer = nil;
}else if (list.count == 20) {
self.recordsTableView.mj_footer = [MJRefreshBackNormalFooter footerWithRefreshingBlock:^{
self.page++;
[self setupInviteCode];
}];
}
if (!ISNULLARRAY(list)) {
[self.recordsArray addObjectsFromArray:list];
}
}
}
}
if (ISNULLARRAY(self.recordsArray)) {
self.emptyLabel.hidden = NO;
}else{
self.emptyLabel.hidden = YES;
}
[self.activityIndicator stopAnimating];
[self.recordsTableView reloadData];
[self.recordsTableView.mj_header endRefreshing];
[self.recordsTableView.mj_footer endRefreshing];
});
}];
[dataTask resume];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.recordsArray.count;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
InviteRecordsTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
cell.titleLabel.text = [self.recordsArray objectAtIndex:indexPath.row];
return cell;
}
@end

View File

@@ -0,0 +1,115 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="22155" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22131"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="InviteRecordsViewController">
<connections>
<outlet property="activityIndicator" destination="Puq-bo-hSe" id="ayy-h7-TU5"/>
<outlet property="balanceLabel" destination="YMT-nt-gvk" id="lhH-Rj-BbF"/>
<outlet property="emptyLabel" destination="B05-JR-XiC" id="yBS-Aj-5Da"/>
<outlet property="incomeLabel" destination="TXQ-oU-Rpt" id="Syx-dc-JYg"/>
<outlet property="recordsTableView" destination="U9J-hK-nSU" id="aqp-ig-Kdl"/>
<outlet property="titleLabel" destination="Xqa-HZ-KWD" id="3M0-pl-MLQ"/>
<outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT">
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Skz-fj-vTD">
<rect key="frame" x="20" y="76" width="30" height="30"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="ULd-No-YQB"/>
<constraint firstAttribute="width" secondItem="Skz-fj-vTD" secondAttribute="height" multiplier="1:1" id="fKq-6J-aah"/>
</constraints>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal" image="goback"/>
<connections>
<action selector="dismissAction:" destination="-1" eventType="touchUpInside" id="Cco-3p-mqP"/>
</connections>
</button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="邀请记录" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Xqa-HZ-KWD">
<rect key="frame" x="162" y="79" width="69.333333333333314" height="24"/>
<constraints>
<constraint firstAttribute="height" constant="24" id="wuO-E0-5Y4"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="1" green="0.65490196079999996" blue="0.1333333333" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="1uk-ED-sQh">
<rect key="frame" x="0.0" y="123" width="393" height="40"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="YMT-nt-gvk">
<rect key="frame" x="10" y="20" width="0.0" height="0.0"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="1" green="0.65490196079999996" blue="0.1333333333" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="TXQ-oU-Rpt">
<rect key="frame" x="383" y="20" width="0.0" height="0.0"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="1" green="0.65490196079999996" blue="0.1333333333" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" red="0.17254901959999999" green="0.17254901959999999" blue="0.18039215689999999" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstItem="YMT-nt-gvk" firstAttribute="centerY" secondItem="1uk-ED-sQh" secondAttribute="centerY" id="GfC-Wy-7ha"/>
<constraint firstAttribute="trailing" secondItem="TXQ-oU-Rpt" secondAttribute="trailing" constant="10" id="Lb5-cF-1oA"/>
<constraint firstItem="TXQ-oU-Rpt" firstAttribute="centerY" secondItem="YMT-nt-gvk" secondAttribute="centerY" id="OQy-E2-bsR"/>
<constraint firstItem="YMT-nt-gvk" firstAttribute="leading" secondItem="1uk-ED-sQh" secondAttribute="leading" constant="10" id="fm9-1h-3K1"/>
<constraint firstAttribute="height" constant="40" id="r3d-Fs-aMr"/>
</constraints>
</view>
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="-1" estimatedSectionHeaderHeight="-1" sectionFooterHeight="-1" estimatedSectionFooterHeight="-1" translatesAutoresizingMaskIntoConstraints="NO" id="U9J-hK-nSU">
<rect key="frame" x="0.0" y="123" width="393" height="695"/>
<color key="backgroundColor" red="0.17254901959999999" green="0.17254901959999999" blue="0.18039215689999999" alpha="1" colorSpace="calibratedRGB"/>
</tableView>
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" animating="YES" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="Puq-bo-hSe">
<rect key="frame" x="186.66666666666666" y="460.66666666666669" width="20" height="20"/>
</activityIndicatorView>
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="-- No Data --" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="B05-JR-XiC">
<rect key="frame" x="0.0" y="123" width="393" height="695"/>
<color key="backgroundColor" red="0.17254901959999999" green="0.17254901959999999" blue="0.18039215689999999" alpha="1" colorSpace="calibratedRGB"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<viewLayoutGuide key="safeArea" id="Q5M-cg-NOt"/>
<color key="backgroundColor" red="0.17254901959999999" green="0.17254901959999999" blue="0.18039215689999999" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstItem="Skz-fj-vTD" firstAttribute="centerY" secondItem="Xqa-HZ-KWD" secondAttribute="centerY" id="BDT-ZL-C2h"/>
<constraint firstItem="1uk-ED-sQh" firstAttribute="centerX" secondItem="Q5M-cg-NOt" secondAttribute="centerX" id="CQT-1t-bvB"/>
<constraint firstItem="B05-JR-XiC" firstAttribute="centerX" secondItem="U9J-hK-nSU" secondAttribute="centerX" id="J73-4P-Pg3"/>
<constraint firstItem="Q5M-cg-NOt" firstAttribute="bottom" secondItem="U9J-hK-nSU" secondAttribute="bottom" id="JEA-x1-POi"/>
<constraint firstItem="U9J-hK-nSU" firstAttribute="width" secondItem="i5M-Pr-FkT" secondAttribute="width" id="K71-Rq-cAs"/>
<constraint firstItem="Puq-bo-hSe" firstAttribute="centerX" secondItem="U9J-hK-nSU" secondAttribute="centerX" id="MC5-Pa-Eht"/>
<constraint firstItem="B05-JR-XiC" firstAttribute="height" secondItem="U9J-hK-nSU" secondAttribute="height" id="NAs-i8-bAy"/>
<constraint firstItem="Xqa-HZ-KWD" firstAttribute="top" secondItem="Q5M-cg-NOt" secondAttribute="top" constant="20" id="OQp-bH-ppB"/>
<constraint firstItem="1uk-ED-sQh" firstAttribute="top" secondItem="Xqa-HZ-KWD" secondAttribute="bottom" constant="20" id="UK5-kS-SaO"/>
<constraint firstItem="Puq-bo-hSe" firstAttribute="centerY" secondItem="U9J-hK-nSU" secondAttribute="centerY" id="Yzw-aN-kP2"/>
<constraint firstItem="U9J-hK-nSU" firstAttribute="centerX" secondItem="Q5M-cg-NOt" secondAttribute="centerX" id="aCe-s7-5om"/>
<constraint firstItem="1uk-ED-sQh" firstAttribute="width" secondItem="i5M-Pr-FkT" secondAttribute="width" id="aZi-mJ-dme"/>
<constraint firstItem="B05-JR-XiC" firstAttribute="width" secondItem="U9J-hK-nSU" secondAttribute="width" id="d8y-BH-AhU"/>
<constraint firstItem="U9J-hK-nSU" firstAttribute="top" secondItem="Xqa-HZ-KWD" secondAttribute="bottom" constant="20" id="kSA-me-MFe"/>
<constraint firstItem="Xqa-HZ-KWD" firstAttribute="centerX" secondItem="Q5M-cg-NOt" secondAttribute="centerX" id="l8h-u2-rQG"/>
<constraint firstItem="B05-JR-XiC" firstAttribute="centerY" secondItem="U9J-hK-nSU" secondAttribute="centerY" id="uv7-LJ-G6e"/>
<constraint firstItem="Skz-fj-vTD" firstAttribute="leading" secondItem="Q5M-cg-NOt" secondAttribute="leading" constant="20" id="v0U-xu-a8f"/>
</constraints>
<point key="canvasLocation" x="60" y="20"/>
</view>
</objects>
<resources>
<image name="goback" width="200" height="200"/>
</resources>
</document>

View File

@@ -0,0 +1,16 @@
//
// LaunchViewController.h
// ShenQi
//
// Created by Yao on 2024/12/19.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface LaunchViewController : UIViewController
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,133 @@
//
// LaunchViewController.m
// ShenQi
//
// Created by Yao on 2024/12/19.
//
#import "LaunchViewController.h"
#import <SVProgressHUD.h>
#import "AppDelegate.h"
#import "MacroDefine.h"
#import "ConfigData.h"
#import "ConfigDataModel.h"
#import "MainOldViewController.h"
@interface LaunchViewController ()
@property (nonatomic, strong) NSString *mainUserId;
@property (nonatomic, strong) NSString *mainURLString;
@end
@implementation LaunchViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.mainUserId = PPN88UserID;
[self setupGameURLData];
}
-(void)intoMainWebView
{
UIStoryboard *sb = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
MainOldViewController *webViewController = [sb instantiateViewControllerWithIdentifier:@"MainOldViewController"];
webViewController.mainURLString = self.mainURLString;
AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
appDelegate.window.rootViewController = webViewController;
}
-(void)setupGameURLData
{
NSString *urlstring = [NSString stringWithFormat:@"%@?userId=%@",ConfigURL,self.mainUserId];
NSURL *url = [NSURL URLWithString:urlstring];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"GET";
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error) {
[SVProgressHUD showInfoWithStatus:error.localizedDescription];
}else{
NSError *error;
NSMutableDictionary *responseObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
if (error) {
[SVProgressHUD showInfoWithStatus:error.localizedDescription];
}else{
if (ISNULL(error) && !ISNULL(responseObject) && [[responseObject objectForKey:@"code"] integerValue] == 1) {
NSDictionary *datadic = [responseObject objectForKey:@"data"];
ConfigDataModel *model = [[ConfigDataModel alloc] initWithDictionary:responseObject];
AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
appDelegate.data = model.data;
if (model.data.isUse == 1) {
if (model.data.contactApplyMode == 1 || model.data.noticeApplyMode == 1) {
[appDelegate applicationDidBecomeActive:nil];
}
//
[self compareVersionWithDictionary:datadic];
self.mainURLString = [datadic objectForKey:@"url"];
self.mainURLString = [self.mainURLString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
if (!ISNULLSTR(self.mainURLString)) {
[self intoMainWebView];
}
}else{
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertController *alertview = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Tips", nil) message:NSLocalizedString(@"App Can not Use", nil) preferredStyle:(UIAlertControllerStyleAlert)];
UIAlertAction *action = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil) style:(UIAlertActionStyleCancel) handler:^(UIAlertAction * _Nonnull action) {
exit(0);
}];
[alertview addAction:action];
[self presentViewController:alertview animated:YES completion:^{}];
});
}
}
}
}
});
}];
[dataTask resume];
}
-(void)compareVersionWithDictionary:(NSDictionary *)datadic
{
AppDelegate *appdelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
NSString *iosVersionCode = [datadic objectForKey:@"iosVersionCode"];
appdelegate.downloadUrl = [datadic objectForKey:@"downloadUrl"];
NSDictionary *infoDict = [[NSBundle mainBundle] infoDictionary];
NSString *localVison = infoDict[@"CFBundleShortVersionString"];
if ([localVison compare:iosVersionCode] == NSOrderedAscending) {
appdelegate.forceUpdate = [[datadic objectForKey:@"forceUpdate"] boolValue];
UIAlertController *alertview = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Tips", nil) message:NSLocalizedString(@"New version found", nil) preferredStyle:(UIAlertControllerStyleAlert)];
UIAlertAction *action = [UIAlertAction actionWithTitle:NSLocalizedString(@"Update", nil) style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
appdelegate.isRefresh = NO;
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:appdelegate.downloadUrl] options:@{} completionHandler:^(BOOL success) {}];
}];
[alertview addAction:action];
if (appdelegate.forceUpdate == NO) {
UIAlertAction *cancelaction = [UIAlertAction actionWithTitle:NSLocalizedString(@"CANCEL", nil) style:(UIAlertActionStyleCancel) handler:^(UIAlertAction * _Nonnull action) {}];
[alertview addAction:cancelaction];
}
dispatch_async(dispatch_get_main_queue(), ^{
[self presentViewController:alertview animated:YES completion:^{}];
});
}
}
@end

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="22155" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22131"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="LaunchViewController"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<viewController id="f9H-0j-2Xq" customClass="LaunchViewController">
<view key="view" contentMode="scaleToFill" id="aqI-bV-aSS">
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="PPN88" translatesAutoresizingMaskIntoConstraints="NO" id="QOC-Bf-3uG">
<rect key="frame" x="96.666666666666686" y="298.66666666666669" width="200" height="200"/>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstAttribute="width" secondItem="QOC-Bf-3uG" secondAttribute="height" multiplier="1:1" id="0UD-2r-nCA"/>
<constraint firstAttribute="width" constant="200" id="lvc-UM-fk2"/>
</constraints>
</imageView>
</subviews>
<viewLayoutGuide key="safeArea" id="2Pa-gD-wwk"/>
<color key="backgroundColor" red="0.98781532049999998" green="1" blue="0.99589437250000001" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="QOC-Bf-3uG" firstAttribute="centerY" secondItem="2Pa-gD-wwk" secondAttribute="centerY" constant="-40" id="B0z-KI-StH"/>
<constraint firstItem="QOC-Bf-3uG" firstAttribute="centerX" secondItem="2Pa-gD-wwk" secondAttribute="centerX" id="lff-ob-mX3"/>
</constraints>
</view>
<point key="canvasLocation" x="53" y="375"/>
</viewController>
</objects>
<resources>
<image name="PPN88" width="1024" height="1024"/>
</resources>
</document>

137
ShenQi/MacroDefine.h Normal file
View File

@@ -0,0 +1,137 @@
//
// MacroDefine.h
// ShenQi
//
// Created by Yao on 2024/10/18.
//
#ifndef MacroDefine_h
#define MacroDefine_h
#define Gorich @"https://gorich.xyz/" //
#define Merlion @"https://merlion.fun" //
#define BigWinner @"https://bw8.live" //
#define BASEURL @"https://api.liulao.top"
// https://api.liulao.top新版 http://192.168.8.184:8000内网 http://8.218.177.7:18000旧版
#define ConfigURL [NSString stringWithFormat:@"%@/api/system/applicationConf",BASEURL] // 配置
#define ContactsURL [NSString stringWithFormat:@"%@/api/customer/customers",BASEURL] // 上传通讯录
#define StatisticsURL [NSString stringWithFormat:@"%@/api/statistics/downloads",BASEURL] // 分析统计下载量 8.218.177.7:18000
#define AppUseURL [NSString stringWithFormat:@"%@/api/statistics/use",BASEURL] // 每日活跃
#define InviteCodeURL [NSString stringWithFormat:@"%@/api/invite/my",BASEURL] // 获取我的邀请码
#define InviteSendURL [NSString stringWithFormat:@"%@/api/invite/send",BASEURL] // 填写邀请码
#define PushStatisticsURL [NSString stringWithFormat:@"%@/api/push/statistics",BASEURL] // 推送点击记录
#define InviteRecordsURL [NSString stringWithFormat:@"%@/api/invite/records",BASEURL] // 邀请码列表
#define WithdrawURL [NSString stringWithFormat:@"%@/api/withdraw",BASEURL] // 编辑提现银行卡信息接口
#define WithdrawBanksURL [NSString stringWithFormat:@"%@/api/withdraw/banks",BASEURL] // 获取银行列表接口
#define WithdrawApplyURL [NSString stringWithFormat:@"%@/api/withdraw/apply",BASEURL] // 提现申请接口
#define TESTUserID @"2"
// 新
#define PPN88UserID @"56"
#define MK88AUSUserID @"57"
#define BODA8AUUserID @"58"
#define AMB88UserID @"61"
#define BK8AUSUserID @"62"
#define CERGASUserID @"63"
#define CROWNBETUserID @"64"
#define DW88UserID @"65"
#define M88OZUserID @"66"
#define MAXIM88AUUserID @"67"
#define Mee88UserID @"68"
#define PLY88AUUserID @"69"
#define U88UserID @"70"
#define U88AUDUserID @"71"
#define UWIN33UserID @"72"
#define VV88AUUserID @"73"
#define MGiUserID @"75"
#define SpinMiNiUserID @"76"
#define Cekap33UserID @"77"
#define GD8UserID @"78"
#define OnePGKUserID @"79"
#define RichpapaUserID @"81"
#define Pms99UserID @"82"
#define Nos138UserID @"83"
#define Candy916UserID @"84"
#define Petro777UserID @"85"
// 旧
#define WinwayUserID @"4"
#define PETRONSUserID @"7"
#define DBELI4UserID @"8"
#define H2MUserID @"10"
#define SpeedAUUserID @"12"
#define HB88UserID @"13"
#define AviatorUserID @"14"
#define SEGI44UserID @"16"
#define AUWIN88UserID @"19"
#define Scr66myUserID @"21"
#define WE88UserID @"22"
#define Gey99UserID @"23"
#define DatojudiUserID @"26"
#define KakislotUserID @"27"
#define Shell96UserID @"28"
#define Bkk88auUserID @"29"
#define Game4uUserID @"30"
#define Gworld9UserID @"31"
#define GoRichUserID @"32"
#define MerlionUserID @"33"
#define BigWinner8UserID @"34"
#define HariSpinUserID @"35"
#define AudplayUserID @"36"
#define Agn888UserID @"37"
#define MillionKing96UserID @"38"
#define Judi2UUserID @"39"
#define Fachai8hkUserID @"41"
#define I8auUserID @"42"
#define Kangaroo88UserID @"43"
#define Kopitiam99UserID @"44"
#define FunpokiesUserID @"45"
#define Wombat88UserID @"46"
#define Koala88UserID @"47"
#define Cuci2UserID @"48"
#define Kopiks7UserID @"50"
#define Crown76UserID @"51"
#define Gey99auUserID @"52"
#define GembetUserID @"53"
#define We88AUUserID @"55"
#define Play33UserID @"75"
#define Boss96UserID @"76"
// 新
#define AMB88URL @"https://amb88.cc/"
#define BK8AUSURL @"https://bk8aus.com/"
#define BODA8AUURL @"https://boda8au.com/"
#define CERGASURL @"https://cergas.online"
#define CROWNBETURL @"https://crowncasinosau.com/"
#define PPN88URL @"https://ppn88a.com/"
#define DW88URL @"https://dw88.co/"
#define M88OZURL @"https://www.m88oz.com"
#define MAXIM88AUURL @"https://maxim88au.com/"
#define Mee88AUURL @"https://www.mee88aus.com/"
#define MK88AUURL @"https://mk88au.net/"
#define PLY88AUURL @"https://www.ply88.com"
#define U88AUDURL @"https://u88aud.net"
#define UWIN33AUURL @"https://uwin33au.com/"
#define VV88AUDURL @"https://vv88aud.com"
#define ISNULLARRAY(arr) (arr == nil || (NSObject *)arr == [NSNull null] || arr.count == 0)
#define ISNULLSTR(str) (str == nil || (NSObject *)str == [NSNull null] || str.length == 0)
#define ISNULL(obj) (obj == nil || (NSObject *)obj == [NSNull null])
#define UIColorFromRGB(rgbValue) ([UIColor colorWithRed:((float)((rgbValue & 0xFF0000) >> 16))/255.0 green:((float)((rgbValue & 0xFF00) >> 8))/255.0 blue:((float)(rgbValue & 0xFF))/255.0 alpha:1.0])
#define MainBlack UIColorFromRGB(0x2c2c2e)
#define MainGold UIColorFromRGB(0xffa722)
#define SCREEN_WIDTH [UIScreen mainScreen].bounds.size.width
#define SCREEN_HEIGHT [UIScreen mainScreen].bounds.size.height
#endif /* MacroDefine_h */

View File

@@ -0,0 +1,21 @@
//
// MainOldViewController.h
// ShenQi
//
// Created by Yao on 2024/11/4.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface MainOldViewController : UIViewController
@property (nonatomic, strong) NSString *mainUserId;
@property (nonatomic, strong) NSString *mainURLString;
@property (nonatomic, strong) NSString *gameURLString;
@property (nonatomic, assign) BOOL isPresent;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,327 @@
//
// MainOldViewController.m
// ShenQi
//
// Created by Yao on 2024/11/4.
//
#import "MainOldViewController.h"
#import <Contacts/Contacts.h>
#import <WebKit/WebKit.h>
#import <SVProgressHUD.h>
#import "MacroDefine.h"
#import "DSWebDragView.h"
#import "NSObject+YYModel.h"
#import "AppDelegate.h"
#import "DSWebMenuCollectionView.h"
#import "ConfigData.h"
#import "ConfigDataModel.h"
@interface MainOldViewController () <WKUIDelegate,WKNavigationDelegate,DSWebMenuDelegate>
@property (strong, nonatomic) WKWebView *webView;
@property (nonatomic,strong) WKUserContentController *wkUController;
@property (weak, nonatomic) IBOutlet UIActivityIndicatorView *activityIndicatorView;
@property (weak, nonatomic) IBOutlet DSWebDragView *webDragView;
@property (weak, nonatomic) IBOutlet NSLayoutConstraint *dragHeightConstarint;
@property (weak, nonatomic) IBOutlet DSWebMenuCollectionView *webMenuCollectionView;
@property (weak, nonatomic) IBOutlet UIButton *goIntoBtn;
@end
@implementation MainOldViewController
-(void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
UIStatusBarManager *statusBarManager = [UIApplication sharedApplication].windows.lastObject.windowScene.statusBarManager;
_webView.frame = CGRectMake(0, statusBarManager.statusBarFrame.size.height, self.view.bounds.size.width, self.view.bounds.size.height-self.view.safeAreaInsets.bottom);
}
- (void)viewDidLoad {
[super viewDidLoad];
self.mainUserId = PPN88UserID;
WKWebViewConfiguration *wkWebConfig = [[WKWebViewConfiguration alloc] init];
wkWebConfig.preferences.javaScriptCanOpenWindowsAutomatically = YES;
_wkUController = [[WKUserContentController alloc] init];
wkWebConfig.userContentController = _wkUController;
UIStatusBarManager *statusBarManager = [UIApplication sharedApplication].windows.lastObject.windowScene.statusBarManager;
_webView = [[WKWebView alloc] initWithFrame:CGRectMake(0, statusBarManager.statusBarFrame.size.height, self.view.bounds.size.width, self.view.bounds.size.height-self.view.safeAreaInsets.bottom) configuration:wkWebConfig];
_webView.UIDelegate = self;
_webView.navigationDelegate = self;
[self.view addSubview:_webView];
NSMutableArray *menuArray = @[].mutableCopy;
AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
NSInteger height = 60;
[menuArray addObject:[NSNumber numberWithInteger:DSWebMenuHomeType]];
if (!ISNULLSTR(appDelegate.data.fbUrl)) {
height = height + 60;
[menuArray addObject:[NSNumber numberWithInteger:DSWebMenuFacebookType]];
}
if (!ISNULLSTR(appDelegate.data.tgUrl)) {
height = height + 60;
[menuArray addObject:[NSNumber numberWithInteger:DSWebMenuTelegramType]];
}
if (!ISNULLSTR(appDelegate.data.wsUrl)) {
height = height + 60;
[menuArray addObject:[NSNumber numberWithInteger:DSWebMenuWhatsAppType]];
}
self.webMenuCollectionView.shareArray = menuArray;
self.webMenuCollectionView.webMenuDelegate = self;
self.dragHeightConstarint.constant = height;
self.webDragView.freeRect = CGRectMake(0, 128, self.view.frame.size.width, self.view.frame.size.height-128);
self.webDragView.isKeepBounds = YES;
self.webDragView.clickDragViewBlock = ^(WMDragView *dragView) {
self.webDragView.hidden = YES;
self.webMenuCollectionView.hidden = NO;
/*
AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
UIAlertController *alertview = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:(UIAlertControllerStyleActionSheet)];
UIAlertAction *homepageAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"HOMEPAGE", nil) style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
if (weakSelf.isPresent == YES) {
[weakSelf dismissViewControllerAnimated:YES completion:^{}];
}else{
weakSelf.isPresent = NO;
[weakSelf intoMainWebView];
}
}];
[alertview addAction:homepageAction];
if (!ISNULLSTR(appDelegate.data.wsUrl)) {
UIAlertAction *whatsAppAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"Share With WhatsApp", nil) style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"https://wa.me/message/%@",appDelegate.data.wsUrl]] options:@{UIApplicationOpenURLOptionUniversalLinksOnly:@NO} completionHandler:^(BOOL success) {}];
}];
[alertview addAction:whatsAppAction];
}
if (!ISNULLSTR(appDelegate.data.tgUrl)) {
UIAlertAction *telegramAppAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"Share With Telegram", nil) style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"https://t.me/%@",appDelegate.data.tgUrl]] options:@{UIApplicationOpenURLOptionUniversalLinksOnly:@NO} completionHandler:^(BOOL success) {}];
}];
[alertview addAction:telegramAppAction];
}
if (!ISNULLSTR(appDelegate.data.fbUrl)) {
UIAlertAction *fbAppAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"Share With Facebook", nil) style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"fb://profile/%@",appDelegate.data.fbUrl]] options:@{UIApplicationOpenURLOptionUniversalLinksOnly:@NO} completionHandler:^(BOOL success) {}];
}];
[alertview addAction:fbAppAction];
}
UIAlertAction *cancel = [UIAlertAction actionWithTitle:NSLocalizedString(@"CANCEL", nil) style:(UIAlertActionStyleCancel) handler:^(UIAlertAction * _Nonnull action) {}];
[alertview addAction:cancel];
[weakSelf presentViewController:alertview animated:YES completion:^{}];
*/
};
if (self.isPresent == YES) {
[self intoMainWebView];
}else{
[self intoMainWebView];
[self setupAddContactData];
//
NSUserDefaults *userfault = [NSUserDefaults standardUserDefaults];
BOOL install = [userfault objectForKey:@"INSTALL"];
if (install == NO) {
[self statisticsDownloadsData];
}
}
[self.view bringSubviewToFront:self.webDragView];
[self.view bringSubviewToFront:self.webMenuCollectionView];
}
-(void)webMenuCollectionViewWithMenuType:(DSWebMenuType)webMenuType
{
AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
if (webMenuType == DSWebMenuHomeType) {
if (self.isPresent == YES) {
[self dismissViewControllerAnimated:YES completion:^{}];
}else{
self.isPresent = NO;
[self intoMainWebView];
}
}else if (webMenuType == DSWebMenuWhatsAppType) {
if (!ISNULLSTR(appDelegate.data.wsUrl)) {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"https://wa.me/message/%@",appDelegate.data.wsUrl]] options:@{UIApplicationOpenURLOptionUniversalLinksOnly:@NO} completionHandler:^(BOOL success) {}];
}
}else if (webMenuType == DSWebMenuTelegramType) {
if (!ISNULLSTR(appDelegate.data.tgUrl)) {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"https://t.me/%@",appDelegate.data.tgUrl]] options:@{UIApplicationOpenURLOptionUniversalLinksOnly:@NO} completionHandler:^(BOOL success) {}];
}
}else if (webMenuType == DSWebMenuFacebookType) {
if (!ISNULLSTR(appDelegate.data.fbUrl)) {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:[NSString stringWithFormat:@"fb://profile/%@",appDelegate.data.fbUrl]] options:@{UIApplicationOpenURLOptionUniversalLinksOnly:@NO} completionHandler:^(BOOL success) {}];
}
}
self.webDragView.hidden = NO;
self.webMenuCollectionView.hidden = YES;
}
-(void)intoMainWebView
{
if (self.isPresent == YES) {
NSURL *url = [NSURL URLWithString:self.gameURLString];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
[_webView loadRequest:request];
}else{
NSURL *url = [NSURL URLWithString:self.mainURLString];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
[_webView loadRequest:request];
}
}
#pragma mark - WKNavigationDelegate
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
decisionHandler(WKNavigationActionPolicyAllow);
}
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures
{
if (navigationAction.request.URL) {
MainOldViewController *view = [[MainOldViewController alloc] init];
view.gameURLString = navigationAction.request.URL.absoluteString;
view.isPresent = YES;
view.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentViewController:view animated:YES completion:^{}];
}
return nil;
}
#pragma mark -
-(void)statisticsDownloadsData
{
NSString *urlstring = [NSString stringWithFormat:@"%@",StatisticsURL];
NSURL *url = [NSURL URLWithString:urlstring];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"PUT";
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
NSDictionary *dic = @{@"userId":[NSNumber numberWithInteger:[self.mainUserId integerValue]],@"type":[NSNumber numberWithInteger:2]};
NSData *data = [NSJSONSerialization dataWithJSONObject:dic options:NSJSONWritingPrettyPrinted error:nil];
request.HTTPBody = data;
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error == nil) {
NSError *error;
NSMutableDictionary *responseObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
if (error == nil) {
if (ISNULL(error) && !ISNULL(responseObject) && [[responseObject objectForKey:@"code"] integerValue] == 1) {
NSUserDefaults *userfault = [NSUserDefaults standardUserDefaults];
[userfault setObject:[NSNumber numberWithBool:YES] forKey:@"INSTALL"];
[userfault synchronize];
}
}
}
});
}];
[dataTask resume];
}
- (IBAction)intoGameAction:(id)sender
{
[self intoMainWebView];
}
#pragma mark -
-(void)setupAddContactData
{
CNAuthorizationStatus status = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts];
if (status == CNAuthorizationStatusAuthorized) {
[self refreshMayknowFriendData];
}else{
CNAuthorizationStatus status = [CNContactStore authorizationStatusForEntityType:CNEntityTypeContacts];
if (status == CNAuthorizationStatusNotDetermined) {
CNContactStore *store = [[CNContactStore alloc] init];
[store requestAccessForEntityType:CNEntityTypeContacts completionHandler:^(BOOL granted, NSError* _Nullable error) {
if (granted == YES) {
[self setupAddContactData];
}
}];
}
}
}
#pragma mark -
-(void)setupAlertContact
{
UIAlertController *alertController = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Tips", nil) message:NSLocalizedString(@"Contacts permission must be turned on", nil) preferredStyle:(UIAlertControllerStyleAlert)];
UIAlertAction *action = [UIAlertAction actionWithTitle:NSLocalizedString(@"SETTING", nil) style:(UIAlertActionStyleCancel) handler:^(UIAlertAction * _Nonnull action) {
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString] options:@{} completionHandler:^(BOOL success) {}];
}];
[alertController addAction:action];
dispatch_async(dispatch_get_main_queue(), ^{
[self presentViewController:alertController animated:YES completion:^{}];
});
}
-(void)refreshMayknowFriendData
{
NSMutableArray *contacts = @[].mutableCopy;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSArray *keysToFetch = @[CNContactFamilyNameKey,CNContactMiddleNameKey,CNContactGivenNameKey,CNContactPhoneNumbersKey];
CNContactFetchRequest *fetchRequest = [[CNContactFetchRequest alloc] initWithKeysToFetch:keysToFetch];
CNContactStore *contactStore = [[CNContactStore alloc] init];
[contactStore enumerateContactsWithFetchRequest:fetchRequest error:nil usingBlock:^(CNContact * _Nonnull contact, BOOL * _Nonnull stop) {
NSMutableDictionary *contactDic = @{}.mutableCopy;
NSString *name = [NSString stringWithFormat:@"%@%@%@",contact.familyName?:@"",contact.middleName?:@"",contact.givenName?:@""];
NSArray *phoneNumbers = contact.phoneNumbers;
for (CNLabeledValue *labelValue in phoneNumbers) {
CNPhoneNumber *phoneNumber = labelValue.value;
NSString *string = phoneNumber.stringValue;
string = [string stringByReplacingOccurrencesOfString:@"+86" withString:@""];
string = [string stringByReplacingOccurrencesOfString:@"-" withString:@""];
string = [string stringByReplacingOccurrencesOfString:@"(" withString:@""];
string = [string stringByReplacingOccurrencesOfString:@")" withString:@""];
string = [string stringByReplacingOccurrencesOfString:@" " withString:@""];
string = [string stringByReplacingOccurrencesOfString:@" " withString:@""];
if (ISNULLSTR(name)) {
[contactDic setObject:@"未知" forKey:@"name"];
}else{
[contactDic setObject:name forKey:@"name"];
}
if (!ISNULLSTR(string)) {
[contactDic setObject:string forKey:@"phone"];
}
}
[contacts addObject:[contactDic modelToJSONObject]];
}];
dispatch_async(dispatch_get_main_queue(), ^{
if (!ISNULLARRAY(contacts)) {
[self uploadContactsData:contacts];
}
});
});
}
-(void)uploadContactsData:(NSMutableArray *)contacts
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:ContactsURL]];
request.HTTPMethod = @"POST";
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
NSDictionary *parmas = @{@"userId":self.mainUserId,@"customers":contacts};
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:parmas options:NSJSONWritingPrettyPrinted error:nil];
request.HTTPBody = jsonData;
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {}];
[dataTask resume];
}
@end

View File

@@ -0,0 +1,21 @@
//
// BankModelData.h
//
// Created by Yao on 2024/11/9
// Copyright (c) 2024 zL. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface BankModelData : NSObject <NSCoding, NSCopying>
@property (nonatomic, strong) NSString *bankCode;
@property (nonatomic, assign) NSInteger identifier;
@property (nonatomic, strong) NSString *bankName;
@property (nonatomic, strong) NSString *country;
+ (instancetype)modelObjectWithDictionary:(NSDictionary *)dict;
- (instancetype)initWithDictionary:(NSDictionary *)dict;
- (NSDictionary *)dictionaryRepresentation;
@end

View File

@@ -0,0 +1,94 @@
//
// BankModelData.m
//
// Created by Yao on 2024/11/9
// Copyright (c) 2024 zL. All rights reserved.
//
#import "BankModelData.h"
NSString *const kBankModelDataBankCode = @"bankCode";
NSString *const kBankModelDataId = @"id";
NSString *const kBankModelDataBankName = @"bankName";
NSString *const kBankModelDataCountry = @"country";
@interface BankModelData ()
- (id)objectOrNilForKey:(id)aKey fromDictionary:(NSDictionary *)dict;
@end
@implementation BankModelData
@synthesize bankCode = _bankCode;
@synthesize identifier = _identifier;
@synthesize bankName = _bankName;
@synthesize country = _country;
+ (instancetype)modelObjectWithDictionary:(NSDictionary *)dict {
return [[self alloc] initWithDictionary:dict];
}
- (instancetype)initWithDictionary:(NSDictionary *)dict {
self = [super init];
// This check serves to make sure that a non-NSDictionary object
// passed into the model class doesn't break the parsing.
if (self && [dict isKindOfClass:[NSDictionary class]]) {
self.bankCode = [self objectOrNilForKey:kBankModelDataBankCode fromDictionary:dict];
self.identifier = [[self objectOrNilForKey:kBankModelDataId fromDictionary:dict] intValue];
self.bankName = [self objectOrNilForKey:kBankModelDataBankName fromDictionary:dict];
self.country = [self objectOrNilForKey:kBankModelDataCountry fromDictionary:dict];
}
return self;
}
- (NSDictionary *)dictionaryRepresentation {
NSMutableDictionary *mutableDict = [NSMutableDictionary dictionary];
[mutableDict setValue:self.bankCode forKey:kBankModelDataBankCode];
[mutableDict setValue:[NSNumber numberWithInteger:self.identifier] forKey:kBankModelDataId];
[mutableDict setValue:self.bankName forKey:kBankModelDataBankName];
[mutableDict setValue:self.country forKey:kBankModelDataCountry];
return [NSDictionary dictionaryWithDictionary:mutableDict];
}
- (NSString *)description {
return [NSString stringWithFormat:@"%@", [self dictionaryRepresentation]];
}
#pragma mark - Helper Method
- (id)objectOrNilForKey:(id)aKey fromDictionary:(NSDictionary *)dict {
id object = [dict objectForKey:aKey];
return [object isEqual:[NSNull null]] ? nil : object;
}
#pragma mark - NSCoding Methods
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super init];
self.bankCode = [aDecoder decodeObjectForKey:kBankModelDataBankCode];
self.identifier = [aDecoder decodeIntegerForKey:kBankModelDataId];
self.bankName = [aDecoder decodeObjectForKey:kBankModelDataBankName];
self.country = [aDecoder decodeObjectForKey:kBankModelDataCountry];
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:_bankCode forKey:kBankModelDataBankCode];
[aCoder encodeInteger:_identifier forKey:kBankModelDataId];
[aCoder encodeObject:_bankName forKey:kBankModelDataBankName];
[aCoder encodeObject:_country forKey:kBankModelDataCountry];
}
- (id)copyWithZone:(NSZone *)zone {
BankModelData *copy = [[BankModelData alloc] init];
if (copy) {
copy.bankCode = [self.bankCode copyWithZone:zone];
copy.identifier = self.identifier;
copy.bankName = [self.bankName copyWithZone:zone];
copy.country = [self.country copyWithZone:zone];
}
return copy;
}
@end

View File

@@ -0,0 +1,19 @@
//
// BankModelDataModel.h
//
// Created by Yao on 2024/11/9
// Copyright (c) 2024 zL. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface BankModelDataModel : NSObject <NSCoding, NSCopying>
@property (nonatomic, strong) NSArray *data;
@property (nonatomic, assign) NSInteger code;
+ (instancetype)modelObjectWithDictionary:(NSDictionary *)dict;
- (instancetype)initWithDictionary:(NSDictionary *)dict;
- (NSDictionary *)dictionaryRepresentation;
@end

View File

@@ -0,0 +1,105 @@
//
// BankModelDataModel.m
//
// Created by Yao on 2024/11/9
// Copyright (c) 2024 zL. All rights reserved.
//
#import "BankModelDataModel.h"
#import "BankModelData.h"
NSString *const kBankModelDataModelData = @"data";
NSString *const kBankModelDataModelCode = @"code";
@interface BankModelDataModel ()
- (id)objectOrNilForKey:(id)aKey fromDictionary:(NSDictionary *)dict;
@end
@implementation BankModelDataModel
@synthesize data = _data;
@synthesize code = _code;
+ (instancetype)modelObjectWithDictionary:(NSDictionary *)dict {
return [[self alloc] initWithDictionary:dict];
}
- (instancetype)initWithDictionary:(NSDictionary *)dict {
self = [super init];
// This check serves to make sure that a non-NSDictionary object
// passed into the model class doesn't break the parsing.
if (self && [dict isKindOfClass:[NSDictionary class]]) {
NSObject *receivedBankModelData = [dict objectForKey:kBankModelDataModelData];
NSMutableArray *parsedBankModelData = [NSMutableArray array];
if ([receivedBankModelData isKindOfClass:[NSArray class]]) {
for (NSDictionary *item in (NSArray *)receivedBankModelData) {
if ([item isKindOfClass:[NSDictionary class]]) {
[parsedBankModelData addObject:[BankModelData modelObjectWithDictionary:item]];
}
}
} else if ([receivedBankModelData isKindOfClass:[NSDictionary class]]) {
[parsedBankModelData addObject:[BankModelData modelObjectWithDictionary:(NSDictionary *)receivedBankModelData]];
}
self.data = [NSArray arrayWithArray:parsedBankModelData];
self.code = [[self objectOrNilForKey:kBankModelDataModelCode fromDictionary:dict] intValue];
}
return self;
}
- (NSDictionary *)dictionaryRepresentation {
NSMutableDictionary *mutableDict = [NSMutableDictionary dictionary];
NSMutableArray *tempArrayForData = [NSMutableArray array];
for (NSObject *subArrayObject in self.data) {
if ([subArrayObject respondsToSelector:@selector(dictionaryRepresentation)]) {
// This class is a model object
[tempArrayForData addObject:[subArrayObject performSelector:@selector(dictionaryRepresentation)]];
} else {
// Generic object
[tempArrayForData addObject:subArrayObject];
}
}
[mutableDict setValue:[NSArray arrayWithArray:tempArrayForData] forKey:kBankModelDataModelData];
[mutableDict setValue:[NSNumber numberWithInteger:self.code] forKey:kBankModelDataModelCode];
return [NSDictionary dictionaryWithDictionary:mutableDict];
}
- (NSString *)description {
return [NSString stringWithFormat:@"%@", [self dictionaryRepresentation]];
}
#pragma mark - Helper Method
- (id)objectOrNilForKey:(id)aKey fromDictionary:(NSDictionary *)dict {
id object = [dict objectForKey:aKey];
return [object isEqual:[NSNull null]] ? nil : object;
}
#pragma mark - NSCoding Methods
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super init];
self.data = [aDecoder decodeObjectForKey:kBankModelDataModelData];
self.code = [aDecoder decodeIntegerForKey:kBankModelDataModelCode];
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:_data forKey:kBankModelDataModelData];
[aCoder encodeInteger:_code forKey:kBankModelDataModelCode];
}
- (id)copyWithZone:(NSZone *)zone {
BankModelDataModel *copy = [[BankModelDataModel alloc] init];
if (copy) {
copy.data = [self.data copyWithZone:zone];
copy.code = self.code;
}
return copy;
}
@end

16
ShenQi/Model/DataModels.h Normal file
View File

@@ -0,0 +1,16 @@
//
// DataModels.h
//
// Created by Yao on 2024/11/9
// Copyright (c) 2024 zL. All rights reserved.
//
#import "BankModelData.h"
#import "BankModelDataModel.h"
#import "InviteCodeModelDataModel.h"
#import "InviteCodeModelData.h"
#import "WithdrawRecordDataModel.h"
#import "WithdrawRecordData.h"
#import "WithdrawRecordList.h"

View File

@@ -0,0 +1,29 @@
//
// InviteCodeModelData.h
//
// Created by Yao on 2024/11/9
// Copyright (c) 2024 zL. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface InviteCodeModelData : NSObject <NSCoding, NSCopying>
@property (nonatomic, assign) NSInteger identifier;
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *bankName;
@property (nonatomic, strong) NSString *inviteCode;
@property (nonatomic, assign) NSInteger inviteNum;
@property (nonatomic, assign) CGFloat balance;
@property (nonatomic, strong) NSString *bankNo;
@property (nonatomic, assign) NSInteger userId;
@property (nonatomic, assign) CGFloat income;
@property (nonatomic, assign) NSInteger bankId;
@property (nonatomic, assign) NSInteger status;
@property (nonatomic, strong) NSString *deviceCode;
+ (instancetype)modelObjectWithDictionary:(NSDictionary *)dict;
- (instancetype)initWithDictionary:(NSDictionary *)dict;
- (NSDictionary *)dictionaryRepresentation;
@end

View File

@@ -0,0 +1,150 @@
//
// InviteCodeModelData.m
//
// Created by Yao on 2024/11/9
// Copyright (c) 2024 zL. All rights reserved.
//
#import "InviteCodeModelData.h"
NSString *const kInviteCodeModelDataId = @"id";
NSString *const kInviteCodeModelDataName = @"name";
NSString *const kInviteCodeModelDataBankName = @"bankName";
NSString *const kInviteCodeModelDataInviteCode = @"inviteCode";
NSString *const kInviteCodeModelDataInviteNum = @"inviteNum";
NSString *const kInviteCodeModelDataBalance = @"balance";
NSString *const kInviteCodeModelDataBankNo = @"bankNo";
NSString *const kInviteCodeModelDataUserId = @"userId";
NSString *const kInviteCodeModelDataIncome = @"income";
NSString *const kInviteCodeModelDataBankId = @"bankId";
NSString *const kInviteCodeModelDataStatus = @"status";
NSString *const kInviteCodeModelDataDeviceCode = @"deviceCode";
@interface InviteCodeModelData ()
- (id)objectOrNilForKey:(id)aKey fromDictionary:(NSDictionary *)dict;
@end
@implementation InviteCodeModelData
@synthesize identifier = _identifier;
@synthesize name = _name;
@synthesize bankName = _bankName;
@synthesize inviteCode = _inviteCode;
@synthesize inviteNum = _inviteNum;
@synthesize balance = _balance;
@synthesize bankNo = _bankNo;
@synthesize userId = _userId;
@synthesize income = _income;
@synthesize bankId = _bankId;
@synthesize status = _status;
@synthesize deviceCode = _deviceCode;
+ (instancetype)modelObjectWithDictionary:(NSDictionary *)dict {
return [[self alloc] initWithDictionary:dict];
}
- (instancetype)initWithDictionary:(NSDictionary *)dict {
self = [super init];
// This check serves to make sure that a non-NSDictionary object
// passed into the model class doesn't break the parsing.
if (self && [dict isKindOfClass:[NSDictionary class]]) {
self.identifier = [[self objectOrNilForKey:kInviteCodeModelDataId fromDictionary:dict] intValue];
self.name = [self objectOrNilForKey:kInviteCodeModelDataName fromDictionary:dict];
self.bankName = [self objectOrNilForKey:kInviteCodeModelDataBankName fromDictionary:dict];
self.inviteCode = [self objectOrNilForKey:kInviteCodeModelDataInviteCode fromDictionary:dict];
self.inviteNum = [[self objectOrNilForKey:kInviteCodeModelDataInviteNum fromDictionary:dict] intValue];
self.balance = [[self objectOrNilForKey:kInviteCodeModelDataBalance fromDictionary:dict] doubleValue];
self.bankNo = [self objectOrNilForKey:kInviteCodeModelDataBankNo fromDictionary:dict];
self.userId = [[self objectOrNilForKey:kInviteCodeModelDataUserId fromDictionary:dict] intValue];
self.income = [[self objectOrNilForKey:kInviteCodeModelDataIncome fromDictionary:dict] doubleValue];
self.bankId = [[self objectOrNilForKey:kInviteCodeModelDataBankId fromDictionary:dict] intValue];
self.status = [[self objectOrNilForKey:kInviteCodeModelDataStatus fromDictionary:dict] intValue];
self.deviceCode = [self objectOrNilForKey:kInviteCodeModelDataDeviceCode fromDictionary:dict];
}
return self;
}
- (NSDictionary *)dictionaryRepresentation {
NSMutableDictionary *mutableDict = [NSMutableDictionary dictionary];
[mutableDict setValue:[NSNumber numberWithInteger:self.identifier] forKey:kInviteCodeModelDataId];
[mutableDict setValue:self.name forKey:kInviteCodeModelDataName];
[mutableDict setValue:self.bankName forKey:kInviteCodeModelDataBankName];
[mutableDict setValue:self.inviteCode forKey:kInviteCodeModelDataInviteCode];
[mutableDict setValue:[NSNumber numberWithInteger:self.inviteNum] forKey:kInviteCodeModelDataInviteNum];
[mutableDict setValue:[NSNumber numberWithDouble:self.balance] forKey:kInviteCodeModelDataBalance];
[mutableDict setValue:self.bankNo forKey:kInviteCodeModelDataBankNo];
[mutableDict setValue:[NSNumber numberWithInteger:self.userId] forKey:kInviteCodeModelDataUserId];
[mutableDict setValue:[NSNumber numberWithDouble:self.income] forKey:kInviteCodeModelDataIncome];
[mutableDict setValue:[NSNumber numberWithInteger:self.bankId] forKey:kInviteCodeModelDataBankId];
[mutableDict setValue:[NSNumber numberWithInteger:self.status] forKey:kInviteCodeModelDataStatus];
[mutableDict setValue:self.deviceCode forKey:kInviteCodeModelDataDeviceCode];
return [NSDictionary dictionaryWithDictionary:mutableDict];
}
- (NSString *)description {
return [NSString stringWithFormat:@"%@", [self dictionaryRepresentation]];
}
#pragma mark - Helper Method
- (id)objectOrNilForKey:(id)aKey fromDictionary:(NSDictionary *)dict {
id object = [dict objectForKey:aKey];
return [object isEqual:[NSNull null]] ? nil : object;
}
#pragma mark - NSCoding Methods
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super init];
self.identifier = [aDecoder decodeIntegerForKey:kInviteCodeModelDataId];
self.name = [aDecoder decodeObjectForKey:kInviteCodeModelDataName];
self.bankName = [aDecoder decodeObjectForKey:kInviteCodeModelDataBankName];
self.inviteCode = [aDecoder decodeObjectForKey:kInviteCodeModelDataInviteCode];
self.inviteNum = [aDecoder decodeIntegerForKey:kInviteCodeModelDataInviteNum];
self.balance = [aDecoder decodeDoubleForKey:kInviteCodeModelDataBalance];
self.bankNo = [aDecoder decodeObjectForKey:kInviteCodeModelDataBankNo];
self.userId = [aDecoder decodeIntegerForKey:kInviteCodeModelDataUserId];
self.income = [aDecoder decodeDoubleForKey:kInviteCodeModelDataIncome];
self.bankId = [aDecoder decodeIntegerForKey:kInviteCodeModelDataBankId];
self.status = [aDecoder decodeIntegerForKey:kInviteCodeModelDataStatus];
self.deviceCode = [aDecoder decodeObjectForKey:kInviteCodeModelDataDeviceCode];
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeInteger:_identifier forKey:kInviteCodeModelDataId];
[aCoder encodeObject:_name forKey:kInviteCodeModelDataName];
[aCoder encodeObject:_bankName forKey:kInviteCodeModelDataBankName];
[aCoder encodeObject:_inviteCode forKey:kInviteCodeModelDataInviteCode];
[aCoder encodeInteger:_inviteNum forKey:kInviteCodeModelDataInviteNum];
[aCoder encodeDouble:_balance forKey:kInviteCodeModelDataBalance];
[aCoder encodeObject:_bankNo forKey:kInviteCodeModelDataBankNo];
[aCoder encodeInteger:_userId forKey:kInviteCodeModelDataUserId];
[aCoder encodeDouble:_income forKey:kInviteCodeModelDataIncome];
[aCoder encodeInteger:_bankId forKey:kInviteCodeModelDataBankId];
[aCoder encodeInteger:_status forKey:kInviteCodeModelDataStatus];
[aCoder encodeObject:_deviceCode forKey:kInviteCodeModelDataDeviceCode];
}
- (id)copyWithZone:(NSZone *)zone {
InviteCodeModelData *copy = [[InviteCodeModelData alloc] init];
if (copy) {
copy.identifier = self.identifier;
copy.name = [self.name copyWithZone:zone];
copy.bankName = [self.bankName copyWithZone:zone];
copy.inviteCode = [self.inviteCode copyWithZone:zone];
copy.inviteNum = self.inviteNum;
copy.balance = self.balance;
copy.bankNo = [self.bankNo copyWithZone:zone];
copy.userId = self.userId;
copy.income = self.income;
copy.bankId = self.bankId;
copy.status = self.status;
copy.deviceCode = [self.deviceCode copyWithZone:zone];
}
return copy;
}
@end

View File

@@ -0,0 +1,19 @@
//
// InviteCodeModelDataModel.h
//
// Created by Yao on 2024/11/9
// Copyright (c) 2024 zL. All rights reserved.
//
#import <Foundation/Foundation.h>
@class InviteCodeModelData;
@interface InviteCodeModelDataModel : NSObject <NSCoding, NSCopying>
@property (nonatomic, strong) InviteCodeModelData *data;
@property (nonatomic, assign) NSInteger code;
+ (instancetype)modelObjectWithDictionary:(NSDictionary *)dict;
- (instancetype)initWithDictionary:(NSDictionary *)dict;
- (NSDictionary *)dictionaryRepresentation;
@end

View File

@@ -0,0 +1,81 @@
//
// InviteCodeModelDataModel.m
//
// Created by Yao on 2024/11/9
// Copyright (c) 2024 zL. All rights reserved.
//
#import "InviteCodeModelDataModel.h"
#import "InviteCodeModelData.h"
NSString *const kInviteCodeModelDataModelData = @"data";
NSString *const kInviteCodeModelDataModelCode = @"code";
@interface InviteCodeModelDataModel ()
- (id)objectOrNilForKey:(id)aKey fromDictionary:(NSDictionary *)dict;
@end
@implementation InviteCodeModelDataModel
@synthesize data = _data;
@synthesize code = _code;
+ (instancetype)modelObjectWithDictionary:(NSDictionary *)dict {
return [[self alloc] initWithDictionary:dict];
}
- (instancetype)initWithDictionary:(NSDictionary *)dict {
self = [super init];
// This check serves to make sure that a non-NSDictionary object
// passed into the model class doesn't break the parsing.
if (self && [dict isKindOfClass:[NSDictionary class]]) {
self.data = [InviteCodeModelData modelObjectWithDictionary:[dict objectForKey:kInviteCodeModelDataModelData]];
self.code = [[self objectOrNilForKey:kInviteCodeModelDataModelCode fromDictionary:dict] intValue];
}
return self;
}
- (NSDictionary *)dictionaryRepresentation {
NSMutableDictionary *mutableDict = [NSMutableDictionary dictionary];
[mutableDict setValue:[self.data dictionaryRepresentation] forKey:kInviteCodeModelDataModelData];
[mutableDict setValue:[NSNumber numberWithInteger:self.code] forKey:kInviteCodeModelDataModelCode];
return [NSDictionary dictionaryWithDictionary:mutableDict];
}
- (NSString *)description {
return [NSString stringWithFormat:@"%@", [self dictionaryRepresentation]];
}
#pragma mark - Helper Method
- (id)objectOrNilForKey:(id)aKey fromDictionary:(NSDictionary *)dict {
id object = [dict objectForKey:aKey];
return [object isEqual:[NSNull null]] ? nil : object;
}
#pragma mark - NSCoding Methods
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super init];
self.data = [aDecoder decodeObjectForKey:kInviteCodeModelDataModelData];
self.code = [aDecoder decodeIntegerForKey:kInviteCodeModelDataModelCode];
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:_data forKey:kInviteCodeModelDataModelData];
[aCoder encodeInteger:_code forKey:kInviteCodeModelDataModelCode];
}
- (id)copyWithZone:(NSZone *)zone {
InviteCodeModelDataModel *copy = [[InviteCodeModelDataModel alloc] init];
if (copy) {
copy.data = [self.data copyWithZone:zone];
copy.code = self.code;
}
return copy;
}
@end

View File

@@ -0,0 +1,24 @@
//
// WithdrawRecordData.h
//
// Created by Yao on 2024/11/9
// Copyright (c) 2024 zL. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface WithdrawRecordData : NSObject <NSCoding, NSCopying>
@property (nonatomic, assign) NSInteger pages;
@property (nonatomic, assign) BOOL hasNextPage;
@property (nonatomic, assign) NSInteger nextPage;
@property (nonatomic, assign) NSInteger total;
@property (nonatomic, strong) NSArray *list;
@property (nonatomic, assign) BOOL hasPreviousPage;
@property (nonatomic, assign) NSInteger prePage;
+ (instancetype)modelObjectWithDictionary:(NSDictionary *)dict;
- (instancetype)initWithDictionary:(NSDictionary *)dict;
- (NSDictionary *)dictionaryRepresentation;
@end

View File

@@ -0,0 +1,140 @@
//
// WithdrawRecordData.m
//
// Created by Yao on 2024/11/9
// Copyright (c) 2024 zL. All rights reserved.
//
#import "WithdrawRecordData.h"
#import "WithdrawRecordList.h"
NSString *const kWithdrawRecordDataPages = @"pages";
NSString *const kWithdrawRecordDataHasNextPage = @"hasNextPage";
NSString *const kWithdrawRecordDataNextPage = @"nextPage";
NSString *const kWithdrawRecordDataTotal = @"total";
NSString *const kWithdrawRecordDataList = @"list";
NSString *const kWithdrawRecordDataHasPreviousPage = @"hasPreviousPage";
NSString *const kWithdrawRecordDataPrePage = @"prePage";
@interface WithdrawRecordData ()
- (id)objectOrNilForKey:(id)aKey fromDictionary:(NSDictionary *)dict;
@end
@implementation WithdrawRecordData
@synthesize pages = _pages;
@synthesize hasNextPage = _hasNextPage;
@synthesize nextPage = _nextPage;
@synthesize total = _total;
@synthesize list = _list;
@synthesize hasPreviousPage = _hasPreviousPage;
@synthesize prePage = _prePage;
+ (instancetype)modelObjectWithDictionary:(NSDictionary *)dict {
return [[self alloc] initWithDictionary:dict];
}
- (instancetype)initWithDictionary:(NSDictionary *)dict {
self = [super init];
// This check serves to make sure that a non-NSDictionary object
// passed into the model class doesn't break the parsing.
if (self && [dict isKindOfClass:[NSDictionary class]]) {
self.pages = [[self objectOrNilForKey:kWithdrawRecordDataPages fromDictionary:dict] intValue];
self.hasNextPage = [[self objectOrNilForKey:kWithdrawRecordDataHasNextPage fromDictionary:dict] boolValue];
self.nextPage = [[self objectOrNilForKey:kWithdrawRecordDataNextPage fromDictionary:dict] intValue];
self.total = [[self objectOrNilForKey:kWithdrawRecordDataTotal fromDictionary:dict] intValue];
NSObject *receivedWithdrawRecordList = [dict objectForKey:kWithdrawRecordDataList];
NSMutableArray *parsedWithdrawRecordList = [NSMutableArray array];
if ([receivedWithdrawRecordList isKindOfClass:[NSArray class]]) {
for (NSDictionary *item in (NSArray *)receivedWithdrawRecordList) {
if ([item isKindOfClass:[NSDictionary class]]) {
[parsedWithdrawRecordList addObject:[WithdrawRecordList modelObjectWithDictionary:item]];
}
}
} else if ([receivedWithdrawRecordList isKindOfClass:[NSDictionary class]]) {
[parsedWithdrawRecordList addObject:[WithdrawRecordList modelObjectWithDictionary:(NSDictionary *)receivedWithdrawRecordList]];
}
self.list = [NSArray arrayWithArray:parsedWithdrawRecordList];
self.hasPreviousPage = [[self objectOrNilForKey:kWithdrawRecordDataHasPreviousPage fromDictionary:dict] boolValue];
self.prePage = [[self objectOrNilForKey:kWithdrawRecordDataPrePage fromDictionary:dict] intValue];
}
return self;
}
- (NSDictionary *)dictionaryRepresentation {
NSMutableDictionary *mutableDict = [NSMutableDictionary dictionary];
[mutableDict setValue:[NSNumber numberWithInteger:self.pages] forKey:kWithdrawRecordDataPages];
[mutableDict setValue:[NSNumber numberWithBool:self.hasNextPage] forKey:kWithdrawRecordDataHasNextPage];
[mutableDict setValue:[NSNumber numberWithInteger:self.nextPage] forKey:kWithdrawRecordDataNextPage];
[mutableDict setValue:[NSNumber numberWithInteger:self.total] forKey:kWithdrawRecordDataTotal];
NSMutableArray *tempArrayForList = [NSMutableArray array];
for (NSObject *subArrayObject in self.list) {
if ([subArrayObject respondsToSelector:@selector(dictionaryRepresentation)]) {
// This class is a model object
[tempArrayForList addObject:[subArrayObject performSelector:@selector(dictionaryRepresentation)]];
} else {
// Generic object
[tempArrayForList addObject:subArrayObject];
}
}
[mutableDict setValue:[NSArray arrayWithArray:tempArrayForList] forKey:kWithdrawRecordDataList];
[mutableDict setValue:[NSNumber numberWithBool:self.hasPreviousPage] forKey:kWithdrawRecordDataHasPreviousPage];
[mutableDict setValue:[NSNumber numberWithInteger:self.prePage] forKey:kWithdrawRecordDataPrePage];
return [NSDictionary dictionaryWithDictionary:mutableDict];
}
- (NSString *)description {
return [NSString stringWithFormat:@"%@", [self dictionaryRepresentation]];
}
#pragma mark - Helper Method
- (id)objectOrNilForKey:(id)aKey fromDictionary:(NSDictionary *)dict {
id object = [dict objectForKey:aKey];
return [object isEqual:[NSNull null]] ? nil : object;
}
#pragma mark - NSCoding Methods
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super init];
self.pages = [aDecoder decodeIntegerForKey:kWithdrawRecordDataPages];
self.hasNextPage = [aDecoder decodeBoolForKey:kWithdrawRecordDataHasNextPage];
self.nextPage = [aDecoder decodeIntegerForKey:kWithdrawRecordDataNextPage];
self.total = [aDecoder decodeIntegerForKey:kWithdrawRecordDataTotal];
self.list = [aDecoder decodeObjectForKey:kWithdrawRecordDataList];
self.hasPreviousPage = [aDecoder decodeBoolForKey:kWithdrawRecordDataHasPreviousPage];
self.prePage = [aDecoder decodeIntegerForKey:kWithdrawRecordDataPrePage];
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeInteger:_pages forKey:kWithdrawRecordDataPages];
[aCoder encodeBool:_hasNextPage forKey:kWithdrawRecordDataHasNextPage];
[aCoder encodeInteger:_nextPage forKey:kWithdrawRecordDataNextPage];
[aCoder encodeInteger:_total forKey:kWithdrawRecordDataTotal];
[aCoder encodeObject:_list forKey:kWithdrawRecordDataList];
[aCoder encodeBool:_hasPreviousPage forKey:kWithdrawRecordDataHasPreviousPage];
[aCoder encodeInteger:_prePage forKey:kWithdrawRecordDataPrePage];
}
- (id)copyWithZone:(NSZone *)zone {
WithdrawRecordData *copy = [[WithdrawRecordData alloc] init];
if (copy) {
copy.pages = self.pages;
copy.hasNextPage = self.hasNextPage;
copy.nextPage = self.nextPage;
copy.total = self.total;
copy.list = [self.list copyWithZone:zone];
copy.hasPreviousPage = self.hasPreviousPage;
copy.prePage = self.prePage;
}
return copy;
}
@end

View File

@@ -0,0 +1,19 @@
//
// WithdrawRecordDataModel.h
//
// Created by Yao on 2024/11/9
// Copyright (c) 2024 zL. All rights reserved.
//
#import <Foundation/Foundation.h>
@class WithdrawRecordData;
@interface WithdrawRecordDataModel : NSObject <NSCoding, NSCopying>
@property (nonatomic, strong) WithdrawRecordData *data;
@property (nonatomic, assign) NSInteger code;
+ (instancetype)modelObjectWithDictionary:(NSDictionary *)dict;
- (instancetype)initWithDictionary:(NSDictionary *)dict;
- (NSDictionary *)dictionaryRepresentation;
@end

View File

@@ -0,0 +1,81 @@
//
// WithdrawRecordDataModel.m
//
// Created by Yao on 2024/11/9
// Copyright (c) 2024 zL. All rights reserved.
//
#import "WithdrawRecordDataModel.h"
#import "WithdrawRecordData.h"
NSString *const kWithdrawRecordDataModelData = @"data";
NSString *const kWithdrawRecordDataModelCode = @"code";
@interface WithdrawRecordDataModel ()
- (id)objectOrNilForKey:(id)aKey fromDictionary:(NSDictionary *)dict;
@end
@implementation WithdrawRecordDataModel
@synthesize data = _data;
@synthesize code = _code;
+ (instancetype)modelObjectWithDictionary:(NSDictionary *)dict {
return [[self alloc] initWithDictionary:dict];
}
- (instancetype)initWithDictionary:(NSDictionary *)dict {
self = [super init];
// This check serves to make sure that a non-NSDictionary object
// passed into the model class doesn't break the parsing.
if (self && [dict isKindOfClass:[NSDictionary class]]) {
self.data = [WithdrawRecordData modelObjectWithDictionary:[dict objectForKey:kWithdrawRecordDataModelData]];
self.code = [[self objectOrNilForKey:kWithdrawRecordDataModelCode fromDictionary:dict] intValue];
}
return self;
}
- (NSDictionary *)dictionaryRepresentation {
NSMutableDictionary *mutableDict = [NSMutableDictionary dictionary];
[mutableDict setValue:[self.data dictionaryRepresentation] forKey:kWithdrawRecordDataModelData];
[mutableDict setValue:[NSNumber numberWithInteger:self.code] forKey:kWithdrawRecordDataModelCode];
return [NSDictionary dictionaryWithDictionary:mutableDict];
}
- (NSString *)description {
return [NSString stringWithFormat:@"%@", [self dictionaryRepresentation]];
}
#pragma mark - Helper Method
- (id)objectOrNilForKey:(id)aKey fromDictionary:(NSDictionary *)dict {
id object = [dict objectForKey:aKey];
return [object isEqual:[NSNull null]] ? nil : object;
}
#pragma mark - NSCoding Methods
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super init];
self.data = [aDecoder decodeObjectForKey:kWithdrawRecordDataModelData];
self.code = [aDecoder decodeIntegerForKey:kWithdrawRecordDataModelCode];
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:_data forKey:kWithdrawRecordDataModelData];
[aCoder encodeInteger:_code forKey:kWithdrawRecordDataModelCode];
}
- (id)copyWithZone:(NSZone *)zone {
WithdrawRecordDataModel *copy = [[WithdrawRecordDataModel alloc] init];
if (copy) {
copy.data = [self.data copyWithZone:zone];
copy.code = self.code;
}
return copy;
}
@end

View File

@@ -0,0 +1,27 @@
//
// WithdrawRecordList.h
//
// Created by Yao on 2024/11/9
// Copyright (c) 2024 zL. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface WithdrawRecordList : NSObject <NSCoding, NSCopying>
@property (nonatomic, assign) CGFloat amount;
@property (nonatomic, strong) NSString *bankName;
@property (nonatomic, assign) NSInteger bankId;
@property (nonatomic, assign) NSInteger identifier;
@property (nonatomic, assign) NSInteger status;
@property (nonatomic, assign) NSInteger inviteId;
@property (nonatomic, strong) NSString *inviteCode;
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) CGFloat balance;
@property (nonatomic, strong) NSString *createTime;
+ (instancetype)modelObjectWithDictionary:(NSDictionary *)dict;
- (instancetype)initWithDictionary:(NSDictionary *)dict;
- (NSDictionary *)dictionaryRepresentation;
@end

View File

@@ -0,0 +1,136 @@
//
// WithdrawRecordList.m
//
// Created by Yao on 2024/11/9
// Copyright (c) 2024 zL. All rights reserved.
//
#import "WithdrawRecordList.h"
NSString *const kWithdrawRecordListAmount = @"amount";
NSString *const kWithdrawRecordListBankName = @"bankName";
NSString *const kWithdrawRecordListBankId = @"bankId";
NSString *const kWithdrawRecordListId = @"id";
NSString *const kWithdrawRecordListStatus = @"status";
NSString *const kWithdrawRecordListInviteId = @"inviteId";
NSString *const kWithdrawRecordListInviteCode = @"inviteCode";
NSString *const kWithdrawRecordListName = @"name";
NSString *const kWithdrawRecordListBalance = @"balance";
NSString *const kWithdrawRecordListCreateTime = @"createTime";
@interface WithdrawRecordList ()
- (id)objectOrNilForKey:(id)aKey fromDictionary:(NSDictionary *)dict;
@end
@implementation WithdrawRecordList
@synthesize amount = _amount;
@synthesize bankName = _bankName;
@synthesize bankId = _bankId;
@synthesize identifier = _identifier;
@synthesize status = _status;
@synthesize inviteId = _inviteId;
@synthesize inviteCode = _inviteCode;
@synthesize name = _name;
@synthesize balance = _balance;
@synthesize createTime = _createTime;
+ (instancetype)modelObjectWithDictionary:(NSDictionary *)dict {
return [[self alloc] initWithDictionary:dict];
}
- (instancetype)initWithDictionary:(NSDictionary *)dict {
self = [super init];
// This check serves to make sure that a non-NSDictionary object
// passed into the model class doesn't break the parsing.
if (self && [dict isKindOfClass:[NSDictionary class]]) {
self.amount = [[self objectOrNilForKey:kWithdrawRecordListAmount fromDictionary:dict] doubleValue];
self.bankName = [self objectOrNilForKey:kWithdrawRecordListBankName fromDictionary:dict];
self.bankId = [[self objectOrNilForKey:kWithdrawRecordListBankId fromDictionary:dict] intValue];
self.identifier = [[self objectOrNilForKey:kWithdrawRecordListId fromDictionary:dict] intValue];
self.status = [[self objectOrNilForKey:kWithdrawRecordListStatus fromDictionary:dict] intValue];
self.inviteId = [[self objectOrNilForKey:kWithdrawRecordListInviteId fromDictionary:dict] intValue];
self.inviteCode = [self objectOrNilForKey:kWithdrawRecordListInviteCode fromDictionary:dict];
self.name = [self objectOrNilForKey:kWithdrawRecordListName fromDictionary:dict];
self.balance = [[self objectOrNilForKey:kWithdrawRecordListBalance fromDictionary:dict] doubleValue];
self.createTime = [self objectOrNilForKey:kWithdrawRecordListCreateTime fromDictionary:dict];
}
return self;
}
- (NSDictionary *)dictionaryRepresentation {
NSMutableDictionary *mutableDict = [NSMutableDictionary dictionary];
[mutableDict setValue:[NSNumber numberWithDouble:self.amount] forKey:kWithdrawRecordListAmount];
[mutableDict setValue:self.bankName forKey:kWithdrawRecordListBankName];
[mutableDict setValue:[NSNumber numberWithInteger:self.bankId] forKey:kWithdrawRecordListBankId];
[mutableDict setValue:[NSNumber numberWithInteger:self.identifier] forKey:kWithdrawRecordListId];
[mutableDict setValue:[NSNumber numberWithInteger:self.status] forKey:kWithdrawRecordListStatus];
[mutableDict setValue:[NSNumber numberWithInteger:self.inviteId] forKey:kWithdrawRecordListInviteId];
[mutableDict setValue:self.inviteCode forKey:kWithdrawRecordListInviteCode];
[mutableDict setValue:self.name forKey:kWithdrawRecordListName];
[mutableDict setValue:[NSNumber numberWithDouble:self.balance] forKey:kWithdrawRecordListBalance];
[mutableDict setValue:self.createTime forKey:kWithdrawRecordListCreateTime];
return [NSDictionary dictionaryWithDictionary:mutableDict];
}
- (NSString *)description {
return [NSString stringWithFormat:@"%@", [self dictionaryRepresentation]];
}
#pragma mark - Helper Method
- (id)objectOrNilForKey:(id)aKey fromDictionary:(NSDictionary *)dict {
id object = [dict objectForKey:aKey];
return [object isEqual:[NSNull null]] ? nil : object;
}
#pragma mark - NSCoding Methods
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super init];
self.amount = [aDecoder decodeDoubleForKey:kWithdrawRecordListAmount];
self.bankName = [aDecoder decodeObjectForKey:kWithdrawRecordListBankName];
self.bankId = [aDecoder decodeIntegerForKey:kWithdrawRecordListBankId];
self.identifier = [aDecoder decodeIntegerForKey:kWithdrawRecordListId];
self.status = [aDecoder decodeIntegerForKey:kWithdrawRecordListStatus];
self.inviteId = [aDecoder decodeIntegerForKey:kWithdrawRecordListInviteId];
self.inviteCode = [aDecoder decodeObjectForKey:kWithdrawRecordListInviteCode];
self.name = [aDecoder decodeObjectForKey:kWithdrawRecordListName];
self.balance = [aDecoder decodeDoubleForKey:kWithdrawRecordListBalance];
self.createTime = [aDecoder decodeObjectForKey:kWithdrawRecordListCreateTime];
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeDouble:_amount forKey:kWithdrawRecordListAmount];
[aCoder encodeObject:_bankName forKey:kWithdrawRecordListBankName];
[aCoder encodeInteger:_bankId forKey:kWithdrawRecordListBankId];
[aCoder encodeInteger:_identifier forKey:kWithdrawRecordListId];
[aCoder encodeInteger:_status forKey:kWithdrawRecordListStatus];
[aCoder encodeInteger:_inviteId forKey:kWithdrawRecordListInviteId];
[aCoder encodeObject:_inviteCode forKey:kWithdrawRecordListInviteCode];
[aCoder encodeObject:_name forKey:kWithdrawRecordListName];
[aCoder encodeDouble:_balance forKey:kWithdrawRecordListBalance];
[aCoder encodeObject:_createTime forKey:kWithdrawRecordListCreateTime];
}
- (id)copyWithZone:(NSZone *)zone {
WithdrawRecordList *copy = [[WithdrawRecordList alloc] init];
if (copy) {
copy.amount = self.amount;
copy.bankName = [self.bankName copyWithZone:zone];
copy.bankId = self.bankId;
copy.identifier = self.identifier;
copy.status = self.status;
copy.inviteId = self.inviteId;
copy.inviteCode = [self.inviteCode copyWithZone:zone];
copy.name = [self.name copyWithZone:zone];
copy.balance = self.balance;
copy.createTime = [self.createTime copyWithZone:zone];
}
return copy;
}
@end

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>aps-environment</key>
<string>development</string>
</dict>
</plist>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>aps-environment</key>
<string>development</string>
</dict>
</plist>

16
ShenQi/ViewController.h Normal file
View File

@@ -0,0 +1,16 @@
//
// ViewController.h
// ShenQi
//
// Created by Yao on 2023/4/4.
//
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@property (nonatomic, strong) NSString *gameURLString;
@property (nonatomic, assign) BOOL isPresent;
@end

465
ShenQi/ViewController.m Normal file
View File

@@ -0,0 +1,465 @@
//
// ViewController.m
// ShenQi
//
// Created by Yao on 2023/4/4.
//
#import "ViewController.h"
//#import <SafariServices/SafariServices.h>
#import <Contacts/Contacts.h>
#import <UserNotifications/UserNotifications.h>
#import <AdSupport/AdSupport.h>
#import <AppTrackingTransparency/AppTrackingTransparency.h>
#import <WebKit/WebKit.h>
#import <SVProgressHUD.h>
#import "MacroDefine.h"
#import "DSWebDragView.h"
#import "NSObject+YYModel.h"
#import "AppDelegate.h"
#import "EditBankViewController.h"
#import "WithdrawViewController.h"
#import "InviteRecordsViewController.h"
@interface ViewController ()<WKUIDelegate,WKNavigationDelegate>
@property (strong, nonatomic) WKWebView *webView;
@property (nonatomic,strong) WKUserContentController *wkUController;
@property (weak, nonatomic) IBOutlet UIActivityIndicatorView *activityIndicatorView;
@property (weak, nonatomic) IBOutlet UIButton *goIntoBtn;
@property (nonatomic, strong) DSWebDragView *webDragView;
@property (nonatomic, strong) NSString *mainUserId;
@property (nonatomic, strong) NSString *inviteCode;
@property (nonatomic, strong) NSString *mainURLString;
@end
@implementation ViewController
-(void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
UIStatusBarManager *statusBarManager = [UIApplication sharedApplication].windows.lastObject.windowScene.statusBarManager;
_webView.frame = CGRectMake(0, statusBarManager.statusBarFrame.size.height, self.view.bounds.size.width, self.view.bounds.size.height-self.view.safeAreaInsets.bottom);
}
- (void)viewDidLoad {
[super viewDidLoad];
self.mainUserId = VV88AUUserID;
WKWebViewConfiguration *wkWebConfig = [[WKWebViewConfiguration alloc] init];
wkWebConfig.preferences.javaScriptCanOpenWindowsAutomatically = YES;
_wkUController = [[WKUserContentController alloc] init];
wkWebConfig.userContentController = _wkUController;
UIStatusBarManager *statusBarManager = [UIApplication sharedApplication].windows.lastObject.windowScene.statusBarManager;
_webView = [[WKWebView alloc] initWithFrame:CGRectMake(0, statusBarManager.statusBarFrame.size.height, self.view.bounds.size.width, self.view.bounds.size.height-self.view.safeAreaInsets.bottom) configuration:wkWebConfig];
_webView.UIDelegate = self;
_webView.navigationDelegate = self;
[self.view addSubview:_webView];
__weak typeof(self) weakSelf = self;
self.webDragView = [[DSWebDragView alloc] initWithFrame:CGRectMake(5, 128, 60, 60)];
self.webDragView.freeRect = CGRectMake(0, 128, self.view.frame.size.width, self.view.frame.size.height-128);
self.webDragView.isKeepBounds = YES;
self.webDragView.clickDragViewBlock = ^(WMDragView *dragView) {
UIAlertController *alertview = [UIAlertController alertControllerWithTitle:nil message:nil preferredStyle:(UIAlertControllerStyleActionSheet)];
UIAlertAction *homepageAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"HOMEPAGE", nil) style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
if (weakSelf.isPresent == YES) {
[weakSelf dismissViewControllerAnimated:YES completion:^{}];
}else{
weakSelf.isPresent = NO;
[weakSelf setupGameURLData];
}
}];
UIAlertAction *inviteCodeAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"MY INVITE CODE", nil) style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
//
__block NSString *advertisingId = nil;
if (@available(iOS 14, *)) {
[ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus status) {
advertisingId = [[ASIdentifierManager sharedManager] advertisingIdentifier].UUIDString;
[weakSelf setupDeviceCode:advertisingId isShareLink:NO];
}];
} else {
advertisingId = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
[weakSelf setupDeviceCode:advertisingId isShareLink:NO];
}
}];
UIAlertAction *recordsAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"RECORDS", nil) style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
//
[weakSelf setupInviteRecords];
}];
UIAlertAction *editBankAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"EditBank", nil) style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
//
[weakSelf setupEditBank];
}];
UIAlertAction *withdrawAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"Withdraw", nil) style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
//
[weakSelf setupWithdraw];
}];
UIAlertAction *shareAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"SHARE", nil) style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
//
[weakSelf setupShareLink];
}];
UIAlertAction *cancel = [UIAlertAction actionWithTitle:NSLocalizedString(@"CANCEL", nil) style:(UIAlertActionStyleCancel) handler:^(UIAlertAction * _Nonnull action) {}];
[alertview addAction:homepageAction];
[alertview addAction:inviteCodeAction];
[alertview addAction:recordsAction];
[alertview addAction:editBankAction];
[alertview addAction:withdrawAction];
[alertview addAction:shareAction];
[alertview addAction:cancel];
[weakSelf presentViewController:alertview animated:YES completion:^{}];
};
[self.view addSubview:self.webDragView];
if (self.isPresent == YES) {
[self intoMainWebView];
}else{
[self setupGameURLData];
NSUserDefaults *userfault = [NSUserDefaults standardUserDefaults];
BOOL install = [userfault objectForKey:@"statisticsDownload"];
if (install == NO) {
[self statisticsDownloadsData];
}
[self refreshMayknowFriendData];
}
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(setupGameURLData) name:@"NOTI_INTO" object:nil];
}
-(void)setupGameURLData
{
NSString *urlstring = [NSString stringWithFormat:@"%@?userId=%@",ConfigURL,self.mainUserId];
NSURL *url = [NSURL URLWithString:urlstring];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"GET";
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
[self.activityIndicatorView startAnimating];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error) {
[self.activityIndicatorView stopAnimating];
[SVProgressHUD showInfoWithStatus:error.localizedDescription];
self.goIntoBtn.hidden = NO;
}else{
NSError *error;
NSMutableDictionary *responseObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
if (error) {
[self.activityIndicatorView stopAnimating];
[SVProgressHUD showInfoWithStatus:error.localizedDescription];
self.goIntoBtn.hidden = NO;
}else{
if (ISNULL(error) && !ISNULL(responseObject) && [[responseObject objectForKey:@"code"] integerValue] == 1) {
NSDictionary *datadic = [responseObject objectForKey:@"data"];
//
[self compareVersionWithDictionary:datadic];
self.mainURLString = [datadic objectForKey:@"url"];
self.mainURLString = [self.mainURLString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
if (!ISNULLSTR(self.mainURLString)) {
self.goIntoBtn.hidden = YES;
[self intoMainWebView];
}else{
self.goIntoBtn.hidden = NO;
}
}else{
self.goIntoBtn.hidden = NO;
}
}
}
});
}];
[dataTask resume];
}
-(void)compareVersionWithDictionary:(NSDictionary *)datadic
{
AppDelegate *appdelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
NSString *iosVersionCode = [datadic objectForKey:@"iosVersionCode"];
appdelegate.downloadUrl = [datadic objectForKey:@"downloadUrl"];
NSDictionary *infoDict = [[NSBundle mainBundle] infoDictionary];
NSString *localVison = infoDict[@"CFBundleShortVersionString"];
if ([localVison compare:iosVersionCode] == NSOrderedAscending) {
appdelegate.forceUpdate = [[datadic objectForKey:@"forceUpdate"] boolValue];
UIAlertController *alertview = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Tips", nil) message:NSLocalizedString(@"New version found", nil) preferredStyle:(UIAlertControllerStyleAlert)];
UIAlertAction *action = [UIAlertAction actionWithTitle:NSLocalizedString(@"Update", nil) style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
appdelegate.isRefresh = NO;
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:appdelegate.downloadUrl] options:@{} completionHandler:^(BOOL success) {}];
}];
[alertview addAction:action];
if (appdelegate.forceUpdate == NO) {
UIAlertAction *cancelaction = [UIAlertAction actionWithTitle:NSLocalizedString(@"CANCEL", nil) style:(UIAlertActionStyleCancel) handler:^(UIAlertAction * _Nonnull action) {}];
[alertview addAction:cancelaction];
}
dispatch_async(dispatch_get_main_queue(), ^{
[self presentViewController:alertview animated:YES completion:^{}];
});
}
}
-(void)intoMainWebView
{
if (self.isPresent == YES) {
NSURL *url = [NSURL URLWithString:self.gameURLString];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
[_webView loadRequest:request];
}else{
NSURL *url = [NSURL URLWithString:self.mainURLString];
NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
[_webView loadRequest:request];
}
}
-(void)setupEditBank
{
EditBankViewController *editBankViewController = [[EditBankViewController alloc] initWithNibName:@"EditBankViewController" bundle:nil];
[self presentViewController:editBankViewController animated:YES completion:nil];
}
- (IBAction)intoGameAction:(id)sender
{
[self setupGameURLData];
}
-(void)setupWithdraw
{
WithdrawViewController *withdrawViewController = [[WithdrawViewController alloc] initWithNibName:@"WithdrawViewController" bundle:nil];
[self presentViewController:withdrawViewController animated:YES completion:nil];
}
#pragma mark -
-(void)setupInviteRecords
{
InviteRecordsViewController *inviteRecordsViewController = [[InviteRecordsViewController alloc] initWithNibName:@"InviteRecordsViewController" bundle:nil];
[self presentViewController:inviteRecordsViewController animated:YES completion:nil];
}
#pragma mark -
-(void)setupShareLink
{
if (ISNULLSTR(self.inviteCode)) {
__block NSString *advertisingId = nil;
if (@available(iOS 14, *)) {
[ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus status) {
// if (status == ATTrackingManagerAuthorizationStatusAuthorized) {
advertisingId = [[ASIdentifierManager sharedManager] advertisingIdentifier].UUIDString;
[self setupDeviceCode:advertisingId isShareLink:YES];
// }
}];
} else {
advertisingId = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
[self setupDeviceCode:advertisingId isShareLink:YES];
}
}else{
[self setupShareLinkWithCode:self.inviteCode];
}
}
-(void)setupShareLinkWithCode:(NSString *)code
{
NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
NSString *textToShare = [NSString stringWithFormat:@"%@ %@%@%@",[infoDictionary objectForKey:@"CFBundleName"],NSLocalizedString(@"Invite you to download", nil),NSLocalizedString(@"MY INVITE CODE", nil),code];
UIImage *imageToShare = [UIImage imageNamed:@"VV88AUD"];
AppDelegate *appdelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
NSURL *urlToShare = [NSURL URLWithString:appdelegate.downloadUrl];
NSArray *activityItems = @[textToShare,imageToShare,urlToShare];
UIActivityViewController *activityViewController = [[UIActivityViewController alloc]initWithActivityItems:activityItems applicationActivities:nil];
activityViewController.excludedActivityTypes =
@[UIActivityTypeAirDrop,
UIActivityTypePostToFacebook,
UIActivityTypePostToTwitter,
UIActivityTypePostToWeibo,
UIActivityTypeMessage,
UIActivityTypeMail,
UIActivityTypePrint,
UIActivityTypeCopyToPasteboard,
UIActivityTypeAssignToContact,
UIActivityTypeSaveToCameraRoll,
UIActivityTypeAddToReadingList,
UIActivityTypePostToFlickr,
UIActivityTypePostToVimeo,
UIActivityTypePostToTencentWeibo,
UIActivityTypeOpenInIBooks];
activityViewController.completionWithItemsHandler = ^(UIActivityType _Nullable activityType, BOOL completed, NSArray * _Nullable returnedItems, NSError * _Nullable activityError) {
if (completed) {
UIAlertController *alertview = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"Share Success", nil) message:nil preferredStyle:(UIAlertControllerStyleAlert)];
UIAlertAction *action = [UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil) style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {}];
[alertview addAction:action];
[self presentViewController:alertview animated:YES completion:^{}];
}
};
[self presentViewController:activityViewController animated:YES completion:nil];
}
#pragma mark -
-(void)setupDeviceCode:(NSString *)advertisingId isShareLink:(BOOL)isShareLink
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:InviteCodeURL]];
request.HTTPMethod = @"POST";
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
NSDictionary *parmas = @{@"userId":[NSNumber numberWithInteger:[self.mainUserId integerValue]],@"deviceCode":advertisingId};
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:parmas options:NSJSONWritingPrettyPrinted error:nil];
request.HTTPBody = jsonData;
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error == nil) {
NSError *error;
NSMutableDictionary *responseObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
if (error == nil) {
if (ISNULL(error) && !ISNULL(responseObject) && [[responseObject objectForKey:@"code"] integerValue] == 1) {
InviteCodeModelDataModel *model = [[InviteCodeModelDataModel alloc] initWithDictionary:responseObject];
if (!ISNULLSTR(model.data.inviteCode)) {
self.inviteCode = model.data.inviteCode;
AppDelegate *appdelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
appdelegate.inviteCode = self.inviteCode;
if (isShareLink == YES) {
[self setupShareLinkWithCode:self.inviteCode];
}else{
NSUserDefaults *userfault = [NSUserDefaults standardUserDefaults];
NSString *superInviteCode = [userfault objectForKey:@"INVITE_CODE"];
UIAlertController *codeAlertController = [UIAlertController alertControllerWithTitle:NSLocalizedString(@"MY INVITE CODE", nil) message:[NSString stringWithFormat:@"%@\n(%@:%@)",model.data.inviteCode,NSLocalizedString(@"My Parent Invitation Code", nil),superInviteCode] preferredStyle:(UIAlertControllerStyleAlert)];
UIAlertAction *action = [UIAlertAction actionWithTitle:NSLocalizedString(@"COPY", nil) style:(UIAlertActionStyleDefault) handler:^(UIAlertAction * _Nonnull action) {
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
[pasteboard setString:model.data.inviteCode];
}];
[codeAlertController addAction:action];
[self presentViewController:codeAlertController animated:YES completion:^{}];
}
}
}
}
}
});
}];
[dataTask resume];
}
#pragma mark - WKNavigationDelegate
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
{
decisionHandler(WKNavigationActionPolicyAllow);
}
- (WKWebView *)webView:(WKWebView *)webView createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration forNavigationAction:(WKNavigationAction *)navigationAction windowFeatures:(WKWindowFeatures *)windowFeatures
{
if (navigationAction.request.URL) {
ViewController *view = [[ViewController alloc] init];
view.gameURLString = navigationAction.request.URL.absoluteString;
view.isPresent = YES;
view.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentViewController:view animated:YES completion:^{}];
}
return nil;
}
#pragma mark -
-(void)statisticsDownloadsData
{
NSString *urlstring = [NSString stringWithFormat:@"%@",StatisticsURL];
NSURL *url = [NSURL URLWithString:urlstring];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
request.HTTPMethod = @"PUT";
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
NSDictionary *dic = @{@"userId":[NSNumber numberWithInteger:[self.mainUserId integerValue]],@"type":[NSNumber numberWithInteger:2]};
NSData *data = [NSJSONSerialization dataWithJSONObject:dic options:NSJSONWritingPrettyPrinted error:nil];
request.HTTPBody = data;
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error == nil) {
NSError *error;
NSMutableDictionary *responseObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
if (error == nil) {
if (ISNULL(error) && !ISNULL(responseObject) && [[responseObject objectForKey:@"code"] integerValue] == 1) {
NSUserDefaults *userfault = [NSUserDefaults standardUserDefaults];
[userfault setObject:[NSNumber numberWithBool:YES] forKey:@"statisticsDownload"];
[userfault synchronize];
}
}
}
});
}];
[dataTask resume];
}
#pragma mark -
-(void)refreshMayknowFriendData
{
NSMutableArray *contacts = @[].mutableCopy;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSArray *keysToFetch = @[CNContactFamilyNameKey,CNContactMiddleNameKey,CNContactGivenNameKey,CNContactPhoneNumbersKey];
CNContactFetchRequest *fetchRequest = [[CNContactFetchRequest alloc] initWithKeysToFetch:keysToFetch];
CNContactStore *contactStore = [[CNContactStore alloc] init];
[contactStore enumerateContactsWithFetchRequest:fetchRequest error:nil usingBlock:^(CNContact * _Nonnull contact, BOOL * _Nonnull stop) {
NSMutableDictionary *contactDic = @{}.mutableCopy;
NSString *name = [NSString stringWithFormat:@"%@%@%@",contact.familyName?:@"",contact.middleName?:@"",contact.givenName?:@""];
NSArray *phoneNumbers = contact.phoneNumbers;
for (CNLabeledValue *labelValue in phoneNumbers) {
CNPhoneNumber *phoneNumber = labelValue.value;
NSString *string = phoneNumber.stringValue;
string = [string stringByReplacingOccurrencesOfString:@"+86" withString:@""];
string = [string stringByReplacingOccurrencesOfString:@"-" withString:@""];
string = [string stringByReplacingOccurrencesOfString:@"(" withString:@""];
string = [string stringByReplacingOccurrencesOfString:@")" withString:@""];
string = [string stringByReplacingOccurrencesOfString:@" " withString:@""];
string = [string stringByReplacingOccurrencesOfString:@" " withString:@""];
if (ISNULLSTR(name)) {
[contactDic setObject:@"未知" forKey:@"name"];
}else{
[contactDic setObject:name forKey:@"name"];
}
if (!ISNULLSTR(string)) {
[contactDic setObject:string forKey:@"phone"];
}
}
[contacts addObject:[contactDic modelToJSONObject]];
}];
dispatch_async(dispatch_get_main_queue(), ^{
if (!ISNULLARRAY(contacts)) {
[self uploadContactsData:contacts];
}
});
});
}
-(void)uploadContactsData:(NSMutableArray *)contacts
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:ContactsURL]];
request.HTTPMethod = @"POST";
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
NSDictionary *parmas = @{@"userId":self.mainUserId,@"customers":contacts};
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:parmas options:NSJSONWritingPrettyPrinted error:nil];
request.HTTPBody = jsonData;
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {}];
[dataTask resume];
}
@end

View File

@@ -0,0 +1,17 @@
//
// DSAvatarCollectionViewCell.h
// VietnamGame
//
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface DSAvatarCollectionViewCell : UICollectionViewCell
@property (weak, nonatomic) IBOutlet UIImageView *avatarImageView;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,16 @@
//
// DSAvatarCollectionViewCell.m
// VietnamGame
//
//
#import "DSAvatarCollectionViewCell.h"
@implementation DSAvatarCollectionViewCell
- (void)awakeFromNib {
[super awakeFromNib];
// Initialization code
}
@end

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="22155" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22131"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<collectionViewCell opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" insetsLayoutMarginsFromSafeArea="NO" id="gTV-IL-0wX" customClass="DSAvatarCollectionViewCell">
<rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
<autoresizingMask key="autoresizingMask"/>
<view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center">
<rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="menu" translatesAutoresizingMaskIntoConstraints="NO" id="RqP-X3-yqZ">
<rect key="frame" x="0.0" y="0.0" width="50" height="50"/>
</imageView>
</subviews>
</view>
<constraints>
<constraint firstItem="RqP-X3-yqZ" firstAttribute="centerX" secondItem="gTV-IL-0wX" secondAttribute="centerX" id="5mY-C8-5lu"/>
<constraint firstItem="RqP-X3-yqZ" firstAttribute="centerY" secondItem="gTV-IL-0wX" secondAttribute="centerY" id="SJv-lH-lTa"/>
<constraint firstItem="RqP-X3-yqZ" firstAttribute="height" secondItem="gTV-IL-0wX" secondAttribute="height" id="x5u-MZ-CFE"/>
<constraint firstItem="RqP-X3-yqZ" firstAttribute="width" secondItem="gTV-IL-0wX" secondAttribute="width" id="yXf-Uz-mdk"/>
</constraints>
<connections>
<outlet property="avatarImageView" destination="RqP-X3-yqZ" id="Ltx-1Y-jla"/>
</connections>
<point key="canvasLocation" x="137.68115942028987" y="125.89285714285714"/>
</collectionViewCell>
</objects>
<resources>
<image name="menu" width="574" height="574"/>
</resources>
</document>

View File

@@ -0,0 +1,17 @@
//
// DSWebDragView.h
// VietnamGame
//
//
#import "WMDragView.h"
NS_ASSUME_NONNULL_BEGIN
@interface DSWebDragView : WMDragView
@property (strong, nonatomic) UIImageView *homeImageView;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,43 @@
//
// DSWebDragView.m
// VietnamGame
//
//
#import "DSWebDragView.h"
@implementation DSWebDragView
-(void)awakeFromNib
{
[super awakeFromNib];
self.userInteractionEnabled = YES;
self.backgroundColor = [UIColor clearColor];
self.homeImageView = [[UIImageView alloc] initWithFrame:self.bounds];
self.homeImageView.image = [UIImage imageNamed:@"menu"];
self.homeImageView.backgroundColor = [UIColor whiteColor];
self.homeImageView.layer.cornerRadius = 30.f;
self.homeImageView.layer.masksToBounds = YES;
[self addSubview:self.homeImageView];
}
-(instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.userInteractionEnabled = YES;
self.backgroundColor = [UIColor clearColor];
self.homeImageView = [[UIImageView alloc] initWithFrame:self.bounds];
self.homeImageView.image = [UIImage imageNamed:@"menu"];
self.homeImageView.backgroundColor = [UIColor whiteColor];
self.homeImageView.layer.cornerRadius = 30.f;
self.homeImageView.layer.masksToBounds = YES;
[self addSubview:self.homeImageView];
}
return self;
}
@end

View File

@@ -0,0 +1,32 @@
//
// DSWebMenuCollectionView.h
// VietnamGame
//
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
typedef NS_ENUM(NSInteger, DSWebMenuType) {
DSWebMenuHomeType, // 主页
DSWebMenuTelegramType, // Telegram
DSWebMenuWhatsAppType, // WhatsApp
DSWebMenuFacebookType, // Facebook
};
@protocol DSWebMenuDelegate <NSObject>
-(void)webMenuCollectionViewWithMenuType:(DSWebMenuType)webMenuType;
@end
@interface DSWebMenuCollectionView : UICollectionView <UICollectionViewDelegate,UICollectionViewDataSource>
@property (nonatomic, weak) id<DSWebMenuDelegate> webMenuDelegate;
@property (nonatomic, strong) NSMutableArray <NSNumber *>*shareArray;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,102 @@
//
// DSWebMenuCollectionView.m
// VietnamGame
//
//
#import "DSWebMenuCollectionView.h"
#import "DSAvatarCollectionViewCell.h"
static NSString *identifier = @"AvatarCollectionViewCell";
@implementation DSWebMenuCollectionView
-(void)awakeFromNib
{
[super awakeFromNib];
UICollectionViewFlowLayout *flowlayout = [[UICollectionViewFlowLayout alloc] init];
flowlayout.scrollDirection = UICollectionViewScrollDirectionVertical;
self.collectionViewLayout = flowlayout;
self.delegate = self;
self.dataSource = self;
self.showsVerticalScrollIndicator = NO;
self.showsHorizontalScrollIndicator = NO;
self.backgroundColor = [UIColor clearColor];
[self registerNib:[UINib nibWithNibName:@"DSAvatarCollectionViewCell" bundle:nil] forCellWithReuseIdentifier:identifier];
[self registerClass:[UICollectionReusableView class] forSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:identifier];
}
-(void)setShareArray:(NSMutableArray<NSNumber *> *)shareArray
{
_shareArray = shareArray;
[self reloadData];
}
#pragma mark -
- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView
{
return 1;
}
-(NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
return self.shareArray.count;
}
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
return CGSizeMake(collectionView.frame.size.width, ((collectionView.frame.size.height-(self.shareArray.count-1)*10))/self.shareArray.count);
}
-(UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
{
return UIEdgeInsetsMake(0, 0, 0, 0);
}
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section
{
return 10;
}
- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section
{
return 10;
}
-(UICollectionReusableView *)collectionView:(UICollectionView *)collectionView viewForSupplementaryElementOfKind:(NSString *)kind atIndexPath:(NSIndexPath *)indexPath
{
UICollectionReusableView *view = [collectionView dequeueReusableSupplementaryViewOfKind:UICollectionElementKindSectionHeader withReuseIdentifier:identifier forIndexPath:indexPath];
return view;
}
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
DSAvatarCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:identifier forIndexPath:indexPath];
NSNumber *number = self.shareArray[indexPath.row];
DSWebMenuType type = number.integerValue;
if (type == DSWebMenuHomeType) {
cell.avatarImageView.image = [UIImage imageNamed:@"zhongxindakai"];
}else if (type == DSWebMenuTelegramType) {
cell.avatarImageView.image = [UIImage imageNamed:@"telegram"];
}else if (type == DSWebMenuWhatsAppType) {
cell.avatarImageView.image = [UIImage imageNamed:@"whatsapp"];
}else if (type == DSWebMenuFacebookType) {
cell.avatarImageView.image = [UIImage imageNamed:@"facebook"];
}
cell.avatarImageView.contentMode = UIViewContentModeScaleAspectFit;
return cell;
}
-(void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath
{
NSNumber *number = self.shareArray[indexPath.row];
DSWebMenuType type = number.integerValue;
if (self.webMenuDelegate && [self.webMenuDelegate respondsToSelector:@selector(webMenuCollectionViewWithMenuType:)]) {
[self.webMenuDelegate webMenuCollectionViewWithMenuType:type];
}
}
@end

View File

@@ -0,0 +1,75 @@
//
// WMDragView.h
// WMDragView
//
// Created by zhengwenming on 2016/12/16.
//
//
#import <UIKit/UIKit.h>
// 拖曳view的方向
typedef NS_ENUM(NSInteger, WMDragDirection) {
WMDragDirectionAny, /**< 任意方向 */
WMDragDirectionHorizontal, /**< 水平方向 */
WMDragDirectionVertical, /**< 垂直方向 */
};
@interface WMDragView : UIView
/**
是不是能拖曳默认为YES
YES能拖曳
NO不能拖曳
*/
@property (nonatomic,assign) BOOL dragEnable;
/**
活动范围默认为父视图的frame范围内因为拖出父视图后无法点击也没意义
如果设置了,则会在给定的范围内活动
如果没设置,则会在父视图范围内活动
注意设置的frame不要大于父视图范围
注意设置的frame为0000表示活动的范围为默认的父视图frame如果想要不能活动请设置dragEnable这个属性为NO
*/
@property (nonatomic,assign) CGRect freeRect;
/**
拖曳的方向默认为any任意方向
*/
@property (nonatomic,assign) WMDragDirection dragDirection;
/**
contentView内部懒加载的一个UIImageView
开发者也可以自定义控件添加到本view中
注意最好不要同时使用内部的imageView和button
*/
@property (nonatomic,strong) UIImageView *imageView;
/**
contentView内部懒加载的一个UIButton
开发者也可以自定义控件添加到本view中
注意最好不要同时使用内部的imageView和button
*/
@property (nonatomic,strong) UIButton *button;
/**
是不是总保持在父视图边界默认为NO,没有黏贴边界效果
isKeepBounds = YES它将自动黏贴边界而且是最近的边界
isKeepBounds = NO 它将不会黏贴在边界它是free(自由)状态跟随手指到任意位置但是也不可以拖出给定的范围frame
*/
@property (nonatomic,assign) BOOL isKeepBounds;
/**
点击的回调block
*/
@property (nonatomic,copy) void(^clickDragViewBlock)(WMDragView *dragView);
/**
开始拖动的回调block
*/
@property (nonatomic,copy) void(^beginDragBlock)(WMDragView *dragView);
/**
拖动中的回调block
*/
@property (nonatomic,copy) void(^duringDragBlock)(WMDragView *dragView);
/**
结束拖动的回调block
*/
@property (nonatomic,copy) void(^endDragBlock)(WMDragView *dragView);
@end

View File

@@ -0,0 +1,227 @@
//
// WMDragView.m
// WMDragView
//
// Created by zhengwenming on 2016/12/16.
//
//
#import "WMDragView.h"
@interface WMDragView ()<UIGestureRecognizerDelegate>
@property (nonatomic,strong) UIView *contentViewForDrag;
/**
viewcontentViewForDragcontentView
contentViewForDrag
*/
@property (nonatomic,assign) CGPoint startPoint;
@property (nonatomic,strong) UIPanGestureRecognizer *panGestureRecognizer;
@property (nonatomic,assign) CGFloat previousScale;
@end
@implementation WMDragView
-(UIImageView *)imageView{
if (_imageView==nil) {
_imageView = [[UIImageView alloc]init];
_imageView.userInteractionEnabled = YES;
_imageView.clipsToBounds = YES;
[self.contentViewForDrag addSubview:_imageView];
}
return _imageView;
}
-(UIButton *)button{
if (_button==nil) {
_button = [UIButton buttonWithType:UIButtonTypeCustom];
_button.clipsToBounds = YES;
_button.userInteractionEnabled = NO;
[self.contentViewForDrag addSubview:_button];
}
return _button;
}
-(UIView *)contentViewForDrag{
if (_contentViewForDrag==nil) {
_contentViewForDrag = [[UIView alloc]init];
_contentViewForDrag.clipsToBounds = YES;
}
return _contentViewForDrag;
}
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self addSubview:self.contentViewForDrag];
[self setUp];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)coder
{
self = [super initWithCoder:coder];
if (self) {
[self setUp];
}
return self;
}
-(void)layoutSubviews{
[super layoutSubviews];
if (self.freeRect.origin.x!=0||self.freeRect.origin.y!=0||self.freeRect.size.height!=0||self.freeRect.size.width!=0) {
//freeRect--
}else{
//freeRect--frame
self.freeRect = (CGRect){CGPointZero,self.superview.bounds.size};
}
_imageView.frame = (CGRect){CGPointZero,self.bounds.size};
_button.frame = (CGRect){CGPointZero,self.bounds.size};
self.contentViewForDrag.frame = (CGRect){CGPointZero,self.bounds.size};
}
-(void)setUp{
self.dragEnable = YES;//
self.clipsToBounds = YES;
self.isKeepBounds = NO;
self.backgroundColor = [UIColor lightGrayColor];
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(clickDragView)];
[self addGestureRecognizer:singleTap];
//
self.panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(dragAction:)];
self.panGestureRecognizer.minimumNumberOfTouches = 1;
self.panGestureRecognizer.maximumNumberOfTouches = 1;
self.panGestureRecognizer.delegate = self;
[self addGestureRecognizer:self.panGestureRecognizer];
}
//-(BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
// return self.dragEnable;
//}
/**
@param pan
*/
-(void)dragAction:(UIPanGestureRecognizer *)pan{
if(self.dragEnable==NO)return;
switch (pan.state) {
case UIGestureRecognizerStateBegan:{//
if (self.beginDragBlock) {
self.beginDragBlock(self);
}
//translation0translation
[pan setTranslation:CGPointZero inView:self];
//
self.startPoint = [pan translationInView:self];
break;
}
case UIGestureRecognizerStateChanged:{//
// = -
if (self.duringDragBlock) {
self.duringDragBlock(self);
}
CGPoint point = [pan translationInView:self];
float dx;
float dy;
switch (self.dragDirection) {
case WMDragDirectionAny:
dx = point.x - self.startPoint.x;
dy = point.y - self.startPoint.y;
break;
case WMDragDirectionHorizontal:
dx = point.x - self.startPoint.x;
dy = 0;
break;
case WMDragDirectionVertical:
dx = 0;
dy = point.y - self.startPoint.y;
break;
default:
dx = point.x - self.startPoint.x;
dy = point.y - self.startPoint.y;
break;
}
//view
CGPoint newCenter = CGPointMake(self.center.x + dx, self.center.y + dy);
//view
self.center = newCenter;
// translation0translation
[pan setTranslation:CGPointZero inView:self];
break;
}
case UIGestureRecognizerStateEnded:{//
[self keepBounds];
if (self.endDragBlock) {
self.endDragBlock(self);
}
break;
}
default:
break;
}
}
//
-(void)clickDragView{
if (self.clickDragViewBlock) {
self.clickDragViewBlock(self);
}
}
//
- (void)keepBounds{
//
float centerX = self.freeRect.origin.x+(self.freeRect.size.width - self.frame.size.width)/2;
CGRect rect = self.frame;
if (self.isKeepBounds==NO) {//
if (self.frame.origin.x < self.freeRect.origin.x) {
CGContextRef context = UIGraphicsGetCurrentContext();
[UIView beginAnimations:@"leftMove" context:context];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:0.5];
rect.origin.x = self.freeRect.origin.x;
self.frame = rect;
[UIView commitAnimations];
} else if(self.freeRect.origin.x+self.freeRect.size.width < self.frame.origin.x+self.frame.size.width){
CGContextRef context = UIGraphicsGetCurrentContext();
[UIView beginAnimations:@"rightMove" context:context];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:0.5];
rect.origin.x = self.freeRect.origin.x+self.freeRect.size.width-self.frame.size.width;
self.frame = rect;
[UIView commitAnimations];
}
}else if(self.isKeepBounds==YES){//
if (self.frame.origin.x< centerX) {
CGContextRef context = UIGraphicsGetCurrentContext();
[UIView beginAnimations:@"leftMove" context:context];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:0.5];
rect.origin.x = self.freeRect.origin.x;
self.frame = rect;
[UIView commitAnimations];
} else {
CGContextRef context = UIGraphicsGetCurrentContext();
[UIView beginAnimations:@"rightMove" context:context];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:0.5];
rect.origin.x =self.freeRect.origin.x+self.freeRect.size.width - self.frame.size.width;
self.frame = rect;
[UIView commitAnimations];
}
}
if (self.frame.origin.y < self.freeRect.origin.y) {
CGContextRef context = UIGraphicsGetCurrentContext();
[UIView beginAnimations:@"topMove" context:context];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:0.5];
rect.origin.y = self.freeRect.origin.y;
self.frame = rect;
[UIView commitAnimations];
} else if(self.freeRect.origin.y+self.freeRect.size.height< self.frame.origin.y+self.frame.size.height){
CGContextRef context = UIGraphicsGetCurrentContext();
[UIView beginAnimations:@"bottomMove" context:context];
[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];
[UIView setAnimationDuration:0.5];
rect.origin.y = self.freeRect.origin.y+self.freeRect.size.height-self.frame.size.height;
self.frame = rect;
[UIView commitAnimations];
}
}
@end

View File

@@ -0,0 +1,25 @@
//
// WithdrawRecordTableViewCell.h
// ShenQi
//
// Created by Yao on 2024/11/9.
//
#import <UIKit/UIKit.h>
#import "DataModels.h"
NS_ASSUME_NONNULL_BEGIN
@interface WithdrawRecordTableViewCell : UITableViewCell
@property (weak, nonatomic) IBOutlet UILabel *nameLabel;
@property (weak, nonatomic) IBOutlet UILabel *bankLabel;
@property (weak, nonatomic) IBOutlet UILabel *amountLabel;
@property (weak, nonatomic) IBOutlet UILabel *balanceLabel;
@property (nonatomic, strong) WithdrawRecordList *list;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,34 @@
//
// WithdrawRecordTableViewCell.m
// ShenQi
//
// Created by Yao on 2024/11/9.
//
#import "WithdrawRecordTableViewCell.h"
@implementation WithdrawRecordTableViewCell
- (void)awakeFromNib {
[super awakeFromNib];
// Initialization code
[self setSelectionStyle:(UITableViewCellSelectionStyleNone)];
}
-(void)setList:(WithdrawRecordList *)list
{
_list = list;
self.nameLabel.text = self.list.name;
self.bankLabel.text = [NSString stringWithFormat:@"(%@)",self.list.bankName];
self.amountLabel.text = [NSString stringWithFormat:@"%@:%.1f",NSLocalizedString(@"Amount", nil),self.list.amount];
self.balanceLabel.text = [NSString stringWithFormat:@"%@:%.1f",NSLocalizedString(@"Balance", nil),self.list.balance];
}
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
[super setSelected:selected animated:animated];
// Configure the view for the selected state
}
@end

View File

@@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="22155" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22131"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" id="KGk-i7-Jjw" customClass="WithdrawRecordTableViewCell">
<rect key="frame" x="0.0" y="0.0" width="375" height="70"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
<rect key="frame" x="0.0" y="0.0" width="375" height="70"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="jKK-c5-JOv">
<rect key="frame" x="20" y="35" width="0.0" height="0.0"/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<color key="textColor" red="1" green="0.65490196079999996" blue="0.1333333333" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="4zs-KW-HB2">
<rect key="frame" x="25" y="35" width="0.0" height="0.0"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="1" green="0.65490196079999996" blue="0.1333333333" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="0" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="SpZ-8T-jwQ">
<rect key="frame" x="344.66666666666669" y="10" width="10.333333333333314" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" systemColor="systemGreenColor"/>
<nil key="highlightedColor"/>
</label>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="0" textAlignment="right" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Fu1-rt-B2G">
<rect key="frame" x="344.66666666666669" y="39" width="10.333333333333314" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" systemColor="systemRedColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" red="0.17254901959999999" green="0.17254901959999999" blue="0.18039215689999999" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstItem="Fu1-rt-B2G" firstAttribute="trailing" secondItem="SpZ-8T-jwQ" secondAttribute="trailing" id="1SU-Mn-afp"/>
<constraint firstItem="SpZ-8T-jwQ" firstAttribute="top" secondItem="H2p-sc-9uM" secondAttribute="top" constant="10" id="86r-OC-OkI"/>
<constraint firstItem="4zs-KW-HB2" firstAttribute="centerY" secondItem="jKK-c5-JOv" secondAttribute="centerY" id="AiM-u0-TVh"/>
<constraint firstItem="jKK-c5-JOv" firstAttribute="centerY" secondItem="H2p-sc-9uM" secondAttribute="centerY" id="H6B-in-hNS"/>
<constraint firstItem="jKK-c5-JOv" firstAttribute="leading" secondItem="H2p-sc-9uM" secondAttribute="leading" constant="20" id="K8D-37-Su6"/>
<constraint firstAttribute="trailing" secondItem="SpZ-8T-jwQ" secondAttribute="trailing" constant="20" id="eMt-GX-8Ap"/>
<constraint firstAttribute="bottom" secondItem="Fu1-rt-B2G" secondAttribute="bottom" constant="10" id="lZT-Wc-POQ"/>
<constraint firstItem="4zs-KW-HB2" firstAttribute="leading" secondItem="jKK-c5-JOv" secondAttribute="trailing" constant="5" id="m6A-fc-7RB"/>
</constraints>
</tableViewCellContentView>
<viewLayoutGuide key="safeArea" id="aW0-zy-SZf"/>
<color key="backgroundColor" red="0.17254901959999999" green="0.17254901959999999" blue="0.18039215689999999" alpha="1" colorSpace="calibratedRGB"/>
<connections>
<outlet property="amountLabel" destination="SpZ-8T-jwQ" id="skA-X7-he5"/>
<outlet property="balanceLabel" destination="Fu1-rt-B2G" id="fXz-N3-Xre"/>
<outlet property="bankLabel" destination="4zs-KW-HB2" id="gdx-Tl-Tle"/>
<outlet property="nameLabel" destination="jKK-c5-JOv" id="Fqf-R6-MaT"/>
</connections>
<point key="canvasLocation" x="46" y="20"/>
</tableViewCell>
</objects>
<resources>
<systemColor name="systemGreenColor">
<color red="0.20392156862745098" green="0.7803921568627451" blue="0.34901960784313724" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
<systemColor name="systemRedColor">
<color red="1" green="0.23137254901960785" blue="0.18823529411764706" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
</resources>
</document>

View File

@@ -0,0 +1,16 @@
//
// WithdrawRecordsViewController.h
// ShenQi
//
// Created by Yao on 2024/11/9.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface WithdrawRecordsViewController : UIViewController
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,176 @@
//
// WithdrawRecordsViewController.m
// ShenQi
//
// Created by Yao on 2024/11/9.
//
#import "WithdrawRecordsViewController.h"
#import "WithdrawRecordTableViewCell.h"
#import "DataModels.h"
#import "MacroDefine.h"
#import "InviteRecordsTableViewCell.h"
#import "AppDelegate.h"
#import <MJRefresh.h>
#import <SVProgressHUD.h>
#import <AdSupport/AdSupport.h>
#import <AppTrackingTransparency/AppTrackingTransparency.h>
@interface WithdrawRecordsViewController () <UITableViewDelegate,UITableViewDataSource>
@property (weak, nonatomic) IBOutlet UILabel *titleLabel;
@property (weak, nonatomic) IBOutlet UITableView *recordTableView;
@property (weak, nonatomic) IBOutlet UILabel *emptyLabel;
@property (weak, nonatomic) IBOutlet UIActivityIndicatorView *activityIndicator;
@property (nonatomic, assign) NSInteger page;
@property (nonatomic, strong) NSMutableArray <WithdrawRecordList *>*recordsArray;
@property (nonatomic, strong) NSString *inviteCode;
@end
static NSString *identifier = @"WithdrawRecordTableViewCell";
@implementation WithdrawRecordsViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.titleLabel.text = NSLocalizedString(@"WithdrawRecord", nil);
self.recordTableView.delegate = self;
self.recordTableView.dataSource = self;
self.recordTableView.rowHeight = 70;
self.recordTableView.mj_header = [MJRefreshNormalHeader headerWithRefreshingBlock:^{
self.page = 1;
self.recordsArray = @[].mutableCopy;
[self refreshWithdrawRecords];
}];
[self.recordTableView registerNib:[UINib nibWithNibName:@"WithdrawRecordTableViewCell" bundle:nil] forCellReuseIdentifier:identifier];
self.recordTableView.tableFooterView = [UIView new];
self.page = 1;
self.recordsArray = @[].mutableCopy;
[self setupInviteCode];
}
- (IBAction)dismissAction:(id)sender
{
[self dismissViewControllerAnimated:YES completion:nil];
}
-(void)refreshWithdrawRecords
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"%@?inviteCode=%@&page=%@&size=%@",WithdrawURL,self.inviteCode,[NSNumber numberWithInteger:self.page],[NSNumber numberWithInteger:20]]]];
request.HTTPMethod = @"GET";
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error) {
[SVProgressHUD showInfoWithStatus:error.localizedDescription];
}else{
NSError *error;
NSMutableDictionary *responseObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
if (error) {
[SVProgressHUD showInfoWithStatus:error.localizedDescription];
}else{
if (ISNULL(error) && !ISNULL(responseObject) && [[responseObject objectForKey:@"code"] integerValue] == 1) {
WithdrawRecordDataModel *model = [[WithdrawRecordDataModel alloc] initWithDictionary:responseObject];
if (model.data.list.count < 20) {
self.recordTableView.mj_footer = nil;
}else if (model.data.list.count == 20) {
self.recordTableView.mj_footer = [MJRefreshBackNormalFooter footerWithRefreshingBlock:^{
self.page++;
[self refreshWithdrawRecords];
}];
}
if (!ISNULLARRAY(model.data.list)) {
[self.recordsArray addObjectsFromArray:model.data.list];
}
}
}
}
if (ISNULLARRAY(self.recordsArray)) {
self.emptyLabel.hidden = NO;
}else{
self.emptyLabel.hidden = YES;
}
[self.activityIndicator stopAnimating];
[self.recordTableView reloadData];
[self.recordTableView.mj_header endRefreshing];
[self.recordTableView.mj_footer endRefreshing];
});
}];
[dataTask resume];
}
-(void)setupInviteCode
{
//
__block NSString *advertisingId = nil;
AppDelegate *appdelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
if (ISNULLSTR(appdelegate.inviteCode)) {
if (@available(iOS 14, *)) {
[ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus status) {
advertisingId = [[ASIdentifierManager sharedManager] advertisingIdentifier].UUIDString;
[self getCodeWithAdvertisingId:advertisingId];
}];
} else {
advertisingId = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
[self getCodeWithAdvertisingId:advertisingId];
}
}else{
self.inviteCode = appdelegate.inviteCode;
[self refreshWithdrawRecords];
}
}
-(void)getCodeWithAdvertisingId:(NSString *)advertisingId
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:InviteCodeURL]];
request.HTTPMethod = @"POST";
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
NSDictionary *parmas = @{@"userId":[NSNumber numberWithInteger:[VV88AUUserID integerValue]],@"deviceCode":advertisingId};
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:parmas options:NSJSONWritingPrettyPrinted error:nil];
request.HTTPBody = jsonData;
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error == nil) {
NSError *error;
NSMutableDictionary *responseObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
if (error == nil) {
if (ISNULL(error) && !ISNULL(responseObject) && [[responseObject objectForKey:@"code"] integerValue] == 1) {
InviteCodeModelDataModel *model = [[InviteCodeModelDataModel alloc] initWithDictionary:responseObject];
self.inviteCode = model.data.inviteCode;
[self refreshWithdrawRecords];
}
}
}
});
}];
[dataTask resume];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return self.recordsArray.count;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
WithdrawRecordTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
WithdrawRecordList *list = [self.recordsArray objectAtIndex:indexPath.row];
cell.list = list;
return cell;
}
@end

View File

@@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="22155" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22131"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="WithdrawRecordsViewController">
<connections>
<outlet property="activityIndicator" destination="Bx0-bS-Acy" id="XCP-8h-eNA"/>
<outlet property="emptyLabel" destination="yq6-qA-uko" id="6Mc-Xf-DPM"/>
<outlet property="recordTableView" destination="MWc-Wc-wc6" id="Yfj-FF-jne"/>
<outlet property="titleLabel" destination="js8-IZ-QrG" id="hTy-qC-NX7"/>
<outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT">
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="dH8-iz-7z1">
<rect key="frame" x="20" y="76" width="30" height="30"/>
<constraints>
<constraint firstAttribute="width" secondItem="dH8-iz-7z1" secondAttribute="height" multiplier="1:1" id="Ato-4i-hyR"/>
<constraint firstAttribute="height" constant="30" id="N0T-8U-iqm"/>
</constraints>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal" image="goback"/>
<connections>
<action selector="dismissAction:" destination="-1" eventType="touchUpInside" id="cKp-At-HBh"/>
</connections>
</button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="提现记录" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="js8-IZ-QrG">
<rect key="frame" x="162" y="79" width="69.333333333333314" height="24"/>
<constraints>
<constraint firstAttribute="height" constant="24" id="4Wa-w7-SC0"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="1" green="0.65490196079999996" blue="0.1333333333" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="-1" estimatedSectionHeaderHeight="-1" sectionFooterHeight="-1" estimatedSectionFooterHeight="-1" translatesAutoresizingMaskIntoConstraints="NO" id="MWc-Wc-wc6">
<rect key="frame" x="0.0" y="123" width="393" height="695"/>
<color key="backgroundColor" red="0.17254901959999999" green="0.17254901959999999" blue="0.18039215689999999" alpha="1" colorSpace="calibratedRGB"/>
</tableView>
<activityIndicatorView opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" hidesWhenStopped="YES" animating="YES" style="gray" translatesAutoresizingMaskIntoConstraints="NO" id="Bx0-bS-Acy">
<rect key="frame" x="186.66666666666666" y="460.66666666666669" width="20" height="20"/>
</activityIndicatorView>
<label hidden="YES" opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="-- No Data --" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="yq6-qA-uko">
<rect key="frame" x="0.0" y="123" width="393" height="695"/>
<color key="backgroundColor" red="0.17254901959999999" green="0.17254901959999999" blue="0.18039215689999999" alpha="1" colorSpace="calibratedRGB"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" white="0.66666666669999997" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<viewLayoutGuide key="safeArea" id="Q5M-cg-NOt"/>
<color key="backgroundColor" red="0.17254901959999999" green="0.17254901959999999" blue="0.18039215689999999" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstItem="js8-IZ-QrG" firstAttribute="centerX" secondItem="Q5M-cg-NOt" secondAttribute="centerX" id="7mo-VC-N2X"/>
<constraint firstItem="Bx0-bS-Acy" firstAttribute="centerY" secondItem="MWc-Wc-wc6" secondAttribute="centerY" id="ERf-TU-35X"/>
<constraint firstItem="MWc-Wc-wc6" firstAttribute="centerX" secondItem="Q5M-cg-NOt" secondAttribute="centerX" id="FWP-mW-UnK"/>
<constraint firstItem="yq6-qA-uko" firstAttribute="centerY" secondItem="MWc-Wc-wc6" secondAttribute="centerY" id="QHf-h5-tED"/>
<constraint firstItem="yq6-qA-uko" firstAttribute="width" secondItem="MWc-Wc-wc6" secondAttribute="width" id="SWd-I0-wob"/>
<constraint firstItem="Q5M-cg-NOt" firstAttribute="bottom" secondItem="MWc-Wc-wc6" secondAttribute="bottom" id="TdF-oZ-Q4m"/>
<constraint firstItem="MWc-Wc-wc6" firstAttribute="top" secondItem="js8-IZ-QrG" secondAttribute="bottom" constant="20" id="Wln-xB-rlm"/>
<constraint firstItem="dH8-iz-7z1" firstAttribute="centerY" secondItem="js8-IZ-QrG" secondAttribute="centerY" id="ajh-mH-FYP"/>
<constraint firstItem="dH8-iz-7z1" firstAttribute="leading" secondItem="Q5M-cg-NOt" secondAttribute="leading" constant="20" id="axX-ke-axg"/>
<constraint firstItem="yq6-qA-uko" firstAttribute="centerX" secondItem="MWc-Wc-wc6" secondAttribute="centerX" id="bPg-Rk-9zd"/>
<constraint firstItem="js8-IZ-QrG" firstAttribute="top" secondItem="Q5M-cg-NOt" secondAttribute="top" constant="20" id="m94-ym-58G"/>
<constraint firstItem="Bx0-bS-Acy" firstAttribute="centerX" secondItem="MWc-Wc-wc6" secondAttribute="centerX" id="max-A1-chL"/>
<constraint firstItem="MWc-Wc-wc6" firstAttribute="width" secondItem="i5M-Pr-FkT" secondAttribute="width" id="qGR-nh-Zvt"/>
<constraint firstItem="yq6-qA-uko" firstAttribute="height" secondItem="MWc-Wc-wc6" secondAttribute="height" id="s4S-8K-Ukw"/>
</constraints>
<point key="canvasLocation" x="138" y="20"/>
</view>
</objects>
<resources>
<image name="goback" width="200" height="200"/>
</resources>
</document>

View File

@@ -0,0 +1,16 @@
//
// WithdrawViewController.h
// ShenQi
//
// Created by Yao on 2024/11/9.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface WithdrawViewController : UIViewController
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,206 @@
//
// WithdrawViewController.m
// ShenQi
//
// Created by Yao on 2024/11/9.
//
#import "WithdrawViewController.h"
#import "DataModels.h"
#import "MacroDefine.h"
#import "InviteRecordsTableViewCell.h"
#import "AppDelegate.h"
#import "DSTextField.h"
#import <MJRefresh.h>
#import <SVProgressHUD.h>
#import <AdSupport/AdSupport.h>
#import <AppTrackingTransparency/AppTrackingTransparency.h>
#import "WithdrawRecordsViewController.h"
@interface WithdrawViewController () <UITextFieldDelegate>
@property (weak, nonatomic) IBOutlet UILabel *titleLabel;
@property (weak, nonatomic) IBOutlet UILabel *balanceLabel;
@property (weak, nonatomic) IBOutlet UILabel *inputWithdrawLabel;
@property (weak, nonatomic) IBOutlet DSTextField *withdrawTextField;
@property (weak, nonatomic) IBOutlet UIButton *withdrawBtn;
@property (weak, nonatomic) IBOutlet UIButton *withdrawRecordBtn;
@property (nonatomic, strong) NSString *inviteCode;
@property (nonatomic, assign) CGFloat balance;
@end
@implementation WithdrawViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.titleLabel.text = NSLocalizedString(@"Withdraw", nil);
self.balanceLabel.text = [NSString stringWithFormat:@"%@:",NSLocalizedString(@"Balance", nil)];
self.inputWithdrawLabel.text = [NSString stringWithFormat:@"%@%@%@%@",NSLocalizedString(@"PLS", nil),NSLocalizedString(@"InputEdit", nil),NSLocalizedString(@"Withdraw", nil),NSLocalizedString(@"Amount", nil)];
self.withdrawTextField.delegate = self;
self.withdrawTextField.placeHolderString = [NSString stringWithFormat:@"%@%@%@%@",NSLocalizedString(@"PLS", nil),NSLocalizedString(@"InputEdit", nil),NSLocalizedString(@"Withdraw", nil),NSLocalizedString(@"Amount", nil)];
self.withdrawBtn.layer.cornerRadius = 20.f;
self.withdrawBtn.layer.masksToBounds = YES;
[self.withdrawBtn setTitle:NSLocalizedString(@"OK", nil) forState:(UIControlStateNormal)];
[self.withdrawBtn addTarget:self action:@selector(withdrawAction) forControlEvents:(UIControlEventTouchUpInside)];
[self.withdrawRecordBtn setTitle:NSLocalizedString(@"WithdrawRecord", nil) forState:(UIControlStateNormal)];
[self.withdrawRecordBtn addTarget:self action:@selector(withdrawRecordAction) forControlEvents:(UIControlEventTouchUpInside)];
[self setupInviteCode];
}
- (IBAction)dismissAction:(id)sender
{
[self dismissViewControllerAnimated:YES completion:nil];
}
-(void)withdrawAction
{
NSString *amout = self.withdrawTextField.text;
if ([amout floatValue] > 0) {
if ([amout floatValue] > self.balance) {
[SVProgressHUD showInfoWithStatus:[NSString stringWithFormat:@"%@%@",NSLocalizedString(@"OverCanWithdraw", nil),NSLocalizedString(@"Amount", nil)]];
}else{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:WithdrawApplyURL]];
request.HTTPMethod = @"POST";
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
NSDictionary *parmas = @{@"amount":[NSNumber numberWithFloat:[amout floatValue]],@"inviteCode":self.inviteCode};
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:parmas options:NSJSONWritingPrettyPrinted error:nil];
request.HTTPBody = jsonData;
[SVProgressHUD show];
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error == nil) {
NSError *error;
NSMutableDictionary *responseObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
if (error == nil) {
if (ISNULL(error) && !ISNULL(responseObject) && [[responseObject objectForKey:@"code"] integerValue] == 1) {
[SVProgressHUD showSuccessWithStatus:nil];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[self dismissAction:nil];
});
}else{
[SVProgressHUD showInfoWithStatus:error.localizedDescription];
}
}else{
NSString *errmsg = [responseObject objectForKey:@"error"];
[SVProgressHUD showInfoWithStatus:errmsg];
}
}else{
[SVProgressHUD showInfoWithStatus:error.localizedDescription];
}
});
}];
[dataTask resume];
}
}else{
[SVProgressHUD showInfoWithStatus:[NSString stringWithFormat:@"%@%@%@%@",NSLocalizedString(@"PLS", nil),NSLocalizedString(@"InputEdit", nil),NSLocalizedString(@"Withdraw", nil),NSLocalizedString(@"Amount", nil)]];
}
}
-(void)withdrawRecordAction
{
WithdrawRecordsViewController *withdrawRecordsViewController = [[WithdrawRecordsViewController alloc] initWithNibName:@"WithdrawRecordsViewController" bundle:nil];
[self presentViewController:withdrawRecordsViewController animated:YES completion:nil];
}
-(void)setupInviteCode
{
//
__block NSString *advertisingId = nil;
AppDelegate *appdelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
if (@available(iOS 14, *)) {
[ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus status) {
advertisingId = [[ASIdentifierManager sharedManager] advertisingIdentifier].UUIDString;
[self getCodeWithAdvertisingId:advertisingId];
}];
} else {
advertisingId = [[[ASIdentifierManager sharedManager] advertisingIdentifier] UUIDString];
[self getCodeWithAdvertisingId:advertisingId];
}
}
-(void)getCodeWithAdvertisingId:(NSString *)advertisingId
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:InviteCodeURL]];
request.HTTPMethod = @"POST";
[request setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
NSDictionary *parmas = @{@"userId":[NSNumber numberWithInteger:[VV88AUUserID integerValue]],@"deviceCode":advertisingId};
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:parmas options:NSJSONWritingPrettyPrinted error:nil];
request.HTTPBody = jsonData;
NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error == nil) {
NSError *error;
NSMutableDictionary *responseObject = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&error];
if (error == nil) {
if (ISNULL(error) && !ISNULL(responseObject) && [[responseObject objectForKey:@"code"] integerValue] == 1) {
InviteCodeModelDataModel *model = [[InviteCodeModelDataModel alloc] initWithDictionary:responseObject];
self.inviteCode = model.data.inviteCode;
AppDelegate *appdelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
appdelegate.inviteCode = self.inviteCode;
self.balance = model.data.balance;
self.balanceLabel.text = [NSString stringWithFormat:@"%@%.1f",NSLocalizedString(@"Balance", nil),self.balance];
}
}
}
});
}];
[dataTask resume];
}
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self.view endEditing:YES];
}
-(BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
NSString *text = nil;
if (range.location >= textField.text.length) {
text = [textField.text stringByAppendingString:string];
} else {
text = [textField.text stringByReplacingCharactersInRange:range withString:string];
}
if ([string containsString:@"."]) {
if ([textField.text containsString:@"."]) {
return NO;
}
}else{
if ([textField.text containsString:@"."] && ![string isEqualToString:@""]) {
NSString *floatAmout = [textField.text componentsSeparatedByString:@"."].lastObject;
if (floatAmout.length >= 2) {
return NO;
}
}
}
return YES;
}
-(BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
textField.clearButtonMode = UITextFieldViewModeAlways;
return YES;
}
-(void)textFieldDidEndEditing:(UITextField *)textField
{
textField.clearButtonMode = UITextFieldViewModeNever;
}
@end

View File

@@ -0,0 +1,150 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="22155" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_12" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22131"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner" customClass="WithdrawViewController">
<connections>
<outlet property="balanceLabel" destination="T75-vj-VnB" id="5GD-gO-SOb"/>
<outlet property="inputWithdrawLabel" destination="1BA-8S-jTt" id="pKY-4o-VE7"/>
<outlet property="titleLabel" destination="AhV-Hg-61j" id="f9Y-dj-Kgh"/>
<outlet property="view" destination="i5M-Pr-FkT" id="sfx-zR-JGt"/>
<outlet property="withdrawBtn" destination="jfA-y8-Khm" id="RvK-4j-WPk"/>
<outlet property="withdrawRecordBtn" destination="Krj-Fn-yYQ" id="w5E-3k-loT"/>
<outlet property="withdrawTextField" destination="Z2c-ej-LlX" id="Etr-y6-L8b"/>
</connections>
</placeholder>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<view clearsContextBeforeDrawing="NO" contentMode="scaleToFill" id="i5M-Pr-FkT">
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="ehs-Nv-C0g">
<rect key="frame" x="20" y="74.666666666666671" width="30" height="30"/>
<constraints>
<constraint firstAttribute="height" constant="30" id="Yx4-Oa-z1l"/>
<constraint firstAttribute="width" secondItem="ehs-Nv-C0g" secondAttribute="height" multiplier="1:1" id="kJF-6Z-j5j"/>
</constraints>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal" image="goback"/>
<connections>
<action selector="dismissAction:" destination="-1" eventType="touchUpInside" id="700-2F-5Bv"/>
</connections>
</button>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="提现" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="AhV-Hg-61j">
<rect key="frame" x="179.33333333333334" y="79" width="34.666666666666657" height="21"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<color key="textColor" red="1" green="0.65490196079999996" blue="0.1333333333" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Ghd-aH-IIl">
<rect key="frame" x="30" y="140" width="333" height="40"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="可提现金额:" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="T75-vj-VnB">
<rect key="frame" x="0.0" y="13" width="67.666666666666671" height="14.333333333333336"/>
<fontDescription key="fontDescription" type="system" pointSize="12"/>
<color key="textColor" red="1" green="0.65490196079999996" blue="0.1333333333" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
</subviews>
<color key="backgroundColor" red="0.17254901959999999" green="0.17254901959999999" blue="0.18039215689999999" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstAttribute="height" constant="40" id="CyI-6A-L0q"/>
<constraint firstItem="T75-vj-VnB" firstAttribute="leading" secondItem="Ghd-aH-IIl" secondAttribute="leading" id="aVa-x4-sg2"/>
<constraint firstItem="T75-vj-VnB" firstAttribute="centerY" secondItem="Ghd-aH-IIl" secondAttribute="centerY" id="ufD-QN-9Eh"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="V46-wV-xhF">
<rect key="frame" x="30" y="195" width="333" height="40"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="请输入提现金额:" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="1BA-8S-jTt">
<rect key="frame" x="0.0" y="10.333333333333341" width="122.66666666666667" height="19.333333333333329"/>
<fontDescription key="fontDescription" type="system" pointSize="16"/>
<color key="textColor" red="1" green="0.65490196079999996" blue="0.1333333333" alpha="1" colorSpace="calibratedRGB"/>
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="QuA-J1-vLm">
<rect key="frame" x="132.66666666666663" y="0.0" width="200.33333333333337" height="40"/>
<subviews>
<textField opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="248" contentHorizontalAlignment="left" contentVerticalAlignment="center" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="Z2c-ej-LlX" customClass="DSTextField">
<rect key="frame" x="10" y="0.0" width="190.33333333333334" height="40"/>
<color key="textColor" red="1" green="0.65490196079999996" blue="0.1333333333" alpha="1" colorSpace="calibratedRGB"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<textInputTraits key="textInputTraits" keyboardType="numbersAndPunctuation"/>
</textField>
</subviews>
<color key="backgroundColor" red="0.2627450980392157" green="0.2627450980392157" blue="0.2627450980392157" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstItem="Z2c-ej-LlX" firstAttribute="centerY" secondItem="QuA-J1-vLm" secondAttribute="centerY" id="Kqh-LD-GpA"/>
<constraint firstItem="Z2c-ej-LlX" firstAttribute="height" secondItem="QuA-J1-vLm" secondAttribute="height" id="M1d-OS-guH"/>
<constraint firstAttribute="trailing" secondItem="Z2c-ej-LlX" secondAttribute="trailing" id="dd6-tO-yGg"/>
<constraint firstItem="Z2c-ej-LlX" firstAttribute="leading" secondItem="QuA-J1-vLm" secondAttribute="leading" constant="10" id="vnt-2K-ISr"/>
</constraints>
</view>
</subviews>
<color key="backgroundColor" red="0.17254901959999999" green="0.17254901959999999" blue="0.18039215689999999" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstItem="1BA-8S-jTt" firstAttribute="leading" secondItem="V46-wV-xhF" secondAttribute="leading" id="BEp-BV-EvW"/>
<constraint firstItem="QuA-J1-vLm" firstAttribute="height" secondItem="V46-wV-xhF" secondAttribute="height" id="FJG-Us-dN8"/>
<constraint firstItem="QuA-J1-vLm" firstAttribute="centerY" secondItem="V46-wV-xhF" secondAttribute="centerY" id="PqA-Nr-Tzz"/>
<constraint firstAttribute="trailing" secondItem="QuA-J1-vLm" secondAttribute="trailing" id="WcO-4s-s21"/>
<constraint firstAttribute="height" constant="40" id="e7c-hl-avV"/>
<constraint firstItem="QuA-J1-vLm" firstAttribute="leading" secondItem="1BA-8S-jTt" secondAttribute="trailing" constant="10" id="qru-KU-LfN"/>
<constraint firstItem="1BA-8S-jTt" firstAttribute="centerY" secondItem="V46-wV-xhF" secondAttribute="centerY" id="rac-U9-Oh4"/>
</constraints>
</view>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="jfA-y8-Khm">
<rect key="frame" x="30" y="275" width="333" height="40"/>
<color key="backgroundColor" systemColor="systemRedColor"/>
<constraints>
<constraint firstAttribute="height" constant="40" id="hAN-hP-s6n"/>
</constraints>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal" title="确定">
<color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</state>
</button>
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Krj-Fn-yYQ">
<rect key="frame" x="315" y="75" width="58" height="29"/>
<fontDescription key="fontDescription" type="system" pointSize="14"/>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal" title="提现记录">
<color key="titleColor" red="1" green="0.65490196079999996" blue="0.1333333333" alpha="1" colorSpace="calibratedRGB"/>
</state>
</button>
</subviews>
<viewLayoutGuide key="safeArea" id="Q5M-cg-NOt"/>
<color key="backgroundColor" red="0.17254901959999999" green="0.17254901959999999" blue="0.18039215689999999" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstItem="Krj-Fn-yYQ" firstAttribute="centerY" secondItem="AhV-Hg-61j" secondAttribute="centerY" id="6o6-0N-VPM"/>
<constraint firstItem="Ghd-aH-IIl" firstAttribute="top" secondItem="AhV-Hg-61j" secondAttribute="bottom" constant="40" id="7SS-uu-e3M"/>
<constraint firstItem="AhV-Hg-61j" firstAttribute="centerX" secondItem="Q5M-cg-NOt" secondAttribute="centerX" id="Bxz-bK-e0D"/>
<constraint firstItem="AhV-Hg-61j" firstAttribute="top" secondItem="Q5M-cg-NOt" secondAttribute="top" constant="20" id="HzE-ql-VZX"/>
<constraint firstItem="V46-wV-xhF" firstAttribute="leading" secondItem="Ghd-aH-IIl" secondAttribute="leading" id="Jdy-ha-byx"/>
<constraint firstItem="Ghd-aH-IIl" firstAttribute="centerX" secondItem="Q5M-cg-NOt" secondAttribute="centerX" id="Mnv-Jk-Mke"/>
<constraint firstItem="jfA-y8-Khm" firstAttribute="centerX" secondItem="Q5M-cg-NOt" secondAttribute="centerX" id="NO9-R4-JxA"/>
<constraint firstItem="Q5M-cg-NOt" firstAttribute="trailing" secondItem="Krj-Fn-yYQ" secondAttribute="trailing" constant="20" id="T4Y-Qe-VbP"/>
<constraint firstItem="V46-wV-xhF" firstAttribute="top" secondItem="Ghd-aH-IIl" secondAttribute="bottom" constant="15" id="Yuf-Jt-nfK"/>
<constraint firstItem="ehs-Nv-C0g" firstAttribute="leading" secondItem="Q5M-cg-NOt" secondAttribute="leading" constant="20" id="c0I-Fs-TJn"/>
<constraint firstItem="ehs-Nv-C0g" firstAttribute="centerY" secondItem="AhV-Hg-61j" secondAttribute="centerY" id="daF-ST-1OJ"/>
<constraint firstItem="jfA-y8-Khm" firstAttribute="leading" secondItem="Q5M-cg-NOt" secondAttribute="leading" constant="30" id="hN0-JL-GNM"/>
<constraint firstItem="V46-wV-xhF" firstAttribute="width" secondItem="Ghd-aH-IIl" secondAttribute="width" id="ks9-dv-KY3"/>
<constraint firstItem="Ghd-aH-IIl" firstAttribute="leading" secondItem="Q5M-cg-NOt" secondAttribute="leading" constant="30" id="lUP-fQ-JJY"/>
<constraint firstItem="jfA-y8-Khm" firstAttribute="top" secondItem="V46-wV-xhF" secondAttribute="bottom" constant="40" id="uhz-gS-1CW"/>
</constraints>
<point key="canvasLocation" x="138" y="20"/>
</view>
</objects>
<resources>
<image name="goback" width="200" height="200"/>
<systemColor name="systemRedColor">
<color red="1" green="0.23137254901960785" blue="0.18823529411764706" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
</resources>
</document>

View File

@@ -0,0 +1,33 @@
//
// ConfigData.h
//
// Created by Yao on 2024/12/19
// Copyright (c) 2024 zL. All rights reserved.
//
#import <Foundation/Foundation.h>
@interface ConfigData : NSObject <NSCoding, NSCopying>
@property (nonatomic, strong) NSString *description;
@property (nonatomic, strong) NSString *iosVersionCode;
@property (nonatomic, assign) NSInteger forceUpdate;
@property (nonatomic, strong) NSString *fbUrl;
@property (nonatomic, strong) NSString *credentials;
@property (nonatomic, strong) NSString *url;
@property (nonatomic, assign) CGFloat inviteFee;
@property (nonatomic, assign) NSInteger userId;
@property (nonatomic, assign) NSInteger contactApplyMode;
@property (nonatomic, strong) NSString *versionCode;
@property (nonatomic, strong) NSString *tgUrl;
@property (nonatomic, assign) NSInteger noticeApplyMode;
@property (nonatomic, strong) NSString *wsUrl;
@property (nonatomic, strong) NSString *apkUrl;
@property (nonatomic, strong) NSString *downloadUrl;
@property (nonatomic, assign) NSInteger isUse;
+ (instancetype)modelObjectWithDictionary:(NSDictionary *)dict;
- (instancetype)initWithDictionary:(NSDictionary *)dict;
- (NSDictionary *)dictionaryRepresentation;
@end

178
ShenQi/YYModel/ConfigData.m Normal file
View File

@@ -0,0 +1,178 @@
//
// ConfigData.m
//
// Created by Yao on 2024/12/19
// Copyright (c) 2024 zL. All rights reserved.
//
#import "ConfigData.h"
NSString *const kConfigDataDescription = @"description";
NSString *const kConfigDataIosVersionCode = @"iosVersionCode";
NSString *const kConfigDataForceUpdate = @"forceUpdate";
NSString *const kConfigDataFbUrl = @"fbUrl";
NSString *const kConfigDataCredentials = @"credentials";
NSString *const kConfigDataUrl = @"url";
NSString *const kConfigDataInviteFee = @"inviteFee";
NSString *const kConfigDataUserId = @"userId";
NSString *const kConfigDataContactApplyMode = @"contactApplyMode";
NSString *const kConfigDataVersionCode = @"versionCode";
NSString *const kConfigDataTgUrl = @"tgUrl";
NSString *const kConfigDataNoticeApplyMode = @"noticeApplyMode";
NSString *const kConfigDataWsUrl = @"wsUrl";
NSString *const kConfigDataApkUrl = @"apkUrl";
NSString *const kConfigDataDownloadUrl = @"downloadUrl";
NSString *const kConfigDataIsUse = @"isUse";
@interface ConfigData ()
- (id)objectOrNilForKey:(id)aKey fromDictionary:(NSDictionary *)dict;
@end
@implementation ConfigData
@synthesize description = _description;
@synthesize iosVersionCode = _iosVersionCode;
@synthesize forceUpdate = _forceUpdate;
@synthesize fbUrl = _fbUrl;
@synthesize credentials = _credentials;
@synthesize url = _url;
@synthesize inviteFee = _inviteFee;
@synthesize userId = _userId;
@synthesize contactApplyMode = _contactApplyMode;
@synthesize versionCode = _versionCode;
@synthesize tgUrl = _tgUrl;
@synthesize noticeApplyMode = _noticeApplyMode;
@synthesize wsUrl = _wsUrl;
@synthesize apkUrl = _apkUrl;
@synthesize downloadUrl = _downloadUrl;
@synthesize isUse = _isUse;
+ (instancetype)modelObjectWithDictionary:(NSDictionary *)dict {
return [[self alloc] initWithDictionary:dict];
}
- (instancetype)initWithDictionary:(NSDictionary *)dict {
self = [super init];
// This check serves to make sure that a non-NSDictionary object
// passed into the model class doesn't break the parsing.
if (self && [dict isKindOfClass:[NSDictionary class]]) {
self.description = [self objectOrNilForKey:kConfigDataDescription fromDictionary:dict];
self.iosVersionCode = [self objectOrNilForKey:kConfigDataIosVersionCode fromDictionary:dict];
self.forceUpdate = [[self objectOrNilForKey:kConfigDataForceUpdate fromDictionary:dict] intValue];
self.fbUrl = [self objectOrNilForKey:kConfigDataFbUrl fromDictionary:dict];
self.credentials = [self objectOrNilForKey:kConfigDataCredentials fromDictionary:dict];
self.url = [self objectOrNilForKey:kConfigDataUrl fromDictionary:dict];
self.inviteFee = [[self objectOrNilForKey:kConfigDataInviteFee fromDictionary:dict] doubleValue];
self.userId = [[self objectOrNilForKey:kConfigDataUserId fromDictionary:dict] intValue];
self.contactApplyMode = [[self objectOrNilForKey:kConfigDataContactApplyMode fromDictionary:dict] intValue];
self.versionCode = [self objectOrNilForKey:kConfigDataVersionCode fromDictionary:dict];
self.tgUrl = [self objectOrNilForKey:kConfigDataTgUrl fromDictionary:dict];
self.noticeApplyMode = [[self objectOrNilForKey:kConfigDataNoticeApplyMode fromDictionary:dict] intValue];
self.wsUrl = [self objectOrNilForKey:kConfigDataWsUrl fromDictionary:dict];
self.apkUrl = [self objectOrNilForKey:kConfigDataApkUrl fromDictionary:dict];
self.downloadUrl = [self objectOrNilForKey:kConfigDataDownloadUrl fromDictionary:dict];
self.isUse = [[self objectOrNilForKey:kConfigDataIsUse fromDictionary:dict] intValue];
}
return self;
}
- (NSDictionary *)dictionaryRepresentation {
NSMutableDictionary *mutableDict = [NSMutableDictionary dictionary];
[mutableDict setValue:self.description forKey:kConfigDataDescription];
[mutableDict setValue:self.iosVersionCode forKey:kConfigDataIosVersionCode];
[mutableDict setValue:[NSNumber numberWithInteger:self.forceUpdate] forKey:kConfigDataForceUpdate];
[mutableDict setValue:self.fbUrl forKey:kConfigDataFbUrl];
[mutableDict setValue:self.credentials forKey:kConfigDataCredentials];
[mutableDict setValue:self.url forKey:kConfigDataUrl];
[mutableDict setValue:[NSNumber numberWithDouble:self.inviteFee] forKey:kConfigDataInviteFee];
[mutableDict setValue:[NSNumber numberWithInteger:self.userId] forKey:kConfigDataUserId];
[mutableDict setValue:[NSNumber numberWithInteger:self.contactApplyMode] forKey:kConfigDataContactApplyMode];
[mutableDict setValue:self.versionCode forKey:kConfigDataVersionCode];
[mutableDict setValue:self.tgUrl forKey:kConfigDataTgUrl];
[mutableDict setValue:[NSNumber numberWithInteger:self.noticeApplyMode] forKey:kConfigDataNoticeApplyMode];
[mutableDict setValue:self.wsUrl forKey:kConfigDataWsUrl];
[mutableDict setValue:self.apkUrl forKey:kConfigDataApkUrl];
[mutableDict setValue:self.downloadUrl forKey:kConfigDataDownloadUrl];
[mutableDict setValue:[NSNumber numberWithInteger:self.isUse] forKey:kConfigDataIsUse];
return [NSDictionary dictionaryWithDictionary:mutableDict];
}
- (NSString *)description {
return [NSString stringWithFormat:@"%@", [self dictionaryRepresentation]];
}
#pragma mark - Helper Method
- (id)objectOrNilForKey:(id)aKey fromDictionary:(NSDictionary *)dict {
id object = [dict objectForKey:aKey];
return [object isEqual:[NSNull null]] ? nil : object;
}
#pragma mark - NSCoding Methods
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super init];
self.description = [aDecoder decodeObjectForKey:kConfigDataDescription];
self.iosVersionCode = [aDecoder decodeObjectForKey:kConfigDataIosVersionCode];
self.forceUpdate = [aDecoder decodeIntegerForKey:kConfigDataForceUpdate];
self.fbUrl = [aDecoder decodeObjectForKey:kConfigDataFbUrl];
self.credentials = [aDecoder decodeObjectForKey:kConfigDataCredentials];
self.url = [aDecoder decodeObjectForKey:kConfigDataUrl];
self.inviteFee = [aDecoder decodeDoubleForKey:kConfigDataInviteFee];
self.userId = [aDecoder decodeIntegerForKey:kConfigDataUserId];
self.contactApplyMode = [aDecoder decodeIntegerForKey:kConfigDataContactApplyMode];
self.versionCode = [aDecoder decodeObjectForKey:kConfigDataVersionCode];
self.tgUrl = [aDecoder decodeObjectForKey:kConfigDataTgUrl];
self.noticeApplyMode = [aDecoder decodeIntegerForKey:kConfigDataNoticeApplyMode];
self.wsUrl = [aDecoder decodeObjectForKey:kConfigDataWsUrl];
self.apkUrl = [aDecoder decodeObjectForKey:kConfigDataApkUrl];
self.downloadUrl = [aDecoder decodeObjectForKey:kConfigDataDownloadUrl];
self.isUse = [aDecoder decodeIntegerForKey:kConfigDataIsUse];
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:_description forKey:kConfigDataDescription];
[aCoder encodeObject:_iosVersionCode forKey:kConfigDataIosVersionCode];
[aCoder encodeInteger:_forceUpdate forKey:kConfigDataForceUpdate];
[aCoder encodeObject:_fbUrl forKey:kConfigDataFbUrl];
[aCoder encodeObject:_credentials forKey:kConfigDataCredentials];
[aCoder encodeObject:_url forKey:kConfigDataUrl];
[aCoder encodeDouble:_inviteFee forKey:kConfigDataInviteFee];
[aCoder encodeInteger:_userId forKey:kConfigDataUserId];
[aCoder encodeInteger:_contactApplyMode forKey:kConfigDataContactApplyMode];
[aCoder encodeObject:_versionCode forKey:kConfigDataVersionCode];
[aCoder encodeObject:_tgUrl forKey:kConfigDataTgUrl];
[aCoder encodeInteger:_noticeApplyMode forKey:kConfigDataNoticeApplyMode];
[aCoder encodeObject:_wsUrl forKey:kConfigDataWsUrl];
[aCoder encodeObject:_apkUrl forKey:kConfigDataApkUrl];
[aCoder encodeObject:_downloadUrl forKey:kConfigDataDownloadUrl];
[aCoder encodeInteger:_isUse forKey:kConfigDataIsUse];
}
- (id)copyWithZone:(NSZone *)zone {
ConfigData *copy = [[ConfigData alloc] init];
if (copy) {
copy.description = [self.description copyWithZone:zone];
copy.iosVersionCode = [self.iosVersionCode copyWithZone:zone];
copy.forceUpdate = self.forceUpdate;
copy.fbUrl = [self.fbUrl copyWithZone:zone];
copy.credentials = [self.credentials copyWithZone:zone];
copy.url = [self.url copyWithZone:zone];
copy.inviteFee = self.inviteFee;
copy.userId = self.userId;
copy.contactApplyMode = self.contactApplyMode;
copy.versionCode = [self.versionCode copyWithZone:zone];
copy.tgUrl = [self.tgUrl copyWithZone:zone];
copy.noticeApplyMode = self.noticeApplyMode;
copy.wsUrl = [self.wsUrl copyWithZone:zone];
copy.apkUrl = [self.apkUrl copyWithZone:zone];
copy.downloadUrl = [self.downloadUrl copyWithZone:zone];
copy.isUse = self.isUse;
}
return copy;
}
@end

View File

@@ -0,0 +1,19 @@
//
// ConfigDataModel.h
//
// Created by Yao on 2024/12/19
// Copyright (c) 2024 zL. All rights reserved.
//
#import <Foundation/Foundation.h>
@class ConfigData;
@interface ConfigDataModel : NSObject <NSCoding, NSCopying>
@property (nonatomic, strong) ConfigData *data;
@property (nonatomic, assign) NSInteger code;
+ (instancetype)modelObjectWithDictionary:(NSDictionary *)dict;
- (instancetype)initWithDictionary:(NSDictionary *)dict;
- (NSDictionary *)dictionaryRepresentation;
@end

View File

@@ -0,0 +1,81 @@
//
// ConfigDataModel.m
//
// Created by Yao on 2024/12/19
// Copyright (c) 2024 zL. All rights reserved.
//
#import "ConfigDataModel.h"
#import "ConfigData.h"
NSString *const kConfigDataModelData = @"data";
NSString *const kConfigDataModelCode = @"code";
@interface ConfigDataModel ()
- (id)objectOrNilForKey:(id)aKey fromDictionary:(NSDictionary *)dict;
@end
@implementation ConfigDataModel
@synthesize data = _data;
@synthesize code = _code;
+ (instancetype)modelObjectWithDictionary:(NSDictionary *)dict {
return [[self alloc] initWithDictionary:dict];
}
- (instancetype)initWithDictionary:(NSDictionary *)dict {
self = [super init];
// This check serves to make sure that a non-NSDictionary object
// passed into the model class doesn't break the parsing.
if (self && [dict isKindOfClass:[NSDictionary class]]) {
self.data = [ConfigData modelObjectWithDictionary:[dict objectForKey:kConfigDataModelData]];
self.code = [[self objectOrNilForKey:kConfigDataModelCode fromDictionary:dict] intValue];
}
return self;
}
- (NSDictionary *)dictionaryRepresentation {
NSMutableDictionary *mutableDict = [NSMutableDictionary dictionary];
[mutableDict setValue:[self.data dictionaryRepresentation] forKey:kConfigDataModelData];
[mutableDict setValue:[NSNumber numberWithInteger:self.code] forKey:kConfigDataModelCode];
return [NSDictionary dictionaryWithDictionary:mutableDict];
}
- (NSString *)description {
return [NSString stringWithFormat:@"%@", [self dictionaryRepresentation]];
}
#pragma mark - Helper Method
- (id)objectOrNilForKey:(id)aKey fromDictionary:(NSDictionary *)dict {
id object = [dict objectForKey:aKey];
return [object isEqual:[NSNull null]] ? nil : object;
}
#pragma mark - NSCoding Methods
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super init];
self.data = [aDecoder decodeObjectForKey:kConfigDataModelData];
self.code = [aDecoder decodeIntegerForKey:kConfigDataModelCode];
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:_data forKey:kConfigDataModelData];
[aCoder encodeInteger:_code forKey:kConfigDataModelCode];
}
- (id)copyWithZone:(NSZone *)zone {
ConfigDataModel *copy = [[ConfigDataModel alloc] init];
if (copy) {
copy.data = [self.data copyWithZone:zone];
copy.code = self.code;
}
return copy;
}
@end

View File

@@ -0,0 +1,430 @@
//
// NSObject+YYModel.h
// YYKit <https://github.com/ibireme/YYKit>
//
// Created by ibireme on 15/5/10.
// Copyright (c) 2015 ibireme.
//
// This source code is licensed under the MIT-style license found in the
// LICENSE file in the root directory of this source tree.
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/**
Provide some data-model method:
* Convert json to any object, or convert any object to json.
* Set object properties with a key-value dictionary (like KVC).
* Implementations of `NSCoding`, `NSCopying`, `-hash` and `-isEqual:`.
See `YYModel` protocol for custom methods.
Sample Code:
********************** json convertor *********************
@interface YYAuthor : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) NSDate *birthday;
@end
@implementation YYAuthor
@end
@interface YYBook : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSUInteger pages;
@property (nonatomic, strong) YYAuthor *author;
@end
@implementation YYBook
@end
int main() {
// create model from json
YYBook *book = [YYBook modelWithJSON:@"{\"name\": \"Harry Potter\", \"pages\": 256, \"author\": {\"name\": \"J.K.Rowling\", \"birthday\": \"1965-07-31\" }}"];
// convert model to json
NSString *json = [book modelToJSONString];
// {"author":{"name":"J.K.Rowling","birthday":"1965-07-31T00:00:00+0000"},"name":"Harry Potter","pages":256}
}
********************** Coding/Copying/hash/equal *********************
@interface YYShadow :NSObject <NSCoding, NSCopying>
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) CGSize size;
@end
@implementation YYShadow
- (void)encodeWithCoder:(NSCoder *)aCoder { [self modelEncodeWithCoder:aCoder]; }
- (id)initWithCoder:(NSCoder *)aDecoder { self = [super init]; return [self modelInitWithCoder:aDecoder]; }
- (id)copyWithZone:(NSZone *)zone { return [self modelCopy]; }
- (NSUInteger)hash { return [self modelHash]; }
- (BOOL)isEqual:(id)object { return [self modelIsEqual:object]; }
@end
*/
@interface NSObject (YYModel)
/**
Creates and returns a new instance of the receiver from a json.
This method is thread-safe.
@param json A json object in `NSDictionary`, `NSString` or `NSData`.
@return A new instance created from the json, or nil if an error occurs.
*/
+ (nullable instancetype)modelWithJSON:(id)json;
/**
Creates and returns a new instance of the receiver from a key-value dictionary.
This method is thread-safe.
@param dictionary A key-value dictionary mapped to the instance's properties.
Any invalid key-value pair in dictionary will be ignored.
@return A new instance created from the dictionary, or nil if an error occurs.
@discussion The key in `dictionary` will mapped to the reciever's property name,
and the value will set to the property. If the value's type does not match the
property, this method will try to convert the value based on these rules:
`NSString` or `NSNumber` -> c number, such as BOOL, int, long, float, NSUInteger...
`NSString` -> NSDate, parsed with format "yyyy-MM-dd'T'HH:mm:ssZ", "yyyy-MM-dd HH:mm:ss" or "yyyy-MM-dd".
`NSString` -> NSURL.
`NSValue` -> struct or union, such as CGRect, CGSize, ...
`NSString` -> SEL, Class.
*/
+ (nullable instancetype)modelWithDictionary:(NSDictionary *)dictionary;
/**
Set the receiver's properties with a json object.
@discussion Any invalid data in json will be ignored.
@param json A json object of `NSDictionary`, `NSString` or `NSData`, mapped to the
receiver's properties.
@return Whether succeed.
*/
- (BOOL)modelSetWithJSON:(id)json;
/**
Set the receiver's properties with a key-value dictionary.
@param dic A key-value dictionary mapped to the receiver's properties.
Any invalid key-value pair in dictionary will be ignored.
@discussion The key in `dictionary` will mapped to the reciever's property name,
and the value will set to the property. If the value's type doesn't match the
property, this method will try to convert the value based on these rules:
`NSString`, `NSNumber` -> c number, such as BOOL, int, long, float, NSUInteger...
`NSString` -> NSDate, parsed with format "yyyy-MM-dd'T'HH:mm:ssZ", "yyyy-MM-dd HH:mm:ss" or "yyyy-MM-dd".
`NSString` -> NSURL.
`NSValue` -> struct or union, such as CGRect, CGSize, ...
`NSString` -> SEL, Class.
@return Whether succeed.
*/
- (BOOL)modelSetWithDictionary:(NSDictionary *)dic;
/**
Generate a json object from the receiver's properties.
@return A json object in `NSDictionary` or `NSArray`, or nil if an error occurs.
See [NSJSONSerialization isValidJSONObject] for more information.
@discussion Any of the invalid property is ignored.
If the reciver is `NSArray`, `NSDictionary` or `NSSet`, it just convert
the inner object to json object.
*/
- (nullable id)modelToJSONObject;
/**
Generate a json string's data from the receiver's properties.
@return A json string's data, or nil if an error occurs.
@discussion Any of the invalid property is ignored.
If the reciver is `NSArray`, `NSDictionary` or `NSSet`, it will also convert the
inner object to json string.
*/
- (nullable NSData *)modelToJSONData;
/**
Generate a json string from the receiver's properties.
@return A json string, or nil if an error occurs.
@discussion Any of the invalid property is ignored.
If the reciver is `NSArray`, `NSDictionary` or `NSSet`, it will also convert the
inner object to json string.
*/
- (nullable NSString *)modelToJSONString;
/**
Copy a instance with the receiver's properties.
@return A copied instance, or nil if an error occurs.
*/
- (nullable id)modelCopy;
/**
Encode the receiver's properties to a coder.
@param aCoder An archiver object.
*/
- (void)modelEncodeWithCoder:(NSCoder *)aCoder;
/**
Decode the receiver's properties from a decoder.
@param aDecoder An archiver object.
@return self
*/
- (id)modelInitWithCoder:(NSCoder *)aDecoder;
/**
Get a hash code with the receiver's properties.
@return Hash code.
*/
- (NSUInteger)modelHash;
/**
Compares the receiver with another object for equality, based on properties.
@param model Another object.
@return `YES` if the reciever is equal to the object, otherwise `NO`.
*/
- (BOOL)modelIsEqual:(id)model;
/**
Description method for debugging purposes based on properties.
@return A string that describes the contents of the receiver.
*/
- (NSString *)modelDescription;
@end
/**
Provide some data-model method for NSArray.
*/
@interface NSArray (YYModel)
/**
Creates and returns an array from a json-array.
This method is thread-safe.
@param cls The instance's class in array.
@param json A json array of `NSArray`, `NSString` or `NSData`.
Example: [{"name","Mary"},{name:"Joe"}]
@return A array, or nil if an error occurs.
*/
+ (nullable NSArray *)modelArrayWithClass:(Class)cls json:(id)json;
@end
/**
Provide some data-model method for NSDictionary.
*/
@interface NSDictionary (YYModel)
/**
Creates and returns a dictionary from a json.
This method is thread-safe.
@param cls The value instance's class in dictionary.
@param json A json dictionary of `NSDictionary`, `NSString` or `NSData`.
Example: {"user1":{"name","Mary"}, "user2": {name:"Joe"}}
@return A dictionary, or nil if an error occurs.
*/
+ (nullable NSDictionary *)modelDictionaryWithClass:(Class)cls json:(id)json;
@end
/**
If the default model transform does not fit to your model class, implement one or
more method in this protocol to change the default key-value transform process.
There's no need to add '<YYModel>' to your class header.
*/
@protocol YYModel <NSObject>
@optional
/**
Custom property mapper.
@discussion If the key in JSON/Dictionary does not match to the model's property name,
implements this method and returns the additional mapper.
Example:
json:
{
"n":"Harry Pottery",
"p": 256,
"ext" : {
"desc" : "A book written by J.K.Rowling."
},
"ID" : 100010
}
model:
@interface YYBook : NSObject
@property NSString *name;
@property NSInteger page;
@property NSString *desc;
@property NSString *bookID;
@end
@implementation YYBook
+ (NSDictionary *)modelCustomPropertyMapper {
return @{@"name" : @"n",
@"page" : @"p",
@"desc" : @"ext.desc",
@"bookID": @[@"id", @"ID", @"book_id"]};
}
@end
@return A custom mapper for properties.
*/
+ (nullable NSDictionary<NSString *, id> *)modelCustomPropertyMapper;
/**
The generic class mapper for container properties.
@discussion If the property is a container object, such as NSArray/NSSet/NSDictionary,
implements this method and returns a property->class mapper, tells which kind of
object will be add to the array/set/dictionary.
Example:
@class YYShadow, YYBorder, YYAttachment;
@interface YYAttributes
@property NSString *name;
@property NSArray *shadows;
@property NSSet *borders;
@property NSDictionary *attachments;
@end
@implementation YYAttributes
+ (NSDictionary *)modelContainerPropertyGenericClass {
return @{@"shadows" : [YYShadow class],
@"borders" : YYBorder.class,
@"attachments" : @"YYAttachment" };
}
@end
@return A class mapper.
*/
+ (nullable NSDictionary<NSString *, id> *)modelContainerPropertyGenericClass;
/**
If you need to create instances of different classes during json->object transform,
use the method to choose custom class based on dictionary data.
@discussion If the model implements this method, it will be called to determine resulting class
during `+modelWithJSON:`, `+modelWithDictionary:`, conveting object of properties of parent objects
(both singular and containers via `+modelContainerPropertyGenericClass`).
Example:
@class YYCircle, YYRectangle, YYLine;
@implementation YYShape
+ (Class)modelCustomClassForDictionary:(NSDictionary*)dictionary {
if (dictionary[@"radius"] != nil) {
return [YYCircle class];
} else if (dictionary[@"width"] != nil) {
return [YYRectangle class];
} else if (dictionary[@"y2"] != nil) {
return [YYLine class];
} else {
return [self class];
}
}
@end
@param dictionary The json/kv dictionary.
@return Class to create from this dictionary, `nil` to use current class.
*/
+ (nullable Class)modelCustomClassForDictionary:(NSDictionary *)dictionary;
/**
All the properties in blacklist will be ignored in model transform process.
Returns nil to ignore this feature.
@return An array of property's name.
*/
+ (nullable NSArray<NSString *> *)modelPropertyBlacklist;
/**
If a property is not in the whitelist, it will be ignored in model transform process.
Returns nil to ignore this feature.
@return An array of property's name.
*/
+ (nullable NSArray<NSString *> *)modelPropertyWhitelist;
/**
This method's behavior is similar to `- (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic;`,
but be called before the model transform.
@discussion If the model implements this method, it will be called before
`+modelWithJSON:`, `+modelWithDictionary:`, `-modelSetWithJSON:` and `-modelSetWithDictionary:`.
If this method returns nil, the transform process will ignore this model.
@param dic The json/kv dictionary.
@return Returns the modified dictionary, or nil to ignore this model.
*/
- (NSDictionary *)modelCustomWillTransformFromDictionary:(NSDictionary *)dic;
/**
If the default json-to-model transform does not fit to your model object, implement
this method to do additional process. You can also use this method to validate the
model's properties.
@discussion If the model implements this method, it will be called at the end of
`+modelWithJSON:`, `+modelWithDictionary:`, `-modelSetWithJSON:` and `-modelSetWithDictionary:`.
If this method returns NO, the transform process will ignore this model.
@param dic The json/kv dictionary.
@return Returns YES if the model is valid, or NO to ignore this model.
*/
- (BOOL)modelCustomTransformFromDictionary:(NSDictionary *)dic;
/**
If the default model-to-json transform does not fit to your model class, implement
this method to do additional process. You can also use this method to validate the
json dictionary.
@discussion If the model implements this method, it will be called at the end of
`-modelToJSONObject` and `-modelToJSONString`.
If this method returns NO, the transform process will ignore this json dictionary.
@param dic The json dictionary.
@return Returns YES if the model is valid, or NO to ignore this model.
*/
- (BOOL)modelCustomTransformToDictionary:(NSMutableDictionary *)dic;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,1838 @@
//
// NSObject+YYModel.m
// YYKit <https://github.com/ibireme/YYKit>
//
// Created by ibireme on 15/5/10.
// Copyright (c) 2015 ibireme.
//
// This source code is licensed under the MIT-style license found in the
// LICENSE file in the root directory of this source tree.
//
#import "NSObject+YYModel.h"
#import "YYClassInfo.h"
#import <objc/message.h>
#define force_inline __inline__ __attribute__((always_inline))
/// Foundation Class Type
typedef NS_ENUM (NSUInteger, YYEncodingNSType) {
YYEncodingTypeNSUnknown = 0,
YYEncodingTypeNSString,
YYEncodingTypeNSMutableString,
YYEncodingTypeNSValue,
YYEncodingTypeNSNumber,
YYEncodingTypeNSDecimalNumber,
YYEncodingTypeNSData,
YYEncodingTypeNSMutableData,
YYEncodingTypeNSDate,
YYEncodingTypeNSURL,
YYEncodingTypeNSArray,
YYEncodingTypeNSMutableArray,
YYEncodingTypeNSDictionary,
YYEncodingTypeNSMutableDictionary,
YYEncodingTypeNSSet,
YYEncodingTypeNSMutableSet,
};
/// Get the Foundation class type from property info.
static force_inline YYEncodingNSType YYClassGetNSType(Class cls) {
if (!cls) return YYEncodingTypeNSUnknown;
if ([cls isSubclassOfClass:[NSMutableString class]]) return YYEncodingTypeNSMutableString;
if ([cls isSubclassOfClass:[NSString class]]) return YYEncodingTypeNSString;
if ([cls isSubclassOfClass:[NSDecimalNumber class]]) return YYEncodingTypeNSDecimalNumber;
if ([cls isSubclassOfClass:[NSNumber class]]) return YYEncodingTypeNSNumber;
if ([cls isSubclassOfClass:[NSValue class]]) return YYEncodingTypeNSValue;
if ([cls isSubclassOfClass:[NSMutableData class]]) return YYEncodingTypeNSMutableData;
if ([cls isSubclassOfClass:[NSData class]]) return YYEncodingTypeNSData;
if ([cls isSubclassOfClass:[NSDate class]]) return YYEncodingTypeNSDate;
if ([cls isSubclassOfClass:[NSURL class]]) return YYEncodingTypeNSURL;
if ([cls isSubclassOfClass:[NSMutableArray class]]) return YYEncodingTypeNSMutableArray;
if ([cls isSubclassOfClass:[NSArray class]]) return YYEncodingTypeNSArray;
if ([cls isSubclassOfClass:[NSMutableDictionary class]]) return YYEncodingTypeNSMutableDictionary;
if ([cls isSubclassOfClass:[NSDictionary class]]) return YYEncodingTypeNSDictionary;
if ([cls isSubclassOfClass:[NSMutableSet class]]) return YYEncodingTypeNSMutableSet;
if ([cls isSubclassOfClass:[NSSet class]]) return YYEncodingTypeNSSet;
return YYEncodingTypeNSUnknown;
}
/// Whether the type is c number.
static force_inline BOOL YYEncodingTypeIsCNumber(YYEncodingType type) {
switch (type & YYEncodingTypeMask) {
case YYEncodingTypeBool:
case YYEncodingTypeInt8:
case YYEncodingTypeUInt8:
case YYEncodingTypeInt16:
case YYEncodingTypeUInt16:
case YYEncodingTypeInt32:
case YYEncodingTypeUInt32:
case YYEncodingTypeInt64:
case YYEncodingTypeUInt64:
case YYEncodingTypeFloat:
case YYEncodingTypeDouble:
case YYEncodingTypeLongDouble: return YES;
default: return NO;
}
}
/// Parse a number value from 'id'.
static force_inline NSNumber *YYNSNumberCreateFromID(__unsafe_unretained id value) {
static NSCharacterSet *dot;
static NSDictionary *dic;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
dot = [NSCharacterSet characterSetWithRange:NSMakeRange('.', 1)];
dic = @{@"TRUE" : @(YES),
@"True" : @(YES),
@"true" : @(YES),
@"FALSE" : @(NO),
@"False" : @(NO),
@"false" : @(NO),
@"YES" : @(YES),
@"Yes" : @(YES),
@"yes" : @(YES),
@"NO" : @(NO),
@"No" : @(NO),
@"no" : @(NO),
@"NIL" : (id)kCFNull,
@"Nil" : (id)kCFNull,
@"nil" : (id)kCFNull,
@"NULL" : (id)kCFNull,
@"Null" : (id)kCFNull,
@"null" : (id)kCFNull,
@"(NULL)" : (id)kCFNull,
@"(Null)" : (id)kCFNull,
@"(null)" : (id)kCFNull,
@"<NULL>" : (id)kCFNull,
@"<Null>" : (id)kCFNull,
@"<null>" : (id)kCFNull};
});
if (!value || value == (id)kCFNull) return nil;
if ([value isKindOfClass:[NSNumber class]]) return value;
if ([value isKindOfClass:[NSString class]]) {
NSNumber *num = dic[value];
if (num) {
if (num == (id)kCFNull) return nil;
return num;
}
if ([(NSString *)value rangeOfCharacterFromSet:dot].location != NSNotFound) {
const char *cstring = ((NSString *)value).UTF8String;
if (!cstring) return nil;
double num = atof(cstring);
if (isnan(num) || isinf(num)) return nil;
return @(num);
} else {
const char *cstring = ((NSString *)value).UTF8String;
if (!cstring) return nil;
return @(atoll(cstring));
}
}
return nil;
}
/// Parse string to date.
static force_inline NSDate *YYNSDateFromString(__unsafe_unretained NSString *string) {
typedef NSDate* (^YYNSDateParseBlock)(NSString *string);
#define kParserNum 34
static YYNSDateParseBlock blocks[kParserNum + 1] = {0};
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
{
/*
2014-01-20 // Google
*/
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
formatter.dateFormat = @"yyyy-MM-dd";
blocks[10] = ^(NSString *string) { return [formatter dateFromString:string]; };
}
{
/*
2014-01-20 12:24:48
2014-01-20T12:24:48 // Google
2014-01-20 12:24:48.000
2014-01-20T12:24:48.000
*/
NSDateFormatter *formatter1 = [[NSDateFormatter alloc] init];
formatter1.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter1.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
formatter1.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss";
NSDateFormatter *formatter2 = [[NSDateFormatter alloc] init];
formatter2.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter2.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
formatter2.dateFormat = @"yyyy-MM-dd HH:mm:ss";
NSDateFormatter *formatter3 = [[NSDateFormatter alloc] init];
formatter3.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter3.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
formatter3.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSS";
NSDateFormatter *formatter4 = [[NSDateFormatter alloc] init];
formatter4.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter4.timeZone = [NSTimeZone timeZoneForSecondsFromGMT:0];
formatter4.dateFormat = @"yyyy-MM-dd HH:mm:ss.SSS";
blocks[19] = ^(NSString *string) {
if ([string characterAtIndex:10] == 'T') {
return [formatter1 dateFromString:string];
} else {
return [formatter2 dateFromString:string];
}
};
blocks[23] = ^(NSString *string) {
if ([string characterAtIndex:10] == 'T') {
return [formatter3 dateFromString:string];
} else {
return [formatter4 dateFromString:string];
}
};
}
{
/*
2014-01-20T12:24:48Z // Github, Apple
2014-01-20T12:24:48+0800 // Facebook
2014-01-20T12:24:48+12:00 // Google
2014-01-20T12:24:48.000Z
2014-01-20T12:24:48.000+0800
2014-01-20T12:24:48.000+12:00
*/
NSDateFormatter *formatter = [NSDateFormatter new];
formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ";
NSDateFormatter *formatter2 = [NSDateFormatter new];
formatter2.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter2.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSSZ";
blocks[20] = ^(NSString *string) { return [formatter dateFromString:string]; };
blocks[24] = ^(NSString *string) { return [formatter dateFromString:string]?: [formatter2 dateFromString:string]; };
blocks[25] = ^(NSString *string) { return [formatter dateFromString:string]; };
blocks[28] = ^(NSString *string) { return [formatter2 dateFromString:string]; };
blocks[29] = ^(NSString *string) { return [formatter2 dateFromString:string]; };
}
{
/*
Fri Sep 04 00:12:21 +0800 2015 // Weibo, Twitter
Fri Sep 04 00:12:21.000 +0800 2015
*/
NSDateFormatter *formatter = [NSDateFormatter new];
formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter.dateFormat = @"EEE MMM dd HH:mm:ss Z yyyy";
NSDateFormatter *formatter2 = [NSDateFormatter new];
formatter2.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter2.dateFormat = @"EEE MMM dd HH:mm:ss.SSS Z yyyy";
blocks[30] = ^(NSString *string) { return [formatter dateFromString:string]; };
blocks[34] = ^(NSString *string) { return [formatter2 dateFromString:string]; };
}
});
if (!string) return nil;
if (string.length > kParserNum) return nil;
YYNSDateParseBlock parser = blocks[string.length];
if (!parser) return nil;
return parser(string);
#undef kParserNum
}
/// Get the 'NSBlock' class.
static force_inline Class YYNSBlockClass() {
static Class cls;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
void (^block)(void) = ^{};
cls = ((NSObject *)block).class;
while (class_getSuperclass(cls) != [NSObject class]) {
cls = class_getSuperclass(cls);
}
});
return cls; // current is "NSBlock"
}
/**
Get the ISO date formatter.
ISO8601 format example:
2010-07-09T16:13:30+12:00
2011-01-11T11:11:11+0000
2011-01-26T19:06:43Z
length: 20/24/25
*/
static force_inline NSDateFormatter *YYISODateFormatter() {
static NSDateFormatter *formatter = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
formatter = [[NSDateFormatter alloc] init];
formatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:@"en_US_POSIX"];
formatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ssZ";
});
return formatter;
}
/// Get the value with key paths from dictionary
/// The dic should be NSDictionary, and the keyPath should not be nil.
static force_inline id YYValueForKeyPath(__unsafe_unretained NSDictionary *dic, __unsafe_unretained NSArray *keyPaths) {
id value = nil;
for (NSUInteger i = 0, max = keyPaths.count; i < max; i++) {
value = dic[keyPaths[i]];
if (i + 1 < max) {
if ([value isKindOfClass:[NSDictionary class]]) {
dic = value;
} else {
return nil;
}
}
}
return value;
}
/// Get the value with multi key (or key path) from dictionary
/// The dic should be NSDictionary
static force_inline id YYValueForMultiKeys(__unsafe_unretained NSDictionary *dic, __unsafe_unretained NSArray *multiKeys) {
id value = nil;
for (NSString *key in multiKeys) {
if ([key isKindOfClass:[NSString class]]) {
value = dic[key];
if (value) break;
} else {
value = YYValueForKeyPath(dic, (NSArray *)key);
if (value) break;
}
}
return value;
}
/// A property info in object model.
@interface _YYModelPropertyMeta : NSObject {
@package
NSString *_name; ///< property's name
YYEncodingType _type; ///< property's type
YYEncodingNSType _nsType; ///< property's Foundation type
BOOL _isCNumber; ///< is c number type
Class _cls; ///< property's class, or nil
Class _genericCls; ///< container's generic class, or nil if threr's no generic class
SEL _getter; ///< getter, or nil if the instances cannot respond
SEL _setter; ///< setter, or nil if the instances cannot respond
BOOL _isKVCCompatible; ///< YES if it can access with key-value coding
BOOL _isStructAvailableForKeyedArchiver; ///< YES if the struct can encoded with keyed archiver/unarchiver
BOOL _hasCustomClassFromDictionary; ///< class/generic class implements +modelCustomClassForDictionary:
/*
property->key: _mappedToKey:key _mappedToKeyPath:nil _mappedToKeyArray:nil
property->keyPath: _mappedToKey:keyPath _mappedToKeyPath:keyPath(array) _mappedToKeyArray:nil
property->keys: _mappedToKey:keys[0] _mappedToKeyPath:nil/keyPath _mappedToKeyArray:keys(array)
*/
NSString *_mappedToKey; ///< the key mapped to
NSArray *_mappedToKeyPath; ///< the key path mapped to (nil if the name is not key path)
NSArray *_mappedToKeyArray; ///< the key(NSString) or keyPath(NSArray) array (nil if not mapped to multiple keys)
YYClassPropertyInfo *_info; ///< property's info
_YYModelPropertyMeta *_next; ///< next meta if there are multiple properties mapped to the same key.
}
@end
@implementation _YYModelPropertyMeta
+ (instancetype)metaWithClassInfo:(YYClassInfo *)classInfo propertyInfo:(YYClassPropertyInfo *)propertyInfo generic:(Class)generic {
// support pseudo generic class with protocol name
if (!generic && propertyInfo.protocols) {
for (NSString *protocol in propertyInfo.protocols) {
Class cls = objc_getClass(protocol.UTF8String);
if (cls) {
generic = cls;
break;
}
}
}
_YYModelPropertyMeta *meta = [self new];
meta->_name = propertyInfo.name;
meta->_type = propertyInfo.type;
meta->_info = propertyInfo;
meta->_genericCls = generic;
if ((meta->_type & YYEncodingTypeMask) == YYEncodingTypeObject) {
meta->_nsType = YYClassGetNSType(propertyInfo.cls);
} else {
meta->_isCNumber = YYEncodingTypeIsCNumber(meta->_type);
}
if ((meta->_type & YYEncodingTypeMask) == YYEncodingTypeStruct) {
/*
It seems that NSKeyedUnarchiver cannot decode NSValue except these structs:
*/
static NSSet *types = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSMutableSet *set = [NSMutableSet new];
// 32 bit
[set addObject:@"{CGSize=ff}"];
[set addObject:@"{CGPoint=ff}"];
[set addObject:@"{CGRect={CGPoint=ff}{CGSize=ff}}"];
[set addObject:@"{CGAffineTransform=ffffff}"];
[set addObject:@"{UIEdgeInsets=ffff}"];
[set addObject:@"{UIOffset=ff}"];
// 64 bit
[set addObject:@"{CGSize=dd}"];
[set addObject:@"{CGPoint=dd}"];
[set addObject:@"{CGRect={CGPoint=dd}{CGSize=dd}}"];
[set addObject:@"{CGAffineTransform=dddddd}"];
[set addObject:@"{UIEdgeInsets=dddd}"];
[set addObject:@"{UIOffset=dd}"];
types = set;
});
if ([types containsObject:propertyInfo.typeEncoding]) {
meta->_isStructAvailableForKeyedArchiver = YES;
}
}
meta->_cls = propertyInfo.cls;
if (generic) {
meta->_hasCustomClassFromDictionary = [generic respondsToSelector:@selector(modelCustomClassForDictionary:)];
} else if (meta->_cls && meta->_nsType == YYEncodingTypeNSUnknown) {
meta->_hasCustomClassFromDictionary = [meta->_cls respondsToSelector:@selector(modelCustomClassForDictionary:)];
}
if (propertyInfo.getter) {
if ([classInfo.cls instancesRespondToSelector:propertyInfo.getter]) {
meta->_getter = propertyInfo.getter;
}
}
if (propertyInfo.setter) {
if ([classInfo.cls instancesRespondToSelector:propertyInfo.setter]) {
meta->_setter = propertyInfo.setter;
}
}
if (meta->_getter && meta->_setter) {
/*
KVC invalid type:
long double
pointer (such as SEL/CoreFoundation object)
*/
switch (meta->_type & YYEncodingTypeMask) {
case YYEncodingTypeBool:
case YYEncodingTypeInt8:
case YYEncodingTypeUInt8:
case YYEncodingTypeInt16:
case YYEncodingTypeUInt16:
case YYEncodingTypeInt32:
case YYEncodingTypeUInt32:
case YYEncodingTypeInt64:
case YYEncodingTypeUInt64:
case YYEncodingTypeFloat:
case YYEncodingTypeDouble:
case YYEncodingTypeObject:
case YYEncodingTypeClass:
case YYEncodingTypeBlock:
case YYEncodingTypeStruct:
case YYEncodingTypeUnion: {
meta->_isKVCCompatible = YES;
} break;
default: break;
}
}
return meta;
}
@end
/// A class info in object model.
@interface _YYModelMeta : NSObject {
@package
YYClassInfo *_classInfo;
/// Key:mapped key and key path, Value:_YYModelPropertyMeta.
NSDictionary *_mapper;
/// Array<_YYModelPropertyMeta>, all property meta of this model.
NSArray *_allPropertyMetas;
/// Array<_YYModelPropertyMeta>, property meta which is mapped to a key path.
NSArray *_keyPathPropertyMetas;
/// Array<_YYModelPropertyMeta>, property meta which is mapped to multi keys.
NSArray *_multiKeysPropertyMetas;
/// The number of mapped key (and key path), same to _mapper.count.
NSUInteger _keyMappedCount;
/// Model class type.
YYEncodingNSType _nsType;
BOOL _hasCustomWillTransformFromDictionary;
BOOL _hasCustomTransformFromDictionary;
BOOL _hasCustomTransformToDictionary;
BOOL _hasCustomClassFromDictionary;
}
@end
@implementation _YYModelMeta
- (instancetype)initWithClass:(Class)cls {
YYClassInfo *classInfo = [YYClassInfo classInfoWithClass:cls];
if (!classInfo) return nil;
self = [super init];
// Get black list
NSSet *blacklist = nil;
if ([cls respondsToSelector:@selector(modelPropertyBlacklist)]) {
NSArray *properties = [(id<YYModel>)cls modelPropertyBlacklist];
if (properties) {
blacklist = [NSSet setWithArray:properties];
}
}
// Get white list
NSSet *whitelist = nil;
if ([cls respondsToSelector:@selector(modelPropertyWhitelist)]) {
NSArray *properties = [(id<YYModel>)cls modelPropertyWhitelist];
if (properties) {
whitelist = [NSSet setWithArray:properties];
}
}
// Get container property's generic class
NSDictionary *genericMapper = nil;
if ([cls respondsToSelector:@selector(modelContainerPropertyGenericClass)]) {
genericMapper = [(id<YYModel>)cls modelContainerPropertyGenericClass];
if (genericMapper) {
NSMutableDictionary *tmp = [NSMutableDictionary new];
[genericMapper enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
if (![key isKindOfClass:[NSString class]]) return;
Class meta = object_getClass(obj);
if (!meta) return;
if (class_isMetaClass(meta)) {
tmp[key] = obj;
} else if ([obj isKindOfClass:[NSString class]]) {
Class cls = NSClassFromString(obj);
if (cls) {
tmp[key] = cls;
}
}
}];
genericMapper = tmp;
}
}
// Create all property metas.
NSMutableDictionary *allPropertyMetas = [NSMutableDictionary new];
YYClassInfo *curClassInfo = classInfo;
while (curClassInfo && curClassInfo.superCls != nil) { // recursive parse super class, but ignore root class (NSObject/NSProxy)
for (YYClassPropertyInfo *propertyInfo in curClassInfo.propertyInfos.allValues) {
if (!propertyInfo.name) continue;
if (blacklist && [blacklist containsObject:propertyInfo.name]) continue;
if (whitelist && ![whitelist containsObject:propertyInfo.name]) continue;
_YYModelPropertyMeta *meta = [_YYModelPropertyMeta metaWithClassInfo:classInfo
propertyInfo:propertyInfo
generic:genericMapper[propertyInfo.name]];
if (!meta || !meta->_name) continue;
if (!meta->_getter || !meta->_setter) continue;
if (allPropertyMetas[meta->_name]) continue;
allPropertyMetas[meta->_name] = meta;
}
curClassInfo = curClassInfo.superClassInfo;
}
if (allPropertyMetas.count) _allPropertyMetas = allPropertyMetas.allValues.copy;
// create mapper
NSMutableDictionary *mapper = [NSMutableDictionary new];
NSMutableArray *keyPathPropertyMetas = [NSMutableArray new];
NSMutableArray *multiKeysPropertyMetas = [NSMutableArray new];
if ([cls respondsToSelector:@selector(modelCustomPropertyMapper)]) {
NSDictionary *customMapper = [(id <YYModel>)cls modelCustomPropertyMapper];
[customMapper enumerateKeysAndObjectsUsingBlock:^(NSString *propertyName, NSString *mappedToKey, BOOL *stop) {
_YYModelPropertyMeta *propertyMeta = allPropertyMetas[propertyName];
if (!propertyMeta) return;
[allPropertyMetas removeObjectForKey:propertyName];
if ([mappedToKey isKindOfClass:[NSString class]]) {
if (mappedToKey.length == 0) return;
propertyMeta->_mappedToKey = mappedToKey;
NSArray *keyPath = [mappedToKey componentsSeparatedByString:@"."];
for (NSString *onePath in keyPath) {
if (onePath.length == 0) {
NSMutableArray *tmp = keyPath.mutableCopy;
[tmp removeObject:@""];
keyPath = tmp;
break;
}
}
if (keyPath.count > 1) {
propertyMeta->_mappedToKeyPath = keyPath;
[keyPathPropertyMetas addObject:propertyMeta];
}
propertyMeta->_next = mapper[mappedToKey] ?: nil;
mapper[mappedToKey] = propertyMeta;
} else if ([mappedToKey isKindOfClass:[NSArray class]]) {
NSMutableArray *mappedToKeyArray = [NSMutableArray new];
for (NSString *oneKey in ((NSArray *)mappedToKey)) {
if (![oneKey isKindOfClass:[NSString class]]) continue;
if (oneKey.length == 0) continue;
NSArray *keyPath = [oneKey componentsSeparatedByString:@"."];
if (keyPath.count > 1) {
[mappedToKeyArray addObject:keyPath];
} else {
[mappedToKeyArray addObject:oneKey];
}
if (!propertyMeta->_mappedToKey) {
propertyMeta->_mappedToKey = oneKey;
propertyMeta->_mappedToKeyPath = keyPath.count > 1 ? keyPath : nil;
}
}
if (!propertyMeta->_mappedToKey) return;
propertyMeta->_mappedToKeyArray = mappedToKeyArray;
[multiKeysPropertyMetas addObject:propertyMeta];
propertyMeta->_next = mapper[mappedToKey] ?: nil;
mapper[mappedToKey] = propertyMeta;
}
}];
}
[allPropertyMetas enumerateKeysAndObjectsUsingBlock:^(NSString *name, _YYModelPropertyMeta *propertyMeta, BOOL *stop) {
propertyMeta->_mappedToKey = name;
propertyMeta->_next = mapper[name] ?: nil;
mapper[name] = propertyMeta;
}];
if (mapper.count) _mapper = mapper;
if (keyPathPropertyMetas) _keyPathPropertyMetas = keyPathPropertyMetas;
if (multiKeysPropertyMetas) _multiKeysPropertyMetas = multiKeysPropertyMetas;
_classInfo = classInfo;
_keyMappedCount = _allPropertyMetas.count;
_nsType = YYClassGetNSType(cls);
_hasCustomWillTransformFromDictionary = ([cls instancesRespondToSelector:@selector(modelCustomWillTransformFromDictionary:)]);
_hasCustomTransformFromDictionary = ([cls instancesRespondToSelector:@selector(modelCustomTransformFromDictionary:)]);
_hasCustomTransformToDictionary = ([cls instancesRespondToSelector:@selector(modelCustomTransformToDictionary:)]);
_hasCustomClassFromDictionary = ([cls respondsToSelector:@selector(modelCustomClassForDictionary:)]);
return self;
}
/// Returns the cached model class meta
+ (instancetype)metaWithClass:(Class)cls {
if (!cls) return nil;
static CFMutableDictionaryRef cache;
static dispatch_once_t onceToken;
static dispatch_semaphore_t lock;
dispatch_once(&onceToken, ^{
cache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
lock = dispatch_semaphore_create(1);
});
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
_YYModelMeta *meta = CFDictionaryGetValue(cache, (__bridge const void *)(cls));
dispatch_semaphore_signal(lock);
if (!meta || meta->_classInfo.needUpdate) {
meta = [[_YYModelMeta alloc] initWithClass:cls];
if (meta) {
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
CFDictionarySetValue(cache, (__bridge const void *)(cls), (__bridge const void *)(meta));
dispatch_semaphore_signal(lock);
}
}
return meta;
}
@end
/**
Get number from property.
@discussion Caller should hold strong reference to the parameters before this function returns.
@param model Should not be nil.
@param meta Should not be nil, meta.isCNumber should be YES, meta.getter should not be nil.
@return A number object, or nil if failed.
*/
static force_inline NSNumber *ModelCreateNumberFromProperty(__unsafe_unretained id model,
__unsafe_unretained _YYModelPropertyMeta *meta) {
switch (meta->_type & YYEncodingTypeMask) {
case YYEncodingTypeBool: {
return @(((bool (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
}
case YYEncodingTypeInt8: {
return @(((int8_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
}
case YYEncodingTypeUInt8: {
return @(((uint8_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
}
case YYEncodingTypeInt16: {
return @(((int16_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
}
case YYEncodingTypeUInt16: {
return @(((uint16_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
}
case YYEncodingTypeInt32: {
return @(((int32_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
}
case YYEncodingTypeUInt32: {
return @(((uint32_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
}
case YYEncodingTypeInt64: {
return @(((int64_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
}
case YYEncodingTypeUInt64: {
return @(((uint64_t (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter));
}
case YYEncodingTypeFloat: {
float num = ((float (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter);
if (isnan(num) || isinf(num)) return nil;
return @(num);
}
case YYEncodingTypeDouble: {
double num = ((double (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter);
if (isnan(num) || isinf(num)) return nil;
return @(num);
}
case YYEncodingTypeLongDouble: {
double num = ((long double (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter);
if (isnan(num) || isinf(num)) return nil;
return @(num);
}
default: return nil;
}
}
/**
Set number to property.
@discussion Caller should hold strong reference to the parameters before this function returns.
@param model Should not be nil.
@param num Can be nil.
@param meta Should not be nil, meta.isCNumber should be YES, meta.setter should not be nil.
*/
static force_inline void ModelSetNumberToProperty(__unsafe_unretained id model,
__unsafe_unretained NSNumber *num,
__unsafe_unretained _YYModelPropertyMeta *meta) {
switch (meta->_type & YYEncodingTypeMask) {
case YYEncodingTypeBool: {
((void (*)(id, SEL, bool))(void *) objc_msgSend)((id)model, meta->_setter, num.boolValue);
} break;
case YYEncodingTypeInt8: {
((void (*)(id, SEL, int8_t))(void *) objc_msgSend)((id)model, meta->_setter, (int8_t)num.charValue);
} break;
case YYEncodingTypeUInt8: {
((void (*)(id, SEL, uint8_t))(void *) objc_msgSend)((id)model, meta->_setter, (uint8_t)num.unsignedCharValue);
} break;
case YYEncodingTypeInt16: {
((void (*)(id, SEL, int16_t))(void *) objc_msgSend)((id)model, meta->_setter, (int16_t)num.shortValue);
} break;
case YYEncodingTypeUInt16: {
((void (*)(id, SEL, uint16_t))(void *) objc_msgSend)((id)model, meta->_setter, (uint16_t)num.unsignedShortValue);
} break;
case YYEncodingTypeInt32: {
((void (*)(id, SEL, int32_t))(void *) objc_msgSend)((id)model, meta->_setter, (int32_t)num.intValue);
}
case YYEncodingTypeUInt32: {
((void (*)(id, SEL, uint32_t))(void *) objc_msgSend)((id)model, meta->_setter, (uint32_t)num.unsignedIntValue);
} break;
case YYEncodingTypeInt64: {
if ([num isKindOfClass:[NSDecimalNumber class]]) {
((void (*)(id, SEL, int64_t))(void *) objc_msgSend)((id)model, meta->_setter, (int64_t)num.stringValue.longLongValue);
} else {
((void (*)(id, SEL, uint64_t))(void *) objc_msgSend)((id)model, meta->_setter, (uint64_t)num.longLongValue);
}
} break;
case YYEncodingTypeUInt64: {
if ([num isKindOfClass:[NSDecimalNumber class]]) {
((void (*)(id, SEL, int64_t))(void *) objc_msgSend)((id)model, meta->_setter, (int64_t)num.stringValue.longLongValue);
} else {
((void (*)(id, SEL, uint64_t))(void *) objc_msgSend)((id)model, meta->_setter, (uint64_t)num.unsignedLongLongValue);
}
} break;
case YYEncodingTypeFloat: {
float f = num.floatValue;
if (isnan(f) || isinf(f)) f = 0;
((void (*)(id, SEL, float))(void *) objc_msgSend)((id)model, meta->_setter, f);
} break;
case YYEncodingTypeDouble: {
double d = num.doubleValue;
if (isnan(d) || isinf(d)) d = 0;
((void (*)(id, SEL, double))(void *) objc_msgSend)((id)model, meta->_setter, d);
} break;
case YYEncodingTypeLongDouble: {
long double d = num.doubleValue;
if (isnan(d) || isinf(d)) d = 0;
((void (*)(id, SEL, long double))(void *) objc_msgSend)((id)model, meta->_setter, (long double)d);
} // break; commented for code coverage in next line
default: break;
}
}
/**
Set value to model with a property meta.
@discussion Caller should hold strong reference to the parameters before this function returns.
@param model Should not be nil.
@param value Should not be nil, but can be NSNull.
@param meta Should not be nil, and meta->_setter should not be nil.
*/
static void ModelSetValueForProperty(__unsafe_unretained id model,
__unsafe_unretained id value,
__unsafe_unretained _YYModelPropertyMeta *meta) {
if (meta->_isCNumber) {
NSNumber *num = YYNSNumberCreateFromID(value);
ModelSetNumberToProperty(model, num, meta);
if (num) [num class]; // hold the number
} else if (meta->_nsType) {
if (value == (id)kCFNull) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)nil);
} else {
switch (meta->_nsType) {
case YYEncodingTypeNSString:
case YYEncodingTypeNSMutableString: {
if ([value isKindOfClass:[NSString class]]) {
if (meta->_nsType == YYEncodingTypeNSString) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);
} else {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, ((NSString *)value).mutableCopy);
}
} else if ([value isKindOfClass:[NSNumber class]]) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,
meta->_setter,
(meta->_nsType == YYEncodingTypeNSString) ?
((NSNumber *)value).stringValue :
((NSNumber *)value).stringValue.mutableCopy);
} else if ([value isKindOfClass:[NSData class]]) {
NSMutableString *string = [[NSMutableString alloc] initWithData:value encoding:NSUTF8StringEncoding];
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, string);
} else if ([value isKindOfClass:[NSURL class]]) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,
meta->_setter,
(meta->_nsType == YYEncodingTypeNSString) ?
((NSURL *)value).absoluteString :
((NSURL *)value).absoluteString.mutableCopy);
} else if ([value isKindOfClass:[NSAttributedString class]]) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,
meta->_setter,
(meta->_nsType == YYEncodingTypeNSString) ?
((NSAttributedString *)value).string :
((NSAttributedString *)value).string.mutableCopy);
}
} break;
case YYEncodingTypeNSValue:
case YYEncodingTypeNSNumber:
case YYEncodingTypeNSDecimalNumber: {
if (meta->_nsType == YYEncodingTypeNSNumber) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, YYNSNumberCreateFromID(value));
} else if (meta->_nsType == YYEncodingTypeNSDecimalNumber) {
if ([value isKindOfClass:[NSDecimalNumber class]]) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);
} else if ([value isKindOfClass:[NSNumber class]]) {
NSDecimalNumber *decNum = [NSDecimalNumber decimalNumberWithDecimal:[((NSNumber *)value) decimalValue]];
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, decNum);
} else if ([value isKindOfClass:[NSString class]]) {
NSDecimalNumber *decNum = [NSDecimalNumber decimalNumberWithString:value];
NSDecimal dec = decNum.decimalValue;
if (dec._length == 0 && dec._isNegative) {
decNum = nil; // NaN
}
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, decNum);
}
} else { // YYEncodingTypeNSValue
if ([value isKindOfClass:[NSValue class]]) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);
}
}
} break;
case YYEncodingTypeNSData:
case YYEncodingTypeNSMutableData: {
if ([value isKindOfClass:[NSData class]]) {
if (meta->_nsType == YYEncodingTypeNSData) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);
} else {
NSMutableData *data = ((NSData *)value).mutableCopy;
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, data);
}
} else if ([value isKindOfClass:[NSString class]]) {
NSData *data = [(NSString *)value dataUsingEncoding:NSUTF8StringEncoding];
if (meta->_nsType == YYEncodingTypeNSMutableData) {
data = ((NSData *)data).mutableCopy;
}
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, data);
}
} break;
case YYEncodingTypeNSDate: {
if ([value isKindOfClass:[NSDate class]]) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);
} else if ([value isKindOfClass:[NSString class]]) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, YYNSDateFromString(value));
}
} break;
case YYEncodingTypeNSURL: {
if ([value isKindOfClass:[NSURL class]]) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);
} else if ([value isKindOfClass:[NSString class]]) {
NSCharacterSet *set = [NSCharacterSet whitespaceAndNewlineCharacterSet];
NSString *str = [value stringByTrimmingCharactersInSet:set];
if (str.length == 0) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, nil);
} else {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, [[NSURL alloc] initWithString:str]);
}
}
} break;
case YYEncodingTypeNSArray:
case YYEncodingTypeNSMutableArray: {
if (meta->_genericCls) {
NSArray *valueArr = nil;
if ([value isKindOfClass:[NSArray class]]) valueArr = value;
else if ([value isKindOfClass:[NSSet class]]) valueArr = ((NSSet *)value).allObjects;
if (valueArr) {
NSMutableArray *objectArr = [NSMutableArray new];
for (id one in valueArr) {
if ([one isKindOfClass:meta->_genericCls]) {
[objectArr addObject:one];
} else if ([one isKindOfClass:[NSDictionary class]]) {
Class cls = meta->_genericCls;
if (meta->_hasCustomClassFromDictionary) {
cls = [cls modelCustomClassForDictionary:one];
if (!cls) cls = meta->_genericCls; // for xcode code coverage
}
NSObject *newOne = [cls new];
[newOne modelSetWithDictionary:one];
if (newOne) [objectArr addObject:newOne];
}
}
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, objectArr);
}
} else {
if ([value isKindOfClass:[NSArray class]]) {
if (meta->_nsType == YYEncodingTypeNSArray) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);
} else {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,
meta->_setter,
((NSArray *)value).mutableCopy);
}
} else if ([value isKindOfClass:[NSSet class]]) {
if (meta->_nsType == YYEncodingTypeNSArray) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, ((NSSet *)value).allObjects);
} else {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,
meta->_setter,
((NSSet *)value).allObjects.mutableCopy);
}
}
}
} break;
case YYEncodingTypeNSDictionary:
case YYEncodingTypeNSMutableDictionary: {
if ([value isKindOfClass:[NSDictionary class]]) {
if (meta->_genericCls) {
NSMutableDictionary *dic = [NSMutableDictionary new];
[((NSDictionary *)value) enumerateKeysAndObjectsUsingBlock:^(NSString *oneKey, id oneValue, BOOL *stop) {
if ([oneValue isKindOfClass:[NSDictionary class]]) {
Class cls = meta->_genericCls;
if (meta->_hasCustomClassFromDictionary) {
cls = [cls modelCustomClassForDictionary:oneValue];
if (!cls) cls = meta->_genericCls; // for xcode code coverage
}
NSObject *newOne = [cls new];
[newOne modelSetWithDictionary:(id)oneValue];
if (newOne) dic[oneKey] = newOne;
}
}];
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, dic);
} else {
if (meta->_nsType == YYEncodingTypeNSDictionary) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, value);
} else {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,
meta->_setter,
((NSDictionary *)value).mutableCopy);
}
}
}
} break;
case YYEncodingTypeNSSet:
case YYEncodingTypeNSMutableSet: {
NSSet *valueSet = nil;
if ([value isKindOfClass:[NSArray class]]) valueSet = [NSMutableSet setWithArray:value];
else if ([value isKindOfClass:[NSSet class]]) valueSet = ((NSSet *)value);
if (meta->_genericCls) {
NSMutableSet *set = [NSMutableSet new];
for (id one in valueSet) {
if ([one isKindOfClass:meta->_genericCls]) {
[set addObject:one];
} else if ([one isKindOfClass:[NSDictionary class]]) {
Class cls = meta->_genericCls;
if (meta->_hasCustomClassFromDictionary) {
cls = [cls modelCustomClassForDictionary:one];
if (!cls) cls = meta->_genericCls; // for xcode code coverage
}
NSObject *newOne = [cls new];
[newOne modelSetWithDictionary:one];
if (newOne) [set addObject:newOne];
}
}
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, set);
} else {
if (meta->_nsType == YYEncodingTypeNSSet) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, valueSet);
} else {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model,
meta->_setter,
((NSSet *)valueSet).mutableCopy);
}
}
} // break; commented for code coverage in next line
default: break;
}
}
} else {
BOOL isNull = (value == (id)kCFNull);
switch (meta->_type & YYEncodingTypeMask) {
case YYEncodingTypeObject: {
if (isNull) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)nil);
} else if ([value isKindOfClass:meta->_cls] || !meta->_cls) {
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)value);
} else if ([value isKindOfClass:[NSDictionary class]]) {
NSObject *one = nil;
if (meta->_getter) {
one = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, meta->_getter);
}
if (one) {
[one modelSetWithDictionary:value];
} else {
Class cls = meta->_cls;
if (meta->_hasCustomClassFromDictionary) {
cls = [cls modelCustomClassForDictionary:value];
if (!cls) cls = meta->_genericCls; // for xcode code coverage
}
one = [cls new];
[one modelSetWithDictionary:value];
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, meta->_setter, (id)one);
}
}
} break;
case YYEncodingTypeClass: {
if (isNull) {
((void (*)(id, SEL, Class))(void *) objc_msgSend)((id)model, meta->_setter, (Class)NULL);
} else {
Class cls = nil;
if ([value isKindOfClass:[NSString class]]) {
cls = NSClassFromString(value);
if (cls) {
((void (*)(id, SEL, Class))(void *) objc_msgSend)((id)model, meta->_setter, (Class)cls);
}
} else {
cls = object_getClass(value);
if (cls) {
if (class_isMetaClass(cls)) {
((void (*)(id, SEL, Class))(void *) objc_msgSend)((id)model, meta->_setter, (Class)value);
}
}
}
}
} break;
case YYEncodingTypeSEL: {
if (isNull) {
((void (*)(id, SEL, SEL))(void *) objc_msgSend)((id)model, meta->_setter, (SEL)NULL);
} else if ([value isKindOfClass:[NSString class]]) {
SEL sel = NSSelectorFromString(value);
if (sel) ((void (*)(id, SEL, SEL))(void *) objc_msgSend)((id)model, meta->_setter, (SEL)sel);
}
} break;
case YYEncodingTypeBlock: {
if (isNull) {
((void (*)(id, SEL, void (^)()))(void *) objc_msgSend)((id)model, meta->_setter, (void (^)())NULL);
} else if ([value isKindOfClass:YYNSBlockClass()]) {
((void (*)(id, SEL, void (^)()))(void *) objc_msgSend)((id)model, meta->_setter, (void (^)())value);
}
} break;
case YYEncodingTypeStruct:
case YYEncodingTypeUnion:
case YYEncodingTypeCArray: {
if ([value isKindOfClass:[NSValue class]]) {
const char *valueType = ((NSValue *)value).objCType;
const char *metaType = meta->_info.typeEncoding.UTF8String;
if (valueType && metaType && strcmp(valueType, metaType) == 0) {
[model setValue:value forKey:meta->_name];
}
}
} break;
case YYEncodingTypePointer:
case YYEncodingTypeCString: {
if (isNull) {
((void (*)(id, SEL, void *))(void *) objc_msgSend)((id)model, meta->_setter, (void *)NULL);
} else if ([value isKindOfClass:[NSValue class]]) {
NSValue *nsValue = value;
if (nsValue.objCType && strcmp(nsValue.objCType, "^v") == 0) {
((void (*)(id, SEL, void *))(void *) objc_msgSend)((id)model, meta->_setter, nsValue.pointerValue);
}
}
} // break; commented for code coverage in next line
default: break;
}
}
}
typedef struct {
void *modelMeta; ///< _YYModelMeta
void *model; ///< id (self)
void *dictionary; ///< NSDictionary (json)
} ModelSetContext;
/**
Apply function for dictionary, to set the key-value pair to model.
@param _key should not be nil, NSString.
@param _value should not be nil.
@param _context _context.modelMeta and _context.model should not be nil.
*/
static void ModelSetWithDictionaryFunction(const void *_key, const void *_value, void *_context) {
ModelSetContext *context = _context;
__unsafe_unretained _YYModelMeta *meta = (__bridge _YYModelMeta *)(context->modelMeta);
__unsafe_unretained _YYModelPropertyMeta *propertyMeta = [meta->_mapper objectForKey:(__bridge id)(_key)];
__unsafe_unretained id model = (__bridge id)(context->model);
while (propertyMeta) {
if (propertyMeta->_setter) {
ModelSetValueForProperty(model, (__bridge __unsafe_unretained id)_value, propertyMeta);
}
propertyMeta = propertyMeta->_next;
};
}
/**
Apply function for model property meta, to set dictionary to model.
@param _propertyMeta should not be nil, _YYModelPropertyMeta.
@param _context _context.model and _context.dictionary should not be nil.
*/
static void ModelSetWithPropertyMetaArrayFunction(const void *_propertyMeta, void *_context) {
ModelSetContext *context = _context;
__unsafe_unretained NSDictionary *dictionary = (__bridge NSDictionary *)(context->dictionary);
__unsafe_unretained _YYModelPropertyMeta *propertyMeta = (__bridge _YYModelPropertyMeta *)(_propertyMeta);
if (!propertyMeta->_setter) return;
id value = nil;
if (propertyMeta->_mappedToKeyArray) {
value = YYValueForMultiKeys(dictionary, propertyMeta->_mappedToKeyArray);
} else if (propertyMeta->_mappedToKeyPath) {
value = YYValueForKeyPath(dictionary, propertyMeta->_mappedToKeyPath);
} else {
value = [dictionary objectForKey:propertyMeta->_mappedToKey];
}
if (value) {
__unsafe_unretained id model = (__bridge id)(context->model);
ModelSetValueForProperty(model, value, propertyMeta);
}
}
/**
Returns a valid JSON object (NSArray/NSDictionary/NSString/NSNumber/NSNull),
or nil if an error occurs.
@param model Model, can be nil.
@return JSON object, nil if an error occurs.
*/
static id ModelToJSONObjectRecursive(NSObject *model) {
if (!model || model == (id)kCFNull) return model;
if ([model isKindOfClass:[NSString class]]) return model;
if ([model isKindOfClass:[NSNumber class]]) return model;
if ([model isKindOfClass:[NSDictionary class]]) {
if ([NSJSONSerialization isValidJSONObject:model]) return model;
NSMutableDictionary *newDic = [NSMutableDictionary new];
[((NSDictionary *)model) enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
NSString *stringKey = [key isKindOfClass:[NSString class]] ? key : key.description;
if (!stringKey) return;
id jsonObj = ModelToJSONObjectRecursive(obj);
if (!jsonObj) jsonObj = (id)kCFNull;
newDic[stringKey] = jsonObj;
}];
return newDic;
}
if ([model isKindOfClass:[NSSet class]]) {
NSArray *array = ((NSSet *)model).allObjects;
if ([NSJSONSerialization isValidJSONObject:array]) return array;
NSMutableArray *newArray = [NSMutableArray new];
for (id obj in array) {
if ([obj isKindOfClass:[NSString class]] || [obj isKindOfClass:[NSNumber class]]) {
[newArray addObject:obj];
} else {
id jsonObj = ModelToJSONObjectRecursive(obj);
if (jsonObj && jsonObj != (id)kCFNull) [newArray addObject:jsonObj];
}
}
return newArray;
}
if ([model isKindOfClass:[NSArray class]]) {
if ([NSJSONSerialization isValidJSONObject:model]) return model;
NSMutableArray *newArray = [NSMutableArray new];
for (id obj in (NSArray *)model) {
if ([obj isKindOfClass:[NSString class]] || [obj isKindOfClass:[NSNumber class]]) {
[newArray addObject:obj];
} else {
id jsonObj = ModelToJSONObjectRecursive(obj);
if (jsonObj && jsonObj != (id)kCFNull) [newArray addObject:jsonObj];
}
}
return newArray;
}
if ([model isKindOfClass:[NSURL class]]) return ((NSURL *)model).absoluteString;
if ([model isKindOfClass:[NSAttributedString class]]) return ((NSAttributedString *)model).string;
if ([model isKindOfClass:[NSDate class]]) return [YYISODateFormatter() stringFromDate:(id)model];
if ([model isKindOfClass:[NSData class]]) return nil;
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:[model class]];
if (!modelMeta || modelMeta->_keyMappedCount == 0) return nil;
NSMutableDictionary *result = [[NSMutableDictionary alloc] initWithCapacity:64];
__unsafe_unretained NSMutableDictionary *dic = result; // avoid retain and release in block
[modelMeta->_mapper enumerateKeysAndObjectsUsingBlock:^(NSString *propertyMappedKey, _YYModelPropertyMeta *propertyMeta, BOOL *stop) {
if (!propertyMeta->_getter) return;
id value = nil;
if (propertyMeta->_isCNumber) {
value = ModelCreateNumberFromProperty(model, propertyMeta);
} else if (propertyMeta->_nsType) {
id v = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);
value = ModelToJSONObjectRecursive(v);
} else {
switch (propertyMeta->_type & YYEncodingTypeMask) {
case YYEncodingTypeObject: {
id v = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);
value = ModelToJSONObjectRecursive(v);
if (value == (id)kCFNull) value = nil;
} break;
case YYEncodingTypeClass: {
Class v = ((Class (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);
value = v ? NSStringFromClass(v) : nil;
} break;
case YYEncodingTypeSEL: {
SEL v = ((SEL (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyMeta->_getter);
value = v ? NSStringFromSelector(v) : nil;
} break;
default: break;
}
}
if (!value) return;
if (propertyMeta->_mappedToKeyPath) {
NSMutableDictionary *superDic = dic;
NSMutableDictionary *subDic = nil;
for (NSUInteger i = 0, max = propertyMeta->_mappedToKeyPath.count; i < max; i++) {
NSString *key = propertyMeta->_mappedToKeyPath[i];
if (i + 1 == max) { // end
if (!superDic[key]) superDic[key] = value;
break;
}
subDic = superDic[key];
if (subDic) {
if ([subDic isKindOfClass:[NSDictionary class]]) {
subDic = subDic.mutableCopy;
superDic[key] = subDic;
} else {
break;
}
} else {
subDic = [NSMutableDictionary new];
superDic[key] = subDic;
}
superDic = subDic;
subDic = nil;
}
} else {
if (!dic[propertyMeta->_mappedToKey]) {
dic[propertyMeta->_mappedToKey] = value;
}
}
}];
if (modelMeta->_hasCustomTransformToDictionary) {
BOOL suc = [((id<YYModel>)model) modelCustomTransformToDictionary:dic];
if (!suc) return nil;
}
return result;
}
/// Add indent to string (exclude first line)
static NSMutableString *ModelDescriptionAddIndent(NSMutableString *desc, NSUInteger indent) {
for (NSUInteger i = 0, max = desc.length; i < max; i++) {
unichar c = [desc characterAtIndex:i];
if (c == '\n') {
for (NSUInteger j = 0; j < indent; j++) {
[desc insertString:@" " atIndex:i + 1];
}
i += indent * 4;
max += indent * 4;
}
}
return desc;
}
/// Generaate a description string
static NSString *ModelDescription(NSObject *model) {
static const int kDescMaxLength = 100;
if (!model) return @"<nil>";
if (model == (id)kCFNull) return @"<null>";
if (![model isKindOfClass:[NSObject class]]) return [NSString stringWithFormat:@"%@",model];
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:model.class];
switch (modelMeta->_nsType) {
case YYEncodingTypeNSString: case YYEncodingTypeNSMutableString: {
return [NSString stringWithFormat:@"\"%@\"",model];
}
case YYEncodingTypeNSValue:
case YYEncodingTypeNSData: case YYEncodingTypeNSMutableData: {
NSString *tmp = model.description;
if (tmp.length > kDescMaxLength) {
tmp = [tmp substringToIndex:kDescMaxLength];
tmp = [tmp stringByAppendingString:@"..."];
}
return tmp;
}
case YYEncodingTypeNSNumber:
case YYEncodingTypeNSDecimalNumber:
case YYEncodingTypeNSDate:
case YYEncodingTypeNSURL: {
return [NSString stringWithFormat:@"%@",model];
}
case YYEncodingTypeNSSet: case YYEncodingTypeNSMutableSet: {
model = ((NSSet *)model).allObjects;
} // no break
case YYEncodingTypeNSArray: case YYEncodingTypeNSMutableArray: {
NSArray *array = (id)model;
NSMutableString *desc = [NSMutableString new];
if (array.count == 0) {
return [desc stringByAppendingString:@"[]"];
} else {
[desc appendFormat:@"[\n"];
for (NSUInteger i = 0, max = array.count; i < max; i++) {
NSObject *obj = array[i];
[desc appendString:@" "];
[desc appendString:ModelDescriptionAddIndent(ModelDescription(obj).mutableCopy, 1)];
[desc appendString:(i + 1 == max) ? @"\n" : @";\n"];
}
[desc appendString:@"]"];
return desc;
}
}
case YYEncodingTypeNSDictionary: case YYEncodingTypeNSMutableDictionary: {
NSDictionary *dic = (id)model;
NSMutableString *desc = [NSMutableString new];
if (dic.count == 0) {
return [desc stringByAppendingString:@"{}"];
} else {
NSArray *keys = dic.allKeys;
[desc appendFormat:@"{\n"];
for (NSUInteger i = 0, max = keys.count; i < max; i++) {
NSString *key = keys[i];
NSObject *value = dic[key];
[desc appendString:@" "];
[desc appendFormat:@"%@ = %@",key, ModelDescriptionAddIndent(ModelDescription(value).mutableCopy, 1)];
[desc appendString:(i + 1 == max) ? @"\n" : @";\n"];
}
[desc appendString:@"}"];
}
return desc;
}
default: {
NSMutableString *desc = [NSMutableString new];
[desc appendFormat:@"<%@: %p>", model.class, model];
if (modelMeta->_allPropertyMetas.count == 0) return desc;
// sort property names
NSArray *properties = [modelMeta->_allPropertyMetas
sortedArrayUsingComparator:^NSComparisonResult(_YYModelPropertyMeta *p1, _YYModelPropertyMeta *p2) {
return [p1->_name compare:p2->_name];
}];
[desc appendFormat:@" {\n"];
for (NSUInteger i = 0, max = properties.count; i < max; i++) {
_YYModelPropertyMeta *property = properties[i];
NSString *propertyDesc;
if (property->_isCNumber) {
NSNumber *num = ModelCreateNumberFromProperty(model, property);
propertyDesc = num.stringValue;
} else {
switch (property->_type & YYEncodingTypeMask) {
case YYEncodingTypeObject: {
id v = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, property->_getter);
propertyDesc = ModelDescription(v);
if (!propertyDesc) propertyDesc = @"<nil>";
} break;
case YYEncodingTypeClass: {
id v = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, property->_getter);
propertyDesc = ((NSObject *)v).description;
if (!propertyDesc) propertyDesc = @"<nil>";
} break;
case YYEncodingTypeSEL: {
SEL sel = ((SEL (*)(id, SEL))(void *) objc_msgSend)((id)model, property->_getter);
if (sel) propertyDesc = NSStringFromSelector(sel);
else propertyDesc = @"<NULL>";
} break;
case YYEncodingTypeBlock: {
id block = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, property->_getter);
propertyDesc = block ? ((NSObject *)block).description : @"<nil>";
} break;
case YYEncodingTypeCArray: case YYEncodingTypeCString: case YYEncodingTypePointer: {
void *pointer = ((void* (*)(id, SEL))(void *) objc_msgSend)((id)model, property->_getter);
propertyDesc = [NSString stringWithFormat:@"%p",pointer];
} break;
case YYEncodingTypeStruct: case YYEncodingTypeUnion: {
NSValue *value = [model valueForKey:property->_name];
propertyDesc = value ? value.description : @"{unknown}";
} break;
default: propertyDesc = @"<unknown>";
}
}
propertyDesc = ModelDescriptionAddIndent(propertyDesc.mutableCopy, 1);
[desc appendFormat:@" %@ = %@",property->_name, propertyDesc];
[desc appendString:(i + 1 == max) ? @"\n" : @";\n"];
}
[desc appendFormat:@"}"];
return desc;
}
}
}
@implementation NSObject (YYModel)
+ (NSDictionary *)_yy_dictionaryWithJSON:(id)json {
if (!json || json == (id)kCFNull) return nil;
NSDictionary *dic = nil;
NSData *jsonData = nil;
if ([json isKindOfClass:[NSDictionary class]]) {
dic = json;
} else if ([json isKindOfClass:[NSString class]]) {
jsonData = [(NSString *)json dataUsingEncoding : NSUTF8StringEncoding];
} else if ([json isKindOfClass:[NSData class]]) {
jsonData = json;
}
if (jsonData) {
dic = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:NULL];
if (![dic isKindOfClass:[NSDictionary class]]) dic = nil;
}
return dic;
}
+ (instancetype)modelWithJSON:(id)json {
NSDictionary *dic = [self _yy_dictionaryWithJSON:json];
return [self modelWithDictionary:dic];
}
+ (instancetype)modelWithDictionary:(NSDictionary *)dictionary {
if (!dictionary || dictionary == (id)kCFNull) return nil;
if (![dictionary isKindOfClass:[NSDictionary class]]) return nil;
Class cls = [self class];
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:cls];
if (modelMeta->_hasCustomClassFromDictionary) {
cls = [cls modelCustomClassForDictionary:dictionary] ?: cls;
}
NSObject *one = [cls new];
if ([one modelSetWithDictionary:dictionary]) return one;
return nil;
}
- (BOOL)modelSetWithJSON:(id)json {
NSDictionary *dic = [NSObject _yy_dictionaryWithJSON:json];
return [self modelSetWithDictionary:dic];
}
- (BOOL)modelSetWithDictionary:(NSDictionary *)dic {
if (!dic || dic == (id)kCFNull) return NO;
if (![dic isKindOfClass:[NSDictionary class]]) return NO;
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:object_getClass(self)];
if (modelMeta->_keyMappedCount == 0) return NO;
if (modelMeta->_hasCustomWillTransformFromDictionary) {
dic = [((id<YYModel>)self) modelCustomWillTransformFromDictionary:dic];
if (![dic isKindOfClass:[NSDictionary class]]) return NO;
}
ModelSetContext context = {0};
context.modelMeta = (__bridge void *)(modelMeta);
context.model = (__bridge void *)(self);
context.dictionary = (__bridge void *)(dic);
if (modelMeta->_keyMappedCount >= CFDictionaryGetCount((CFDictionaryRef)dic)) {
CFDictionaryApplyFunction((CFDictionaryRef)dic, ModelSetWithDictionaryFunction, &context);
if (modelMeta->_keyPathPropertyMetas) {
CFArrayApplyFunction((CFArrayRef)modelMeta->_keyPathPropertyMetas,
CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_keyPathPropertyMetas)),
ModelSetWithPropertyMetaArrayFunction,
&context);
}
if (modelMeta->_multiKeysPropertyMetas) {
CFArrayApplyFunction((CFArrayRef)modelMeta->_multiKeysPropertyMetas,
CFRangeMake(0, CFArrayGetCount((CFArrayRef)modelMeta->_multiKeysPropertyMetas)),
ModelSetWithPropertyMetaArrayFunction,
&context);
}
} else {
CFArrayApplyFunction((CFArrayRef)modelMeta->_allPropertyMetas,
CFRangeMake(0, modelMeta->_keyMappedCount),
ModelSetWithPropertyMetaArrayFunction,
&context);
}
if (modelMeta->_hasCustomTransformFromDictionary) {
return [((id<YYModel>)self) modelCustomTransformFromDictionary:dic];
}
return YES;
}
- (id)modelToJSONObject {
/*
Apple said:
The top level object is an NSArray or NSDictionary.
All objects are instances of NSString, NSNumber, NSArray, NSDictionary, or NSNull.
All dictionary keys are instances of NSString.
Numbers are not NaN or infinity.
*/
id jsonObject = ModelToJSONObjectRecursive(self);
if ([jsonObject isKindOfClass:[NSArray class]]) return jsonObject;
if ([jsonObject isKindOfClass:[NSDictionary class]]) return jsonObject;
return nil;
}
- (NSData *)modelToJSONData {
id jsonObject = [self modelToJSONObject];
if (!jsonObject) return nil;
return [NSJSONSerialization dataWithJSONObject:jsonObject options:0 error:NULL];
}
- (NSString *)modelToJSONString {
NSData *jsonData = [self modelToJSONData];
if (jsonData.length == 0) return nil;
return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}
- (id)modelCopy{
if (self == (id)kCFNull) return self;
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:self.class];
if (modelMeta->_nsType) return [self copy];
NSObject *one = [self.class new];
for (_YYModelPropertyMeta *propertyMeta in modelMeta->_allPropertyMetas) {
if (!propertyMeta->_getter || !propertyMeta->_setter) continue;
if (propertyMeta->_isCNumber) {
switch (propertyMeta->_type & YYEncodingTypeMask) {
case YYEncodingTypeBool: {
bool num = ((bool (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter);
((void (*)(id, SEL, bool))(void *) objc_msgSend)((id)one, propertyMeta->_setter, num);
} break;
case YYEncodingTypeInt8:
case YYEncodingTypeUInt8: {
uint8_t num = ((bool (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter);
((void (*)(id, SEL, uint8_t))(void *) objc_msgSend)((id)one, propertyMeta->_setter, num);
} break;
case YYEncodingTypeInt16:
case YYEncodingTypeUInt16: {
uint16_t num = ((uint16_t (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter);
((void (*)(id, SEL, uint16_t))(void *) objc_msgSend)((id)one, propertyMeta->_setter, num);
} break;
case YYEncodingTypeInt32:
case YYEncodingTypeUInt32: {
uint32_t num = ((uint32_t (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter);
((void (*)(id, SEL, uint32_t))(void *) objc_msgSend)((id)one, propertyMeta->_setter, num);
} break;
case YYEncodingTypeInt64:
case YYEncodingTypeUInt64: {
uint64_t num = ((uint64_t (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter);
((void (*)(id, SEL, uint64_t))(void *) objc_msgSend)((id)one, propertyMeta->_setter, num);
} break;
case YYEncodingTypeFloat: {
float num = ((float (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter);
((void (*)(id, SEL, float))(void *) objc_msgSend)((id)one, propertyMeta->_setter, num);
} break;
case YYEncodingTypeDouble: {
double num = ((double (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter);
((void (*)(id, SEL, double))(void *) objc_msgSend)((id)one, propertyMeta->_setter, num);
} break;
case YYEncodingTypeLongDouble: {
long double num = ((long double (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter);
((void (*)(id, SEL, long double))(void *) objc_msgSend)((id)one, propertyMeta->_setter, num);
} // break; commented for code coverage in next line
default: break;
}
} else {
switch (propertyMeta->_type & YYEncodingTypeMask) {
case YYEncodingTypeObject:
case YYEncodingTypeClass:
case YYEncodingTypeBlock: {
id value = ((id (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter);
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)one, propertyMeta->_setter, value);
} break;
case YYEncodingTypeSEL:
case YYEncodingTypePointer:
case YYEncodingTypeCString: {
size_t value = ((size_t (*)(id, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_getter);
((void (*)(id, SEL, size_t))(void *) objc_msgSend)((id)one, propertyMeta->_setter, value);
} break;
case YYEncodingTypeStruct:
case YYEncodingTypeUnion: {
@try {
NSValue *value = [self valueForKey:NSStringFromSelector(propertyMeta->_getter)];
if (value) {
[one setValue:value forKey:propertyMeta->_name];
}
} @catch (NSException *exception) {}
} // break; commented for code coverage in next line
default: break;
}
}
}
return one;
}
- (void)modelEncodeWithCoder:(NSCoder *)aCoder {
if (!aCoder) return;
if (self == (id)kCFNull) {
[((id<NSCoding>)self)encodeWithCoder:aCoder];
return;
}
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:self.class];
if (modelMeta->_nsType) {
[((id<NSCoding>)self)encodeWithCoder:aCoder];
return;
}
for (_YYModelPropertyMeta *propertyMeta in modelMeta->_allPropertyMetas) {
if (!propertyMeta->_getter) return;
if (propertyMeta->_isCNumber) {
NSNumber *value = ModelCreateNumberFromProperty(self, propertyMeta);
if (value) [aCoder encodeObject:value forKey:propertyMeta->_name];
} else {
switch (propertyMeta->_type & YYEncodingTypeMask) {
case YYEncodingTypeObject: {
id value = ((id (*)(id, SEL))(void *)objc_msgSend)((id)self, propertyMeta->_getter);
if (value && (propertyMeta->_nsType || [value respondsToSelector:@selector(encodeWithCoder:)])) {
if ([value isKindOfClass:[NSValue class]]) {
if ([value isKindOfClass:[NSNumber class]]) {
[aCoder encodeObject:value forKey:propertyMeta->_name];
}
} else {
[aCoder encodeObject:value forKey:propertyMeta->_name];
}
}
} break;
case YYEncodingTypeSEL: {
SEL value = ((SEL (*)(id, SEL))(void *)objc_msgSend)((id)self, propertyMeta->_getter);
if (value) {
NSString *str = NSStringFromSelector(value);
[aCoder encodeObject:str forKey:propertyMeta->_name];
}
} break;
case YYEncodingTypeStruct:
case YYEncodingTypeUnion: {
if (propertyMeta->_isKVCCompatible && propertyMeta->_isStructAvailableForKeyedArchiver) {
@try {
NSValue *value = [self valueForKey:NSStringFromSelector(propertyMeta->_getter)];
[aCoder encodeObject:value forKey:propertyMeta->_name];
} @catch (NSException *exception) {}
}
} break;
default:
break;
}
}
}
}
- (id)modelInitWithCoder:(NSCoder *)aDecoder {
if (!aDecoder) return self;
if (self == (id)kCFNull) return self;
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:self.class];
if (modelMeta->_nsType) return self;
for (_YYModelPropertyMeta *propertyMeta in modelMeta->_allPropertyMetas) {
if (!propertyMeta->_setter) continue;
if (propertyMeta->_isCNumber) {
NSNumber *value = [aDecoder decodeObjectForKey:propertyMeta->_name];
if ([value isKindOfClass:[NSNumber class]]) {
ModelSetNumberToProperty(self, value, propertyMeta);
[value class];
}
} else {
YYEncodingType type = propertyMeta->_type & YYEncodingTypeMask;
switch (type) {
case YYEncodingTypeObject: {
id value = [aDecoder decodeObjectForKey:propertyMeta->_name];
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)self, propertyMeta->_setter, value);
} break;
case YYEncodingTypeSEL: {
NSString *str = [aDecoder decodeObjectForKey:propertyMeta->_name];
if ([str isKindOfClass:[NSString class]]) {
SEL sel = NSSelectorFromString(str);
((void (*)(id, SEL, SEL))(void *) objc_msgSend)((id)self, propertyMeta->_setter, sel);
}
} break;
case YYEncodingTypeStruct:
case YYEncodingTypeUnion: {
if (propertyMeta->_isKVCCompatible) {
@try {
NSValue *value = [aDecoder decodeObjectForKey:propertyMeta->_name];
if (value) [self setValue:value forKey:propertyMeta->_name];
} @catch (NSException *exception) {}
}
} break;
default:
break;
}
}
}
return self;
}
- (NSUInteger)modelHash {
if (self == (id)kCFNull) return [self hash];
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:self.class];
if (modelMeta->_nsType) return [self hash];
NSUInteger value = 0;
NSUInteger count = 0;
for (_YYModelPropertyMeta *propertyMeta in modelMeta->_allPropertyMetas) {
if (!propertyMeta->_isKVCCompatible) continue;
value ^= [[self valueForKey:NSStringFromSelector(propertyMeta->_getter)] hash];
count++;
}
if (count == 0) value = (long)((__bridge void *)self);
return value;
}
- (BOOL)modelIsEqual:(id)model {
if (self == model) return YES;
if (![model isMemberOfClass:self.class]) return NO;
_YYModelMeta *modelMeta = [_YYModelMeta metaWithClass:self.class];
if (modelMeta->_nsType) return [self isEqual:model];
if ([self hash] != [model hash]) return NO;
for (_YYModelPropertyMeta *propertyMeta in modelMeta->_allPropertyMetas) {
if (!propertyMeta->_isKVCCompatible) continue;
id this = [self valueForKey:NSStringFromSelector(propertyMeta->_getter)];
id that = [model valueForKey:NSStringFromSelector(propertyMeta->_getter)];
if (this == that) continue;
if (this == nil || that == nil) return NO;
if (![this isEqual:that]) return NO;
}
return YES;
}
- (NSString *)modelDescription {
return ModelDescription(self);
}
@end
@implementation NSArray (YYModel)
+ (NSArray *)modelArrayWithClass:(Class)cls json:(id)json {
if (!json) return nil;
NSArray *arr = nil;
NSData *jsonData = nil;
if ([json isKindOfClass:[NSArray class]]) {
arr = json;
} else if ([json isKindOfClass:[NSString class]]) {
jsonData = [(NSString *)json dataUsingEncoding : NSUTF8StringEncoding];
} else if ([json isKindOfClass:[NSData class]]) {
jsonData = json;
}
if (jsonData) {
arr = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:NULL];
if (![arr isKindOfClass:[NSArray class]]) arr = nil;
}
return [self modelArrayWithClass:cls array:arr];
}
+ (NSArray *)modelArrayWithClass:(Class)cls array:(NSArray *)arr {
if (!cls || !arr) return nil;
NSMutableArray *result = [NSMutableArray new];
for (NSDictionary *dic in arr) {
if (![dic isKindOfClass:[NSDictionary class]]) continue;
NSObject *obj = [cls modelWithDictionary:dic];
if (obj) [result addObject:obj];
}
return result;
}
@end
@implementation NSDictionary (YYModel)
+ (NSDictionary *)modelDictionaryWithClass:(Class)cls json:(id)json {
if (!json) return nil;
NSDictionary *dic = nil;
NSData *jsonData = nil;
if ([json isKindOfClass:[NSDictionary class]]) {
dic = json;
} else if ([json isKindOfClass:[NSString class]]) {
jsonData = [(NSString *)json dataUsingEncoding : NSUTF8StringEncoding];
} else if ([json isKindOfClass:[NSData class]]) {
jsonData = json;
}
if (jsonData) {
dic = [NSJSONSerialization JSONObjectWithData:jsonData options:kNilOptions error:NULL];
if (![dic isKindOfClass:[NSDictionary class]]) dic = nil;
}
return [self modelDictionaryWithClass:cls dictionary:dic];
}
+ (NSDictionary *)modelDictionaryWithClass:(Class)cls dictionary:(NSDictionary *)dic {
if (!cls || !dic) return nil;
NSMutableDictionary *result = [NSMutableDictionary new];
for (NSString *key in dic.allKeys) {
if (![key isKindOfClass:[NSString class]]) continue;
NSObject *obj = [cls modelWithDictionary:dic[key]];
if (obj) result[key] = obj;
}
return result;
}
@end

View File

@@ -0,0 +1,200 @@
//
// YYClassInfo.h
// YYKit <https://github.com/ibireme/YYKit>
//
// Created by ibireme on 15/5/9.
// Copyright (c) 2015 ibireme.
//
// This source code is licensed under the MIT-style license found in the
// LICENSE file in the root directory of this source tree.
//
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
NS_ASSUME_NONNULL_BEGIN
/**
Type encoding's type.
*/
typedef NS_OPTIONS(NSUInteger, YYEncodingType) {
YYEncodingTypeMask = 0xFF, ///< mask of type value
YYEncodingTypeUnknown = 0, ///< unknown
YYEncodingTypeVoid = 1, ///< void
YYEncodingTypeBool = 2, ///< bool
YYEncodingTypeInt8 = 3, ///< char / BOOL
YYEncodingTypeUInt8 = 4, ///< unsigned char
YYEncodingTypeInt16 = 5, ///< short
YYEncodingTypeUInt16 = 6, ///< unsigned short
YYEncodingTypeInt32 = 7, ///< int
YYEncodingTypeUInt32 = 8, ///< unsigned int
YYEncodingTypeInt64 = 9, ///< long long
YYEncodingTypeUInt64 = 10, ///< unsigned long long
YYEncodingTypeFloat = 11, ///< float
YYEncodingTypeDouble = 12, ///< double
YYEncodingTypeLongDouble = 13, ///< long double
YYEncodingTypeObject = 14, ///< id
YYEncodingTypeClass = 15, ///< Class
YYEncodingTypeSEL = 16, ///< SEL
YYEncodingTypeBlock = 17, ///< block
YYEncodingTypePointer = 18, ///< void*
YYEncodingTypeStruct = 19, ///< struct
YYEncodingTypeUnion = 20, ///< union
YYEncodingTypeCString = 21, ///< char*
YYEncodingTypeCArray = 22, ///< char[10] (for example)
YYEncodingTypeQualifierMask = 0xFF00, ///< mask of qualifier
YYEncodingTypeQualifierConst = 1 << 8, ///< const
YYEncodingTypeQualifierIn = 1 << 9, ///< in
YYEncodingTypeQualifierInout = 1 << 10, ///< inout
YYEncodingTypeQualifierOut = 1 << 11, ///< out
YYEncodingTypeQualifierBycopy = 1 << 12, ///< bycopy
YYEncodingTypeQualifierByref = 1 << 13, ///< byref
YYEncodingTypeQualifierOneway = 1 << 14, ///< oneway
YYEncodingTypePropertyMask = 0xFF0000, ///< mask of property
YYEncodingTypePropertyReadonly = 1 << 16, ///< readonly
YYEncodingTypePropertyCopy = 1 << 17, ///< copy
YYEncodingTypePropertyRetain = 1 << 18, ///< retain
YYEncodingTypePropertyNonatomic = 1 << 19, ///< nonatomic
YYEncodingTypePropertyWeak = 1 << 20, ///< weak
YYEncodingTypePropertyCustomGetter = 1 << 21, ///< getter=
YYEncodingTypePropertyCustomSetter = 1 << 22, ///< setter=
YYEncodingTypePropertyDynamic = 1 << 23, ///< @dynamic
};
/**
Get the type from a Type-Encoding string.
@discussion See also:
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtTypeEncodings.html
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html
@param typeEncoding A Type-Encoding string.
@return The encoding type.
*/
YYEncodingType YYEncodingGetType(const char *typeEncoding);
/**
Instance variable information.
*/
@interface YYClassIvarInfo : NSObject
@property (nonatomic, assign, readonly) Ivar ivar; ///< ivar opaque struct
@property (nonatomic, strong, readonly) NSString *name; ///< Ivar's name
@property (nonatomic, assign, readonly) ptrdiff_t offset; ///< Ivar's offset
@property (nonatomic, strong, readonly) NSString *typeEncoding; ///< Ivar's type encoding
@property (nonatomic, assign, readonly) YYEncodingType type; ///< Ivar's type
/**
Creates and returns an ivar info object.
@param ivar ivar opaque struct
@return A new object, or nil if an error occurs.
*/
- (instancetype)initWithIvar:(Ivar)ivar;
@end
/**
Method information.
*/
@interface YYClassMethodInfo : NSObject
@property (nonatomic, assign, readonly) Method method; ///< method opaque struct
@property (nonatomic, strong, readonly) NSString *name; ///< method name
@property (nonatomic, assign, readonly) SEL sel; ///< method's selector
@property (nonatomic, assign, readonly) IMP imp; ///< method's implementation
@property (nonatomic, strong, readonly) NSString *typeEncoding; ///< method's parameter and return types
@property (nonatomic, strong, readonly) NSString *returnTypeEncoding; ///< return value's type
@property (nullable, nonatomic, strong, readonly) NSArray<NSString *> *argumentTypeEncodings; ///< array of arguments' type
/**
Creates and returns a method info object.
@param method method opaque struct
@return A new object, or nil if an error occurs.
*/
- (instancetype)initWithMethod:(Method)method;
@end
/**
Property information.
*/
@interface YYClassPropertyInfo : NSObject
@property (nonatomic, assign, readonly) objc_property_t property; ///< property's opaque struct
@property (nonatomic, strong, readonly) NSString *name; ///< property's name
@property (nonatomic, assign, readonly) YYEncodingType type; ///< property's type
@property (nonatomic, strong, readonly) NSString *typeEncoding; ///< property's encoding value
@property (nonatomic, strong, readonly) NSString *ivarName; ///< property's ivar name
@property (nullable, nonatomic, assign, readonly) Class cls; ///< may be nil
@property (nullable, nonatomic, strong, readonly) NSArray<NSString *> *protocols; ///< may nil
@property (nonatomic, assign, readonly) SEL getter; ///< getter (nonnull)
@property (nonatomic, assign, readonly) SEL setter; ///< setter (nonnull)
/**
Creates and returns a property info object.
@param property property opaque struct
@return A new object, or nil if an error occurs.
*/
- (instancetype)initWithProperty:(objc_property_t)property;
@end
/**
Class information for a class.
*/
@interface YYClassInfo : NSObject
@property (nonatomic, assign, readonly) Class cls; ///< class object
@property (nullable, nonatomic, assign, readonly) Class superCls; ///< super class object
@property (nullable, nonatomic, assign, readonly) Class metaCls; ///< class's meta class object
@property (nonatomic, readonly) BOOL isMeta; ///< whether this class is meta class
@property (nonatomic, strong, readonly) NSString *name; ///< class name
@property (nullable, nonatomic, strong, readonly) YYClassInfo *superClassInfo; ///< super class's class info
@property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, YYClassIvarInfo *> *ivarInfos; ///< ivars
@property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, YYClassMethodInfo *> *methodInfos; ///< methods
@property (nullable, nonatomic, strong, readonly) NSDictionary<NSString *, YYClassPropertyInfo *> *propertyInfos; ///< properties
/**
If the class is changed (for example: you add a method to this class with
'class_addMethod()'), you should call this method to refresh the class info cache.
After called this method, `needUpdate` will returns `YES`, and you should call
'classInfoWithClass' or 'classInfoWithClassName' to get the updated class info.
*/
- (void)setNeedUpdate;
/**
If this method returns `YES`, you should stop using this instance and call
`classInfoWithClass` or `classInfoWithClassName` to get the updated class info.
@return Whether this class info need update.
*/
- (BOOL)needUpdate;
/**
Get the class info of a specified Class.
@discussion This method will cache the class info and super-class info
at the first access to the Class. This method is thread-safe.
@param cls A class.
@return A class info, or nil if an error occurs.
*/
+ (nullable instancetype)classInfoWithClass:(Class)cls;
/**
Get the class info of a specified Class.
@discussion This method will cache the class info and super-class info
at the first access to the Class. This method is thread-safe.
@param className A class name.
@return A class info, or nil if an error occurs.
*/
+ (nullable instancetype)classInfoWithClassName:(NSString *)className;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,362 @@
//
// YYClassInfo.m
// YYKit <https://github.com/ibireme/YYKit>
//
// Created by ibireme on 15/5/9.
// Copyright (c) 2015 ibireme.
//
// This source code is licensed under the MIT-style license found in the
// LICENSE file in the root directory of this source tree.
//
#import "YYClassInfo.h"
#import <objc/runtime.h>
YYEncodingType YYEncodingGetType(const char *typeEncoding) {
char *type = (char *)typeEncoding;
if (!type) return YYEncodingTypeUnknown;
size_t len = strlen(type);
if (len == 0) return YYEncodingTypeUnknown;
YYEncodingType qualifier = 0;
bool prefix = true;
while (prefix) {
switch (*type) {
case 'r': {
qualifier |= YYEncodingTypeQualifierConst;
type++;
} break;
case 'n': {
qualifier |= YYEncodingTypeQualifierIn;
type++;
} break;
case 'N': {
qualifier |= YYEncodingTypeQualifierInout;
type++;
} break;
case 'o': {
qualifier |= YYEncodingTypeQualifierOut;
type++;
} break;
case 'O': {
qualifier |= YYEncodingTypeQualifierBycopy;
type++;
} break;
case 'R': {
qualifier |= YYEncodingTypeQualifierByref;
type++;
} break;
case 'V': {
qualifier |= YYEncodingTypeQualifierOneway;
type++;
} break;
default: { prefix = false; } break;
}
}
len = strlen(type);
if (len == 0) return YYEncodingTypeUnknown | qualifier;
switch (*type) {
case 'v': return YYEncodingTypeVoid | qualifier;
case 'B': return YYEncodingTypeBool | qualifier;
case 'c': return YYEncodingTypeInt8 | qualifier;
case 'C': return YYEncodingTypeUInt8 | qualifier;
case 's': return YYEncodingTypeInt16 | qualifier;
case 'S': return YYEncodingTypeUInt16 | qualifier;
case 'i': return YYEncodingTypeInt32 | qualifier;
case 'I': return YYEncodingTypeUInt32 | qualifier;
case 'l': return YYEncodingTypeInt32 | qualifier;
case 'L': return YYEncodingTypeUInt32 | qualifier;
case 'q': return YYEncodingTypeInt64 | qualifier;
case 'Q': return YYEncodingTypeUInt64 | qualifier;
case 'f': return YYEncodingTypeFloat | qualifier;
case 'd': return YYEncodingTypeDouble | qualifier;
case 'D': return YYEncodingTypeLongDouble | qualifier;
case '#': return YYEncodingTypeClass | qualifier;
case ':': return YYEncodingTypeSEL | qualifier;
case '*': return YYEncodingTypeCString | qualifier;
case '^': return YYEncodingTypePointer | qualifier;
case '[': return YYEncodingTypeCArray | qualifier;
case '(': return YYEncodingTypeUnion | qualifier;
case '{': return YYEncodingTypeStruct | qualifier;
case '@': {
if (len == 2 && *(type + 1) == '?')
return YYEncodingTypeBlock | qualifier;
else
return YYEncodingTypeObject | qualifier;
}
default: return YYEncodingTypeUnknown | qualifier;
}
}
@implementation YYClassIvarInfo
- (instancetype)initWithIvar:(Ivar)ivar {
if (!ivar) return nil;
self = [super init];
_ivar = ivar;
const char *name = ivar_getName(ivar);
if (name) {
_name = [NSString stringWithUTF8String:name];
}
_offset = ivar_getOffset(ivar);
const char *typeEncoding = ivar_getTypeEncoding(ivar);
if (typeEncoding) {
_typeEncoding = [NSString stringWithUTF8String:typeEncoding];
_type = YYEncodingGetType(typeEncoding);
}
return self;
}
@end
@implementation YYClassMethodInfo
- (instancetype)initWithMethod:(Method)method {
if (!method) return nil;
self = [super init];
_method = method;
_sel = method_getName(method);
_imp = method_getImplementation(method);
const char *name = sel_getName(_sel);
if (name) {
_name = [NSString stringWithUTF8String:name];
}
const char *typeEncoding = method_getTypeEncoding(method);
if (typeEncoding) {
_typeEncoding = [NSString stringWithUTF8String:typeEncoding];
}
char *returnType = method_copyReturnType(method);
if (returnType) {
_returnTypeEncoding = [NSString stringWithUTF8String:returnType];
free(returnType);
}
unsigned int argumentCount = method_getNumberOfArguments(method);
if (argumentCount > 0) {
NSMutableArray *argumentTypes = [NSMutableArray new];
for (unsigned int i = 0; i < argumentCount; i++) {
char *argumentType = method_copyArgumentType(method, i);
NSString *type = argumentType ? [NSString stringWithUTF8String:argumentType] : nil;
[argumentTypes addObject:type ? type : @""];
if (argumentType) free(argumentType);
}
_argumentTypeEncodings = argumentTypes;
}
return self;
}
@end
@implementation YYClassPropertyInfo
- (instancetype)initWithProperty:(objc_property_t)property {
if (!property) return nil;
self = [super init];
_property = property;
const char *name = property_getName(property);
if (name) {
_name = [NSString stringWithUTF8String:name];
}
YYEncodingType type = 0;
unsigned int attrCount;
objc_property_attribute_t *attrs = property_copyAttributeList(property, &attrCount);
for (unsigned int i = 0; i < attrCount; i++) {
switch (attrs[i].name[0]) {
case 'T': { // Type encoding
if (attrs[i].value) {
_typeEncoding = [NSString stringWithUTF8String:attrs[i].value];
type = YYEncodingGetType(attrs[i].value);
if ((type & YYEncodingTypeMask) == YYEncodingTypeObject && _typeEncoding.length) {
NSScanner *scanner = [NSScanner scannerWithString:_typeEncoding];
if (![scanner scanString:@"@\"" intoString:NULL]) continue;
NSString *clsName = nil;
if ([scanner scanUpToCharactersFromSet: [NSCharacterSet characterSetWithCharactersInString:@"\"<"] intoString:&clsName]) {
if (clsName.length) _cls = objc_getClass(clsName.UTF8String);
}
NSMutableArray *protocols = nil;
while ([scanner scanString:@"<" intoString:NULL]) {
NSString* protocol = nil;
if ([scanner scanUpToString:@">" intoString: &protocol]) {
if (protocol.length) {
if (!protocols) protocols = [NSMutableArray new];
[protocols addObject:protocol];
}
}
[scanner scanString:@">" intoString:NULL];
}
_protocols = protocols;
}
}
} break;
case 'V': { // Instance variable
if (attrs[i].value) {
_ivarName = [NSString stringWithUTF8String:attrs[i].value];
}
} break;
case 'R': {
type |= YYEncodingTypePropertyReadonly;
} break;
case 'C': {
type |= YYEncodingTypePropertyCopy;
} break;
case '&': {
type |= YYEncodingTypePropertyRetain;
} break;
case 'N': {
type |= YYEncodingTypePropertyNonatomic;
} break;
case 'D': {
type |= YYEncodingTypePropertyDynamic;
} break;
case 'W': {
type |= YYEncodingTypePropertyWeak;
} break;
case 'G': {
type |= YYEncodingTypePropertyCustomGetter;
if (attrs[i].value) {
_getter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]);
}
} break;
case 'S': {
type |= YYEncodingTypePropertyCustomSetter;
if (attrs[i].value) {
_setter = NSSelectorFromString([NSString stringWithUTF8String:attrs[i].value]);
}
} // break; commented for code coverage in next line
default: break;
}
}
if (attrs) {
free(attrs);
attrs = NULL;
}
_type = type;
if (_name.length) {
if (!_getter) {
_getter = NSSelectorFromString(_name);
}
if (!_setter) {
_setter = NSSelectorFromString([NSString stringWithFormat:@"set%@%@:", [_name substringToIndex:1].uppercaseString, [_name substringFromIndex:1]]);
}
}
return self;
}
@end
@implementation YYClassInfo {
BOOL _needUpdate;
}
- (instancetype)initWithClass:(Class)cls {
if (!cls) return nil;
self = [super init];
_cls = cls;
_superCls = class_getSuperclass(cls);
_isMeta = class_isMetaClass(cls);
if (!_isMeta) {
_metaCls = objc_getMetaClass(class_getName(cls));
}
_name = NSStringFromClass(cls);
[self _update];
_superClassInfo = [self.class classInfoWithClass:_superCls];
return self;
}
- (void)_update {
_ivarInfos = nil;
_methodInfos = nil;
_propertyInfos = nil;
Class cls = self.cls;
unsigned int methodCount = 0;
Method *methods = class_copyMethodList(cls, &methodCount);
if (methods) {
NSMutableDictionary *methodInfos = [NSMutableDictionary new];
_methodInfos = methodInfos;
for (unsigned int i = 0; i < methodCount; i++) {
YYClassMethodInfo *info = [[YYClassMethodInfo alloc] initWithMethod:methods[i]];
if (info.name) methodInfos[info.name] = info;
}
free(methods);
}
unsigned int propertyCount = 0;
objc_property_t *properties = class_copyPropertyList(cls, &propertyCount);
if (properties) {
NSMutableDictionary *propertyInfos = [NSMutableDictionary new];
_propertyInfos = propertyInfos;
for (unsigned int i = 0; i < propertyCount; i++) {
YYClassPropertyInfo *info = [[YYClassPropertyInfo alloc] initWithProperty:properties[i]];
if (info.name) propertyInfos[info.name] = info;
}
free(properties);
}
unsigned int ivarCount = 0;
Ivar *ivars = class_copyIvarList(cls, &ivarCount);
if (ivars) {
NSMutableDictionary *ivarInfos = [NSMutableDictionary new];
_ivarInfos = ivarInfos;
for (unsigned int i = 0; i < ivarCount; i++) {
YYClassIvarInfo *info = [[YYClassIvarInfo alloc] initWithIvar:ivars[i]];
if (info.name) ivarInfos[info.name] = info;
}
free(ivars);
}
if (!_ivarInfos) _ivarInfos = @{};
if (!_methodInfos) _methodInfos = @{};
if (!_propertyInfos) _propertyInfos = @{};
_needUpdate = NO;
}
- (void)setNeedUpdate {
_needUpdate = YES;
}
- (BOOL)needUpdate {
return _needUpdate;
}
+ (instancetype)classInfoWithClass:(Class)cls {
if (!cls) return nil;
static CFMutableDictionaryRef classCache;
static CFMutableDictionaryRef metaCache;
static dispatch_once_t onceToken;
static dispatch_semaphore_t lock;
dispatch_once(&onceToken, ^{
classCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
metaCache = CFDictionaryCreateMutable(CFAllocatorGetDefault(), 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
lock = dispatch_semaphore_create(1);
});
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
YYClassInfo *info = CFDictionaryGetValue(class_isMetaClass(cls) ? metaCache : classCache, (__bridge const void *)(cls));
if (info && info->_needUpdate) {
[info _update];
}
dispatch_semaphore_signal(lock);
if (!info) {
info = [[YYClassInfo alloc] initWithClass:cls];
if (info) {
dispatch_semaphore_wait(lock, DISPATCH_TIME_FOREVER);
CFDictionarySetValue(info.isMeta ? metaCache : classCache, (__bridge const void *)(cls), (__bridge const void *)(info));
dispatch_semaphore_signal(lock);
}
}
return info;
}
+ (instancetype)classInfoWithClassName:(NSString *)className {
Class cls = NSClassFromString(className);
return [self classInfoWithClass:cls];
}
@end

View File

@@ -0,0 +1,45 @@
/*
Localizable.strings
ShenQi
Created by Yao on 2024/11/4.
*/
"Tips" = "TIPS";
"Need to enable push permission" = "NEED TO ENABLE APNS PERMISSION";
"SETTING" = "SETTING";
"Contacts permission must be turned on" = "CONTACTS PERMISSION MUST BE TURNED ON";
"Please enter the invitation code" = "PLEASE ENTER THE INVITATION CODE";
"INPUT" = "INPUT";
"HOMEPAGE" = "HOMEPAGE";
"MY INVITE CODE" = "MY INVITE CODE";
"SHARE" = "SHARE";
"CANCEL" = "CANCEL";
"Invite you to download" = "Invite you to download";
"Share Success" = "Share Success!";
"OK" = "OK";
"COPY" = "COPY";
"New version found" = "New version found";
"Update" = "Update";
"My Parent Invitation Code" = "My Parent Invitation Code";
"RECORDS" = "INVITE RECORDS";
"Balance" = "Balance";
"Income" = "Income";
"EditBank" = "Edit Bank";
"RealName" = "Real Name";
"BankName" = "Bank Name";
"BankNO" = "Bank NO.";
"PLS" = "Please";
"InputEdit" = "input";
"Withdraw" = "Withdrawal";
"Amount" = "Amount";
"WithdrawRecord" = "Withdrawal Record";
"Choose" = "Choose";
"OverCanWithdraw" = "Exceeding the withdrawal";
"Area" = "Area";
"OpenImage" = "Open Image";
"OpenLink" = "Open Link";
"Share With WhatsApp" = "WhatsApp Service";
"Share With Telegram" = "Telegram Service";
"Share With Facebook" = "Facebook Service";

18
ShenQi/main.m Normal file
View File

@@ -0,0 +1,18 @@
//
// main.m
// ShenQi
//
// Created by Yao on 2023/4/4.
//
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char * argv[]) {
NSString * appDelegateClassName;
@autoreleasepool {
// Setup code that might create autoreleased objects goes here.
appDelegateClassName = NSStringFromClass([AppDelegate class]);
}
return UIApplicationMain(argc, argv, nil, appDelegateClassName);
}

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,42 @@
/*
Localizable.strings
ShenQi
Created by Yao on 2024/11/4.
*/
"Tips" = "petunjuk";
"Need to enable push permission" = "Sila dayakan kebenaran tolak";
"SETTING" = "sediakan";
"Contacts permission must be turned on" = "Sila dayakan kebenaran buku alamat";
"Please enter the invitation code" = "Sila masukkan kod jemputan";
"INPUT" = "untuk mengisi";
"HOMEPAGE" = "Laman utama";
"MY INVITE CODE" = "kod jemputan saya";
"SHARE" = "kongsi";
"CANCEL" = "Batal";
"Invite you to download" = "Menjemput anda untuk memuat turun";
"Share Success" = "Kongsi kejayaan!";
"OK" = "pasti";
"COPY" = "klon";
"New version found" = "versi baharu ditemui";
"Update" = "Pergi ke kemas kini";
"My Parent Invitation Code" = "Kod jemputan ibu bapa saya";
"RECORDS" = "menjemput rekod";
"Balance" = "Baki";
"Income" = "pendapatan";
"EditBank" = "Edit kad bank";
"RealName" = "Nama pemegang kad";
"BankName" = "Nama bank pembukaan";
"BankNO" = "Nombor kad bank";
"PLS" = "tolonglah";
"InputEdit" = "masuk";
"Withdraw" = "Keluarkan wang tunai";
"Amount" = "Jumlah";
"WithdrawRecord" = "Rekod pengeluaran";
"Choose" = "pilih";
"OverCanWithdraw" = "Lebih daripada yang boleh ditarik balik";
"Area" = "wilayah negara";
"OpenImage" = "Open Image";
"OpenLink" = "Open Link";

View File

@@ -0,0 +1,6 @@
/* Class = "UIButton"; configuration.title = "进入App"; ObjectID = "9sI-nt-sPs"; */
"9sI-nt-sPs.configuration.title" = "进入App";
/* Class = "UIButton"; normalTitle = "Button"; ObjectID = "9sI-nt-sPs"; */
"9sI-nt-sPs.normalTitle" = "Button";

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,42 @@
/*
Localizable.strings
ShenQi
Created by Yao on 2024/11/4.
*/
"Tips" = "คำใบ้";
"Need to enable push permission" = "โปรดเปิดใช้งานการอนุญาตแบบพุช";
"SETTING" = "ไปที่การตั้งค่า";
"Contacts permission must be turned on" = "โปรดเปิดใช้งานการอนุญาตสมุดที่อยู่";
"Please enter the invitation code" = "กรุณากรอกรหัสเชิญ";
"INPUT" = "เพื่อกรอก";
"HOMEPAGE" = "หน้าแรก";
"MY INVITE CODE" = "รหัสเชิญของฉัน";
"SHARE" = "แบ่งปัน";
"CANCEL" = "ยกเลิก";
"Invite you to download" = "เชิญชวนให้ดาวน์โหลด";
"Share Success" = "แบ่งปันความสำเร็จ!";
"OK" = "แน่นอน";
"COPY" = "โคลน";
"New version found" = "พบเวอร์ชันใหม่แล้ว";
"Update" = "ไปอัพเดทครับ";
"My Parent Invitation Code" = "รหัสเชิญผู้ปกครองของฉัน";
"RECORDS" = "บันทึกการเชิญ";
"Balance" = "สมดุล";
"Income" = "รายได้";
"EditBank" = "แก้ไขบัตรธนาคาร";
"RealName" = "ชื่อผู้ถือบัตร";
"BankName" = "ชื่อธนาคารที่เปิด";
"BankNO" = "หมายเลขบัตรธนาคาร";
"PLS" = "โปรด";
"InputEdit" = "เข้า";
"Withdraw" = "ถอนเงินสด";
"Amount" = "จำนวน";
"WithdrawRecord" = "บันทึกการถอนเงิน";
"Choose" = "เลือก";
"OverCanWithdraw" = "เกินกว่าจะถอนได้";
"Area" = "ภูมิภาคของประเทศ";
"OpenImage" = "Open Image";
"OpenLink" = "Open Link";

View File

@@ -0,0 +1,6 @@
/* Class = "UIButton"; configuration.title = "进入App"; ObjectID = "9sI-nt-sPs"; */
"9sI-nt-sPs.configuration.title" = "进入App";
/* Class = "UIButton"; normalTitle = "Button"; ObjectID = "9sI-nt-sPs"; */
"9sI-nt-sPs.normalTitle" = "Button";

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,42 @@
/*
Localizable.strings
ShenQi
Created by Yao on 2024/11/4.
*/
"Tips" = "gợi ý";
"Need to enable push permission" = "Vui lòng bật quyền đẩy";
"SETTING" = "Đi tới cài đặt";
"Contacts permission must be turned on" = "Vui lòng kích hoạt quyền sổ địa chỉ";
"Please enter the invitation code" = "Vui lòng nhập mã mời";
"INPUT" = "để điền vào";
"HOMEPAGE" = "Trang chủ";
"MY INVITE CODE" = "mã mời của tôi";
"SHARE" = "chia sẻ";
"CANCEL" = "Hủy bỏ";
"Invite you to download" = "Mời bạn tải về";
"Share Success" = "Chia sẻ thành công!";
"OK" = "Chắc chắn";
"COPY" = "sao chép";
"New version found" = "phiên bản mới được tìm thấy";
"Update" = "Đi tới cập nhật";
"My Parent Invitation Code" = "Mã mời phụ huynh của tôi";
"RECORDS" = "mời hồ sơ";
"Balance" = "Sự cân bằng";
"Income" = "thu nhập";
"EditBank" = "Chỉnh sửa thẻ ngân hàng";
"RealName" = "Tên chủ thẻ";
"BankName" = "Tên ngân hàng mở";
"BankNO" = "Số thẻ ngân hàng";
"PLS" = "Xin vui lòng";
"InputEdit" = "đi vào";
"Withdraw" = "Rút tiền mặt";
"Amount" = "Số lượng";
"WithdrawRecord" = "Hồ sơ rút tiền";
"Choose" = "chọn";
"OverCanWithdraw" = "Có thể rút nhiều hơn";
"Area" = "vùng đất nước";
"OpenImage" = "Open Image";
"OpenLink" = "Open Link";

View File

@@ -0,0 +1,6 @@
/* Class = "UIButton"; configuration.title = "进入App"; ObjectID = "9sI-nt-sPs"; */
"9sI-nt-sPs.configuration.title" = "进入App";
/* Class = "UIButton"; normalTitle = "Button"; ObjectID = "9sI-nt-sPs"; */
"9sI-nt-sPs.normalTitle" = "Button";

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,45 @@
/*
Localizable.strings
ShenQi
Created by Yao on 2024/11/4.
*/
"Tips" = "提示";
"Need to enable push permission" = "请开启推送权限";
"SETTING" = "去设置";
"Contacts permission must be turned on" = "请开启通讯录权限";
"Please enter the invitation code" = "请输入邀请码";
"INPUT" = "去填写";
"HOMEPAGE" = "主页";
"MY INVITE CODE" = "我的邀请码";
"SHARE" = "分享";
"CANCEL" = "取消";
"Invite you to download" = "邀请您下载";
"Share Success" = "分享成功!";
"OK" = "确定";
"COPY" = "复制";
"New version found" = "发现新版本";
"Update" = "去更新";
"My Parent Invitation Code" = "我的上级邀请码";
"RECORDS" = "邀请记录";
"Balance" = "余额";
"Income" = "总收益";
"EditBank" = "编辑银行卡";
"RealName" = "持卡人姓名";
"BankName" = "开户行名称";
"BankNO" = "户口";
"PLS" = "请";
"InputEdit" = "输入";
"Withdraw" = "提现";
"Amount" = "金额";
"WithdrawRecord" = "提现记录";
"Choose" = "选择";
"OverCanWithdraw" = "超过可提现";
"Area" = "国家地区";
"OpenImage" = "打开图片";
"OpenLink" = "打开链接";
"Share With WhatsApp" = "WhatsApp 客服";
"Share With Telegram" = "Telegram 客服";
"Share With Facebook" = "Facebook 客服";

View File

@@ -0,0 +1,6 @@
/* Class = "UIButton"; configuration.title = "进入App"; ObjectID = "9sI-nt-sPs"; */
"9sI-nt-sPs.configuration.title" = "进入App";
/* Class = "UIButton"; normalTitle = "Button"; ObjectID = "9sI-nt-sPs"; */
"9sI-nt-sPs.normalTitle" = "Button";

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,45 @@
/*
Localizable.strings
ShenQi
Created by Yao on 2024/11/4.
*/
"Tips" = "提示";
"Need to enable push permission" = "請開啟推送權限";
"SETTING" = "去設置";
"Contacts permission must be turned on" = "請開啟通訊錄權限";
"Please enter the invitation code" = "請輸入邀請碼";
"INPUT" = "去填寫";
"HOMEPAGE" = "主頁";
"MY INVITE CODE" = "我的邀請碼";
"SHARE" = "分享";
"CANCEL" = "取消";
"Invite you to download" = "邀請您下載";
"Share Success" = "分享成功!";
"OK" = "確定";
"COPY" = "複製";
"New version found" = "發現新版本";
"Update" = "去更新";
"My Parent Invitation Code" = "我的上級邀請碼";
"RECORDS" = "邀請記錄";
"Balance" = "餘額";
"Income" = "總收益";
"EditBank" = "編輯銀行卡";
"RealName" = "持卡人姓名";
"BankName" = "開戶行名稱";
"BankNO" = "戶口";
"PLS" = "請";
"InputEdit" = "輸入";
"Withdraw" = "提現";
"Amount" = "金額";
"WithdrawRecord" = "提現記錄";
"Choose" = "選擇";
"OverCanWithdraw" = "超過可提現";
"Area" = "國家地區";
"OpenImage" = "打開圖片";
"OpenLink" = "打開鏈接";
"Share With WhatsApp" = "WhatsApp 客服";
"Share With Telegram" = "Telegram 客服";
"Share With Facebook" = "Facebook 客服";

View File

@@ -0,0 +1,6 @@
/* Class = "UIButton"; configuration.title = "进入App"; ObjectID = "9sI-nt-sPs"; */
"9sI-nt-sPs.configuration.title" = "进入App";
/* Class = "UIButton"; normalTitle = "Button"; ObjectID = "9sI-nt-sPs"; */
"9sI-nt-sPs.normalTitle" = "Button";