导入 XuXuCheck 工程(iOS 干化学尿液试纸分析 App)

从桌面工程复制而来,作为本仓库的 Xuxucheck/ 子目录。

为控制仓库体积做了两处删减:
- 移除 Frameworks/opencv2.xcframework 下的两个 visionOS 切片
  (xros-arm64、xros-arm64-simulator,合计约 106 MB),并同步删掉该
  xcframework 内 Info.plist 里对应的两条 AvailableLibraries 记录,
  只保留 ios-arm64 与 ios-arm64_x86_64-simulator。
- 移除全部 .DS_Store 与 xcuserdata/。

已用 xcodebuild 从本目录完整编译验证:BUILD SUCCEEDED,
ProcessXCFramework 以 --platform ios 成功选中框架,产物含 3699 个 cv:: 符号
(OpenCV 确实链入),嵌入 framework 3 个,与移除无用 pod 之后的状态一致。

仓库约 206 MB,其中 OpenCV 203 MB、其余源码与依赖约 3 MB。
This commit is contained in:
2026-09-23 14:17:31 +08:00
commit d6aeede2bc
901 changed files with 327267 additions and 0 deletions
+36
View File
@@ -0,0 +1,36 @@
//
// Home.swift
// XuXuCheck
//
// Created by henseycai on 2025/6/18.
//
import Foundation
import SwiftUI
struct ScrollPreferenceKey: PreferenceKey {
static var defaultValue: CGFloat = 0
static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) {
value = nextValue()
}
}
struct BlockItem: Identifiable {
let id = UUID()
var title: String
var subtitle: String
var text: String
var image: String
var background: String
var logo: String
var link: String?
}
var homeFuncblocks = [
BlockItem(title: localizeString("Introduction"), subtitle: "-", text: "What's XuXuCheck", image: "home_intro", background: "Background 5", logo: "Logo 2",link: API.webProductIntroductionUrl + GetGeneralRequestParams()),
BlockItem(title: localizeString("Manual"), subtitle: "-", text: "How to use XuXuCheck", image: "home_tutorial", background: "Background 4", logo: "Logo 4",link: API.webCourseUrl + GetGeneralRequestParams()),
BlockItem(title: localizeString("ColorCard"), subtitle: "-", text: "Dry chemical Test strip's color card", image: "home_chart", background: "Background 1", logo: "Logo 1",link: API.webColorimetricCardUrl + GetGeneralRequestParams()),
BlockItem(title: localizeString("Records"), subtitle: "-", text: "test records", image: "home_records", background: "Background 2", logo: "Logo 3",link: API.webrecordUrl + GetGeneralRequestParams())
]
@@ -0,0 +1,74 @@
//
// JS2SwiftModel.swift
// XuXuCheck
//
// Created by henseycai on 2025/7/10.
//
import Foundation
//A funny bug , the member name cannot have prefix "self".
//if change jsSelf to selfData, compiling error will happen
struct JS2SwiftData: Codable{
var url: String?
var phone_number: String?
var redirect_url: String?
var record_type: Int?
var member_data: JSMember?
var jsSelf: JSSelf?
enum CodingKeys: String, CodingKey {
case url,phone_number,redirect_url
case record_type
case member_data
case jsSelf = "self"
}
}
struct JS2SwiftModel: Codable {
var command: String?
var parameter: JS2SwiftData?
enum CodingKeys: String, CodingKey {
case command
case parameter = "parameter"
}
}
struct JSMember: Codable {
var member_id: Int?
var user_id: Int?
var member_name: String?
var member_avatar, member_birth, member_height, member_weight,member_contact,member_sex_name: String?
var member_sex: Int?
var member_default: Int?
var member_main: Int?
var created_at,updated_at: Int?
// enum CodingKeys: String, CodingKey {
// case member_name,member_avatar, member_birth, member_height, member_weight,member_contact,member_sex_name
// case member_sex
// case member_default
// case member_main
// case user_id
// case member_id
// case created_at,updated_at
// }
}
struct JSSelf: Codable{
var user_id: Int?
var username: String?
var user_email, user_mobile, user_region, user_nickname: String?
var user_avatar: String?
var user_sex: Int?
// enum CodingKeys: String, CodingKey {
// case username,user_email, user_mobile, user_region, user_nickname,user_avatar
// case user_id
// case user_sex
// }
}
@@ -0,0 +1,14 @@
//
// LoginStatus.swift
// XuXuCheck
//
// Created by henseycai on 2025/7/8.
//
import Foundation
@Observable
class LoginStatus {
var isLogin = false
var routeToLoginView = false // set to true, tell us we need to route to LoginView()
}
@@ -0,0 +1,97 @@
//
// MemberModel.swift
// XuXuCheck
//
// Created by henseycai on 2025/6/17.
//
import Foundation
import Alamofire
import ObjectMapper
class MemberModel: Mappable {
var code: Int = 0
var data: MemberData?
var message: String?
required init?(map:Map) {
}
func mapping(map: Map) {
data <- map["data"]
code <- map["code"]
message <- map["message"]
}
}
class MemberData: Mappable{
var usertoken: String?
var userinfo: UserInfo?
var defaultMember: Member?
required init?(map:Map) {
}
func mapping(map: Map) {
usertoken <- map["user_token"]
userinfo <- map["user_info"]
defaultMember <- map["default_member"]
}
}
class UserInfo: Mappable{
var user_id: Int = 0
var username: String?
var user_email, user_mobile, user_region, user_nickname: String?
var user_avatar: String?
var user_sex: Int?
required init?(map:Map) {
}
func mapping(map: Map) {
user_id <- map["user_id"]
username <- map["username"]
user_email <- map["user_email"]
user_mobile <- map["user_mobile"]
user_region <- map["user_region"]
user_nickname <- map["user_nickname"]
user_avatar <- map["user_avatar"]
user_sex <- map["user_sex"]
}
}
class Member: Mappable {
var member_id: Int = 0
var user_id: Int = 0
var member_name: String?
var member_avatar, member_birth, member_height, member_weight,member_contact,member_sex_name: String?
var member_sex: Int?
var member_default: Int?
var member_main: Int?
var created_at,updated_at: Int64?;
required init?(map:Map) {
}
func mapping(map: Map) {
member_id <- map["member_id"]
user_id <- map["user_id"]
member_name <- map["member_name"]
member_avatar <- map["member_avatar"]
member_birth <- map["member_birth"]
member_height <- map["member_height"]
member_weight <- map["member_weight"]
member_contact <- map["member_contact"]
member_sex_name <- map["member_sex_name"]
member_default <- map["member_default"]
member_sex <- map["member_sex"]
member_main <- map["member_main"]
created_at <- map["created_at"]
updated_at <- map["updated_at"]
}
}
@@ -0,0 +1,65 @@
//
// RecordModel.swift
// XuXuCheck
//
// Created by henseycai on 2025/6/30.
//
import Foundation
import Alamofire
import ObjectMapper
// MARK: - Welcome
struct RecordModel: Codable {
let usertoken: String
let memberID: Int
let sign: String
let time, userid, color, items: Int
let locator, recordType: Int
let channel: String
var data: [[Datum]]
enum CodingKeys: String, CodingKey {
case usertoken
case memberID = "member_id"
case sign, time, userid, color, items, locator
case recordType = "record_type"
case channel, data
}
}
// MARK: - Datum
struct Datum: Codable {
let i: Int
let v: String
}
class RecordResultModel: Mappable {
var code: Int = 0
var data: RecordResult?
var message: String?
required init?(map:Map) {
}
func mapping(map: Map) {
data <- map["data"]
code <- map["code"]
message <- map["message"]
}
}
class RecordResult: Mappable{
var record_id: Int?
required init?(map:Map) {
}
func mapping(map: Map) {
record_id <- map["record_id"]
}
}
+15
View File
@@ -0,0 +1,15 @@
//
// Status.swift
// XuXuCheck
//
// Created by henseycai on 2025/6/18.
//
import Foundation
import SwiftUI
import Combine
@Observable
class Status {
var showNavDetail = false
}
@@ -0,0 +1,104 @@
//
// WebViewModel.swift
// WKWebViewLocal
//
// Created by Gary Newby on 25/11/2024.
//
import SwiftUI
import WebKit
@Observable
class WebViewModel {
var url: URL?
var error: Error?
var isLoading = false
var backButtonIsEnabled = false
/// Load a file or a HTML string
let loadFile = false
weak var webView: WKWebView?
var jsCommand: String?
var jsParameter: JS2SwiftData?
var hasNewCommand = false
func assignWebView(webView: WKWebView) {
self.webView = webView
if (url != nil) {
hasNewCommand = false
self.webView?.load(URLRequest(url: url!))
}else{
if loadFile {
loadFile(name: "index")
} else {
loadString(name: "index")
}
}
}
func loadFile(name: String) {
guard let filePath = Bundle.main.path(forResource: name, ofType: "html", inDirectory: "Web_Assets") else {
print("file not found:", name)
return
}
let filePathURL = URL.init(fileURLWithPath: filePath)
let fileDirectoryURL = filePathURL.deletingLastPathComponent()
webView?.loadFileURL(filePathURL, allowingReadAccessTo: fileDirectoryURL)
}
func loadString(name: String) {
guard let filePath = Bundle.main.path(forResource: name, ofType: "html", inDirectory: "Web_Assets") else {
print("file not found:", name)
return
}
do {
let html = try String(contentsOfFile: filePath, encoding: .utf8)
/// baseURL needs to be set for local files to load correctly
webView?.loadHTMLString(html, baseURL: Bundle.main.resourceURL?.appendingPathComponent("Web_Assets"))
} catch {
print("Error loading html")
}
}
func loadUrl(url: String){
webView?.load(URLRequest(url: URL(string:url)!))
}
func callJsImage(imageString: String){
let script = "javascript:CallJS('SelectPhoto','data:image/jpeg;base64,\(imageString)')"
webView?.evaluateJavaScript(script) { (result: Any?, error: Error?) in
if let error = error {
print("evaluateJavaScript error: \(error)")
} else {
print("evaluateJavaScript result: \(result ?? "")")
}
}
}
func callJsBarcode(named: String) {
let script = "javascript:CallJS('BarCode','\(named)')"
webView?.evaluateJavaScript(script) { (result: Any?, error: Error?) in
if let error = error {
print("evaluateJavaScript error: \(error)")
} else {
print("evaluateJavaScript result: \(result ?? "")")
}
}
}
func isButtonVisible(_ action: String) -> Bool {
switch action {
case "goBack()":
return backButtonIsEnabled
case "changeText('hello from Swift')":
return !backButtonIsEnabled
default:
return false
}
}
}
@@ -0,0 +1,16 @@
//
// ModelProtocol.swift
// WanAndroid_SwiftUI
//
// Created by zxc on 2023/6/12.
//
import Foundation
internal protocol ModelProtocol {
func newJSONDecoder() -> JSONDecoder
func newJSONEncoder() -> JSONEncoder
}