从 Notification.Name 看 Swift 如何优雅的解决 String 硬编码

本文介绍了在Swift中NSNotification的使用方法,通过自定义NSNotification.Name的扩展,避免了字符串硬编码的问题,提高了代码的可读性和维护性。同时,文章还提供了一种使用RawRepresentable协议来优化埋点事件ID的方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前面

初学 Swift 中相关 NSNotification 的代码时, 发现了之前熟悉的 name 参数的类型由 Objective-C 中的 NSString 变成了 Notification.Name 类型. 并不是我期望的 String 类型...这是怎么回事呢?

Swift 中如何使用 Notification

那么, 在 Swift 中如何使用 Notification 呢, 以 post 为例.

NotificationCenter.default.post(name: Notification.Name.UIApplicationDidFinishLaunching, object: nil)
复制代码

其中, Notification.Name 是可以省略的, 就变为了

NotificationCenter.default.post(name: .UIApplicationDidFinishLaunching, object: nil)
复制代码

查看定义发现了 UIApplicationDidFinishLaunching 实际上是定义在结构体 NSNotification.Name 扩展(extension)中的的一个静态常量 (static let), 类型是 NSNotification.Name

extension NSNotification.Name {

    @available(iOS 4.0, *)
    public static let UIApplicationDidEnterBackground: NSNotification.Name

    @available(iOS 4.0, *)
    public static let UIApplicationWillEnterForeground: NSNotification.Name

    public static let UIApplicationDidFinishLaunching: NSNotification.Name
    
    ...
}
复制代码

所以我们才可以省略前面的 Notification.Name 直接使用 .UIApplicationDidFinishLaunching (Notification.Name 是 NSNotification.Name 的别名)

那我们如果想自定义一个通知怎么办呢, 直接可以仿照系统的方式, 我们自己为其增加一个 extension

extension Notification.Name {
	static let LoginStatusChanged = Notification.Name("LoginStatusChanged")
}
复制代码

其中 Notification.Name("LoginStatusChanged") 是其初始化方法, 可查看文档说明, 使用时, 可直接

NotificationCenter.default.post(name: .LoginStatusChanged, object: nil)
复制代码

因为这个通知 LoginStatusChanged 是定义在 Notification.Name 中的了, 所以也没必要在名称后面增加 Notification 等字样来表示这是一个通知了. 所以 Swift 中很多定义的名称都是非常简洁的.

对比 Objective-C 中的使用

对比之前在 Objective-C 中的使用

[[NSNotificationCenter defaultCenter] postNotificationName:"xxxxxxxxxx" object:nil

这样是非常容易出错的, 查这样的错误经常也是非常费时费力的, 也让人看来是非常不优雅的, 所以我们经常会进行宏定义或者是常量来防止字符串硬编码的问题.

但这实际上也是会带来一些令人头疼的问题的:

  1. 为了表明定义的字符串常量是一个通知名, 还要为其增加冗长的前缀或者是后缀
  2. 在开发中还经常会在代码补全中, 看到根本不和场合的一些常量名
  3. 通常为了使用方便和易于维护, 还会在将所有的通知定义在一个 xxDefine.h 的头文件中, 并在 pch 文件中引用, 此时如果增删或者修改了任意通知. 将会引起工程的全量重新编译. 也很是头疼. ...

所以, Swift 这种使用方式可谓是十分优雅.

举一反三

在开发中, 其实类似于 Notification 这种需要传递字符串的场景还有很多, 我们都可以使用这类使用方法进行优化.

场景

假设有这样一个场景, 定义一个类 EventReporter 用来处理埋点请求.

class EventReporter {

	static let shared = EventReporter()

	func reportEvent(_ eventId: String, withParams params: [String:Any]?) {
		// 埋点上报逻辑
	}
}
复制代码

相信这样的场景是很多人都见过的, 其中 eventId 是我们埋点的事件的ID, 那么该如何使用类似 Notification.Name 的方式来优化这类场景呢?

原理

从文档中看出 Notification.Name 实际上是遵从了一个协议 RawRepresentable

Overview

With a RawRepresentable type, you can switch back and forth between a custom type and an associated RawValue type without losing the value of the original RawRepresentable type. Using the raw value of a conforming type streamlines interoperation with Objective-C and legacy APIs and simplifies conformance to other protocols, such as Equatable, Comparable, and Hashable.

The RawRepresentable protocol is seen mainly in two categories of types: enumerations with raw value types and option sets.

简单的说就是, 使用 RawRepresentable 类型, 可以在自定义类型和其关联的 RawValue 类型之间来回切换, 可简化与 Objective-C 和传统 API 的交互, 两类:具有原始值类型和选项集的枚举(OptionSet, 其实 Swift 中的选项集枚举就是集成自 RawRepresentable 这个 Protocol 实现的), 说白了. 就是用一个类型封装一下我们想要使用的类型比如说 String, 来方便交互.

实现

使用起来很简单, 定义一个结构体来管理所有的埋点事件

struct EventID: RawRepresentable {
	
}
复制代码

根据编译器提示, 补全协议代码

struct EventID: RawRepresentable {
	typealias RawValue = String
	
	var rawValue: String
	
	init?(rawValue: String) {
		
	}
}
复制代码

从这就更容易看出其原理, 实际上内部的 rawValue 属性就是我们需要使用的 String 类型的事件名, 初始化方法传入该 String 对其赋值即可, 返回 EventID 类型的结构体

这里发现初始化方法返回的是一个 Optional 类型, 这样使用起来还需要解包, 不太方便, 可以看到 Notification.Name 的初始化方法返回并不是 Optional, 因为定义都是非常确定的事件名(通知名), 而且 init 方法中也不会产生异常, 所以此处没什么必要使用 Optional, 去掉 ? 即可

struct EventID: RawRepresentable {
	typealias RawValue = String
	
	var rawValue: String
	
	init(rawValue: String) {
		self.rawValue = rawValue
	}
}
复制代码

那么, 我们的上报类的代码可以修改如下, 这里还可以给 params 一个默认值, 这样如果没有参数时, 可以只传递 eventId 一个参数即可.

class EventReporter {

	static let shared = EventReporter()

	func reportEvent(_ eventId: EventID, withParams params: [String:Any]? = nil) {
		let event = eventId.rawValue
		// 埋点逻辑
	}
}
复制代码

最后, 定义一个埋点事件看看吧~, 推荐写到 extension 中易于维护.

extension EventID {
	static let LoginPageExposure = EventID(rawValue: "login_page_exposure")
}
复制代码

那么使用的时候,

EventReporter.shared.reportEvent(.LoginPageExposure)
复制代码

当我们打出 . 的时候, 代码补全就已经将 LoginPageExposure 提示给我们了.

总结

使用这种方式优化代码, 不仅可以让代码意图容易理解, 使用也更加简单不会出错. 而且也不会使得 LoginPageExposure 事件名在不想要出现的时候被代码补全功能强行弹出来.

Reference

<think>好的,用户的问题是:如何正确使用 notification.setLatestEventInfo 在 Android 中设置通知内容。根据引用[4],提到在SDK 23之后,这个方法被废弃了,改用Notification.Builder来构建。不过用户可能还是想了解旧方法的使用,或者需要维护旧代码,所以需要先解释setLatestEventInfo的用法,然后说明替代方案。 首先,我需要确认notification.setLatestEventInfo的参数。这个方法需要Context、通知标题、内容和PendingIntent。代码示例可能需要展示如何创建PendingIntent,并调用该方法。但根据引用[4],这个方法已经废弃,所以必须提醒用户改用Notification.Builder,并给出相应的代码示例。 然后,引用[3]提到使用Notification.Builder来构建通知,特别是覆盖相同ID的情况。这可能涉及到设置通知的ID和更新通知。同时,引用[2]讨论了Intent和Task的问题,可能需要设置正确的Flags和launchMode,确保通知点击后回到正确的Activity实例,而不是新建一个。这可能需要在PendingIntent中使用FLAG_UPDATE_CURRENT或FLAG_ACTIVITY_SINGLE_TOP等标志。 用户可能遇到的问题包括:方法废弃导致的编译错误,通知点击后的行为不符合预期,比如重复创建Activity实例。需要指导用户如何迁移到新的API,并处理Intent的Flags和Activity的launchMode。 最后,生成相关问题时要围绕通知的设置、兼容性处理、点击行为优化等方向。确保问题有针对性,帮助用户深入理解。</think>### 如何正确使用 `notification.setLatestEventInfo` 设置通知内容 #### 1. `setLatestEventInfo` 的旧版本用法(已废弃) 在 Android API 11(Android 3.0)之前,`setLatestEventInfo` 是设置通知标题、内容和点击事件的主要方法。其语法为: ```java notification.setLatestEventInfo(Context context, CharSequence title, CharSequence content, PendingIntent intent); ``` 示例代码: ```java Notification notification = new Notification(R.drawable.icon, "新通知", System.currentTimeMillis()); PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, new Intent(context, TargetActivity.class), PendingIntent.FLAG_UPDATE_CURRENT); notification.setLatestEventInfo(context, "标题", "内容", pendingIntent); notification.flags |= Notification.FLAG_ONGOING_EVENT; // 设置常驻通知(如QQ通知)[^2] ``` 此方法在 **Android 3.0(API 11)后已被废弃**,建议使用 `Notification.Builder` 替代[^4]。 --- #### 2. 新版本推荐使用 `Notification.Builder` 从 Android 3.0 开始,Google 推荐通过 `Notification.Builder` 构建通知,兼容性更好且功能更丰富: ```java Notification.Builder builder = new Notification.Builder(context) .setSmallIcon(R.drawable.icon) .setContentTitle("标题") .setContentText("内容") .setContentIntent(pendingIntent) .setAutoCancel(true); // 点击后自动取消通知 Notification notification = builder.build(); ``` 对于 **Android 4.1(API 16)及以上**,可使用 `NotificationCompat.Builder` 增强兼容性: ```java NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID) .setSmallIcon(R.drawable.icon) .setContentTitle("标题") .setContentText("内容") .setPriority(NotificationCompat.PRIORITY_DEFAULT); ``` --- #### 3. 关键注意事项 1. **兼容性处理** 若需支持旧版本设备,需在代码中判断 API 级别,例如: ```java if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { notification.setLatestEventInfo(...); // 旧方法 } else { // 使用 Notification.Builder } ``` 2. **PendingIntent 配置** 通知点击后若需返回已有的 Activity 实例而非新建,需在 `Intent` 中设置: ```java Intent intent = new Intent(context, MainActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); // 避免重复创建实例 PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); ``` 3. **常驻通知(如QQ通知)** 通过设置 `FLAG_ONGOING_EVENT` 可使通知常驻状态栏: ```java notification.flags |= Notification.FLAG_ONGOING_EVENT; ``` --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值