You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

430 lines
13 KiB

import Foundation
import UIKit
// MARK: -
class QRCodeParser {
// MARK: -
static func parseQRCode(_ content: String) -> ParsedQRData {
let trimmedContent = content.trimmingCharacters(in: .whitespacesAndNewlines)
// Wi-Fi
if trimmedContent.hasPrefix("WIFI:") {
return parseWiFi(trimmedContent)
}
// Email
if trimmedContent.hasPrefix("mailto:") {
return parseEmail(trimmedContent)
}
// Phone
if trimmedContent.hasPrefix("tel:") {
return parsePhone(trimmedContent)
}
// SMS
if trimmedContent.hasPrefix("SMSTO:") {
return parseSMS(trimmedContent)
}
// vCard
if trimmedContent.hasPrefix("BEGIN:VCARD") {
return parseVCard(trimmedContent)
}
// MeCard
if trimmedContent.hasPrefix("MECARD:") {
return parseMeCard(trimmedContent)
}
// Calendar
if trimmedContent.hasPrefix("BEGIN:VEVENT") {
return parseCalendar(trimmedContent)
}
// Instagram
if trimmedContent.contains("instagram.com") {
return parseInstagram(trimmedContent)
}
// Facebook
if trimmedContent.contains("facebook.com") {
return parseFacebook(trimmedContent)
}
// Spotify
if trimmedContent.hasPrefix("spotify:") {
return parseSpotify(trimmedContent)
}
// Twitter
if trimmedContent.contains("twitter.com") {
return parseTwitter(trimmedContent)
}
// WhatsApp
if trimmedContent.contains("wa.me") {
return parseWhatsApp(trimmedContent)
}
// Viber
if trimmedContent.hasPrefix("viber://") {
return parseViber(trimmedContent)
}
// Snapchat
if trimmedContent.hasPrefix("snapchat://") {
return parseSnapchat(trimmedContent)
}
// TikTok
if trimmedContent.contains("tiktok.com") {
return parseTikTok(trimmedContent)
}
// URL (URL)
if isValidURL(trimmedContent) {
return parseURL(trimmedContent)
}
// Location
if trimmedContent.hasPrefix("geo:") {
return parseLocation(trimmedContent)
}
//
return ParsedQRData(
type: .text,
title: "文本信息",
subtitle: trimmedContent.count > 50 ? String(trimmedContent.prefix(50)) + "..." : trimmedContent,
icon: "text.quote"
)
}
// MARK: - Wi-Fi
private static func parseWiFi(_ content: String) -> ParsedQRData {
let wifiInfo = content.replacingOccurrences(of: "WIFI:", with: "")
let components = wifiInfo.components(separatedBy: ";")
var ssid = ""
var password = ""
var encryption = "WPA"
for component in components {
if component.hasPrefix("S:") {
ssid = String(component.dropFirst(2))
} else if component.hasPrefix("P:") {
password = String(component.dropFirst(2))
} else if component.hasPrefix("T:") {
encryption = String(component.dropFirst(2))
}
}
let title = "Wi-Fi网络"
let subtitle = "网络名称: \(ssid)\n加密类型: \(encryption)\n密码: \(password.isEmpty ? "" : "已设置")"
return ParsedQRData(
type: .wifi,
title: title,
subtitle: subtitle,
icon: "wifi"
)
}
// MARK: - Email
private static func parseEmail(_ content: String) -> ParsedQRData {
let email = content.replacingOccurrences(of: "mailto:", with: "")
return ParsedQRData(
type: .mail,
title: "邮箱地址",
subtitle: email,
icon: "envelope"
)
}
// MARK: - Phone
private static func parsePhone(_ content: String) -> ParsedQRData {
let phone = content.replacingOccurrences(of: "tel:", with: "")
return ParsedQRData(
type: .phone,
title: "电话号码",
subtitle: phone,
icon: "phone"
)
}
// MARK: - SMS
private static func parseSMS(_ content: String) -> ParsedQRData {
let smsInfo = content.replacingOccurrences(of: "SMSTO:", with: "")
let components = smsInfo.components(separatedBy: ":")
let phone = components.first ?? ""
let message = components.count > 1 ? components[1] : ""
let title = "短信"
let subtitle = "号码: \(phone)\n内容: \(message)"
return ParsedQRData(
type: .sms,
title: title,
subtitle: subtitle,
icon: "message"
)
}
// MARK: - vCard
private static func parseVCard(_ content: String) -> ParsedQRData {
let lines = content.components(separatedBy: .newlines)
var name = ""
var phone = ""
var email = ""
for line in lines {
if line.hasPrefix("FN:") {
name = String(line.dropFirst(3))
} else if line.hasPrefix("TEL:") {
phone = String(line.dropFirst(4))
} else if line.hasPrefix("EMAIL:") {
email = String(line.dropFirst(6))
}
}
let title = "联系人信息"
let subtitle = "姓名: \(name)\n电话: \(phone)\n邮箱: \(email)"
return ParsedQRData(
type: .vcard,
title: title,
subtitle: subtitle,
icon: "person.crop.rectangle"
)
}
// MARK: - MeCard
private static func parseMeCard(_ content: String) -> ParsedQRData {
let mecardInfo = content.replacingOccurrences(of: "MECARD:", with: "")
let components = mecardInfo.components(separatedBy: ";")
var name = ""
var phone = ""
var email = ""
var address = ""
for component in components {
if component.hasPrefix("N:") {
name = String(component.dropFirst(2))
} else if component.hasPrefix("TEL:") {
phone = String(component.dropFirst(4))
} else if component.hasPrefix("EMAIL:") {
email = String(component.dropFirst(6))
} else if component.hasPrefix("ADR:") {
address = String(component.dropFirst(4))
}
}
let title = "联系人信息"
let subtitle = "姓名: \(name)\n电话: \(phone)\n邮箱: \(email)\n地址: \(address)"
return ParsedQRData(
type: .mecard,
title: title,
subtitle: subtitle,
icon: "person.crop.rectangle"
)
}
// MARK: - Calendar
private static func parseCalendar(_ content: String) -> ParsedQRData {
let lines = content.components(separatedBy: .newlines)
var summary = ""
var startTime = ""
var endTime = ""
var location = ""
var description = ""
for line in lines {
if line.hasPrefix("SUMMARY:") {
summary = String(line.dropFirst(8))
} else if line.hasPrefix("DTSTART:") {
startTime = String(line.dropFirst(8))
} else if line.hasPrefix("DTEND:") {
endTime = String(line.dropFirst(6))
} else if line.hasPrefix("LOCATION:") {
location = String(line.dropFirst(9))
} else if line.hasPrefix("DESCRIPTION:") {
description = String(line.dropFirst(12))
}
}
//
let formattedStartTime = formatCalendarTime(startTime)
let formattedEndTime = formatCalendarTime(endTime)
let title = "日历事件"
var subtitle = "事件: \(summary)\n开始: \(formattedStartTime)\n结束: \(formattedEndTime)"
if !location.isEmpty {
subtitle += "\n地点: \(location)"
}
if !description.isEmpty {
subtitle += "\n描述: \(description)"
}
return ParsedQRData(
type: .calendar,
title: title,
subtitle: subtitle,
icon: "calendar"
)
}
// MARK: -
private static func formatCalendarTime(_ timeString: String) -> String {
guard timeString.count >= 15 else { return timeString }
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyyMMdd'T'HHmmss"
if let date = dateFormatter.date(from: timeString) {
let displayFormatter = DateFormatter()
displayFormatter.dateStyle = .medium
displayFormatter.timeStyle = .short
return displayFormatter.string(from: date)
}
return timeString
}
// MARK: - Instagram
private static func parseInstagram(_ content: String) -> ParsedQRData {
let username = content.components(separatedBy: "/").dropLast().last ?? ""
return ParsedQRData(
type: .instagram,
title: "Instagram",
subtitle: "用户名: \(username)",
icon: "camera"
)
}
// MARK: - Facebook
private static func parseFacebook(_ content: String) -> ParsedQRData {
let pageId = content.components(separatedBy: "/").dropLast().last ?? ""
return ParsedQRData(
type: .facebook,
title: "Facebook",
subtitle: "页面: \(pageId)",
icon: "person.2"
)
}
// MARK: - Spotify
private static func parseSpotify(_ content: String) -> ParsedQRData {
let trackId = content.replacingOccurrences(of: "spotify:track:", with: "")
return ParsedQRData(
type: .spotify,
title: "Spotify",
subtitle: "曲目ID: \(trackId)",
icon: "music.note"
)
}
// MARK: - Twitter
private static func parseTwitter(_ content: String) -> ParsedQRData {
let username = content.components(separatedBy: "/").dropLast().last ?? ""
return ParsedQRData(
type: .twitter,
title: "Twitter",
subtitle: "用户名: \(username)",
icon: "bird"
)
}
// MARK: - WhatsApp
private static func parseWhatsApp(_ content: String) -> ParsedQRData {
let phone = content.replacingOccurrences(of: "https://wa.me/", with: "")
return ParsedQRData(
type: .whatsapp,
title: "WhatsApp",
subtitle: "电话号码: \(phone)",
icon: "message.circle"
)
}
// MARK: - Viber
private static func parseViber(_ content: String) -> ParsedQRData {
let phone = content.replacingOccurrences(of: "viber://contact?number=", with: "")
return ParsedQRData(
type: .viber,
title: "Viber",
subtitle: "电话号码: \(phone)",
icon: "bubble.left.and.bubble.right"
)
}
// MARK: - Snapchat
private static func parseSnapchat(_ content: String) -> ParsedQRData {
let username = content.replacingOccurrences(of: "snapchat://", with: "")
return ParsedQRData(
type: .snapchat,
title: "Snapchat",
subtitle: "用户名: \(username)",
icon: "camera.viewfinder"
)
}
// MARK: - TikTok
private static func parseTikTok(_ content: String) -> ParsedQRData {
let username = content.components(separatedBy: "@").last?.replacingOccurrences(of: "/", with: "") ?? ""
return ParsedQRData(
type: .tiktok,
title: "TikTok",
subtitle: "用户名: \(username)",
icon: "music.mic"
)
}
// MARK: - URL
private static func parseURL(_ content: String) -> ParsedQRData {
return ParsedQRData(
type: .url,
title: "网址链接",
subtitle: content,
icon: "link"
)
}
// MARK: - Location
private static func parseLocation(_ content: String) -> ParsedQRData {
let coordinates = content.replacingOccurrences(of: "geo:", with: "")
let coords = coordinates.components(separatedBy: ",")
let latitude = coords.first ?? ""
let longitude = coords.count > 1 ? coords[1] : ""
let title = "地理位置"
let subtitle = "纬度: \(latitude)\n经度: \(longitude)"
return ParsedQRData(
type: .location,
title: title,
subtitle: subtitle,
icon: "location"
)
}
// MARK: - URL
private static func isValidURL(_ string: String) -> Bool {
guard let url = URL(string: string) else { return false }
return UIApplication.shared.canOpenURL(url)
}
}