SwiftDDP

Meteor 服务器的客户端,使用 Swift 编写

版本 0.4.1

许可

MIT

Version License Platform

安装

使用 CocoaPods。将以下行添加到您的 Podfile

pod "SwiftDDP", "~> 0.4.1"

使用 Swift Package Manager。将以下行添加到您的 Package.swift 中的 dependencies

        .package(url: "https://github.com/y-ich/SwiftDDP.git", .branch("master")),

快速开始

设置基本配置选项

import SwiftDDP

Meteor.client.allowSelfSignedSSL = true     // Connect to a server that uses a self signed ssl certificate
Meteor.client.logLevel = .Info              // Options are: .Verbose, .Debug, .Info, .Warning, .Error, .Severe, .None

连接到 Meteor 服务器

// Meteor.connect will automatically connect and will sign in using
// a stored login token if the client was previously signed in.

Meteor.connect("wss://todos.meteor.com/websocket") {
    // do something after the client connects
}

使用密码登录和注销

使用电子邮件和密码登录。

Meteor.loginWithPassword("user@swiftddp.com", password: "********") { result, error in
    // do something after login
}

使用用户名和密码登录。

Meteor.loginWithUsername("swiftddp", password: "********") { result, error in
    // do something after login
}

注销。

Meteor.logout() { result, error in
    // do something after logout
}

当用户登录和注销以及连接失败事件期间,客户端还会发布通知。

// Notification name (a string global variable)
DDP_USER_DID_LOGIN
DDP_USER_DID_LOGOUT
//Websocket/DDP connection failure events
DDP_WEBSOCKET_CLOSE
DDP_WEBSOCKET_ERROR
DDP_DISCONNECTED
DDP_FAILED

// Example
NSNotificationCenter.defaultCenter().addObserver(self, selector: "userDidLogin", name: DDP_USER_DID_LOGIN, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "userDidLogout", name: DDP_USER_DID_LOGOUT, object: nil)

func userDidLogin() {
    print("The user just signed in!")
}

func userDidLogout() {
    print("The user just signed out!")
}

订阅集合的子集

Meteor.subscribe("todos")

Meteor.subscribe("todos") {
    // Do something when the todos subscription is ready
}

Meteor.subscribe("todos", [1,2,3,4]) {
    // Do something when the todos subscription is ready
}

更改订阅的参数并使用取消订阅管理您的订阅

// Suppose you want to subscribe to a list of all cities and towns near a specific major city

// Subscribe to cities near Boston
let id1 = Meteor.subscribe("cities", ["lat": 42.358056 ,"lon": -71.063611]) {
    // You are now subscribed to cities associated with the coordinates 42.358056, -71.063611
    // id1 contains a key that allows you to cancel the subscription associated with 
    // the parameters ["lat": 42.358056 ,"lon": -71.063611]
}

// Subscribe to cities near Paris
let id2 = Meteor.subscribe("cities", ["lat": 48.8567, "lon": 2.3508]){
    // You are now subscribed to cities associated with the coordinates 48.8567, 2.3508
    // id2 contains a key that allows you to cancel the subscription associated with 
    // the parameters ["lat": 48.8567 ,"lon": 2.3508]
}

// Subscribe to cities near New York
let id3 = Meteor.subscribe("cities", ["lat": 40.7127, "lon": -74.0059]){
    // You are now subscribed to cities associated with the coordinates 40.7127, -74.0059
    // id3 contains a key that allows you to cancel the subscription associated with 
    // the parameters ["lat": 40.7127 ,"lon": -74.0059]
}

// When these subscriptions have completed, the collection associated with "cities" will now contain all
// documents returned from the three subscriptions

Meteor.unsubscribe(withId: id2) 
// Your collection will now contain cities near Boston and New York, but not Paris
Meteor.unsubscribe("cities")    
// You are now unsubscribed to all subscriptions associated with the publication "cities"

在服务器上调用方法

Meteor.call("foo", [1, 2, 3, 4]) { result, error in
    // Do something with the method result
}

当向服务器方法传递参数时,参数对象必须可使用 NSJSONSerialization 序列化

简单的内存持久化

SwiftDDP 包含一个名为 MeteorCollection 的类,它提供简单的、临时的字典支持的持久化。MeteorCollection 存储从 MeteorDocument 子类化的对象。创建一个集合非常简单,只需

class List: MeteorDocument {

    var collection:String = "lists"
    var name:String?
    var userId:String?

}

let lists = MeteorCollection<List>(name: "lists")   // As with Meteorjs, the name is the name of the server-side collection  
Meteor.subscribe("lists")

对于客户端的插入、更新和删除

let list = List(id: Meteor.client.getId(), fields: ["name": "foo"])

// Insert the object on both the client and server.
lists.insert(list)

// Update the object on both the client and server
list.name = "bar"
lists.update(list)

// Remove the object on both the client and server
lists.remove(list)

对于每个操作,操作都在客户端执行,如果服务器返回错误,则回滚。

CLI 提示

如果您制作命令行工具,您需要在适当的 DDP 设置后在主线程中调用函数 dispatchMain。否则,您将遇到死锁。

Meteor.connect("wss://todos.meteor.com/websocket") {
    // do something after the client connects
}
dispatchMain()

示例:创建基于数组的自定义集合

以下模式可用于创建由任何数据存储支持的自定义集合

在本示例中,我们将创建一个简单的集合来保存联系人列表。我们要做的第一件事是创建一个对象来表示联系人。此对象具有四个属性和一个名为 update 的方法,该方法将 fields NSDictionary 映射到结构体的属性。当创建对象和执行更新时,将调用Update。Meteor 将始终传输一个 id 来标识应该添加、更新或删除的对象,因此表示 Meteor 文档的对象必须始终具有 id 字段。在这里,我们坚持 MongoDB 的命名约定,将我们的 id 命名为 _id

struct Contact {

    var _id:String?
    var name:String?
    var phone:String?
    var email:String?

    init(id:String, fields:NSDictionary?) {
        self._id = id
        update(fields)
    }

    mutating func update(fields:NSDictionary?) {

        if let name = fields?.valueForKey("name") as? String {
            self.name = name
        }

        if let phone = fields?.valueForKey("phone") as? String {
            self.phone = phone
        }

        if let email = fields?.valueForKey("email") as? String {
            self.email = email
        }
    }
}

接下来,我们将创建集合类,该类将保存我们的联系人并提供逻辑来响应服务器端对文档和订阅集的更改。SwiftDDP 包含一个名为 AbstractCollection 的抽象类,可用于构建自定义集合。子类化 AbstractCollection 允许您覆盖在响应服务器上的事件时调用的三个方法:documentWasAddeddocumentWasChangeddocumentWasRemoved

class UserCollection: AbstractCollection {

    var contacts = [Contact]()

    // Include any logic that needs to occur when a document is added to the collection on the server
    override public func documentWasAdded(collection:String, id:String, fields:NSDictionary?) {
        let user = User(id, fields)
        users.append(user)
    }

    // Include any logic that needs to occur when a document is changed on the server
    override public func documentWasChanged(collection:String, id:String, fields:NSDictionary?, cleared:[String]?) {
        if let index = contacts.indexOf({ contact in return contact._id == id }) {
            contact = contacts[index]
            contact.update(fields)
            contacts[index] = contact
        }
    }

  // Include any logic that needs to occur when a document is removed on the server
  override public func documentWasRemoved(collection:String, id:String) {
    if let index = contacts.indexOf({ contact in return contact._id == id }) {
        contacts[index] = nil
        }
    }
}

到目前为止,我们能够处理在服务器上添加、更改或删除的文档。但是 UserCollection 类仍然缺乏更改本地数据存储和服务器的能力。我们将改变这一点。在 UserCollection 类中,创建一个名为 insert 的方法。

class UserCollection: AbstractCollection {
    /*
    override public func documentWasAdded ...
    override public func documentWasChanged ...
    override public func documentWasRemoved ...
    */

    public func insert(contact: Contact) {

        // (1) save the document to the contacts array
        contacts[contacts._id] = contact

        // (2) now try to insert the document on the server
        client.insert(self.name, document: [contacts.fields()]) { result, error in

            // (3) However, if the server returns an error, reverse the action on the client by
            //     removing the document from the contacts collection
            if error != nil {
                self.contacts[contact._id] = nil
                log.error("\(error!)")
            }

        }

    }
}

此方法的关键部分是

创建 update 和 remove 方法也很容易,并且遵循与 insert 相同的模式。有关此处显示的模式的更广泛示例,请查看 MeteorCollection.swift。MeteorCollection 是一个适用于简单应用程序的内存集合实现。

更新日志

0.4.0

0.3.2

0.3.1

0.3.0

版本 0.3.0 包含重大更改

0.2.2.1

0.2.1

0.2.0

贡献

欢迎提交拉取请求、功能请求和反馈。如果您在生产应用程序中使用 SwiftDDP,请告诉我们。