Error handling.
This commit is contained in:
parent
2b90a9c28d
commit
80932adf37
218
README.md
218
README.md
|
@ -1,205 +1,51 @@
|
|||
SwiftyHTTP
|
||||
==========
|
||||
SwiftyExpat
|
||||
===========
|
||||
|
||||
A simple GCD based HTTP library for Swift.
|
||||
|
||||
SwiftyHTTP is kind of a demo on how to integrate Swift with raw C APIs. More
|
||||
for stealing Swift coding ideas than for actually using the code in a real
|
||||
project. In most real world Swift apps you have access to Cocoa, use it.
|
||||
|
||||
**Note**: This is just my second [Swift](https://developer.apple.com/swift/)
|
||||
project. Any suggestions on how to improve the code are welcome. I expect
|
||||
lots and lots :-)
|
||||
Simple wrapper for the Expat XML parser. Which had to be adjusted to use
|
||||
blocks instead of function pointer callbacks.
|
||||
|
||||
###Targets
|
||||
|
||||
The project includes three targets:
|
||||
- SwiftyHTTP
|
||||
- SwiftyServer
|
||||
- SwiftyClient
|
||||
The project includes two targets:
|
||||
- SwiftyExpat
|
||||
- SwiftyExpatTests
|
||||
|
||||
I suggest you start out looking at the SwiftyServer.
|
||||
I suggest you start out looking at the SwiftyExpatTests.
|
||||
|
||||
####SwiftyHTTP
|
||||
####SwiftyExpat
|
||||
|
||||
A framework containing the HTTP classes and relevant extensions. It has a few
|
||||
'subprojects':
|
||||
- Foundation
|
||||
- Sockets
|
||||
- Parser
|
||||
- HTTP
|
||||
This is a tiny framework containing the modified Expat parser. Plus a small
|
||||
Swift class to make the API nicer, though this is not really necessary - the
|
||||
block based Expat is reasonably easy to use from Swift.
|
||||
|
||||
#####Foundation
|
||||
|
||||
This has just the 'RawByteBuffer' class. Which is kinda like a RawByte array.
|
||||
I bet there are better ways to implement this! Please suggest some! :-)
|
||||
|
||||
Also a few - highly inefficient - extensions to convert between String's and
|
||||
CString's. I would love some suggestions on those as well.
|
||||
|
||||
But remember: NSxyz is forbidden for this venture! :-)
|
||||
|
||||
#####Sockets
|
||||
|
||||
Just a local copy of the SwiftSockets project - I wish GIT had proper externals
|
||||
;-) (https://github.com/AlwaysRightInstitute/SwiftSockets)
|
||||
|
||||
#####Parser
|
||||
|
||||
This uses the C HTTP parser which is also used in Node.JS. I couldn't
|
||||
directly use it being C callback driven - a feature (currently?)
|
||||
unsupported by Swift.
|
||||
The fix was to rewrite the parser to use C blocks instead of function pointers.
|
||||
Along that way I also removed some great features of the parser, like no
|
||||
malloc() at all :->
|
||||
|
||||
It also contains the main request/response classes: HTTPRequest and
|
||||
HTTPResponse, both subclasses of HTTPMessage.
|
||||
And enums for HTTP status values (like `💰Required`) and request methods (GET
|
||||
etc).
|
||||
|
||||
#####HTTP
|
||||
|
||||
HTTPConnectionPool is an abstract base class and manages open connections,
|
||||
either incoming or outgoing. The HTTPConnection sits on top of the SwiftSockets
|
||||
and manages one HTTP connection.
|
||||
|
||||
HTTPServer is the server class. Uses the SwiftSockets to listen for incoming
|
||||
connections. Sample:
|
||||
```Swift
|
||||
let httpd = HTTPServer()
|
||||
.onRequest {
|
||||
rq, res, con in
|
||||
res.bodyAsString = "<h2>Always Right, Never Wrong!</h2>"
|
||||
con.sendResponse(res)
|
||||
}
|
||||
.listen(1337)
|
||||
```
|
||||
That's it.
|
||||
|
||||
As a bonus - this also has a tiny Connect class - which is modelled after the
|
||||
Node.JS Connect thingy (which in turn is apparently modelled after RoR Rack).
|
||||
It allows you to hook up a set of blocks for request processing, instead of
|
||||
having just a single entry point.
|
||||
Not sure I like that stuff, but it seems to fit into Swift quite well.
|
||||
|
||||
It works like this:
|
||||
```Swift
|
||||
let httpd = Connect()
|
||||
.use { rq, res, _, next in
|
||||
println("\(rq.method) \(rq.url) \(res.status)")
|
||||
next()
|
||||
}
|
||||
.use("/hello") { rq, res, con, next in
|
||||
res.bodyAsString = "Hello!"
|
||||
con.sendResponse(res)
|
||||
}
|
||||
.use("/") { rq, res, con, next in
|
||||
res.bodyAsString = "Always almost sometimes"
|
||||
con.sendResponse(res)
|
||||
}
|
||||
.listen(1337)
|
||||
let p = Expat()
|
||||
.onStartElement { name, attrs in println("<\(name) \(attrs)") }
|
||||
.onEndElement { name in println(">\(name)") }
|
||||
.onStartNamespace { prefix, uri in println("+NS[\(prefix)] = \(uri)") }
|
||||
.onEndNamespace { prefix in println("-NS[\(prefix)]") }
|
||||
.onError { error in println("ERROR: \(error)") }
|
||||
p.write("<hello>world</hello>")
|
||||
p.close()
|
||||
```
|
||||
|
||||
Finally there is a tiny HTTP client, use it like this:
|
||||
The raw Expat API works like this:
|
||||
```Swift
|
||||
GET("http://www.apple.com/")
|
||||
.done {
|
||||
println()
|
||||
println("request \($0)")
|
||||
println("response \($1)")
|
||||
println("body:\n\($1.bodyAsString)")
|
||||
}
|
||||
.fail {
|
||||
println("failed \($0): \($1)")
|
||||
}
|
||||
.always { println("---") }
|
||||
var p = XML_ParserCreate("UTF-8")
|
||||
XML_SetStartElementHandler(p) { _, name, attrs in println("start tag \(name)") }
|
||||
XML_SetEndElementHandler (p) { _, name in println("end tag \(name)") }
|
||||
|
||||
XML_Parse(parser, "<hello/>", 8, 0)
|
||||
XML_Parse(parser, "", 0, 1)
|
||||
|
||||
XML_ParserFree(p); p = nil
|
||||
```
|
||||
You get the idea ...
|
||||
|
||||
####SwiftyServer
|
||||
####SwiftyExpatTests
|
||||
|
||||
Great httpd server - great in counting the requests it got sent. This is not
|
||||
actually serving any files ;-) Comes along as a Cocoa app. Compile it, run it,
|
||||
then connect to it in the browser via http://127.0.0.1:1337/Awesome-O!
|
||||
|
||||
![](http://i.imgur.com/4ShGZXS.png)
|
||||
|
||||
####SwiftyClient
|
||||
|
||||
Just a demo on how to do HTTP requests via SwiftyHTTP. No, it doesn't do JSON
|
||||
decoding and such.
|
||||
|
||||
Again: You do NOT want to use it in a real iOS/OSX app! Use NSURLSession and
|
||||
companions - it gives you plenty of extra features you want to have for realz.
|
||||
|
||||
![](http://i.imgur.com/ny0PSKH.png)
|
||||
|
||||
###Goals
|
||||
|
||||
- [x] Max line length: 80 characters
|
||||
- [ ] Great error handling
|
||||
- [x] PS style great error handling
|
||||
- [x] println() error handling
|
||||
- [ ] Real error handling
|
||||
- [x] Twisted (no blocking reads or writes)
|
||||
- [x] Async reads and writes
|
||||
- [x] Never block on reads
|
||||
- [x] Never block on listen
|
||||
- [ ] Async connect()
|
||||
- [x] No NS'ism
|
||||
- [ ] Use as many language features Swift provides
|
||||
- [x] Generics
|
||||
- [x] Generic function
|
||||
- [x] typealias
|
||||
- [x] Closures
|
||||
- [x] weak self
|
||||
- [x] trailing closures
|
||||
- [x] implicit parameters
|
||||
- [x] Unowned
|
||||
- [x] Extensions on structs
|
||||
- [x] Extensions to organize classes
|
||||
- [x] Protocols on structs
|
||||
- [x] Tuples
|
||||
- [x] Trailing closures
|
||||
- [ ] @Lazy
|
||||
- [x] Pure Swift weak delegates via @class
|
||||
- [x] Optionals
|
||||
- [x] Convenience initializers
|
||||
- [x] Class variables on structs
|
||||
- [x] CConstPointer, CConstVoidPointer
|
||||
- [x] withCString {}
|
||||
- [x] UnsafePointer
|
||||
- [x] sizeof()
|
||||
- [x] Standard Protocols
|
||||
- [x] Printable
|
||||
- [x] LogicValue
|
||||
- [x] OutputStream
|
||||
- [x] Equatable
|
||||
- [x] Hashable
|
||||
- [x] Sequence (GeneratorOf<T>)
|
||||
- [x] Left shift AND right shift
|
||||
- [x] Enums on steroids
|
||||
- [ ] Dynamic type system, reflection
|
||||
- [x] Operator overloading
|
||||
- [x] UCS-4 identifiers (🐔🐔🐔)
|
||||
- [ ] ~~RTF source code with images and code sections in different fonts~~
|
||||
- [x] Nested classes/types
|
||||
- [ ] Patterns
|
||||
- [x] Use wildcard pattern to ignore value
|
||||
- [ ] @auto-closure
|
||||
- [ ] reinterpretCast()
|
||||
|
||||
###Why?!
|
||||
|
||||
This is an experiment to get acquainted with Swift. To check whether something
|
||||
real can be implemented in 'pure' Swift. Meaning, without using any Objective-C
|
||||
Cocoa classes (no NS'ism).
|
||||
Or in other words: Can you use Swift without writing all the 'real' code in
|
||||
wrapped Objective-C? :-)
|
||||
Just a tiny demo on how to invoke the parser.
|
||||
|
||||
###Contact
|
||||
|
||||
[@helje5](http://twitter.com/helje5) | helge@alwaysrightinstitute.com
|
||||
|
||||
![](http://www.alwaysrightinstitute.com/ARI.png)
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
/* Begin PBXBuildFile section */
|
||||
E8C667EC19757405004EFA0C /* Expat.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8C667EB19757405004EFA0C /* Expat.swift */; };
|
||||
E8C667EE19757B9D004EFA0C /* SwiftExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E8C667ED19757B9D004EFA0C /* SwiftExtensions.swift */; };
|
||||
E8FB77441971609A00E0557D /* SwiftyExpat.h in Headers */ = {isa = PBXBuildFile; fileRef = E8FB77431971609A00E0557D /* SwiftyExpat.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
E8FB77721971667F00E0557D /* ascii.h in Headers */ = {isa = PBXBuildFile; fileRef = E8FB775F1971667F00E0557D /* ascii.h */; };
|
||||
E8FB77731971667F00E0557D /* asciitab.h in Headers */ = {isa = PBXBuildFile; fileRef = E8FB77601971667F00E0557D /* asciitab.h */; };
|
||||
|
@ -65,6 +66,8 @@
|
|||
|
||||
/* Begin PBXFileReference section */
|
||||
E8C667EB19757405004EFA0C /* Expat.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Expat.swift; sourceTree = "<group>"; };
|
||||
E8C667ED19757B9D004EFA0C /* SwiftExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftExtensions.swift; sourceTree = "<group>"; };
|
||||
E8C667EF19757C40004EFA0C /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
|
||||
E8FB773E1971609A00E0557D /* SwiftyExpat.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftyExpat.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
E8FB77421971609A00E0557D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
E8FB77431971609A00E0557D /* SwiftyExpat.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SwiftyExpat.h; sourceTree = "<group>"; };
|
||||
|
@ -72,7 +75,7 @@
|
|||
E8FB77601971667F00E0557D /* asciitab.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = asciitab.h; sourceTree = "<group>"; };
|
||||
E8FB77611971667F00E0557D /* Changes */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Changes; sourceTree = "<group>"; };
|
||||
E8FB77621971667F00E0557D /* COPYING */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = COPYING; sourceTree = "<group>"; };
|
||||
E8FB77631971667F00E0557D /* expat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = expat.h; sourceTree = "<group>"; };
|
||||
E8FB77631971667F00E0557D /* expat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = expat.h; path = expat/expat.h; sourceTree = "<group>"; };
|
||||
E8FB77641971667F00E0557D /* expat_config.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = expat_config.h; sourceTree = "<group>"; };
|
||||
E8FB77651971667F00E0557D /* expat_external.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = expat_external.h; sourceTree = "<group>"; };
|
||||
E8FB77661971667F00E0557D /* iasciitab.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = iasciitab.h; sourceTree = "<group>"; };
|
||||
|
@ -116,6 +119,7 @@
|
|||
E8FB77341971609A00E0557D = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E8C667EF19757C40004EFA0C /* README.md */,
|
||||
E8FB77401971609A00E0557D /* SwiftyExpat */,
|
||||
E8FB77901971767500E0557D /* SwiftyExpatTests */,
|
||||
E8FB773F1971609A00E0557D /* Products */,
|
||||
|
@ -135,6 +139,8 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
E8C667EB19757405004EFA0C /* Expat.swift */,
|
||||
E8C667ED19757B9D004EFA0C /* SwiftExtensions.swift */,
|
||||
E8FB77631971667F00E0557D /* expat.h */,
|
||||
E8FB77431971609A00E0557D /* SwiftyExpat.h */,
|
||||
E8FB775E1971667F00E0557D /* expat */,
|
||||
E8FB77411971609A00E0557D /* Supporting Files */,
|
||||
|
@ -156,7 +162,6 @@
|
|||
children = (
|
||||
E8FB77621971667F00E0557D /* COPYING */,
|
||||
E8FB77611971667F00E0557D /* Changes */,
|
||||
E8FB77631971667F00E0557D /* expat.h */,
|
||||
E8FB7787197166E600E0557D /* char tables */,
|
||||
E8FB779F1971E5EE00E0557D /* implementation */,
|
||||
);
|
||||
|
@ -340,6 +345,7 @@
|
|||
files = (
|
||||
E8FB77811971667F00E0557D /* xmltok.c in Sources */,
|
||||
E8FB777E1971667F00E0557D /* xmlparse.c in Sources */,
|
||||
E8C667EE19757B9D004EFA0C /* SwiftExtensions.swift in Sources */,
|
||||
E8C667EC19757405004EFA0C /* Expat.swift in Sources */,
|
||||
E8FB777F1971667F00E0557D /* xmlrole.c in Sources */,
|
||||
);
|
||||
|
|
|
@ -12,9 +12,9 @@
|
|||
*
|
||||
* Done as a class as this is no value object (and struct's have no deinit())
|
||||
*/
|
||||
class Expat : OutputStream {
|
||||
class Expat : OutputStream, LogicValue {
|
||||
|
||||
let parser : XML_Parser
|
||||
var parser : XML_Parser = nil
|
||||
var isClosed = false
|
||||
|
||||
init(encoding: String = "UTF-8", nsSeparator: Character = ":") {
|
||||
|
@ -32,30 +32,64 @@ class Expat : OutputStream {
|
|||
parser = newParser
|
||||
}
|
||||
deinit {
|
||||
println("freeing parser ...")
|
||||
XML_ParserFree(parser)
|
||||
if parser {
|
||||
XML_ParserFree(parser)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* valid? */
|
||||
|
||||
func getLogicValue() -> Bool {
|
||||
return parser != nil
|
||||
}
|
||||
|
||||
|
||||
/* feed the parser */
|
||||
|
||||
func write(cs: CString) {
|
||||
let cslen = strlen(cs)
|
||||
XML_Parse(parser, cs, Int32(cslen), 0)
|
||||
func feed(cs: CString, final: Bool = false) -> ExpatResult {
|
||||
let cslen = cs ? strlen(cs) : 0 // cs? checks for a NULL C string
|
||||
let isFinal : Int32 = final ? 1 : 0
|
||||
let status : XML_Status = XML_Parse(parser, cs, Int32(cslen), isFinal)
|
||||
|
||||
switch status.value { // the Expat enum's don't work?
|
||||
case 1: return ExpatResult.OK
|
||||
case 2: return ExpatResult.Suspended
|
||||
default:
|
||||
let error = XML_GetErrorCode(parser)
|
||||
if let cb = errorCB {
|
||||
cb(error)
|
||||
}
|
||||
return ExpatResult.Error(error)
|
||||
}
|
||||
}
|
||||
|
||||
func feed(s: String) -> ExpatResult {
|
||||
return s.withCString { cs in self.feed(cs) }
|
||||
}
|
||||
|
||||
func write(s: String) {
|
||||
s.withCString { cs in self.write(cs) }
|
||||
let result = feed(s)
|
||||
|
||||
// doesn't work with associated value?: assert(ExpatResult.OK == result)
|
||||
switch result {
|
||||
case .OK: break
|
||||
default: assert(false)
|
||||
}
|
||||
}
|
||||
|
||||
func close() {
|
||||
if isClosed { return }
|
||||
func close() -> ExpatResult {
|
||||
if isClosed { return ExpatResult.OK /* do not complain */ }
|
||||
|
||||
let isFinal : Int32 = 1
|
||||
XML_Parse(parser, "", 0, isFinal)
|
||||
let result = feed("", final: true)
|
||||
|
||||
resetCallbacks()
|
||||
isClosed = true
|
||||
|
||||
XML_ParserFree(parser)
|
||||
parser = nil
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func resetCallbacks() {
|
||||
|
@ -119,6 +153,7 @@ class Expat : OutputStream {
|
|||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func onEndNamespace(cb: ( String? ) -> Void) -> Self {
|
||||
XML_SetEndNamespaceDeclHandler(parser) {
|
||||
_, prefix in
|
||||
|
@ -128,4 +163,65 @@ class Expat : OutputStream {
|
|||
return self
|
||||
}
|
||||
|
||||
func onCharacterData(cb: ( String ) -> Void) -> Self {
|
||||
//const XML_Char *s, int len);
|
||||
XML_SetCharacterDataHandler(parser) {
|
||||
_, cs, cslen in
|
||||
assert(cslen > 0)
|
||||
if cslen > 0 {
|
||||
let s = String.fromCString(cs, length: Int(cslen))!
|
||||
cb(s)
|
||||
}
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
func onError(cb: ( XML_Error ) -> Void) -> Self {
|
||||
errorCB = cb
|
||||
return self
|
||||
}
|
||||
var errorCB : (( XML_Error ) -> Void)? = nil
|
||||
}
|
||||
|
||||
extension XML_Error : Printable {
|
||||
|
||||
var description: String {
|
||||
switch self.value {
|
||||
// doesn't work?: case .XML_ERROR_NONE: return "OK"
|
||||
case 0 /* XML_ERROR_NONE */: return "OK"
|
||||
case 1 /* XML_ERROR_NO_MEMORY */: return "XMLError::NoMemory"
|
||||
case 2 /* XML_ERROR_SYNTAX */: return "XMLError::Syntax"
|
||||
case 3 /* XML_ERROR_NO_ELEMENTS */: return "XMLError::NoElements"
|
||||
case 4 /* XML_ERROR_INVALID_TOKEN */: return "XMLError::InvalidToken"
|
||||
case 5 /* XML_ERROR_UNCLOSED_TOKEN */: return "XMLError::UnclosedToken"
|
||||
case 6 /* XML_ERROR_PARTIAL_CHAR */: return "XMLError::PartialChar"
|
||||
case 7 /* XML_ERROR_TAG_MISMATCH */: return "XMLError::TagMismatch"
|
||||
case 8 /* XML_ERROR_DUPLICATE_ATTRIBUTE */: return "XMLError::DupeAttr"
|
||||
// FIXME: complete me
|
||||
default:
|
||||
return "XMLError(\(self.value))"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum ExpatResult : Printable, LogicValue {
|
||||
|
||||
case OK
|
||||
case Suspended
|
||||
case Error(XML_Error) // we cannot make this XML_Error, fails swiftc
|
||||
|
||||
var description: String {
|
||||
switch self {
|
||||
case .OK: return "OK"
|
||||
case .Suspended: return "Suspended"
|
||||
case .Error(let error): return "XMLError(\(error))"
|
||||
}
|
||||
}
|
||||
|
||||
func getLogicValue() -> Bool {
|
||||
switch self {
|
||||
case .OK: return true
|
||||
default: return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,34 @@
|
|||
// SwiftExtensions.swift
|
||||
// SwiftyExpat
|
||||
//
|
||||
// Created by Helge Heß on 7/15/14.
|
||||
// Created by Helge Heß on 6/18/14.
|
||||
// Copyright (c) 2014 Always Right Institute. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
// Those are mostly dirty hacks to get what I need :-)
|
||||
// I would be very interested in better way to do those things, W/O using
|
||||
// Foundation.
|
||||
|
||||
extension String {
|
||||
|
||||
static func fromCString(cs: CString, length: Int?) -> String? {
|
||||
if length == .None { // no length given, use \0 standard variant
|
||||
return String.fromCString(cs)
|
||||
}
|
||||
|
||||
// hh: this is really lame, there must be a better way :-)
|
||||
// Also: it could be a constant string! So we probably need to copy ...
|
||||
if let buf = cs.persist() {
|
||||
return buf.withUnsafePointerToElements {
|
||||
(p: UnsafePointer<CChar>) in
|
||||
let old = p[length!]
|
||||
p[length!] = 0
|
||||
let s = String.fromCString(CString(p))
|
||||
p[length!] = old
|
||||
return s
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,9 +19,11 @@ class SwiftyExpatTests: XCTestCase {
|
|||
|
||||
p = Expat()
|
||||
.onStartElement { name, attrs in println("<\(name) \(attrs)") }
|
||||
.onEndElement { name in println(">\(name)") }
|
||||
.onEndElement { name in println(">\(name)") }
|
||||
.onStartNamespace { prefix, uri in println("+NS[\(prefix)] = \(uri)") }
|
||||
.onEndNamespace { prefix in println("-NS[\(prefix)]") }
|
||||
.onEndNamespace { prefix in println("-NS[\(prefix)]") }
|
||||
.onCharacterData { content in println("TEXT: \(content)") }
|
||||
.onError { error in println("ERROR \(error)") }
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
|
@ -31,18 +33,28 @@ class SwiftyExpatTests: XCTestCase {
|
|||
|
||||
func testSimpleParsing() {
|
||||
XCTAssert(true, "Pass")
|
||||
|
||||
|
||||
var result : ExpatResult
|
||||
let testXML = "<hello xmlns='YoYo' a='5'><x>world</x></hello>"
|
||||
p.write(testXML)
|
||||
p.close() // EOF
|
||||
|
||||
result = p.feed(testXML)
|
||||
XCTAssert(result)
|
||||
|
||||
result = p.close() // EOF
|
||||
XCTAssert(result)
|
||||
}
|
||||
|
||||
/*
|
||||
func testPerformanceExample() {
|
||||
// This is an example of a performance test case.
|
||||
self.measureBlock() {
|
||||
// Put the code you want to measure the time of here.
|
||||
}
|
||||
|
||||
func testErrorHandling() {
|
||||
XCTAssert(true, "Pass")
|
||||
|
||||
var result : ExpatResult
|
||||
let testXML = "<hello xmlns='YoYo' a='5'>x>world</x></hello>"
|
||||
|
||||
result = p.feed(testXML)
|
||||
println("Feed result: \(result)")
|
||||
XCTAssert(!result)
|
||||
|
||||
result = p.close() // EOF
|
||||
XCTAssert(!result)
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue