vinhnx / swift_learning_notes

Notes from learning Swift
https://github.com/vinhnx/swift_learning_notes/issues
0 stars 0 forks source link

[Advanced] Protocol Oriented Programming 😱 #2

Open vinhnx opened 8 years ago

vinhnx commented 8 years ago

http://code.tutsplus.com/tutorials/protocol-oriented-programming-in-swift-2--cms-24979

// POP -- protocol oriented programming
// http://code.tutsplus.com/tutorials/protocol-oriented-programming-in-swift-2--cms-24979
protocol Drivable {
    var topSpeed: Int { get }
}

protocol Reversible {
    var reverseSpeed: Int { get }
}

protocol Transport {
    var seatCount: Int { get }
}

// protocol extesion: provide default behaviors for protocols
extension Drivable {
    func isFaterThan(item: Drivable) -> Bool {
        return self.topSpeed > item.topSpeed
    }
}

// extension for value types that ALSO conform to Reversible protocol
extension Drivable where Self: Reversible {
    func hasLargerRangeThan(item: Self) -> Bool {
        return (self.topSpeed + self.reverseSpeed) > (item.topSpeed + item.reverseSpeed)
    }
}

// example: define a MyCar struct value type that conform to three protocols
struct MyCar: Drivable, Reversible, Transport {
    var topSpeed = 150
    var reverseSpeed = 20
    var seatCount = 5
}

let sedan = MyCar()
let sportsCar = MyCar(topSpeed: 300, reverseSpeed: 30, seatCount: 3) // method definition is provided by default for struct
sportsCar.isFaterThan(sedan)
sportsCar.hasLargerRangeThan(sedan)

// extenns Swift standard library extension

extension CollectionType where Self.Generator.Element: Drivable {
    func averageTopSpeed() -> Int {
        var total = 0, count = 0
        for item in self {
            total += item.topSpeed
            count+=1
        }

        return (total/count)
    }
}

func averageReverseSpeed<T: CollectionType where T.Generator.Element: Reversible> (items: T) -> Int {
    var total = 0, count = 0
    for item in items {
        total += item.reverseSpeed
        count += 1
    }

    return (total/count)
}

let cars = [MyCar(), sedan, sportsCar]
cars.averageTopSpeed()
averageReverseSpeed(cars)
vinhnx commented 8 years ago

[updated] see http://khanlou.com/2016/05/protocol-oriented-programming/ for an updated

protocol Request {
    var baseURL: NSURL? { get }
    var method: String { get }
    var path: String { get }
    var parameters: Dictionary<String, String> { get }
    var headers: Dictionary<String, String> { get }
}

extension Request {
    var method : String { return "GET" }
    var path : String { return "" }
    var parameters : Dictionary<String, String> { return Dictionary() }
    var headers : Dictionary<String, String> { return Dictionary() }
}

protocol ConstructableRequest: Request {
    func buildRequest() -> NSURLRequest?
}

protocol JSONConstructableRequest: ConstructableRequest { }
extension JSONConstructableRequest {
    func buildRequest() -> NSURLRequest? {
        guard let baseURL = baseURL else { return nil }
        guard let URLComponents = NSURLComponents(URL: baseURL, resolvingAgainstBaseURL: true) else { return nil }
        URLComponents.path = (URLComponents.path ?? "") + path
        guard let URL = URLComponents.URL else { return nil }
        let request = NSMutableURLRequest(URL: URL)
        if method != "GET" {
            request.HTTPBody = try? NSJSONSerialization.dataWithJSONObject(parameters, options: [])
        }
        request.HTTPMethod = method
        return request
    }
}

protocol ResultParsing {
    associatedtype ParsedType
    func parseData(data: NSData) -> ParsedType?
}

protocol StringParsing: ResultParsing { }

extension StringParsing {
    func parseData(data: NSData) -> String? {
        return NSString(data: data, encoding: NSUTF8StringEncoding) as? String
    }
}

protocol JSONConstructable {
    static func fromData(data: NSData) -> Self?
}

struct User: JSONConstructable {
    static func fromData(data: NSData) -> User? {
        return User()
    }
}

struct Tweet: JSONConstructable {
    static func fromData(data: NSData) -> Tweet? {
        return Tweet()
    }
}

protocol JSONParsing: ResultParsing {
    associatedtype JSONType: JSONConstructable
    func parseData(data: NSData) -> JSONType?
}

extension JSONParsing {
    func parseData(data: NSData) -> JSONType? {
        return JSONType.fromData(data)
    }
}

protocol SendableRequest: ConstructableRequest, ResultParsing { }
extension SendableRequest {
    func sendRequest(success success: (result: ParsedType) -> (), failure: (error: ErrorType) -> ()) {
        let session = NSURLSession.sharedSession()
        guard let request = buildRequest() else { return }
        let task = session.dataTaskWithRequest(request, completionHandler: { (taskData, taskResponse, taskError) -> Void in
            if let taskError = taskError {
                failure (error: taskError)
            } else if let taskData = taskData {
                guard let result = self.parseData(taskData) else { return }
                success(result: result)
            }
        })
        task.resume()
    }
}

struct ZenRequest: Request, JSONConstructableRequest, SendableRequest, StringParsing {
    let baseURL = NSURL(string: "https://api.github.com/")
    let path: String = "zen"
}

ZenRequest().sendRequest(
    success: { string in
        print(string)
    },
    failure: { error in
        print(error)
})

struct UserRequest: Request, JSONConstructableRequest, SendableRequest, JSONParsing {
    let baseURL = NSURL(string: "https://api.github.com/")
    let path: String = "zen"

    typealias JSONType = User
}

UserRequest().sendRequest(
    success: { string in
        print(string)
    },
    failure: { error in
        print(error)
})

protocol oririented networking, http://khanlou.com/2015/06/protocol-oriented-networking/

vinhnx commented 8 years ago

Handle optional protocol in Swift via protocol extension http://ashfurrow.com/blog/protocols-and-swift/

protocol FoodConsumer {
    var calorieCount: Double { get set }
    var hydrationLevel: Double { get set }
}

protocol Food {
    func beConsumedBy<T: FoodConsumer>(consumer: T, grams: Double) -> T
}

extension FoodConsumer {
    func eat(food: Food, grams: Double) -> Self {
        return food.beConsumedBy(self, grams: grams)
    }
}

struct Cat: FoodConsumer {
    var calorieCount: Double = 0
    var hydrationLevel: Double = 0
}

struct Kibble: Food {
    let caloriesPerGram: Double = 40

    func beConsumedBy<T: FoodConsumer>(consumer: T, grams: Double) -> T {
        var newConsumer = consumer
        newConsumer.calorieCount += grams * caloriesPerGram
        return newConsumer
    }
}

struct FancyFeast: Food {
    let caloriesPerGram: Double = 80
    let milliLitresWaterPerGram: Double = 0.2

    func beConsumedBy<T: FoodConsumer>(consumer: T, grams: Double) -> T {
        var newConsumer = consumer
        newConsumer.calorieCount += grams * caloriesPerGram
        newConsumer.hydrationLevel += grams * milliLitresWaterPerGram
        return newConsumer
    }
}

let catFood = Kibble()
let wetFood = FancyFeast()
var dave = Cat()

dave = dave.eat(catFood, grams: 30)
dave = dave.eat(wetFood, grams: 20)