在Firebase中构建用户数据库模型

时间:2016-05-10 21:00:24

标签: swift xcode firebase firebase-realtime-database backend

所以我已经为此完成了所有实际的应用程序。我只需要设置后端。我认为Firebase是最好的解决方案,因为Parse不再是一件事。我想要的是:

拥有个人资料的用户 - 这些个人资料可以由添加的朋友查看,但仅由实际的个人资料所有者编辑(书写)。

所以我阅读了Firebase文档,但仍无法弄清楚如何执行此操作。他们只有一个Swift应用程序示例,它没有做任何类似的事情,一个Obj C twitter一个,甚至不会构建。他们所有的文档仍然有Swift的println,这让我觉得它不经常更新。

有没有人有这方面的好例子/教程?我一直在努力寻找东西,但没有任何东西与我想要的东西相似。我更关注如何为每个用户设置数据库并访问它,而不是实际使用Swift中的Firebase。

1 个答案:

答案 0 :(得分:6)

正如我在对您的问题的评论中写的那样,这个答案是基于我们在真实社交应用Impether中使用Swift + Firebase所做的事情。

数据结构

假设您要为单个用户存储以下信息:

  • 电子邮件
  • 用户名
  • 命名
  • 关注者 - 关注特定用户的人数
  • 关注 - 特定用户关注的人数
  • avatar_url - 他们的头像的网址
  • bio - 一些额外的文字

由于在Firebase中所有内容都存储了JSON对象,因此您可以将上述结构存储在节点下,其中包含users/$userId之类的路径,其中$userId是Firebase用户UID,如果您使用,则为每个注册用户创建简单的电子邮件/密码Firebase授权。

Firebase电子邮件/密码授权在其文档中有所描述: https://www.firebase.com/docs/ios/guide/user-auth.html https://www.firebase.com/docs/ios/guide/login/password.html

请注意,Obj-C和Swift片段都有。我发现Firebase文档非常棒,因为它在我构建应用程序时帮助了我很多。

出于此答案的目的,我们假设我们的用户名为jack,Firebase用户UID等于jack_uid(实际上这将是Firebase生成的字符串)。

然后,该用户的示例数据将存储在路径users/jack_uid下,并且可以如下所示:

{
  "email" : "jack@example.com",
  "username" : "jack",
  "name" : "Jack",
  "followers" : 8,
  "following" : 11,
  "avatar_url" : "http://yourstoragesystem.com/avatars/jack.jpg",
  "bio" : "Blogger, YouTuber",
}

Firebase电子邮件/密码授权非常有效,但说实话,如果用户想要登录应用程序,那么使用他的用户名比他给他的电子邮件要好得多他注册了他的账户。

为此,我们决定存储从用户名到用户ID的映射。这个想法是,如果用户在登录表单中输入他的用户名和密码,我们使用该映射来检索他的用户ID,然后我们尝试使用他的用户ID和提供的密码签名。

映射可以存储在路径username_to_uid下,如下所示:

{ 
   "sample_username_1": "firebase_generated_userid_1",
   "sample_username_2": "firebase_generated_userid_2",
   ...
   "jack": "jack_uid",
   "sample_username_123": "firebase_generated_userid_123"
}

然后创建个人资料可能看起来像这样,只要新帐户注册成功就完成了(此代码段与我们在制作中使用的确切代码非常接近):

func createProfile(uid: String, email: String,
                   username: String, avatarUrl: String,
                   successBlock: () -> Void, errorBlock: () -> Void) {

    //path to user data node
    let userDataPath = "/users/\(uid)"

    //path to user's username to uid mapping
    let usernameToUidDataPath = "/username_to_uid/\(username)"

    //you want to have JSON object representing user data
    //and we do use our User Swift structures to do that
    //but you can just create a raw JSON object here.
    //name, avatarUrl, bio, followers and following are
    //initialized with default values
    let user = User(uid: uid, username: username, name: "",
                    avatarUrl: avatarUrl, bio: "",
                    followers: 0, following: 0)

    //this produces a JSON object from User instance
    var userData = user.serialize()
    //we add email to JSON data, because we don't store
    //it directly in our objects
    userData["email"] = email

    //we use fanoutObject to update both user data
    //and username to uid mapping at the same time
    //this is very convinient, because either both
    //write are successful or in case of any error,
    //nothing is written, so you avoid inconsistencies
    //in you database. You can read more about that technique
    //here: https://www.firebase.com/blog/2015-10-07-how-to-keep-your-data-consistent.html

    var fanoutObject = [String:AnyObject]()
    fanoutObject[userDataPath] = userData
    fanoutObject[usernameToUidDataPath] = uid

    let ref = Firebase(url: "https://YOUR-FIREBASE-URL.firebaseio.com/images")
    ref.updateChildValues(fanoutObject, withCompletionBlock: {
        err, snap in
        if err == nil {
            //call success call back if there were no errors
            successBlock()
        } else {
            //handle error here
            errorBlock()
        }
    })
}

除此之外,您可能希望为每个用户存储他的关注者列表以及他所遵循的单独用户列表。这可以通过将用户ID存储在followers/jack_uid之类的路径来完成,例如它可能如下所示:

{
   "firebase_generated_userid_4": true,
   "firebase_generated_userid_14": true
}

这是我们在应用中存储多组值的方式。它非常方便,因为真的是用户更新它并检查是否存在某些值。

为了计算关注者的数量,我们直接将此计数器放入用户的数据中。这使得阅读计数器非常有效。但是,更新此计数器需要使用事务性写入,这个想法与我在这里的答案几乎完全相同:Upvote/Downvote system within Swift via Firebase

读/写权限

您的问题的一部分是如何处理您存储的数据的权限。好消息是Firebase在这里非常好。如果您转到Firebase信息中心,则会出现一个名为Security&Rules的标签,您可以在此处控制数据的权限。

Firebase规则的优点在于它们是声明性的,这使得它们非常易于使用和维护。但是,在纯JSON中编写规则并不是最好的主意,因为当你想将一些原子规则组合成一个更大的规则或​​者你的应用程序简单增长并且你存储的数据越来越多时,它很难控制它们在您的Firebase数据库中。幸运的是,Firebase团队编写了Bolt,这种语言可以让您轻松编写所需的所有规则。

首先,我建议您阅读有关安全性的Firebase文档,尤其是对节点的权限如何影响其子级的权限。然后,您可以在这里看看Bolt:

https://www.firebase.com/docs/security/bolt/guide.html https://www.firebase.com/blog/2015-11-09-introducing-the-bolt-compiler.html https://github.com/firebase/bolt/blob/master/docs/guide.md

例如,我们使用规则来管理与此类似的用户数据:

//global helpers
isCurrentUser(userId) {
    auth != null && auth.uid == userId;
}

isLogged() {
    auth != null;
}

//custom types, you can extend them
//if you want to
type UserId extends String;
type Username extends String;
type AvatarUrl extends String;
type Email extends String;

type User {
    avatar_url: AvatarUrl,
    bio: String,
    email: Email,
    followers: Number,
    following: Number,
    name: String,
    username: Username,
}

//user data rules
path /users/{$userId} is User {
    write() { isCurrentUser($userId) }
    read() { isLogged() }
}

//user's followers rules
//rules for users a particular
//user follows are similar
path /followers/{$userId} {
    read() { isLogged() }
}

path /followers/{$userId}/{$followerId} is Boolean {
    create() { isCurrentUser($followerId) && this == true }
    delete() { isCurrentUser($followerId) }
}

//username to uid rules
path /username_to_uid {
    read() { true }
}

path /username_to_uid/{$username} is UserId {
    create() { isCurrentUser(this) }
}

底线是您使用Bolt编写所需的规则,然后使用Bolt编译器将它们编译为JSON,然后使用命令行工具将它们部署到Firebase中,或者将它们粘贴到仪表板中,但是命令行更有效率。一个很好的附加功能是,您可以使用信息中心的Simulator标签中的工具来测试您的规则。

摘要

对我来说,Firebase是实现所需系统的绝佳工具。但是,我建议从简单的功能开始,并首先了解如何使用Firebase。使用像Instagram这样的功能实现社交应用程序是一个相当大的挑战,特别是如果你想要做到这一点:)将所有功能放在那里非常诱人并且Firebase使它相对容易,但是我建议在这里耐心等待。

此外,请花时间投资写作工具。例如,我们有两个独立的Firebase数据库,一个用于生产,另一个用于测试,如果您想要有效地编写单元和UI测试,这非常重要。

另外,我建议从头开始构建权限规则。稍后添加它们可能很诱人,但也非常压倒性。

最后但并非最不重要的,请关注Firebase博客。他们定期发布,你可以及时了解他们的最新功能和更新 - 这就是我学习如何使用扇出技术使用并发写入的方法。