Correct check for Game Center availability

Apple’s example code for Game Center does something strange — in fact, we can simply go out and call those things bugs. Keep reading to see what I’m talking about.

Here’s the code.

        // check for presence of GKLocalPlayer API
        Class gcClass = (NSClassFromString(@"GKLocalPlayer"));
        
        // check if the device is running iOS 4.1 or later
        NSString *reqSysVer = @"4.1";
        NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
        BOOL osVersionSupported = ([currSysVer compare:reqSysVer options:NSNumericSearch] != NSOrderedAscending);

        return (gcClass && osVersionSupported);

Well, that’s incorrect and doesn’t count on the fact that Game Center is unavailable on iPhone 3G despite iOS 4.1 and 4.2 existing there. If your goal wasn’t to avoid crashes caused by Game Center classes not existing, but to check if Game Center really is available, you need to check if you are running on iPhone 3G. Update April 6th 2011: I have missed a few lines in the documentation that say you will receive GKErrorNotSupported when authenticating local player, if Game Center is not available. However, a library I’m working on needs this sort of check to return availability prior to authenticating player, to determine best achievement engine. You may need this as well, so I’m keeping this up. An excellent way to do this was posted on iOS Developer Tips and uses sysctlbyname() C function found in BSD operating systems such as OS X or iOS to query the kernel for value of hw.machine string.

The above method proposes adding a category to UIDevice that adds -(NSString*)machine. While this would be a most excellent way to do this, it is not an appropriate way to handle stuff in a C++ library; it should not add categories or anything like that unless it is absolutely necessary, so I just patched the function that checks availability of Game Center directly.

Compared to the iOS Developer Tips’ solution, I also added a check whether or not sysctlbyname() fails. This should prevent any problems in case Apple decides to remove support for hw.machine (although I see no reason for them doing that).

We don’t need to check for iPhone, iPod Touch (no iOS4) or iPod Touch 2G (Game Center available).

Let’s take a look at the code:

    bool GameCenterAchievementsService::isGameCenterAvailable()
    {
        // check for presence of GKLocalPlayer API
        Class gcClass = (NSClassFromString(@"GKLocalPlayer"));
        
        // check if the device is running iOS 4.1 or later
        NSString *reqSysVer = @"4.1";
        NSString *currSysVer = [[UIDevice currentDevice] systemVersion];
        BOOL osVersionSupported = ([currSysVer compare:reqSysVer options:NSNumericSearch] != NSOrderedAscending);
        
        // device must not be an iphone 3g
        bool validDevice = true;
        {
            size_t size;
            if(sysctlbyname("hw.machine", NULL, &size, NULL, 0)!=-1)
            {
                char*name = (char*)malloc(size);
                if(sysctlbyname("hw.machine", name, &size, NULL, 0)!=-1)
                {
                    if (!strcmp(name, "iPhone1,1") || !strcmp(name, "iPhone1,2") || !strcmp(name, "iPod1,1")) 
                    {
                        validDevice = false;
                    }
                }
                free(name);
            }
        }
        
        return (gcClass && osVersionSupported && validDevice);
    }

Note that this is Objective-C++, but is trivial to patch for pure Objective-C. Report bugs in comments section below!

-=-
Tip me with Bitcoin to: 1ASA9q5VQUxPZvit8X2AP4JYzPcSDk7dFV or using ChangeTip (button below)


Leave a Reply

Your email address will not be published. Required fields are marked *