首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >火柴observeEventType没有在FirebaseSimpleLogin使用Facebook后开火吗?

火柴observeEventType没有在FirebaseSimpleLogin使用Facebook后开火吗?
EN

Stack Overflow用户
提问于 2014-02-27 15:42:19
回答 1查看 1.5K关注 0票数 3

我正在测试基于firechat-ios示例的代码。我添加了FirebaseSimpleLogin调用loginToFacebookAppWithId并设置了它,以便一个视图控制器执行登录,然后转换到另一个包含聊天逻辑的视图控制器:

代码语言:javascript
复制
self.firebase = [[Firebase alloc] initWithUrl:@"https://xxxxx.firebaseio.com/"];

[self observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) {
    NSLog(@"%@", snapshot.value);

    // Add the chat message to the array.
    [self.chat addObject:snapshot.value];
    // Reload the table view so the new message will show up.
    [self.tableView reloadData];

    [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:([self.tableView numberOfRowsInSection:0] - 1) inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}];

FirebaseSimpleLogin *authClient = [[FirebaseSimpleLogin alloc] initWithRef:self.firebase];

[authClient loginToFacebookAppWithId:kFacebookAppID permissions:@[@"email"]
            audience:ACFacebookAudienceOnlyMe
            withCompletionBlock:^(NSError *error, FAUser *user) {

    if (error != nil) {
        // There was an error logging in
        NSLog(@"facebook error");
    } else {
        // We have a logged in facebook user
        NSLog(@"facebook logged in");

        [authClient checkAuthStatusWithBlock:^(NSError* error, FAUser* user) {
            if (error != nil) {
    // Oh no! There was an error performing the check
    NSLog(@"auth error");
            } else if (user == nil) {
    // No user is logged in
    NSLog(@"auth not logged in");
            } else {
    // There is a logged in user
    NSLog(@"auth logged in");

    // segue to the chat view controller
    [self performSegueWithIdentifier:@"segueToViewController" sender:self];
            }
        }];
    }
}];

以下是防火规则:

代码语言:javascript
复制
{
    "rules": {
        ".read": "auth != null",
        ".write": "auth != null"
    }
}

问题是,大约有10%的时间,聊天消息的UITableView是空的,我在日志中没有看到任何聊天消息条目。我试着使用observeEventType的命令,在loginToFacebookAppWithId调用之前和之后放置它。

我在想,在我打电话给observeEventType之前,是否存在消息到达的竞赛条件。我检查了observeEventType的返回值,即使没有消息到达,也得到了1的FirebaseHandle。我还将firechat附带的防火墙框架升级到https://cdn.firebase.com/ObjC/Firebase.framework-LATEST.zip,但仍然失败。

我认为连接可能中断了,但是我能够在通过身份验证后用childByAutoId发布消息,并看到它们出现在firebase服务器上。我只是从来没有收到过任何信息。

我不知道它是否试图在我通过身份验证之前的短时间内向我发送消息,但由于我没有读取权限,所以失败了。有什么办法把事件的观察推迟到我加入之后吗?

我已经试过了我能想到的一切,但我无法使它可靠地工作。

如果手动输入凭据,我似乎每次都可以登录。目前,我正在检查先前成功登录的以下内容:

代码语言:javascript
复制
[FBSession openActiveSessionWithAllowLoginUI:false]

以确定我是否在上一次启动应用程序时成功登录。如果失败,我将转到FirebaseSimpleLogin的视图控制器。但是,如果它有效,我将在当前视图控制器中调用FirebaseSimpleLogin,并等待它在后台成功运行。

我在模拟器中运行,所以我尝试在以下位置删除preferences plist:

代码语言:javascript
复制
~/Library/Application Support/iPhone Simulator/7.0.3/Applications/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/Library/Preferences/com.xxxxxxxxxx.plist

重新启动,这迫使我重新确认身份。然后,我试着输入我的凭证,登录25次,没有问题。

因此,我认为问题要么与在使用FirebaseSimpleLogin之前尝试使用Facebook登录有关,要么与使用上一次启动时的凭据登录有关(而不打开登录对话框)。我还在努力缩小罪魁祸首。

我只想补充一句,经过进一步的测试,我发现调用:

代码语言:javascript
复制
[FBSession openActiveSessionWithAllowLoginUI:false]

对FirebaseSimpleLogin无影响。如果我完全跳过这个调用,在那里简单地替换为true或false,我可以复制这个问题。这个问题被证明是种族问题,见下面的答案。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2014-02-28 18:51:37

我终于明白了发生了什么,这是由于我对UIViewController消息回调和CFRunLoop的错误假设。

我问题中的代码示例是从我真正的代码中提炼出来的,目的是删除无关的调用,但事实证明,我删除的部分实际上是罪魁祸首。通过使用run循环,我编写了一个函数来登录并等待成功或失败(而不是稍后在块中接收响应):

代码语言:javascript
复制
-(bool)loginUsingFacebookReturningError:(NSError**)error andUser:(FAUser**)user
{
    __block NSError *errorTemp;
    __block FAUser  *userTemp;

    [self loginUsingFacebookWithCompletionBlock:^(NSError *error, FAUser *user) {
        errorTemp = error;
        userTemp = user;

        CFRunLoopStop(CFRunLoopGetCurrent());
    }];

    CFRunLoopRun(); // needs a timeout or way for the user to cancel but I haven't implemented it yet

    if(error) *error = errorTemp;
    if(user) *user = userTemp;

    return !errorTemp && userTemp;
}

-(void)loginUsingFacebookWithCompletionBlock:(void (^)(NSError* error, FAUser* user))block
{
    FirebaseSimpleLogin *authClient = [[FirebaseSimpleLogin alloc] initWithRef:self.firebase];

    [authClient loginToFacebookAppWithId:kFacebookAppID permissions:@[@"email"]
        audience:ACFacebookAudienceOnlyMe
         withCompletionBlock:^(NSError *error, FAUser *user) {

             if (error != nil) {
                 // There was an error logging in
                 NSLog(@"facebook error");

                 block(error, nil);
             } else {
                 // We have a logged in facebook user
                 NSLog(@"facebook logged in");

                 [authClient checkAuthStatusWithBlock:block];
             }
         }];
}

这样做的原因是:

代码语言:javascript
复制
NSError *error;
FAUser  *user;
bool    success = [self loginUsingFacebookReturningError:&error andUser:&user];

loginUsingFacebookReturningError的工作方式是,它调用loginUsingFacebookWithCompletionBlock,它像往常一样触发loginToFacebookAppWithId和checkAuthStatusWithBlock消息,但随后我启动了一个run循环。run循环允许在后台进行处理,即使主线程在CFRunLoopRun()上暂停,直到完成块调用CFRunLoopStop()。

我没有意识到的是,运行循环继续在后台处理应用程序的消息。因此,当我认为程序流在viewDidLoad中停止时,它实际上一直在继续,并调用了viewWillAppear,这就是我调用observeEventType的地方(因为我假设程序到达时,身份验证已经完成)。

这创建了一个种族条件,该程序在Facebook和Firebase进行身份验证期间附加了observeEventType回调。90%的时间,身份验证在observeEventType被调用之前已经完成,但有10%的时间存在滞后或其他网络延迟,而observeEventType被过早地调用。

我通过将FirebaseSimpleLogin代码移动到情节提要中的自己的视图控制器,并使用完成块来启动下一个安装了observeEventType回调的视图控制器的segue来解决这个问题。

总之,是这样的:解决方案是调用FirebaseSimpleLogin的身份验证,然后在它完成并完成块之后,调用observeEventType。否则,Firebase的规则将拒绝您的请求,以查看只有经过身份验证的用户才能看到的数据(这是正确的)。

下面是最后的代码,未经测试,但该方法有效:

代码语言:javascript
复制
// only global for illustration purposes, should really go in a singleton or AppDelegate, or be passed through the segue to the next view controller
Firebase    *gFirebase;

// LoginViewController (root view controller in storyboard)
- (void)viewDidLoad
{
    [super viewDidLoad];

    gFirebase = [[Firebase alloc] initWithUrl:@"https://xxxxx.firebaseio.com/"];

    FirebaseSimpleLogin *authClient = [[FirebaseSimpleLogin alloc] initWithRef:gFirebase];

    [authClient loginToFacebookAppWithId:kFacebookAppID permissions:@[@"email"]
                audience:ACFacebookAudienceOnlyMe
                withCompletionBlock:^(NSError *error, FAUser *user) {

        if (error != nil) {
            // There was an error logging in
            NSLog(@"facebook error");
        } else {
            // We have a logged in facebook user
            NSLog(@"facebook logged in");

            [authClient checkAuthStatusWithBlock:^(NSError* error, FAUser* user) {
                if (error != nil) {
                    // Oh no! There was an error performing the check
                    NSLog(@"auth error");
                } else if (user == nil) {
                    // No user is logged in
                    NSLog(@"auth not logged in");
                } else {
                    // There is a logged in user
                    NSLog(@"auth logged in");

                    // segue to the chat view controller
                    [self performSegueWithIdentifier:@"segueToViewController" sender:self];
                }
            }];
        }
    }];
}

// ViewController (destination of segueToViewController)
- (void)viewDidLoad
{
    [super viewDidLoad];

    [gFirebase observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *snapshot) {
        NSLog(@"%@", snapshot.value);

        // Add the chat message to the array.
        [self.chat addObject:snapshot.value];
        // Reload the table view so the new message will show up.
        [self.tableView reloadData];

        [self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:([self.tableView numberOfRowsInSection:0] - 1) inSection:0] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
    }];
}
票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/22073318

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档