// // WeChatShareService.swift // suixinkan // import UIKit import WechatOpenSDK /// 微信分享服务,封装文本、网页、图片和小程序分享能力,不包含微信支付。 enum WeChatShareService { /// 当前设备是否已安装微信且支持 Open SDK。 static var isWeChatAvailable: Bool { WXApi.isWXAppInstalled() && WXApi.isWXAppSupport() } /// 分享纯文本到微信。 @MainActor static func shareText( _ payload: WeChatTextSharePayload, scene: WeChatShareScene, completion: ((WeChatShareResult) -> Void)? = nil ) { let trimmed = payload.text.trimmingCharacters(in: .whitespacesAndNewlines) guard !trimmed.isEmpty else { completion?(.invalidPayload("分享文本不能为空")) return } let request = SendMessageToWXReq() request.bText = true request.text = trimmed request.scene = scene.wxSceneValue WeChatManager.shared.sendShareRequest(request, completion: completion) } /// 分享网页链接到微信。 @MainActor static func shareWebPage( _ payload: WeChatWebPageSharePayload, scene: WeChatShareScene, thumbImage: UIImage? = nil, completion: ((WeChatShareResult) -> Void)? = nil ) { let title = payload.title.trimmingCharacters(in: .whitespacesAndNewlines) let url = payload.url.trimmingCharacters(in: .whitespacesAndNewlines) guard !title.isEmpty else { completion?(.invalidPayload("分享标题不能为空")) return } guard !url.isEmpty else { completion?(.invalidPayload("分享链接不能为空")) return } let webpageObject = WXWebpageObject() webpageObject.webpageUrl = url let message = WXMediaMessage() message.title = title message.description = payload.description message.mediaObject = webpageObject if let thumbImage, let thumbData = WeChatThumbImageProcessor.makeThumbData(from: thumbImage) { message.thumbData = thumbData } let request = SendMessageToWXReq() request.bText = false request.message = message request.scene = scene.wxSceneValue WeChatManager.shared.sendShareRequest(request, completion: completion) } /// 分享图片到微信。 @MainActor static func shareImage( _ payload: WeChatImageSharePayload, scene: WeChatShareScene, thumbImage: UIImage? = nil, completion: ((WeChatShareResult) -> Void)? = nil ) { guard !payload.imageData.isEmpty else { completion?(.invalidPayload("分享图片不能为空")) return } let imageObject = WXImageObject() imageObject.imageData = payload.imageData let message = WXMediaMessage() message.mediaObject = imageObject if let thumbImage, let thumbData = WeChatThumbImageProcessor.makeThumbData(from: thumbImage) { message.thumbData = thumbData } else if let image = UIImage(data: payload.imageData), let thumbData = WeChatThumbImageProcessor.makeThumbData(from: image) { message.thumbData = thumbData } let request = SendMessageToWXReq() request.bText = false request.message = message request.scene = scene.wxSceneValue WeChatManager.shared.sendShareRequest(request, completion: completion) } /// 分享小程序到微信好友会话。 @MainActor static func shareMiniProgram( _ payload: WeChatMiniProgramSharePayload, previewImage: UIImage? = nil, completion: ((WeChatShareResult) -> Void)? = nil ) { switch makeMiniProgramRequest(payload, scene: .session, previewImage: previewImage ?? defaultThumbImage()) { case .success(let request): WeChatManager.shared.sendShareRequest(request, completion: completion) case .failure(let result): completion?(result) } } /// 构建小程序分享请求,供业务分享入口和单元测试复用。 static func makeMiniProgramRequest( _ payload: WeChatMiniProgramSharePayload, scene: WeChatShareScene, previewImage: UIImage? = nil ) -> Result { guard scene == .session else { return .failure(.invalidPayload("小程序仅支持分享到微信好友")) } let title = payload.title.trimmingCharacters(in: .whitespacesAndNewlines) let webpageURL = payload.webpageURL.trimmingCharacters(in: .whitespacesAndNewlines) let userName = payload.userName.trimmingCharacters(in: .whitespacesAndNewlines) let path = payload.path?.trimmingCharacters(in: .whitespacesAndNewlines) guard !title.isEmpty else { return .failure(.invalidPayload("分享标题不能为空")) } guard !webpageURL.isEmpty else { return .failure(.invalidPayload("小程序兼容网页链接不能为空")) } guard webpageURL.lengthOfBytes(using: .utf8) <= 1024 else { return .failure(.invalidPayload("小程序兼容网页链接不能超过1024字节")) } guard !userName.isEmpty else { return .failure(.invalidPayload("小程序原始 ID 不能为空")) } let miniProgramObject = WXMiniProgramObject() miniProgramObject.webpageUrl = webpageURL miniProgramObject.userName = userName miniProgramObject.path = path?.isEmpty == false ? path : nil miniProgramObject.miniProgramType = WXMiniProgramType(rawValue: UInt(payload.miniProgramType.resolvedForCurrentBuild.rawValue)) ?? WXMiniProgramType(rawValue: UInt(WeChatMiniProgramType.release.rawValue))! miniProgramObject.withShareTicket = payload.withShareTicket miniProgramObject.disableForward = payload.disableForward if let previewImage { miniProgramObject.hdImageData = WeChatThumbImageProcessor.makeMiniProgramHDImageData(from: previewImage) } let message = WXMediaMessage() message.title = title message.description = payload.description message.mediaObject = miniProgramObject let request = SendMessageToWXReq() request.bText = false request.message = message request.scene = scene.wxSceneValue return .success(request) } /// 读取 App 图标,用作微信分享缩略图兜底。 static func defaultThumbImage() -> UIImage? { guard let icons = Bundle.main.infoDictionary?["CFBundleIcons"] as? [String: Any], let primary = icons["CFBundlePrimaryIcon"] as? [String: Any], let iconFiles = primary["CFBundleIconFiles"] as? [String], let iconName = iconFiles.last else { return nil } return UIImage(named: iconName) } }