2016年IOS新技術回顧

WWDC2016,Apple發布了ios10,每次版本發布,都會帶來新的接口,新的機會,也能間接的看到Apple的未來發展方向。

這里就不討論iphone7、MacBook Pro、Apple Watch、Apple TV以及AirPods了。

下面回顧一下,Apple今年為我們帶來了什么

一,一些限制

IPV6

HTTPS

1.IPV6

由于IPv4最大的問題在于網絡地址資源有限,嚴重制約了互聯網的應用和發展。IPv6的使用,不僅能解決網絡地址資源數量的問題,而且也解決了多種接入設備連入互聯網的障礙

6月1號以后提交新版本需要支持IPV6-Only的網絡

與IPv4地址空間需要擴充迫在眉睫,越來越多的企業和移動電話運營商部署IPv6 DNS64和NAT64網絡。DNS64 / NAT64網絡是一種能使ipv6網絡。取決于應用程序的性質,過渡有不同的含義:如果您正在編寫一個客戶端應用程序使用高級網絡api,如NSURLSession和CFNetwork框架,你應該不需要改變任何東西為您的應用程序使用IPv6地址。如果項目中用了老的AFNetworking或ASIHTTPRequest等第三方網絡請求框架則可能需要做一下適配。因為,蘋果審核是不會讓你過得。


ipv6.png

客戶端向服務器端請求域名解析,首先通過DNS64 Server查詢IPv6的地址,如果查詢不到,再向DNS Server查詢IPv4地址,通過DNS64 Server合成一個IPV6的地址,最終將一個IPV6的地址返回給客戶端

關于IPV6的詳細介紹請見:
IPV6

2.HTTPS
前面一個大神已經詳細得介紹了HTTPS的相關知識,講得非常詳細,大家想看的話可以回顧一下。我在這也就不做過多贅述了。
HTTPS

  • 2017年1月1日起,蘋果App Store中的所有App都必須啟用 App Transport Security(ATS)安全功能。(但延期了)

"應用傳輸安全協議是與iOS9和OS X 10.11一同發布的,該協議需要應用程序通過HTTPS使用安全的網絡連接,以提高用戶的數據和隱私安全。
在2016年WWDC上我們宣布在今年年底之前,提交到App Store的應用程序必須支持應用傳輸安全協議。為了給你們充裕的時間去準備,這個截止日期已被延長,當新的截止日期確定的時候,我們將及時提供相關信息。"

  • App Transport Security(應用程序安全傳輸),簡稱 ATS,是蘋果在 iOS 9 中首次推出的一項隱私安全保護功能,啟用ATS后,它會屏蔽明文HTTP資源加載,強制App通過HTTPS連接網絡服務,通過傳輸加密保障用戶數據安全。
  • ATS要求服務器必須支持傳輸層安全(TLS)協議1.2以上版本;證書必須使用SHA256或更高的哈希算法簽名;必須使用2048位以上RSA密鑰或256位以上ECC算法等等,不滿足條件的證書,ATS都會拒絕連接。強制開啟ATS體現了蘋果一貫的隱私保護態度。 開發者需要HTTPS證書。

二,ios10

IMessage

SiriKit

Speech Recognition

User Notifications

Others

1.IMessage
IMessage獲得重大更新,提供消息氣泡框效果,手寫信息。同時內置的emoji表情也得到優化,除了圖片變大3倍外,還能將文字直接轉化成emoji表情。蘋果還特別為IMessage開辟了應用專區,所以你也可以做IMessage App了。

使用Messages framework創建兩種類型的app extensions:
Sticker packs
IMessage apps
第一種方式僅僅提供靜態的貼紙,貼紙包圖片,用戶可以發送內聯消息附加到消息氣泡中。貼紙包不需要任何代碼。你添加標簽圖像文件拖到貼紙包文件夾內的貼紙資源目錄。有效的圖像文件必須符合以下規格:

  • 圖片必須是png、apng、gif、jpeg中的一種
  • 圖片必須小于500k
  • 為了效果好,圖像不應少于100×100pt,或超過206×206pt
    如果你想做這些事的時候你就要用到第二種方式了

Use iMessage apps to:

  • Present a custom user interface inside the Messages app; see MSMessagesAppViewController.
  • Create a custom or dynamic sticker browser; see MSStickerBrowserViewController.
  • Insert text, stickers, or media files into the Messages app’s input field; see MSConversation.
  • Create interactive messages that carry app-specific data; see MSMessage.
  • Update interactive messages (for example, to create games or collaborative apps); see MSSession.
//example1作為一個貼紙瀏覽器

class MessagesViewController: MSMessagesAppViewController, MSStickerBrowserViewDataSource {
var stickers = [MSSticker]()
func loadStickers() {
for i in 1...2 {
if let url = Bundle.main.url(forResource: "Sticker \(i)", withExtension: "png"){
 do {
    let sticker = try MSSticker(contentsOfFileURL: url, localizedDescription: "")
    stickers.append(sticker)
    } catch {
    print(error)
    }
   }
 }
}
func createStickerBrowser() {
    let controller = MSStickerBrowserViewController(stickerSize: .large)
    
    addChildViewController(controller)
    view.addSubview(controller.view)
    
    controller.stickerBrowserView.backgroundColor = UIColor.purple
    controller.stickerBrowserView.dataSource = self
    
    view.topAnchor.constraint(equalTo: controller.view.topAnchor).isActive = true
    view.bottomAnchor.constraint(equalTo: controller.view.bottomAnchor).isActive = true
    view.leftAnchor.constraint(equalTo: controller.view.leftAnchor).isActive = true
    view.rightAnchor.constraint(equalTo: controller.view.rightAnchor).isActive = true
}

func numberOfStickers(in stickerBrowserView: MSStickerBrowserView) -> Int {
  return stickers.count
}

func stickerBrowserView(_ stickerBrowserView: MSStickerBrowserView, stickerAt index: Int) -> MSSticker {
  return stickers[index]
}

override func viewDidLoad() {
    super.viewDidLoad()
    loadStickers()
    createStickerBrowser()
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
}

// MARK: - Conversation Handling
    override func willBecomeActive(with conversation: MSConversation) {
    }
    override func didResignActive(with conversation: MSConversation) {
    }
    override func didReceive(_ message: MSMessage, conversation: MSConversation) {
    }
    override func didStartSending(_ message: MSMessage, conversation: MSConversation) {
    }
    override func didCancelSending(_ message: MSMessage, conversation: MSConversation) {
    }
    override func willTransition(to presentationStyle: MSMessagesAppPresentationStyle) {
    }
    override func didTransition(to presentationStyle: MSMessagesAppPresentationStyle) {
    }
}
example2(交互式)

class MessagesViewController: MSMessagesAppViewController {

@IBOutlet weak var stepper: UIStepper!

@IBAction func didPress(button sender: AnyObject) {
if let image = createImageForMessage(), let conversation = activeConversation {
    let layout = MSMessageTemplateLayout()
    layout.image = image
    layout.caption = "云萊塢"
    
    let message = MSMessage()
    message.layout = layout
    message.url = URL(string: "http://www.yunlaiwu.com/")
    
    conversation.insert(message, completionHandler: { (error) in
      print(error ?? "no error")
    })
   }
}

func createImageForMessage() -> UIImage? {
    let background = UIView(frame: CGRect(x: 0, y: 0, width: 300, height: 300))
    background.backgroundColor = UIColor.white
    
    let label = UILabel(frame: CGRect(x: 75, y: 75, width: 150, height: 150))
    label.font = UIFont.systemFont(ofSize: 56.0)
    label.backgroundColor = UIColor.red
    label.textColor = UIColor.white
    label.text = "\(Int(stepper.value))"
    label.textAlignment = .center
    label.layer.cornerRadius = label.frame.size.width/2.0
    label.clipsToBounds = true
    
    let imageView = UIImageView(frame: CGRect(x: 0, y: 200, width: 300, height: 100))
    imageView.image = UIImage(named:"Sticker 2")
    background.addSubview(label)
    background.addSubview(imageView)
    
    background.frame.origin = CGPoint(x: view.frame.size.width, y: view.frame.size.height)
    view.addSubview(background)
    
    UIGraphicsBeginImageContextWithOptions(background.frame.size, false, UIScreen.main.scale)
    background.drawHierarchy(in: background.bounds, afterScreenUpdates: true)
    let image = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    
    background.removeFromSuperview()
    
    return image
}

2.SiriKit
Apple 為我們開放了兩個framework讓我們更好的接入Siri
Intents、IntentsUI
Intents框架是必須支持的,支持基本應用程序和系統之間的通信。
IntentsUI框架是可選的,他提供了任務成功操作后的自定義UI接口。

它所包括的領域:

  • VoIP calling
  • Messaging
  • Payments
  • Photo
  • Workouts
  • Ride booking
  • CarPlay (automotive vendors only)
  • Restaurant reservations (requires additional support from Apple)

接入siri的注意事項:

  • 證書支持
  • plist文件支持你所要的操作事件


    siri.png
import Intents

// As an example, this class is set up to handle Message intents.
// You will want to replace this or add other intents as appropriate.
// The intents you wish to handle must be declared in the extension's Info.plist.

// You can test your example integration by saying things to Siri like:
// "Send a message using <myApp>"
// "<myApp> John saying hello"
// "Search for messages in <myApp>"

class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessagesIntentHandling, INSetMessageAttributeIntentHandling {

override func handler(for intent: INIntent) -> Any {
    // This is the default implementation.  If you want different objects to handle different intents,
    // you can override this and return the handler you want for that particular intent.
    return self
}

// MARK: - INSendMessageIntentHandling

// Implement resolution methods to provide additional information about your intent (optional).
func resolveRecipients(forSendMessage intent: INSendMessageIntent, with completion: @escaping ([INPersonResolutionResult]) -> Void) {
    if let recipients = intent.recipients {
    
    // If no recipients were provided we'll need to prompt for a value.
    if recipients.count == 0 {
    completion([INPersonResolutionResult.needsValue()])
    return
}

var resolutionResults = [INPersonResolutionResult]()
for recipient in recipients {
    let matchingContacts = [recipient] // Implement your contact matching logic here to create an array of matching contacts
    switch matchingContacts.count {
    case 2  ... Int.max:
    // We need Siri's help to ask user to pick one from the matches.
    resolutionResults += [INPersonResolutionResult.disambiguation(with: matchingContacts)]
    
    case 1:
    // We have exactly one matching contact
    resolutionResults += [INPersonResolutionResult.success(with: recipient)]
    
    case 0:
    // We have no contacts matching the description provided
    resolutionResults += [INPersonResolutionResult.unsupported()]
    
    default:
    break
    
    }
}
    completion(resolutionResults)
    }
}

func resolveContent(forSendMessage intent: INSendMessageIntent, with completion: @escaping (INStringResolutionResult) -> Void) {
        if let text = intent.content, !text.isEmpty {
        completion(INStringResolutionResult.success(with: text))
    } else {
        completion(INStringResolutionResult.needsValue())
    }
}

// Once resolution is completed, perform validation on the intent and provide confirmation (optional).

func confirm(sendMessage intent: INSendMessageIntent, completion: @escaping (INSendMessageIntentResponse) -> Void) {
    // Verify user is authenticated and your app is ready to send a message.
    
    let userActivity = NSUserActivity(activityType: NSStringFromClass(INSendMessageIntent.self))
    let response = INSendMessageIntentResponse(code: .ready, userActivity: userActivity)
    completion(response)
}

// Handle the completed intent (required).

func handle(sendMessage intent: INSendMessageIntent, completion: @escaping (INSendMessageIntentResponse) -> Void) {
    // Implement your application logic to send a message here.
    
    let userActivity = NSUserActivity(activityType: NSStringFromClass(INSendMessageIntent.self))
    let response = INSendMessageIntentResponse(code: .success, userActivity: userActivity)
    completion(response)
}

// Implement handlers for each intent you wish to handle.  As an example for messages, you may wish to also handle searchForMessages and setMessageAttributes.

// MARK: - INSearchForMessagesIntentHandling

func handle(searchForMessages intent: INSearchForMessagesIntent, completion: @escaping (INSearchForMessagesIntentResponse) -> Void) {
    // Implement your application logic to find a message that matches the information in the intent.
    
    let userActivity = NSUserActivity(activityType: NSStringFromClass(INSearchForMessagesIntent.self))
    let response = INSearchForMessagesIntentResponse(code: .success, userActivity: userActivity)
    // Initialize with found message's attributes
    response.messages = [INMessage(
    identifier: "identifier",
    content: "I am so excited about SiriKit!",
    dateSent: Date(),
    sender: INPerson(personHandle: INPersonHandle(value: "sarah@example.com", type: .emailAddress), nameComponents: nil, displayName: "Sarah", image: nil,  contactIdentifier: nil, customIdentifier: nil),
    recipients: [INPerson(personHandle: INPersonHandle(value: "+1-415-555-5555", type: .phoneNumber), nameComponents: nil, displayName: "John", image: nil,  contactIdentifier: nil, customIdentifier: nil)]
    )]
    completion(response)
}

// MARK: - INSetMessageAttributeIntentHandling

func handle(setMessageAttribute intent: INSetMessageAttributeIntent, completion: @escaping (INSetMessageAttributeIntentResponse) -> Void) {
    // Implement your application logic to set the message attribute here.
    
    let userActivity = NSUserActivity(activityType: NSStringFromClass(INSetMessageAttributeIntent.self))
    let response = INSetMessageAttributeIntentResponse(code: .success, userActivity: userActivity)
    completion(response)
}
}

3.Speech Recognition

語音識別技術原理

iOS 10引入了一個新的API,支持連續語音識別,可以將識別語音轉錄成文本??梢詧绦姓Z音實時轉錄和記錄音頻。

SpeechFramework框架中的重要類

SFSpeechRecognizer:這個類是語音識別的操作類,用于語音識別用戶權限的申請,語言環境的設置,語音模式的設置以及向Apple服務發送語音識別的請求。
SFSpeechRecognitionTask:這個類是語音識別服務請求任務類,每一個語音識別請求都可以抽象為一個SFSpeechRecognitionTask實例,其中SFSpeechRecognitionTaskDelegate協議中約定了許多請求任務過程中的監聽方法。
SFSpeechRecognitionRequest:語音識別請求類,需要通過其子類來進行實例化。
SFSpeechURLRecognitionRequest:通過音頻URL來創建語音識別請求。
SFSpeechAudioBufferRecognitionRequest:通過音頻流來創建語音識別請求。
SFSpeechRecognitionResult:語音識別請求結果類。
SFTranscription:語音轉換后的信息類。

當然,首先為了安全你得到plist文件中加權限

SFSpeechRecognizer.requestAuthorization { authStatus in
    if authStatus == SFSpeechRecognizerAuthorizationStatus.authorized {
        if let path = Bundle.main.url(forResource: "PPAP", withExtension: ".mp3") {
            let recognizer = SFSpeechRecognizer()
                let request = SFSpeechURLRecognitionRequest(url: path)
                    recognizer?.recognitionTask(with: request, resultHandler: { (result, error) in
                if let error = error {
                   print("There was an error: \(error)")
                } else {
                   print(result?.bestTranscription.formattedString)
                }
            })
        }
    }
}

4.User Notifications
支持了很多用戶定義的通知,并且可以捕捉到各個通知狀態的回調。以往的通知是大家想接收的都提前做好準備,然后一下全量分發,沒收到也不管了,也不關心發送者?,F在用戶通知做成了和網絡請求有點像,一個request,response的流程,也支持了error處理,可以在各個狀態的方法中做一些額外操作,并且也能取到一些字段,如發送者等。
更為重要的是,新增了UserNotificationsUI.framework框架,在收到通知的時候,可自定義通知UI樣式

let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert,.badge,.sound]) { (granted, error) in
    if granted {
        print("授權成功")
    }else{
        print("授權失敗")
    }
}

let content = UNMutableNotificationContent()
content.title = "云萊塢 PPAP"
content.body = "I have a pen I have an apple"
content.subtitle = "have a pineapple"
content.sound = UNNotificationSound.default()

let trigger1 = UNTimeIntervalNotificationTrigger(timeInterval: 61, repeats: true)

let request = UNNotificationRequest(identifier: "notificationTest", content: content, trigger:trigger1)

UNUserNotificationCenter.current().add(request) { (error) in
    if (error != nil) {
        print(error?.localizedDescription)
    }
}

5.Others

  • CallKit 集成了VoIP及信息標識,還有了黑名單功能,當然不是云萊塢的黑名單
//開始請求的方法,在打開設置-電話-來電阻止與身份識別開關時,系統自動調用
- (void)beginRequestWithExtensionContext:(CXCallDirectoryExtensionContext *)context;
//添加黑名單:根據生產的模板,只需要修改CXCallDirectoryPhoneNumber數組,數組內號碼要按升序排列
- (BOOL)addBlockingPhoneNumbersToContext:(CXCallDirectoryExtensionContext *)context;
// 添加信息標識:需要修改CXCallDirectoryPhoneNumber數組和對應的標識數組;CXCallDirectoryPhoneNumber數組存放的號碼和標識數組存放的標識要一一對應,CXCallDirectoryPhoneNumber數組內的號碼要按升序排列
- (BOOL)addIdentificationPhoneNumbersToContext:(CXCallDirectoryExtensionContext *)context;
  • Wide Color 寬域顏色
@available(iOS 10.0, *)
public init(displayP3Red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat)
  • Adapting to the True Tone Display 真彩色顯示

UIWhitePointAdaptivityStyle

共有五種選擇:
UIWhitePointAdaptivityStyleStandard 標準
UIWhitePointAdaptivityStyleReading 閱讀
UIWhitePointAdaptivityStylePhoto 圖片
UIWhitePointAdaptivityStyleVideo 視頻
UIWhitePointAdaptivityStyleGame 游戲

  • App Search Enhancements 應用搜索增強

  • Widget Enhancements 鎖屏部件增強

  • News Publisher Enhancements 新聞出版商增強

  • Apple Pay Enhancements 支付增強

  • Security and Privacy Enhancements 安全和保密性增強

三,swift3.0

1.去除了swift2系列棄用得特性
2.語法精簡,更加哲理性(這詞用得好啊)
3.響應式,函數式,面向協議等語言現代化。

let queue = dispatch_queue_create("this is Swift 2.2", nil)
dispatch_async(queue) {
    print("Swift 2.2 queue")
}

Swift 3 取消了這種冗余的寫法,而采用了更為面向對象的方式:

let queue = DispatchQueue(label: "this is Swift 3.0")
queue.async {
    print("Swift 3 queue")
}

當然,問題還很多,但4.0就快來了

四,watchOS3.0,tvOS,AppStore,macOS Sierra...

1.watchOS3.0

  • 新增智能回覆與 Scribble 功能,可在表面手寫文字用以回覆訊息。
  • 新增 SOS 功能,其支持全球的語言與緊急電話,危急時刻可立即通話或發出訊息。
  • 新增的《Breathe》App 能提醒用戶休息片刻、多做深呼吸。
  • 《活動記錄》App 加入 Activity Sharing 功能,能將您個人的活動量分享給親朋好友或健身教練,與他人運動競爭或幫助維持健康?!痘顒佑涗洝犯槍Τ俗喴蔚臍堈嫌脩魞灮O計,提醒何時該稍做休息。

2.tvOS

整合 Siri,用以加強語音搜索功能。此外,tvOS 將新增 Single Sign-on 功能,僅需登入一次即可瀏覽所有付費電視頻道。
當然,國內是被墻的

3.AppStore
AppStore 2.0 變革:更改拆帳比例、置入搜索廣告、縮短審查時間

4.macOS Sierra

  • 新增 Auto Unlock 功能,當用戶戴著配對認證的 Apple Watch 開啟 Mac 電腦立即自動登入,無須輸入密碼。
  • 新增 Universal Clipboard 功能,使用 iCloud 可跨蘋果設備復制與貼上文字、照片、影片等。
  • 使用 iCloud Drive,將允許多部 Mac 電腦共用桌面,而且 iPhone、iPad 也能存取桌面上的檔案。
  • 新增 Optimized Storage 功能,當 Mac 電腦容量不足,自動將不常用的檔案上傳 iCloud,提醒用戶清空垃圾桶。
  • 線上付款將支持 Apple Pay,搭配 iPhone 或 Apple Watch 快速且安全地完成網購的付款動作。
  • 包括《地圖》、《郵件》、《Pages》、《Numbers》、《Keynote》甚至第三方 Apps 將支持分頁功能。
  • Safari 與 iTunes 新增 Picture in Picture 功能,以子母畫面的方式觀賞影片。
  • 加入 Siri 功能,可在 macOS Sierra 當中語音搜索訊息、文件、照片、網頁等,甚至語音建立備忘錄或開啟 FaceTime 視訊。登陸 macOS Sierra 之后,Siri 終能跨蘋果四大平臺使用。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,606評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,582評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,540評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,028評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,801評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,223評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,294評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,442評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,976評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,800評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,996評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,543評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,233評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,662評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,926評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,702評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,991評論 2 374

推薦閱讀更多精彩內容