基于Firebase平台开发(八) —— 使用Firebase Cloud Messaging进行Push Notification的发送和接收(一)

版本记录

版本号 时间
V1.0 2021.04.10 星期六

前言

Firebase是一家实时后端数据库创业公司,它能帮助开发者很快的写出Web端和移动端的应用。自2014年10月Google收购Firebase以来,用户可以在更方便地使用Firebase的同时,结合Google的云服务。Firebase能让你的App从零到一。也就是说它可以帮助手机以及网页应用的开发者轻松构建App。通过Firebase背后负载的框架就可以简单地开发一个App,无需服务器以及基础设施。接下来几篇我们就一起看一下基于Firebase平台的开发。感兴趣的看下面几篇文章。
1. 基于Firebase平台开发(一) —— 基于ML Kit的iOS图片中文字的识别(一)
2. 基于Firebase平台开发(二) —— 基于ML Kit的iOS图片中文字的识别(二)
3. 基于Firebase平台开发(三) —— Firebase基本使用简介(一)
4. 基于Firebase平台开发(四) —— Firebase基本使用简介(二)
5. 基于Firebase平台开发(五) —— Firebase基本使用简介(三)
6. 基于Firebase平台开发(六) —— 基于Firebase Analytics的App使用率的跟踪(一)
7. 基于Firebase平台开发(七) —— iOS的A/B Test(一)

开始

首先我们看下写作内容:

了解如何在您的SwiftUI iOS应用中使用Firebase Cloud Messaging发送和接收远程推送通知。内容来自翻译

接着看下写作环境:

Swift 5, iOS 14, Xcode 12

接着就是正文啦。

人们与应用程序进行交互的方式因人而异。这可能使您很难知道您的客户何时使用您的应用程序,尤其是在您需要向他们提供重要信息时。推送通知(Push notification)提供了一种统一,一致的方式来通知您的应用程序用户几乎所有内容。

要接收推送通知,您的设备需要通过接收唯一的device tokenApple Push Notification service (APNs)注册。注册设备后,您可以通过使用device tokenAPNs发送请求来向设备发送推送通知。所有这些通信都需要通过某种Web服务器进行。

您可以实现自己的Web服务来与APNs通信,但是有更简单的选择。其中之一是Firebase Cloud Messaging(FCM)。有了Firebase Cloud Messaging(FCM),您就可以轻松使用易于使用的系统! FCM处理push notifications的云方面,使您无需编写自己的Web服务即可发送和接收推送。

在本教程中,您将学习如何:

  • 设置您的SwiftUI应用,以使用Swift Package Manager通过GoogleFirebase Cloud Messaging接收推送通知
  • 通过media发送通知
  • 接收基于主题的通知
  • 通过push notification的内容向您的应用添加信息
  • 获取有关推式通知打开频率的信息

要继续学习本教程,您需要:

  • 付费的Apple Developer Program会员资格,可在您的应用程序中使用push notifications
  • 一个Google Firebase帐户。您无需支付任何费用即可完成本教程。如果您选择在生产环境中使用Firebase,则可以在关联的站点上找到定价信息pricing information
  • 物理iOSiPadOS设备,因此您可以接收推送通知

注意:虽然您可以在模拟器上测试本地推送通知,但是您需要在物理设备上运行才能从FCM接收推送通知。

您将使用的应用程序Good News为用户提供了一个只有好消息的新闻提要。 这是因为即使世界上发生了坏事,也要记住也要发生好事!

在启动文件夹中,打开GoodNews.xcodeproj。 打开Project navigator,然后选择Good News target。 在Signing & Capabilities选项卡中,选择您的开发团队,然后输入一个新的Bundle Identifier。 现在,可以在真实设备或模拟器上进行构建和运行。

您会看到该应用程序具有用于向用户显示新闻文章的feed,以及一个供用户选择订阅主题的标签。


Configuring Firebase

接下来,您将学习如何配置Firebase

1. Creating the p8 Certificate

Firebase要求您将p8证书上传到您的应用程序。 这是一个特殊文件,其中包含允许Firebase发送通知的私钥。 要获取p8证书,请登录Apple Developer

选择Certificates, Identifiers & Profiles,然后转到Keys。 选择圆圈+按钮以创建新的key

给它命名并启用Apple Push Notifications service (APNs)服务。 选择Continue,然后在下一个屏幕上选择Register

请务必在此屏幕上记下以下三个项目:

  • 选择Downloadp8文件保存在本地。 您需要将其上传到Firebase。 退出此屏幕后,您将无法下载此文件(You cannot download this after leaving this screen)
  • Key ID复制并保存到文件中。
  • 复制并保存您的Apple membership ID。 该名称位于Membership Center右上角下的您的姓名旁边或Membership Details下面。

2. Setting up the Firebase Project

接下来,转到您的Firebase帐户,然后选择页面右上角的Go to console。 选择Add project,然后执行以下操作来创建您的项目:

  • 使用名称Good News
  • 启用Google Analytics
  • 选择Google Analytics的名称和国家/地区。
  • 使用默认的分析设置。

注意:如果您不需要跟踪用户与推送通知的交互方式,则可以禁用Google Analytics(分析)并跳过设置。

然后,您需要使用Apple p8和会员信息配置Firebase。 在您的Firebase项目中,选择Project Overview旁边的齿轮,然后选择Project settings

接下来,在项目设置的General部分下设置一个iOS应用:

从那里转到应用程序配置页面:

  • 为您的项目添加Bundle Identifier
  • 如果需要,可以将App nicknameApp Store ID保留为空白。

注册您的应用程序后,下载GoogleServices-Info.plist。 稍后,您需要使用此功能在您的应用中配置Firebase。 您可以为其余步骤选择Next。 您将不会使用CocoaPods,并且现在您可以忽略将初始化代码添加到您的应用程序的步骤。

接下来,转到Firebase项目设置中的Cloud Messaging,上传您的p8证书。 在APNs Authentication Key下,选择Upload

您会看到一个弹出窗口,要求您:

  • 上载从Apple下载的.p8文件。
  • 输入创建p8时保存的Key ID
  • 输入您的Apple membership ID

选择Upload以完成Firebase项目的设置。

3. Adding the Package

现在,您将使用Swift Package ManagerFirebase依赖(dependency)项添加到您的项目中。在Xcode中,选择File ▸ Swift Packages ▸ Add Package Dependency…。在Choose Package Repository弹出窗口中,输入https://github.com/firebase/firebase-ios-sdk.git

选择Next,保留默认选项,直到出现包含软件包列表的屏幕。 Xcode下载必要的数据时可能会花费一些时间。从列表中选择以下软件包:

  • FirebaseAnalytics
  • FirebaseMessaging

如果您不想收集有关用户如何使用推送通知的信息,请随时取消选中FirebaseAnalytics。添加这些软件包后,可能需要花费几分钟来添加和构建依赖关系。

注意:在撰写本文时,Firebase的最新版本是7.3

接下来,将GoogleService-Info.plist添加到您的Xcode项目的Assets组中的项目:

最后,您可以开始向您的应用添加代码!

4. Configuring Your App

首先打开Info.plist并添加以下条目:

  • Key: FirebaseAppDelegateProxyEnabled
  • Type: Boolean
  • Value: NO (Xcode will show this as 0)

默认情况下,FirebaseMessaging使用method swizzling处理推送通知。 您将自己处理所有代码,因此请使用刚添加的plist条目将其关闭。

接下来,您将一个app delegate添加到您的项目中,该委托将负责设置push notifications。 创建一个名为AppDelegate.swift的新文件,并将其代码替换为以下代码:

import UIKit
import Firebase
import FirebaseMessaging
import FirebaseAnalytics

class AppDelegate: NSObject, UIApplicationDelegate {
  func application(
    _ application: UIApplication, 
    didFinishLaunchingWithOptions 
      launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
  ) -> Bool {
    return true
  }
}

在上面的代码中,您首先导入必要的Firebase框架,然后实现UIApplicationDelegate协议。

接下来,在AppMain.swift中的AppMain中添加一个新属性:

@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate

这将使SwiftUI知道您新创建的应用程序代理。 现在,您可以开始配置Firebase

5. Launching Firebase

return之前,再次打开AppDelegate.swift并将以下内容添加到application(_:didFinishLaunchingWithOptions :)中:

// 1
FirebaseApp.configure()
// 2
FirebaseConfiguration.shared.setLoggerLevel(.min)

这是这样做的:

  • 1) 它将您的应用程序配置为可与Firebase一起使用。
  • 2) 它设置Firebase将记录的数量。 将此设置为min会减少您在调试器中看到的数据量。

由于您不希望Firebase通过swizzling自动处理notification code,因此您需要遵守UNUserNotificationCenterDelegate。 将以下内容添加到AppDelegate.swift的末尾:

extension AppDelegate: UNUserNotificationCenterDelegate {
  func userNotificationCenter(
    _ center: UNUserNotificationCenter,
    willPresent notification: UNNotification,
    withCompletionHandler completionHandler:
    @escaping (UNNotificationPresentationOptions) -> Void
  ) {
    completionHandler([[.banner, .sound]])
  }

  func userNotificationCenter(
    _ center: UNUserNotificationCenter,
    didReceive response: UNNotificationResponse,
    withCompletionHandler completionHandler: @escaping () -> Void
  ) {
    completionHandler()
  }
}

当通知到达或用户与之交互时,用户通知中心将调用这些方法。 您稍后再与他们合作。

6. Registering for Notifications

在配置了Firebase的情况下,您可以开始注册以接收通知。 将以下方法添加到UNUserNotificationCenterDelegate扩展中:

func application(
  _ application: UIApplication,
  didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
) {
  Messaging.messaging().apnsToken = deviceToken
}

当用户授予推送通知权限时,APNs将生成并注册token。 该token标识单个设备,因此您可以向其发送通知。 您将使用Firebase分发通知,此代码使该tokenFirebase中可用。

现在,您可以在return之前将以下内容添加到application(_:didFinishLaunchingWithOptions :)中:

// 1
UNUserNotificationCenter.current().delegate = self
// 2
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(
  options: authOptions) { _, _ in }
// 3
application.registerForRemoteNotifications()

代码是这样的:

  • 1) 将AppDelegate设置为UNUserNotificationCenter的代理。 在上一步中,您实现了必要的代理方法。
  • 2) 创建与您的应用程序将请求哪种推送通知权限相关的选项。 在这种情况下,您需要alerts, badges and sound
  • 3) 注册您的应用以获取远程通知。

接下来,在文件底部添加以下扩展名:

extension AppDelegate: MessagingDelegate {
  func messaging(
    _ messaging: Messaging,
    didReceiveRegistrationToken fcmToken: String?
  ) {
    let tokenDict = ["token": fcmToken ?? ""]
    NotificationCenter.default.post(
      name: Notification.Name("FCMToken"),
      object: nil,
      userInfo: tokenDict)
  }
}

MessagingFirebase的类,用于管理与推送通知相关的所有内容。 像许多iOS API一样,它具有一个称为MessagingDelegate的代理,您可以在上面的代码中实现该代理。 每当您的应用程序启动或Firebase更新您的token时,Firebase都会调用您刚添加的方法以使应用程序与其保持同步。

现在,在return之前,将以下内容添加到application(_:didFinishLaunchingWithOptions :)

Messaging.messaging().delegate = self

这会将AppDelegate设置为Messaging的代理。

您已经很接近了 —— 仅需一步就可以准备好您的应用。 在Xcode中,打开应用程序的项目设置,然后转到Signing & Capabilities。 选择+ Capability按钮。 在字段中搜索Push Notifications,然后按Enter

7. Sending Notifications

您的应用现在可以接收通知了! 在真实设备上构建并运行。 它的外观应与以前相同,但带有alert,要求您允许向您发送通知。 确保选择Allow

现在,转到您的Firebase项目,然后选择在Engage下找到的Cloud Messaging。 然后选择Send your first message

Step 1下,输入以下内容:

  • Notification title: Notification Test
  • Notification text: This is a test

接下来,从Step 2的下拉列表中选择您的应用:

使用Cloud Messaging控制台时,这将针对您的特定应用程序。

最后,选择Review,然后选择Publish。 在您的设备上,您会看到以下通知:

注意:如果您没有收到通知,请按照说明进行操作,以完成所有设置。 您可能错过了设置或无法上传.p8文件。 此外,您可以在AppDelegate.swift中实现application(_:didFailToRegisterForRemoteNotificationsWithError :),以打印出注册通知期间可能发生的任何错误。


Sending Data in Notifications

Firebase使通过Cloud Messaging控制台轻松发送通知中的其他数据成为可能。 在下一部分中,您将向通知有效内容中添加数据,以便在通知到达时向新闻源中添加新闻故事。

首先,在AppDelegate.swift中,在UNUserNotificationCenterDelegate扩展内添加以下代码:

private func process(_ notification: UNNotification) {
  // 1
  let userInfo = notification.request.content.userInfo
  // 2
  UIApplication.shared.applicationIconBadgeNumber = 0
  if let newsTitle = userInfo["newsTitle"] as? String,
    let newsBody = userInfo["newsBody"] as? String {
    let newsItem = NewsItem(title: newsTitle, body: newsBody, date: Date())
    NewsModel.shared.add([newsItem])
  }
}

这是您添加的内容:

  • 1) 这将在notification payload中获取所需的信息。
  • 2) 如果userInfo具有创建新闻项的值,则此代码创建一个新闻项并将其添加到启动程序项目的新闻提要中。

然后,在调用completionHandler之前,将以下内容添加到userNotificationCenter(_:willPresent:withCompletionHandler :)

process(notification)

每当您在应用程序处于前台时收到通知时,都会调用userNotificationCenter(_:willPresent:withCompletionHandler :)。 这样可以确保在用户使用您的应用程序时,在收到新通知时添加新闻项。

然后,在调用completionHandler之前,将以下代码添加到userNotification(_:didReceive:withCompletionHandler :)

process(response.notification)

用户点击通知时将调用userNotification(_:didReceive:withCompletionHandler :)。 同样,只要发生这种情况,您都会添加一个新闻项目。

构建并运行。 该应用看起来与以前相同,但可以处理其他通知数据。

返回控制台,创建一个新的通知。 和以前一样,输入通知标题和文本,然后选择您的应用程序。 然后转到Step 5,并在Custom data下添加以下项目:

  • Key: newsTitle
  • Value: Child Saves Kittens

这将向应用程序发送标题。 现在,添加主体的条目:

  • Key: newsBody
  • Value: Local child saves kittens from a tall tree

您添加的键和值将作为通知的userInfo发送,您将在process(_:)中进行解析。 发送通知。 如果您打开了应用程序,或者在到达通知时点按了通知,则您将在Feed中看到新的新闻项。

还可以处理来自后台进程的通知,而无需用户进行任何交互。 虽然这不在本教程的讨论范围之内,但我们的 Push Notifications video course 视频课程以及Push Notifications by Tutorials book 一书涵盖了对推送通知和其他许多主题的后台处理。


Subscribing to Topics

Firebase Cloud Messaging的另一个功能是订阅主题,这是一种使用户灵活自定义通知的好方法。 Firebase允许您使用主题标记特定的推送通知。 然后,您可以为您的用户订阅不同的主题。 例如,也许用户对诸如直接消息之类的重要通知感兴趣,但无需通知您应用程序feed中新的投递。 或者,您可以让您的用户关注特定的帖子或人员,接收有关群聊的推送通知,等等。

幸运的是,这很容易做到。 首先,打开TopicsModel.swift并在顶部添加以下导入:

import FirebaseMessaging

入门项目包括一个开关,该开关使用户可以切换他们感兴趣的主题。您将这些开关连接到Firebase

用以下代码替换subscribe(to :)unsubscribe(from :)

private func subscribe(to topic: String) {
  // 1
  Messaging.messaging().subscribe(toTopic: topic)
}

private func unsubscribe(from topic: String) {
  // 2. 
  Messaging.messaging().unsubscribe(fromTopic: topic)
}

就是这样! 这是您添加的内容:

  • 1) 当用户打开主题开关时,此代码将要求Firebase订阅该主题。
  • 2) 当用户关闭主题关闭时,此代码将要求Firebase取消订阅该主题。

构建并运行。 然后,转到Topics选项卡并打开Pets开关。

在控制台中,通过选择通知最右边的三个点来复制上一步的通知:

保留所有相同的内容,除了Step 2。将User Segment更改为Topic,然后为Message topic输入pets

现在,这只会向打开Pets切换开关的用户发送通知。发送通知,您将看到上一部分中的所有操作。再次复制通知,然后将主题更改为family并发送。由于您的应用未订阅该主题,因此Firebase不会将通知发送到您的设备。


Sending Images

到目前为止,您的通知都只包含文本。但是,如果您收到了很多通知,则知道通知可以包含丰富的内容,例如图像。如果您的通知向用户显示了与其内容相关的漂亮图片,那将是很好的选择。 Firebase再一次使此超级简单。

1. Adding a Notification Service Extension

要在push notifications中显示图片,您需要创建一个Notification Service Extension。这是您应用中的一个单独target,当您的用户收到推送通知时,该target将在后台运行。在iOS向用户显示通知之前,服务扩展(service extension)可以接收通知并更改其内容。您将使用Firebase在通知内发送图片网址。然后,您将使用内容扩展程序下载图片并将其添加到通知的内容中。

在Xcode中,转到File ▸ New ▸ Target…。搜索Notification Service Extension,然后选择Next。将名称设置为Good News Notifications,并将其配置为添加到项目Good News中,并嵌入到应用程序Good News中,如下面的屏幕截图所示:

选择Finish,然后在出现提示时选择Activate

当您将Firebase程序包添加到项目中时,它只是添加到了Good News target中,因此现在您需要向新的扩展程序中添加必要的依赖项。 打开应用程序的项目设置,然后在Targets下选择Good News Notifications。 在Frameworks and Libraries下,选择+按钮,然后搜索FirebaseMessaging。 然后,选择Add。 您的项目应反映以下图像:

2. Customizing Notifications

现在,打开NotificationService.swift。 在该文件中,您可以在用户看到通知之前自定义通知。

首先,将以下导入添加到文件顶部:

import FirebaseMessaging

接下来,将didReceive(_:withContentHandler :)的内容替换为以下内容:

self.contentHandler = contentHandler
bestAttemptContent = request.content
  .mutableCopy() as? UNMutableNotificationContent
guard let bestAttemptContent = bestAttemptContent else { return }
FIRMessagingExtensionHelper().populateNotificationContent(
  bestAttemptContent,
  withContentHandler: contentHandler)

通常,您必须搜索包含图像URL的字段,下载图像,然后以图像作为附件完成演示。在这里,您正在使用FirebaseFIRMessagingExtensionHelper通过一个简单的helper方法调用自动执行所有工作。

请记住,iOS仅给您有限的时间来下载附件图像。如果扩展程序的代码花费的时间太长,系统将调用serviceExtensionTimeWillExpire()。这使您有机会优雅地完成扩展中正在执行的所有操作,或仅按原样显示通知,这是默认实现。

确保Good News是您的active target,然后进行构建和运行。该应用程序应与以前的外观相同。

返回Firebase Cloud Messaging控制台,重复最后几步中您一直在使用的有关kittens的通知。确保您没有使用主题设为family的主题,因为您的应用未订阅该主题。

这次,在Notification image图像下的Step 1中,添加以下URL:https://koenig-media.raywenderlich.com/uploads/2021/01/good_news_petsicon.png

您的控制台应显示如下:

发送您的通知。 在您的设备上,您会看到一条带有kitten图片的通知,如下所示:


Analytics

现在,您正在发送和处理通知,请考虑了解用户如何与您的通知进行交互的重要性。 他们完全忽略了他们吗? 他们会定期打开吗? 借助Firebase AnalyticsCloud Messaging,您可以跟踪通知周围的事件。

到目前为止,您还缺少处理通知的一个步骤:将您收到的通知告知Firebase。 在AppDelegate.swift中,在方法末尾将以下内容添加到process(_ :)

Messaging.messaging().appDidReceiveMessage(userInfo)

构建并运行。 然后,停止调试并关闭您的应用程序。 接下来,在Cloud Messaging控制台中复制您先前的通知并发送。 收到通知后,请点击它以打开您的应用程序。

Cloud Messaging控制台中打开通知。 它将显示Sends计数,如下所示:

1. Tracking Custom Events

既然您的应用程序正在通知Firebase收到了通知,那么Opens次数也可能会有所增加。但是,这个数字不太可能会增加。您最多可能需要24小时才能在Firebase控制台中看到分析信息得到处理并呈现给您。实际上,即使之后,您也可能根本看不到这个数字的增加。在撰写本文时,Google可以针对iOSAndroid通知进行报告的内容受到限制。没关系-您仍然可以利用Google Analytics(分析)并跟踪自定义事件。

注意:本教程的此部分不是push notifications起作用所必需的。如果您不希望跟踪用户与推送通知的交互方式,请随时跳过此部分。如果您之前跳过了设置分析的步骤,则需要添加所需的程序包,并设置Firebase应用程序进行分析,然后再继续。

AppDelegate.swift中,在NewsModel.shared.add([newsItem])之后紧接在process(_:)中添加以下代码行:

Analytics.logEvent("NEWS_ITEM_PROCESSED", parameters: nil)

此代码使用FirebaseAnalytics(分析)来跟踪您是否已将新闻项添加到Feed中。 现在,在Messaging.messaging().appDidReceiveMessage(userInfo)之后添加以下代码行:

Analytics.logEvent("NOTIFICATION_PROCESSED", parameters: nil)

这将记录一个事件,说明您已处理该通知,无论该通知是否包含新闻项。

构建并运行。 再一次在Cloud Messaging控制台中复制您的通知并发送。 返回Firebase中,在Analytics下选择Events。 在这里,您将开始看到事件显示,如下所示:

在此示例中,您仅看到NEWS_ITEM_PROCESSED的条目。 这是因为Google尚未完成对该应用程序的分析处理。 同样,在跟随本教程的同时,您不太可能会在帐户中看到这两个事件中的任何一个。 让数小时到一天过去,然后再次检查。

现在,您可以使用Firebase Cloud Messaging发送和接收通知! 您也熟悉Firebase Analytics,可以从GoogleFirebase Analytics: Getting Started

如果您正在寻找如何开始使用Android上的推送通知,请查看Firebase Cloud Messaging for Android: Sending Push Notifications教程。

要了解有关没有FirebaseiOS上的推送通知的更多信息,请查看 Push Notifications Tutorial: Getting Started

如果您想深入了解推送通知并查找提供的所有内容,请查看我们的Push Notifications video course 课程以及Push Notifications by Tutorials book

后记

本篇主要讲述了使用Firebase Cloud Messaging进行Push Notification的发送和接收,感兴趣的给个赞或者关注~~~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容