Phase 1 Day 1: 基础架构搭建

- 创建 white-label-base 分支
- 添加 APIConfig.swift(API 域名动态生成,XOR + Base64 加密)
  * DEV 环境使用原测试域名
  * RELEASE 环境使用新域名 https://api.epartylive.com
- 添加 Swift/OC 混编支持(YuMi-Bridging-Header.h)
- 创建 GlobalEventManager(全局事件管理器)
  * 迁移 NIMSDK 代理
  * 迁移房间最小化逻辑
  * 迁移全局通知处理
- 创建 NewTabBarController(Swift TabBar,只有 2 个 Tab)
  * Moment Tab
  * Mine Tab
  * 新的主色调和样式
This commit is contained in:
edwinQQQ
2025-10-09 17:48:07 +08:00
parent cebe158f7b
commit e980cd5553
7 changed files with 778 additions and 4 deletions

View File

@@ -0,0 +1,93 @@
//
// APIConfig.swift
// YuMi
//
// Created by AI on 2025-10-09.
// Copyright © 2025 YuMi. All rights reserved.
//
import Foundation
/// API
/// 使 XOR + Base64
@objc class APIConfig: NSObject {
// MARK: - Private Properties
/// XOR
private static let xorKey: UInt8 = 77
/// RELEASE
/// https://api.epartylive.com
private static let releaseEncodedParts: [String] = [
"JTk5PT53YmI=", // https:// (XOR Base64)
"LD0kYw==", // api. (XOR Base64)
"KD0sPzk0ISQ7KGMuIiA=", // epartylive.com (XOR Base64)
]
// MARK: - Public Methods
/// API
/// - Returns:
@objc static func baseURL() -> String {
#if DEBUG
// DEV 使
return HttpRequestHelper.getHostUrl()
#else
// RELEASE 使
let url = decodeURL(from: releaseEncodedParts)
//
if url.isEmpty || !url.hasPrefix("http") {
NSLog("[APIConfig] 警告:域名解密失败,使用备用域名")
return backupURL()
}
return url
#endif
}
///
/// - Returns: 使
@objc static func backupURL() -> String {
return HttpRequestHelper.getHostUrl()
}
// MARK: - Private Methods
///
/// - Parameter parts:
/// - Returns:
private static func decodeURL(from parts: [String]) -> String {
let decoded = parts.compactMap { part -> String? in
guard let data = Data(base64Encoded: part) else {
NSLog("[APIConfig] Base64 解码失败: \(part)")
return nil
}
let xored = data.map { $0 ^ xorKey }
return String(bytes: xored, encoding: .utf8)
}
let result = decoded.joined()
#if DEBUG
NSLog("[APIConfig] 解密后的域名: \(result)")
#endif
return result
}
}
// MARK: - Debug Helper
#if DEBUG
extension APIConfig {
/// /
@objc static func testEncryption() {
print("=== APIConfig 加密测试 ===")
print("Release 域名: \(decodeURL(from: releaseEncodedParts))")
print("当前环境域名: \(baseURL())")
print("备用域名: \(backupURL())")
}
}
#endif

View File

@@ -0,0 +1,71 @@
//
// GlobalEventManager.h
// YuMi
//
// Created by AI on 2025-10-09.
// Copyright © 2025 YuMi. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
/// 全局事件管理器
/// 负责处理原 TabBar 中的全局逻辑SDK 代理、房间最小化、通知等)
@interface GlobalEventManager : NSObject
/// 单例
+ (instancetype)shared;
// MARK: - SDK Delegates Setup
/// 设置所有第三方 SDK 的代理
- (void)setupSDKDelegates;
/// 移除所有代理dealloc 时调用)
- (void)removeAllDelegates;
// MARK: - Room Mini View
/// 设置房间最小化视图
/// @param containerView 父视图(通常是 TabBar 的 view
- (void)setupRoomMiniViewOn:(UIView *)containerView;
/// 处理房间最小化通知
/// @param userInfo 通知携带的数据
- (void)handleRoomMini:(NSDictionary * _Nullable)userInfo;
/// 隐藏房间最小化视图
- (void)hideRoomMiniView;
// MARK: - Global Notifications
/// 处理配置重载通知
- (void)handleConfigReload;
/// 处理新用户充值通知
- (void)handleNewUserRecharge;
/// 处理主播卡片通知
/// @param notification 通知对象
- (void)handleAnchorCard:(NSNotification * _Nullable)notification;
/// 处理语言切换通知
/// @param notification 通知对象
- (void)handleLanguageSwitch:(NSNotification * _Nullable)notification;
// MARK: - User Info
/// 获取用户信息成功后的处理
/// @param userInfo 用户信息模型
- (void)handleUserInfoSuccess:(id)userInfo;
// MARK: - Social Share
/// 注册社交分享回调
- (void)registerSocialShareCallback;
@end
NS_ASSUME_NONNULL_END

View File

@@ -0,0 +1,242 @@
//
// GlobalEventManager.m
// YuMi
//
// Created by AI on 2025-10-09.
// Copyright © 2025 YuMi. All rights reserved.
//
#import "GlobalEventManager.h"
#import "XPMiniRoomView.h"
#import "RoomBoomManager.h"
#import "PublicRoomManager.h"
#import "XPSkillCardPlayerManager.h"
#import "SocialShareManager.h"
#import "YUMIConstant.h"
#import <NIMSDK/NIMSDK.h>
@interface GlobalEventManager () <NIMLoginManagerDelegate, NIMChatManagerDelegate, NIMSystemNotificationManagerDelegate, NIMBroadcastManagerDelegate>
// MARK: - Private Properties
///
@property (nonatomic, strong) XPMiniRoomView *miniRoomView;
///
@property (nonatomic, copy) void(^configReloadCallback)(void);
///
@property (nonatomic, copy) void(^newUserRechargeCallback)(void);
@end
@implementation GlobalEventManager
// MARK: - Lifecycle
+ (instancetype)shared {
static GlobalEventManager *instance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[GlobalEventManager alloc] init];
});
return instance;
}
- (instancetype)init {
if (self = [super init]) {
[self setupNotificationObservers];
}
return self;
}
- (void)dealloc {
[self removeAllDelegates];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
// MARK: - SDK Delegates Setup
- (void)setupSDKDelegates {
// NIMSDK
[[NIMSDK sharedSDK].loginManager addDelegate:self];
[[NIMSDK sharedSDK].chatManager addDelegate:self];
[[NIMSDK sharedSDK].systemNotificationManager addDelegate:self];
[[NIMSDK sharedSDK].broadcastManager addDelegate:self];
// RoomBoomManager
__weak typeof(self) weakSelf = self;
[[RoomBoomManager sharedManager] registerBoomBanner:^(id sth) {
__strong typeof(weakSelf) strongSelf = weakSelf;
if (!strongSelf) return;
dispatch_async(dispatch_get_main_queue(), ^{
//
if ([XPSkillCardPlayerManager shareInstance].isInRoom) {
NSLog(@"[GlobalEventManager] 收到 RoomBoom 通知");
// TODO: Boom Banner
// [RoomBoomBannerAnimation display:window with:sth tapToRoom:YES complete:^{}];
}
});
} target:self];
NSLog(@"[GlobalEventManager] SDK 代理设置完成");
}
- (void)removeAllDelegates {
[[NIMSDK sharedSDK].loginManager removeDelegate:self];
[[NIMSDK sharedSDK].chatManager removeDelegate:self];
[[NIMSDK sharedSDK].systemNotificationManager removeDelegate:self];
[[NIMSDK sharedSDK].broadcastManager removeDelegate:self];
[[RoomBoomManager sharedManager] removeEventListenerForTarget:self];
NSLog(@"[GlobalEventManager] 所有代理已移除");
}
// MARK: - Room Mini View
- (void)setupRoomMiniViewOn:(UIView *)containerView {
if (!self.miniRoomView) {
self.miniRoomView = [[XPMiniRoomView alloc] init];
}
[containerView addSubview:self.miniRoomView];
NSLog(@"[GlobalEventManager] 房间最小化视图已添加");
}
- (void)handleRoomMini:(NSDictionary *)userInfo {
if (self.miniRoomView) {
// TODO:
NSLog(@"[GlobalEventManager] 处理房间最小化: %@", userInfo);
}
}
- (void)hideRoomMiniView {
if (self.miniRoomView) {
[self.miniRoomView hiddenRoomMiniView];
NSLog(@"[GlobalEventManager] 房间最小化视图已隐藏");
}
}
// MARK: - Notification Observers
- (void)setupNotificationObservers {
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
//
[center addObserver:self
selector:@selector(onRoomMiniNotification:)
name:kRoomMiniNotificationKey
object:nil];
//
[center addObserver:self
selector:@selector(onConfigReloadNotification:)
name:@"reloadAfterLoadConfig"
object:nil];
//
[center addObserver:self
selector:@selector(onLanguageSwitchNotification:)
name:@"kSwitchLanguage"
object:nil];
NSLog(@"[GlobalEventManager] 通知监听已设置");
}
- (void)onRoomMiniNotification:(NSNotification *)notification {
[self handleRoomMini:notification.userInfo];
}
- (void)onConfigReloadNotification:(NSNotification *)notification {
[self handleConfigReload];
}
- (void)onLanguageSwitchNotification:(NSNotification *)notification {
[self handleLanguageSwitch:notification];
}
// MARK: - Global Notifications Handler
- (void)handleConfigReload {
NSLog(@"[GlobalEventManager] 配置重载");
if (self.configReloadCallback) {
self.configReloadCallback();
}
}
- (void)handleNewUserRecharge {
NSLog(@"[GlobalEventManager] 新用户充值");
if (self.newUserRechargeCallback) {
self.newUserRechargeCallback();
}
}
- (void)handleAnchorCard:(NSNotification *)notification {
NSLog(@"[GlobalEventManager] 主播卡片通知: %@", notification.userInfo);
// TODO:
}
- (void)handleLanguageSwitch:(NSNotification *)notification {
NSLog(@"[GlobalEventManager] 语言切换: %@", notification.userInfo);
// TODO:
}
// MARK: - User Info
- (void)handleUserInfoSuccess:(id)userInfo {
NSLog(@"[GlobalEventManager] 用户信息获取成功");
// Manager
if ([userInfo respondsToSelector:@selector(uid)]) {
[[PublicRoomManager sharedManager] initialize];
[[PublicRoomManager sharedManager] updateUserInfo:userInfo];
[[RoomBoomManager sharedManager] saveUserInfo:userInfo];
[[XPSkillCardPlayerManager shareInstance] setUserInfoModel:userInfo];
}
}
// MARK: - Social Share
- (void)registerSocialShareCallback {
// 2
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[[SocialShareManager sharedManager] checkSocialShareItem];
NSLog(@"[GlobalEventManager] 社交分享回调已注册");
});
}
// MARK: - NIMSDK Delegate Methods
#pragma mark - NIMLoginManagerDelegate
- (void)onLogin:(NIMLoginStep)step {
NSLog(@"[GlobalEventManager] NIMSDK 登录步骤: %ld", (long)step);
}
- (void)onKickout:(NIMKickReason)code clientType:(NIMLoginClientType)clientType {
NSLog(@"[GlobalEventManager] NIMSDK 被踢出: reason=%ld, clientType=%ld", (long)code, (long)clientType);
}
- (void)onAutoLoginFailed:(NSError *)error {
NSLog(@"[GlobalEventManager] NIMSDK 自动登录失败: %@", error);
}
#pragma mark - NIMChatManagerDelegate
- (void)onRecvMessages:(NSArray<NIMMessage *> *)messages {
NSLog(@"[GlobalEventManager] 收到 %lu 条消息", (unsigned long)messages.count);
}
#pragma mark - NIMSystemNotificationManagerDelegate
- (void)onReceiveSystemNotification:(NIMSystemNotification *)notification {
NSLog(@"[GlobalEventManager] 收到系统通知: %@", notification.notificationId);
}
#pragma mark - NIMBroadcastManagerDelegate
- (void)onReceiveBroadcastMessage:(NIMBroadcastMessage *)message {
NSLog(@"[GlobalEventManager] 收到广播消息: %@", message.content);
}
@end

View File

@@ -0,0 +1,197 @@
//
// NewTabBarController.swift
// YuMi
//
// Created by AI on 2025-10-09.
// Copyright © 2025 YuMi. All rights reserved.
//
import UIKit
/// TabBar
/// Moment Mine Tab
class NewTabBarController: UITabBarController {
// MARK: - Properties
///
private var globalEventManager: GlobalEventManager?
///
private var isLoggedIn: Bool = false
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
//
#if DEBUG
APIConfig.testEncryption()
#endif
setupTabBarAppearance()
setupGlobalManagers()
setupInitialViewControllers()
NSLog("[NewTabBarController] 初始化完成")
}
deinit {
globalEventManager?.removeAllDelegates()
NSLog("[NewTabBarController] 已释放")
}
// MARK: - Setup
/// TabBar
private func setupTabBarAppearance() {
// TabBar
tabBar.tintColor = UIColor(red: 0.2, green: 0.6, blue: 0.86, alpha: 1.0) //
tabBar.unselectedItemTintColor = UIColor(white: 0.6, alpha: 1.0) //
tabBar.backgroundColor = .white
tabBar.isTranslucent = false
// 线
if #available(iOS 13.0, *) {
let appearance = UITabBarAppearance()
appearance.configureWithOpaqueBackground()
appearance.backgroundColor = .white
appearance.stackedLayoutAppearance.selected.iconColor = tabBar.tintColor
appearance.stackedLayoutAppearance.selected.titleTextAttributes = [
.foregroundColor: tabBar.tintColor ?? .blue,
.font: UIFont.systemFont(ofSize: 10, weight: .medium)
]
appearance.stackedLayoutAppearance.normal.titleTextAttributes = [
.foregroundColor: tabBar.unselectedItemTintColor ?? .gray,
.font: UIFont.systemFont(ofSize: 10)
]
tabBar.standardAppearance = appearance
if #available(iOS 15.0, *) {
tabBar.scrollEdgeAppearance = appearance
}
}
NSLog("[NewTabBarController] TabBar 外观设置完成")
}
///
private func setupGlobalManagers() {
globalEventManager = GlobalEventManager.shared()
globalEventManager?.setupSDKDelegates()
//
if let containerView = view {
globalEventManager?.setupRoomMiniView(on: containerView)
}
//
globalEventManager?.registerSocialShareCallback()
NSLog("[NewTabBarController] 全局管理器设置完成")
}
/// ViewController
private func setupInitialViewControllers() {
// TODO: 使
let blankVC1 = UIViewController()
blankVC1.view.backgroundColor = .white
blankVC1.tabBarItem = createTabBarItem(
title: "动态",
normalImage: "tab_moment_normal",
selectedImage: "tab_moment_selected"
)
let blankVC2 = UIViewController()
blankVC2.view.backgroundColor = .white
blankVC2.tabBarItem = createTabBarItem(
title: "我的",
normalImage: "tab_mine_normal",
selectedImage: "tab_mine_selected"
)
viewControllers = [blankVC1, blankVC2]
selectedIndex = 0
NSLog("[NewTabBarController] 初始 ViewControllers 设置完成")
}
/// TabBarItem
/// - Parameters:
/// - title:
/// - normalImage:
/// - selectedImage:
/// - Returns: UITabBarItem
private func createTabBarItem(title: String, normalImage: String, selectedImage: String) -> UITabBarItem {
let item = UITabBarItem(
title: title,
image: UIImage(named: normalImage)?.withRenderingMode(.alwaysOriginal),
selectedImage: UIImage(named: selectedImage)?.withRenderingMode(.alwaysOriginal)
)
return item
}
// MARK: - Public Methods
/// TabBar
/// - Parameter isLogin:
@objc func refreshTabBar(isLogin: Bool) {
isLoggedIn = isLogin
if isLogin {
setupLoggedInViewControllers()
} else {
setupInitialViewControllers()
}
NSLog("[NewTabBarController] TabBar 已刷新,登录状态: \(isLogin)")
}
/// ViewControllers
private func setupLoggedInViewControllers() {
// TODO: NewMomentViewController NewMineViewController
// let momentVC = NewMomentViewController()
// let mineVC = NewMineViewController()
let momentVC = UIViewController()
momentVC.view.backgroundColor = .white
momentVC.tabBarItem = createTabBarItem(
title: "动态",
normalImage: "tab_moment_normal",
selectedImage: "tab_moment_selected"
)
let mineVC = UIViewController()
mineVC.view.backgroundColor = .white
mineVC.tabBarItem = createTabBarItem(
title: "我的",
normalImage: "tab_mine_normal",
selectedImage: "tab_mine_selected"
)
viewControllers = [momentVC, mineVC]
selectedIndex = 0
NSLog("[NewTabBarController] 登录后 ViewControllers 设置完成")
}
}
// MARK: - UITabBarControllerDelegate
extension NewTabBarController: UITabBarControllerDelegate {
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
NSLog("[NewTabBarController] 选中 Tab: \(item.title ?? "Unknown")")
}
}
// MARK: - OC Compatibility
extension NewTabBarController {
/// OC
@objc static func create() -> NewTabBarController {
return NewTabBarController()
}
}

View File

@@ -0,0 +1,46 @@
//
// YuMi-Bridging-Header.h
// YuMi
//
// Created by AI on 2025-10-09.
// Copyright © 2025 YuMi. All rights reserved.
//
// Swift/OC 混编桥接头文件
#ifndef YuMi_Bridging_Header_h
#define YuMi_Bridging_Header_h
// MARK: - Network
#import "HttpRequestHelper.h"
#import "Api.h"
// MARK: - Models
#import "UserInfoModel.h"
#import "BaseModel.h"
// MARK: - Managers
#import "RoomBoomManager.h"
#import "PublicRoomManager.h"
#import "XPSkillCardPlayerManager.h"
#import "RtcManager.h"
#import "IAPManager.h"
#import "SocialShareManager.h"
// MARK: - Views
#import "XPMiniRoomView.h"
#import "XPRoomMiniManager.h"
// MARK: - Third Party SDKs
#import <NIMSDK/NIMSDK.h>
#import <AFNetworking/AFNetworking.h>
// MARK: - Utils
#import "YUMIConstant.h"
#import "ClientConfig.h"
#import "AccountInfoStorage.h"
// MARK: - UI Components
#import "BaseViewController.h"
#import "BaseNavigationController.h"
#endif /* YuMi_Bridging_Header_h */