Thursday, July 28, 2016

How to write custom XMLParser for Swift 3

There are some general purpose XMLParsers libraries on github e.g. SWXMLHash or XML

Sometimes, it is faster to write your own and can use it in Playgrounds. What you need for a simple XMLParser is to define a class with XMLParserDelegate and write parsing rules in "didStartElement", "foundCharacters" and "didEndElement" methods in the delegated class.

Below is an example code in swift 3 (with Swift 2.1 code added as comments underneath). And try to parse the xml file from quandl.com online dataset.
// curl "https://www.quandl.com/api/v3/datasets/FRED/DEXJPUS.xml"

These codes do not work on Linux Swift due to incomplete implementation of Foundation library on Linux yet.

testQuandl_main.swift    Select all
// // main.swift // testQuandl // import Foundation func timetest(_ note: String, block: () -> Void) //func timetest(note: String, block: () -> Void) // Swift 2 { let date = Date() // let date = NSDate() // Swift 2 block() let timeInterval = Date().timeIntervalSince(date) // let timeInterval = NSDate().timeIntervalSinceDate(date) // Swift 2 print("Test:", note); print("Elapsed time: \(String(format: "%.2f", timeInterval)) s") } extension String { private func trimmed() -> String { return self.trimmingCharacters(in: NSCharacterSet.whitespacesAndNewlines()) //return self.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()) // Swift 2 } } class QuandlCCYParser : NSObject, Foundation.XMLParserDelegate { // class QuandlCCYParser : NSObject, Foundation.NSXMLParserDelegate { // Swift 2 var parser = Foundation.XMLParser() // var parser = Foundation.NSXMLParser() // Swift 2 private var element:String = String() private var type:String = String() private var elements:Dictionary<String,String>? = [String: String]() var dataset = [Dictionary<String,String>]() private var name:String? = String() private var newest_available_date:String? = String() var dataPoints:[Dictionary<String,String>] = [[String:String]]() private var date:String? = String() private var value:String? = String() func startParsing(_ xmltxt:String) { // func startParsing(xmltxt:String) { // Swift 2 dataset = [] let xmlData = xmltxt.data(using: String.Encoding.utf8)! // let xmlData = xmltxt.dataUsingEncoding(NSUTF8StringEncoding)! // Swift 2 parser = Foundation.XMLParser(data: xmlData) // parser = Foundation.NSXMLParser(data: xmlData) // Swift 2 parser.delegate = self parser.parse() } func startParsing(urlpath:String) { // func startParsing(urlpath urlpath:String) { // Swift 2 dataset = [] parser = Foundation.XMLParser(contentsOf:(Foundation.URL(string:urlpath))! as Foundation.URL)! // parser = Foundation.NSXMLParser(contentsOfURL:(Foundation.NSURL(string:urlpath))! as Foundation.NSURL)! // Swift 2 parser.delegate = self parser.parse() } func parser(_ parser: Foundation.XMLParser, // func parser(parser: Foundation.NSXMLParser, // Swift 2 didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String: String]) { element = elementName if let type = attributeDict["type"] { self.type = type } else { self.type = "" } if (elementName == "datum" && attributeDict["type"] == "date") { date = nil elements = nil } else if (elementName == "datum" && attributeDict["type"] == "float") { value = nil elements = nil } else if (elementName == "name") { elements = nil name = nil } else if (elementName == "newest-available-date" && attributeDict["type"] == "date") { elements = nil newest_available_date = nil } } func parser(_ parser: Foundation.XMLParser, // func parser(parser: Foundation.NSXMLParser, // Swift 2 foundCharacters string: String) { if element == "datum" && type == "date" { if date == nil { date = "" } date!.append(string) // date! += string // Swift 2 } else if element == "datum" && type == "float" { if value == nil { value = "" } value!.append(string) // value! += string // Swift 2 } else if element == "name" { if name == nil { name = "" } name!.append(string) // name! += string // Swift 2 } else if element == "newest-available-date" && type == "date" { if newest_available_date == nil { newest_available_date = "" } newest_available_date!.append(string) // newest_available_date! += string // Swift 2 } } func parser(_ parser: Foundation.XMLParser, // func parser(parser: Foundation.NSXMLParser, // Swift 2 didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) { if (elementName == "datum" && type == "float") { if (date != nil && value != nil) { elements = [:] elements!["date"] = date!.trimmed() elements!["value"] = value!.trimmed() } if elements != nil { dataPoints.append(elements!) (date, value, elements) = (nil, nil, nil) } } else if elementName == "dataset" { elements = [:] if name != nil { elements!["name"] = name!.trimmed() } if newest_available_date != nil { elements!["newest-available-date"] = newest_available_date!.trimmed() } if elements != nil { dataset.append(elements!) } (date, value, elements) = (nil, nil, nil) } } func showdataset() { for data in dataset { if let name = data["name"], newest_available_date = data["newest-available-date"] { print("\(name) \(newest_available_date)") } } } } func test1() { let test = QuandlCCYParser() // curl "https://www.quandl.com/api/v3/datasets/FRED/DEXJPUS.xml" test.startParsing(urlpath:"https://www.quandl.com/api/v3/datasets/FRED/DEXJPUS.xml") test.showdataset() for data in test.dataPoints.reversed() where data["date"] >= "2016-01-01" { // for data in test.dataPoints.reverse() where data["date"] >= "2016-01-01" { // Swift 2 if let date = data["date"], value = Float(data["value"]!) { print("Date \(date) , USDJPY = \(String(format: "%.2f", value)) JPYUSD = \(1/value)") } } } timetest("DEXJPUS XMLParser", block:test1)

Xcode 8 Beta output




Below is another example XMLParser for Markit Interest Rate Curve Data and to retrieve curvepoints and other field items under Deposits and Swaps tags.
testMarkit_main.swift    Select all
// // main.swift // testMarkit // import Foundation func timetest(_ note: String, block: () -> Void) //func timetest(note: String, block: () -> Void) // Swift 2 { let date = Date() // let date = NSDate() // Swift 2 block() let timeInterval = Date().timeIntervalSince(date) // let timeInterval = NSDate().timeIntervalSinceDate(date) // Swift 2 print("Test:", note); print("Elapsed time: \(String(format: "%.2f", timeInterval)) s") } extension String { private func trimmed() -> String { return self.trimmingCharacters(in: NSCharacterSet.whitespacesAndNewlines()) //return self.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceAndNewlineCharacterSet()) // Swift 2 } } class ParserBase : NSObject, Foundation.XMLParserDelegate { // class ParserBase : NSObject, Foundation.NSXMLParserDelegate { // Swift 2 var parser = Foundation.XMLParser() // var parser = Foundation.NSXMLParser() // Swift 2 private var currentElement:String = "" private var foundCharacters = "" weak var parent:ParserBase? = nil func startParsing(_ xmltxt:String) { // func startParsing(xmltxt:String) { // Swift 2 let xmlData = xmltxt.data(using: String.Encoding.utf8)! // let xmlData = xmltxt.dataUsingEncoding(NSUTF8StringEncoding) // Swift 2 parser = Foundation.XMLParser(data: xmlData) // parser = Foundation.NSXMLParser(data: xmlData) // Swift 2 parser.delegate = self parser.parse() } func startParsing(urlpath:String) { // func startParsing(urlpath urlpath:String) { // Swift 2 parser = Foundation.XMLParser(contentsOf:(Foundation.URL(string:urlpath))! as Foundation.URL)! // parser = Foundation.NSXMLParser(contentsOfURL:(Foundation.NSURL(string:urlpath))! as Foundation.NSURL)! // Swift 2 parser.delegate = self parser.parse() } func startParsing(url:URL) { // func startParsing(url url:Foundation.NSURL) { // Swift 2 parser = Foundation.XMLParser(contentsOf:url)! // parser = Foundation.NSXMLParser(contentsOfURL:url)! // Swift 2 parser.delegate = self parser.parse() } func parser(_ parser: Foundation.XMLParser, // func parser(parser: Foundation.NSXMLParser, // Swift 2 didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String: String]) { currentElement = elementName } func parser(_ parser: Foundation.XMLParser, foundCharacters string: String) // func parser(parser: Foundation.NSXMLParser, foundCharacters string: String) // Swift 2 { self.foundCharacters += string } } class InterestRateCurve : ParserBase { var currency = "" var effectiveasof = "" lazy var deposits = Deposit() lazy var swaps = Swap() override func parser(_ parser: Foundation.XMLParser, // override func parser(parser: Foundation.NSXMLParser, // Swift 2 didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String: String]) { // if we found a tag, delegate further responsibility // to parsing to a new instance of Deposit or Swap if elementName == "deposits" { self.deposits = Deposit() // push responsibility parser.delegate = deposits // let delegate know who is the parent // so that once done XML processing // it can return parsing responsibility back deposits.parent = self } else if elementName == "swaps" { self.swaps = Swap() // push responsibility parser.delegate = swaps // let delegate know who is the parent // so that once done XML processing // it can return parsing responsibility back swaps.parent = self } super.parser(parser, didStartElement: elementName, namespaceURI: namespaceURI, qualifiedName: qName, attributes: attributeDict) } func parser(_ parser: Foundation.XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) // func parser(parser: Foundation.NSXMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) // Swift 2 { // get other elements for deposits if elementName == "currency" { self.currency = foundCharacters.trimmed() } else if elementName == "effectiveasof" { self.effectiveasof = foundCharacters.trimmed() } // reset found characters foundCharacters = "" } } class Deposit : ParserBase { var curvepoints = [CurvePoint]() var spotdate:String = "" var daycountconvention:String = "" var snaptime:String = "" override func parser(_ parser: Foundation.XMLParser, // override func parser(parser: Foundation.NSXMLParser, // Swift 2 didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String: String]) { if elementName == "curvepoint" { let depo = CurvePoint() self.curvepoints.append(depo) // push responsibility parser.delegate = depo // let delegate know who is the parent depo.parent = self } super.parser(parser, didStartElement: elementName, namespaceURI: namespaceURI, qualifiedName: qName, attributes: attributeDict) } func parser(_ parser: Foundation.XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) // func parser(parser: Foundation.NSXMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) // Swift 2 { // print("processing didEndElement <\(elementName)> tag from Deposit") // if we reached the tag, we do not // have anything further to do, so delegate // parsing responsibility to parent if elementName == "deposits" { parser.delegate = self.parent } // get other field items for deposits else if elementName == "spotdate" { self.spotdate = foundCharacters.trimmed() } else if elementName == "daycountconvention" { self.daycountconvention = foundCharacters.trimmed() } else if elementName == "snaptime" { self.snaptime = foundCharacters.trimmed() } // reset found characters foundCharacters = "" } } class Swap : ParserBase { var curvepoints = [CurvePoint]() var spotdate:String = "" var fixeddaycountconvention:String = "" var floatingdaycountconvention:String = "" var fixedpaymentfrequency:String = "" var floatingpaymentfrequency:String = "" var snaptime:String = "" override func parser(_ parser: Foundation.XMLParser, // override func parser(parser: Foundation.NSXMLParser, // Swift 2 didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String: String]) { if elementName == "curvepoint" { let swap = CurvePoint() self.curvepoints.append(swap) // push responsibility parser.delegate = swap // let delegate know who is the parent // so that it can return parsing responsibility // back after XML processing is done swap.parent = self } super.parser(parser, didStartElement: elementName, namespaceURI: namespaceURI, qualifiedName: qName, attributes: attributeDict) } func parser(_ parser: Foundation.XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) // func parser(parser: Foundation.NSXMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) // Swift 2 { // print("processing didEndElement <\(elementName)> tag from Swap") // if we reached the </swaps> tag, we do not // have anything further to do, so delegate // parsing responsibility to parent if elementName == "swaps" { parser.delegate = self.parent } // get other field items for swaps else if elementName == "spotdate" { self.spotdate = foundCharacters.trimmed() } else if elementName == "fixeddaycountconvention" { self.fixeddaycountconvention = foundCharacters.trimmed() } else if elementName == "floatingdaycountconvention" { self.floatingdaycountconvention = foundCharacters.trimmed() } else if elementName == "fixedpaymentfrequency" { self.fixedpaymentfrequency = foundCharacters.trimmed() } else if elementName == "floatingpaymentfrequency" { self.floatingpaymentfrequency = foundCharacters.trimmed() } else if elementName == "snaptime" { self.snaptime = foundCharacters.trimmed() } // reset found characters foundCharacters = "" } } class CurvePoint : ParserBase { var tenor = "" var maturitydate = "" var parrate:Float = 0.0 func parser(_ parser: Foundation.XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) // func parser(parser: Foundation.NSXMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) // Swift 2 { // print("processing didEndElement <\(elementName)> tag from CurvePoint") // if we finished an tag, the ParserBase parent // would have accumulated the found characters // so just assign that to our variable if elementName == "tenor" { self.tenor = foundCharacters.trimmed() } else if elementName == "maturitydate" { self.maturitydate = foundCharacters.trimmed() } // similarly for other tags else if elementName == "parrate" { if let l = Float(foundCharacters.trimmed()) { self.parrate = l } } // if we reached the </curvepoint> tag, we do not // have anything further to do, so delegate // parsing responsibility to parent else if elementName == "curvepoint" { parser.delegate = self.parent } // reset found characters foundCharacters = "" } } func printcurve(_ xmltxt:String) -> Void { //func printcurve(xmltxt:String) -> Void { // Swift 2 let interestRateCurve = InterestRateCurve() interestRateCurve.startParsing(xmltxt) print("interestRateCurve currency is \(interestRateCurve.currency) and effectiveasof is \(interestRateCurve.effectiveasof)") print(String(repeating: ("-" as Character), count: 65)) print("Deposits spotdate is \(interestRateCurve.deposits.spotdate) daycountconvention is \(interestRateCurve.deposits.daycountconvention)") for depo in interestRateCurve.deposits.curvepoints { print("Depo tenor = \(depo.tenor) maturitydate = \(depo.maturitydate) and parrate = \(depo.parrate)") } print("") print("Swaps spotdate is \(interestRateCurve.swaps.spotdate) fixeddaycountconvention is \(interestRateCurve.swaps.fixeddaycountconvention) floatingdaycountconvention is \(interestRateCurve.swaps.floatingdaycountconvention)") for swap in interestRateCurve.swaps.curvepoints { print("Swap tenor = \(swap.tenor) maturitydate = \(swap.maturitydate) and parrate = \(swap.parrate)") } } let xmltxt = "<?xml version=\"1.0\" standalone=\"yes\" ?><interestRateCurve><effectiveasof>2016-07-05</effectiveasof><currency>GBP</currency><baddayconvention>M</baddayconvention><deposits><daycountconvention>ACT/365</daycountconvention><snaptime>2016-07-04T15:00:00.000Z</snaptime><spotdate>2016-07-05</spotdate><calendars><calendar>none</calendar></calendars><curvepoint><tenor>1M</tenor><maturitydate>2016-08-05</maturitydate><parrate>0.004848</parrate></curvepoint><curvepoint><tenor>2M</tenor><maturitydate>2016-09-05</maturitydate><parrate>0.005009</parrate></curvepoint><curvepoint><tenor>3M</tenor><maturitydate>2016-10-05</maturitydate><parrate>0.005163</parrate></curvepoint><curvepoint><tenor>6M</tenor><maturitydate>2017-01-05</maturitydate><parrate>0.006185</parrate></curvepoint><curvepoint><tenor>1Y</tenor><maturitydate>2017-07-05</maturitydate><parrate>0.008459</parrate></curvepoint></deposits><swaps><fixeddaycountconvention>ACT/365</fixeddaycountconvention><floatingdaycountconvention>ACT/365</floatingdaycountconvention><fixedpaymentfrequency>6M</fixedpaymentfrequency><floatingpaymentfrequency>6M</floatingpaymentfrequency><snaptime>2016-07-04T15:00:00.000Z</snaptime><spotdate>2016-07-05</spotdate><calendars><calendar>none</calendar></calendars><curvepoint><tenor>2Y</tenor><maturitydate>2018-07-05</maturitydate><parrate>0.00497</parrate></curvepoint><curvepoint><tenor>3Y</tenor><maturitydate>2019-07-05</maturitydate><parrate>0.00504</parrate></curvepoint><curvepoint><tenor>4Y</tenor><maturitydate>2020-07-05</maturitydate><parrate>0.00539</parrate></curvepoint><curvepoint><tenor>5Y</tenor><maturitydate>2021-07-05</maturitydate><parrate>0.00589</parrate></curvepoint><curvepoint><tenor>6Y</tenor><maturitydate>2022-07-05</maturitydate><parrate>0.00656</parrate></curvepoint><curvepoint><tenor>7Y</tenor><maturitydate>2023-07-05</maturitydate><parrate>0.00732</parrate></curvepoint><curvepoint><tenor>8Y</tenor><maturitydate>2024-07-05</maturitydate><parrate>0.00809</parrate></curvepoint><curvepoint><tenor>9Y</tenor><maturitydate>2025-07-05</maturitydate><parrate>0.00879</parrate></curvepoint><curvepoint><tenor>10Y</tenor><maturitydate>2026-07-05</maturitydate><parrate>0.0094</parrate></curvepoint><curvepoint><tenor>12Y</tenor><maturitydate>2028-07-05</maturitydate><parrate>0.01041</parrate></curvepoint><curvepoint><tenor>15Y</tenor><maturitydate>2031-07-05</maturitydate><parrate>0.01134</parrate></curvepoint><curvepoint><tenor>20Y</tenor><maturitydate>2036-07-05</maturitydate><parrate>0.01186</parrate></curvepoint><curvepoint><tenor>25Y</tenor><maturitydate>2041-07-05</maturitydate><parrate>0.01179</parrate></curvepoint><curvepoint><tenor>30Y</tenor><maturitydate>2046-07-05</maturitydate><parrate>0.01162</parrate></curvepoint></swaps></interestRateCurve>" timetest("Interest Rate Curve XMLParser", block:{printcurve(xmltxt)})

Xcode 8 Beta output




MarKit data are zipped archives. Here is a sample shell script to retrieve and unzip the interest rate curve data. Alternative way is to use other unzip Swift Framework and unzip the url data contents in the code.
getMakit.sh    Select all
#!/bin/bash mkdir -p ~/Downloads/MarKit cd ~/Downloads/MarKit for date in "20160704" "20160705" ;do for ccy in "USD" "GBP" "EUR" "JPY" "CHF" "CAD" "AUD" "NZD" "SGD" "HKD" ;do echo InterestRates_${ccy}_${date}.zip curl -OL https://www.markit.com/news/InterestRates_${ccy}_${date}.zip unzip -o InterestRates_${ccy}_${date}.zip done done


After download and unzip in a folder, it is possible to run a series of XMLParser like this code below

testMarkit2_main.swift    Select all
func test2(url:Foundation.URL) { //func test2(url url:Foundation.NSURL) { // Swift 2 let interestRateCurve = InterestRateCurve() interestRateCurve.startParsing(url:url) print("interestRateCurve currency is \(interestRateCurve.currency) and effectiveasof is \(interestRateCurve.effectiveasof)") print(String(repeating: ("-" as Character), count: 65)) print("Deposits spotdate is \(interestRateCurve.deposits.spotdate) daycountconvention is \(interestRateCurve.deposits.daycountconvention)") for depo in interestRateCurve.deposits.curvepoints { print("Depo tenor = \(depo.tenor) maturitydate = \(depo.maturitydate) and parrate = \(depo.parrate)") } print("") print("Swaps spotdate is \(interestRateCurve.swaps.spotdate) fixeddaycountconvention is \(interestRateCurve.swaps.fixeddaycountconvention) floatingdaycountconvention is \(interestRateCurve.swaps.floatingdaycountconvention)") for swap in interestRateCurve.swaps.curvepoints { print("Swap tenor = \(swap.tenor) maturitydate = \(swap.maturitydate) and parrate = \(swap.parrate)") } } for date in ["20160704","20160705"] { for ccy in ["USD","GBP"] { let url = Foundation.URL(fileURLWithPath: NSHomeDirectory() + "/Downloads/MarKit/InterestRates_\(ccy)_\(date).xml") // let url = Foundation.NSURL(fileURLWithPath: NSHomeDirectory() + "/Downloads/MarKit/InterestRates_\(ccy)_\(date).xml") // Swift 2 timetest("InterestRates_\(ccy)_\(date).xml", block:{test2(url:url)}) } }


Below is the playground code (swift 3) that make use of the compression library (introduced from iOS9 and macOS10.11) to unzip the MarKit Interest Rate Curve url download and then possibly use the InterestRateCurve() XMLParser to print curve points.

unzip_getMarkit.swift    Select all
import Foundation import Compression func timetest(_ note: String, block: () -> Void) //func timetest(note: String, block: () -> Void) // Swift 2 { let date = Date() // let date = NSDate() // Swift 2 block() let timeInterval = Date().timeIntervalSince(date) // let timeInterval = NSDate().timeIntervalSinceDate(date) // Swift 2 print("Test:", note); print("Elapsed time: \(String(format: "%.2f", timeInterval)) s") } func getMarkit(ccy:String, date:String) -> String? { //func getMarkit(ccy ccy:String, date:String) -> String? { // Swift 2 var bytes:[UInt8] = [] var dst:[UInt8] = [] let markitURL = Foundation.URL(string: "https://www.markit.com/news/InterestRates_"+ccy+"_"+date+".zip") // let markitURL = Foundation.NSURL(string: "https://www.markit.com/news/InterestRates_"+ccy+"_"+date+".zip") // Swift 2 guard let markitData = try? Data(contentsOf: markitURL!) where markitData.subdata(in: 0..<4).elementsEqual([0x50,0x4b,0x03,0x04]) // find <504b0304> little-endian <04034b50> // guard let markitData = NSData(contentsOfURL: markitURL!) where markitData.subdataWithRange(NSMakeRange(0,4)).isEqualToData(NSData(bytes:[0x50,0x4b,0x03,0x04] as [UInt8], length:4)) // Swift 2 else { return nil } if let data:NSData = markitData as NSData { // convert from NSData to [UInt8] bytes = Array(UnsafeBufferPointer(start: UnsafePointer<UInt8>(data.bytes), count: data.length)) } var firstcentralheader: Int = 0 var offset: Int = 0 var nextoffset: Int = 0 var found:Bool = false let xmlfilenameprefix = "InterestRates" var xmlfilenamelength: Int = 0 var xmlfilename : String = "" for i in 1...bytes.count-4 { // Central directory file header signature = 0x02014b50 if bytes[i..<i+4].elementsEqual([0x50,0x4b,0x01,0x02]) { // find <504b0102> little-endian <02014b50> if firstcentralheader == 0 { firstcentralheader = i } // Central directory : 28 2 File name length (n) let filenamelength = bytes[(i+28)..<(i+28+2)] .enumerated() // .enumerate() // Swift 2 .map { (index, element) in return Int(element) * Int(pow(256,index)) } // .map { (index, element) in return Int(element) * Int(pow(Double(256),Double(index))) } // Swift 2 .reduce(0, combine: +) let filename = bytes[(i+46)..<(i+46+filenamelength)].reduce("", combine: { $0 + String(format: "%c", $1)}) if !found && filename.hasPrefix(xmlfilenameprefix) { // Central directory : 42 4 Relative offset of local file header offset = bytes[(i+42)..<(i+42+4)] .enumerated() // .enumerate() // Swift 2 .map { (index, element) in return Int(element) * Int(pow(256,index)) } // .map { (index, element) in return Int(element) * Int(pow(Double(256),Double(index))) } // Swift 2 .reduce(0, combine: +) // Local file header : offset+26 2 File name length (n) xmlfilenamelength = bytes[(offset+26)..<(offset+26+2)] .enumerated() // .enumerate() // Swift 2 .map { (index, element) in return Int(element) * Int(pow(256,index)) } // .map { (index, element) in return Int(element) * Int(pow(Double(256),Double(index))) } // Swift 2 .reduce(0, combine: +) xmlfilename = bytes[(i+46)..<(i+46+xmlfilenamelength)].reduce("", combine: { $0 + String(format: "%c", $1)}) found = true } else { if found && nextoffset == 0 { nextoffset = bytes[(i+42)..<(i+42+4)] .enumerated() // .enumerate() // Swift 2 .map { (index, element) in return Int(element) * Int(pow(256,index)) } // .map { (index, element) in return Int(element) * Int(pow(Double(256),Double(index))) } // Swift 2 .reduce(0, combine: +) break } } } } //print(xmlfilename) // Local file header : offset+30 n File name let bytes2 = Array(bytes[(offset+30+xmlfilenamelength)..<(nextoffset > 0 ? nextoffset :firstcentralheader)]) if bytes2.count > 0 { dst = [UInt8](repeating: 0, count: bytes2.count*10) // destination buffer // dst = [UInt8](count: bytes2.count*10, repeatedValue: 0 ) // destination buffer // Swift 2 // use compression library function (iOS9 or macOS 10.11 or above) if #available(OSX 10.11, iOS 9.0, *) { let size = compression_decode_buffer(&dst, dst.count, bytes2, bytes2.count, nil, COMPRESSION_ZLIB) if size > 0 { if let outxml = String(bytes: dst[0..<size], encoding: String.Encoding.ascii) { // if let outxml = String(bytes: dst[0..<size], encoding: NSASCIIStringEncoding) { // Swift 2 return outxml } } } } return nil } timetest("Get MarKit xml", block:{ for date in ["20160808","20160809"] { for ccy in ["USD","GBP"] { if let xml = getMarkit(ccy: ccy, date: date) { print(xml) // printcurve(xml) // if using the InterestRateCurve() XMLParser to print curve points } } } })




Thursday, July 14, 2016

How to use Custom Framework in Xcode Swift Playground

This is a working example

(1) Open Mac Terminal and
cd ~
git clone --depth=1 http://github.com/neonichu/ThisCouldBeUsButYouPlaying.git
cd ThisCouldBeUsButYouPlaying
sudo gem install cocoapods-playgrounds
pod playgrounds Alamofire


(2) In Xcode Build Settings, change the SDKROOT to latest iOS SDK Platform and then select Product -> Destination to iPhone 5s or above (that is simulator destination)


(3) Select Product -> Build (or ⌘B shortcut) to build the framework

(4) In Alamofire.playground, input swift code to play
import Alamofire
print(AlamofireVersionNumber)







Important: The following conditions are required for the custom framework to work in a playground: (ref)
- The framework is in the same workspace as the playground. Use "Save as Workspace" if it is not.
- If it is an iOS framework, it is built for a 64-bit runtime destination (e.g. iPhone 5s simulator).
- The workspace contains at least one active scheme that builds a target. (avoid using legacy build location in project folder)
- The framework has already been built. (⌘B)
- If it is an Objective-C framework, sets the Defines Module build setting to Yes. If it is a Swift framework, sets to No.

The working example above is an iOS Framework for Simulator built and works for iOS Playground in the same workspace. Based on various testings, OS X Custom Framework currently does not work for OS X Playground in Xcode 7.x.


Thursday, July 7, 2016

Wrapping C++ classes and extending for Swift 3.0

Wrapping C++ classes or libraries for Swift requires the following files

(1) Wrapper.h
(2) Wrapper.mm
(3) Bridging-Header.h, this will include all header files (e.g. #include "Wrapper.h") for bridging to Swift
(4) pre-compiled libraries preferably in a framework. E.g. QuantLib and Boost frameworks for macOS and iOS which can be downloaded from the previous post here


The following example code uses the pre-built QuantLib and Boost frameworks to demonstrate a working C++ Wrapper for Swift.

(1) Wrapper.h
Wrapper.h is the header file for the wrapper classes of QuantLib for Swift. Important points for writing header file when wrapping C++ classes to Swift are:
(a) No C++ header files, no C++ classes nor other C++ syntax stuff here, use void* to point to shared_ptr object instead. This header file Wrapper.h will be included in the bridging header file, so avoid c++ stuffs and use only C and Objective C stuffs when compiling in Swift.
(b) Avoid using C++ typedef here, define them in the implementation .mm source files.
(c) Use NS_ENUM macro to replace c++ enum or struct enum here, cast them to c++ enum in the implementation .mm source files. This helps when bridging to Swift enum
(d) use NS_ASSUME_NONNULL macro here and put nullable for optional variable (when bridging to Swift).
(e) use the new lightweight generic <ClassName*> feature in Objective C for NSArray and other collection types e.g. (NSArray<QLRateHelper*>*)rateHelpers
(f) Use the following Objective C types for conversion to Swift
Objective C TypeSwift Type
boolBool
char,unsigned charUInt8, UInt8
short, unsigned shortInt16, UInt16
long, unsigned longInt, UInt
long long, unsigned long longInt64, UInt64
double, floatDouble, Float
NSStringString
NSArray, NSDictionaryArray, Dictionary
Void *UnsafePointer<Void>

Wrapper.h    Select all
// // Wrapper.h // #import <Foundation/Foundation.h> NS_ASSUME_NONNULL_BEGIN @interface QLDate : NSObject typedef NS_ENUM(int, QLMonth) { QLMonthJan=1, QLMonthFeb=2, QLMonthMar=3, QLMonthApr=4, QLMonthMay=5, QLMonthJun=6, QLMonthJul=7, QLMonthAug=8, QLMonthSep=9, QLMonthOct=10, QLMonthNov=11, QLMonthDec=12, QLMonthJanuary=1, QLMonthFebruary=2, QLMonthMarch=3, QLMonthApril=4, QLMonthJune=6, QLMonthJuly=7, QLMonthAugust=8, QLMonthSeptember=9, QLMonthOctober=10, QLMonthNovember=11, QLMonthDecember=12 }; @property (unsafe_unretained, nonatomic, readonly, nullable) void *impl; -(instancetype) initWithD:(long)day Month:(QLMonth)month Y:(long)year; -(instancetype) initWithD:(long)dy M:(long)mn Y:(long)yr; -(instancetype) initWithYear:(long)year Month:(long)month Day:(long)day; -(instancetype) initWithSerialNumber:(long) serialNumber; -(NSString *) weekday; -(NSString *) description; @end typedef NS_ENUM(int, QLTimeUnit) { QLTimeUnitDays, QLTimeUnitWeeks, QLTimeUnitMonths, QLTimeUnitYears }; @interface QLCalendar : NSObject @property (unsafe_unretained, nonatomic, readonly, nullable) void *impl; +(instancetype) TARGET; +(instancetype) UnitedStatesGovernmentBond; +(instancetype) UnitedStatesNYSE; -(QLDate *) adjust:(QLDate *)settlementDate; -(QLDate *) advance:(QLDate *)settlementDate fixingDays:(long)fixingDays timeUnit:(QLTimeUnit)timeUnit; -(NSString *) description; @end #pragma mark - #pragma mark To do: Other Classes definition here NS_ASSUME_NONNULL_END


(2) Wrapper.mm
Wrapper.mm is the Objective-C++ implementation file for the Wrapper Classes of QuantLib for Swift
(a) Put all the required C++ header files here.
(b) Use boost::shared_ptr<QuantLib::ClassName> or std::shared_ptr<QuantLib::ClassName> in the implementation and point to the C++ objects.
(c) Use static_cast to cast the shared_ptr impl back to QuantLib C++ object. For example
    // cast void* back to QuantLib::ClassName
    QuantLib::Date tmpSettlementDate = *static_cast<QuantLib::Date *>(settlementDate.impl);
    // cast boost::shared_ptr<QuantLib::ClassName> back to QuantLib::ClassName
    static_cast<QuantLib::Date>(*(self._impl));
(d) Use pointer function to access the QuantLib member function. For example
    [[QLDate alloc] initWithSerialNumber:((long)self._impl->adjust(tmpSettlementDate).serialNumber())] ;
(e) Should implement description method for Swift.
    -(NSString *) description

Wrapper.mm    Select all
#import "Wrapper.h" #include <memory> #include <iostream> #include <boost/shared_ptr.hpp> #include <ql/quantlib.hpp> #pragma mark - #pragma mark To do: QLDate @interface QLDate() @property (unsafe_unretained, nonatomic) boost::shared_ptr<QuantLib::Date> _impl; @end @implementation QLDate -(instancetype) init { self = [super init]; if (self) { self._impl = boost::shared_ptr<QuantLib::Date>(new QuantLib::Date()); } return self; } -(instancetype) initWithD:(long)day Month:(QLMonth)month Y:(long)year { self = [super init]; if (self) { self._impl = boost::shared_ptr<QuantLib::Date>(new QuantLib::Date((int)day, static_cast<QuantLib::Month>(month), (int)year)); } return self; } -(instancetype) initWithD:(long)dy M:(long)mn Y:(long)yr { self = [super init]; if (self) { self._impl = boost::shared_ptr<QuantLib::Date>(new QuantLib::Date((int)dy, static_cast<QuantLib::Month>(mn), (int)yr)); } return self; } -(instancetype) initWithYear:(long)year Month:(long)month Day:(long)day { self = [super init]; if (self) { self._impl = boost::shared_ptr<QuantLib::Date>(new QuantLib::Date((int)day, static_cast<QuantLib::Month>((int)month), (int)year)); } return self; } -(instancetype) initWithSerialNumber:(long) serialNumber { self = [super init]; if (self) { self._impl = boost::shared_ptr<QuantLib::Date>(new QuantLib::Date((QuantLib::BigInteger)serialNumber)); } return self; } -(void *) impl { return static_cast<void *>(self._impl.get()); } -(NSString *) description { std::ostringstream stream; stream << static_cast<QuantLib::Date>(*(self._impl)); std::string name = stream.str(); return [NSString stringWithUTF8String:name.c_str()]; } -(NSString *) weekday { std::ostringstream stream; stream << static_cast<QuantLib::Weekday>(self._impl->weekday()); std::string name = stream.str(); return [NSString stringWithUTF8String:name.c_str()]; } @end #pragma mark - #pragma mark To do: QLCalendar @implementation QLCalendar -(instancetype) init { self = [super init]; if (self) { self._impl = boost::shared_ptr<QuantLib::Calendar>(new QuantLib::Calendar()); } return self; } -(instancetype) initWithTarget { self = [super init]; if (self) { self._impl = boost::shared_ptr<QuantLib::Calendar>(new QuantLib::TARGET()); } return self; } -(instancetype) initWithUnitedStatesGovernmentBond { self = [super init]; if (self) { self._impl = boost::shared_ptr<QuantLib::Calendar>(new QuantLib::UnitedStates(QuantLib::UnitedStates::GovernmentBond)); } return self; } -(instancetype) initWithUnitedStatesNYSE { self = [super init]; if (self) { self._impl = boost::shared_ptr<QuantLib::Calendar>(new QuantLib::UnitedStates(QuantLib::UnitedStates::NYSE)); } return self; } +(instancetype) TARGET { return [[QLCalendar alloc] initWithTarget]; } +(instancetype) UnitedStatesGovernmentBond { return [[QLCalendar alloc] initWithUnitedStatesGovernmentBond]; } +(instancetype) UnitedStatesNYSE { return [[QLCalendar alloc] initWithUnitedStatesNYSE]; } -(void *) impl { return static_cast<void *>(self._impl.get()); } -(NSString *) description { std::string name = self._impl->name(); return [NSString stringWithUTF8String:name.c_str()]; } -(QLDate *) adjust:(QLDate *)settlementDate { // cast void* to std::shared_ptr<mytype> QuantLib::Date tmpSettlementDate = *static_cast<QuantLib::Date *>(settlementDate.impl); return [[QLDate alloc] initWithSerialNumber:((long)self._impl->adjust(tmpSettlementDate).serialNumber())] ; } -(QLDate *) advance:(QLDate *)settlementDate fixingDays:(long)fixingDays timeUnit:(QLTimeUnit)timeUnit { // cast void* to std::shared_ptr<mytype> QuantLib::Date tmpSettlementDate = *static_cast<QuantLib::Date *>(settlementDate.impl); return [[QLDate alloc] initWithSerialNumber:((long)self._impl->advance(tmpSettlementDate,QuantLib::Integer((int)fixingDays), static_cast<QuantLib::TimeUnit>(timeUnit)).serialNumber())] ; } @end #pragma mark - #pragma mark To do: Other Classes implementation here


(3) main.swift
This is the swift 3.0 example source file (Xcode 8.0) for extending and using the QuantLib C++ classes after wrapping. This example use the calculation method of zeroCouponBond in Bonds.cpp from QuantLib Example source repo.
main.swift    Select all
// // main.swift // import Foundation public extension QLDate { public class func parse(_ string : String, format : String = "yyyy-mm-dd") -> QLDate { var slist : [String] var flist : [String] var d : Int = 0 var m : Int = 0 var y : Int = 0 var delim : String if string.range(of:"/") != nil { delim = "/" } else { delim = "-" } slist = string.components(separatedBy: delim) flist = format.components(separatedBy: delim) for i in 0..<flist.count { let sub = flist[i] if sub.lowercased() == "dd" { d = Int(slist[i])! } else if sub.lowercased() == "mm" { m = Int(slist[i])! } else if sub.lowercased() == "yyyy" { y = Int(slist[i])! } } if y < 100 { y += 2000 } return QLDate(year: y, month: m, day: d) } } public func char2TimeUnit(_ unit:String) -> QLTimeUnit { switch unit { case "D", "d": return .days case "W", "w": return .weeks case "M", "m": return .months case "Y", "y": return .years default: return .months } } /********************* *** MARKET DATA *** *********************/ let calendar = QLCalendar.target() var settlementDate = QLDate.parse("2008-09-18") settlementDate = calendar.adjust(settlementDate); let fixingDays = 3 let settlementDays = 3 let todaysDate = calendar.advance(settlementDate, fixingDays: -(fixingDays), timeUnit: .days) print("Today: \(todaysDate.weekday()), \(todaysDate)") print("Settlement date: \(settlementDate.weekday()), \(settlementDate)") /*********************************************** ** CURVE BUILDING DEPOSIT + FIXED RATE BOND ** ***********************************************/ var rateHelpers = [QLRateHelper]() /********************* ** DEPOSIT DATA ** *********************/ let depositData: [(Double, Int, String)] = [(0.0096, 3, "M"), (0.0145, 6, "M"), (0.0194, 1, "Y")] for (rate, num, unit) in depositData { let timeunit:QLTimeUnit = char2TimeUnit(unit) let ratehandle = QLHandleQuote(simpleQuoteRate: QLRate(rate:rate)) let depositehelper = QLDepositRateHelper(handle: ratehandle, tenor: QLPeriod(num: num, of: timeunit), fixingDays: fixingDays, calendar: calendar, convention: .modifiedFollowing, endOfMonth: true, dayCounter: QLDayCounter.actual365Fixed()) rateHelpers.append(depositehelper) } /**************************** ** FIXED RATE BOND DATA ** ****************************/ let faceAmount = 100.0 let redemption = 100.0 let tolerance = 1.0e-15 let fixedRateBondData:[(Double, Double, String, String, Double, Double )] = [(faceAmount, redemption, "2005-03-15", "2010-08-31", 0.02375, 100.390625), (faceAmount, redemption, "2005-06-15", "2011-08-31", 0.04625, 106.21875), (faceAmount, redemption, "2006-06-30", "2013-08-31", 0.03125, 100.59375), (faceAmount, redemption, "2002-11-15", "2018-08-15", 0.04000, 101.6875), (faceAmount, redemption, "1987-05-15", "2038-05-15", 0.04500, 102.140625)] for (faceamount, redemption, issuedate, maturitydate, couponrate, marketquote) in fixedRateBondData { let schedule = QLSchedule(issueDate: QLDate.parse(issuedate), maturityDate: QLDate.parse(maturitydate), frequency: QLPeriod(frequency:QLFrequency.semiannual), calendar: QLCalendar.unitedStatesGovernmentBond(), convention: .unadjusted, terminationConvention: .unadjusted, dateGenerationRule: .backward, endOfMonth: false) let quoteHandle = QLRelinkableHandleQuote(marketQuote: marketquote) let bondHelper = QLFixedRateBondHelper(handle: quoteHandle, settlementDays: settlementDays, faceAmount: faceamount, schedule: schedule, couponRate: couponrate, dayCounter: QLDayCounter.actualActualBond(), convention: .unadjusted, redemption: redemption, issueDate: QLDate.parse(issuedate)) rateHelpers.append(bondHelper) } let bondEngine = QLBondPricingEngine(rateHelpers: rateHelpers, settlementDate: settlementDate, tolerance: tolerance) print("bondEngine: \(bondEngine)") /********************* * BONDS TO BE PRICED * **********************/ let zeroCouponBond = QLZeroCouponBond(bondPricingEngine: bondEngine, settlementDays: settlementDays, calendar: QLCalendar.unitedStatesGovernmentBond(), faceAmount: 100.0, maturityDate: QLDate.parse("2013-08-15"), convention: .following, price: 116.92, issueDate: QLDate.parse("2003-08-15")) print("\(zeroCouponBond) NPV = \(String(format: "%.2f", zeroCouponBond.npv()))") print("\(zeroCouponBond) Clean price = \(String(format: "%.2f", zeroCouponBond.cleanPrice()))") print("\(zeroCouponBond) Dirty price = \(String(format: "%.2f", zeroCouponBond.dirtyPrice()))") print("\(zeroCouponBond) Yield = \(String(format: "%.2f%%", zeroCouponBond.yield()*100))")





example xcode project file will be posted here once ready


Sunday, February 21, 2016

no codesigning certificates found

From the "Known issues" of https://developer.apple.com/support/certificates/expiration/:

"in the Keychain Access application, select the System keychain. Select 'Show Expired Certificates' in the View menu and then delete the expired version of the Apple Worldwide Developer Relations Certificate Authority Intermediate certificate."

and then install the new Apple Worldwide Developer Relations Certificate from apple from here

https://developer.apple.com/support/certificates/expiration/
Even if you have installed the new certificate, you have to delete the expired one from the System keychain.



Thursday, June 25, 2015

QuantLib 1.6 framework (libc++) for iOS and Mac OS X

QuantLib 1.6 framework (compile with -std=c++11 -stdlib=libc++) and prefix pre-built binary.
Architectures in the fat file: framework/ql.framework/ql are: armv7 i386 x86_64 arm64

Pre-built framework and libraries prefix for QuantLib 1.6 can be downloaded from here.

Below is the bash script (modified from http://github.com/philipbarnes/quantlib-on-iOS) to build the framework (compile with -std=c++11 -stdlib=libc++).
Requires Boost 1.58.0 pre-built binary which can be downloaded from here.

build-ql.sh    Select all
#!/bin/bash #=============================================================================== # Filename: build-ql.sh #=============================================================================== # Changes: # # Builds a quantlib framework for iOS & MacOSX # # Requires a pre-built version of boost built using the boost.sh script by # Pete Goodliffe. The structure of this script is based on Pete's boost.sh # # This takes a brute-force approach to the build and builds the arm6, arm7, arm64 and # i386 and x86_64 versions of quantlib one after the other. It makes clean between builds. # # To configure the script, change the variables below to point to the build of # boost lib, include and SYSROOT #=============================================================================== : ${CURRENTDIR:=`pwd`/} : ${BOOST_HOME:=$CURRENTDIR/../ofxiOSBoost-master/usr/local/lib} : ${BOOST_SRC:=$CURRENTDIR/../ofxiOSBoost-master/usr/local/include} : ${ARMV6_SYSROOT:=/Applications/Xcode431.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.1.sdk} : ${ARMV7_SYSROOT:=/Applications/Xcode501.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS7.0.sdk} : ${ARMV7S_SYSROOT:=/Applications/Xcode501.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS7.0.sdk} : ${ARM64_SYSROOT:=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS8.1.sdk} : ${iPhoneSimulator_SYSROOT:=/Applications/Xcode501.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator7.0.sdk} : ${MacOSX_SYSROOT:=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk} #=============================================================================== # The number of jobs for make to run. On a 2.8 Mac Pro 8 core it takes around # 31 minutes with 9 jobs to build all the libraries and framework. #=============================================================================== : ${JOBS:=3} #=============================================================================== # No need to change these variables. # Xcode 4.x and 5.x are used to build the libraries. This now resides in # /Applications/Xcode???.app/Contents #=============================================================================== : ${ARM_DEV_DIR:=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer} : ${SIM_DEV_DIR:=/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer} : ${SRCDIR:=`pwd`/src} : ${BUILDDIR:=`pwd`/build} : ${PREFIXDIR:=`pwd`/prefix} : ${FRAMEWORKDIR:=`pwd`/framework} #=============================================================================== # Utility functions for reporting #=============================================================================== displayConfiguration() { echo echo " =================================================================" echo echo " Configuration" echo " SRCDIR :" $SRCDIR echo " BUILDDIR :" $BUILDDIR echo " PREFIXDIR :" $PREFIXDIR echo " FRAMEWORKDIR :" $FRAMEWORKDIR echo echo " BOOST_HOME :" $BOOST_HOME echo " BOOST_SRC :" $BOOST_SRC echo echo " JOBS :" $JOBS echo echo " ARM_DEV_DIR :" $ARM_DEV_DIR echo " SIM_DEV_DIR :" $SIM_DEV_DIR } displayMessage() { echo echo " =================================================================" echo " $@" echo } doneSection() { echo echo " =================================================================" echo " Done: $@" echo } abort() { echo echo "Aborted: $@" exit 1 } #=============================================================================== # Prepare the directory structures #=============================================================================== cleanEverythingReadyToStart() { displayMessage "Cleaning everything ready to start" rm -rf $BUILDDIR #rm -rf $PREFIXDIR #rm -rf $FRAMEWORKDIR doneSection } #=============================================================================== # Prepare the directory structures #=============================================================================== createDirectoryStructure() { displayMessage "Creating directory structure" [ -d $BUILDDIR ] || mkdir -p $BUILDDIR [ -d $PREFIXDIR ] || mkdir -p $PREFIXDIR [ -d $FRAMEWORKDIR ] || mkdir -p $FRAMEWORKDIR doneSection } #=============================================================================== # Build the armv6 quantlib libraries #=============================================================================== buildArmv6() { displayMessage "Configuring armv6 libraries" rm -rf $PREFIXDIR/armv6 make distclean > /dev/null # requires clang 3.1 lib in Xcode 4.3.1 for building armv6 binary CC="/Applications/Xcode431.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang" \ CXX="/Applications/Xcode431.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++" \ CPP="/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cpp" \ CXXCPP="/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cpp" \ CXXFLAGS="-arch armv6 -std=c++11 -stdlib=libc++ -isysroot $ARMV6_SYSROOT" \ CFLAGS="-arch armv6 -std=c99 -isysroot $ARMV6_SYSROOT" \ LDFLAGS="-stdlib=libc++" \ ./configure --with-boost-include=$BOOST_SRC \ --with-boost-lib=$BOOST_HOME \ --host=arm-apple-darwin10 \ --target=arm-apple-darwin10 \ --with-sysroot=$ARMV6_SYSROOT \ --prefix=/usr/local \ --disable-shared --enable-static displayMessage "Making Armv6 libraries" make -j $JOBS && make DESTDIR=$PREFIXDIR/armv6 install || abort "make armv6 failed" doneSection "armv6 done" } #=============================================================================== # Build the armv7 quantlib libraries #=============================================================================== buildArmv7() { displayMessage "Configuring armv7 libraries" rm -rf $PREFIXDIR/armv7 make distclean > /dev/null CC="xcrun --sdk iphoneos clang" \ CXX="xcrun --sdk iphoneos clang++" \ CPP="/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cpp" \ CXXCPP="/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cpp" \ CXXFLAGS="-arch armv7 -std=c++11 -stdlib=libc++ -isysroot $ARMV7_SYSROOT" \ CFLAGS="-arch armv7 -std=c99 -isysroot $ARMV7_SYSROOT" \ LDFLAGS="-stdlib=libc++" \ ./configure --with-boost-include=$BOOST_SRC \ --with-boost-lib=$BOOST_HOME \ --host=arm-apple-darwin10 \ --target=arm-apple-darwin10 \ --with-sysroot=$ARMV7_SYSROOT \ --prefix=/usr/local \ --disable-shared --enable-static displayMessage "making armv7 libraries" make -j $JOBS && make DESTDIR=$PREFIXDIR/armv7 install || abort "make armv7 failed" doneSection "armv7 done" } #=============================================================================== # Build the armv7s quantlib libraries #=============================================================================== buildArmv7s() { displayMessage "Configuring armv7s libraries" rm -rf $PREFIXDIR/armv7s make distclean > /dev/null CC="xcrun --sdk iphoneos clang" \ CXX="xcrun --sdk iphoneos clang++" \ CPP="/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cpp" \ CXXCPP="/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cpp" \ CXXFLAGS="-arch armv7s -std=c++11 -stdlib=libc++ -isysroot $ARMV7S_SYSROOT" \ CFLAGS="-arch armv7s -std=c99 -isysroot $ARMV7S_SYSROOT" \ LDFLAGS="-stdlib=libc++" \ ./configure --with-boost-include=$BOOST_SRC \ --with-boost-lib=$BOOST_HOME \ --host=arm-apple-darwin10 \ --target=arm-apple-darwin10 \ --with-sysroot=$ARMV7S_SYSROOT \ --prefix=/usr/local \ --disable-shared --enable-static displayMessage "making armv7s libraries" make -j $JOBS && make DESTDIR=$PREFIXDIR/armv7s install || abort "make armv7s failed" doneSection "armv7s done" } #=============================================================================== # Build the arm64 quantlib libraries #=============================================================================== buildArm64() { displayMessage "Configuring arm64 libraries" rm -rf $PREFIXDIR/arm64 make distclean > /dev/null CC="xcrun --sdk iphoneos clang" \ CXX="xcrun --sdk iphoneos clang++" \ CPP="/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cpp" \ CXXCPP="/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cpp" \ CXXFLAGS="-arch arm64 -std=c++11 -stdlib=libc++ -isysroot $ARM64_SYSROOT" \ CFLAGS="-arch arm64 -std=c99 -isysroot $ARM64_SYSROOT" \ LDFLAGS="-stdlib=libc++" \ ./configure --with-boost-include=$BOOST_SRC \ --with-boost-lib=$BOOST_HOME \ --host=arm-apple-darwin10 \ --target=arm-apple-darwin10 \ --with-sysroot=$ARM64_SYSROOT \ --prefix=/usr/local \ --disable-shared --enable-static displayMessage "making arm64 libraries" make -j $JOBS && make DESTDIR=$PREFIXDIR/arm64 install || abort "make arm64 failed" doneSection "arm64 done" } #=============================================================================== # Build the i386 quantlib libraries #=============================================================================== buildi386() { displayMessage "Configuring i386 libraries" rm -rf $PREFIXDIR/i386 make distclean > /dev/null CC="xcrun --sdk iphonesimulator clang" \ CXX="xcrun --sdk iphonesimulator clang++" \ CPP="/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cpp" \ CXXCPP="/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cpp" \ CXXFLAGS="-arch i386 -std=c++11 -stdlib=libc++ -isysroot $iPhoneSimulator_SYSROOT" \ CFLAGS="-arch i386 -isysroot $iPhoneSimulator_SYSROOT" \ LDFLAGS="-stdlib=libc++" \ ./configure --with-boost-include=$BOOST_SRC \ --with-boost-lib=$BOOST_HOME \ --with-sysroot=$iPhoneSimulator_SYSROOT \ --prefix=/usr/local \ --disable-shared --enable-static displayMessage "Building i386 libraries" make -j $JOBS && make DESTDIR=$PREFIXDIR/i386 install || abort "make i386 failed" doneSection "i386 done" } #=============================================================================== # Build the x86_64 quantlib libraries #=============================================================================== buildx86_64() { displayMessage "Configuring x86_64 libraries" rm -rf $PREFIXDIR/x86_64 make distclean > /dev/null CC="xcrun --sdk macosx clang" \ CXX="xcrun --sdk macosx clang++" \ CPP="/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cpp" \ CXXCPP="/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cpp" \ CXXFLAGS="-arch x86_64 -std=c++11 -stdlib=libc++ -mmacosx-version-min=10.7 -isysroot $MacOSX_SYSROOT" \ CFLAGS="-arch x86_64 -mmacosx-version-min=10.7 -isysroot $MacOSX_SYSROOT" \ LDFLAGS="-stdlib=libc++" \ ./configure --with-boost-include=$BOOST_SRC \ --with-boost-lib=$BOOST_HOME \ --with-sysroot=$MacOSX_SYSROOT \ --prefix=/usr/local \ --disable-shared --enable-static displayMessage "Building x86_64 libraries" make -j $JOBS && make DESTDIR=$PREFIXDIR/x86_64 install || abort "make x86_64 failed" doneSection "x86_64 done" } #=============================================================================== # Build the framework # # Unlike the boost build by Pete Goodliffe, all the libraries are created # individually and so do not need to be unpacked and scrunched together. # # Create the framework libraries in-site #=============================================================================== buildFramework() { VERSION_TYPE=Alpha FRAMEWORK_NAME=ql FRAMEWORK_VERSION=A FRAMEWORK_CURRENT_VERSION=1.6 FRAMEWORK_COMPATIBILITY_VERSION=1.6 FRAMEWORK_BUNDLE=$FRAMEWORKDIR/$FRAMEWORK_NAME.framework rm -rf $FRAMEWORKDIR displayMessage "Framework: Setting up directories..." mkdir -p $FRAMEWORK_BUNDLE mkdir -p $FRAMEWORK_BUNDLE/Versions mkdir -p $FRAMEWORK_BUNDLE/Versions/$FRAMEWORK_VERSION mkdir -p $FRAMEWORK_BUNDLE/Versions/$FRAMEWORK_VERSION/Resources mkdir -p $FRAMEWORK_BUNDLE/Versions/$FRAMEWORK_VERSION/Headers mkdir -p $FRAMEWORK_BUNDLE/Versions/$FRAMEWORK_VERSION/Documentation displayMessage "Framework: Creating symlinks..." ln -s $FRAMEWORK_VERSION $FRAMEWORK_BUNDLE/Versions/Current ln -s Versions/Current/Headers $FRAMEWORK_BUNDLE/Headers ln -s Versions/Current/Resources $FRAMEWORK_BUNDLE/Resources ln -s Versions/Current/Documentation $FRAMEWORK_BUNDLE/Documentation ln -s Versions/Current/$FRAMEWORK_NAME $FRAMEWORK_BUNDLE/$FRAMEWORK_NAME FRAMEWORK_INSTALL_NAME=$FRAMEWORK_BUNDLE/Versions/$FRAMEWORK_VERSION/$FRAMEWORK_NAME displayMessage "Framework: Lipoing library into $FRAMEWORK_INSTALL_NAME" /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/lipo \ -arch armv7 "$PREFIXDIR/armv7/usr/local/lib/libQuantLib.a" \ -arch armv7s "$PREFIXDIR/armv7s/usr/local/lib/libQuantLib.a" \ -arch arm64 "$PREFIXDIR/arm64/usr/local/lib/libQuantLib.a" \ -arch i386 "$PREFIXDIR/i386/usr/local/lib/libQuantLib.a" \ -arch x86_64 "$PREFIXDIR/x86_64/usr/local/lib/libQuantLib.a" \ -output "$FRAMEWORK_INSTALL_NAME" \ -create || abort "Lipo $1 failed" displayMessage "Framework: Copying includes..." cp -r $PREFIXDIR/x86_64/usr/local/include/ql/* $FRAMEWORK_BUNDLE/Headers/ displayMessage "Framework: Creating plist..." cat > $FRAMEWORK_BUNDLE/Resources/Info.plist <<EOF <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>CFBundleDevelopmentRegion</key> <string>English</string> <key>CFBundleExecutable</key> <string>${FRAMEWORK_NAME}</string> <key>CFBundleIdentifier</key> <string>org.quantlib</string> <key>CFBundleInfoDictionaryVersion</key> <string>6.0</string> <key>CFBundlePackageType</key> <string>FMWK</string> <key>CFBundleSignature</key> <string>????</string> <key>CFBundleVersion</key> <string>${FRAMEWORK_CURRENT_VERSION}</string> </dict> </plist> EOF doneSection "$FRAMEWORK_BUNDLE" } #=============================================================================== # Execution starts here #=============================================================================== displayConfiguration cleanEverythingReadyToStart createDirectoryStructure buildArmv7 buildArm64 buildi386 buildx86_64 buildFramework displayMessage "Completed successfully"


Note: QuantLib 1.5 is incompatible with Boost 1.58. Use Boost 1.57 instead.

Here is the framework and prefix for Quantlib 1.6+1.5

Here is the framework and prefix for Boost 1.58+1.57

Saturday, June 20, 2015

iOS Drawing in swift playground

swift Playground can be used for prototype iOS Drawing



swift Plyaground    Select all
//: Playground - a place where programmer can prototype import UIKit class IconView: UIView { override func drawRect(rect: CGRect) { drawRawBackgroundWithBaseColor(UIColor.orangeColor(), backgroundRectangle: self.bounds) let textAttributes = [ NSForegroundColorAttributeName : UIColor.blackColor(), NSFontAttributeName : UIFont.systemFontOfSize(32.0) ] let FString: String = "Hello World" let distanceX: CGFloat = -12.0 let distanceY: CGFloat = 0.0 let centerX = CGRectGetMidX(self.bounds) let centerY = CGRectGetMidY(self.bounds) FString.drawAtPoint(CGPointMake(centerX + distanceX, centerY + distanceY), withAttributes:textAttributes) } } func drawRawBackgroundWithBaseColor(strokeColor: UIColor, backgroundRectangle:CGRect) { let lineWidth = backgroundRectangle.width/36.0 let cornerRadius = backgroundRectangle.width/16.0 // let tileRectangle = backgroundRectangle.rectByInsetting(dx: lineWidth/2.0, dy: lineWidth/2.0) let tileRectangle = backgroundRectangle.insetBy(dx: lineWidth/2.0, dy: lineWidth/2.0) // Stroke Drawing let strokePath = UIBezierPath(roundedRect:tileRectangle, cornerRadius:cornerRadius) strokeColor.setStroke() strokePath.lineWidth = lineWidth strokePath.stroke() // Draw an ellipse // let ovalPath = UIBezierPath(ovalInRect: backgroundRectangle.rectByInsetting(dx: lineWidth*1.5, dy: lineWidth*1.5)) let ovalPath = UIBezierPath(ovalInRect: backgroundRectangle.insetBy(dx: lineWidth*1.5, dy: lineWidth*1.5)) UIColor.blueColor().setStroke() ovalPath.lineWidth = lineWidth ovalPath.stroke() // let context:CGContextRef = UIGraphicsGetCurrentContext() let context:CGContextRef = UIGraphicsGetCurrentContext()! CGContextSetFillColorWithColor(context, UIColor.greenColor().CGColor) CGContextAddRect(context, CGRectMake(100.0, 100.0, 60.0, 60.0)) CGContextFillPath(context) } // Instantiate the UIView let rect = CGRect(x: 0.0, y: 0.0, width: 420.0, height: 320.0) let icon = IconView(frame: rect) icon.backgroundColor = UIColor.clearColor() icon




Updated for Swift 3.0 syntax below

swift Plyaground 3.0     Select all
//: Playground - noun: a place where people can play import UIKit class IconView: UIView { override func draw(_ rect: CGRect) { drawRawBackgroundWithBaseColor(strokeColor: UIColor.orange(), backgroundRectangle: self.bounds) let textAttributes = [ NSForegroundColorAttributeName : UIColor.black(), NSFontAttributeName : UIFont.systemFont(ofSize: 32.0) ] let FString: String = "Hello World" let distanceX: CGFloat = -12.0 let distanceY: CGFloat = 0.0 let centerX = self.bounds.midX let centerY = self.bounds.midY FString.draw(at: CGPoint(x:centerX+distanceX, y:centerY+distanceY), withAttributes: textAttributes) } } func drawRawBackgroundWithBaseColor(strokeColor: UIColor, backgroundRectangle:CGRect) { let lineWidth = backgroundRectangle.width/36.0 let cornerRadius = backgroundRectangle.width/16.0 let tileRectangle = backgroundRectangle.insetBy(dx: lineWidth/2.0, dy: lineWidth/2.0) // Stroke Drawing let strokePath = UIBezierPath(roundedRect:tileRectangle, cornerRadius:cornerRadius) strokeColor.setStroke() strokePath.lineWidth = lineWidth strokePath.stroke() // Draw an ellipse let ovalPath = UIBezierPath(ovalIn: backgroundRectangle.insetBy(dx: lineWidth*1.5, dy: lineWidth*1.5)) UIColor.blue().setStroke() ovalPath.lineWidth = lineWidth ovalPath.stroke() let context:CGContext = UIGraphicsGetCurrentContext()! context.setFillColor(UIColor.green().cgColor) context.addRect(CGRect(x: 100.0, y: 100.0, width: 60.0, height: 60.0)) context.fillPath() } // Instantiate the UIView let rect = CGRect(x: 0.0, y: 0.0, width: 420.0, height: 320.0) let icon = IconView(frame: rect) icon.backgroundColor = UIColor.clear() icon