Last active
August 29, 2015 14:16
-
-
Save AliSoftware/80bef0d64cae97d527b3 to your computer and use it in GitHub Desktop.
Swift-Concepts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Without currying | |
func multiplier(x: Int) -> (by: Int) -> Int { | |
func mult(y: Int) -> Int { | |
return x*y | |
} | |
return mult | |
} | |
let doubler = multiplier(2) | |
doubler(by: 7) // returns 14 | |
// The exact same function written using currying | |
func multiplier2(x: Int)(by: Int) -> Int { | |
return x*by | |
} | |
let doubler2 = multiplier2(2) | |
doubler2(by: 7) // returns 14 | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// For implementing the Promises pattern, I suggest http://promisekit.org | |
// But here we will build a very simple, naive and limited implementation just to show very basic concepts | |
import Foundation | |
///////////////////// | |
// Promise Pattern (very simplified and naive) | |
///////////////////// | |
class Promise<T> { | |
private var completion: (T->Void)? | |
init() { } | |
init(forwardValue value: T) { | |
self.fulfill(value) | |
} | |
// Naive fulfill, for simplicity's sake of this simple demo. | |
// This implementation does not work if we `fulfill()` *before* we `then()` | |
// (Whereas in the official Promise pattern, it should) | |
func fulfill(value: T) { | |
dispatch_async(dispatch_get_main_queue()) { | |
self.completion?(value) | |
} | |
} | |
func then(completion: T->Void) { | |
self.completion = completion | |
} | |
func then<U>(completion: T->U) -> Promise<U> { | |
let p = Promise<U>() | |
self.completion = { t in | |
let u = completion(t) | |
p.fulfill( u ) | |
} | |
return p | |
} | |
func then<U>(completion: T->Promise<U>) -> Promise<U> { | |
let p = Promise<U>() | |
self.completion = { t in | |
completion(t).resolve(p) | |
} | |
return p | |
} | |
func resolve(other: Promise<T>) { | |
self.then { other.fulfill($0) } | |
} | |
} | |
/////////////////////////// | |
// Fetch JSON WebService | |
/////////////////////////// | |
typealias JSONDict = [String:AnyObject] | |
// Send network request and parse the JSON response | |
func fetch(urlString: String) -> Promise<JSONDict?> { | |
let p = Promise<JSONDict?>() | |
if let url = NSURL(string: urlString) { | |
NSURLSession.sharedSession().dataTaskWithURL(url) { (data, resp, err) in | |
let obj = NSJSONSerialization.JSONObjectWithData(data, options: .allZeros, error: nil) as? JSONDict | |
p.fulfill(obj) | |
}.resume() | |
} else { | |
println("Invalid URL!") | |
p.fulfill(nil) | |
} | |
return p | |
} | |
///////////////////// | |
// Data Structures | |
///////////////////// | |
struct GeoLoc : Printable { | |
let latitude: Double | |
let longitude: Double | |
var description: String { return "(lat:\(latitude), lng:\(longitude))" } | |
} | |
struct City : Printable { | |
let name: String? = nil | |
let zipcode: String? = nil | |
let country: (code: String?, name: String?) = (nil,nil) | |
let geoloc: GeoLoc? = nil | |
var description: String { | |
let na = "N/A" | |
let latlng = geoloc?.description ?? na | |
let countryStr = country.name ?? country.code ?? na | |
return "\(zipcode ?? na) \(name ?? na), \(countryStr) \(latlng)" | |
} | |
} | |
struct Weather : Printable { | |
let condition: String? | |
let temp: Double | |
var description: String { | |
return (condition ?? "N/A") + ", \(temp)°C" | |
} | |
} | |
///////////////////// | |
// Functional Code | |
///////////////////// | |
func fetchCityFromIP() -> Promise<City?> { | |
let p = Promise<City?>() | |
println(">>> Geolocation in progress...") | |
fetch("https://freegeoip.net/json/").then { dict in | |
if let dict = dict { | |
let geoloc: GeoLoc? | |
if let lat = dict["latitude"] as? Double, let lng = dict["longitude"] as? Double { | |
geoloc = GeoLoc(latitude: lat, longitude: lng) | |
} else { | |
geoloc = nil | |
} | |
let city = City( | |
name: dict["city"] as? String, | |
zipcode: dict["zip_code"] as? String, | |
country: ( | |
code: dict["country_code"] as? String, | |
name: dict["country_name"] as? String | |
), | |
geoloc: geoloc | |
) | |
p.fulfill(city) | |
} else { | |
p.fulfill(nil) | |
} | |
} | |
return p | |
} | |
let apibase = "http://api.openweathermap.org/data/2.5/weather?units=metric&lang=fr" | |
func fetchCurrentWeather(geoloc: GeoLoc) -> Promise<Weather?> { | |
println(">>> Fetching weather for position \(geoloc)...") | |
let url = "\(apibase)&lat=\(geoloc.latitude)&lon=\(geoloc.longitude)" | |
return fetch(url).then(parseWeather) | |
} | |
func fetchCurrentWeather(cityName: String) -> Promise<Weather?> { | |
println(">>> Fetching weather for city \(cityName)...") | |
let url = "\(apibase)&q=\(cityName)" | |
return fetch(url).then(parseWeather) | |
} | |
func fetchCurrentWeather(city: City?) -> Promise<Weather?> { | |
if let geoloc = city?.geoloc { | |
// We have a latitude & longitude | |
return fetchCurrentWeather(geoloc) | |
} else if let name = city?.name { | |
// We have a city name | |
if let cc = city?.country.code { | |
// If we also have a country code, use it | |
return fetchCurrentWeather(name + "," + cc) | |
} else { | |
// Otherwise, use only the city name | |
return fetchCurrentWeather(name) | |
} | |
} else { | |
println(">>> This city has no geoloc nor city name!") | |
return Promise(forwardValue: nil) | |
} | |
} | |
func parseWeather(dict: JSONDict?) -> Weather { | |
let weathers = dict?["weather"] as! [JSONDict] | |
let cond = weathers[0]["description"] as? String | |
let main = dict?["main"] as! JSONDict | |
let temp = main["temp"] as! Double | |
return Weather(condition: cond, temp: temp) | |
} | |
func getWeatherTip(weather: Weather?) -> Promise<String?> { | |
let p = Promise<String?>() | |
if let weather = weather { | |
switch(weather.temp) { | |
case let t where t < 0: | |
p.fulfill( "Ice-skating time!" ) | |
case 0..<10: | |
p.fulfill( "It's cold, get your scarf!" ) | |
case 10..<15: | |
p.fulfill( "It's chilly, get your coat" ) | |
default: | |
p.fulfill( "It's hot, go with a tee-shirt!" ) | |
} | |
} | |
return p | |
} | |
func printResult<T>(label: String)(_ obj: T?) -> Promise<T?> { | |
let desc = (obj != nil) ? "\(obj!)" : "nil" | |
println("\(label)\(desc)") | |
return Promise(forwardValue: obj) | |
} | |
//////////////// | |
// Demo | |
//////////////// | |
fetchCityFromIP() | |
.then(printResult("Your current position is: ")) | |
.then(fetchCurrentWeather) | |
.then(printResult("Current weather there: ")) | |
.then(getWeatherTip) | |
.then(printResult("Tip of the day: ")) | |
// If you are testing this in the Playground, add those lines so that the execution | |
// of the playground code waits for the asynchronous responses (instead of stopping | |
// before the web services asynchronous responses arrived) | |
import XCPlayground | |
XCPSetExecutionShouldContinueIndefinitely(continueIndefinitely: true) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment