refactor: 更新 AppDelegate 和模块导入以简化配置管理

主要变更:
1. 移除不必要的模块导入,简化 AppDelegate 中的代码结构。
2. 引入新的 EPConfigManager 和 EPNIMManager,统一配置管理和 NIMSDK 初始化逻辑。
3. 更新相关方法以使用 block 回调,提升代码的可读性和维护性。
4. 新增 EPClientAPIBridge 和相关配置文件,增强项目的模块化。

此更新旨在提升代码的可维护性,减少冗余实现,确保配置管理的一致性。
This commit is contained in:
edwinQQQ
2025-10-20 18:07:44 +08:00
parent 4256e01820
commit 681b011c99
15 changed files with 583 additions and 81 deletions

View File

@@ -416,6 +416,12 @@
4C1392962D6DA22B00A6DFB5 /* RechargerTransferHistoryViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C1392952D6DA22B00A6DFB5 /* RechargerTransferHistoryViewController.m */; };
4C13929D2D70441500A6DFB5 /* giftgift.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = 4C13929B2D70441500A6DFB5 /* giftgift.mp4 */; };
4C1392A12D71675900A6DFB5 /* coincoin.mp4 in Resources */ = {isa = PBXBuildFile; fileRef = 4C1392A02D71675900A6DFB5 /* coincoin.mp4 */; };
4C14398D2EA62D3C0037B074 /* EPClientAPIBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C14398C2EA62D3C0037B074 /* EPClientAPIBridge.m */; };
4C1439912EA62DD30037B074 /* EPConfigManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C14398F2EA62DD30037B074 /* EPConfigManager.swift */; };
4C1439922EA62DD30037B074 /* EPConfigStorage.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C1439902EA62DD30037B074 /* EPConfigStorage.m */; };
4C1439942EA630490037B074 /* EPConfigAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4C1439932EA630490037B074 /* EPConfigAPI.swift */; };
4C14399C2EA635EB0037B074 /* EPNIMConfig.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C1439972EA635EB0037B074 /* EPNIMConfig.m */; };
4C14399D2EA635EB0037B074 /* EPNIMManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C1439992EA635EB0037B074 /* EPNIMManager.m */; };
4C1892992CF84349004D4426 /* RoomCahtCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C1892982CF84349004D4426 /* RoomCahtCell.m */; };
4C1A141B2DCB4AB700B6D0CA /* ChatFaceVo.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C1A141A2DCB4AB700B6D0CA /* ChatFaceVo.m */; };
4C1E98BF2E9A3A540031AE79 /* EPMineAPIHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C1E98BD2E9A3A540031AE79 /* EPMineAPIHelper.m */; };
@@ -2478,6 +2484,16 @@
4C1392952D6DA22B00A6DFB5 /* RechargerTransferHistoryViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RechargerTransferHistoryViewController.m; sourceTree = "<group>"; };
4C13929B2D70441500A6DFB5 /* giftgift.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = giftgift.mp4; sourceTree = "<group>"; };
4C1392A02D71675900A6DFB5 /* coincoin.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = coincoin.mp4; sourceTree = "<group>"; };
4C14398B2EA62D3C0037B074 /* EPClientAPIBridge.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EPClientAPIBridge.h; sourceTree = "<group>"; };
4C14398C2EA62D3C0037B074 /* EPClientAPIBridge.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EPClientAPIBridge.m; sourceTree = "<group>"; };
4C14398E2EA62D8C0037B074 /* EPConfigStorage.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EPConfigStorage.h; sourceTree = "<group>"; };
4C14398F2EA62DD30037B074 /* EPConfigManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EPConfigManager.swift; sourceTree = "<group>"; };
4C1439902EA62DD30037B074 /* EPConfigStorage.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EPConfigStorage.m; sourceTree = "<group>"; };
4C1439932EA630490037B074 /* EPConfigAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EPConfigAPI.swift; sourceTree = "<group>"; };
4C1439962EA635EB0037B074 /* EPNIMConfig.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EPNIMConfig.h; sourceTree = "<group>"; };
4C1439972EA635EB0037B074 /* EPNIMConfig.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EPNIMConfig.m; sourceTree = "<group>"; };
4C1439982EA635EB0037B074 /* EPNIMManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EPNIMManager.h; sourceTree = "<group>"; };
4C1439992EA635EB0037B074 /* EPNIMManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = EPNIMManager.m; sourceTree = "<group>"; };
4C1892972CF84349004D4426 /* RoomCahtCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RoomCahtCell.h; sourceTree = "<group>"; };
4C1892982CF84349004D4426 /* RoomCahtCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RoomCahtCell.m; sourceTree = "<group>"; };
4C1A14192DCB4AB700B6D0CA /* ChatFaceVo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ChatFaceVo.h; sourceTree = "<group>"; };
@@ -6465,6 +6481,7 @@
4C0642922E98EF0A00BAF413 /* E-P */ = {
isa = PBXGroup;
children = (
4C14399B2EA635EB0037B074 /* SDKManager */,
4C59C0FC2EA2508F00D1F7BD /* NewMessage */,
4CD19C852E9CB31C0069DAA0 /* NewLogin */,
4C1E98C22E9A45160031AE79 /* Common */,
@@ -6485,6 +6502,25 @@
path = Services;
sourceTree = "<group>";
};
4C14399A2EA635EB0037B074 /* NIMSDK */ = {
isa = PBXGroup;
children = (
4C1439962EA635EB0037B074 /* EPNIMConfig.h */,
4C1439972EA635EB0037B074 /* EPNIMConfig.m */,
4C1439982EA635EB0037B074 /* EPNIMManager.h */,
4C1439992EA635EB0037B074 /* EPNIMManager.m */,
);
path = NIMSDK;
sourceTree = "<group>";
};
4C14399B2EA635EB0037B074 /* SDKManager */ = {
isa = PBXGroup;
children = (
4C14399A2EA635EB0037B074 /* NIMSDK */,
);
path = SDKManager;
sourceTree = "<group>";
};
4C1E98BE2E9A3A540031AE79 /* Services */ = {
isa = PBXGroup;
children = (
@@ -6497,6 +6533,12 @@
4C1E98C22E9A45160031AE79 /* Common */ = {
isa = PBXGroup;
children = (
4C1439932EA630490037B074 /* EPConfigAPI.swift */,
4C14398F2EA62DD30037B074 /* EPConfigManager.swift */,
4C1439902EA62DD30037B074 /* EPConfigStorage.m */,
4C14398E2EA62D8C0037B074 /* EPConfigStorage.h */,
4C14398B2EA62D3C0037B074 /* EPClientAPIBridge.h */,
4C14398C2EA62D3C0037B074 /* EPClientAPIBridge.m */,
4C1E98C72E9A4DFD0031AE79 /* EPQCloudConfig.swift */,
4C1E98C82E9A4DFD0031AE79 /* EPSDKManager.swift */,
4C1E98C02E9A45160031AE79 /* EPImageUploader.swift */,
@@ -12526,6 +12568,9 @@
4C1A141B2DCB4AB700B6D0CA /* ChatFaceVo.m in Sources */,
E8664ED627E434D5000171BA /* XPRoomPKRecordViewController.m in Sources */,
E87E914E2796678D00A7B3F2 /* XPMineDressEmptyTableViewCell.m in Sources */,
4C14398D2EA62D3C0037B074 /* EPClientAPIBridge.m in Sources */,
4C14399C2EA635EB0037B074 /* EPNIMConfig.m in Sources */,
4C14399D2EA635EB0037B074 /* EPNIMManager.m in Sources */,
232EBBFF2BD7A25500E8CEAD /* MSParamsDecode.m in Sources */,
9B7D804D27537950003DAC0C /* MessageCell.m in Sources */,
23E9EAA62A84C97C00B792F2 /* XPMineUserInfoTagVC.m in Sources */,
@@ -12849,6 +12894,7 @@
E8751E5928A62A390056EF44 /* Api+Sailing.m in Sources */,
E897ABFF28AF39B4003B3587 /* XPSailingAnimationView.m in Sources */,
E885D5362977CE28004DC088 /* SessionSettingModel.m in Sources */,
4C1439942EA630490037B074 /* EPConfigAPI.swift in Sources */,
9BE01AE128937DBC00B50299 /* XPDressUpShopCardViewController.m in Sources */,
E896EFA22771AE9400AD2CC1 /* XPMineFriendViewController.m in Sources */,
1427218D29A75F6F00C7C423 /* HTTPRedirectResponse.m in Sources */,
@@ -13108,6 +13154,8 @@
2331C1742A5EB71000E1D940 /* XPNobleCenterResidueView.m in Sources */,
4C0A5B842E02675300955219 /* MedalsCollectionViewCell.m in Sources */,
E80E2377299A47F60013FD40 /* AESUtils.m in Sources */,
4C1439912EA62DD30037B074 /* EPConfigManager.swift in Sources */,
4C1439922EA62DD30037B074 /* EPConfigStorage.m in Sources */,
E81060E229876E9100B772F0 /* MessageImageModel.m in Sources */,
E839533F276A0CDB00CF2F24 /* XPMineNameplateTableViewCell.m in Sources */,
E80E09AE2A41336500CD2BE7 /* XPWebViewNavView.m in Sources */,

View File

@@ -7,21 +7,16 @@
#import "AppDelegate.h"
#import "TabbarViewController.h"
#import "BaseNavigationController.h"
#import "AppDelegate+ThirdConfig.h"
#import <NIMSDK/NIMSDK.h>
#import <AppTrackingTransparency/AppTrackingTransparency.h>
#import "ClientConfig.h"
#import "LoginViewController.h"
#import "AccountModel.h"
#import "YuMi-swift.h"
#import "SessionViewController.h"
#import "LoginFullInfoViewController.h"
#import "UIView+VAP.h"
#import "SocialShareManager.h"
#import "EPSignatureColorGuideView.h"
#import "EPEmotionColorStorage.h"
#import "EPNIMManager.h"
UIKIT_EXTERN NSString * const kOpenRoomNotification;
@@ -61,18 +56,29 @@ void qg_VAP_Logger_handler(VAPLogLevel level, const char* file, int line, const
[self.window makeKeyAndVisible];
[VAPView registerHWDLog:qg_VAP_Logger_handler];
/// sdk
[self initThirdConfig];
[self initUM:application launchOptions:launchOptions];
// client/init client/config EPConfigManager
@kWeakify(self);
[[ClientConfig shareConfig] clientConfig:^{
@kStrongify(self);
dispatch_async(dispatch_get_main_queue(), ^{
[[EPConfigManager shared] startColdBootWithOnSuccess:^{
// NIMSDK
[[EPNIMManager sharedManager] initializeWithCompletion:^(NSError * _Nullable error) {
@kStrongify(self);
if (error) {
NSLog(@"[AppDelegate] NIMSDK 初始化失败: %@", error);
} else {
NSLog(@"[AppDelegate] NIMSDK 初始化成功");
}
// NIMSDK
[self loadMainPage];
[self setupLaunchADView];
});
}];
} onFailure:^(NSString * _Nonnull errorMessage) {
@kStrongify(self);
//
UIAlertController *alert = [UIAlertController alertControllerWithTitle:YMLocalizedString(@"提示")
message:errorMessage
preferredStyle:UIAlertControllerStyleAlert];
[alert addAction:[UIAlertAction actionWithTitle:YMLocalizedString(@"确定") style:UIAlertActionStyleDefault handler:nil]];
[self.window.rootViewController presentViewController:alert animated:YES completion:nil];
}];
if (@available(iOS 15, *)) {
@@ -84,11 +90,6 @@ void qg_VAP_Logger_handler(VAPLogLevel level, const char* file, int line, const
// MARK: - Helper Methods
- (void)initUM:(UIApplication *)application
launchOptions:(NSDictionary *)launchOptions {
// MobLink
}
- (void)loadMainPage {
AccountModel *accountModel = [[AccountInfoStorage instance] getCurrentAccountInfo];
if (accountModel == nil ||
@@ -103,10 +104,10 @@ void qg_VAP_Logger_handler(VAPLogLevel level, const char* file, int line, const
[self checkAndShowSignatureColorGuide];
});
}
[[ClientConfig shareConfig] clientInit];
}
// EPConfig block
///
- (void)checkAndShowSignatureColorGuide {
UIWindow *keyWindow = kWindow;
@@ -114,7 +115,7 @@ void qg_VAP_Logger_handler(VAPLogLevel level, const char* file, int line, const
BOOL hasSignatureColor = [EPEmotionColorStorage hasUserSignatureColor];
#if DEBUG
#if 0
// Debug
NSLog(@"[AppDelegate] Debug 模式:显示专属颜色引导页(已有颜色: %@", hasSignatureColor ? @"YES" : @"NO");
@@ -156,16 +157,9 @@ void qg_VAP_Logger_handler(VAPLogLevel level, const char* file, int line, const
[[BaseNavigationController alloc] initWithRootViewController:lvc];
navigationController.modalPresentationStyle = UIModalPresentationFullScreen;
self.window.rootViewController = navigationController;
// 便
// LoginViewController *lvc = [[LoginViewController alloc] init];
// BaseNavigationController * navigationController = [[BaseNavigationController alloc] initWithRootViewController:lvc];
// navigationController.modalPresentationStyle = UIModalPresentationFullScreen;
// self.window.rootViewController = navigationController;
}
- (void)toHomeTabbarPage {
// ========== 使 EPTabBarController ==========
EPTabBarController *epTabBar = [EPTabBarController create];
[epTabBar refreshTabBarWithIsLogin:YES];
@@ -174,19 +168,10 @@ void qg_VAP_Logger_handler(VAPLogLevel level, const char* file, int line, const
window.rootViewController = epTabBar;
[window makeKeyAndVisible];
}
NSLog(@"[AppDelegate] 自动登录后已切换到白牌 TabBarEPTabBarController");
// ========== ==========
/*
TabbarViewController *vc = [[TabbarViewController alloc] init];
BaseNavigationController *navigationController = [[BaseNavigationController alloc] initWithRootViewController:vc];
self.window.rootViewController = navigationController;
*/
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
NSInteger count = [NIMSDK sharedSDK].conversationManager.allUnreadCount;
NSInteger count = [[EPNIMManager sharedManager] allUnreadCount];
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:count];
}
@@ -222,8 +207,8 @@ void qg_VAP_Logger_handler(VAPLogLevel level, const char* file, int line, const
}
- (void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
// devicetoken
[[NIMSDK sharedSDK] updateApnsToken:deviceToken ];
// deviceToken EPNIMManager
[[EPNIMManager sharedManager] updateApnsToken:deviceToken];
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{
@@ -255,45 +240,10 @@ void qg_VAP_Logger_handler(VAPLogLevel level, const char* file, int line, const
///URL Scheme
-(BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString *,id> *)options{
// TODO: EPTabbar [SocialShareManager sharedManager] setHandleJumpToRoom
[[SocialShareManager sharedManager] handleURL:url];
return YES;
}
//- (void)__oldApplicationOpenURLMethod:(NSURL *)url {
// NSString *text = [url query];
// if(text.length){
// NSMutableDictionary *paramsDict = [NSMutableDictionary dictionary];
// NSArray *paramArray = [text componentsSeparatedByString:@"&"];
// for (NSString *param in paramArray) {
// if (param && param.length) {
// NSArray *parArr = [param componentsSeparatedByString:@"="];
// if (parArr.count == 2) {
// [paramsDict setObject:parArr[1] forKey:parArr[0]];
// }
// }
// }
// if(paramsDict[@"type"] != nil){
// NSInteger type = [paramsDict[@"type"] integerValue];
// if (type == 2) {
// NSString *uid = [NSString stringWithFormat:@"%@",paramsDict[@"uid"]];
// [[NSNotificationCenter defaultCenter]postNotificationName:kOpenRoomNotification object:nil userInfo:@{@"uid":uid}];
// ClientConfig *config = [ClientConfig shareConfig];
// config.roomId = uid;
// }else if(type == 7){
// NSString *uid = [NSString stringWithFormat:@"%@",paramsDict[@"uid"]];
// [[NSNotificationCenter defaultCenter]postNotificationName:kOpenRoomNotification object:nil userInfo:@{@"type":@"kOpenChat",@"uid":uid}];
// ClientConfig *config = [ClientConfig shareConfig];
// config.chatId = uid;
// }else if (type == 8){
// NSString *inviteCode = paramsDict[@"inviteCode"];
// if (inviteCode != nil && [[AccountInfoStorage instance]getUid].length == 0){
// ClientConfig *config = [ClientConfig shareConfig];
// config.inviteCode = inviteCode;
// }
// }
//// return YES;
// }
// }
//}
@end

View File

@@ -0,0 +1,26 @@
//
// EPClientAPIBridge.h
// YuMi
//
// Deprecated: replaced by Swift EPConfigAPI
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/// Bridge to wrap existing Objective-C APIs for Swift consumers
__attribute__((deprecated("Use EPConfigAPI (Swift) instead")))
@interface EPClientAPIBridge : NSObject
/// Call Api.clientInitConfig and forward raw dictionary and status
+ (void)clientInit:(void(^)(NSDictionary * _Nullable data, NSInteger code, NSString * _Nullable msg))completion;
/// Call ClientConfig.clientConfig; returns code 200 on success (no payload)
+ (void)clientConfig:(void(^)(NSDictionary * _Nullable data, NSInteger code, NSString * _Nullable msg))completion;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,36 @@
//
// EPClientAPIBridge.m
// YuMi
//
// Objective-C to Swift bridge for client init/config APIs
//
#import "EPClientAPIBridge.h"
#import "Api+Main.h"
#import "ClientConfig.h"
#import "BaseModel.h"
@implementation EPClientAPIBridge
+ (void)clientInit:(void(^)(NSDictionary * _Nullable data, NSInteger code, NSString * _Nullable msg))completion {
if (!completion) { return; }
[Api clientInitConfig:^(BaseModel * _Nullable data, NSInteger code, NSString * _Nullable msg) {
NSDictionary *payload = nil;
if (code == 200 && data.data && [data.data isKindOfClass:[NSDictionary class]]) {
payload = (NSDictionary *)data.data;
}
completion(payload, code, msg);
}];
}
+ (void)clientConfig:(void(^)(NSDictionary * _Nullable data, NSInteger code, NSString * _Nullable msg))completion {
if (!completion) { return; }
// ClientConfig.clientConfig only has a finish block with no parameters; treat success as code 200
[[ClientConfig shareConfig] clientConfig:^{
completion(nil, 200, nil);
}];
}
@end

View File

@@ -0,0 +1,37 @@
//
// EPConfigAPI.swift
// YuMi
//
// Thin Swift wrapper aligning EP module naming, for client/init & client/config
//
import Foundation
@objc final class EPConfigAPI: NSObject {
/// GET client/init returns payload dictionary when code == 200
@objc static func clientInit(
completion: @escaping (_ data: [String: Any]?, _ code: Int, _ msg: String?) -> Void
) {
Api.clientInitConfig { baseModel, code, msg in
var dict: [String: Any]? = nil
if code == 200, let payload = baseModel?.data as? [String: Any] {
dict = payload
}
completion(dict, Int(code), msg)
}
}
/// GET client/config treat success as code 200 (no payload)
@objc static func clientConfig(
completion: @escaping (_ data: [String: Any]?, _ code: Int, _ msg: String?) -> Void
) {
// ClientConfig has + (instancetype)shareConfig; bridged to Swift as .share()
// If the symbol differs, adjust to your Swift name (e.g., shareConfig()).
ClientConfig.share().clientConfig {
completion(nil, 200, nil)
}
}
}

View File

@@ -0,0 +1,133 @@
//
// EPConfigManager.swift
// YuMi
//
// Cold boot configuration manager for client/init and client/config flows
//
import Foundation
@objc final class EPConfigManager: NSObject {
@objc static let shared = EPConfigManager()
// MARK: - State
@objc private(set) var isInitReady: Bool = false
@objc private(set) var isConfigReady: Bool = false
@objc private(set) var isUsingPersistedInit: Bool = false
//
@objc private(set) var initModelRaw: [String: Any]? = nil
@objc private(set) var configModelRaw: [String: Any]? = nil
//
@objc private(set) var clientDataModel: ClientDataModel? = nil
private var hasStarted = false
//
private var successCallback: (() -> Void)?
private var failureCallback: ((String) -> Void)?
// MARK: - Public API
@objc(startColdBootWithOnSuccess:onFailure:)
func startColdBoot(
onSuccess: @escaping () -> Void,
onFailure: @escaping (String) -> Void
) {
guard !hasStarted else {
//
if isInitReady && isConfigReady {
onSuccess()
} else if !isInitReady {
onFailure("配置初始化失败")
}
return
}
hasStarted = true
//
self.successCallback = onSuccess
self.failureCallback = onFailure
runClientInitWithRetry(maxRetry: 5, interval: 1.0)
}
// MARK: - Flow
private func runClientInitWithRetry(maxRetry: Int, interval: TimeInterval) {
attemptClientInit(remaining: maxRetry, interval: interval)
}
private func attemptClientInit(remaining: Int, interval: TimeInterval) {
EPConfigAPI.clientInit { [weak self] data, code, msg in
guard let self = self else { return }
if code == 200, let dict = data {
self.onInitSuccess(dict)
self.runClientConfig()
} else if remaining > 0 {
DispatchQueue.main.asyncAfter(deadline: .now() + interval) {
self.attemptClientInit(remaining: remaining - 1, interval: interval)
}
} else {
self.onInitExhausted()
}
}
}
private func onInitSuccess(_ dict: [String: Any]) {
// 1.
let model = ClientDataModel.model(withJSON: dict)
self.clientDataModel = model
// 2. ClientConfig
ClientConfig.share().configInfo = model
// 3.
_ = EPConfigStorage.saveInit(dict)
// 4.
isUsingPersistedInit = false
initModelRaw = dict
isInitReady = true
// 5. client/config
runClientConfig()
}
private func onInitExhausted() {
if let persistedDict = EPConfigStorage.loadInit() as? [String: Any] {
// 使
let model = ClientDataModel.model(withJSON: persistedDict)
self.clientDataModel = model
ClientConfig.share().configInfo = model
isUsingPersistedInit = true
initModelRaw = persistedDict
isInitReady = true
//
runClientConfig()
} else {
//
failureCallback?("网络异常,请稍后重新启动应用")
}
}
private func runClientConfig() {
EPConfigAPI.clientConfig { [weak self] data, code, msg in
guard let self = self else { return }
if code == 200 {
// client/config
self.isConfigReady = true
self.successCallback?()
} else {
// client/config init
self.isConfigReady = true
self.successCallback?()
}
}
}
}
// Notification

View File

@@ -0,0 +1,27 @@
//
// EPConfigStorage.h
// YuMi
//
// Lightweight persistence for client/init data
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface EPConfigStorage : NSObject
/// Save init payload dictionary to disk (Application Support)
+ (BOOL)saveInit:(NSDictionary *)dict;
/// Load init payload dictionary from disk; returns nil if missing/invalid
+ (NSDictionary * _Nullable)loadInit;
/// Remove persisted init payload
+ (BOOL)clearInit;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,62 @@
//
// EPConfigStorage.m
// YuMi
//
// Lightweight persistence for client/init data
//
#import "EPConfigStorage.h"
@implementation EPConfigStorage
#pragma mark - Public
+ (BOOL)saveInit:(NSDictionary *)dict {
if (![dict isKindOfClass:[NSDictionary class]]) { return NO; }
NSMutableDictionary *wrapped = [dict mutableCopy];
wrapped[@"_version"] = @1;
wrapped[@"_timestamp"] = @((long long)([[NSDate date] timeIntervalSince1970]));
NSData *data = [NSJSONSerialization dataWithJSONObject:wrapped options:0 error:nil];
if (!data) { return NO; }
NSString *path = [self initPath];
NSError *error = nil;
NSFileManager *fm = [NSFileManager defaultManager];
NSString *dir = [path stringByDeletingLastPathComponent];
if (![fm fileExistsAtPath:dir]) {
[fm createDirectoryAtPath:dir withIntermediateDirectories:YES attributes:nil error:&error];
if (error) { return NO; }
}
return [data writeToFile:path atomically:YES];
}
+ (NSDictionary * _Nullable)loadInit {
NSString *path = [self initPath];
NSData *data = [NSData dataWithContentsOfFile:path];
if (!data) { return nil; }
id obj = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
if (![obj isKindOfClass:[NSDictionary class]]) { return nil; }
return (NSDictionary *)obj;
}
+ (BOOL)clearInit {
NSString *path = [self initPath];
NSFileManager *fm = [NSFileManager defaultManager];
if (![fm fileExistsAtPath:path]) { return YES; }
NSError *error = nil;
[fm removeItemAtPath:path error:&error];
return (error == nil);
}
#pragma mark - Helpers
+ (NSString *)initPath {
NSArray<NSURL *> *urls = [[NSFileManager defaultManager] URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask];
NSURL *dirURL = urls.firstObject;
if (!dirURL) { dirURL = [NSURL fileURLWithPath:[NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES) firstObject]]; }
NSURL *fileURL = [dirURL URLByAppendingPathComponent:@"ep_config_init.json"];
return fileURL.path;
}
@end

View File

@@ -0,0 +1,27 @@
//
// EPSDKManager+NIM.swift
// YuMi
//
import Foundation
@objc extension EPSDKManager {
/// NIMSDK ClientConfig nimKey
@objc func initializeNIMSDK(completion: ((NSError?) -> Void)? = nil) {
EPNIMManager.shared().initialize { error in
completion?(error as NSError?)
}
}
/// APNS token NIM
@objc func updateNIMApnsToken(_ deviceToken: Data) {
EPNIMManager.shared().updateApnsToken(deviceToken)
}
/// NIM
@objc func nimUnreadCount() -> Int {
return Int(EPNIMManager.shared().allUnreadCount())
}
}

View File

@@ -0,0 +1,29 @@
//
// EPNIMConfig.h
// YuMi
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
/// NIMSDK 配置模型(从 ClientConfig 派生)
@interface EPNIMConfig : NSObject
@property (nonatomic, copy) NSString *appKey;
@property (nonatomic, copy) NSString *apnsCername;
@property (nonatomic, assign) BOOL shouldConsiderRevokedMessageUnreadCount;
@property (nonatomic, assign) BOOL shouldSyncStickTopSessionInfos;
@property (nonatomic, assign) BOOL enabledHttpsForInfo;
@property (nonatomic, assign) BOOL enabledHttpsForMessage;
@property (nonatomic, assign) NSInteger cdnTrackInterval;
@property (nonatomic, assign) NSInteger chatroomMessageReceiveMinInterval;
/// 从 ClientConfig 创建配置;若缺失 nimKey 则返回 nil
+ (instancetype _Nullable)configFromClientConfig;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,39 @@
//
// EPNIMConfig.m
// YuMi
//
#import "EPNIMConfig.h"
#import "ClientConfig.h"
#import "YUMIConstant.h"
@implementation EPNIMConfig
+ (instancetype _Nullable)configFromClientConfig {
ClientConfig *client = [ClientConfig shareConfig];
if (client.configInfo == nil) {
return nil;
}
NSString *nimKey = client.configInfo.nimKey;
if (nimKey.length == 0) {
return nil;
}
EPNIMConfig *cfg = [[EPNIMConfig alloc] init];
cfg.appKey = nimKey;
#ifdef DEBUG
cfg.apnsCername = @"pikoDevelopPush";
#else
cfg.apnsCername = @"newPiko";
#endif
cfg.shouldConsiderRevokedMessageUnreadCount = YES;
cfg.shouldSyncStickTopSessionInfos = YES;
cfg.enabledHttpsForInfo = YES;
cfg.enabledHttpsForMessage = YES;
cfg.cdnTrackInterval = 0;
cfg.chatroomMessageReceiveMinInterval = 50;
return cfg;
}
@end

View File

@@ -0,0 +1,26 @@
//
// EPNIMManager.h
// YuMi
//
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface EPNIMManager : NSObject
+ (instancetype)sharedManager;
- (void)initializeWithCompletion:(void(^ _Nullable)(NSError * _Nullable error))completion;
- (void)updateApnsToken:(NSData *)deviceToken;
- (NSInteger)allUnreadCount;
- (BOOL)isInitialized;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,61 @@
//
// EPNIMManager.m
// YuMi
//
#import "EPNIMManager.h"
#import "EPNIMConfig.h"
#import <NIMSDK/NIMSDK.h>
#import "CustomAttachmentDecoder.h"
@interface EPNIMManager ()
@property (nonatomic, assign) BOOL initialized;
@end
@implementation EPNIMManager
+ (instancetype)sharedManager {
static EPNIMManager *s;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{ s = [EPNIMManager new]; });
return s;
}
- (void)initializeWithCompletion:(void(^ _Nullable)(NSError * _Nullable error))completion {
if (self.initialized) {
if (completion) completion(nil);
return;
}
EPNIMConfig *cfg = [EPNIMConfig configFromClientConfig];
if (!cfg) {
if (completion) {
completion([NSError errorWithDomain:@"EPNIM" code:-1001 userInfo:@{NSLocalizedDescriptionKey:@"ClientConfig not ready or nimKey missing"}]);
}
return;
}
NIMSDKOption *option = [NIMSDKOption optionWithAppKey:cfg.appKey];
option.apnsCername = cfg.apnsCername;
[[NIMSDK sharedSDK] registerWithOption:option];
[NIMCustomObject registerCustomDecoder:[[CustomAttachmentDecoder alloc] init]];
[NIMSDKConfig sharedConfig].shouldConsiderRevokedMessageUnreadCount = cfg.shouldConsiderRevokedMessageUnreadCount;
[[NIMSDKConfig sharedConfig] setShouldSyncStickTopSessionInfos:cfg.shouldSyncStickTopSessionInfos];
[NIMSDKConfig sharedConfig].enabledHttpsForInfo = cfg.enabledHttpsForInfo;
[NIMSDKConfig sharedConfig].enabledHttpsForMessage = cfg.enabledHttpsForMessage;
[NIMSDKConfig sharedConfig].cdnTrackInterval = cfg.cdnTrackInterval;
[NIMSDKConfig sharedConfig].chatroomMessageReceiveMinInterval = cfg.chatroomMessageReceiveMinInterval;
self.initialized = YES;
if (completion) completion(nil);
}
- (void)updateApnsToken:(NSData *)deviceToken {
if (!deviceToken) return;
[[NIMSDK sharedSDK] updateApnsToken:deviceToken];
}
- (NSInteger)allUnreadCount { return [NIMSDK sharedSDK].conversationManager.allUnreadCount; }
- (BOOL)isInitialized { return self.initialized; }
@end

View File

@@ -20,8 +20,7 @@
///
/// @param complection
+ (void)clientInitConfig:(HttpRequestHelperCompletion)complection {
NSString * fang = [NSString stringFromBase64String:@"Y2xpZW50L2luaXQ="];///client/init
[HttpRequestHelper request:fang method:HttpRequestHelperMethodGET params:@{} completion:complection];
[HttpRequestHelper request:@"client/init" method:HttpRequestHelperMethodGET params:@{} completion:complection];
}
+ (void)clientConfig:(HttpRequestHelperCompletion)completion {

View File

@@ -16,7 +16,8 @@
// MARK: - Foundation
#import <UIKit/UIKit.h>
// MARK: - New Modules (White Label)
// MARK: - New Modules
#import "EPConfigStorage.h"
#import "EPMomentViewController.h"
#import "EPMineViewController.h"
#import "EPMomentCell.h"
@@ -78,6 +79,7 @@
// MARK: - Login - Captcha & Config
#import "ClientConfig.h"
#import "ClientDataModel.h"
#import "TTPopup.h"
// 注意: