Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

StrinovaWiki - 卡拉彼丘游戏 Wiki iOS 客户端

基于 SwiftUI 的卡拉彼丘(Strinova / 卡拉彼丘)游戏 Wiki 浏览器,数据源为 biligame wiki

⚠️ 本项目为纯粹的粉丝作品,仅供学习和交流使用,不涉及任何商业盈利目的。 游戏「卡拉彼丘」及其所有相关素材(角色、武器、图片、名称等)的知识产权均归其版权方所有。


致谢与参考

本项目的功能设计与数据解析思路深受以下开源项目的启发,在此向原作者表示衷心感谢:

  • CalabiYauVoice_GUI — 由 znzsofficial 开发的 Compose Multiplatform 版卡拉彼丘 Wiki 客户端。本项目的模块划分、数据解析逻辑与 API 调用方式均参考了该项目的优秀设计。(原项目采用 Apache-2.0 许可证)

所有游戏数据均来源于 哔哩哔哩游戏 Wiki - 卡拉彼丘,感谢 Wiki 维护者们的辛勤贡献。


项目概述

项目 说明
语言 Swift 6.0
UI 框架 SwiftUI (iOS 26.0+)
架构模式 MVVM(View + ViewModel + Service)
项目管理 XcodeGen (project.yml)
最低版本 iOS 26.0
开发语言 简体中文 (zh-Hans)
Bundle ID hariteki.StrinovaWiki

唯一外部依赖

项目坚持 原生优先 策略,几乎全部使用 Apple 原生 API。唯一引入的第三方库:

依赖 用途 来源
Ifrit (>=4.0.0) SwiftUI 动画/辅助工具 SPM

重要:不引入 Alamofire、Kingfisher、SwiftyJSON 等第三方库。网络用原生 URLSession,图片用原生 AsyncImage + 自定义 ImageCacheService,HTML 解析用原生 NSRegularExpression + 自定义解析函数。

构建与运行

# 1. 生成 Xcode 项目文件
xcodegen generate

# 2. 打开项目
open StrinovaWiki.xcodeproj

# 3. 或直接命令行构建到模拟器
xcodebuild -project StrinovaWiki.xcodeproj -scheme StrinovaWiki \
  -destination 'platform=iOS Simulator,name=iPhone 17 Pro' build

DerivedData/ 目录已在 .gitignore 中忽略。不要编辑 project.pbxproj,所有项目配置通过 project.yml 管理。


代码目录结构

StrinovaWiki/
├── StrinovaWikiApp.swift          # @main 入口 + AppDelegate
├── Info.plist
├── Assets.xcassets/               # 颜色、图标等资源
│
├── Models/                        # Codable 数据模型(每个模块一个文件)
│   ├── CommonModels.swift         # AppRoute、LoadState、NetworkError 等全局类型
│   ├── WikiModels.swift           # Wiki API 通用响应模型
│   ├── CharacterModels.swift      # 角色相关模型
│   ├── WeaponModels.swift         # 武器相关模型
│   ├── GalleryModels.swift        # 壁纸/表情包模型
│   ├── MeowModels.swift           # 喵言喵语模型
│   ├── BiochemicalCardModels.swift
│   ├── ... (其他模块模型)
│
├── Services/                      # 网络、缓存、本地存储(均为 actor 或 @MainActor class)
│   ├── WikiNetworkService.swift   # 核心网络服务:MediaWiki API + HTML 解析
│   ├── WikiNetworkService+*.swift # 各模块的网络请求扩展
│   ├── CacheService.swift         # 离线缓存(FileManager 持久化 + TTL)
│   ├── ImageCacheService.swift    # 图片双级缓存(内存 + 磁盘)
│   ├── LocalStorageService.swift  # UserDefaults 封装(主题、搜索历史等)
│   └── WikiIndexService.swift     # Wiki 索引检索
│
├── ViewModels/                    # @MainActor ObservableObject
│   ├── BaseViewModel.swift        # ViewModel 基类:isLoading、errorMessage、loadCacheThenFetch
│   ├── HomeViewModel.swift        # 首页数据 + 菜单配置
│   ├── GenericWikiPageViewModel.swift  # 通用 Wiki 页面 ViewModel
│   ├── ... (各模块 ViewModel)
│
├── Views/                         # SwiftUI 页面(按功能模块分目录)
│   ├── ContentView.swift          # 根视图:TabView + NavigationStack 路由分发
│   ├── Home/HomeScreen.swift      # 首页(模块入口卡片网格)
│   ├── Character/                 # 角色列表 + 详情
│   ├── Weapon/                    # 武器列表 + 详情
│   ├── Gallery/                   # 壁纸 + 表情包
│   ├── Meow/MeowScreen.swift
│   ├── Biochemical/BiochemicalCardScreen.swift
│   ├── Decoration/                # 玩家装饰
│   ├── GameMode/GameModeScreen.swift
│   ├── Core/CoreScreen.swift      # 游戏核心模块聚合页
│   ├── Map/, Item/, History/, ...
│   └── WikiBrowserView.swift      # WKWebView 内嵌浏览器
│
├── Components/                    # 可复用 UI 组件
│   ├── WikiCard.swift             # 通用卡片容器
│   ├── CachedAsyncImage.swift     # 带持久化缓存的异步图片
│   ├── GIFWebView.swift           # GIF 动图渲染(WKWebView)
│   ├── SearchBar.swift
│   ├── FilterChipGroup.swift      # 筛选标签组
│   ├── ErrorStateView.swift       # 错误状态 + 重试按钮
│   ├── LoadingView.swift
│   ├── EmptyStateView.swift
│   ├── SectionHeader.swift
│   ├── ImagePreviewView.swift     # 图片全屏预览
│   └── WikiBottomSheet.swift
│
└── Utils/
    ├── Theme.swift                 # 全局设计 Token(间距、圆角、颜色)
    ├── Extensions.swift            # View、Color、String 扩展
    └── WikiParsingHelpers.swift    # HTML/Wikitext 解析工具函数

核心架构

数据流

Wiki API (biligame.com)
    │
    ▼
WikiNetworkService (actor, URLSession + NSRegularExpression)
    │  fetchParseHtml / fetchJSON / fetchAskResult
    ▼
ViewModel (@MainActor, ObservableObject)
    │  loadCacheThenFetch → 先读缓存 → 网络更新 → 写缓存
    ▼
View (SwiftUI)
    @StateObject viewModel → 渲染 UI

路由系统

所有页面导航通过 AppRoute 枚举(定义在 Models/CommonModels.swift)统一管理:

enum AppRoute: Hashable {
    case home
    case characterDetail(name: String, imageUrl: String?)
    case weaponDetail(name: String, imageUrl: String?)
    case decorationCategory(type: DecorationType)
    case wikiBrowser(urlString: String)
    // ... 约 20+ 路由
}
  • ContentView 管理 4 个 Tab(首页/武器/角色/设置),每个 Tab 内嵌独立 NavigationStack
  • 路由分发在 ContentView.destination(for:) 中集中处理
  • 详情页通过路由参数(name, imageUrl)定位,ViewModel 按参数加载

网络服务架构

WikiNetworkServiceactor(线程安全),采用 extension 模式组织各模块:

WikiNetworkService.swift           # 核心:fetchCharacters, fetchCategoryMembers, resolveImageURLs, search, fetchParseHtml, fetchAskResult
+CharacterDetail.swift             # fetchCharacterDetail(name:)
+WeaponList.swift                  # fetchWeapons()
+WeaponDetail.swift                # fetchWeaponDetail(name:)
+Decoration.swift                  # fetchDecorations(type:)
+Gallery.swift                     # fetchWallpapers(), fetchStickers()
+Meow.swift                        # fetchMeowQuotes()
+BiochemicalCard.swift             # fetchBiochemicalCards(platform:)
+Map.swift, +Story.swift, +Chronicle.swift, +Collab.swift, +Item.swift, +Achievement.swift, +GameMode.swift, +Activity.swift, +Announcement.swift, +Submission.swift, +Generic.swift

添加新模块时的步骤

  1. Models/ 新建数据模型文件
  2. Services/ 新建 WikiNetworkService+新模块.swift extension
  3. ViewModels/ 新建 ViewModel(继承 BaseViewModel,使用 loadCacheThenFetch
  4. Views/ 新建页面
  5. CommonModels.swiftAppRoute 添加新路由
  6. ContentView.destination(for:) 添加路由分发

ViewModel 模式

所有 ViewModel 继承 BaseViewModel,遵循统一模式:

@MainActor
final class SomeViewModel: BaseViewModel {
    @Published var data: SomeData = []

    func load() async {
        guard data.isEmpty else { return }  // 避免重复请求
        await loadCacheThenFetch(
            cacheType: .genericPage,
            cacheKey: "some_cache_key",
            setValue: { self.data = $0 },
            fetch: { try await WikiNetworkService.shared.fetchSomeData() }
        )
    }
}

loadCacheThenFetch 自动处理:先读缓存立即展示 → 网络请求 → 更新 UI → 写入缓存 → 错误处理。

图片加载策略

场景 方案
一般图片 CachedAsyncImage — 内存+磁盘双级缓存
GIF 动图 GIFWebView — WKWebView 渲染原图(不缩放)
壁纸缩略图 低分辨率(原图缩略图 URL)
表情包非 GIF 低分辨率缩略图
表情包 GIF 原图 URL,WKWebView 渲染

图片 URL 处理规则(定义在 WikiParsingHelpers.swift):

  • normalizeImageURL(_:) — 处理 /// 开头的相对路径,补全 https:wiki.biligame.com
  • thumbnailImageURL(_:width:) — 构造 200px 缩略图路径
  • originalFromThumbnail(_:) — 从缩略图 URL 还原原图 URL
  • 所有外部图片 URL 必须通过 normalizeImageURL 处理,确保路径不重复拼接 /klbq/

设计规范

主题 Token(Theme.swift

所有 UI 尺寸/间距/圆角/颜色必须使用 Theme 枚举,禁止硬编码数值

类别 属性 说明
圆角 chipRadius=12, cardRadius=16, sheetRadius=28, dialogRadius=28 卡片、弹窗使用
间距 tiny=2 ~ xxxLarge=32 8 个等级
图标 iconSmall=18, iconMedium=20, iconLarge=24 SF Symbols 尺寸
颜色 brandColor=#1A73E8 品牌主色

UI 约定

  • 模块入口按钮:3 列 LazyVGrid,72×72 圆形图标,24pt 圆角,标题在按钮下方(.subheadline.weight(.semibold)
  • 预览卡片描边:使用 .overlay + .stroke 绘制在内容上方,内容区加 .padding(4) 防止内容贴边
  • 磨砂描边效果:在描边下方叠加 .ultraThinMaterial + .blur(radius: 1)
  • 筛选菜单:小巧、磨砂半透明背景、大圆角
  • Toast 通知:顶部显示,1.5 秒自动消失
  • 生化卡牌:固定 160pt 宽、3:4 比例、12pt 内边距、向内 3pt 描边(strokeBorder
  • 生化卡牌稀有度颜色:Refined=蓝(#1E3A8A)、Excellent=紫(#4C1D95)、Perfect=金(#78350F);暗黑模式下自动切换亮色

开发注意事项

已知坑点

  1. .task 不能放在条件分支内 — 状态变化会导致 task 取消。必须放在稳定容器 View 上。
  2. system .popover 在 iPhone 上可能全屏 — 自定义卡片 popover 需用 overlay 实现。
  3. SPM 动态库必须 embedproject.ymlembed: true,否则运行时报 Library not loaded
  4. 第三方输入法按回车清空搜索文本MeowScreen.swift 中 submitSearch 加了 0.05 秒延迟 + 文本快照恢复。
  5. WAF/限流重试 — 567/503/429 状态码自动重试一次(WikiNetworkService.perform)。
  6. 筛选 .svg.png 图标 — 仅用后缀匹配,不要用 \d+px- 前缀正则(会误杀有效缩略图)。

代码风格

  • 所有中文注释使用 // MARK: - 分组
  • 文件顶部加模块说明注释(如 // MARK: - 角色列表 ViewModel
  • ViewModel 内部需要缓存的对象定义 fileprivate Codable struct
  • 网络请求方法中 HTML 解析函数标记为 nonisolated(避免阻塞 actor)

修改配置

所有项目配置在 project.yml 中修改后执行 xcodegen generate 重新生成 .xcodeproj。不要直接编辑 .pbxproj


功能模块清单

模块 页面文件 ViewModel 网络服务扩展
首页 Home/HomeScreen.swift HomeViewModel WikiNetworkService(核心)
角色列表 Character/CharacterListScreen.swift CharacterListViewModel +CharacterDetail
角色详情 Character/CharacterDetailScreen.swift CharacterDetailViewModel +CharacterDetail
武器列表 Weapon/WeaponListScreen.swift WeaponListViewModel +WeaponList
武器详情 Weapon/WeaponDetailScreen.swift WeaponDetailViewModel +WeaponDetail
壁纸 Gallery/WallpaperScreen.swift 内联 +Gallery
表情包 Gallery/StickerScreen.swift 内联 +Gallery
图库入口 Gallery/GalleryScreen.swift - -
喵言喵语 Meow/MeowScreen.swift 内联 +Meow
生化卡牌 Biochemical/BiochemicalCardScreen.swift BiochemicalCardViewModel +BiochemicalCard
玩家装饰 Decoration/DecorationScreen.swift DecorationCategoryViewModel +Decoration
战斗模式 GameMode/GameModeScreen.swift GameModeViewModel +GameMode
地图 Map/MapScreen.swift MapViewModel +Map
道具 Item/ItemScreen.swift ItemViewModel +Item
剧情故事 History/StoryScreen.swift StoryViewModel +Story
游戏历史 History/GameHistoryScreen.swift GameHistoryViewModel +Chronicle
成就 Achievement/AchievementScreen.swift AchievementViewModel +Achievement
公告 Announcement/AnnouncementScreen.swift AnnouncementViewModel +Announcement
活动 Activity/ActivityScreen.swift ActivityViewModel +Activity
联动 Collaboration/CollaborationScreen.swift 内联 +Collab
投稿 Submission/SubmissionScreen.swift SubmissionViewModel +Submission
玩家等级 PlayerLevel/PlayerLevelScreen.swift 内联 +Generic
印记 Imprint/ImprintScreen.swift 内联 +Generic
设置 Settings/SettingsScreen.swift SettingsViewModel -
通用Wiki页 Common/GenericWikiPageScreen.swift GenericWikiPageViewModel +Generic
Wiki浏览器 WikiBrowserView.swift - -
游戏核心聚合 Core/CoreScreen.swift - -

网络 API

数据源:https://wiki.biligame.com/klbq/api.php

核心 API 调用方式(封装在 WikiNetworkService):

API action 用途 对应方法
parse (page) 获取 Wiki 页面渲染 HTML fetchParseHtml(page:)
parse (text) 渲染 wikitext 为 HTML(POST) fetchParseHtml(text:)
ask Semantic MediaWiki 查询(POST) fetchAskResult(query:)
query + categorymembers 获取分类成员列表 fetchCategoryMembers(category:)
query + imageinfo 批量解析图片 URL resolveImageURLs(titles:)
query + search 全文搜索 search(query:)

请求头包含随机 User-Agent、zh-CN Accept-Language、Referer 模拟浏览器,以降低 WAF 拦截概率。POST 请求使用 form-urlencoded 编码。


缓存策略

离线数据缓存(CacheService

  • 基于 FileManager,按 CacheType 分目录存储
  • 每个 CacheType 有不同 TTL(1小时~7天)
  • 缓存 Key 使用 MD5 哈希
  • 用户可在设置中开启"永不过期"

图片缓存(ImageCacheService

  • 双级缓存:NSCache(50MB 内存,200 张上限)+ 磁盘持久化
  • 缓存 Key 使用 SHA256 哈希 URL
  • 提供 clearAll() 清除所有缓存

主题与本地化

  • 支持浅色/深色/跟随系统三种主题模式,通过 LocalStorageService.themeMode 持久化
  • UI 颜色使用 .primary/.secondary 等语义色,自动适配深浅模式
  • 中文界面,无多语言国际化需求

法律声明与免责声明

版权归属

游戏「卡拉彼丘(Strinova)」及其所有相关素材(包括但不限于角色形象、武器设计、游戏图标、背景图片、音效、名称、商标等)的知识产权均归其各自的版权方(深圳市腾讯计算机系统有限公司及其关联公司、或相关权利人)所有。本项目不包含任何游戏官方原始素材文件,仅通过公开 API 从哔哩哔哩游戏 Wiki 获取公开信息进行展示。

非官方声明

本项目为个人独立开发的粉丝作品,与游戏官方或哔哩哔哩官方无任何隶属、赞助或认可关系。本项目的任何内容均不代表游戏官方或版权方的立场和观点。

非盈利声明

本项目不以任何方式进行商业盈利,包括但不限于:

  • 不出售、不租赁、不转让本项目的任何部分
  • 不通过本项目获取任何形式的直接经济收益
  • 不包含任何内购、订阅、广告或付费功能

本项目仅供卡拉彼丘爱好者学习、交流和便利查阅游戏资料使用。

免责声明

本项目按「原样」提供,不附带任何明示或默示的担保,包括但不限于适销性、特定用途适用性及非侵权性的担保。使用本项目的风险由用户自行承担。在任何情况下,作者均不对因使用或无法使用本项目而导致的任何损失承担责任。

数据来源与使用规范

本项目所有游戏数据均通过公开 API 从 哔哩哔哩游戏 Wiki - 卡拉彼丘 获取。用户在使用本项目时应遵守:

  • 哔哩哔哩游戏 Wiki 的使用条款和社区规范
  • 卡拉彼丘游戏官方的用户协议
  • 相关法律法规

如 Wiki 数据源发生变更或停止服务,本项目的部分功能可能无法正常使用。


许可证

本项目采用 MIT License 开源许可证。

About

卡拉彼丘(Strinova)游戏 Wiki iOS 客户端 · 纯粉丝作品,非官方、非盈利 · 数据源自哔哩哔哩游戏 Wiki

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages