MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

SwiftUI 与外部URL交互

2023-11-231.9k 阅读

1. SwiftUI 中 URL 的基础认知

在 SwiftUI 开发中,URL(Uniform Resource Locator)是一个关键概念,它用于标识和定位互联网上的资源。无论是加载网页、获取远程数据,还是与外部应用程序进行交互,URL 都扮演着重要角色。

在 Swift 中,URL 是一个结构体,用于表示一个 URL。我们可以通过多种方式来初始化 URL 对象。例如,通过一个字符串字面量创建:

let urlString = "https://www.example.com"
if let url = URL(string: urlString) {
    // 成功创建 URL 对象
} else {
    // 无效的 URL 字符串
}

这里使用 URL(string:) 初始化器,它尝试将给定的字符串转换为 URL。如果字符串格式不正确,初始化将失败,返回 nil

另一种常见的初始化方式是使用 URLComponentsURLComponents 允许我们更细致地构建 URL 的各个部分,如协议、主机、路径、查询参数等。

var components = URLComponents()
components.scheme = "https"
components.host = "www.example.com"
components.path = "/api/data"
let queryItem = URLQueryItem(name: "param1", value: "value1")
components.queryItems = [queryItem]
if let url = components.url {
    // 构建成功的 URL
}

在上述代码中,我们首先创建一个 URLComponents 对象,然后分别设置其 schemehostpath 以及添加一个查询参数。最后通过 url 属性获取完整的 URL

2. 在 SwiftUI 视图中打开外部 URL

在 SwiftUI 应用中,有时需要打开外部的网页链接或者跳转到其他应用程序。这可以通过 UIApplication.shared.open(_:options:completionHandler:) 方法来实现。然而,在 SwiftUI 中,为了更方便地与视图集成,我们可以创建一个自定义的视图修饰符。

首先,我们定义一个 OpenURLAction 结构体,用于封装打开 URL 的逻辑:

struct OpenURLAction: ViewModifier {
    let url: URL
    func body(content: Content) -> some View {
        content.onTapGesture {
            if UIApplication.shared.canOpenURL(url) {
                UIApplication.shared.open(url, options: [:], completionHandler: nil)
            }
        }
    }
}

在这个结构体中,我们接受一个 URL 参数,并使用 onTapGesture 修饰符为视图添加点击手势。当视图被点击时,首先检查应用是否可以打开该 URL(通过 UIApplication.shared.canOpenURL(url)),如果可以,则调用 UIApplication.shared.open(url, options: [:], completionHandler: nil) 来打开 URL。

然后,我们可以为 View 扩展一个 openURL 方法,以便在任何视图上方便地使用这个修饰符:

extension View {
    func openURL(_ url: URL) -> some View {
        modifier(OpenURLAction(url: url))
    }
}

现在,我们可以在 SwiftUI 视图中使用这个扩展了。例如:

struct ContentView: View {
    let url = URL(string: "https://www.apple.com")!
    var body: some View {
        Text("打开苹果官网")
          .openURL(url)
          .foregroundColor(.blue)
          .underline()
    }
}

在上述代码中,Text 视图通过 openURL 方法添加了打开指定 URL 的功能。当用户点击文本时,应用会尝试打开苹果官网。

3. 处理外部 URL 回调

有时候,我们的应用可能需要处理从外部应用返回的 URL。例如,当使用第三方登录时,登录成功后第三方应用会将带有授权信息的 URL 返回给我们的应用。

在 iOS 中,处理外部 URL 回调需要在 AppDelegate 中实现 application(_:open:options:) 方法。然而,在 SwiftUI 应用中,我们可以使用 UIApplicationDelegateAdaptor 来将这个功能集成到 SwiftUI 环境中。

首先,我们创建一个 AppDelegate 类,用于处理 URL 回调:

class AppDelegate: NSObject, UIApplicationDelegate {
    func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
        // 处理返回的 URL
        print("接收到的 URL: \(url)")
        return true
    }
}

在这个方法中,我们可以对返回的 URL 进行解析和处理,例如提取授权码等信息。

然后,在 SwiftUI 的 @main 结构体中,我们使用 UIApplicationDelegateAdaptor 来关联这个 AppDelegate

@main
struct MyApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

这样,当应用接收到外部返回的 URL 时,AppDelegate 中的 application(_:open:options:) 方法就会被调用,我们可以在其中进行相应的处理。

4. 使用 WebView 在 SwiftUI 中显示网页

在某些情况下,我们可能需要在应用内部显示网页内容,而不是跳转到外部浏览器。这可以通过在 SwiftUI 中嵌入 WebView 来实现。在 iOS 14 及以上版本,我们可以使用 WKWebView 来创建一个 WebView

首先,我们创建一个 WebView 结构体,它封装了 WKWebView 的功能:

import WebKit
struct WebView: UIViewRepresentable {
    let url: URL
    func makeUIView(context: Context) -> WKWebView {
        return WKWebView()
    }
    func updateUIView(_ uiView: WKWebView, context: Context) {
        let request = URLRequest(url: url)
        uiView.load(request)
    }
}

在这个结构体中,我们实现了 UIViewRepresentable 协议。makeUIView(context:) 方法用于创建 WKWebView 实例,而 updateUIView(_:context:) 方法用于加载指定的 URL。

然后,我们可以在 SwiftUI 视图中使用这个 WebView

struct ContentView: View {
    let url = URL(string: "https://www.example.com")!
    var body: some View {
        WebView(url: url)
          .edgesIgnoringSafeArea(.all)
    }
}

在上述代码中,WebView 被添加到 ContentView 中,并填充整个安全区域。这样,应用就可以在内部显示指定的网页内容。

5. 与第三方应用通过 URL 交互

与第三方应用通过 URL 交互是一项强大的功能,可以实现更多丰富的应用场景。例如,与地图应用交互以显示特定位置,或者与社交媒体应用分享内容等。

5.1 与地图应用交互

在 iOS 中,我们可以通过 maps:// URL 方案与苹果地图应用进行交互。例如,要在地图上显示某个特定位置,可以构建如下 URL:

let latitude: CLLocationDegrees = 37.7749
let longitude: CLLocationDegrees = -122.4194
let urlString = "maps://?q=\(latitude),\(longitude)"
if let url = URL(string: urlString) {
    if UIApplication.shared.canOpenURL(url) {
        UIApplication.shared.open(url, options: [:], completionHandler: nil)
    }
}

上述代码构建了一个 maps:// URL,其中 q 参数指定了经纬度。如果设备上安装了苹果地图应用,并且可以打开该 URL,应用就会跳转到地图应用并显示指定位置。

5.2 与社交媒体应用分享内容

与社交媒体应用分享内容通常通过 social:// URL 方案实现。例如,与 Twitter 分享文本内容:

let text = "这是一条要分享的推文"
let escapedText = text.addingPercentEncoding(withAllowedCharacters:.urlQueryAllowed)!
let urlString = "twitter://post?message=\(escapedText)"
if let url = URL(string: urlString) {
    if UIApplication.shared.canOpenURL(url) {
        UIApplication.shared.open(url, options: [:], completionHandler: nil)
    }
}

在上述代码中,我们首先对要分享的文本进行 URL 编码,然后构建一个 twitter:// URL,其中 message 参数包含了要分享的文本。如果设备上安装了 Twitter 应用并且可以打开该 URL,应用会跳转到 Twitter 应用并显示分享界面。

不同的第三方应用可能有不同的 URL 方案和参数格式,需要根据具体应用的文档来进行构建和交互。

6. 处理 URL 中的参数和查询字符串

当我们处理外部 URL 或者构建用于交互的 URL 时,经常需要处理其中的参数和查询字符串。在 Swift 中,解析和构建这些部分有多种方法。

6.1 解析 URL 参数

假设我们有一个 URL https://www.example.com/api/data?param1=value1&param2=value2,要解析其中的参数,可以使用 URLComponents

let urlString = "https://www.example.com/api/data?param1=value1&param2=value2"
if let url = URL(string: urlString),
   let components = URLComponents(url: url, resolvingAgainstBaseURL: true),
   let queryItems = components.queryItems {
    for item in queryItems {
        print("参数 \(item.name): \(item.value ?? "无值")")
    }
}

在上述代码中,我们首先将 URL 字符串转换为 URL 对象,然后使用 URLComponents 解析 URL。通过 queryItems 属性,我们可以获取到所有的查询参数,并进行遍历处理。

6.2 构建包含参数的 URL

要构建一个包含参数的 URL,我们可以使用 URLComponents 来逐步构建。

var components = URLComponents()
components.scheme = "https"
components.host = "www.example.com"
components.path = "/api/data"
let param1 = URLQueryItem(name: "param1", value: "value1")
let param2 = URLQueryItem(name: "param2", value: "value2")
components.queryItems = [param1, param2]
if let url = components.url {
    print("构建的 URL: \(url)")
}

在这个例子中,我们创建了一个 URLComponents 对象,设置了协议、主机、路径,并添加了两个查询参数。最后通过 url 属性获取完整的 URL。

7. 安全性与 URL 交互

在进行 URL 交互时,安全性是至关重要的。以下是一些需要注意的安全方面:

7.1 验证 URL 的来源

当处理外部返回的 URL 或者打开外部 URL 时,要确保 URL 的来源是可信的。对于返回的 URL,应该验证其是否来自合法的第三方应用。例如,在处理第三方登录回调 URL 时,检查 URL 中的域名是否与预期的第三方服务提供商匹配。

7.2 防止 URL 注入攻击

URL 注入攻击是一种常见的安全威胁,攻击者可能通过修改 URL 参数来执行恶意操作。为了防止这种攻击,对从 URL 中获取的参数进行严格的验证和过滤。例如,只接受预期格式的参数值,避免直接使用未经过滤的参数值进行数据库查询等操作。

7.3 使用 HTTPS 协议

在与远程服务器进行数据交互时,始终使用 HTTPS 协议。HTTPS 协议通过加密传输数据,防止数据在传输过程中被窃取或篡改。当构建 URL 时,确保 schemehttps,特别是在传输敏感信息时。

8. 高级 URL 交互场景

除了上述常见的 URL 交互场景,还有一些更高级的应用。

8.1 深度链接(Deep Linking)

深度链接允许用户通过点击 URL 直接进入应用内的特定页面或功能。在 SwiftUI 应用中实现深度链接,需要在 AppDelegate 中处理 application(_:continue:restorationHandler:) 方法(适用于 iOS 13 及以上)。

首先,在 Info.plist 文件中配置支持的 URL 方案。例如,添加一个自定义的 URL 方案 myapp://

<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleURLName</key>
        <string>com.example.myapp</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>myapp</string>
        </array>
    </dict>
</array>

然后,在 AppDelegate 中处理深度链接:

class AppDelegate: NSObject, UIApplicationDelegate {
    func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
        if userActivity.activityType == NSUserActivityTypeBrowsingWeb,
           let url = userActivity.webpageURL,
           url.scheme == "myapp" {
            // 处理深度链接的逻辑
            print("处理深度链接: \(url)")
            return true
        }
        return false
    }
}

在上述代码中,我们检查 userActivity 的类型是否为 NSUserActivityTypeBrowsingWeb,并且 URL 的 scheme 是否为我们定义的 myapp。如果匹配,则可以在其中处理深度链接的逻辑,例如导航到应用内的特定视图。

8.2 动态生成和处理 URL

在一些复杂的应用场景中,可能需要根据用户的操作动态生成 URL,并在后续处理这些 URL。例如,一个电商应用可能根据用户选择的商品动态生成一个分享 URL,其他用户点击该 URL 后可以直接进入商品详情页面。

动态生成 URL 可以通过结合用户输入和固定的 URL 模板来实现。例如:

let productId = "12345"
let baseUrl = "https://www.example.com/product/"
let urlString = baseUrl + productId
if let url = URL(string: urlString) {
    // 生成的 URL 可用于分享或其他操作
}

在处理这些动态生成的 URL 时,同样需要在 AppDelegate 或相关的视图逻辑中进行解析和导航到对应的页面。

通过深入理解和掌握 SwiftUI 与外部 URL 的交互,开发者可以为应用添加丰富的功能,提升用户体验,并与其他应用和服务进行有效的集成。无论是打开外部链接、处理回调,还是实现深度链接等高级功能,都需要严谨的编码和对安全性的重视。