Skip to content

Instantly share code, notes, and snippets.

@chrisschreiner
Last active August 29, 2015 14:22
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chrisschreiner/bbb86b22fbe9f652ffed to your computer and use it in GitHub Desktop.
Save chrisschreiner/bbb86b22fbe9f652ffed to your computer and use it in GitHub Desktop.
import Foundation
extension String {
func toDouble() -> Double? {
return NSNumberFormatter().numberFromString(self)?.doubleValue
}
}
public struct Place: Printable {
var name: String!
var phenomenon: String!
var tempmin: Double?
var tempmax: Double?
public var description:String {
var tempString: String = ""
if let min = tempmin {
tempString += "min: \(min)"
}
if let max = tempmax {
tempString += "max: \(max)"
}
return "\(name), \(phenomenon), Temperature \(tempString)"
}
}
public struct Wind: Printable {
var name: String!
var direction: String!
var speedmin: Double!
var speedmax: Double!
var gust: String!
public var description: String {
return "\(name), \(direction), Speed min: \(speedmin), max: \(speedmax)"
}
}
public struct Observation: Printable {
var phenomenon: String!
var tempmin: Double!
var tempmax: Double!
var text: String!
var place: [Place]?
var wind: [Wind]?
var sea: String!
var peipsi: String!
public var description: String {
return "\(phenomenon), Temperature min: \(tempmin), max: \(tempmax)"
}
}
public struct Forecast: Printable {
var date: NSDate!
var night = Observation()
var day = Observation()
private let d = NSDateFormatter()
public var description: String {
d.dateStyle = .MediumStyle
let formattedDate = d.stringFromDate(date)
return "\(formattedDate)"
}
}
public struct Model {
var forecasts: [Forecast]? // = []
}
public class Parser: NSObject, NSXMLParserDelegate {
let xmlFeed = "http://www.ilmateenistus.ee/ilma_andmed/xml/forecast.php"
//MARK:- XML tags
private let kForecasts = "forecasts"
private let kForecast = "forecast"
private let kNight = "night"
private let kPhenomenon = "phenomenon"
private let kDay = "day"
private let kTempMin = "tempmin"
private let kTempMax = "tempmax"
private let kPlace = "place"
private let kName = "name"
private let kText = "text"
private let kDirection = "direction"
private let kSpeedMin = "speedmin"
private let kSpeedMax = "speedmax"
private let kGust = "gust"
private let kWind = "wind"
private let kSea = "sea"
private let kPeipsi = "peipsi"
//MARK:-
private let dateFormatter: NSDateFormatter
var model: Model
var currentDate: String!
var path: [String] = []
var shouldFetchData:Bool = false {
didSet {
xmlStringData = ""
}
}
var xmlStringData = ""
var day = Observation(),
night = Observation(),
forecast = Forecast(),
forecasts = [Forecast](),
forecastDate = "",
phenomenon = "",
phenomenonPlace = "",
place = Place(),
wind = Wind(),
listOfPlace = [Place](),
listOfWind = [Wind]()
public override init() {
dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
dateFormatter.timeZone = NSTimeZone.localTimeZone()
model = Model()
}
public func parse() -> Model? {
var result = false
if let xmlURL: NSURL = NSURL(string: xmlFeed) {
if let parser = NSXMLParser(contentsOfURL: xmlURL) {
parser.delegate = self
result = parser.parse()
}
}
return model
}
//MARK:- Helpers
func push(name: String) {
path.append(name)
}
func pop() {
path.removeLast()
}
func previousPathPoint() -> String {
return path[path.count-1]
}
//MARK:- NSXMLParserDelegate
public func parser(parser: NSXMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [NSObject : AnyObject]) {
switch elementName {
case kForecasts:
push(elementName)
self.model.forecasts = []
case kForecast:
push(elementName)
self.forecast = Forecast()
let dateString = attributeDict["date"] as! String
self.forecast.date = self.dateFormatter.dateFromString(dateString)
case kDay:
push(elementName)
day = Observation()
case kNight:
push(elementName)
night = Observation()
case kPlace:
push(elementName)
place = Place()
case kWind:
push(elementName)
wind = Wind()
case kPhenomenon, kTempMax, kTempMin, kName, kText, kSea, kPeipsi, kDirection, kSpeedMin, kSpeedMax, kGust:
shouldFetchData = true
default: ()
}
}
public func parser(parser: NSXMLParser, foundCharacters string: String?) {
if shouldFetchData {
if let s = string {
xmlStringData += s
}
}
}
public func parser(parser: NSXMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?){
let parent = previousPathPoint()
switch elementName {
case kForecasts:
self.model.forecasts = forecasts
pop()
case kForecast:
forecasts.append(self.forecast)
pop()
//--- night
case kNight:
night.place = listOfPlace.count > 0 ? listOfPlace : nil
night.wind = listOfWind.count > 0 ? listOfWind : nil
forecast.night = night
listOfPlace.removeAll(keepCapacity: true)
listOfWind.removeAll(keepCapacity: true)
pop()
case kPhenomenon where parent == kNight:
night.phenomenon = xmlStringData
case kTempMin where parent == kNight:
night.tempmin = xmlStringData.toDouble()!
case kTempMax where parent == kNight:
night.tempmax = xmlStringData.toDouble()!
case kText where parent == kNight:
night.text = xmlStringData
case kSea where parent == kNight:
night.sea = xmlStringData
case kPeipsi where parent == kNight:
night.peipsi = xmlStringData
//--- day
case kDay:
day.place = listOfPlace.count > 0 ? listOfPlace : nil
day.wind = listOfWind.count > 0 ? listOfWind : nil
forecast.day = day
listOfPlace.removeAll(keepCapacity: true)
listOfWind.removeAll(keepCapacity: true)
pop()
case kPhenomenon where parent == kDay:
day.phenomenon = xmlStringData
case kTempMin where parent == kDay:
day.tempmin = xmlStringData.toDouble()!
case kTempMax where parent == kDay:
day.tempmax = xmlStringData.toDouble()!
case kText where parent == kDay:
day.text = xmlStringData
case kSea where parent == kDay:
day.sea = xmlStringData
case kPeipsi where parent == kDay:
day.peipsi = xmlStringData
//--- place
case kPlace:
listOfPlace.append(place)
place = Place()
pop()
case kName where parent == kPlace:
place.name = xmlStringData
case kPhenomenon where parent == kPlace:
place.phenomenon = xmlStringData
case kTempMin where parent == kPlace:
place.tempmin = xmlStringData.toDouble()!
case kTempMax where parent == kPlace:
place.tempmax = xmlStringData.toDouble()!
//--- wind
case kWind:
listOfWind.append(wind)
wind = Wind()
pop()
case kName where parent == kWind:
wind.name = xmlStringData
case kDirection:
wind.direction = xmlStringData
case kSpeedMin:
wind.speedmin = xmlStringData.toDouble()!
case kSpeedMax:
wind.speedmax = xmlStringData.toDouble()!
case kGust:
wind.gust = xmlStringData
default:()
}
}
public func parser(parser: NSXMLParser, parseErrorOccurred parseError: NSError) {
println("failure error: %@", parseError)
}
}
class F {
static var indent = 0
static func repeat(s:String,_ number:Int) -> String {
return reduce(0..<number,"") { $0.0 + s }
}
static func output(aString:String) {
println(repeat("\t",indent) + aString)
}
static func header(s:String, block:()->Void) {
output(s)
indent++
block()
indent--
println()
}
static func group<T:SequenceType>(s:String, data:T?) {
if let data = data {
header(s) {
for each in data {
self.output("\(each)")
}
}
}
}
}
//usage
func main() {
let stub = Parser()
if let
model = stub.parse(),
forecasts = model.forecasts {
for forecast in forecasts {
F.header("Date: \(forecast)") {
F.header("Night, \(forecast.night)") {
F.group("Place",data: forecast.night.place)
F.group("Wind",data: forecast.night.wind)
}
F.header("Day, \(forecast.day)") {
F.group("Place",data:forecast.day.place)
F.group("Wind",data:forecast.day.wind)
}
}
}
}
}
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment