QT iOS Google Cloud Messaging 崩溃问题

3

我正在尝试在使用Qt编写的跨平台项目中实现远程推送通知。

我发现谷歌为Android和iOS都提供了Google Cloud Messaging服务。(https://developers.google.com/cloud-messaging/)

我需要使用一些谷歌代码并为每个平台编写一些本地代码(Android使用Java,iOS使用Objective C)。

我已经阅读了Android部分并在我的项目中实现了它,它运行良好。

现在我尝试实现iOS部分(https://developers.google.com/cloud-messaging/ios/start)。

有一些问题。

首先,我无法在Qt项目中使用CocoaPods,因此必须手动链接必要的库。

这是我的Qt项目文件:

ios {

ios_google_plist.files = $$PWD/ios/GoogleService-Info.plist

QMAKE_BUNDLE_DATA += ios_google_plist
QMAKE_INFO_PLIST = $$PWD/ios/Info.plist

LIBS += \
    ./ios/libs/libGGLInstanceIDLib.a \
    ./ios/libs/libGGLCloudMessaging.a \
    ./ios/libs/libGGLCore.a \
    ./ios/libs/libGcmLib.a \
    ./ios/libs/libProtocolBuffers.a \
    ./ios/libs/libGTMSessionFetcher_core.a \
    ./ios/libs/libGTMSessionFetcher_full.a \
    ./ios/libs/libGSDK_Overload.a \
    ./ios/libs/libGTM_AddressBook.a \
    ./ios/libs/libGTM_core.a \
    ./ios/libs/libGTM_DebugUtils.a \
    ./ios/libs/libGTM_GTMURLBuilder.a \
    ./ios/libs/libGTM_iPhone.a \
    ./ios/libs/libGTM_KVO.a \
    ./ios/libs/libGTM_NSDictionary+URLArguments.a \
    ./ios/libs/libGTM_NSScannerJSON.a \
    ./ios/libs/libGTM_NSStringHTML.a \
    ./ios/libs/libGTM_NSStringXML.a \
    ./ios/libs/libGTM_Regex.a \
    ./ios/libs/libGTM_RoundedRectPath.a \
    ./ios/libs/libGTM_StringEncoding.a \
    ./ios/libs/libGTM_SystemVersion.a \
    ./ios/libs/libGTM_UIFont+LineHeight.a \
    ./ios/libs/libGTMStackTrace.a

  }

iOS应用程序构建成功,但在接收令牌时崩溃了。
以下是日志:
2015-08-21 16:59:50.735 MyCustomApp[475:96862] Attempted to configure [Identity, Analytics, AdMob, SignIn, AppInvite, CloudMessaging].
2015-08-21 16:59:50.735 MyCustomApp[475:96862] Successfully configured [].
2015-08-21 16:59:50.736 MyCustomApp[475:96862] Failed to configure [].
2015-08-21 16:59:50.736 MyCustomApp[475:96862] Subspecs not present, so not configured [Identity, Analytics, AdMob, SignIn, AppInvite, CloudMessaging].
2015-08-21 16:59:50.762 MyCustomApp[475:96862] didRegisterForRemoteNotificationsWithDeviceToken begin
2015-08-21 16:59:50.767 MyCustomApp[475:96862] didRegisterForRemoteNotificationsWithDeviceToken end
2015-08-21 16:59:50.787 MyCustomApp[475:96862] -[GGLInstanceIDTokenManager fetchTokenWithAuthorizedEntity:scope:keyPair:options:handler:]: unrecognized selector sent to instance 0x170623c20
2015-08-21 16:59:50.788 MyCustomApp[475:96862] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[GGLInstanceIDTokenManager fetchTokenWithAuthorizedEntity:scope:keyPair:options:handler:]: unrecognized selector sent to instance 0x170623c20'
*** First throw call stack:
()
libc++abi.dylib: terminating with uncaught exception of type  NSException
program received signal 6,thread:17a5e;qaddr:199071490;00:0000000000000000;01:0000000000000000;02:0000000000000000;03:37364e7001000000;04:fddd569401000000;05:60d2ca6f01000000;06:6e00000000000000;07:800f000000000000;08:0000000800000000;09:0000000400000000;0a:0002000000000000;0b:0000000000000000;0c:0000000000000000;0d:0000000000000000;0e:0200000000000000;0f:0000000000000000;10:4801000000000000;11:0000000000000000;12:0000000000000000;13:0600000000000000;14:1013079901000000;15:e89c059901000000;16:b0c7217001000000;17:a09ae60101000000;18:0000000000000000;19:7a54608901000000;1a:0000000000000000;1b:0000000000000000;1c:c0b7049901000000;1d:c0d1ca6f01000000;1e:28d2589501000000;1f:a0d1ca6f01000000;20:70f24e9501000000;21:00000000;metype:5;mecount:2;medata:10003;medata:6;

正如您所看到的,registrationHandler回调函数未被调用,并且在调用registrationHandler回调函数之前发生了崩溃(在google库中的某个位置)。
我使用了谷歌的示例代码并进行了一些更改,例如将AppDelegate接口重命名为QIOSApplicationDelegate(否则Objective-C函数不会被调用)。
以下是Objective-C代码:

AppDelegateGoogle.h

#include <QtCore>
void registerDeviceForNotification_iOS_CPP(void);

并且

AppDelegateGoogle.mm

#import "Google/CloudMessaging.h"
#import <UIKit/UIKit.h>

#import "AppDelegateGoogle.h"


@interface QIOSApplicationDelegate : UIResponder <UIApplicationDelegate, GGLInstanceIDDelegate>    
@property(nonatomic, strong) UIWindow *window;
@property(nonatomic, readonly, strong) NSString *registrationKey;
@property(nonatomic, readonly, strong) NSString *messageKey;
@property(nonatomic, readonly, strong) NSString *gcmSenderID;
@property(nonatomic, readonly, strong) NSDictionary *registrationOptions;

@property(nonatomic, strong) void (^registrationHandler)
    (NSString *registrationToken, NSError *error);
@property(nonatomic, assign) BOOL connectedToGCM;
@property(nonatomic, strong) NSString* registrationToken;
@property(nonatomic, assign) BOOL subscribedToTopic;   
@end

QIOSApplicationDelegate* pApp;

NSString *const SubscriptionTopic = @"/topics/global";

void registerDeviceForNotification_iOS_CPP(void)
{
    [pApp registerDeviceForNotification_iOS];
}

@implementation QIOSApplicationDelegate

// [START register_for_remote_notifications]
- (void)registerDeviceForNotification_iOS {
    // [START_EXCLUDE]
    _registrationKey = @"onRegistrationCompleted";
    _messageKey = @"onMessageReceived";
    // Configure the Google context: parses the GoogleService-Info.plist, and initializes
    // the services that have entries in the file
    NSError* configureError;
    [[GGLContext sharedInstance] configureWithError:&configureError];
    if (configureError != nil) {
      NSLog(@"Error configuring the Google context: %@", configureError);
    }
    _gcmSenderID = [[[GGLContext sharedInstance] configuration] gcmSenderID];
    // [END_EXCLUDE]
    // Register for remote notifications
    UIUserNotificationType allNotificationTypes =
        (UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge);
    UIUserNotificationSettings *settings =
        [UIUserNotificationSettings settingsForTypes:allNotificationTypes categories:nil];
    [[UIApplication sharedApplication] registerUserNotificationSettings:settings];
    [[UIApplication sharedApplication] registerForRemoteNotifications];
    // [END register_for_remote_notifications]
    // [START start_gcm_service]
    [[GCMService sharedInstance] startWithConfig:[GCMConfig defaultConfig]];
    // [END start_gcm_service]
    __weak QIOSApplicationDelegate* weakSelf = self;
    // Handler for registration token request
    _registrationHandler = ^(NSString *registrationToken, NSError *error){
            NSLog(@"_registrationHandler called");
            if (registrationToken != nil) {
              NSLog(@"Registration Token: %@", registrationToken);
              std::string strToken([registrationToken UTF8String]);
              Device::sendRegistrationToServer(strToken);
            } else {
              NSLog(@"Registration to GCM failed with error: %@", error.localizedDescription);
            }
    };
}

// [START register_for_remote_notifications]
- (BOOL)application:(UIApplication *)application
      didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

  pApp = self;
  return YES;
}

- (void)subscribeToTopic {
  // If the app has a registration token and is connected to GCM, proceed to subscribe to the
  // topic
  if (_registrationToken && _connectedToGCM) {
    [[GCMPubSub sharedInstance] subscribeWithToken:_registrationToken
                                             topic:SubscriptionTopic
                                           options:nil
                                           handler:^(NSError *error) {
                                             if (error) {
                                               // Treat the "already subscribed" error more gently
                                               if (error.code == 3001) {
                                                 NSLog(@"Already subscribed to %@",
                                                       SubscriptionTopic);
                                               } else {
                                                 NSLog(@"Subscription failed: %@",
                                                       error.localizedDescription);
                                               }
                                             } else {
                                               self.subscribedToTopic = true;
                                               NSLog(@"Subscribed to %@", SubscriptionTopic);
                                             }
                                           }];
  }
}

// [START connect_gcm_service]
- (void)applicationDidBecomeActive:(UIApplication *)application {
  // Connect to the GCM server to receive non-APNS notifications
  [[GCMService sharedInstance] connectWithHandler:^(NSError *error) {
    if (error) {
      NSLog(@"Could not connect to GCM: %@", error.localizedDescription);
    } else {
      _connectedToGCM = true;
      NSLog(@"Connected to GCM");
      // [START_EXCLUDE]
      [self subscribeToTopic];
      // [END_EXCLUDE]
    }
  }];
}
// [END connect_gcm_service]

// [START disconnect_gcm_service]
- (void)applicationDidEnterBackground:(UIApplication *)application {
  [[GCMService sharedInstance] disconnect];
  // [START_EXCLUDE]
  _connectedToGCM = NO;
  // [END_EXCLUDE]
}
// [END disconnect_gcm_service]

// [START receive_apns_token]
- (void)application:(UIApplication *)application
    didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
// [END receive_apns_token]
                NSLog(@"didRegisterForRemoteNotificationsWithDeviceToken begin");
  // [START get_gcm_reg_token]
  // Start the GGLInstanceID shared instance with the default config and request a registration
  // token to enable reception of notifications
  [[GGLInstanceID sharedInstance] startWithConfig:[GGLInstanceIDConfig defaultConfig]];
  _registrationOptions = @{kGGLInstanceIDRegisterAPNSOption:deviceToken,
                           kGGLInstanceIDAPNSServerTypeSandboxOption:@YES};
  [[GGLInstanceID sharedInstance] tokenWithAuthorizedEntity:_gcmSenderID
                                                      scope:kGGLInstanceIDScopeGCM
                                                    options:_registrationOptions
                                                    handler:_registrationHandler];
  // [END get_gcm_reg_token]
                NSLog(@"didRegisterForRemoteNotificationsWithDeviceToken end");
}

// [START receive_apns_token_error]
- (void)application:(UIApplication *)application
    didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
  NSLog(@"Registration for remote notification failed with error: %@", error.localizedDescription);
// [END receive_apns_token_error]
  NSDictionary *userInfo = @{@"error" :error.localizedDescription};
  [[NSNotificationCenter defaultCenter] postNotificationName:_registrationKey
                                                      object:nil
                                                    userInfo:userInfo];
}

// [START ack_message_reception]
- (void)application:(UIApplication *)application
    didReceiveRemoteNotification:(NSDictionary *)userInfo {
  NSLog(@"Notification received: %@", userInfo);
  // This works only if the app started the GCM service
  [[GCMService sharedInstance] appDidReceiveMessage:userInfo];
  // Handle the received message
  // [START_EXCLUDE]
  [[NSNotificationCenter defaultCenter] postNotificationName:_messageKey
                                                      object:nil
                                                    userInfo:userInfo];
  // [END_EXCLUDE]
}

- (void)application:(UIApplication *)application
    didReceiveRemoteNotification:(NSDictionary *)userInfo
    fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))handler {
  NSLog(@"Notification received: %@", userInfo);
  // This works only if the app started the GCM service
  [[GCMService sharedInstance] appDidReceiveMessage:userInfo];
  // Handle the received message
  // Invoke the completion handler passing the appropriate UIBackgroundFetchResult value
  // [START_EXCLUDE]
  [[NSNotificationCenter defaultCenter] postNotificationName:_messageKey
                                                      object:nil
                                                    userInfo:userInfo];
  handler(UIBackgroundFetchResultNoData);
  // [END_EXCLUDE]
}
// [END ack_message_reception]

// [START on_token_refresh]
- (void)onTokenRefresh {
  // A rotation of the registration tokens is happening, so the app needs to request a new token.
  NSLog(@"The GCM registration token needs to be changed.");
  [[GGLInstanceID sharedInstance] tokenWithAuthorizedEntity:_gcmSenderID
                                                      scope:kGGLInstanceIDScopeGCM
                                                    options:_registrationOptions
                                                    handler:_registrationHandler];
}
// [END on_token_refresh]

@end

有人能帮忙吗?

谢谢,Evgeny

======EDIT1 BEGIN======

当我尝试使用 -ObjC 链接选项时,出现链接错误:

在以下位置有重复符号 _OBJC_METACLASS_$_QIOSApplicationDelegate: /Users/sha/build-MyCustomApp-iphoneos_clang_Qt_5_5_0_for_iOS-Release/MyCustomApp.build/Debug-iphoneos/MyCustomApp.build/Objects-normal/arm64/AppDelegateGoogle.o /Users/sha/Qt/5.5/ios/plugins/platforms/libqios_debug.a(qiosapplicationdelegate.o) 在以下位置有重复符号 _OBJC_CLASS_$_QIOSApplicationDelegate: /Users/sha/build-MyCustomApp-iphoneos_clang_Qt_5_5_0_for_iOS-Release/MyCustomApp.build/Debug-iphoneos/MyCustomApp.build/Objects-normal/arm64/AppDelegateGoogle.o /Users/sha/Qt/5.5/ios/plugins/platforms/libqios_debug.a(qiosapplicationdelegate.o) ld: 2个重复符号,架构为 arm64 clang: 错误:链接器命令失败,退出码为 1(使用 -v 查看调用)

根据我的理解,有两个应用程序代理:

  • 一个是Qt框架提供的
  • 第二个是谷歌Lib提供的。

因此链接器有2个重复符号。

也许我需要将这两个应用程序代理合并成一个,但我不知道如何做到这一点......

======编辑1结束======


嗨,Pieter。 感谢您分享您的代码和问题,这对我也有所帮助。但是我想知道您的构建器是如何知道“QIOSApplicationDelegate”是什么的。我正在使用Qt Creator,看起来与此类型没有关联。 - Vi.
2个回答

0

你是否从CocoaPods配置的其他项目中获取了GCM库?尝试使用从gstatics下载的库,你可以在cocoapods网站上找到链接 -> GoogleCloudMessaging -> 查看Podspecs。

无论如何,请检查以下行:

 2015-08-21 16:59:50.735 MyCustomApp[475:96862] Attempted to configure [Identity, Analytics, AdMob, SignIn, AppInvite, CloudMessaging].
2015-08-21 16:59:50.735 MyCustomApp[475:96862] Successfully configured []. 

CloudMessaging 没有正确配置,您无法接收到令牌。

如果您解决了问题,请告诉我:)


0

该方法定义在一个类别中,该类别似乎未与您的应用程序链接。 您应该将 -ObjC 链接器标志添加到XCode选项中,这将强制它将类别添加到静态库中。

以下是更多信息 https://developer.apple.com/library/mac/qa/qa1490/_index.html

如上面的链接所说

此标志会导致链接器加载库中定义Objective-C类或类别的每个对象文件。虽然此选项通常会导致可执行文件变大(由于加载到应用程序中的附加对象代码),但它将允许成功创建包含现有类别的Objective-C静态库。


你好,感谢回答。 我已经尝试使用这个选项进行链接,但是出现了链接错误。 请查看问题末尾的EDIT1。 - Future Keys

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接