Commit 4fcb2bc6 cgx

完成首页活动运营及首次启动弹框

1 个父辈 bd3d46eb
......@@ -15,7 +15,7 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D0B5ECA027F2D9DE003EDFE3"
BuildableName = "DreamSleep.app"
BuildableName = "&#x5c0f;&#x68a6;&#x7761;&#x7720;-&#x51a5;&#x60f3;&#x89e3;&#x538b;&#x52a9;&#x7720;&#x5065;&#x5eb7;&#x517b;&#x751f;.app"
BlueprintName = "DreamSleep"
ReferencedContainer = "container:DreamSleep.xcodeproj">
</BuildableReference>
......@@ -45,7 +45,7 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D0B5ECA027F2D9DE003EDFE3"
BuildableName = "DreamSleep.app"
BuildableName = "&#x5c0f;&#x68a6;&#x7761;&#x7720;-&#x51a5;&#x60f3;&#x89e3;&#x538b;&#x52a9;&#x7720;&#x5065;&#x5eb7;&#x517b;&#x751f;.app"
BlueprintName = "DreamSleep"
ReferencedContainer = "container:DreamSleep.xcodeproj">
</BuildableReference>
......@@ -62,7 +62,7 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D0B5ECA027F2D9DE003EDFE3"
BuildableName = "DreamSleep.app"
BuildableName = "&#x5c0f;&#x68a6;&#x7761;&#x7720;-&#x51a5;&#x60f3;&#x89e3;&#x538b;&#x52a9;&#x7720;&#x5065;&#x5eb7;&#x517b;&#x751f;.app"
BlueprintName = "DreamSleep"
ReferencedContainer = "container:DreamSleep.xcodeproj">
</BuildableReference>
......
......@@ -15,7 +15,7 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D0B5ECA027F2D9DE003EDFE3"
BuildableName = "DreamSleep.app"
BuildableName = "&#x5c0f;&#x68a6;&#x7761;&#x7720;-&#x51a5;&#x60f3;&#x89e3;&#x538b;&#x52a9;&#x7720;&#x5065;&#x5eb7;&#x517b;&#x751f;.app"
BlueprintName = "DreamSleep"
ReferencedContainer = "container:DreamSleep.xcodeproj">
</BuildableReference>
......@@ -45,7 +45,7 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D0B5ECA027F2D9DE003EDFE3"
BuildableName = "DreamSleep.app"
BuildableName = "&#x5c0f;&#x68a6;&#x7761;&#x7720;-&#x51a5;&#x60f3;&#x89e3;&#x538b;&#x52a9;&#x7720;&#x5065;&#x5eb7;&#x517b;&#x751f;.app"
BlueprintName = "DreamSleep"
ReferencedContainer = "container:DreamSleep.xcodeproj">
</BuildableReference>
......@@ -62,7 +62,7 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D0B5ECA027F2D9DE003EDFE3"
BuildableName = "DreamSleep.app"
BuildableName = "&#x5c0f;&#x68a6;&#x7761;&#x7720;-&#x51a5;&#x60f3;&#x89e3;&#x538b;&#x52a9;&#x7720;&#x5065;&#x5eb7;&#x517b;&#x751f;.app"
BlueprintName = "DreamSleep"
ReferencedContainer = "container:DreamSleep.xcodeproj">
</BuildableReference>
......
......@@ -15,7 +15,7 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D0B5ECA027F2D9DE003EDFE3"
BuildableName = "DreamSleep.app"
BuildableName = "&#x5c0f;&#x68a6;&#x7761;&#x7720;-&#x51a5;&#x60f3;&#x89e3;&#x538b;&#x52a9;&#x7720;&#x5065;&#x5eb7;&#x517b;&#x751f;.app"
BlueprintName = "DreamSleep"
ReferencedContainer = "container:DreamSleep.xcodeproj">
</BuildableReference>
......@@ -45,7 +45,7 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D0B5ECA027F2D9DE003EDFE3"
BuildableName = "DreamSleep.app"
BuildableName = "&#x5c0f;&#x68a6;&#x7761;&#x7720;-&#x51a5;&#x60f3;&#x89e3;&#x538b;&#x52a9;&#x7720;&#x5065;&#x5eb7;&#x517b;&#x751f;.app"
BlueprintName = "DreamSleep"
ReferencedContainer = "container:DreamSleep.xcodeproj">
</BuildableReference>
......@@ -62,7 +62,7 @@
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D0B5ECA027F2D9DE003EDFE3"
BuildableName = "DreamSleep.app"
BuildableName = "&#x5c0f;&#x68a6;&#x7761;&#x7720;-&#x51a5;&#x60f3;&#x89e3;&#x538b;&#x52a9;&#x7720;&#x5065;&#x5eb7;&#x517b;&#x751f;.app"
BlueprintName = "DreamSleep"
ReferencedContainer = "container:DreamSleep.xcodeproj">
</BuildableReference>
......
#import <Foundation/Foundation.h>
/** 缓存完成结果回调 */
typedef void(^KBNetworkCacheCompletionBlock) (BOOL result);
/** 文件缓存 */
@interface DSNetworkCache : NSObject
/**
* @brief 写入/更新缓存(同步) [按APP版本号缓存,不同版本APP,同一接口缓存数据互不干扰]
* @param jsonResponse 要写入的数据(JSON)
* @param URL 数据请求URL
* @return 是否写入成功
*/
+ (BOOL)saveJsonResponseToCacheFile:(id)jsonResponse andURL:(NSString *)URL;
/**
* @brief 写入/更新缓存(异步) [按APP版本号缓存,不同版本APP,同一接口缓存数据互不干扰]
* @param jsonResponse 要写入的数据(JSON)
* @param URL 数据请求URL
* @param completedBlock 异步完成回调(主线程回调)
*/
+ (void)save_asyncJsonResponseToCacheFile:(id)jsonResponse andURL:(NSString *)URL completed:(KBNetworkCacheCompletionBlock)completedBlock;
/**
* @brief 获取缓存的对象(同步)
* @param URL 数据请求URL
* @return 缓存对象
*/
+ (id)cacheJsonWithURL:(NSString *)URL;
/** 获取缓存路径 */
+ (NSString *)cachePath;
/** 清除缓存 */
+ (BOOL)clearCache;
/** 获取缓存大小(单位:M) */
+ (CGFloat)cacheSize;
/**
* @brief 获取缓存大小,(以..kb/..M)形式获取
* 小于1M,以kb形式返回,大于1M,以M形式返回
* @return 缓存大小+单位
*/
+ (NSString *)cacheSizeFormat;
@end
#import "DSNetworkCache.h"
#import <CommonCrypto/CommonDigest.h>
@implementation DSNetworkCache
+ (BOOL)saveJsonResponseToCacheFile:(id)jsonResponse andURL:(NSString *)URL
{
if(jsonResponse == nil) return NO;
NSData *data = [self jsonToData:jsonResponse];
return [[NSFileManager defaultManager] createFileAtPath:[self cacheFilePathWithURL:URL] contents:data attributes:nil];
}
+ (void)save_asyncJsonResponseToCacheFile:(id)jsonResponse andURL:(NSString *)URL completed:(KBNetworkCacheCompletionBlock)completedBlock
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
BOOL result = [self saveJsonResponseToCacheFile:jsonResponse andURL:URL];
dispatch_async(dispatch_get_main_queue(), ^{
if(completedBlock) completedBlock(result);
});
});
}
/** 默认缓存日期 */
static const NSInteger kDefaultCacheMaxCacheAge = 60 * 60 * 24; // 1 day
+ (id)cacheJsonWithURL:(NSString *)URL
{
NSString *path = [self cacheFilePathWithURL:URL];
// 是否存在缓存文件
if ([[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:nil] == YES) {
// 缓存文件最近修改日期
NSDictionary *fileURLAttributes = [[NSURL fileURLWithPath:path] resourceValuesForKeys:[NSArray arrayWithObjects:NSURLContentModificationDateKey, nil] error:nil];
NSDate *lastModifiedDate = [fileURLAttributes objectForKey:NSURLContentModificationDateKey];
NSDateFormatter *dateFormat = [[NSDateFormatter alloc] init];
[dateFormat setDateFormat:@"yyyy-MM-dd"];
NSString *lastModifiedDateStr = [dateFormat stringFromDate:lastModifiedDate];
// (当前日期-缓存时效)
NSDate *expirationDate = [NSDate dateWithTimeIntervalSinceNow:-kDefaultCacheMaxCacheAge];
NSString *expirationDateStr = [dateFormat stringFromDate:expirationDate];
DSLog(@"lastModifiedDateStr=%@, expirationDateStr=%@", lastModifiedDateStr, expirationDateStr);
if ([lastModifiedDateStr isEqualToString:expirationDateStr]) {
return nil;
}
NSData *data = [[NSFileManager defaultManager] contentsAtPath:path];
if (data == nil) return nil;
return [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil];
}
return nil;
}
+ (NSString *)cacheFilePathWithURL:(NSString *)URL
{
NSString *path = [self cachePath];
// checkDirectory
[self checkDirectory:path];
// fileName
NSString *cacheFileNameString = [NSString stringWithFormat:@"URL:%@ AppVersion:%@", URL, [self appVersionString]];
NSString *cacheFileName = [self md5StringFromString:cacheFileNameString];
path = [path stringByAppendingPathComponent:cacheFileName];
return path;
}
+ (NSData *)jsonToData:(id)jsonResponse
{
if(jsonResponse == nil) return nil;
NSError *error;
NSData *data = [NSJSONSerialization dataWithJSONObject:jsonResponse options:NSJSONWritingPrettyPrinted error:&error];
if (error) {
DSLog(@"ERROR, faild to get json data");
return nil;
}
return data;
}
+ (NSString *)cachePath
{
NSString *pathOfLibrary = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSString *path = [pathOfLibrary stringByAppendingPathComponent:[NSString stringWithFormat:@"%@_%d", @"query_promotion_image", [LoginUtils getUserID]]];
return path;
}
+ (void)checkDirectory:(NSString *)path
{
NSFileManager *fileManager = [NSFileManager defaultManager];
BOOL isDir;
if (![fileManager fileExistsAtPath:path isDirectory:&isDir]) {
[self createBaseDirectoryAtPath:path];
} else {
if (!isDir) {
NSError *error = nil;
[fileManager removeItemAtPath:path error:&error];
[self createBaseDirectoryAtPath:path];
}
}
}
+ (void)createBaseDirectoryAtPath:(NSString *)path {
__autoreleasing NSError *error = nil;
[[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:YES
attributes:nil error:&error];
if (error) {
DSLog(@"create cache directory failed, error = %@", error);
} else {
// DSLog(@"path = %@", path);
[self addDoNotBackupAttribute:path];
}
}
+ (void)addDoNotBackupAttribute:(NSString *)path {
NSURL *url = [NSURL fileURLWithPath:path];
NSError *error = nil;
[url setResourceValue:[NSNumber numberWithBool:YES] forKey:NSURLIsExcludedFromBackupKey error:&error];
if (error) {
DSLog(@"error to set do not backup attribute, error = %@", error);
}
}
+ (NSString *)md5StringFromString:(NSString *)string {
if(string == nil || [string length] == 0) return nil;
const char *value = [string UTF8String];
unsigned char outputBuffer[CC_MD5_DIGEST_LENGTH];
CC_MD5(value, (CC_LONG)strlen(value), outputBuffer);
NSMutableString *outputString = [[NSMutableString alloc] initWithCapacity:CC_MD5_DIGEST_LENGTH * 2];
for(NSInteger count = 0; count < CC_MD5_DIGEST_LENGTH; count++) {
[outputString appendFormat:@"%02x", outputBuffer[count]];
}
return outputString;
}
+ (NSString *)appVersionString {
return [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
}
+ (BOOL)clearCache
{
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *path = [self cachePath];
BOOL result = [fileManager removeItemAtPath:path error:nil];
[self checkDirectory:[self cachePath]];
return result;
}
+ (CGFloat)cacheSize
{
NSString *directoryPath = [self cachePath];
BOOL isDir = NO;
unsigned long long total = 0;
if ([[NSFileManager defaultManager] fileExistsAtPath:directoryPath isDirectory:&isDir]) {
if (isDir) {
NSError *error = nil;
NSArray *array = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:directoryPath error:&error];
if (error == nil) {
for (NSString *subpath in array) {
NSString *path = [directoryPath stringByAppendingPathComponent:subpath];
NSDictionary *dict = [[NSFileManager defaultManager] attributesOfItemAtPath:path
error:&error];
if (!error) {
total += [dict[NSFileSize] unsignedIntegerValue];
}
}
}
}
}
return total/(1024.0*1024.0);
}
+ (NSString *)cacheSizeFormat
{
NSString *sizeUnitString;
float size = [self cacheSize];
if(size < 1) {
size *= 1024.0; // 小于1M转化为kb
sizeUnitString = [NSString stringWithFormat:@"%.1fkb", size];
} else {
sizeUnitString = [NSString stringWithFormat:@"%.1fM", size];
}
return sizeUnitString;
}
@end
......@@ -17,6 +17,8 @@ FOUNDATION_EXTERN NSString * const FirstShowPrivacy;
// 引导页
FOUNDATION_EXTERN NSString * const IsFirstGuide;
// 首次启动APP进入主页弹框
FOUNDATION_EXTERN NSString * const IsFirstAlert;
// 关于我们
FOUNDATION_EXTERN NSString * const AboutUS;
......
......@@ -13,6 +13,7 @@ NSString * const PrivacyStatement = @"欢迎来到小梦睡眠,我们依据最
NSString * const FirstShowPrivacy = @"FirstShowPrivacy";
NSString * const IsFirstGuide = @"isFirstGuide";
NSString * const IsFirstAlert = @"isFirstAlert";
NSString * const AboutUS = @"https://www.cbti.cn/sleep/user/about";
NSString * const MYCBTI = @"https://www.cbti.cn/sleep/ssmian/cbti";
......
......@@ -46,6 +46,13 @@
// Remove
#define kUserDefaultsRemoveObj(key) [[NSUserDefaults standardUserDefaults] removeObjectForKey:key]
#pragma mark - 沙盒
// Document路径
#define kDocumentPath [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]
#pragma mark - 运营弹框标记
#define kPromotionTime [NSString stringWithFormat:@"%@/%@_%d_%@", kDocumentPath, @"query_promotion_image", [LoginUtils getUserID], DSAppVersion]
NS_ASSUME_NONNULL_BEGIN
@interface MacroFuncUtil : NSObject
@end
......
......@@ -9,7 +9,7 @@
NS_ASSUME_NONNULL_BEGIN
/// 三分钟即刻入睡VC
/// 三分钟即刻入睡VC(哄睡界面)
@interface ThreeMinuteController : UIViewController
@end
......
......@@ -29,7 +29,7 @@
[self addSubview:self.titleLab];
[self addSubview:self.aiBtn];
[self addSubview:self.ssBtn];
[self.closeBtn mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.equalTo(self).offset(15);
make.top.equalTo(self).offset(51);
......@@ -60,6 +60,9 @@
}
- (void)dismiss {
kSetUserDefaultsBOOL(YES, IsFirstAlert);
kUserDefaultsSynchronize;
[self removeFromSuperview];
}
......
......@@ -19,6 +19,7 @@ typedef void (^JumpBlock)(void);
- (instancetype)initWithJumpBlock:(JumpBlock)block imageUrl:(NSString *)imageUrl;
- (void)show;
- (void)dismiss;
@end
......
......@@ -45,11 +45,13 @@
}
- (void)dismiss {
// 标记活动弹框时间,用于判断弹框时效
[[NSFileManager defaultManager] createFileAtPath:kPromotionTime contents:nil attributes:nil];
[self removeFromSuperview];
}
- (void)tapAction {
[self dismiss];
if (self.block) { self.block(); }
}
......
......@@ -11,9 +11,11 @@
#import "FirstLeadAlertView.h"
#import "UserRequestModel.h"
#import "HomeRequestModel.h"
#import "ThreeMinuteController.h"
@interface HomeViewController ()
@property (nonatomic, strong) HomeTableView *homeTV;
@property (nonatomic, strong) RescuePlanView *rescuePlanView;
@end
@implementation HomeViewController
......@@ -36,49 +38,97 @@
}];
}
// 运营活动弹框时机(已登录还是未登录)
// [self queryPromotionImageRequest];
// 活动运营弹框(首次启动不弹框,后面每隔24个小时请求后台接口)
[self promotionAlert];
// APP首次启动弹框(只出现一次)
[self showFirstLeadAlertView];
}
#pragma mark - APP首次启动弹框
- (void)showFirstLeadAlertView {
if (kGetUserDefaultsBOOL(IsFirstAlert)) { return; }
FirstLeadAlertView *view = [[FirstLeadAlertView alloc] initWithJumpBlock:^(JumpType type) {
if (type == JumpTypeAI) {
// 切换到AI睡眠教练标签
self.tabBarController.selectedIndex = 1;
} else {
// 进入快速入眠页面
// 进入快速入眠页面(首页)
self.tabBarController.selectedIndex = 0;
}
}];
[view show];
}
#pragma mark - 运营活动弹窗接口
#pragma mark - 运营活动相关
- (void)promotionAlert {
if (kGetUserDefaultsBOOL(IsFirstAlert) == NO) {
return;
}
if ([[NSFileManager defaultManager] fileExistsAtPath:kPromotionTime isDirectory:nil]) {
// 缓存文件最近修改日期
NSDictionary *fileURLAttributes = [[NSURL fileURLWithPath:kPromotionTime] resourceValuesForKeys:[NSArray arrayWithObjects:NSURLContentModificationDateKey, nil] error:nil];
NSDate *lastModifiedDate = [fileURLAttributes objectForKey:NSURLContentModificationDateKey];
// 活动更新时效24小时
NSInteger updateTime = 60 * 60 * 24;
NSDate *expirationDate = [NSDate dateWithTimeIntervalSinceNow:-updateTime];
NSComparisonResult result = [expirationDate compare:lastModifiedDate];
// 运营活动弹框时机(每天调用1次)
if (result == NSOrderedDescending) {
[self queryPromotionImageRequest];
}
} else {
[self queryPromotionImageRequest];
}
}
- (void)queryPromotionImageRequest {
[HomeRequestModel queryPromotionImageWithCompletion:^(HomeRequestModel * _Nonnull requestModel) {
if (requestModel.resCode == DSResCodeSuccess) {
PromotionModel *pModel = requestModel.promotionModel;
[self showRescuePlanView:pModel];
if (pModel) { [self showRescuePlanView:pModel]; }
}
}];
}
#pragma mark - 显示失眠拯救计划弹框
- (void)showRescuePlanView:(PromotionModel *)pModel {
BOOL isNative = [pModel.page_url containsString:@"native"];
WS(weakSelf);
RescuePlanView *rescuePlanView = [[RescuePlanView alloc] initWithJumpBlock:^{
// 跳转到原生页面
if (isNative) {
int type = [[[pModel.page_url componentsSeparatedByString:@"type="] lastObject] intValue];
} else {
// 跳转到H5页面
[weakSelf.navigationController pushViewController:[[DsWebController alloc] initWithTitle:pModel.title link:pModel.page_url] animated:YES];
if (pModel.login_flag == 1) { // 需要用户登录才可以点击进入相应界面
// 判断是否登录成功
if ([LoginUtils getUserLoginData]) {
[self jumpToActiveInterface:pModel];
} else {
// 跳转到登录页面
[LoginUtils jumpToLoginControllerWithTarget:self];
}
} else { // 不需要登录也可以进入相应界面
[self jumpToActiveInterface:pModel];
}
} imageUrl:pModel.image_url];
[rescuePlanView show];
self.rescuePlanView = rescuePlanView;
[self.rescuePlanView show];
}
- (void)jumpToActiveInterface:(PromotionModel *)pModel {
[self.rescuePlanView dismiss];
WS(weakSelf);
BOOL isNative = [pModel.page_url containsString:@"native"];
// 跳转到原生页面
if (isNative) {
int type = [[[pModel.page_url componentsSeparatedByString:@"type="] lastObject] intValue];
if (type == 1) { // AI睡眠教练
// 切换到AI睡眠教练标签
self.tabBarController.selectedIndex = 1;
} else if (type == 2) { // 首页
self.tabBarController.selectedIndex = 0;
} else if (type == 3) { // 哄睡页面(未登录就可以进去)
ThreeMinuteController *threeVC = [[ThreeMinuteController alloc] init];
[self.navigationController pushViewController:threeVC animated:YES];
}
} else {
// 跳转到H5页面
[weakSelf.navigationController pushViewController:[[DsWebController alloc] initWithTitle:pModel.title link:pModel.page_url] animated:YES];
}
}
#pragma mark - 导航栏日间、黑夜模式
......
支持 Markdown 格式
你添加了 0 到此讨论。请谨慎行事。
Finish editing this message first!