使用协议作为第一类类型
面向协议的编程可以用作核心 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)
}
}
}