Appearance
Swift 入门(12):协议
协议(
protocol)就像“契约”,规定了 必须具备的属性和方法。
类、结构体、枚举都可以遵循协议。
1. 定义协议
swift
protocol Drawable {
func draw()
}2. 遵循协议
swift
struct Circle: Drawable {
func draw() {
print("画一个圆")
}
}
let c = Circle()
c.draw() // 画一个圆3. 属性要求
协议可以要求属性,但只规定 读/写权限,不实现。
swift
protocol Named {
var name: String { get set }
var description: String { get }
}
struct Person: Named {
var name: String
var description: String {
"我的名字是 \(name)"
}
}
let p = Person(name: "Alice")
print(p.description) // 我的名字是 Alice4. 多协议
一个类型可以同时遵循多个协议。
swift
protocol Flyable { func fly() }
protocol Swimmable { func swim() }
struct Duck: Flyable, Swimmable {
func fly() { print("鸭子会飞") }
func swim() { print("鸭子会游泳") }
}5. 协议作为类型
协议不仅是规范,也能作为类型使用。
swift
func test(d: Drawable) {
d.draw()
}
test(d: Circle()) // 画一个圆6. 协议继承
协议可以继承其它协议。
swift
protocol Animal {
func eat()
}
protocol Pet: Animal {
func play()
}
struct Dog: Pet {
func eat() { print("狗在吃") }
func play() { print("狗在玩") }
}7. 实战:系统协议实现
AuthenticationServices 协议示例
swift
import AuthenticationServices
class PasskeysManager: NSObject {
// 私有属性存储回调
private var callback: ((String) -> Void)?
}
// 使用 extension 分组实现协议
extension PasskeysManager: ASAuthorizationControllerDelegate {
// 认证成功回调
func authorizationController(controller: ASAuthorizationController,
didCompleteWithAuthorization authorization: ASAuthorization) {
callback?("认证成功")
callback = nil
}
// 认证失败回调
func authorizationController(controller: ASAuthorizationController,
didCompleteWithError error: Error) {
callback?("认证失败: \(error.localizedDescription)")
callback = nil
}
}
extension PasskeysManager: ASAuthorizationControllerPresentationContextProviding {
// 提供展示认证界面的窗口
func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
return UIApplication.shared.windows.first { $0.isKeyWindow } ?? UIWindow()
}
}
#### 协议方法详解:presentationAnchor
```swift
public func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
return UIApplication.shared.windows.first {
$0.isKeyWindow
} ?? ASPresentationAnchor()
}这个方法在做什么?
- 为 AuthenticationServices 提供一个"锚点窗口"
- 系统会在这个窗口上显示 Face ID/Touch ID 认证界面
语法分解
1. 方法签名
swift
// 协议要求的方法签名
func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor2. 核心逻辑:寻找活跃窗口
swift
UIApplication.shared.windows.first {
$0.isKeyWindow // 尾随闭包语法
}等价于:
swift
UIApplication.shared.windows.first(where: { window in
return window.isKeyWindow
})3. 类型别名
swift
// ASPresentationAnchor 实际上就是 UIWindow
typealias ASPresentationAnchor = UIWindow不同的实现方式对比
swift
// ✅ 你的实现:安全的空窗口降级
func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
return UIApplication.shared.windows.first { $0.isKeyWindow } ?? ASPresentationAnchor()
}
// ✅ 常见实现:返回空 UIWindow
func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
return UIApplication.shared.windows.first { $0.isKeyWindow } ?? UIWindow()
}
// ✅ iOS 13+ 现代写法
func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
return view.window ?? UIWindow()
}
// ❌ 不安全的写法(可能崩溃)
func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
return UIApplication.shared.windows.first { $0.isKeyWindow }! // 强制解包危险
}实际应用场景
swift
class PasskeysViewController: UIViewController {
private let passkeysManager = PasskeysManager()
override func viewDidLoad() {
super.viewDidLoad()
// 当用户点击认证按钮时
let authController = ASAuthorizationController(authorizationRequests: [/* ... */])
authController.delegate = passkeysManager
authController.presentationContextProvider = passkeysManager // 👈 会调用 presentationAnchor
authController.performRequests()
}
}为什么需要这个方法?
- 系统需要知道在哪个窗口显示认证界面
- 确保认证界面显示在正确的屏幕上(多屏设备)
- 处理应用的窗口层级关系
常见问题和解决方案
swift
// 问题:iOS 13+ 中 windows 数组可能为空
// 解决:使用 scene-based 方法
@available(iOS 13.0, *)
func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene {
return windowScene.windows.first { $0.isKeyWindow } ?? UIWindow()
}
return UIWindow()
}
// 问题:SwiftUI 环境中的处理
func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor {
return UIApplication.shared.windows.first { $0.isKeyWindow } ??
UIApplication.shared.windows.first ??
UIWindow()
}
### @objc 协议兼容
```swift
// 跨语言调用的协议设计
@objc public protocol AuthenticationAPI {
func register(_ options: String, callback: @escaping (String) -> Void)
func authenticate(_ options: String, callback: @escaping (String) -> Void)
}
@objc public class AuthManager: NSObject, AuthenticationAPI {
@objc public func register(_ options: String, callback: @escaping (String) -> Void) {
// 实现注册逻辑
callback("注册成功")
}
@objc public func authenticate(_ options: String, callback: @escaping (String) -> Void) {
// 实现认证逻辑
callback("认证成功")
}
}协议实现的最佳实践
swift
// ✅ 使用 extension 分组组织协议实现
extension MyClass: ProtocolA {
// ProtocolA 的方法
}
extension MyClass: ProtocolB {
// ProtocolB 的方法
}
// ✅ 使用 MARK 注释分组
class MyClass {
// MARK: - ProtocolA Implementation
// MARK: - ProtocolB Implementation
}总结
protocol用来定义 方法和属性的规范。- 类/结构体/枚举都可以遵循协议。
- 协议也可以 继承 和 组合。
- 使用
extension分组实现协议方法,代码更清晰。 @objc协议支持跨语言调用。- 协议能作为类型使用,是 Swift 面向协议编程的基础。