dramatic naming refactor

This commit is contained in:
Peter 2015-10-30 10:13:45 -04:00
parent b9dae4db1a
commit fe2107e446
15 changed files with 714 additions and 628 deletions

View File

@ -1,10 +1,22 @@
// Copyright (c) 2015 Peter Siegesmund <peter.siegesmund@icloud.com>
//
// CoreDataExtensions.swift
// SwiftTodos
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// Created by Peter Siegesmund on 10/29/15.
// Copyright © 2015 Peter Siegesmund. All rights reserved.
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
import CoreData

View File

@ -32,351 +32,359 @@ import XCGLogger
let log = XCGLogger(identifier: "DDP")
public class DDP {
public class Client: NSObject {
// included for storing login id and token
let userData = NSUserDefaults.standardUserDefaults()
public let incomingData:NSOperationQueue = {
let queue = NSOperationQueue()
queue.name = "DDP Incoming Data Queue"
queue.maxConcurrentOperationCount = 1
return queue
}()
// Calling methods on the server + their callbacks
public let outgoingData:NSOperationQueue = {
let queue = NSOperationQueue()
queue.name = "DDP Outgoing Data Queue"
// queue.maxConcurrentOperationCount = 1
return queue
}()
public let operation:NSOperationQueue = {
let queue = NSOperationQueue()
queue.name = "DDP Operation Queue"
queue.maxConcurrentOperationCount = 1
return queue
}()
public let heartbeat:NSOperationQueue = {
let queue = NSOperationQueue()
queue.name = "DDP Heartbeat Queue"
//queue.maxConcurrentOperationCount = 1
return queue
}()
public let mainQueue = NSOperationQueue.mainQueue()
private var socket:WebSocket!
private var server:(ping:NSDate?, pong:NSDate?) = (nil, nil)
var resultCallbacks:[String:(result:AnyObject?, error:DDP.Error?) -> ()] = [:]
var subCallbacks:[String:() -> ()] = [:]
var unsubCallbacks:[String:() -> ()] = [:]
var url:String!
var subscriptions = [String:(id:String, name:String, ready:Bool)]()
public var logLevel = XCGLogger.LogLevel.Debug
public var events = DDP.Events()
public var connection:(ddp:Bool, session:String?) = (false, nil)
public override init() {
super.init()
setLogLevel(logLevel)
}
public func getId() -> String {
let numbers = Set<Character>(["0","1","2","3","4","5","6","7","8","9"])
let uuid = NSUUID().UUIDString.stringByReplacingOccurrencesOfString("-", withString: "")
var id = ""
for character in uuid.characters {
if (!numbers.contains(character) && (round(Float(arc4random()) / Float(UINT32_MAX)) == 1)) {
id += String(character).lowercaseString
} else {
id += String(character)
}
}
return id
}
public func connect(url:String, callback:((session:String)->())?) {
socket = WebSocket(url)
socket.event.close = {code, reason, clean in
log.info("Web socket connection closed with code \(code). Clean: \(clean). \(reason)")
let event = self.socket.event
self.socket = WebSocket(url)
self.socket.event = event
self.ping()
}
socket.event.error = events.onWebsocketError
socket.event.open = {
self.heartbeat.addOperationWithBlock() {
if let c = callback { self.events.onConnected = c }
self.sendMessage(["msg":"connect", "version":"1", "support":["1"]])
}
}
socket.event.message = { message in
self.operation.addOperationWithBlock() {
if let text = message as? String {
do { try self.ddpMessageHandler(DDP.Message(message: text)) }
catch { log.debug("Message handling error. Raw message: \(text)")}
}
}
}
}
public func setLogLevel(logLevel:XCGLogger.LogLevel) {
log.setup(logLevel, showLogIdentifier: true, showFunctionName: true, showThreadName: true, showLogLevel: true, showFileNames: false, showLineNumbers: true, showDate: false, writeToFile: nil, fileLogLevel: .None)
}
func ping() {
heartbeat.addOperationWithBlock() {
self.sendMessage(["msg":"ping", "id":self.getId()])
}
}
// Respond to a server ping
private func pong(ping: DDP.Message) {
heartbeat.addOperationWithBlock() {
self.server.ping = NSDate()
log.debug("Ping")
var response = ["msg":"pong"]
if let id = ping.id { response["id"] = id }
self.sendMessage(response)
}
}
// Parse DDP messages and dispatch to the appropriate function
func ddpMessageHandler(message: DDP.Message) throws {
log.debug("Received message: \(message.json)")
switch message.type {
case .Connected:
self.connection = (true, message.session!)
self.events.onConnected(session:message.session!)
case .Result: incomingData.addOperationWithBlock() {
if let id = message.id, // Message has id
let callback = self.resultCallbacks[id], // There is a callback registered for the message
let result = message.result {
callback(result:result, error: message.error)
self.resultCallbacks[id] = nil
} else if let id = message.id,
let callback = self.resultCallbacks[id] {
callback(result:nil, error:message.error)
self.resultCallbacks[id] = nil
}
}
// Principal callbacks for managing data
// Document was added
case .Added: incomingData.addOperationWithBlock() {
if let collection = message.collection,
let id = message.id {
self.documentWasAdded(collection, id: id, fields: message.fields)
}
}
// Document was changed
case .Changed: incomingData.addOperationWithBlock() {
if let collection = message.collection,
let id = message.id {
self.documentWasChanged(collection, id: id, fields: message.fields, cleared: message.cleared)
}
}
// Document was removed
case .Removed: incomingData.addOperationWithBlock() {
if let collection = message.collection,
let id = message.id {
self.documentWasRemoved(collection, id: id)
}
}
// Notifies you when the result of a method changes
case .Updated: incomingData.addOperationWithBlock() {
if let methods = message.methods {
self.methodWasUpdated(methods)
}
}
// Callbacks for managing subscriptions
case .Ready: incomingData.addOperationWithBlock() {
if let subs = message.subs {
self.ready(subs)
}
}
// Callback that fires when subscription has been completely removed
//
case .Nosub: incomingData.addOperationWithBlock() {
if let id = message.id {
self.nosub(id, error: message.error)
}
}
case .Ping: heartbeat.addOperationWithBlock() { self.pong(message) }
case .Pong: heartbeat.addOperationWithBlock() { self.server.pong = NSDate() }
case .Error: incomingData.addOperationWithBlock() {
self.didReceiveErrorMessage(DDP.Error(json: message.json))
}
default: log.error("Unhandled message: \(message.json)")
}
}
private func sendMessage(message:NSDictionary) {
if let m = message.stringValue() {
self.socket.send(m)
}
}
// Execute a method on the Meteor server
public func method(name: String, params: AnyObject?, callback: ((result:AnyObject?, error: DDP.Error?) -> ())?) -> String {
let id = getId()
let message = ["msg":"method", "method":name, "id":id] as NSMutableDictionary
if let p = params { message["params"] = p }
if let c = callback { resultCallbacks[id] = c }
outgoingData.addOperationWithBlock() { self.sendMessage(message) }
return id
}
//
// Subscribe
//
public func sub(id: String, name: String, params: [AnyObject]?, callback: (() -> ())?) -> String {
if let c = callback { subCallbacks[id] = c }
subscriptions[id] = (id, name, false)
let message = ["msg":"sub", "name":name, "id":id] as NSMutableDictionary
if let p = params { message["params"] = p }
outgoingData.addOperationWithBlock() { self.sendMessage(message) }
return id
}
// Subscribe to a Meteor collection
public func sub(name: String, params: [AnyObject]?) -> String {
let id = getId()
return sub(id, name: name, params: params, callback:nil)
}
public func sub(name:String, params: [AnyObject]?, callback: (() -> ())?) -> String {
let id = getId()
return sub(id, name: name, params: params, callback: callback)
}
// Iterates over the Dictionary of subscriptions to find a subscription by name
func findSubscription(name:String) -> (id:String, name:String, ready:Bool)? {
for subscription in subscriptions.values {
if (name == subscription.name) {
return subscription
}
}
return nil
}
//
// Unsubscribe
//
public func unsub(withName name: String) -> String? {
return unsub(withName: name, callback: nil)
}
public func unsub(withName name: String, callback:(()->())?) -> String? {
if let sub = findSubscription(name) {
unsub(withId: sub.id, callback: callback)
outgoingData.addOperationWithBlock() { self.sendMessage(["msg":"unsub", "id":sub.id]) }
return sub.id
}
return nil
}
public func unsub(withId id: String, callback: (() -> ())?) {
if let c = callback { unsubCallbacks[id] = c }
outgoingData.addOperationWithBlock() { self.sendMessage(["msg":"unsub", "id":id]) }
}
//
// Responding to server subscription messages
//
private func ready(subs: [String]) {
for id in subs {
if let callback = subCallbacks[id] {
callback() // Run the callback
subCallbacks[id] = nil // Delete the callback after running
} else { // If there is no callback, execute the method
if var sub = subscriptions[id] {
sub.ready = true
subscriptions[id] = sub
subscriptionIsReady(sub.id, subscriptionName: sub.name)
}
}
}
}
private func nosub(id: String, error: DDP.Error?) {
if let e = error where (e.isValid == true) {
print(e)
public class DDPClient: NSObject {
// included for storing login id and token
let userData = NSUserDefaults.standardUserDefaults()
public let incomingData:NSOperationQueue = {
let queue = NSOperationQueue()
queue.name = "DDP Incoming Data Queue"
queue.maxConcurrentOperationCount = 1
return queue
}()
// Calling methods on the server + their callbacks
public let outgoingData:NSOperationQueue = {
let queue = NSOperationQueue()
queue.name = "DDP Outgoing Data Queue"
// queue.maxConcurrentOperationCount = 1
return queue
}()
public let operation:NSOperationQueue = {
let queue = NSOperationQueue()
queue.name = "DDP Operation Queue"
queue.maxConcurrentOperationCount = 1
return queue
}()
public let heartbeat:NSOperationQueue = {
let queue = NSOperationQueue()
queue.name = "DDP Heartbeat Queue"
//queue.maxConcurrentOperationCount = 1
return queue
}()
public let mainQueue = NSOperationQueue.mainQueue()
private var socket:WebSocket!
private var server:(ping:NSDate?, pong:NSDate?) = (nil, nil)
var resultCallbacks:[String:(result:AnyObject?, error:DDPError?) -> ()] = [:]
var subCallbacks:[String:() -> ()] = [:]
var unsubCallbacks:[String:() -> ()] = [:]
private var url:String!
private var subscriptions = [String:(id:String, name:String, ready:Bool)]()
public var logLevel = XCGLogger.LogLevel.Debug
public var events = DDPEvents()
public var connection:(ddp:Bool, session:String?) = (false, nil)
public override init() {
super.init()
setLogLevel(logLevel)
}
public func getId() -> String {
let numbers = Set<Character>(["0","1","2","3","4","5","6","7","8","9"])
let uuid = NSUUID().UUIDString.stringByReplacingOccurrencesOfString("-", withString: "")
var id = ""
for character in uuid.characters {
if (!numbers.contains(character) && (round(Float(arc4random()) / Float(UINT32_MAX)) == 1)) {
id += String(character).lowercaseString
} else {
if let callback = unsubCallbacks[id],
let _ = subscriptions[id] {
callback()
unsubCallbacks[id] = nil
subscriptions[id] = nil
} else {
if let subscription = subscriptions[id] {
subscriptions[id] = nil
subscriptionWasRemoved(subscription.id, subscriptionName: subscription.name)
}
id += String(character)
}
}
return id
}
public func connect(url:String, callback:((session:String)->())?) {
socket = WebSocket(url)
socket.event.close = {code, reason, clean in
log.info("Web socket connection closed with code \(code). Clean: \(clean). \(reason)")
let event = self.socket.event
self.socket = WebSocket(url)
self.socket.event = event
self.ping()
}
socket.event.error = events.onWebsocketError
socket.event.open = {
self.heartbeat.addOperationWithBlock() {
if let c = callback { self.events.onConnected = c }
self.sendMessage(["msg":"connect", "version":"1", "support":["1"]])
}
}
socket.event.message = { message in
self.operation.addOperationWithBlock() {
if let text = message as? String {
do { try self.ddpMessageHandler(DDPMessage(message: text)) }
catch { log.debug("Message handling error. Raw message: \(text)")}
}
}
}
//
// public callbacks: should be overridden
//
public func subscriptionIsReady(subscriptionId:String, subscriptionName:String) {}
public func subscriptionWasRemoved(subscriptionId:String, subscriptionName:String) {}
public func documentWasAdded(collection:String, id:String, fields:NSDictionary?) {
if let added = events.onAdded { added(collection: collection, id: id, fields: fields) }
}
public func setLogLevel(logLevel:XCGLogger.LogLevel) {
log.setup(logLevel, showLogIdentifier: true, showFunctionName: true, showThreadName: true, showLogLevel: true, showFileNames: false, showLineNumbers: true, showDate: false, writeToFile: nil, fileLogLevel: .None)
}
private func ping() {
heartbeat.addOperationWithBlock() {
self.sendMessage(["msg":"ping", "id":self.getId()])
}
public func documentWasRemoved(collection:String, id:String) {
if let removed = events.onRemoved { removed(collection: collection, id: id) }
}
// Respond to a server ping
private func pong(ping: DDPMessage) {
heartbeat.addOperationWithBlock() {
self.server.ping = NSDate()
log.debug("Ping")
var response = ["msg":"pong"]
if let id = ping.id { response["id"] = id }
self.sendMessage(response)
}
}
// Parse DDP messages and dispatch to the appropriate function
func ddpMessageHandler(message: DDPMessage) throws {
public func documentWasChanged(collection:String, id:String, fields:NSDictionary?, cleared:[String]?) {
if let changed = events.onChanged { changed(collection:collection, id:id, fields:fields, cleared:cleared) }
}
log.debug("Received message: \(message.json)")
public func methodWasUpdated(methods:[String]) {
if let updated = events.onUpdated { updated(methods: methods) }
switch message.type {
case .Connected:
self.connection = (true, message.session!)
self.events.onConnected(session:message.session!)
case .Result: incomingData.addOperationWithBlock() {
if let id = message.id, // Message has id
let callback = self.resultCallbacks[id], // There is a callback registered for the message
let result = message.result {
callback(result:result, error: message.error)
self.resultCallbacks[id] = nil
} else if let id = message.id,
let callback = self.resultCallbacks[id] {
callback(result:nil, error:message.error)
self.resultCallbacks[id] = nil
}
}
// Principal callbacks for managing data
// Document was added
case .Added: incomingData.addOperationWithBlock() {
if let collection = message.collection,
let id = message.id {
self.documentWasAdded(collection, id: id, fields: message.fields)
}
}
// Document was changed
case .Changed: incomingData.addOperationWithBlock() {
if let collection = message.collection,
let id = message.id {
self.documentWasChanged(collection, id: id, fields: message.fields, cleared: message.cleared)
}
}
// Document was removed
case .Removed: incomingData.addOperationWithBlock() {
if let collection = message.collection,
let id = message.id {
self.documentWasRemoved(collection, id: id)
}
}
// Notifies you when the result of a method changes
case .Updated: incomingData.addOperationWithBlock() {
if let methods = message.methods {
self.methodWasUpdated(methods)
}
}
// Callbacks for managing subscriptions
case .Ready: incomingData.addOperationWithBlock() {
if let subs = message.subs {
self.ready(subs)
}
}
// Callback that fires when subscription has been completely removed
//
case .Nosub: incomingData.addOperationWithBlock() {
if let id = message.id {
self.nosub(id, error: message.error)
}
}
case .Ping: heartbeat.addOperationWithBlock() { self.pong(message) }
case .Pong: heartbeat.addOperationWithBlock() { self.server.pong = NSDate() }
case .Error: incomingData.addOperationWithBlock() {
self.didReceiveErrorMessage(DDPError(json: message.json))
}
default: log.error("Unhandled message: \(message.json)")
}
public func didReceiveErrorMessage(message: DDP.Error) {
if let error = events.onError { error(message: message) }
}
private func sendMessage(message:NSDictionary) {
if let m = message.stringValue() {
self.socket.send(m)
}
}
/**
Executes a method on the server. If a callback is passed, the callback is asynchronously
executed when the method has completed. The callback takes two arguments: result and error. It
the method call is successful, result contains the return value of the method, if any. If the method fails,
error contains information about the error.
- parameter name: The name of the method.
- parameter params: An object containing method arguments, if any.
- parameter callback: The closure to be executed when the save operation completes.
*/
public func method(name: String, params: AnyObject?, callback: ((result:AnyObject?, error: DDPError?) -> ())?) -> String {
let id = getId()
let message = ["msg":"method", "method":name, "id":id] as NSMutableDictionary
if let p = params { message["params"] = p }
if let c = callback { resultCallbacks[id] = c }
outgoingData.addOperationWithBlock() { self.sendMessage(message) }
return id
}
//
// Subscribe
//
public func sub(id: String, name: String, params: [AnyObject]?, callback: (() -> ())?) -> String {
if let c = callback { subCallbacks[id] = c }
subscriptions[id] = (id, name, false)
let message = ["msg":"sub", "name":name, "id":id] as NSMutableDictionary
if let p = params { message["params"] = p }
outgoingData.addOperationWithBlock() { self.sendMessage(message) }
return id
}
// Subscribe to a Meteor collection
public func sub(name: String, params: [AnyObject]?) -> String {
let id = getId()
return sub(id, name: name, params: params, callback:nil)
}
public func sub(name:String, params: [AnyObject]?, callback: (() -> ())?) -> String {
let id = getId()
return sub(id, name: name, params: params, callback: callback)
}
// Iterates over the Dictionary of subscriptions to find a subscription by name
func findSubscription(name:String) -> (id:String, name:String, ready:Bool)? {
for subscription in subscriptions.values {
if (name == subscription.name) {
return subscription
}
}
return nil
}
//
// Unsubscribe
//
public func unsub(withName name: String) -> String? {
return unsub(withName: name, callback: nil)
}
public func unsub(withName name: String, callback:(()->())?) -> String? {
if let sub = findSubscription(name) {
unsub(withId: sub.id, callback: callback)
outgoingData.addOperationWithBlock() { self.sendMessage(["msg":"unsub", "id":sub.id]) }
return sub.id
}
return nil
}
public func unsub(withId id: String, callback: (() -> ())?) {
if let c = callback { unsubCallbacks[id] = c }
outgoingData.addOperationWithBlock() { self.sendMessage(["msg":"unsub", "id":id]) }
}
//
// Responding to server subscription messages
//
private func ready(subs: [String]) {
for id in subs {
if let callback = subCallbacks[id] {
callback() // Run the callback
subCallbacks[id] = nil // Delete the callback after running
} else { // If there is no callback, execute the method
if var sub = subscriptions[id] {
sub.ready = true
subscriptions[id] = sub
subscriptionIsReady(sub.id, subscriptionName: sub.name)
}
}
}
}
private func nosub(id: String, error: DDPError?) {
if let e = error where (e.isValid == true) {
print(e)
} else {
if let callback = unsubCallbacks[id],
let _ = subscriptions[id] {
callback()
unsubCallbacks[id] = nil
subscriptions[id] = nil
} else {
if let subscription = subscriptions[id] {
subscriptions[id] = nil
subscriptionWasRemoved(subscription.id, subscriptionName: subscription.name)
}
}
}
}
//
// public callbacks: should be overridden
//
public func subscriptionIsReady(subscriptionId:String, subscriptionName:String) {}
public func subscriptionWasRemoved(subscriptionId:String, subscriptionName:String) {}
public func documentWasAdded(collection:String, id:String, fields:NSDictionary?) {
if let added = events.onAdded { added(collection: collection, id: id, fields: fields) }
}
public func documentWasRemoved(collection:String, id:String) {
if let removed = events.onRemoved { removed(collection: collection, id: id) }
}
public func documentWasChanged(collection:String, id:String, fields:NSDictionary?, cleared:[String]?) {
if let changed = events.onChanged { changed(collection:collection, id:id, fields:fields, cleared:cleared) }
}
public func methodWasUpdated(methods:[String]) {
if let updated = events.onUpdated { updated(methods: methods) }
}
public func didReceiveErrorMessage(message: DDPError) {
if let error = events.onError { error(message: message) }
}
}

View File

@ -20,27 +20,24 @@
import Foundation
extension DDP {
public struct DDPEvents {
public var onWebsocketClose: ((code:Int, reason:String, clean:Bool) -> ())?
public var onWebsocketError: (error:ErrorType) -> () = {error in log.error("websocket error \(error)")}
public var onConnected: (session:String) -> () = {session in log.info("connected with session: \(session)")}
public var onDisconnected: () -> () = {log.debug("disconnected")}
public var onFailed: () -> () = {log.error("failed")}
// Data messages
public var onAdded: ((collection:String, id:String, fields:NSDictionary?) -> ())?
public var onChanged: ((collection:String, id:String, fields:NSDictionary?, cleared:NSArray?) -> ())?
public var onRemoved: ((collection:String, id:String) -> ())?
// RPC Messages
// public var onResult: (json: NSDictionary?, callback:(result:AnyObject?, error:AnyObject?) -> ()) -> () = {json, callback in callback(result: json, error:nil) }
public var onUpdated: ((methods: [String]) -> ())?
public var onError: ((message:DDPError) -> ())?
public struct Events {
public var onWebsocketClose: ((code:Int, reason:String, clean:Bool) -> ())?
public var onWebsocketError: (error:ErrorType) -> () = {error in log.error("websocket error \(error)")}
public var onConnected: (session:String) -> () = {session in log.info("connected with session: \(session)")}
public var onDisconnected: () -> () = {log.debug("disconnected")}
public var onFailed: () -> () = {log.error("failed")}
// Data messages
public var onAdded: ((collection:String, id:String, fields:NSDictionary?) -> ())?
public var onChanged: ((collection:String, id:String, fields:NSDictionary?, cleared:NSArray?) -> ())?
public var onRemoved: ((collection:String, id:String) -> ())?
// RPC Messages
// public var onResult: (json: NSDictionary?, callback:(result:AnyObject?, error:AnyObject?) -> ()) -> () = {json, callback in callback(result: json, error:nil) }
public var onUpdated: ((methods: [String]) -> ())?
public var onError: ((message:DDP.Error) -> ())?
}
}

View File

@ -21,16 +21,16 @@
import Foundation
import CryptoSwift
let DDP_ID = "DDP_ID"
let DDP_EMAIL = "DDP_EMAIL"
let DDP_USERNAME = "DDP_USERNAME"
let DDP_TOKEN = "DDP_TOKEN"
let DDP_TOKEN_EXPIRES = "DDP_TOKEN_EXPIRES"
let DDP_LOGGED_IN = "DDP_LOGGED_IN"
private let DDP_ID = "DDP_ID"
private let DDP_EMAIL = "DDP_EMAIL"
private let DDP_USERNAME = "DDP_USERNAME"
private let DDP_TOKEN = "DDP_TOKEN"
private let DDP_TOKEN_EXPIRES = "DDP_TOKEN_EXPIRES"
private let DDP_LOGGED_IN = "DDP_LOGGED_IN"
let SWIFT_DDP_CALLBACK_DISPATCH_TIME = DISPATCH_TIME_FOREVER
private let SWIFT_DDP_CALLBACK_DISPATCH_TIME = DISPATCH_TIME_FOREVER
let syncWarning = {(name:String) -> Void in
private let syncWarning = {(name:String) -> Void in
if NSThread.isMainThread() {
print("\(name) is running synchronously on the main thread. This will block the main thread and should be run on a background thread")
}
@ -58,7 +58,7 @@ extension NSDictionary {
}
// These are implemented as an extension because they're not a part of the DDP spec
extension DDP.Client {
extension DDPClient {
public func subscribe(name:String) -> String { return sub(name, params:nil) }
@ -70,7 +70,7 @@ extension DDP.Client {
// callback is optional. If present, called with an error object as the first argument and,
// if no error, the _id as the second.
public func insert(collection: String, document: NSArray, callback: ((result:AnyObject?, error:DDP.Error?) -> ())?) -> String {
public func insert(collection: String, document: NSArray, callback: ((result:AnyObject?, error:DDPError?) -> ())?) -> String {
let arg = "/\(collection)/insert"
return self.method(arg, params: document, callback: callback)
}
@ -98,7 +98,7 @@ extension DDP.Client {
return serverResponse
}
public func update(collection: String, document: NSArray, callback: ((result:AnyObject?, error:DDP.Error?) -> ())?) -> String {
public func update(collection: String, document: NSArray, callback: ((result:AnyObject?, error:DDPError?) -> ())?) -> String {
let arg = "/\(collection)/update"
return method(arg, params: document, callback: callback)
}
@ -125,7 +125,7 @@ extension DDP.Client {
return serverResponse
}
public func remove(collection: String, document: NSArray, callback: ((result:AnyObject?, error:DDP.Error?) -> ())?) -> String {
public func remove(collection: String, document: NSArray, callback: ((result:AnyObject?, error:DDPError?) -> ())?) -> String {
let arg = "/\(collection)/remove"
return method(arg, params: document, callback: callback)
}
@ -152,7 +152,7 @@ extension DDP.Client {
return serverResponse
}
private func login(params: NSDictionary, callback: ((result: AnyObject?, error: DDP.Error?) -> ())?) {
private func login(params: NSDictionary, callback: ((result: AnyObject?, error: DDPError?) -> ())?) {
method("login", params: NSArray(arrayLiteral: params)) { result, error in
guard let e = error where (e.isValid == true) else {
@ -183,7 +183,7 @@ extension DDP.Client {
}
// Login with email and password
public func loginWithPassword(email: String, password: String, callback: ((result:AnyObject?, error:DDP.Error?) -> ())?) {
public func loginWithPassword(email: String, password: String, callback: ((result:AnyObject?, error:DDPError?) -> ())?) {
if !(loginWithToken(callback)) {
let params = ["user": ["email": email], "password":["digest": password.sha256()!, "algorithm":"sha-256"]] as NSDictionary
login(params, callback: callback)
@ -191,7 +191,7 @@ extension DDP.Client {
}
// Does the date comparison account for TimeZone?
public func loginWithToken(callback:((result: AnyObject?, error: DDP.Error?) -> ())?) -> Bool {
func loginWithToken(callback:((result: AnyObject?, error: DDPError?) -> ())?) -> Bool {
if let token = userData.stringForKey(DDP_TOKEN),
let tokenDate = userData.objectForKey(DDP_TOKEN_EXPIRES) {
if (tokenDate.compare(NSDate()) == NSComparisonResult.OrderedDescending) {
@ -203,7 +203,7 @@ extension DDP.Client {
return false
}
public func signup(params:NSDictionary, callback:((result: AnyObject?, error: DDP.Error?) -> ())?) {
public func signup(params:NSDictionary, callback:((result: AnyObject?, error: DDPError?) -> ())?) {
method("createUser", params: NSArray(arrayLiteral: params)) { result, error in
guard let e = error where (e.isValid == true) else {
@ -232,12 +232,12 @@ extension DDP.Client {
}
}
public func signupWithEmail(email: String, password: String, callback: ((result:AnyObject?, error:DDP.Error?) -> ())?) {
public func signupWithEmail(email: String, password: String, callback: ((result:AnyObject?, error:DDPError?) -> ())?) {
let params = ["email":email, "password":["digest":password.sha256()!, "algorithm":"sha-256"]]
signup(params, callback: callback)
}
public func signupWithEmail(email: String, password: String, profile: NSDictionary, callback: ((result:AnyObject?, error:DDP.Error?) -> ())?) {
public func signupWithEmail(email: String, password: String, profile: NSDictionary, callback: ((result:AnyObject?, error:DDPError?) -> ())?) {
let params = ["email":email, "password":["digest":password.sha256()!, "algorithm":"sha-256"], "profile":profile]
signup(params, callback: callback)
}
@ -255,11 +255,11 @@ extension DDP.Client {
}
}
public func logout(callback: ((result: AnyObject?, error: DDP.Error?) -> ())?) {
public func logout(callback: ((result: AnyObject?, error: DDPError?) -> ())?) {
method("logout", params: nil, callback: callback)
}
public convenience init(url: String, email: String, password: String, callback: (result:AnyObject?, error:DDP.Error?) -> ()) {
public convenience init(url: String, email: String, password: String, callback: (result:AnyObject?, error:DDPError?) -> ()) {
self.init()
connect(url) { session in
self.loginWithPassword(email, password: password, callback:callback)

View File

@ -20,33 +20,32 @@
import Foundation
extension DDP {
// Handled Message Types
public enum DDPMessageType:String {
// Handled Message Types
public enum MessageType:String {
// case Connect = "connect" // (client -> server)
case Connected = "connected"
case Failed = "failed"
case Ping = "ping"
case Pong = "pong"
// case Sub = "sub" // (client -> server)
// case Unsub = "unsub" // (client -> server)
case Nosub = "nosub"
case Added = "added"
case Changed = "changed"
case Removed = "removed"
case Ready = "ready"
case AddedBefore = "addedBefore"
case MovedBefore = "movedBefore"
// case Method = "method" // (client -> server)
case Result = "result"
case Updated = "updated"
case Error = "error"
case Unhandled = "unhandled"
}
// case Connect = "connect" // (client -> server)
case Connected = "connected"
case Failed = "failed"
case Ping = "ping"
case Pong = "pong"
// case Sub = "sub" // (client -> server)
// case Unsub = "unsub" // (client -> server)
case Nosub = "nosub"
case Added = "added"
case Changed = "changed"
case Removed = "removed"
case Ready = "ready"
case AddedBefore = "addedBefore"
case MovedBefore = "movedBefore"
// case Method = "method" // (client -> server)
case Result = "result"
case Updated = "updated"
case Error = "error"
case Unhandled = "unhandled"
}
// Method or Nosub error
// Such an Error is used to represent errors raised by the method or subscription,
// as well as an attempt to subscribe to an unknown subscription or call an unknown method.
@ -61,156 +60,158 @@ extension DDP {
// - reason: string describing the error
// - offendingMessage: if the original message parsed properly, it is included here
public struct DDPMessage {
public struct Error {
// SwiftyJSON JSON Object
public var json:NSDictionary!
public init(message:String) {
private var json:NSDictionary?
public var error:String? { return json?["error"] as? String } // Error code
public var reason:String? { return json?["reason"] as? String }
public var details:String? { return json?["details"] as? String }
public var offendingMessage:String? { return json?["offendingMessage"] as? String }
var isValid:Bool {
if let _ = error { return true }
if let _ = reason { return true }
return false
}
init(json:AnyObject?) {
self.json = json as? NSDictionary
if let JSON = message.dictionaryValue() { json = JSON }
else {
json = ["msg":"error", "reason":"SwiftDDP JSON serialization error.",
"details": "SwiftDDP JSON serialization error. JSON string was: \(message). Message will be handled as a DDP message error."]
}
}
public struct Message {
// SwiftyJSON JSON Object
public var json:NSDictionary!
public init(message:String) {
if let JSON = message.dictionaryValue() { json = JSON }
else {
json = ["msg":"error", "reason":"SwiftDDP JSON serialization error.",
"details": "SwiftDDP JSON serialization error. JSON string was: \(message). Message will be handled as a DDP message error."]
}
public init(message:[String:String]) {
json = message as NSDictionary
}
// Converts an NSDictionary to a JSON String
public static func toString(json:AnyObject) -> String? {
if let data = try? NSJSONSerialization.dataWithJSONObject(json, options: NSJSONWritingOptions(rawValue: 0)) {
let message = NSString(data: data, encoding: NSASCIIStringEncoding) as String?
return message
}
public init(message:[String:String]) {
json = message as NSDictionary
return nil
}
//
// Computed variables
//
// Returns the type of DDP message, or unhandled if it is not a DDP message
public var type:DDPMessageType {
if let msg = message,
let type = DDPMessageType(rawValue: msg) {
return type
}
// Converts an NSDictionary to a JSON String
public static func toString(json:AnyObject) -> String? {
if let data = try? NSJSONSerialization.dataWithJSONObject(json, options: NSJSONWritingOptions(rawValue: 0)) {
let message = NSString(data: data, encoding: NSASCIIStringEncoding) as String?
return message
}
return nil
}
//
// Computed variables
//
// Returns the type of DDP message, or unhandled if it is not a DDP message
public var type:DDP.MessageType {
if let msg = message,
let type = DDP.MessageType(rawValue: msg) {
return type
}
return DDP.MessageType(rawValue: "unhandled")!
}
public var isError:Bool {
if (self.type == .Error) { return true } // if message is a top level error ("msg"="error")
if let _ = self.error { return true } // if message contains an error object, as in method or nosub
return false
}
// Returns the root-level keys of the JSON object
public var keys:[String] {
return json.allKeys as! [String]
}
public func hasProperty(name:String) -> Bool {
if let property = json[name] where ((property as! NSObject) != NSNull()) {
return true
}
return false
}
public var message:String? {
get { return json["msg"] as? String }
}
public var session:String? {
get { return json["session"] as? String }
}
public var version:String? {
get { return json["version"] as? String }
}
public var support:String? {
get { return json["support"] as? String }
}
public var id:String? {
get { return json["id"] as? String }
}
public var name:String? {
get { return json["name"] as? String }
}
public var params:String? {
get { return json["params"] as? String }
}
public var error:DDP.Error? {
get { if let e = json["error"] as? NSDictionary { return DDP.Error(json:e) } else { return nil }}
}
public var collection:String? {
get { return json["collection"] as? String }
}
public var fields:NSDictionary? {
get { return json["fields"] as? NSDictionary }
}
public var cleared:[String]? {
get { return json["cleared"] as? [String] }
}
public var method:String? {
get { return json["method"] as? String }
}
public var randomSeed:String? {
get { return json["randomSeed"] as? String }
}
public var result:AnyObject? {
get { return json["result"] }
}
public var methods:[String]? {
get { return json["methods"] as? [String] }
}
public var subs:[String]? {
get { return json["subs"] as? [String] }
}
// Communication error properties
public var reason:String? {
get { return json["reason"] as? String }
}
public var offendingMessage:String? {
get { return json["offendingMessage"] as? String }
return DDPMessageType(rawValue: "unhandled")!
}
public var isError:Bool {
if (self.type == .Error) { return true } // if message is a top level error ("msg"="error")
if let _ = self.error { return true } // if message contains an error object, as in method or nosub
return false
}
// Returns the root-level keys of the JSON object
public var keys:[String] {
return json.allKeys as! [String]
}
public func hasProperty(name:String) -> Bool {
if let property = json[name] where ((property as! NSObject) != NSNull()) {
return true
}
return false
}
public var message:String? {
get { return json["msg"] as? String }
}
public var session:String? {
get { return json["session"] as? String }
}
public var version:String? {
get { return json["version"] as? String }
}
public var support:String? {
get { return json["support"] as? String }
}
public var id:String? {
get { return json["id"] as? String }
}
public var name:String? {
get { return json["name"] as? String }
}
public var params:String? {
get { return json["params"] as? String }
}
public var error:DDPError? {
get { if let e = json["error"] as? NSDictionary { return DDPError(json:e) } else { return nil }}
}
public var collection:String? {
get { return json["collection"] as? String }
}
public var fields:NSDictionary? {
get { return json["fields"] as? NSDictionary }
}
public var cleared:[String]? {
get { return json["cleared"] as? [String] }
}
public var method:String? {
get { return json["method"] as? String }
}
public var randomSeed:String? {
get { return json["randomSeed"] as? String }
}
public var result:AnyObject? {
get { return json["result"] }
}
public var methods:[String]? {
get { return json["methods"] as? [String] }
}
public var subs:[String]? {
get { return json["subs"] as? [String] }
}
// Communication error properties
public var reason:String? {
get { return json["reason"] as? String }
}
public var offendingMessage:String? {
get { return json["offendingMessage"] as? String }
}
}
public struct DDPError {
private var json:NSDictionary?
public var error:String? { return json?["error"] as? String } // Error code
public var reason:String? { return json?["reason"] as? String }
public var details:String? { return json?["details"] as? String }
public var offendingMessage:String? { return json?["offendingMessage"] as? String }
var isValid:Bool {
if let _ = error { return true }
if let _ = reason { return true }
return false
}
init(json:AnyObject?) {
self.json = json as? NSDictionary
}
}

View File

@ -22,5 +22,5 @@ import Foundation
public struct Result {
public var result:AnyObject?
public var error:DDP.Error?
public var error:DDPError?
}

View File

@ -53,7 +53,7 @@ public class Meteor {
}
}
public class Client: DDP.Client {
public class Client: DDPClient {
typealias SubscriptionCallback = () -> ()
let notifications = NSNotificationCenter.defaultCenter()
@ -93,11 +93,11 @@ public class Meteor {
}
}
public class Collection: NSObject, MeteorCollectionType {
public class MeteorCollection: NSObject, MeteorCollectionType {
internal var name:String
internal let client = Meteor.client
public let client = Meteor.client
public var name:String!
var token: dispatch_once_t = 0
// Can also set these closures to modify behavior on added, changed, removed
public var onAdded:((collection:String, id:String, fields:NSDictionary?) -> ())?
public var onChanged:((collection:String, id:String, fields:NSDictionary?, cleared:[String]?) -> ())?
@ -105,8 +105,8 @@ public class Collection: NSObject, MeteorCollectionType {
// Must use the constructor function to create the collection
public init(name:String) {
super.init()
self.name = name
super.init()
Meteor.collections[name] = self
}
@ -114,35 +114,6 @@ public class Collection: NSObject, MeteorCollectionType {
Meteor.collections[name] = nil
}
// Because this class must inherit from NSObject (an Objective-C class) to use NSNotificationCenter, and Objective-C does not
// support method overloading, these conflict with collection subclasses and have been commented out.
/*
public func insert(doc:[NSDictionary]) -> String {
return client.insert(name, doc: doc)
}
public func insert(doc:NSArray, callback:((result:AnyObject?, error:DDP.Error?) -> ())?) -> String {
return client.insert(name, doc:doc, callback:callback)
}
public func update(doc:[NSDictionary]) -> String {
return client.update(name, doc: doc)
}
public func update(doc:[NSDictionary], callback:((result:AnyObject?, error:DDP.Error?) -> ())?) -> String {
return client.update(name, doc:doc, callback:callback)
}
public func remove(doc:[NSDictionary]) -> String {
return client.remove(name, doc: doc)
}
public func remove(doc:[NSDictionary], callback:((result:AnyObject?, error:DDP.Error?) -> ())?) -> String {
return client.remove(name, doc:doc, callback:callback)
}
*/
// Override these methods to subclass Collection
public func documentWasAdded(collection:String, id:String, fields:NSDictionary?) {
if let added = onAdded { added(collection: collection, id: id, fields:fields) }

View File

@ -1,3 +1,22 @@
// Copyright (c) 2015 Peter Siegesmund <peter.siegesmund@icloud.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation

View File

@ -1,8 +1,27 @@
// Copyright (c) 2015 Peter Siegesmund <peter.siegesmund@icloud.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
public class MeteorCoreData {
static let stack:MeteorCoreDataCollectionStack = {
static let stack:MeteorCoreDataStack = {
print("Initializing MeteorCoreDataStack")
return MeteorCoreDataStackPersisted()
}()

View File

@ -1,3 +1,22 @@
// Copyright (c) 2015 Peter Siegesmund <peter.siegesmund@icloud.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import UIKit
import CoreData
@ -8,7 +27,7 @@ public protocol MeteorCoreDataCollectionDelegate {
func document(willBeUpdatedWith fields:NSDictionary?, cleared:[String]?, forObject object:NSManagedObject) -> NSManagedObject
}
public class MeteorCoreDataCollection:Collection {
public class MeteorCoreDataCollection:MeteorCollection {
private let entityName:String
private let stack:MeteorCoreDataStack

View File

@ -1,3 +1,24 @@
// Copyright (c) 2015 Peter Siegesmund <peter.siegesmund@icloud.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import Foundation
import CoreData

View File

@ -1,3 +1,22 @@
// Copyright (c) 2015 Peter Siegesmund <peter.siegesmund@icloud.com>
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
import UIKit
import CoreData

View File

@ -15,36 +15,36 @@ let url = "ws://swiftddp.meteor.com/websocket"
let user = "test@user.com"
let pass = "swiftddp"
let ready = DDP.Message(message: "{\"msg\":\"ready\", \"subs\":[\"AllStates\"]}")
let nosub = DDP.Message(message: ["msg":"nosub", "id":"AllStates"])
let ready = DDPMessage(message: "{\"msg\":\"ready\", \"subs\":[\"AllStates\"]}")
let nosub = DDPMessage(message: ["msg":"nosub", "id":"AllStates"])
let added = [DDP.Message(message: "{\"collection\" : \"test-collection\", \"id\" : \"2gAMzqvE8K8kBWK8F\", \"fields\" : {\"state\" : \"MA\", \"city\" : \"Boston\"}, \"msg\" : \"added\"}"),
DDP.Message(message:"{\"collection\" : \"test-collection\", \"id\" : \"ByuwhKPGuLru8h4TT\", \"fields\" : {\"state\" : \"MA\", \"city\" : \"Truro\"}, \"msg\" : \"added\"}"),
DDP.Message(message:"{\"collection\" : \"test-collection\", \"id\" : \"AGX6vyxCJtjqdxbFH\", \"fields\" : {\"state\" : \"TX\", \"city\" : \"Austin\"}, \"msg\" : \"added\"}")]
let added = [DDPMessage(message: "{\"collection\" : \"test-collection\", \"id\" : \"2gAMzqvE8K8kBWK8F\", \"fields\" : {\"state\" : \"MA\", \"city\" : \"Boston\"}, \"msg\" : \"added\"}"),
DDPMessage(message:"{\"collection\" : \"test-collection\", \"id\" : \"ByuwhKPGuLru8h4TT\", \"fields\" : {\"state\" : \"MA\", \"city\" : \"Truro\"}, \"msg\" : \"added\"}"),
DDPMessage(message:"{\"collection\" : \"test-collection\", \"id\" : \"AGX6vyxCJtjqdxbFH\", \"fields\" : {\"state\" : \"TX\", \"city\" : \"Austin\"}, \"msg\" : \"added\"}")]
let removed = [DDP.Message(message: ["msg" : "removed", "id" : "2gAMzqvE8K8kBWK8F","collection" : "test-collection"]),
DDP.Message(message: ["msg" : "removed", "id" : "ByuwhKPGuLru8h4TT", "collection" : "test-collection"]),
DDP.Message(message:["msg" : "removed", "id" : "AGX6vyxCJtjqdxbFH", "collection" : "test-collection"])]
let removed = [DDPMessage(message: ["msg" : "removed", "id" : "2gAMzqvE8K8kBWK8F","collection" : "test-collection"]),
DDPMessage(message: ["msg" : "removed", "id" : "ByuwhKPGuLru8h4TT", "collection" : "test-collection"]),
DDPMessage(message:["msg" : "removed", "id" : "AGX6vyxCJtjqdxbFH", "collection" : "test-collection"])]
let changed = [DDP.Message(message: "{\"collection\" : \"test-collection\", \"id\" : \"2gAMzqvE8K8kBWK8F\",\"cleared\" : [\"city\"], \"fields\" : {\"state\" : \"MA\", \"city\" : \"Amherst\"}, \"msg\" : \"changed\"}"),
DDP.Message(message:"{\"collection\" : \"test-collection\", \"id\" : \"ByuwhKPGuLru8h4TT\", \"fields\" : {\"state\" : \"MA\", \"city\" : \"Cambridge\"}, \"msg\" : \"changed\"}"),
DDP.Message(message:"{\"collection\" : \"test-collection\", \"id\" : \"AGX6vyxCJtjqdxbFH\", \"fields\" : {\"state\" : \"TX\", \"city\" : \"Houston\"}, \"msg\" : \"changed\"}")]
let changed = [DDPMessage(message: "{\"collection\" : \"test-collection\", \"id\" : \"2gAMzqvE8K8kBWK8F\",\"cleared\" : [\"city\"], \"fields\" : {\"state\" : \"MA\", \"city\" : \"Amherst\"}, \"msg\" : \"changed\"}"),
DDPMessage(message:"{\"collection\" : \"test-collection\", \"id\" : \"ByuwhKPGuLru8h4TT\", \"fields\" : {\"state\" : \"MA\", \"city\" : \"Cambridge\"}, \"msg\" : \"changed\"}"),
DDPMessage(message:"{\"collection\" : \"test-collection\", \"id\" : \"AGX6vyxCJtjqdxbFH\", \"fields\" : {\"state\" : \"TX\", \"city\" : \"Houston\"}, \"msg\" : \"changed\"}")]
let userAddedWithPassword = DDP.Message(message: "{\"collection\" : \"users\", \"id\" : \"123456abcdefg\", \"fields\" : {\"roles\" : [\"admin\"], \"emails\" : [{\"address\" : \"test@user.com\", \"verified\" : false}], \"username\" : \"test\"}, \"msg\" : \"added\"}")
let userAddedWithPassword = DDPMessage(message: "{\"collection\" : \"users\", \"id\" : \"123456abcdefg\", \"fields\" : {\"roles\" : [\"admin\"], \"emails\" : [{\"address\" : \"test@user.com\", \"verified\" : false}], \"username\" : \"test\"}, \"msg\" : \"added\"}")
let addedRealm = [DDP.Message(message: "{\"collection\" : \"Cities\", \"id\" : \"2gAMzqvE8K8kBWK8F\", \"fields\" : {\"state\" : \"MA\", \"city\" : \"Boston\"}, \"msg\" : \"added\"}"),
DDP.Message(message:"{\"collection\" : \"Cities\", \"id\" : \"ByuwhKPGuLru8h4TT\", \"fields\" : {\"state\" : \"MA\", \"city\" : \"Truro\"}, \"msg\" : \"added\"}"),
DDP.Message(message:"{\"collection\" : \"Cities\", \"id\" : \"AGX6vyxCJtjqdxbFH\", \"fields\" : {\"state\" : \"TX\", \"city\" : \"Austin\"}, \"msg\" : \"added\"}")]
let addedRealm = [DDPMessage(message: "{\"collection\" : \"Cities\", \"id\" : \"2gAMzqvE8K8kBWK8F\", \"fields\" : {\"state\" : \"MA\", \"city\" : \"Boston\"}, \"msg\" : \"added\"}"),
DDPMessage(message:"{\"collection\" : \"Cities\", \"id\" : \"ByuwhKPGuLru8h4TT\", \"fields\" : {\"state\" : \"MA\", \"city\" : \"Truro\"}, \"msg\" : \"added\"}"),
DDPMessage(message:"{\"collection\" : \"Cities\", \"id\" : \"AGX6vyxCJtjqdxbFH\", \"fields\" : {\"state\" : \"TX\", \"city\" : \"Austin\"}, \"msg\" : \"added\"}")]
let removedRealm = [DDP.Message(message: ["msg" : "removed", "id" : "2gAMzqvE8K8kBWK8F","collection" : "Cities"]),
DDP.Message(message: ["msg" : "removed", "id" : "ByuwhKPGuLru8h4TT", "collection" : "Cities"]),
DDP.Message(message:["msg" : "removed", "id" : "AGX6vyxCJtjqdxbFH", "collection" : "Cities"])]
let removedRealm = [DDPMessage(message: ["msg" : "removed", "id" : "2gAMzqvE8K8kBWK8F","collection" : "Cities"]),
DDPMessage(message: ["msg" : "removed", "id" : "ByuwhKPGuLru8h4TT", "collection" : "Cities"]),
DDPMessage(message:["msg" : "removed", "id" : "AGX6vyxCJtjqdxbFH", "collection" : "Cities"])]
let changedRealm = [DDP.Message(message: "{\"collection\" : \"Cities\", \"id\" : \"2gAMzqvE8K8kBWK8F\",\"cleared\" : [\"city\"], \"fields\" : {\"state\" : \"MA\", \"city\" : \"Amherst\"}, \"msg\" : \"changed\"}"),
DDP.Message(message:"{\"collection\" : \"Cities\", \"id\" : \"ByuwhKPGuLru8h4TT\", \"fields\" : {\"state\" : \"MA\", \"city\" : \"Cambridge\"}, \"msg\" : \"changed\"}"),
DDP.Message(message:"{\"collection\" : \"Cities\", \"id\" : \"AGX6vyxCJtjqdxbFH\", \"fields\" : {\"state\" : \"TX\", \"city\" : \"Houston\"}, \"msg\" : \"changed\"}")]
let changedRealm = [DDPMessage(message: "{\"collection\" : \"Cities\", \"id\" : \"2gAMzqvE8K8kBWK8F\",\"cleared\" : [\"city\"], \"fields\" : {\"state\" : \"MA\", \"city\" : \"Amherst\"}, \"msg\" : \"changed\"}"),
DDPMessage(message:"{\"collection\" : \"Cities\", \"id\" : \"ByuwhKPGuLru8h4TT\", \"fields\" : {\"state\" : \"MA\", \"city\" : \"Cambridge\"}, \"msg\" : \"changed\"}"),
DDPMessage(message:"{\"collection\" : \"Cities\", \"id\" : \"AGX6vyxCJtjqdxbFH\", \"fields\" : {\"state\" : \"TX\", \"city\" : \"Houston\"}, \"msg\" : \"changed\"}")]

View File

@ -14,7 +14,7 @@ class DDPMessageTest:QuickSpec {
describe ("DDPMessage") {
it ("can be created from a Dictionary") {
let message = DDP.Message(message: ["msg":"test", "id":"test100"])
let message = DDPMessage(message: ["msg":"test", "id":"test100"])
expect(message.hasProperty("msg")).to(beTrue())
expect(message.hasProperty("id")).to(beTruthy())
expect(message.id!).to(equal("test100"))
@ -22,7 +22,7 @@ class DDPMessageTest:QuickSpec {
}
it ("can be created from a String") {
let message = DDP.Message(message: "{\"msg\":\"test\", \"id\":\"test100\"}")
let message = DDPMessage(message: "{\"msg\":\"test\", \"id\":\"test100\"}")
expect(message.hasProperty("msg")).to(beTruthy())
expect(message.hasProperty("id")).to(beTruthy())
expect(message.id!).to(equal("test100"))
@ -31,18 +31,18 @@ class DDPMessageTest:QuickSpec {
it ("handles malformed json without crashing") {
let message = DDP.Message(message: "{\"msg\":\"test\", \"id\"test100\"}")
let message = DDPMessage(message: "{\"msg\":\"test\", \"id\"test100\"}")
expect(message.isError).to(beTrue())
expect(message.reason!).to(equal("SwiftDDP JSON serialization error."))
}
it ("Sends malformed json to the error handler callback") {
var error:DDP.Error!
var error:DDPError!
let client = DDP.Client()
let client = DDPClient()
client.events.onError = {e in error = e }
let message = DDP.Message(message: "{\"msg\":\"test\", \"id\"test100\"}")
let message = DDPMessage(message: "{\"msg\":\"test\", \"id\"test100\"}")
try! client.ddpMessageHandler(message)
expect(message.isError).to(beTrue())
@ -58,7 +58,7 @@ class DDPMessageTest:QuickSpec {
describe ("DDPMessageHandler routing") {
it ("can handle an 'added' message"){
let client = DDP.Client()
let client = DDPClient()
client.events.onAdded = {collection, id, fields in
expect(collection).to(equal("test-collection"))
expect(id).to(equal("2gAMzqvE8K8kBWK8F"))
@ -69,7 +69,7 @@ class DDPMessageTest:QuickSpec {
}
it ("can handle a 'removed' message") {
let client = DDP.Client()
let client = DDPClient()
client.events.onRemoved = {collection, id in
expect(collection).to(equal("test-collection"))
expect(id).to(equal("2gAMzqvE8K8kBWK8F"))
@ -80,16 +80,16 @@ class DDPMessageTest:QuickSpec {
it ("can handle a result message that returns a value") {
var value:String!
var r:AnyObject?
var e:DDP.Error?
var e:DDPError?
let client = DDP.Client()
client.resultCallbacks["1"] = {(result:AnyObject?, error:DDP.Error?) -> () in
let client = DDPClient()
client.resultCallbacks["1"] = {(result:AnyObject?, error:DDPError?) -> () in
value = result as! String
r = result
e = error
}
try! client.ddpMessageHandler(DDP.Message(message: ["id":"1", "msg":"result", "result":"test123"]))
try! client.ddpMessageHandler(DDPMessage(message: ["id":"1", "msg":"result", "result":"test123"]))
expect(r).toEventuallyNot(beNil())
expect(e).to(beNil())
expect(value).toEventually(equal("test123"))
@ -99,16 +99,16 @@ class DDPMessageTest:QuickSpec {
it ("can handle a result message that does not return a value") {
var value:String!
var r:AnyObject?
var e:DDP.Error?
var e:DDPError?
let client = DDP.Client()
client.resultCallbacks["1"] = {(result:AnyObject?, error:DDP.Error?) -> ()
let client = DDPClient()
client.resultCallbacks["1"] = {(result:AnyObject?, error:DDPError?) -> ()
in if let v = result as? String { value = v }
r = result
e = error
}
try! client.ddpMessageHandler(DDP.Message(message: ["id":"1", "msg":"result"]))
try! client.ddpMessageHandler(DDPMessage(message: ["id":"1", "msg":"result"]))
expect(value).to(beNil())
expect(r).to(beNil())
expect(e).to(beNil())
@ -125,7 +125,7 @@ class DDPServerTests:QuickSpec {
it ("can connect to a DDP server"){
var testSession:String?
let client = DDP.Client()
let client = DDPClient()
client.connect(url) { session in testSession = session }
expect(client.connection.ddp).toEventually(beTrue(), timeout:5)
expect(client.connection.session).toEventually(equal(testSession), timeout:5)
@ -145,7 +145,7 @@ class DDPServerTests:QuickSpec {
var testResult:NSDictionary!
var testSession:String!
let client = DDP.Client()
let client = DDPClient()
client.connect(url) { session in
testSession = session
client.loginWithPassword(user, password: pass) { result, e in
@ -167,7 +167,7 @@ class DDPServerTests:QuickSpec {
it ("can add and remove a document on the server"){
var added = [NSDictionary]()
var removed = [String]()
let client = DDP.Client()
let client = DDPClient()
let _id = client.getId()
client.events.onAdded = { collection, id, fields in if ((collection == "test-collection2") && (_id == id)) { added.append(fields!) } }
@ -200,7 +200,7 @@ class DDPServerTests:QuickSpec {
it ("can update a document in a collection") {
var added = [NSDictionary]()
var updated = [NSDictionary]()
let client = DDP.Client()
let client = DDPClient()
let _id = client.getId()
@ -236,7 +236,7 @@ class DDPServerTests:QuickSpec {
it ("can execute a method on the server that returns a value") {
var response:String!
let client = DDP.Client()
let client = DDPClient()
client.connect(url) { session in
client.loginWithPassword(user, password: pass) { result, error in
@ -268,7 +268,7 @@ class DDPServerTests:QuickSpec {
it ("can subscribe and unsubscribe to a collection") {
var added = [String]()
var removed = [String]()
let client = DDP.Client()
let client = DDPClient()
client.connect(url) {session in
client.loginWithPassword(user, password: pass) {result, error in
client.events.onAdded = {collection, id, fields in added.append(id) }

View File

@ -8,7 +8,7 @@ class MeteorTest: QuickSpec {
override func spec() {
let client = Meteor.client
let collection = Collection(name: "test-collection")
let collection = MeteorCollection(name: "test-collection")
describe("Collections") {
/*