使用協議作為第一類型別
面向協議的程式設計可以用作核心 Swift 設計模式。
不同型別能夠符合相同的協議,值型別甚至可以符合多種協議,甚至提供預設方法實現。
最初定義的協議可以表示具有特定或通用型別的常用屬性和/或方法。
protocol ItemData {
var title: String { get }
var description: String { get }
var thumbnailURL: NSURL { get }
var created: NSDate { get }
var updated: NSDate { get }
}
protocol DisplayItem {
func hasBeenUpdated() -> Bool
func getFormattedTitle() -> String
func getFormattedDescription() -> String
}
protocol GetAPIItemDataOperation {
static func get(url: NSURL, completed: ([ItemData]) -> Void)
}
可以建立 get 方法的預設實現,但是如果需要,符合型別可以覆蓋實現。
extension GetAPIItemDataOperation {
static func get(url: NSURL, completed: ([ItemData]) -> Void) {
let date = NSDate(
timeIntervalSinceNow: NSDate().timeIntervalSince1970
+ 5000)
// get data from url
let urlData: [String: AnyObject] = [
"title": "Red Camaro",
"desc": "A fast red car.",
"thumb":"http://cars.images.com/red-camaro.png",
"created": NSDate(), "updated": date]
// in this example forced unwrapping is used
// forced unwrapping should never be used in practice
// instead conditional unwrapping should be used (guard or if/let)
let item = Item(
title: urlData["title"] as! String,
description: urlData["desc"] as! String,
thumbnailURL: NSURL(string: urlData["thumb"] as! String)!,
created: urlData["created"] as! NSDate,
updated: urlData["updated"] as! NSDate)
completed([item])
}
}
struct ItemOperation: GetAPIItemDataOperation { }
符合 ItemData 協議的值型別,此值型別也能夠符合其他協議。
struct Item: ItemData {
let title: String
let description: String
let thumbnailURL: NSURL
let created: NSDate
let updated: NSDate
}
這裡擴充套件了 item 結構以符合顯示項。
extension Item: DisplayItem {
func hasBeenUpdated() -> Bool {
return updated.timeIntervalSince1970 >
created.timeIntervalSince1970
}
func getFormattedTitle() -> String {
return title.stringByTrimmingCharactersInSet(
.whitespaceAndNewlineCharacterSet())
}
func getFormattedDescription() -> String {
return description.stringByTrimmingCharactersInSet(
.whitespaceAndNewlineCharacterSet())
}
}
使用靜態 get 方法的示例呼叫站點。
ItemOperation.get(NSURL()) { (itemData) in
// perhaps inform a view of new data
// or parse the data for user requested info, etc.
dispatch_async(dispatch_get_main_queue(), {
// self.items = itemData
})
}
不同的用例需要不同的實現。這裡的主要思想是展示不同型別的一致性,其中協議是設計焦點的主要點。在此示例中,API 資料可能有條件地儲存到 Core Data 實體。
// the default core data created classes + extension
class LocalItem: NSManagedObject { }
extension LocalItem {
@NSManaged var title: String
@NSManaged var itemDescription: String
@NSManaged var thumbnailURLStr: String
@NSManaged var createdAt: NSDate
@NSManaged var updatedAt: NSDate
}
此處 Core Data 支援的類也可以符合 DisplayItem 協議。
extension LocalItem: DisplayItem {
func hasBeenUpdated() -> Bool {
return updatedAt.timeIntervalSince1970 >
createdAt.timeIntervalSince1970
}
func getFormattedTitle() -> String {
return title.stringByTrimmingCharactersInSet(
.whitespaceAndNewlineCharacterSet())
}
func getFormattedDescription() -> String {
return itemDescription.stringByTrimmingCharactersInSet(
.whitespaceAndNewlineCharacterSet())
}
}
// In use, the core data results can be
// conditionally casts as a protocol
class MyController: UIViewController {
override func viewDidLoad() {
let fr: NSFetchRequest = NSFetchRequest(
entityName: "Items")
let context = NSManagedObjectContext(
concurrencyType: .MainQueueConcurrencyType)
do {
let items: AnyObject = try context.executeFetchRequest(fr)
if let displayItems = items as? [DisplayItem] {
print(displayItems)
}
} catch let error as NSError {
print(error.localizedDescription)
}
}
}