- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我正在测试基于 firechat-ios 的代码例子。我添加了 FirebaseSimpleLogin调用 loginToFacebookAppWithId 并设置它,以便一个 View Controller 执行登录,然后转换到另一个包含聊天逻辑的 View Controller :
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];
}
}];
}
}];
以下是 Firebase 规则:
{
"rules": {
".read": "auth != null",
".write": "auth != null"
}
}
问题是,大约有 10% 的时间,聊天消息的 UITableView 是空白的,我在日志中看不到任何聊天消息条目。我试过调整 observeEventType 的顺序,将其放在 loginToFacebookAppWithId 调用之前和之后。
我想知道是否存在竞争条件,消息可能在我调用 observeEventType 之前到达。我已经检查了 observeEventType 的返回值,即使没有消息到达,我也得到了 1 的 FirebaseHandle。我还将firechat ios自带的firebase框架升级到了https://cdn.firebase.com/ObjC/Firebase.framework-LATEST.zip它仍然失败。
我认为可能是连接断开了,但在我通过身份验证并看到它们出现在 firebase 服务器上后,我可以使用 childByAutoId 发布消息。我从来没有收到任何消息。
我想知道它是否试图在我通过身份验证之前的短暂时刻向我发送消息,但由于我没有读取权限而失败。有没有办法将事件观察延迟到我进去之后?
我已经尝试了所有我能想到的方法,但我无法让它可靠地工作。
------------ 更新------------
如果我手动输入凭据,我似乎每次都能登录。我目前正在检查以前是否成功登录:
[FBSession openActiveSessionWithAllowLoginUI:false]
确定我是否在上次启动应用程序时成功登录。如果失败,我会转到 FirebaseSimpleLogin 的 View Controller 。但如果它有效,我会在当前 View Controller 中调用 FirebaseSimpleLogin 并等待它在后台成功。
我在模拟器中运行,所以我尝试在以下位置删除首选项 plist:
~/Library/Application Support/iPhone Simulator/7.0.3/Applications/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/Library/Preferences/com.xxxxxxxxxx.plist
并重新启动,这迫使我重新进行身份验证。然后我尝试输入我的凭据并成功登录 25 次。
所以我认为这个问题要么与我在使用 FirebaseSimpleLogin 之前尝试使用 Facebook 登录有关,要么与使用上次启动时的凭据登录有关(没有显示登录对话框)。我仍在努力缩小罪魁祸首。
------------ 更新 2 ------------
我只是想补充一点,经过进一步测试,我发现调用:
[FBSession openActiveSessionWithAllowLoginUI:false]
对 FirebaseSimpleLogin 没有影响。如果我完全跳过该调用并在那里简单地替换 true 或 false,我可以重现该问题。问题原来是竞争条件,请参阅下面的回答。
最佳答案
我终于弄清楚发生了什么,这是由于我对 UIViewController 消息回调和 CFRunLoop 的错误假设.
我问题中的代码示例是从我的真实代码中提炼出来的,以删除无关的调用,但事实证明我删除的部分实际上是罪魁祸首。我写了一个函数来登录并等待直到现场成功或失败(而不是稍后在一个 block 中接收响应)通过使用运行循环:
-(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];
}
}];
}
这是通过以下方式调用的:
NSError *error;
FAUser *user;
bool success = [self loginUsingFacebookReturningError:&error andUser:&user];
loginUsingFacebookReturningError 的工作方式是,它调用 loginUsingFacebookWithCompletionBlock 像往常一样触发 loginToFacebookAppWithId 和 checkAuthStatusWithBlock 消息,但随后我开始运行循环。运行循环允许在后台进行处理,即使主线程在 CFRunLoopRun() 上暂停,直到完成 block 调用 CFRunLoopStop()。
我没有意识到运行循环会继续在后台处理应用程序的消息。所以当我认为程序流在 viewDidLoad 中停止时,它实际上继续并调用了 viewWillAppear,它是我调用 observeEventType 的地方(因为我假设程序到达那里时身份验证将完成)。
这创建了一个 race condition在 Facebook 和 Firebase 进行身份验证期间,程序附加了 observeEventType 回调。 90% 的情况下,身份验证在调用 observeEventType 之前已完成,但 10% 的情况下存在滞后或其他网络延迟,并且过早调用了 observeEventType。
我通过将 FirebaseSimpleLogin 代码移动到 Storyboard 中它自己的 View Controller ,并使用完成 block 启动到下一个 View Controller 的 segue,安装了 observeEventType 回调来解决这个问题。
总结一下:解决方案是调用 FirebaseSimpleLogin 的身份验证,然后在它完成且完成 block 完成后,调用 observeEventType。否则,Firebase 的规则将拒绝您查看仅对经过身份验证的用户可见的数据的请求(这是正确的)。
这是最终代码,未经测试但方法有效:
// 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];
}];
}
关于ios - 使用 Facebook 的 FirebaseSimpleLogin 后,Firebase observeEventType 没有触发?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22073318/
我正在使用 $firebaseSimpleLogin 通过电子邮件/密码登录 Firebase。 当我使用电子邮件/密码登录时,它运行良好,我可以看到 session key 被自动保存为 cooki
我正在努力寻找为我们使用 firebase 的应用设计安全性的最佳方法 基本问题 我们希望用户的数据是安全的。我们不希望恶意代理能够访问 Firebase 数据库上其他用户的私有(private)数据
我试图理解为什么这段代码执行alert() 3次: $scope.logout = function () { $rootScope.auth.$logout(); $rootScop
我有一个有角度的应用程序,它可以轻松地与 Firebase 的 Facebook 网络版 SimpleLogin 服务集成,但是,它依赖于打开一个弹出窗口来实现这一点。 虽然 iOS 7.0.3 似乎
自 https://www.firebase.com/blog/2014-10-03-major-updates-to-firebase-user-auth.html ,在使用 myRef authU
我正在测试基于 firechat-ios 的代码例子。我添加了 FirebaseSimpleLogin调用 loginToFacebookAppWithId 并设置它,以便一个 View Contro
昨天,我发现了 Firebase 并开始使用它。我部署了 Firebase 提供的聊天应用程序的编辑版本(我刚刚更改了 CSS),一切都很顺利,直到我添加了 Twitter 日志记录选项。即使我关注了
我是一名优秀的程序员,十分优秀!