mixpanel/mixpanel-iphone

Apple Rejection - TMS-90338: Non-public API usage (WKInterfaceDevice)

Closed this issue · 23 comments

Hello, we got a problem with the store that refused our application since Monday the 3rd, so 2 days ago.
We use cordova-mixpanel-plugin, which is basically a copy/paste of mixpanel-iphone.
I was wondering if someone using mixpanel-iphone had the same problem ?

The error message

ITMS-90338: Non-public API usage - The app contains or inherits from non-public classes in MyWonderfulApp: WKInterfaceDevice . If method names in your source code match the private Apple APIs listed above, altering your method names will help prevent this app from being flagged in future submissions.

From the doc WKInterfaceDevice

Do not subclass or create instances of this class (WKInterfaceDevice) yourself. Always call the current() class method to get the shared device object.

When I look at the code in Mixpanel.m, I see

Class WKInterfaceDeviceClass = NSClassFromString(@"WKInterfaceDevice");

I know nothing about Objective-C, but I was wondering if this line is good ?
On the other hand, on MixpanelWatchProperties, we can see that line:

WKInterfaceDevice *device = [WKInterfaceDevice currentDevice];

Maybe it make sense to you,

Regards from a JS dev.

Please help :)

Integration Method: Manual
Library Version: 3.4.5
Platform: iOS
Language: Objective-C

Original issue samzilverberg/cordova-mixpanel-plugin#104

Hi @cyrilgandon , thanks for raising it. We have released a new version with a fix for this issue.
https://github.com/mixpanel/mixpanel-iphone/releases/tag/v3.4.6

This didn't seem to fix the issue for us - anyone else have success?

It also failed again for us uploading to TestFlight with the update. Odd as the only references left call the currentDevice method which is supposed to be public/legit, I'd think.

that's really weird, @KevinKelchen and @dorthwein, have you got the same error as @cyrilgandon ?

Hi @zihejia! 👋

Yes. Here is the text of the emails we've been receiving when trying to upload to TestFlight (edited to remove sensitive information):

Dear Developer,

We identified one or more issues with a recent delivery for your app, "<our_app_name>" <our_app_major_minor_version_number> (<our_app_full_version_number>). Please correct the following issues, then upload again.

ITMS-90338: Non-public API usage - The app contains or inherits from non-public classes in <our_app_name>: WKInterfaceDevice . If method names in your source code match the private Apple APIs listed above, altering your method names will help prevent this app from being flagged in future submissions. In addition, note that one or more of the above APIs may be located in a static library that was included with your app. If so, they must be removed. If you think this message was sent in error and that you have only used Apple-published APIs in accordance with the guidelines, send the app's Apple ID, along with detailed information about why you believe the above APIs were incorrectly flagged, to appreview@apple.com. For further information, visit the Technical Support Information at http://developer.apple.com/support/technical/

Best regards,

The App Store Team

I did confirm that removing mixpanel from our app fixes the issue. Like @cyrilgandon, we use the cordova-mixpanel-plugin, which was just updated to pull in the v3.4.6 changes that were implemented so timely. 🙂

Thank you!

Kevin

Same here, we're waiting a fix :)

The code doesn't violate private APIs, this is apple's detector screwing up.

I just sent a detailed email to Apple as suggested in their email message. Hoping it goes somewhere.

Here was my message with a few minor edits:

App Name: <app_name>
Apple ID: <apple_id>

Hello,

Our app can no longer be uploaded to TestFlight due to the reason mentioned in the title of this email and the forwarded email below.

Our app uses the cordova-mixpanel-plugin, and there’s an issue out there for this problem where others are running into the issue as well. This Cordova plugin contains a copy of the official mixpanel-iphone library, and there’s likewise an issue out there for this problem where others are running into the issue as well.

Removing the library from our app results in TestFlight uploads becoming successful again. However, we would like to continue using this library.

With the latest version of this library, all of the usages of WKInterfaceDevice in the library as well as our app are contained within a single source file: MixpanelWatchProperties.m . Here is the entire contents of the file which contains all usages of WKInterfaceDevice:

//
//  MixpanelWatchProperties.m
//  Mixpanel
//
//  Created by Peter Chien on 10/14/16.
//  Copyright © 2016 Mixpanel. All rights reserved.
//
 
#import "MixpanelWatchProperties.h"
#import <WatchKit/WatchKit.h>
 
@implementation MixpanelWatchProperties
 
+ (NSDictionary *)collectDeviceProperties {
    NSMutableDictionary *mutableProperties = [NSMutableDictionary dictionaryWithCapacity:5];
 
    WKInterfaceDevice *device = [WKInterfaceDevice currentDevice];
    mutableProperties[@"$os"] = [device systemName];
    mutableProperties[@"$os_version"] = [device systemVersion];
    mutableProperties[@"$watch_model"] = [self watchModel];
 
    CGSize screenSize = device.screenBounds.size;
    mutableProperties[@"$screen_width"] = @(screenSize.width);
    mutableProperties[@"$screen_height"] = @(screenSize.height);
 
    return [mutableProperties copy];
}
 
+ (NSString *)watchModel {
    static const CGFloat kAppleWatchScreenWidthSmall = 136.f;
    static const CGFloat kAppleWatchScreenWidthLarge = 152.f;
 
    CGFloat screenWidth = [WKInterfaceDevice currentDevice].screenBounds.size.width;
    if (screenWidth <= kAppleWatchScreenWidthSmall) {
        return @"Apple Watch 38mm";
    } else if (screenWidth <= kAppleWatchScreenWidthLarge) {
        return @"Apple Watch 42mm";
    }
 
    return @"Apple Watch";
}
 
+ (NSString *)systemVersion {
    return [WKInterfaceDevice currentDevice].systemVersion;
}
 
@end

Here is the official API documentation for WKInterfaceDevice. It appears to be a public class as opposed to a non-public API. One of the things noted in the documentation that might have relevance to this issue is, “Do not subclass or create instances of this class yourself. Always call the currentDevice class method to get the shared device object.” As you can see, all usages of WKInterfaceDevice in the source file either call the currentDevice method (as recommended in the doc) or is used as a type declaration—there is no subclassing or creating instances of the class ourselves.

So I’m at a loss as to understanding why this is being flagged as problematic. We’re not using a beta version of Xcode nor anything related to iOS 13, but could it have something to do with upcoming changes being incorporated into the verification tooling? Perhaps the verification tooling is being overly aggressive and is resulting in a false positive? If an adjustment is made to the verification tooling, how confident can we be that it won’t happen again in the future randomly?

Thank you very much for your help!

Kevin

Thanks so much @KevinKelchen ! We believe it's a false positive and sent a support request to Apple yesterday as well.

In the meantime, for anyone having this issue, as a workaround you don't have to remove Mixpanel entirely, you may

  1. Comment out the reference to MixpanelWatchProperties in Mixpanel.m
    image

  2. Comment out the reference to MixpanelWatchProperties in MixpanelPeople.m
    image

  3. Remove MixpanelWatchProperties.h and MixpanelWatchProperties.m from your app target

Alternatively you can also apply this patch for the above changes:

WKInterfaceDevice-patch.diff.zip

Doesn't apple have some sort of priority support channel?

Is there any one here still experienced the issue by using our pure Objective-C sdk v3.4.6 or above and not the cross-platform sdk (ionic, react native and Cordova, etc)? BTW we haven't heard any customer reported the same issue by using our Swift sdk which contains the same WKInterfaceDevice reference. https://github.com/mixpanel/mixpanel-swift

Hi @dorthwein, it seems you had tried our latest fix and still immediately got the rejection? I am curious
in what phase you got the rejection. During uploading the IPA and the automatic validation/scan or After you submit the app for review? We have tried our own app and and it's still pending on waiting for review for now. Any more information will be greatly helpful.

For 3rd party cross-platform sdks, please make sure the embedded Mixpanel is up to date with latest version and apply the patch if necessary.

@zihejia this fork worked

https://github.com/laurant/cordova-mixpanel-plugin

I think it's also a wise idea that Mixpanel takes ownership of the Cordova repository, as it would be pretty trivial to maintain. Cordova is popular enough to justify 1st party support, and this prevents people from using a dated fork.

Are you able to upload build successfully using this fork ? https://github.com/laurant/cordova-mixpanel-plugin @jpike88

I received a reply from Apple 3 days ago (Friday afternoon in the U.S.) Again, this is with the Cordova plugin. Case number is redacted:

Hello Kevin,

My name is James with Apple Developer Program Support. I received your email and I understand that you received notice that a build you had uploaded to testFlight was not processed due to a non-public API error. I’ll be happy to help you with this.

Upon review, I was able to see that you have been able to successfully upload newer builds since you first encountered this message. Have you had any additional warnings or has this issue continued since? If so, please let us know and we can investigate further.

If the issue has been resolved, please let us know if you need assistance with anything else.

Case number for your records: <case_number>.

Have a great weekend,

James
Apple Inc.

My response sent shortly thereafter:

Hi James! 👋

Thank you so much for your response and for investigating this issue for us!

The uploading of newer builds to TestFlight has been due to us temporarily removing the library from our app so that we can at least continue our development/QA efforts for the remainder of the application. We are in a non-releasable state right now because of the issue which we have been continuing to experience. It’s been about 24 hours since we last tried uploading a build that contained the library, and as has been the case since Tuesday (June 4), it invariably failed due to this issue.

Thank you so much, James, and you have a great weekend, too!

Kevin

Haven't heard anything since.

hi @KevinKelchen , could you try making sure the plugin includes our latest v3.4.6/v3.4.7 and then upload again? We believe the issue has been resolved since the v3.4.6 release, we've got apps being approved since using the version.

Hi @zihejia!

The Cordova plugin was updated to incorporate v3.4.6 and that did not fix the issue. From the issue on the Cordova plugin the v3.4.6 update looks to not be fixing it for others as well.

Some are beginning to temporarily use a fork of the Cordova plugin that not only removed most of the dead watchModel code that v3.4.6 did, but also replaced other WKInterfaceDevice references with dummy values. The fork is working for others (I haven't tried it personally as I've been holding out for a more long-term fix).

It's not working using this fork for me https://github.com/laurant/cordova-mixpanel-plugin. Still gets rejection from apple. So is there any other soultion for this issue?

Run a grep for WKInterfaceDevice in the iOS project, post the output here.

Okay let me check it

grep is a command ? @jpike88

I’ve been working with Apple further on the support case. I was asked for additional information including a copy of our IPA app binary.

So after adding the Cordova plugin back to the project (the latest official version that incorporates v3.4.6—not a fork) and running a build/upload to TestFlight, it succeeded. 🤨 I even confirmed that it was successfully logging events on mixpanel.com. I ran a second build/upload and it also succeeded...

I provided the requested information and asked in my message if Apple has changed anything on their end which would have caused uploads to succeed again.

As far as our app goes while we waited for a fix, because it runs in a webview we were able to move forward by using mixpanel-browser instead of the native SDKs provided by the Cordova plugin. We used a couple of other Cordova plugins to log extra properties that we wanted to retain from the native SDKs (app-version plugin for app version number and device plugin for manufacturer, model, and OS version). A downside is that although the properties are named the same as their native counterparts, because they are custom properties they are considered different properties when looking at analytics. Mixpanel does, however, now work when running our app purely on the Web platform and not within a native app. 👍

Thanks so much @KevinKelchen for the updates. I believe this is no longer an issue any more, will close this thread in 2 days.

I'm closing this one now since it's no longer an issue for the latest Mixpanel native SDK any more.