()
17 |
18 | var request:Request?
19 | var response:Response?
20 | var expectedContentLength:Int?
21 | var didSendResponse = false;
22 |
23 | init(socket:GCDAsyncSocket, filterChain:FilterChain)
24 | {
25 | self.socket = socket
26 | self.filterChain = filterChain
27 | requestData = NSMutableData()
28 | super.init()
29 |
30 | socket.delegate = self
31 | socket.readDataWithTimeout(10, tag: 0)
32 | }
33 |
34 | func socket(socket:GCDAsyncSocket!, didReadData data:NSData!, withTag:Int)
35 | {
36 | if !request
37 | {
38 | request = Request(data: data)
39 | var contentLength:String? = request!.value(forHeaderKey: HeaderKey.ContentLength)
40 | expectedContentLength = contentLength ? contentLength!.toInt() : 0
41 | }
42 | else
43 | {
44 | requestData.appendData(data)
45 | }
46 |
47 | if requestData.length == expectedContentLength
48 | {
49 | request!.appendRequestData(requestData)
50 | response = Response();
51 |
52 | filterChain.processFilters(self)
53 | sendResponse()
54 | }
55 | else
56 | {
57 | socket.readDataWithTimeout(10, tag: 0)
58 | }
59 | }
60 |
61 | func sendResponse()
62 | {
63 | if didSendResponse
64 | {
65 | return;
66 | }
67 |
68 | didSendResponse = true;
69 | socket.writeData(response!.messageData, withTimeout: 5, tag: 0)
70 | socket.delegate = nil
71 | socket.disconnect()
72 | }
73 | }
74 |
75 |
76 |
--------------------------------------------------------------------------------
/SwiftServe/SwiftServe/ErrorPage.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ErrorPage.swift
3 | // SwiftServe
4 | //
5 | // Created by Anthony Picciano on 7/18/14.
6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class ErrorPage:Filter
12 | {
13 | override func processResponse(connection:Connection)
14 | {
15 | if connection.response!.data.length > 0
16 | {
17 | // there is already response data (perhaps JSON?), just return
18 | return
19 | }
20 |
21 | switch connection.response!.statusCode.code {
22 | case StatusCode.NOT_SET.code:
23 | connection.response!.statusCode = StatusCode.NOT_IMPLEMENTED
24 | fallthrough
25 |
26 | case 1...199, 300...599:
27 | sendErrorPage(connection)
28 |
29 | default:
30 | break
31 | }
32 | }
33 |
34 | func sendErrorPage(connection:Connection)
35 | {
36 | let appName = NSBundle.mainBundle().infoDictionary.objectForKey(kCFBundleNameKey) as String
37 | let version = NSBundle.mainBundle().infoDictionary.objectForKey("CFBundleShortVersionString") as String
38 | let host = connection.request!.value(forHeaderKey: HeaderKey.Host)
39 | let statusCodeCode:String = String(connection.response!.statusCode.code)
40 | let statusCodeDescription:String = connection.response!.statusCode.description
41 | let requestDescription:String = connection.request!.description
42 | let connectedHost:String = connection.socket.connectedHost
43 |
44 | var message:String
45 |
46 | if let constHost = host
47 | {
48 | message = ""
49 | + "\(statusCodeCode) \(statusCodeDescription)"
50 | + "\(statusCodeDescription)
"
51 | + "The request \"\(requestDescription)\" from \(connectedHost) failed.
"
52 | + "
\(appName)/\(version) (MacOSX) at \(constHost)"
53 | }
54 | else
55 | {
56 | message = ""
57 | + "\(connection.response!.statusCode.code) \(connection.response!.statusCode.description)"
58 | + "\(connection.response!.statusCode.description)
"
59 | + "The request \"\(connection.request!)\" from \(connection.socket.connectedHost) failed.
"
60 | + "
\(appName)/\(version) (MacOSX)"
61 | }
62 |
63 | let messageData = message.bridgeToObjectiveC().dataUsingEncoding(NSUTF8StringEncoding)
64 | connection.response!.data.appendData(messageData)
65 | }
66 | }
--------------------------------------------------------------------------------
/SwiftServe/SwiftServe/Filter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Filter.swift
3 | // SwiftServe
4 | //
5 | // Created by Anthony Picciano on 7/18/14.
6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | // TODO: class variable are not yet supported
12 | let connectionKey = "SwiftServe.Connection"
13 |
14 | class Filter
15 | {
16 | var includePaths:JLRoutes?
17 | var nextFilter:Filter?
18 |
19 | init()
20 | {
21 |
22 | }
23 |
24 | func processFilter(connection:Connection)
25 | {
26 | if !includePaths || includePaths!.canRouteURL(connection.request!.url)
27 | {
28 | processRequest(connection)
29 | }
30 |
31 | if nextFilter
32 | {
33 | nextFilter!.processFilter(connection)
34 | }
35 |
36 | if !includePaths || includePaths!.canRouteURL(connection.request!.url)
37 | {
38 | processResponse(connection)
39 | }
40 | }
41 |
42 | func processRequest(connection:Connection)
43 | {
44 | // override this method in subclasses
45 | }
46 |
47 | func processResponse(connection:Connection)
48 | {
49 | // override this method in subclasses
50 | }
51 |
52 | func includePath(path:String)
53 | {
54 | if !includePaths
55 | {
56 | let classname = NSString(UTF8String: object_getClassName(self))
57 | includePaths = JLRoutes(forScheme: classname)
58 | }
59 |
60 | includePaths!.addRoute(path, handler: nil)
61 | }
62 | }
--------------------------------------------------------------------------------
/SwiftServe/SwiftServe/FilterChain.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FilterChain.swift
3 | // SwiftServe
4 | //
5 | // Created by Anthony Picciano on 7/18/14.
6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class FilterChain
12 | {
13 | var filters = Array()
14 |
15 | func add(filter:Filter)
16 | {
17 | if filters.count > 0
18 | {
19 | let index = filters.count - 1
20 | let lastFiler = filters[index]
21 | lastFiler.nextFilter = filter
22 | }
23 | filters.append(filter)
24 | }
25 |
26 | func processFilters(connection:Connection)
27 | {
28 | if filters.count > 0
29 | {
30 | let firstFilter = filters[0]
31 | firstFilter.processFilter(connection)
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/SwiftServe/SwiftServe/GZipCompressor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GZipCompressor.swift
3 | // SwiftServe
4 | //
5 | // Created by Anthony Picciano on 7/19/14.
6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class GZipCompressor:Filter
12 | {
13 | override func processResponse(connection:Connection)
14 | {
15 | let data = connection.response!.data
16 |
17 | if data != nil && shouldGZipResponse(connection)
18 | {
19 | let compressedData = data.barista_gzipDeflate()
20 | if compressedData != nil && !compressedData.isEqualToData(data) && compressedData.length < data.length
21 | {
22 | connection.response!.data.setData(compressedData)
23 | connection.response!.setValue("gzip", forHeaderKey: HeaderKey.ContentEncoding)
24 | }
25 | }
26 | }
27 |
28 | func shouldGZipResponse(connection:Connection) -> Bool
29 | {
30 | let acceptEncoding:String? = connection.request!.value(forHeaderKey: HeaderKey.AcceptEncoding)
31 | return acceptEncoding && acceptEncoding!.rangeOfString("gzip")
32 | }
33 | }
--------------------------------------------------------------------------------
/SwiftServe/SwiftServe/HTTPMethod.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HTTPMethod.swift
3 | // SwiftServe
4 | //
5 | // Created by Anthony Picciano on 7/21/14.
6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | enum HTTPMethod:String
12 | {
13 | // common methods
14 | case GET = "GET"
15 | case POST = "POST"
16 | case PUT = "PUT"
17 | case DELETE = "DELETE"
18 |
19 | // lesser used methods
20 | case OPTIONS = "OPTIONS"
21 | case HEAD = "HEAD"
22 | case TRACE = "TRACE"
23 | }
--------------------------------------------------------------------------------
/SwiftServe/SwiftServe/HeaderKey.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HeaderKey.swift
3 | // SwiftServe
4 | //
5 | // Created by Anthony Picciano on 7/18/14.
6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | enum HeaderKey:String
12 | {
13 | case Accept = "Accept"
14 | case AcceptEncoding = "Accept-Encoding"
15 | case AcceptLanguage = "Accept-Language"
16 | case Authorization = "Authorization"
17 | case CacheControl = "Cache-Control"
18 | case Connection = "Connection"
19 | case Content = "Content"
20 | case ContentEncoding = "Content-Encoding"
21 | case ContentLength = "Content-Length"
22 | case ContentType = "Content-Type"
23 | case Cookie = "Cookie"
24 | case DNT = "DNT"
25 | case Host = "Host"
26 | case UserAgent = "User-Agent"
27 | }
--------------------------------------------------------------------------------
/SwiftServe/SwiftServe/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | com.picciano.${PRODUCT_NAME:rfc1034identifier}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | ${PRODUCT_NAME}
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | ${CURRENT_PROJECT_VERSION}
23 | NSHumanReadableCopyright
24 | Copyright © 2014 Anthony Picciano. All rights reserved.
25 | NSPrincipalClass
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/SwiftServe/SwiftServe/JLRoutes/JLRoutes.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2013, Joel Levin
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
6 |
7 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
8 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
9 | Neither the name of JLRoutes nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
10 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
11 | */
12 |
13 | #import
14 |
15 |
16 | static NSString *const kJLRoutePatternKey = @"JLRoutePattern";
17 | static NSString *const kJLRouteURLKey = @"JLRouteURL";
18 | static NSString *const kJLRouteNamespaceKey = @"JLRouteNamespace";
19 | static NSString *const kJLRouteWildcardComponentsKey = @"JLRouteWildcardComponents";
20 | static NSString *const kJLRoutesGlobalNamespaceKey = @"JLRoutesGlobalNamespace";
21 |
22 |
23 | @interface JLRoutes : NSObject
24 | /** @class JLRoutes
25 | JLRoutes is a way to manage URL routes and invoke them from a URL.
26 | */
27 |
28 | /// Returns the global routing namespace (this is used by the +addRoute methods by default)
29 | + (instancetype)globalRoutes;
30 |
31 | /// Returns a routing namespace for the given scheme
32 | + (instancetype)routesForScheme:(NSString *)scheme;
33 |
34 | /// Tells JLRoutes that it should manually replace '+' in parsed values to ' '. Defaults to YES.
35 | + (void)setShouldDecodePlusSymbols:(BOOL)shouldDeecode;
36 | + (BOOL)shouldDecodePlusSymbols;
37 |
38 | /// Registers a routePattern with default priority (0) in the receiving scheme namespace.
39 | + (void)addRoute:(NSString *)routePattern handler:(BOOL (^)(NSDictionary *parameters))handlerBlock;
40 | - (void)addRoute:(NSString *)routePattern handler:(BOOL (^)(NSDictionary *parameters))handlerBlock; // instance method
41 |
42 | /// Removes a routePattern from the receiving scheme namespace.
43 | + (void)removeRoute:(NSString *)routePattern;
44 | - (void)removeRoute:(NSString *)routePattern; // instance method
45 |
46 | /// Removes all routes from the receiving scheme namespace.
47 | + (void)removeAllRoutes;
48 | - (void)removeAllRoutes; // instance method
49 |
50 | /// Unregister and delete an entire scheme namespace
51 | + (void)unregisterRouteScheme:(NSString *)scheme;
52 |
53 | /// Registers a routePattern with default priority (0) using dictionary-style subscripting.
54 | - (void)setObject:(id)handlerBlock forKeyedSubscript:(NSString *)routePatten;
55 |
56 | /// Registers a routePattern in the global scheme namespace with a handlerBlock to call when the route pattern is matched by a URL.
57 | /// The block returns a BOOL representing if the handlerBlock actually handled the route or not. If
58 | /// a block returns NO, JLRoutes will continue trying to find a matching route.
59 | + (void)addRoute:(NSString *)routePattern priority:(NSUInteger)priority handler:(BOOL (^)(NSDictionary *parameters))handlerBlock;
60 | - (void)addRoute:(NSString *)routePattern priority:(NSUInteger)priority handler:(BOOL (^)(NSDictionary *parameters))handlerBlock; // instance method
61 |
62 | /// Routes a URL, calling handler blocks (for patterns that match URL) until one returns YES, optionally specifying add'l parameters
63 | + (BOOL)routeURL:(NSURL *)URL;
64 | + (BOOL)routeURL:(NSURL *)URL withParameters:(NSDictionary *)parameters;
65 |
66 | - (BOOL)routeURL:(NSURL *)URL; // instance method
67 | - (BOOL)routeURL:(NSURL *)URL withParameters:(NSDictionary *)parameters; // instance method
68 |
69 | /// Returns whether a route exists for a URL
70 | + (BOOL)canRouteURL:(NSURL *)URL;
71 | + (BOOL)canRouteURL:(NSURL *)URL withParameters:(NSDictionary *)parameters;
72 |
73 | - (BOOL)canRouteURL:(NSURL *)URL; // instance method
74 | - (BOOL)canRouteURL:(NSURL *)URL withParameters:(NSDictionary *)parameters; // instance method
75 |
76 | /// Prints the entire routing table
77 | + (NSString *)description;
78 |
79 | /// Allows configuration of verbose logging. Default is NO. This is mostly just helpful with debugging.
80 | + (void)setVerboseLoggingEnabled:(BOOL)loggingEnabled;
81 | + (BOOL)isVerboseLoggingEnabled;
82 |
83 | /// Controls whether or not this routes controller will try to match a URL with global routes if it can't be matched in the current namespace. Default is NO.
84 | @property (nonatomic, assign) BOOL shouldFallbackToGlobalRoutes;
85 |
86 | /// Called any time routeURL returns NO. Respects shouldFallbackToGlobalRoutes.
87 | @property (nonatomic, copy) void (^unmatchedURLHandler)(JLRoutes *routes, NSURL *URL, NSDictionary *parameters);
88 |
89 | @end
90 |
--------------------------------------------------------------------------------
/SwiftServe/SwiftServe/JLRoutes/JLRoutes.m:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright (c) 2013, Joel Levin
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
6 |
7 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
8 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
9 | Neither the name of JLRoutes nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
10 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
11 | */
12 |
13 | #import "JLRoutes.h"
14 |
15 |
16 | static NSMutableDictionary *routeControllersMap = nil;
17 | static BOOL verboseLoggingEnabled = NO;
18 | static BOOL shouldDecodePlusSymbols = YES;
19 |
20 |
21 | @interface JLRoutes ()
22 |
23 | @property (nonatomic, strong) NSMutableArray *routes;
24 | @property (nonatomic, strong) NSString *namespaceKey;
25 |
26 | + (void)verboseLogWithFormat:(NSString *)format, ...;
27 | + (BOOL)routeURL:(NSURL *)URL withController:(JLRoutes *)routesController parameters:(NSDictionary *)parameters;
28 | - (BOOL)isGlobalRoutesController;
29 |
30 | @end
31 |
32 |
33 | @interface NSString (JLRoutes)
34 |
35 | - (NSString *)JLRoutes_URLDecodedString;
36 | - (NSNumber *)JLRoutes_URLDecodedNumber;
37 | - (NSDictionary *)JLRoutes_URLParameterDictionary;
38 |
39 | @end
40 |
41 |
42 | @implementation NSString (JLRoutes)
43 |
44 | - (NSString *)JLRoutes_URLDecodedString {
45 | NSString *input = shouldDecodePlusSymbols ? [self stringByReplacingOccurrencesOfString:@"+" withString:@" " options:NSLiteralSearch range:NSMakeRange(0, self.length)] : self;
46 | return [input stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
47 | }
48 |
49 | - (NSNumber *)JLRoutes_URLDecodedNumber {
50 | NSNumberFormatter * f = [[NSNumberFormatter alloc] init];
51 | [f setNumberStyle:NSNumberFormatterDecimalStyle];
52 | return [f numberFromString:self];
53 | }
54 |
55 | - (NSDictionary *)JLRoutes_URLParameterDictionary {
56 | NSMutableDictionary *parameters = [NSMutableDictionary dictionary];
57 |
58 | if (self.length && [self rangeOfString:@"="].location != NSNotFound) {
59 | NSArray *keyValuePairs = [self componentsSeparatedByString:@"&"];
60 | for (NSString *keyValuePair in keyValuePairs) {
61 | NSArray *pair = [keyValuePair componentsSeparatedByString:@"="];
62 | // don't assume we actually got a real key=value pair. start by assuming we only got @[key] before checking count
63 | NSString *paramValue = pair.count == 2 ? pair[1] : @"";
64 | // CFURLCreateStringByReplacingPercentEscapesUsingEncoding may return NULL
65 | parameters[pair[0]] = [paramValue JLRoutes_URLDecodedString] ?: @"";
66 | }
67 | }
68 |
69 | return parameters;
70 | }
71 |
72 | @end
73 |
74 |
75 | @interface _JLRoute : NSObject
76 |
77 | @property (nonatomic, weak) JLRoutes *parentRoutesController;
78 | @property (nonatomic, strong) NSString *pattern;
79 | @property (nonatomic, strong) BOOL (^block)(NSDictionary *parameters);
80 | @property (nonatomic, assign) NSUInteger priority;
81 | @property (nonatomic, strong) NSArray *patternPathComponents;
82 |
83 | - (NSDictionary *)parametersForURL:(NSURL *)URL components:(NSArray *)URLComponents;
84 |
85 | @end
86 |
87 |
88 | @implementation _JLRoute
89 |
90 | - (NSDictionary *)parametersForURL:(NSURL *)URL components:(NSArray *)URLComponents {
91 | NSDictionary *routeParameters = nil;
92 |
93 | if (!self.patternPathComponents) {
94 | self.patternPathComponents = [[self.pattern pathComponents] filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"NOT SELF like '/'"]];
95 | }
96 |
97 | // do a quick component count check to quickly eliminate incorrect patterns
98 | BOOL componentCountEqual = self.patternPathComponents.count == URLComponents.count;
99 | BOOL routeContainsWildcard = !NSEqualRanges([self.pattern rangeOfString:@"*"], NSMakeRange(NSNotFound, 0));
100 | if (componentCountEqual || routeContainsWildcard) {
101 | // now that we've identified a possible match, move component by component to check if it's a match
102 | NSUInteger componentIndex = 0;
103 | NSMutableDictionary *variables = [NSMutableDictionary dictionary];
104 | BOOL isMatch = YES;
105 |
106 | for (NSString *patternComponent in self.patternPathComponents) {
107 | NSString *URLComponent = nil;
108 | if (componentIndex < [URLComponents count]) {
109 | URLComponent = URLComponents[componentIndex];
110 | } else if ([patternComponent isEqualToString:@"*"]) { // match /foo by /foo/*
111 | URLComponent = [URLComponents lastObject];
112 | }
113 |
114 | if ([patternComponent hasPrefix:@":"]) {
115 | // this component is a variable
116 | NSString *variableName = [patternComponent substringFromIndex:1];
117 | NSString *variableValue = URLComponent;
118 | if ([variableName length] > 0 && [variableValue length] > 0) {
119 | if ([variableValue JLRoutes_URLDecodedNumber] != nil) {
120 | variables[variableName] = [variableValue JLRoutes_URLDecodedNumber];
121 | } else {
122 | variables[variableName] = [variableValue JLRoutes_URLDecodedString];
123 | }
124 | }
125 | } else if ([patternComponent isEqualToString:@"*"]) {
126 | // match wildcards
127 | variables[kJLRouteWildcardComponentsKey] = [URLComponents subarrayWithRange:NSMakeRange(componentIndex, URLComponents.count-componentIndex)];
128 | isMatch = YES;
129 | break;
130 | } else if (![patternComponent isEqualToString:URLComponent]) {
131 | // a non-variable component did not match, so this route doesn't match up - on to the next one
132 | isMatch = NO;
133 | break;
134 | }
135 | componentIndex++;
136 | }
137 |
138 | if (isMatch) {
139 | routeParameters = variables;
140 | }
141 | }
142 |
143 | return routeParameters;
144 | }
145 |
146 |
147 | - (NSString *)description {
148 | return [NSString stringWithFormat:@"JLRoute %@ (%@)", self.pattern, @(self.priority)];
149 | }
150 |
151 |
152 | @end
153 |
154 |
155 | @implementation JLRoutes
156 |
157 | - (id)init {
158 | if ((self = [super init])) {
159 | self.routes = [NSMutableArray array];
160 | }
161 | return self;
162 | }
163 |
164 | + (void)setShouldDecodePlusSymbols:(BOOL)shouldDecode {
165 | shouldDecodePlusSymbols = shouldDecode;
166 | }
167 |
168 | + (BOOL)shouldDecodePlusSymbols {
169 | return shouldDecodePlusSymbols;
170 | }
171 |
172 | #pragma mark -
173 | #pragma mark Routing API
174 |
175 | + (instancetype)globalRoutes {
176 | return [self routesForScheme:kJLRoutesGlobalNamespaceKey];
177 | }
178 |
179 |
180 | + (instancetype)routesForScheme:(NSString *)scheme {
181 | JLRoutes *routesController = nil;
182 |
183 | static dispatch_once_t onceToken;
184 | dispatch_once(&onceToken, ^{
185 | routeControllersMap = [[NSMutableDictionary alloc] init];
186 | });
187 |
188 | if (!routeControllersMap[scheme]) {
189 | routesController = [[self alloc] init];
190 | routesController.namespaceKey = scheme;
191 | routeControllersMap[scheme] = routesController;
192 | }
193 |
194 | routesController = routeControllersMap[scheme];
195 |
196 | return routesController;
197 | }
198 |
199 |
200 | + (void)addRoute:(NSString *)routePattern handler:(BOOL (^)(NSDictionary *parameters))handlerBlock {
201 | [[self globalRoutes] addRoute:routePattern handler:handlerBlock];
202 | }
203 |
204 |
205 | + (void)addRoute:(NSString *)routePattern priority:(NSUInteger)priority handler:(BOOL (^)(NSDictionary *parameters))handlerBlock {
206 | [[self globalRoutes] addRoute:routePattern priority:priority handler:handlerBlock];
207 | }
208 |
209 |
210 | - (void)addRoute:(NSString *)routePattern handler:(BOOL (^)(NSDictionary *parameters))handlerBlock {
211 | [self addRoute:routePattern priority:0 handler:handlerBlock];
212 | }
213 |
214 |
215 | - (void)addRoute:(NSString *)routePattern priority:(NSUInteger)priority handler:(BOOL (^)(NSDictionary *parameters))handlerBlock {
216 | _JLRoute *route = [[_JLRoute alloc] init];
217 | route.pattern = routePattern;
218 | route.priority = priority;
219 | route.block = [handlerBlock copy];
220 | route.parentRoutesController = self;
221 |
222 | if (!route.block) {
223 | route.block = [^BOOL (NSDictionary *params) {
224 | return YES;
225 | } copy];
226 | }
227 |
228 | if (priority == 0) {
229 | [self.routes addObject:route];
230 | } else {
231 | NSArray *existingRoutes = self.routes;
232 | NSUInteger index = 0;
233 | for (_JLRoute *existingRoute in existingRoutes) {
234 | if (existingRoute.priority < priority) {
235 | [self.routes insertObject:route atIndex:index];
236 | break;
237 | }
238 | index++;
239 | }
240 | }
241 | }
242 |
243 |
244 | + (void)removeRoute:(NSString *)routePattern {
245 | [[JLRoutes globalRoutes] removeRoute:routePattern];
246 | }
247 |
248 |
249 | - (void)removeRoute:(NSString *)routePattern {
250 | if (![routePattern hasPrefix:@"/"]) {
251 | routePattern = [NSString stringWithFormat:@"/%@", routePattern];
252 | }
253 |
254 | NSInteger routeIndex = NSNotFound;
255 | NSInteger index = 0;
256 |
257 | for (_JLRoute *route in self.routes) {
258 | if ([route.pattern isEqualToString:routePattern]) {
259 | routeIndex = index;
260 | break;
261 | }
262 | index++;
263 | }
264 |
265 | if (routeIndex != NSNotFound) {
266 | [self.routes removeObjectAtIndex:(NSUInteger)routeIndex];
267 | }
268 | }
269 |
270 |
271 | + (void)removeAllRoutes {
272 | [[JLRoutes globalRoutes] removeAllRoutes];
273 | }
274 |
275 |
276 | - (void)removeAllRoutes {
277 | [self.routes removeAllObjects];
278 | }
279 |
280 |
281 | + (void)unregisterRouteScheme:(NSString *)scheme {
282 | [routeControllersMap removeObjectForKey:scheme];
283 | }
284 |
285 |
286 | + (BOOL)routeURL:(NSURL *)URL {
287 | return [self routeURL:URL withParameters:nil executeRouteBlock:YES];
288 | }
289 |
290 | + (BOOL)routeURL:(NSURL *)URL withParameters:(NSDictionary *)parameters {
291 | return [self routeURL:URL withParameters:parameters executeRouteBlock:YES];
292 | }
293 |
294 |
295 | + (BOOL)canRouteURL:(NSURL *)URL {
296 | return [self routeURL:URL withParameters:nil executeRouteBlock:NO];
297 | }
298 |
299 | + (BOOL)canRouteURL:(NSURL *)URL withParameters:(NSDictionary *)parameters {
300 | return [self routeURL:URL withParameters:parameters executeRouteBlock:NO];
301 | }
302 |
303 | + (BOOL)routeURL:(NSURL *)URL withParameters:(NSDictionary *)parameters executeRouteBlock:(BOOL)execute {
304 | if (!URL) {
305 | return NO;
306 | }
307 |
308 | // figure out which routes controller to use based on the scheme
309 | JLRoutes *routesController = routeControllersMap[[URL scheme]] ?: [self globalRoutes];
310 |
311 | return [self routeURL:URL withController:routesController parameters:parameters executeBlock:execute];
312 | }
313 |
314 | - (BOOL)routeURL:(NSURL *)URL {
315 | return [[self class] routeURL:URL withController:self];
316 | }
317 |
318 | - (BOOL)routeURL:(NSURL *)URL withParameters:(NSDictionary *)parameters {
319 | return [[self class] routeURL:URL withController:self parameters:parameters];
320 | }
321 |
322 | - (BOOL)canRouteURL:(NSURL *)URL {
323 | return [[self class] routeURL:URL withController:self parameters:nil executeBlock:NO];
324 | }
325 |
326 | - (BOOL)canRouteURL:(NSURL *)URL withParameters:(NSDictionary *)parameters {
327 | return [[self class] routeURL:URL withController:self parameters:parameters executeBlock:NO];
328 | }
329 |
330 | #pragma mark -
331 | #pragma mark Debugging Aids
332 |
333 | - (NSString *)description {
334 | return [self.routes description];
335 | }
336 |
337 |
338 | + (NSString *)description {
339 | NSMutableString *descriptionString = [NSMutableString stringWithString:@"\n"];
340 |
341 | for (NSString *routesNamespace in routeControllersMap) {
342 | JLRoutes *routesController = routeControllersMap[routesNamespace];
343 | [descriptionString appendFormat:@"\"%@\":\n%@\n\n", routesController.namespaceKey, routesController.routes];
344 | }
345 |
346 | return descriptionString;
347 | }
348 |
349 |
350 | + (void)setVerboseLoggingEnabled:(BOOL)loggingEnabled {
351 | verboseLoggingEnabled = loggingEnabled;
352 | }
353 |
354 |
355 | + (BOOL)isVerboseLoggingEnabled {
356 | return verboseLoggingEnabled;
357 | }
358 |
359 |
360 | #pragma mark -
361 | #pragma mark Internal API
362 |
363 | + (BOOL)routeURL:(NSURL *)URL withController:(JLRoutes *)routesController {
364 | return [self routeURL:URL withController:routesController parameters:nil executeBlock:YES];
365 | }
366 |
367 | + (BOOL)routeURL:(NSURL *)URL withController:(JLRoutes *)routesController parameters:(NSDictionary *)parameters {
368 | return [self routeURL:URL withController:routesController parameters:parameters executeBlock:YES];
369 | }
370 |
371 | + (BOOL)routeURL:(NSURL *)URL withController:(JLRoutes *)routesController parameters:(NSDictionary *)parameters executeBlock:(BOOL)executeBlock {
372 | [self verboseLogWithFormat:@"Trying to route URL %@", URL];
373 | BOOL didRoute = NO;
374 | NSArray *routes = routesController.routes;
375 | NSDictionary *queryParameters = [URL.query JLRoutes_URLParameterDictionary];
376 | [self verboseLogWithFormat:@"Parsed query parameters: %@", queryParameters];
377 |
378 | NSDictionary *fragmentParameters = [URL.fragment JLRoutes_URLParameterDictionary];
379 | [self verboseLogWithFormat:@"Parsed fragment parameters: %@", fragmentParameters];
380 |
381 | // break the URL down into path components and filter out any leading/trailing slashes from it
382 | NSArray *pathComponents = [(URL.pathComponents ?: @[]) filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"NOT SELF like '/'"]];
383 |
384 | if ([URL.host rangeOfString:@"."].location == NSNotFound && ![URL.host isEqualToString:@"localhost"]) {
385 | // For backward compatibility, handle scheme://path/to/ressource as if path was part of the
386 | // path if it doesn't look like a domain name (no dot in it)
387 | pathComponents = [@[URL.host] arrayByAddingObjectsFromArray:pathComponents];
388 | }
389 |
390 | [self verboseLogWithFormat:@"URL path components: %@", pathComponents];
391 |
392 | for (_JLRoute *route in routes) {
393 | NSDictionary *matchParameters = [route parametersForURL:URL components:pathComponents];
394 | if (matchParameters) {
395 | [self verboseLogWithFormat:@"Successfully matched %@", route];
396 | if (!executeBlock) {
397 | return YES;
398 | }
399 |
400 | // add the URL parameters
401 | NSMutableDictionary *finalParameters = [NSMutableDictionary dictionary];
402 |
403 | // in increasing order of precedence: query, fragment, route, builtin
404 | [finalParameters addEntriesFromDictionary:queryParameters];
405 | [finalParameters addEntriesFromDictionary:fragmentParameters];
406 | [finalParameters addEntriesFromDictionary:matchParameters];
407 | [finalParameters addEntriesFromDictionary:parameters];
408 | finalParameters[kJLRoutePatternKey] = route.pattern;
409 | finalParameters[kJLRouteURLKey] = URL;
410 | __strong __typeof(route.parentRoutesController) strongParentRoutesController = route.parentRoutesController;
411 | finalParameters[kJLRouteNamespaceKey] = strongParentRoutesController.namespaceKey ?: [NSNull null];
412 |
413 | [self verboseLogWithFormat:@"Final parameters are %@", finalParameters];
414 | didRoute = route.block(finalParameters);
415 | if (didRoute) {
416 | break;
417 | }
418 | }
419 | }
420 |
421 | if (!didRoute) {
422 | [self verboseLogWithFormat:@"Could not find a matching route, returning NO"];
423 | }
424 |
425 | // if we couldn't find a match and this routes controller specifies to fallback and its also not the global routes controller, then...
426 | if (!didRoute && routesController.shouldFallbackToGlobalRoutes && ![routesController isGlobalRoutesController]) {
427 | [self verboseLogWithFormat:@"Falling back to global routes..."];
428 | didRoute = [self routeURL:URL withController:[self globalRoutes] parameters:parameters executeBlock:executeBlock];
429 | }
430 |
431 | // if, after everything, we did not route anything and we have an unmatched URL handler, then call it
432 | if (!didRoute && routesController.unmatchedURLHandler) {
433 | routesController.unmatchedURLHandler(routesController, URL, parameters);
434 | }
435 |
436 | return didRoute;
437 | }
438 |
439 |
440 | - (BOOL)isGlobalRoutesController {
441 | return [self.namespaceKey isEqualToString:kJLRoutesGlobalNamespaceKey];
442 | }
443 |
444 |
445 | + (void)verboseLogWithFormat:(NSString *)format, ... {
446 | if (verboseLoggingEnabled && format) {
447 | va_list argsList;
448 | va_start(argsList, format);
449 | #pragma clang diagnostic push
450 | #pragma clang diagnostic ignored "-Wformat-nonliteral"
451 | NSString *formattedLogMessage = [[NSString alloc] initWithFormat:format arguments:argsList];
452 | #pragma clang diagnostic pop
453 |
454 | va_end(argsList);
455 | NSLog(@"[JLRoutes]: %@", formattedLogMessage);
456 | }
457 | }
458 |
459 | #pragma mark -
460 | #pragma mark Subscripting
461 |
462 | - (void)setObject:(id)handlerBlock forKeyedSubscript:(NSString *)routePatten {
463 | [self addRoute:routePatten handler:handlerBlock];
464 | }
465 |
466 | @end
467 |
--------------------------------------------------------------------------------
/SwiftServe/SwiftServe/JSONSerialization.swift:
--------------------------------------------------------------------------------
1 | //
2 | // JSONSerialization.swift
3 | // SwiftServe
4 | //
5 | // Created by Anthony Picciano on 7/21/14.
6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | // TODO: class variable are not yet supported
12 | let jsonRequestObjectKey = "JSONRequestObject"
13 | let jsonResponseObjectKey = "JSONResponseObject"
14 |
15 | extension Connection
16 | {
17 | func JSONRequestObject() -> NSDictionary?
18 | {
19 | let value:Any? = customValues[jsonRequestObjectKey]
20 | return value as? NSDictionary
21 | }
22 |
23 | func setJSONRequestObject(object:NSDictionary)
24 | {
25 | customValues[jsonRequestObjectKey] = object
26 | }
27 |
28 | func JSONResponseObject() -> NSDictionary?
29 | {
30 | let value:Any? = customValues[jsonResponseObjectKey]
31 | return value as? NSDictionary
32 | }
33 |
34 | func setJSONResponseObject(object:NSDictionary)
35 | {
36 | customValues[jsonResponseObjectKey] = object
37 | }
38 | }
39 |
40 | class JSONSerialization:Filter
41 | {
42 | let options:NSJSONWritingOptions
43 |
44 | init (usePrettyPrint:Bool = false)
45 | {
46 | if usePrettyPrint
47 | {
48 | options = NSJSONWritingOptions.PrettyPrinted
49 | }
50 | else
51 | {
52 | options = nil
53 | }
54 | }
55 |
56 | override func processRequest(connection: Connection)
57 | {
58 | let contentType:String? = connection.request!.value(forHeaderKey: HeaderKey.ContentType)
59 | let contentTypeIsJSON = contentType && contentType!.rangeOfString(MimeType.APPLICATION_JSON.string)
60 | let httpMethod:String = connection.request!.httpMethod
61 |
62 | if httpMethod != HTTPMethod.POST.toRaw()
63 | && httpMethod != HTTPMethod.PUT.toRaw()
64 | && httpMethod != HTTPMethod.DELETE.toRaw()
65 | {
66 | return
67 | }
68 |
69 | if contentTypeIsJSON
70 | {
71 | let data = connection.request!.data
72 | var error:NSError?
73 | let object:AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &error)
74 |
75 | if error
76 | {
77 | connection.response!.statusCode = StatusCode.BAD_REQUEST
78 |
79 | let errorMessage = [ "message":"Missing or invalid JSON was received.", "debugDescription":error!.debugDescription ]
80 | connection.setJSONResponseObject(errorMessage);
81 |
82 | return
83 | }
84 |
85 | if object
86 | {
87 | connection.setJSONRequestObject(object as NSDictionary)
88 | }
89 | }
90 | }
91 |
92 | override func processResponse(connection: Connection)
93 | {
94 | let responseObject = connection.JSONResponseObject()
95 | let accept:String? = connection.request!.value(forHeaderKey: HeaderKey.Accept)
96 | let acceptJSON = accept && accept!.rangeOfString(MimeType.APPLICATION_JSON.string)
97 |
98 | if acceptJSON || responseObject
99 | {
100 | if responseObject
101 | {
102 | var error:NSError?
103 | let data = NSJSONSerialization.dataWithJSONObject(responseObject, options: options, error: &error)
104 |
105 | if (connection.response!.statusCode.code == StatusCode.NOT_SET.code)
106 | {
107 | connection.response!.statusCode = StatusCode.OK
108 | }
109 | connection.response!.setValue(MimeType.APPLICATION_JSON.string, forHeaderKey: HeaderKey.ContentType)
110 | connection.response!.data.appendData(data)
111 | }
112 | else
113 | {
114 | // Missing response object
115 | connection.response!.statusCode = StatusCode.NOT_IMPLEMENTED
116 |
117 | let errorMessage = [ "message":"Server did not return a response." ]
118 | connection.setJSONResponseObject(errorMessage);
119 |
120 | processResponse(connection)
121 | }
122 | }
123 | }
124 | }
--------------------------------------------------------------------------------
/SwiftServe/SwiftServe/Logging.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Logging.swift
3 | // SwiftServe
4 | //
5 | // Created by Anthony Picciano on 7/18/14.
6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class Logging:Filter
12 | {
13 | let df = NSDateFormatter()
14 |
15 | var dateFormatString:String
16 |
17 | init(dateFormatString:String = "MMM/dd/yyyy hh:mm:ss ZZZZ")
18 | {
19 | self.dateFormatString = dateFormatString
20 | }
21 |
22 | override func processResponse(connection: Connection)
23 | {
24 | println(logEntry(connection))
25 | }
26 |
27 | func logEntry(connection: Connection) -> String
28 | {
29 | let host:String = connection.socket.connectedHost
30 | let user:String = "-" //TODO: Get Remote-User
31 | let date:String = dateAsFormattedString()
32 | let requestDescription:String = connection.request!.description
33 | let responseDescription:String = connection.response!.description
34 |
35 | let log = "\(host) \(user) [\(date)] \"\(requestDescription)\" \(responseDescription)"
36 | return log
37 | }
38 |
39 | func dateAsFormattedString() -> String
40 | {
41 | df.dateFormat = dateFormatString
42 | let dateString:String = df.stringFromDate(NSDate())
43 | return dateString
44 | }
45 | }
--------------------------------------------------------------------------------
/SwiftServe/SwiftServe/MimeType.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MimeType.swift
3 | // SwiftServe
4 | //
5 | // Created by Anthony Picciano on 7/23/14.
6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct MimeType
12 | {
13 | let string:String
14 |
15 | static let APPLICATION_JSON = MimeType(string:"application/json")
16 | }
--------------------------------------------------------------------------------
/SwiftServe/SwiftServe/NSData+BaristaExtensions.h:
--------------------------------------------------------------------------------
1 | //
2 | // NSData+BaristaExtensions.h
3 | // Barista
4 | //
5 | // Created by Steve Streza on 4/28/13.
6 | // Copyright (c)barista_2013 Mustacheware. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | @interface NSData (BaristaExtensions)
12 |
13 | // ZLIB
14 | - (NSData *)barista_zlibInflate;
15 | - (NSData *)barista_zlibDeflate;
16 |
17 | // GZIP
18 | - (NSData *)barista_gzipInflate;
19 | - (NSData *)barista_gzipDeflate;
20 |
21 | @end
22 |
--------------------------------------------------------------------------------
/SwiftServe/SwiftServe/NSData+BaristaExtensions.m:
--------------------------------------------------------------------------------
1 | //
2 | // NSData+BaristaExtensions.m
3 | // Barista
4 | //
5 | // Created by Steve Streza on 4/28/13.
6 | // Copyright (c) 2013: Steve Streza
7 | //
8 | // Permission is hereby granted, free of charge, to any person obtaining a copy of
9 | // this software and associated documentation files (the "Software"), to deal in
10 | // the Software without restriction, including without limitation the rights to
11 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12 | // of the Software, and to permit persons to whom the Software is furnished to do
13 | // so, subject to the following conditions:
14 | //
15 | // The above copyright notice and this permission notice shall be included in all
16 | // copies or substantial portions of the Software.
17 | //
18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 | // SOFTWARE.
25 | //
26 |
27 | #import "NSData+BaristaExtensions.h"
28 | #include
29 |
30 | @implementation NSData (BaristaExtensions)
31 |
32 | - (NSData *)barista_zlibInflate
33 | {
34 | if ([self length] == 0) return self;
35 |
36 | unsigned full_length = (uInt)[self length];
37 | unsigned half_length = (uInt)[self length] / 2;
38 |
39 | NSMutableData *decompressed = [NSMutableData dataWithLength: full_length + half_length];
40 | BOOL done = NO;
41 | int status;
42 |
43 | z_stream strm;
44 | strm.next_in = (Bytef *)[self bytes];
45 | strm.avail_in = (uInt)[self length];
46 | strm.total_out = 0;
47 | strm.zalloc = Z_NULL;
48 | strm.zfree = Z_NULL;
49 |
50 | if (inflateInit (&strm) != Z_OK) return nil;
51 |
52 | while (!done)
53 | {
54 | // Make sure we have enough room and reset the lengths.
55 | if (strm.total_out >= [decompressed length])
56 | [decompressed increaseLengthBy: half_length];
57 | strm.next_out = [decompressed mutableBytes] + strm.total_out;
58 | strm.avail_out = [decompressed length] - strm.total_out;
59 |
60 | // Inflate another chunk.
61 | status = inflate (&strm, Z_SYNC_FLUSH);
62 | if (status == Z_STREAM_END) done = YES;
63 | else if (status != Z_OK) break;
64 | }
65 | if (inflateEnd (&strm) != Z_OK) return nil;
66 |
67 | // Set real length.
68 | if (done)
69 | {
70 | [decompressed setLength: strm.total_out];
71 | return [NSData dataWithData: decompressed];
72 | }
73 | else return nil;
74 | }
75 |
76 | - (NSData *)barista_zlibDeflate
77 | {
78 | if ([self length] == 0) return self;
79 |
80 | z_stream strm;
81 |
82 | strm.zalloc = Z_NULL;
83 | strm.zfree = Z_NULL;
84 | strm.opaque = Z_NULL;
85 | strm.total_out = 0;
86 | strm.next_in=(Bytef *)[self bytes];
87 | strm.avail_in = [self length];
88 |
89 | // Compresssion Levels:
90 | // Z_NO_COMPRESSION
91 | // Z_BEST_SPEED
92 | // Z_BEST_COMPRESSION
93 | // Z_DEFAULT_COMPRESSION
94 |
95 | if (deflateInit(&strm, Z_DEFAULT_COMPRESSION) != Z_OK) return nil;
96 |
97 | NSMutableData *compressed = [NSMutableData dataWithLength:16 * 1024]; // 16K chuncks for expansion
98 |
99 | do {
100 |
101 | if (strm.total_out >= [compressed length])
102 | [compressed increaseLengthBy:16 * 1024];
103 |
104 | strm.next_out = [compressed mutableBytes] + strm.total_out;
105 | strm.avail_out = [compressed length] - strm.total_out;
106 |
107 | deflate(&strm, Z_FINISH);
108 |
109 | } while (strm.avail_out == 0);
110 |
111 | deflateEnd(&strm);
112 |
113 | [compressed setLength: strm.total_out];
114 | return [NSData dataWithData: compressed];
115 | }
116 |
117 | - (NSData *)barista_gzipInflate
118 | {
119 | if ([self length] == 0) return self;
120 |
121 | unsigned full_length = [self length];
122 | unsigned half_length = [self length] / 2;
123 |
124 | NSMutableData *decompressed = [NSMutableData dataWithLength: full_length + half_length];
125 | BOOL done = NO;
126 | int status;
127 |
128 | z_stream strm;
129 | strm.next_in = (Bytef *)[self bytes];
130 | strm.avail_in = [self length];
131 | strm.total_out = 0;
132 | strm.zalloc = Z_NULL;
133 | strm.zfree = Z_NULL;
134 |
135 | if (inflateInit2(&strm, (15+32)) != Z_OK) return nil;
136 | while (!done)
137 | {
138 | // Make sure we have enough room and reset the lengths.
139 | if (strm.total_out >= [decompressed length])
140 | [decompressed increaseLengthBy: half_length];
141 | strm.next_out = [decompressed mutableBytes] + strm.total_out;
142 | strm.avail_out = [decompressed length] - strm.total_out;
143 |
144 | // Inflate another chunk.
145 | status = inflate (&strm, Z_SYNC_FLUSH);
146 | if (status == Z_STREAM_END) done = YES;
147 | else if (status != Z_OK) break;
148 | }
149 | if (inflateEnd (&strm) != Z_OK) return nil;
150 |
151 | // Set real length.
152 | if (done)
153 | {
154 | [decompressed setLength: strm.total_out];
155 | return [NSData dataWithData: decompressed];
156 | }
157 | else return nil;
158 | }
159 |
160 | - (NSData *)barista_gzipDeflate
161 | {
162 | if ([self length] == 0) return self;
163 |
164 | z_stream strm;
165 |
166 | strm.zalloc = Z_NULL;
167 | strm.zfree = Z_NULL;
168 | strm.opaque = Z_NULL;
169 | strm.total_out = 0;
170 | strm.next_in=(Bytef *)[self bytes];
171 | strm.avail_in = (unsigned int)[self length];
172 |
173 | // Compresssion Levels:
174 | // Z_NO_COMPRESSION
175 | // Z_BEST_SPEED
176 | // Z_BEST_COMPRESSION
177 | // Z_DEFAULT_COMPRESSION
178 |
179 | if (deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, (15+16), 8, Z_DEFAULT_STRATEGY) != Z_OK) return nil;
180 |
181 | NSMutableData *compressed = [NSMutableData dataWithLength:16 * 1024]; // 16K chunks for expansion
182 |
183 | do {
184 |
185 | if (strm.total_out >= [compressed length])
186 | [compressed increaseLengthBy:16 * 1024];
187 |
188 | strm.next_out = [compressed mutableBytes] + strm.total_out;
189 | strm.avail_out = (unsigned int)([compressed length] - strm.total_out);
190 |
191 | deflate(&strm, Z_FINISH);
192 |
193 | } while (strm.avail_out == 0);
194 |
195 | deflateEnd(&strm);
196 |
197 | [compressed setLength: strm.total_out];
198 | return [NSData dataWithData:compressed];
199 | }
200 |
201 | @end
202 |
--------------------------------------------------------------------------------
/SwiftServe/SwiftServe/NSDataExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSDataExptensions.swift
3 | // SwiftServe
4 | //
5 | // Created by Anthony Picciano on 7/18/14.
6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension NSData
12 | {
13 | func byteDataAsUInt8() -> UInt8[]
14 | {
15 | var array = UInt8[](count:length, repeatedValue: 0)
16 | getBytes(&array)
17 |
18 | return array
19 | }
20 | }
--------------------------------------------------------------------------------
/SwiftServe/SwiftServe/Request.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Request.swift
3 | // SwiftServe
4 | //
5 | // Created by Anthony Picciano on 7/18/14.
6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class Request:Printable
12 | {
13 | let message:CFHTTPMessage
14 | var headers = Dictionary()
15 |
16 | init(data:NSData)
17 | {
18 | message = CFHTTPMessageCreateEmpty(nil, 1).takeRetainedValue()
19 | CFHTTPMessageAppendBytes(message, data.byteDataAsUInt8(), data.length)
20 | parseHeaders()
21 | }
22 |
23 | func parseHeaders()
24 | {
25 | let headersNSDictionary:NSDictionary = CFHTTPMessageCopyAllHeaderFields(message).takeRetainedValue()
26 |
27 | for (key : AnyObject, value : AnyObject) in headersNSDictionary
28 | {
29 | headers[key as String] = value as? String
30 | // println("\(key): \(value)")
31 | }
32 | }
33 |
34 | func appendRequestData(data:NSData)
35 | {
36 | CFHTTPMessageAppendBytes(message, data.byteDataAsUInt8(), data.length)
37 | }
38 |
39 | var httpMethod:String
40 | {
41 | return CFHTTPMessageCopyRequestMethod(message).takeRetainedValue()
42 | }
43 |
44 | var url:NSURL
45 | {
46 | return CFHTTPMessageCopyRequestURL(message).takeRetainedValue()
47 | }
48 |
49 | var version:String
50 | {
51 | return CFHTTPMessageCopyVersion(message).takeRetainedValue()
52 | }
53 |
54 | var data:NSData
55 | {
56 | return CFHTTPMessageCopyBody(message).takeRetainedValue()
57 | }
58 |
59 | func value(forHeaderKey key:HeaderKey) -> String?
60 | {
61 | return headers[key.toRaw()];
62 | }
63 |
64 | var description:String
65 | {
66 | let result = httpMethod + " " + url.path + " " + version
67 | return result
68 | }
69 | }
--------------------------------------------------------------------------------
/SwiftServe/SwiftServe/Response.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Response.swift
3 | // SwiftServe
4 | //
5 | // Created by Anthony Picciano on 7/18/14.
6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class Response:Printable
12 | {
13 | let data:NSMutableData
14 | var headers:Dictionary
15 | var statusCode:StatusCode;
16 |
17 | init(statusCode:StatusCode = StatusCode.NOT_SET)
18 | {
19 | data = NSMutableData()
20 |
21 | self.statusCode = statusCode
22 |
23 | headers = Dictionary()
24 | setValue("close", forHeaderKey: HeaderKey.Content)
25 | }
26 |
27 | var message:CFHTTPMessageRef
28 | {
29 | // Create the response object reference
30 | let message = CFHTTPMessageCreateResponse(nil, statusCode.code, "", kCFHTTPVersion1_1).takeRetainedValue()
31 |
32 | // Set the content length
33 | CFHTTPMessageSetHeaderFieldValue(message, HeaderKey.ContentLength.toRaw() as NSString, "\(data.length)" as NSString);
34 |
35 | // Add the headers
36 | for (key, value) in headers
37 | {
38 | CFHTTPMessageSetHeaderFieldValue(message, key as NSString, value as NSString);
39 | }
40 |
41 | // Add the message body
42 | CFHTTPMessageSetBody(message, data)
43 |
44 | return message
45 | }
46 |
47 | var messageData:NSData
48 | {
49 | return CFHTTPMessageCopySerializedMessage(message).takeRetainedValue()
50 | }
51 |
52 | func setValue(value:String?, forHeaderKey key:HeaderKey)
53 | {
54 | headers[key.toRaw()] = value;
55 | }
56 |
57 | func value(forHeaderKey key:HeaderKey) -> String?
58 | {
59 | return headers[key.toRaw()];
60 | }
61 |
62 | var description:String
63 | {
64 | return "\(statusCode.code) \(data.length)"
65 | }
66 | }
--------------------------------------------------------------------------------
/SwiftServe/SwiftServe/Router.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Router.swift
3 | // SwiftServe
4 | //
5 | // Created by Anthony Picciano on 7/23/14.
6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class Router:Filter
12 | {
13 | override func processFilter(connection:Connection)
14 | {
15 | var handled = false
16 |
17 | if !includePaths || includePaths!.canRouteURL(connection.request!.url)
18 | {
19 | let classname = NSString(UTF8String: object_getClassName(self))
20 | let httpMethod = connection.request!.httpMethod
21 | let scheme = classname + httpMethod
22 | let parameters = [connectionKey:connection]
23 |
24 | handled = JLRoutes(forScheme: scheme).routeURL(connection.request!.url, withParameters: parameters)
25 | }
26 |
27 | if !handled && nextFilter
28 | {
29 | nextFilter!.processFilter(connection)
30 | }
31 | }
32 |
33 | func processRoutes(connection:Connection, parameters:Dictionary) -> Bool
34 | {
35 | // override this method in subclasses
36 | return true
37 | }
38 |
39 | func handler(parameters:NSDictionary!) -> Bool
40 | {
41 | if let connection = parameters[connectionKey] as? Connection
42 | {
43 | return processRoutes(connection, parameters: parameters)
44 | }
45 |
46 | return false
47 | }
48 |
49 | func addEndpoint(endpoint:String, httpMethod:HTTPMethod)
50 | {
51 | let classname = NSString(UTF8String: object_getClassName(self))
52 | let scheme = classname + httpMethod.toRaw()
53 | JLRoutes(forScheme: scheme).addRoute(endpoint, handler: handler)
54 | }
55 | }
--------------------------------------------------------------------------------
/SwiftServe/SwiftServe/Server.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Server.swift
3 | // SwiftServe
4 | //
5 | // Created by Anthony Picciano on 7/17/14.
6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class Server: GCDAsyncSocketDelegate
12 | {
13 | let filterChain = FilterChain()
14 | let boundHost:String?
15 | let port:UInt16
16 | @lazy var socket:GCDAsyncSocket = GCDAsyncSocket(delegate: self, delegateQueue: dispatch_get_main_queue())
17 | var isListening = false
18 |
19 | init(boundHost: String?, port: UInt16)
20 | {
21 | if boundHost {
22 | self.boundHost = boundHost!
23 | }
24 | self.port = port
25 |
26 | if let constBoundHost = self.boundHost
27 | {
28 | println("Server created with host: \(constBoundHost) and port: \(self.port).")
29 | }
30 | else
31 | {
32 | println("Server created with host: * and port: \(self.port).")
33 | }
34 | }
35 |
36 | convenience init(port:UInt16)
37 | {
38 | self.init(boundHost: nil, port: port)
39 | }
40 |
41 | func startServer() -> Bool
42 | {
43 | if isListening
44 | {
45 | return false
46 | }
47 |
48 | var error:NSError?
49 | if !socket.acceptOnInterface(boundHost, port: port, error: &error)
50 | {
51 | println("Couldn't start socket: \(error)")
52 | }
53 | else
54 | {
55 | println("Listening on \(port).")
56 | isListening = true
57 | }
58 |
59 | return isListening
60 | }
61 |
62 | func stopServer() -> Bool
63 | {
64 | if isListening
65 | {
66 | socket.disconnect()
67 | isListening = false
68 | return true
69 | }
70 | return false
71 | }
72 |
73 | func runForever()
74 | {
75 | while true
76 | {
77 | NSRunLoop.mainRunLoop().run()
78 | }
79 | }
80 |
81 | func socket(socket:GCDAsyncSocket!, didAcceptNewSocket newSocket:GCDAsyncSocket!)
82 | {
83 | Connection(socket: newSocket, filterChain:filterChain)
84 | }
85 |
86 | }
--------------------------------------------------------------------------------
/SwiftServe/SwiftServe/SimpleEndpoint.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SimpleEndpoint.swift
3 | // SwiftServe
4 | //
5 | // Created by Anthony Picciano on 7/22/14.
6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class SimpleEndpoint:Router
12 | {
13 | override func processRoutes(connection:Connection, parameters:Dictionary) -> Bool
14 | {
15 |
16 | if let id = parameters["id"] as? NSNumber
17 | {
18 | connection.response!.statusCode = StatusCode.OK
19 |
20 | let response = ["name":"Anthony", "answer":id, "keys":["asdf", "qwerty", "dvorak"] ]
21 | connection.setJSONResponseObject(response)
22 | }
23 | else
24 | {
25 | connection.response!.statusCode = StatusCode.BAD_REQUEST
26 |
27 | let response = ["message":"The id must be specified as numeric."]
28 | connection.setJSONResponseObject(response)
29 | }
30 |
31 | return true
32 | }
33 | }
--------------------------------------------------------------------------------
/SwiftServe/SwiftServe/StatusCode.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StatusCode.swift
3 | // SwiftServe
4 | //
5 | // Created by Anthony Picciano on 7/18/14.
6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct StatusCode
12 | {
13 | let code:Int
14 | let description:String
15 |
16 | static let CONTINUE = StatusCode(code:100, description:"Continue")
17 | static let SWITCHING_PROTOCOLS = StatusCode(code: 101, description:"Switching Protocols")
18 | static let OK = StatusCode(code: 200, description:"Okay")
19 | static let CREATED = StatusCode(code: 201, description:"Created")
20 | static let ACCEPTED = StatusCode(code: 202, description:"Accepted")
21 | static let NON_AUTHORITIVE_INFORMATION = StatusCode(code: 203, description:"Non-authoritive Information")
22 | static let NO_CONTENT = StatusCode(code: 204, description:"No Content")
23 | static let RESET_CONTENT = StatusCode(code: 205, description:"Reset Content")
24 | static let PARTIAL_CONTENT = StatusCode(code: 206, description:"Partial Content")
25 | static let MULTIPLE_CHOICES = StatusCode(code: 300, description:"Multiple Choices")
26 | static let MOVED_PERMANENTLY = StatusCode(code: 301, description:"Moved Permanently")
27 | static let FOUND = StatusCode(code: 302, description:"Found")
28 | static let SEE_OTHER = StatusCode(code: 303, description:"See Other")
29 | static let NOT_MODIFIED = StatusCode(code: 304, description:"Not Modified")
30 | static let USE_PROXY = StatusCode(code: 305, description:"Use Proxy")
31 | static let TEMPORARY_REDIRECT = StatusCode(code: 307, description:"Temporary Redirect")
32 | static let BAD_REQUEST = StatusCode(code: 400, description:"Bad Request")
33 | static let UNAUTHORIZED = StatusCode(code: 401, description:"Unauthorized")
34 | static let PAYMENT_REQUIRED = StatusCode(code: 402, description:"Payment Required")
35 | static let FORBIDDEN = StatusCode(code: 403, description:"Forbidden")
36 | static let NOT_FOUND = StatusCode(code: 404, description:"Not Found")
37 | static let METHOD_NOT_ALLOWED = StatusCode(code: 405, description:"Method Not Allowed")
38 | static let NOT_ACCEPTABLE = StatusCode(code: 406, description:"Not Acceptable")
39 | static let PROXY_AUTHENTICATION_REQUIRED = StatusCode(code: 407, description:"Proxy Authentication Required")
40 | static let REQUEST_TIMEOUT = StatusCode(code: 408, description:"Request Timeout")
41 | static let CONFLICT = StatusCode(code: 409, description:"Conflict")
42 | static let GONE = StatusCode(code: 410, description:"Gone")
43 | static let LENGTH_REQUIRED = StatusCode(code: 411, description:"Length Required")
44 | static let PRECONDITION_FAILED = StatusCode(code: 412, description:"Precondition Failed")
45 | static let REQUEST_ENTITY_TOO_LARGE = StatusCode(code: 413, description:"Request Entity Too Large")
46 | static let REQUEST_URI_TOO_LONG = StatusCode(code: 414, description:"Request URI Too Long")
47 | static let UNSUPPORTED_MEDIA_TYPE = StatusCode(code: 415, description:"Unsupported Media Type")
48 | static let REQUESTED_RANGE_NOT_SATISFIABLE = StatusCode(code: 416, description:"Requested Range Not Satisfiable")
49 | static let EXPECTATION_FAILED = StatusCode(code: 417, description:"Expectation Failed")
50 | static let SERVER_ERROR = StatusCode(code: 500, description:"Server Error")
51 | static let NOT_IMPLEMENTED = StatusCode(code: 501, description:"Not Implemented")
52 | static let BAD_GATEWAY = StatusCode(code: 502, description:"Bad Gateway")
53 | static let SERVICE_UNAVAILABLE = StatusCode(code: 503, description:"Service Unavailable")
54 | static let GATEWAY_TIMEOUT = StatusCode(code: 504, description:"Gateway Timeout")
55 | static let HTTP_VERSION_NOT_SUPPORTED = StatusCode(code: 505, description:"HTTP Version Not Supported")
56 |
57 | static let NOT_SET = StatusCode(code: 0, description:"Status Not Set")
58 | }
59 |
--------------------------------------------------------------------------------
/SwiftServe/SwiftServe/SwiftServe-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftServe-Bridging-Header.h
3 | // SwiftServe
4 | //
5 | // Created by Anthony Picciano on 7/19/14.
6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved.
7 | //
8 |
9 |
--------------------------------------------------------------------------------
/SwiftServe/SwiftServe/SwiftServe.h:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftServe.h
3 | // SwiftServe
4 | //
5 | // Created by Anthony Picciano on 7/17/14.
6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved.
7 | //
8 |
9 | #import
10 | #import
11 | #import
12 | #import
13 |
14 | //! Project version number for SwiftServe.
15 | FOUNDATION_EXPORT double SwiftServeVersionNumber;
16 |
17 | //! Project version string for SwiftServe.
18 | FOUNDATION_EXPORT const unsigned char SwiftServeVersionString[];
19 |
20 | // In this header, you should import all the public headers of your framework using statements like #import
21 |
22 |
--------------------------------------------------------------------------------
/SwiftServe/SwiftServeTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | com.picciano.${PRODUCT_NAME:rfc1034identifier}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | ${PRODUCT_NAME}
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/SwiftServe/SwiftServeTests/SwiftServeTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftServeTests.swift
3 | // SwiftServeTests
4 | //
5 | // Created by Anthony Picciano on 7/17/14.
6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | class SwiftServeTests: XCTestCase {
12 |
13 | override func setUp() {
14 | super.setUp()
15 | // Put setup code here. This method is called before the invocation of each test method in the class.
16 | }
17 |
18 | override func tearDown() {
19 | // Put teardown code here. This method is called after the invocation of each test method in the class.
20 | super.tearDown()
21 | }
22 |
23 | func testExample() {
24 | // This is an example of a functional test case.
25 | XCTAssert(true, "Pass")
26 | }
27 |
28 | func testPerformanceExample() {
29 | // This is an example of a performance test case.
30 | self.measureBlock() {
31 | // Put the code you want to measure the time of here.
32 | }
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/SwiftServeDemo/SwiftServeDemo.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | E4C8CCBC197886E1000B8FAE /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C8CCBB197886E1000B8FAE /* main.swift */; };
11 | E4C8CCBE197886E1000B8FAE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C8CCBD197886E1000B8FAE /* AppDelegate.swift */; };
12 | E4C8CCC1197886E1000B8FAE /* SwiftServeDemo.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = E4C8CCBF197886E1000B8FAE /* SwiftServeDemo.xcdatamodeld */; };
13 | E4C8CCC3197886E1000B8FAE /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E4C8CCC2197886E1000B8FAE /* Images.xcassets */; };
14 | E4C8CCC6197886E1000B8FAE /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = E4C8CCC4197886E1000B8FAE /* MainMenu.xib */; };
15 | E4C8CCD2197886E1000B8FAE /* SwiftServeDemoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C8CCD1197886E1000B8FAE /* SwiftServeDemoTests.swift */; };
16 | /* End PBXBuildFile section */
17 |
18 | /* Begin PBXContainerItemProxy section */
19 | E4C8CCCC197886E1000B8FAE /* PBXContainerItemProxy */ = {
20 | isa = PBXContainerItemProxy;
21 | containerPortal = E4C8CCAE197886E1000B8FAE /* Project object */;
22 | proxyType = 1;
23 | remoteGlobalIDString = E4C8CCB5197886E1000B8FAE;
24 | remoteInfo = SwiftServeDemo;
25 | };
26 | /* End PBXContainerItemProxy section */
27 |
28 | /* Begin PBXFileReference section */
29 | E4C8CCB6197886E1000B8FAE /* SwiftServeDemo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftServeDemo.app; sourceTree = BUILT_PRODUCTS_DIR; };
30 | E4C8CCBA197886E1000B8FAE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
31 | E4C8CCBB197886E1000B8FAE /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; };
32 | E4C8CCBD197886E1000B8FAE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
33 | E4C8CCC0197886E1000B8FAE /* SwiftServeDemo.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = SwiftServeDemo.xcdatamodel; sourceTree = ""; };
34 | E4C8CCC2197886E1000B8FAE /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; };
35 | E4C8CCC5197886E1000B8FAE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; };
36 | E4C8CCCB197886E1000B8FAE /* SwiftServeDemoTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftServeDemoTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
37 | E4C8CCD0197886E1000B8FAE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
38 | E4C8CCD1197886E1000B8FAE /* SwiftServeDemoTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftServeDemoTests.swift; sourceTree = ""; };
39 | /* End PBXFileReference section */
40 |
41 | /* Begin PBXFrameworksBuildPhase section */
42 | E4C8CCB3197886E1000B8FAE /* Frameworks */ = {
43 | isa = PBXFrameworksBuildPhase;
44 | buildActionMask = 2147483647;
45 | files = (
46 | );
47 | runOnlyForDeploymentPostprocessing = 0;
48 | };
49 | E4C8CCC8197886E1000B8FAE /* Frameworks */ = {
50 | isa = PBXFrameworksBuildPhase;
51 | buildActionMask = 2147483647;
52 | files = (
53 | );
54 | runOnlyForDeploymentPostprocessing = 0;
55 | };
56 | /* End PBXFrameworksBuildPhase section */
57 |
58 | /* Begin PBXGroup section */
59 | E4C8CCAD197886E1000B8FAE = {
60 | isa = PBXGroup;
61 | children = (
62 | E4C8CCB8197886E1000B8FAE /* SwiftServeDemo */,
63 | E4C8CCCE197886E1000B8FAE /* SwiftServeDemoTests */,
64 | E4C8CCB7197886E1000B8FAE /* Products */,
65 | );
66 | sourceTree = "";
67 | };
68 | E4C8CCB7197886E1000B8FAE /* Products */ = {
69 | isa = PBXGroup;
70 | children = (
71 | E4C8CCB6197886E1000B8FAE /* SwiftServeDemo.app */,
72 | E4C8CCCB197886E1000B8FAE /* SwiftServeDemoTests.xctest */,
73 | );
74 | name = Products;
75 | sourceTree = "";
76 | };
77 | E4C8CCB8197886E1000B8FAE /* SwiftServeDemo */ = {
78 | isa = PBXGroup;
79 | children = (
80 | E4C8CCBD197886E1000B8FAE /* AppDelegate.swift */,
81 | E4C8CCC2197886E1000B8FAE /* Images.xcassets */,
82 | E4C8CCC4197886E1000B8FAE /* MainMenu.xib */,
83 | E4C8CCBF197886E1000B8FAE /* SwiftServeDemo.xcdatamodeld */,
84 | E4C8CCB9197886E1000B8FAE /* Supporting Files */,
85 | );
86 | path = SwiftServeDemo;
87 | sourceTree = "";
88 | };
89 | E4C8CCB9197886E1000B8FAE /* Supporting Files */ = {
90 | isa = PBXGroup;
91 | children = (
92 | E4C8CCBA197886E1000B8FAE /* Info.plist */,
93 | E4C8CCBB197886E1000B8FAE /* main.swift */,
94 | );
95 | name = "Supporting Files";
96 | sourceTree = "";
97 | };
98 | E4C8CCCE197886E1000B8FAE /* SwiftServeDemoTests */ = {
99 | isa = PBXGroup;
100 | children = (
101 | E4C8CCD1197886E1000B8FAE /* SwiftServeDemoTests.swift */,
102 | E4C8CCCF197886E1000B8FAE /* Supporting Files */,
103 | );
104 | path = SwiftServeDemoTests;
105 | sourceTree = "";
106 | };
107 | E4C8CCCF197886E1000B8FAE /* Supporting Files */ = {
108 | isa = PBXGroup;
109 | children = (
110 | E4C8CCD0197886E1000B8FAE /* Info.plist */,
111 | );
112 | name = "Supporting Files";
113 | sourceTree = "";
114 | };
115 | /* End PBXGroup section */
116 |
117 | /* Begin PBXNativeTarget section */
118 | E4C8CCB5197886E1000B8FAE /* SwiftServeDemo */ = {
119 | isa = PBXNativeTarget;
120 | buildConfigurationList = E4C8CCD5197886E1000B8FAE /* Build configuration list for PBXNativeTarget "SwiftServeDemo" */;
121 | buildPhases = (
122 | E4C8CCB2197886E1000B8FAE /* Sources */,
123 | E4C8CCB3197886E1000B8FAE /* Frameworks */,
124 | E4C8CCB4197886E1000B8FAE /* Resources */,
125 | );
126 | buildRules = (
127 | );
128 | dependencies = (
129 | );
130 | name = SwiftServeDemo;
131 | productName = SwiftServeDemo;
132 | productReference = E4C8CCB6197886E1000B8FAE /* SwiftServeDemo.app */;
133 | productType = "com.apple.product-type.application";
134 | };
135 | E4C8CCCA197886E1000B8FAE /* SwiftServeDemoTests */ = {
136 | isa = PBXNativeTarget;
137 | buildConfigurationList = E4C8CCD8197886E1000B8FAE /* Build configuration list for PBXNativeTarget "SwiftServeDemoTests" */;
138 | buildPhases = (
139 | E4C8CCC7197886E1000B8FAE /* Sources */,
140 | E4C8CCC8197886E1000B8FAE /* Frameworks */,
141 | E4C8CCC9197886E1000B8FAE /* Resources */,
142 | );
143 | buildRules = (
144 | );
145 | dependencies = (
146 | E4C8CCCD197886E1000B8FAE /* PBXTargetDependency */,
147 | );
148 | name = SwiftServeDemoTests;
149 | productName = SwiftServeDemoTests;
150 | productReference = E4C8CCCB197886E1000B8FAE /* SwiftServeDemoTests.xctest */;
151 | productType = "com.apple.product-type.bundle.unit-test";
152 | };
153 | /* End PBXNativeTarget section */
154 |
155 | /* Begin PBXProject section */
156 | E4C8CCAE197886E1000B8FAE /* Project object */ = {
157 | isa = PBXProject;
158 | attributes = {
159 | LastUpgradeCheck = 0600;
160 | ORGANIZATIONNAME = "Anthony Picciano";
161 | TargetAttributes = {
162 | E4C8CCB5197886E1000B8FAE = {
163 | CreatedOnToolsVersion = 6.0;
164 | };
165 | E4C8CCCA197886E1000B8FAE = {
166 | CreatedOnToolsVersion = 6.0;
167 | TestTargetID = E4C8CCB5197886E1000B8FAE;
168 | };
169 | };
170 | };
171 | buildConfigurationList = E4C8CCB1197886E1000B8FAE /* Build configuration list for PBXProject "SwiftServeDemo" */;
172 | compatibilityVersion = "Xcode 3.2";
173 | developmentRegion = English;
174 | hasScannedForEncodings = 0;
175 | knownRegions = (
176 | en,
177 | Base,
178 | );
179 | mainGroup = E4C8CCAD197886E1000B8FAE;
180 | productRefGroup = E4C8CCB7197886E1000B8FAE /* Products */;
181 | projectDirPath = "";
182 | projectRoot = "";
183 | targets = (
184 | E4C8CCB5197886E1000B8FAE /* SwiftServeDemo */,
185 | E4C8CCCA197886E1000B8FAE /* SwiftServeDemoTests */,
186 | );
187 | };
188 | /* End PBXProject section */
189 |
190 | /* Begin PBXResourcesBuildPhase section */
191 | E4C8CCB4197886E1000B8FAE /* Resources */ = {
192 | isa = PBXResourcesBuildPhase;
193 | buildActionMask = 2147483647;
194 | files = (
195 | E4C8CCC3197886E1000B8FAE /* Images.xcassets in Resources */,
196 | E4C8CCC6197886E1000B8FAE /* MainMenu.xib in Resources */,
197 | );
198 | runOnlyForDeploymentPostprocessing = 0;
199 | };
200 | E4C8CCC9197886E1000B8FAE /* Resources */ = {
201 | isa = PBXResourcesBuildPhase;
202 | buildActionMask = 2147483647;
203 | files = (
204 | );
205 | runOnlyForDeploymentPostprocessing = 0;
206 | };
207 | /* End PBXResourcesBuildPhase section */
208 |
209 | /* Begin PBXSourcesBuildPhase section */
210 | E4C8CCB2197886E1000B8FAE /* Sources */ = {
211 | isa = PBXSourcesBuildPhase;
212 | buildActionMask = 2147483647;
213 | files = (
214 | E4C8CCBE197886E1000B8FAE /* AppDelegate.swift in Sources */,
215 | E4C8CCBC197886E1000B8FAE /* main.swift in Sources */,
216 | E4C8CCC1197886E1000B8FAE /* SwiftServeDemo.xcdatamodeld in Sources */,
217 | );
218 | runOnlyForDeploymentPostprocessing = 0;
219 | };
220 | E4C8CCC7197886E1000B8FAE /* Sources */ = {
221 | isa = PBXSourcesBuildPhase;
222 | buildActionMask = 2147483647;
223 | files = (
224 | E4C8CCD2197886E1000B8FAE /* SwiftServeDemoTests.swift in Sources */,
225 | );
226 | runOnlyForDeploymentPostprocessing = 0;
227 | };
228 | /* End PBXSourcesBuildPhase section */
229 |
230 | /* Begin PBXTargetDependency section */
231 | E4C8CCCD197886E1000B8FAE /* PBXTargetDependency */ = {
232 | isa = PBXTargetDependency;
233 | target = E4C8CCB5197886E1000B8FAE /* SwiftServeDemo */;
234 | targetProxy = E4C8CCCC197886E1000B8FAE /* PBXContainerItemProxy */;
235 | };
236 | /* End PBXTargetDependency section */
237 |
238 | /* Begin PBXVariantGroup section */
239 | E4C8CCC4197886E1000B8FAE /* MainMenu.xib */ = {
240 | isa = PBXVariantGroup;
241 | children = (
242 | E4C8CCC5197886E1000B8FAE /* Base */,
243 | );
244 | name = MainMenu.xib;
245 | sourceTree = "";
246 | };
247 | /* End PBXVariantGroup section */
248 |
249 | /* Begin XCBuildConfiguration section */
250 | E4C8CCD3197886E1000B8FAE /* Debug */ = {
251 | isa = XCBuildConfiguration;
252 | buildSettings = {
253 | ALWAYS_SEARCH_USER_PATHS = NO;
254 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
255 | CLANG_CXX_LIBRARY = "libc++";
256 | CLANG_ENABLE_MODULES = YES;
257 | CLANG_ENABLE_OBJC_ARC = YES;
258 | CLANG_WARN_BOOL_CONVERSION = YES;
259 | CLANG_WARN_CONSTANT_CONVERSION = YES;
260 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
261 | CLANG_WARN_EMPTY_BODY = YES;
262 | CLANG_WARN_ENUM_CONVERSION = YES;
263 | CLANG_WARN_INT_CONVERSION = YES;
264 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
265 | CLANG_WARN_UNREACHABLE_CODE = YES;
266 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
267 | CODE_SIGN_IDENTITY = "-";
268 | COPY_PHASE_STRIP = NO;
269 | ENABLE_STRICT_OBJC_MSGSEND = YES;
270 | GCC_C_LANGUAGE_STANDARD = gnu99;
271 | GCC_DYNAMIC_NO_PIC = NO;
272 | GCC_OPTIMIZATION_LEVEL = 0;
273 | GCC_PREPROCESSOR_DEFINITIONS = (
274 | "DEBUG=1",
275 | "$(inherited)",
276 | );
277 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
278 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
279 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
280 | GCC_WARN_UNDECLARED_SELECTOR = YES;
281 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
282 | GCC_WARN_UNUSED_FUNCTION = YES;
283 | GCC_WARN_UNUSED_VARIABLE = YES;
284 | MACOSX_DEPLOYMENT_TARGET = 10.9;
285 | METAL_ENABLE_DEBUG_INFO = YES;
286 | ONLY_ACTIVE_ARCH = YES;
287 | SDKROOT = macosx;
288 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
289 | };
290 | name = Debug;
291 | };
292 | E4C8CCD4197886E1000B8FAE /* Release */ = {
293 | isa = XCBuildConfiguration;
294 | buildSettings = {
295 | ALWAYS_SEARCH_USER_PATHS = NO;
296 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
297 | CLANG_CXX_LIBRARY = "libc++";
298 | CLANG_ENABLE_MODULES = YES;
299 | CLANG_ENABLE_OBJC_ARC = YES;
300 | CLANG_WARN_BOOL_CONVERSION = YES;
301 | CLANG_WARN_CONSTANT_CONVERSION = YES;
302 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
303 | CLANG_WARN_EMPTY_BODY = YES;
304 | CLANG_WARN_ENUM_CONVERSION = YES;
305 | CLANG_WARN_INT_CONVERSION = YES;
306 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
307 | CLANG_WARN_UNREACHABLE_CODE = YES;
308 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
309 | CODE_SIGN_IDENTITY = "-";
310 | COPY_PHASE_STRIP = YES;
311 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
312 | ENABLE_NS_ASSERTIONS = NO;
313 | ENABLE_STRICT_OBJC_MSGSEND = YES;
314 | GCC_C_LANGUAGE_STANDARD = gnu99;
315 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
316 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
317 | GCC_WARN_UNDECLARED_SELECTOR = YES;
318 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
319 | GCC_WARN_UNUSED_FUNCTION = YES;
320 | GCC_WARN_UNUSED_VARIABLE = YES;
321 | MACOSX_DEPLOYMENT_TARGET = 10.9;
322 | METAL_ENABLE_DEBUG_INFO = NO;
323 | SDKROOT = macosx;
324 | };
325 | name = Release;
326 | };
327 | E4C8CCD6197886E1000B8FAE /* Debug */ = {
328 | isa = XCBuildConfiguration;
329 | buildSettings = {
330 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
331 | COMBINE_HIDPI_IMAGES = YES;
332 | INFOPLIST_FILE = SwiftServeDemo/Info.plist;
333 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
334 | PRODUCT_NAME = "$(TARGET_NAME)";
335 | };
336 | name = Debug;
337 | };
338 | E4C8CCD7197886E1000B8FAE /* Release */ = {
339 | isa = XCBuildConfiguration;
340 | buildSettings = {
341 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
342 | COMBINE_HIDPI_IMAGES = YES;
343 | INFOPLIST_FILE = SwiftServeDemo/Info.plist;
344 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks";
345 | PRODUCT_NAME = "$(TARGET_NAME)";
346 | };
347 | name = Release;
348 | };
349 | E4C8CCD9197886E1000B8FAE /* Debug */ = {
350 | isa = XCBuildConfiguration;
351 | buildSettings = {
352 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/SwiftServeDemo.app/Contents/MacOS/SwiftServeDemo";
353 | COMBINE_HIDPI_IMAGES = YES;
354 | FRAMEWORK_SEARCH_PATHS = (
355 | "$(DEVELOPER_FRAMEWORKS_DIR)",
356 | "$(inherited)",
357 | );
358 | GCC_PREPROCESSOR_DEFINITIONS = (
359 | "DEBUG=1",
360 | "$(inherited)",
361 | );
362 | INFOPLIST_FILE = SwiftServeDemoTests/Info.plist;
363 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
364 | METAL_ENABLE_DEBUG_INFO = YES;
365 | PRODUCT_NAME = "$(TARGET_NAME)";
366 | TEST_HOST = "$(BUNDLE_LOADER)";
367 | };
368 | name = Debug;
369 | };
370 | E4C8CCDA197886E1000B8FAE /* Release */ = {
371 | isa = XCBuildConfiguration;
372 | buildSettings = {
373 | BUNDLE_LOADER = "$(BUILT_PRODUCTS_DIR)/SwiftServeDemo.app/Contents/MacOS/SwiftServeDemo";
374 | COMBINE_HIDPI_IMAGES = YES;
375 | FRAMEWORK_SEARCH_PATHS = (
376 | "$(DEVELOPER_FRAMEWORKS_DIR)",
377 | "$(inherited)",
378 | );
379 | INFOPLIST_FILE = SwiftServeDemoTests/Info.plist;
380 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
381 | METAL_ENABLE_DEBUG_INFO = NO;
382 | PRODUCT_NAME = "$(TARGET_NAME)";
383 | TEST_HOST = "$(BUNDLE_LOADER)";
384 | };
385 | name = Release;
386 | };
387 | /* End XCBuildConfiguration section */
388 |
389 | /* Begin XCConfigurationList section */
390 | E4C8CCB1197886E1000B8FAE /* Build configuration list for PBXProject "SwiftServeDemo" */ = {
391 | isa = XCConfigurationList;
392 | buildConfigurations = (
393 | E4C8CCD3197886E1000B8FAE /* Debug */,
394 | E4C8CCD4197886E1000B8FAE /* Release */,
395 | );
396 | defaultConfigurationIsVisible = 0;
397 | defaultConfigurationName = Release;
398 | };
399 | E4C8CCD5197886E1000B8FAE /* Build configuration list for PBXNativeTarget "SwiftServeDemo" */ = {
400 | isa = XCConfigurationList;
401 | buildConfigurations = (
402 | E4C8CCD6197886E1000B8FAE /* Debug */,
403 | E4C8CCD7197886E1000B8FAE /* Release */,
404 | );
405 | defaultConfigurationIsVisible = 0;
406 | };
407 | E4C8CCD8197886E1000B8FAE /* Build configuration list for PBXNativeTarget "SwiftServeDemoTests" */ = {
408 | isa = XCConfigurationList;
409 | buildConfigurations = (
410 | E4C8CCD9197886E1000B8FAE /* Debug */,
411 | E4C8CCDA197886E1000B8FAE /* Release */,
412 | );
413 | defaultConfigurationIsVisible = 0;
414 | };
415 | /* End XCConfigurationList section */
416 |
417 | /* Begin XCVersionGroup section */
418 | E4C8CCBF197886E1000B8FAE /* SwiftServeDemo.xcdatamodeld */ = {
419 | isa = XCVersionGroup;
420 | children = (
421 | E4C8CCC0197886E1000B8FAE /* SwiftServeDemo.xcdatamodel */,
422 | );
423 | currentVersion = E4C8CCC0197886E1000B8FAE /* SwiftServeDemo.xcdatamodel */;
424 | path = SwiftServeDemo.xcdatamodeld;
425 | sourceTree = "";
426 | versionGroupType = wrapper.xcdatamodel;
427 | };
428 | /* End XCVersionGroup section */
429 | };
430 | rootObject = E4C8CCAE197886E1000B8FAE /* Project object */;
431 | }
432 |
--------------------------------------------------------------------------------
/SwiftServeDemo/SwiftServeDemo/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // SwiftServeDemo
4 | //
5 | // Created by Anthony Picciano on 7/17/14.
6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 | import SwiftServe
11 |
12 | class AppDelegate: NSObject, NSApplicationDelegate {
13 |
14 | @IBOutlet var window: NSWindow
15 | @lazy var server:Server = Server(boundHost: nil, port: 3333)
16 |
17 | func applicationDidFinishLaunching(aNotification: NSNotification?)
18 | {
19 | server.filterChain.add(Logging())
20 | server.filterChain.add(GZipCompressor())
21 | server.filterChain.add(ErrorPage())
22 | server.filterChain.add(JSONSerialization(usePrettyPrint: true))
23 |
24 | var simpleEndpoint = SimpleEndpoint()
25 | simpleEndpoint.includePath("/api/*")
26 | simpleEndpoint.addEndpoint("/api/complex/:id", httpMethod: HTTPMethod.GET)
27 | server.filterChain.add(simpleEndpoint)
28 |
29 | startServer(self)
30 | }
31 |
32 | @IBAction func startServer(sender: AnyObject)
33 | {
34 | let success = server.startServer()
35 | }
36 |
37 | func applicationWillTerminate(aNotification: NSNotification?)
38 | {
39 |
40 | }
41 |
42 | @IBAction func saveAction(sender: AnyObject) {
43 | // Performs the save action for the application, which is to send the save: message to the application's managed object context. Any encountered errors are presented to the user.
44 | var error: NSError? = nil
45 |
46 | if let moc = self.managedObjectContext {
47 | if !moc.commitEditing() {
48 | println("\(NSStringFromClass(self.dynamicType)) unable to commit editing before saving")
49 | }
50 | if !moc.save(&error) {
51 | NSApplication.sharedApplication().presentError(error)
52 | }
53 | }
54 | }
55 |
56 | var applicationFilesDirectory: NSURL {
57 | // Returns the directory the application uses to store the Core Data store file. This code uses a directory named "com.picciano.SwiftServeDemo" in the user's Application Support directory.
58 | let fileManager = NSFileManager.defaultManager()
59 | let urls = fileManager.URLsForDirectory(.ApplicationSupportDirectory, inDomains: .UserDomainMask)
60 | let appSupportURL: AnyObject = urls[urls.endIndex - 1]
61 | return appSupportURL.URLByAppendingPathComponent("com.picciano.SwiftServeDemo")
62 | }
63 |
64 | var managedObjectModel: NSManagedObjectModel {
65 | // Creates if necessary and returns the managed object model for the application.
66 | if let mom = _managedObjectModel {
67 | return mom
68 | }
69 |
70 | let modelURL = NSBundle.mainBundle().URLForResource("SwiftServeDemo", withExtension: "momd")
71 | _managedObjectModel = NSManagedObjectModel(contentsOfURL: modelURL)
72 | return _managedObjectModel!
73 | }
74 | var _managedObjectModel: NSManagedObjectModel? = nil
75 |
76 | var persistentStoreCoordinator: NSPersistentStoreCoordinator? {
77 | // Returns the persistent store coordinator for the application. This implementation creates and return a coordinator, having added the store for the application to it. (The directory for the store is created, if necessary.)
78 | if let psc = _persistentStoreCoordinator {
79 | return psc
80 | }
81 |
82 | let mom = self.managedObjectModel
83 |
84 | let fileManager = NSFileManager.defaultManager()
85 | let applicationFilesDirectory = self.applicationFilesDirectory
86 | var error: NSError? = nil
87 |
88 | let optProperties: NSDictionary? = applicationFilesDirectory.resourceValuesForKeys([NSURLIsDirectoryKey], error: &error)
89 |
90 | if let properties = optProperties {
91 | if !properties[NSURLIsDirectoryKey].boolValue {
92 | // Customize and localize this error.
93 | let failureDescription = "Expected a folder to store application data, found a file \(applicationFilesDirectory.path)."
94 | let dict = NSMutableDictionary()
95 | dict[NSLocalizedDescriptionKey] = failureDescription
96 | error = NSError.errorWithDomain("YOUR_ERROR_DOMAIN", code: 101, userInfo: dict)
97 |
98 | NSApplication.sharedApplication().presentError(error)
99 | return nil
100 | }
101 | } else {
102 | var ok = false
103 | if error!.code == NSFileReadNoSuchFileError {
104 | ok = fileManager.createDirectoryAtPath(applicationFilesDirectory.path, withIntermediateDirectories: true, attributes: nil, error: &error)
105 | }
106 | if !ok {
107 | NSApplication.sharedApplication().presentError(error)
108 | return nil
109 | }
110 | }
111 |
112 | let url = applicationFilesDirectory.URLByAppendingPathComponent("SwiftServeDemo.storedata")
113 | var coordinator = NSPersistentStoreCoordinator(managedObjectModel: mom)
114 | if coordinator.addPersistentStoreWithType(NSXMLStoreType, configuration: nil, URL: url, options: nil, error: &error) == nil {
115 | NSApplication.sharedApplication().presentError(error)
116 | return nil
117 | }
118 | _persistentStoreCoordinator = coordinator
119 |
120 | return _persistentStoreCoordinator
121 | }
122 | var _persistentStoreCoordinator: NSPersistentStoreCoordinator? = nil
123 |
124 | var managedObjectContext: NSManagedObjectContext? {
125 | // Returns the managed object context for the application (which is already bound to the persistent store coordinator for the application.)
126 | if let moc = _managedObjectContext {
127 | return moc
128 | }
129 |
130 | let coordinator = self.persistentStoreCoordinator
131 | if !coordinator {
132 | var dict = NSMutableDictionary()
133 | dict[NSLocalizedDescriptionKey] = "Failed to initialize the store"
134 | dict[NSLocalizedFailureReasonErrorKey] = "There was an error building up the data file."
135 | let error = NSError.errorWithDomain("YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)
136 | NSApplication.sharedApplication().presentError(error)
137 | return nil
138 | }
139 | _managedObjectContext = NSManagedObjectContext()
140 | _managedObjectContext!.persistentStoreCoordinator = coordinator!
141 |
142 | return _managedObjectContext
143 | }
144 | var _managedObjectContext: NSManagedObjectContext? = nil
145 |
146 | func windowWillReturnUndoManager(window: NSWindow?) -> NSUndoManager? {
147 | // Returns the NSUndoManager for the application. In this case, the manager returned is that of the managed object context for the application.
148 | if let moc = self.managedObjectContext {
149 | return moc.undoManager
150 | } else {
151 | return nil
152 | }
153 | }
154 |
155 | func applicationShouldTerminate(sender: NSApplication) -> NSApplicationTerminateReply {
156 | // Save changes in the application's managed object context before the application terminates.
157 |
158 | if !_managedObjectContext {
159 | // Accesses the underlying stored property because we don't want to cause the lazy initialization
160 | return .TerminateNow
161 | }
162 | let moc = self.managedObjectContext!
163 | if !moc.commitEditing() {
164 | println("\(NSStringFromClass(self.dynamicType)) unable to commit editing to terminate")
165 | return .TerminateCancel
166 | }
167 |
168 | if !moc.hasChanges {
169 | return .TerminateNow
170 | }
171 |
172 | var error: NSError? = nil
173 | if !moc.save(&error) {
174 | // Customize this code block to include application-specific recovery steps.
175 | let result = sender.presentError(error)
176 | if (result) {
177 | return .TerminateCancel
178 | }
179 |
180 | let question = "Could not save changes while quitting. Quit anyway?" // NSLocalizedString(@"Could not save changes while quitting. Quit anyway?", @"Quit without saves error question message")
181 | let info = "Quitting now will lose any changes you have made since the last successful save" // NSLocalizedString(@"Quitting now will lose any changes you have made since the last successful save", @"Quit without saves error question info");
182 | let quitButton = "Quit anyway" // NSLocalizedString(@"Quit anyway", @"Quit anyway button title")
183 | let cancelButton = "Cancel" // NSLocalizedString(@"Cancel", @"Cancel button title")
184 | let alert = NSAlert()
185 | alert.messageText = question
186 | alert.informativeText = info
187 | alert.addButtonWithTitle(quitButton)
188 | alert.addButtonWithTitle(cancelButton)
189 |
190 | let answer = alert.runModal()
191 | if answer == NSAlertFirstButtonReturn {
192 | return .TerminateCancel
193 | }
194 | }
195 |
196 | return .TerminateNow
197 | }
198 |
199 | }
200 |
201 |
--------------------------------------------------------------------------------
/SwiftServeDemo/SwiftServeDemo/Base.lproj/MainMenu.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
669 |
670 |
671 |
672 |
673 |
674 |
675 |
676 |
677 |
678 |
679 |
680 |
681 |
682 |
--------------------------------------------------------------------------------
/SwiftServeDemo/SwiftServeDemo/Images.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "mac",
5 | "size" : "16x16",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "mac",
10 | "size" : "16x16",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "mac",
15 | "size" : "32x32",
16 | "scale" : "1x"
17 | },
18 | {
19 | "idiom" : "mac",
20 | "size" : "32x32",
21 | "scale" : "2x"
22 | },
23 | {
24 | "idiom" : "mac",
25 | "size" : "128x128",
26 | "scale" : "1x"
27 | },
28 | {
29 | "idiom" : "mac",
30 | "size" : "128x128",
31 | "scale" : "2x"
32 | },
33 | {
34 | "idiom" : "mac",
35 | "size" : "256x256",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "mac",
40 | "size" : "256x256",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "mac",
45 | "size" : "512x512",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "mac",
50 | "size" : "512x512",
51 | "scale" : "2x"
52 | }
53 | ],
54 | "info" : {
55 | "version" : 1,
56 | "author" : "xcode"
57 | }
58 | }
--------------------------------------------------------------------------------
/SwiftServeDemo/SwiftServeDemo/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIconFile
10 |
11 | CFBundleIdentifier
12 | com.picciano.${PRODUCT_NAME:rfc1034identifier}
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | ${PRODUCT_NAME}
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | 1
25 | LSMinimumSystemVersion
26 | ${MACOSX_DEPLOYMENT_TARGET}
27 | NSHumanReadableCopyright
28 | Copyright © 2014 Anthony Picciano. All rights reserved.
29 | NSMainNibFile
30 | MainMenu
31 | NSPrincipalClass
32 | NSApplication
33 |
34 |
35 |
--------------------------------------------------------------------------------
/SwiftServeDemo/SwiftServeDemo/SwiftServeDemo.xcdatamodeld/.xccurrentversion:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | _XCCurrentVersionName
6 | SwiftServeDemo.xcdatamodel
7 |
8 |
9 |
--------------------------------------------------------------------------------
/SwiftServeDemo/SwiftServeDemo/SwiftServeDemo.xcdatamodeld/SwiftServeDemo.xcdatamodel/contents:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/SwiftServeDemo/SwiftServeDemo/main.swift:
--------------------------------------------------------------------------------
1 | //
2 | // main.swift
3 | // SwiftServeDemo
4 | //
5 | // Created by Anthony Picciano on 7/17/14.
6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | NSApplicationMain(C_ARGC, C_ARGV)
12 |
--------------------------------------------------------------------------------
/SwiftServeDemo/SwiftServeDemoTests/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | ${EXECUTABLE_NAME}
9 | CFBundleIdentifier
10 | com.picciano.${PRODUCT_NAME:rfc1034identifier}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | ${PRODUCT_NAME}
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 |
24 |
25 |
--------------------------------------------------------------------------------
/SwiftServeDemo/SwiftServeDemoTests/SwiftServeDemoTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftServeDemoTests.swift
3 | // SwiftServeDemoTests
4 | //
5 | // Created by Anthony Picciano on 7/17/14.
6 | // Copyright (c) 2014 Anthony Picciano. All rights reserved.
7 | //
8 |
9 | import XCTest
10 |
11 | class SwiftServeDemoTests: XCTestCase {
12 |
13 | override func setUp() {
14 | super.setUp()
15 | // Put setup code here. This method is called before the invocation of each test method in the class.
16 | }
17 |
18 | override func tearDown() {
19 | // Put teardown code here. This method is called after the invocation of each test method in the class.
20 | super.tearDown()
21 | }
22 |
23 | func testExample() {
24 | // This is an example of a functional test case.
25 | XCTAssert(true, "Pass")
26 | }
27 |
28 | func testPerformanceExample() {
29 | // This is an example of a performance test case.
30 | self.measureBlock() {
31 | // Put the code you want to measure the time of here.
32 | }
33 | }
34 |
35 | }
36 |
--------------------------------------------------------------------------------