Files
suixinkan_uikit/suixinkan/Core/WeChat/WeChatShareService.swift
T

183 lines
6.9 KiB
Swift

//
// 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<SendMessageToWXReq, WeChatShareResult> {
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)
}
}