Forum Xamarin.iOS

Xamarin.Mac and the App Store

TonyValentiTonyValenti USMember ✭✭
edited March 2017 in Xamarin.iOS

Hi All,
I'm working on my for app for the AppStore and (I think) it is almost done. I just tried publishing it for the first time today, and I got the following rejection error:

Deprecated API Usage - Apple no longer accepts submissions of apps that use QuickTime or QTKit APIs.

I searched all though my code trying to find out how I had a QTKit reference and I could not find it anywhere. I just learned that QTKit is part of Xamarin.Mac. So here is my question: How do I publish an app to the MacOS AppStore without receiving this error?

Best Answers

Answers

  • ChrisHamonsChrisHamons USForum Administrator, Xamarin Team Xamurai

    Are you using the XM 4.5 target framework or Mobile?

    https://developer.xamarin.com/guides/mac/advanced_topics/target-framework/

    Could you PM me a full build log as well. We'll get it straightened out.

  • ChrisHamonsChrisHamons USForum Administrator, Xamarin Team Xamurai

    One of our QA engineers tested this use case this morning and hit the same thing.

    https://bugzilla.xamarin.com/show_bug.cgi?id=53936

    I'm currently working on a fix.

  • ChrisHamonsChrisHamons USForum Administrator, Xamarin Team Xamurai

    I think I understand the problem and we're testing something now. If so, it'll be a simple mmp argument to fix.

    Expect an answer this afternoon.

  • TonyValentiTonyValenti USMember ✭✭
    Awesome @chrishamons !

    Thanks so much! Let me know if there is anything else you need from me.
  • TonyValentiTonyValenti USMember ✭✭

    Hey @ChrisHamons -
    Did you ever hear back from apple? What did they say? Am I clear to move forward?

    Also, do you have any pointers on how I would use this Mac API:

    CFMutableDictionaryRef mountOpts = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
    CFDictionarySetValue(mountOpts, kNetFSMountAtMountDirKey, CFSTR("true"));
    int ret = NetFSMountURLSync(
        (__bridge CFURLRef) url,
        (__bridge CFURLRef) mountPath,
        NULL,
        NULL,
        0,
        mountOpts,
        &mountPoints
    );
    
  • ChrisHamonsChrisHamons USForum Administrator, Xamarin Team Xamurai

    We never heard back, which makes me believe they accepted our QA "submit this application" test.

    Let me know if Apple pushes back on your next submission attempt.

  • ChrisHamonsChrisHamons USForum Administrator, Xamarin Team Xamurai

    On your other question, I'd read it like this:

    CFMutableDictionaryRef mountOpts = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
    CFDictionarySetValue(mountOpts, kNetFSMountAtMountDirKey, CFSTR("true"));
    

    NSDictionary and CFDictionary are toll free bridged, and the first bit the usual incantation to make a CFDictionary. So the only hard part is getting the constant. Normally constants are symbols you grab like this:

    var lib = ObjCRuntime.Dlfcn.dlopen ("/System/Library/Frameworks/NetFS.framework/NetFS", 0);
    var val = ObjCRuntime.Dlfcn.GetIndirect (lib, "kNetFSMountAtMountDirKey");
    

    but in some CoreFoundation like libs, like this one it is hard coded:

    NetFS.h:#define kNetFSMountAtMountDirKey    CFSTR("MountAtMountDir")
    

    Then the call:

    int ret = NetFSMountURLSync(
        (__bridge CFURLRef) url,
        (__bridge CFURLRef) mountPath,
        NULL,
        NULL,
        0,
        mountOpts,
        &mountPoints
    );
    

    You have to go find the header to get the definition to know how to bind it:

    int
    NetFSMountURLSync(
        CFURLRef url,               // URL to mount, e.g. nfs://server/path
        CFURLRef mountpath,         // Path for the mountpoint
        CFStringRef user,           // Auth user name (overrides URL)
        CFStringRef passwd,             // Auth password (overrides URL)
        CFMutableDictionaryRef open_options,    // Options for session open (see below)
        CFMutableDictionaryRef mount_options,   // Options for mounting (see below)
        CFArrayRef *mountpoints)        // Array of mountpoints
        __OSX_AVAILABLE_STARTING(__MAC_10_8, __IPHONE_NA);
    

    so something like

    [DllImport ("/System/Library/Frameworks/NetFS.framework/NetFS")]
    static extern int int NetFSMountURLSync (IntPtr url, IntPtr mountpath, IntPtr user, IntPtr passwd, IntPtr open_options, IntPtr mount_options, IntPtr mountpoints);
    

    So you'd need to setup the various NSUrl / NSString / NSDictionary / NSArray data structures, and pass each handle into the right place (or use IntPtr.Zero if you are allowed to pass null).

    Be careful and test it well, mistakes in p/invokes can be frightful to track down if you get unlucky and it does not crash right away.

  • TonyValentiTonyValenti USMember ✭✭

    Hi @ChrisHamons -
    Thank you so much for your help so far. I'm really lost on how to convert the code though. I've spent many years in .NET and Win32 but I'm brand new to Mac.

    Is my code below on the right track?

    What is the class type for a CFArray?
    Why do I get a null reference exception when creating my NSMutableDictionary?

    System.NullReferenceException: Object reference not set to an instance of an object
    at ObjCRuntime.Class.Register (System.Type type) [0x00007] in /Users/builder/data/lanes/1381/35ff3ed1/source/xamarin-macios/src/ObjCRuntime/Class.cs:126
    at ObjCRuntime.Class.GetHandle (System.Type type) [0x00002] in /Users/builder/data/lanes/1381/35ff3ed1/source/xamarin-macios/src/ObjCRuntime/Class.cs:94
    at Foundation.NSObject.AllocIfNeeded () [0x0001e] in /Users/builder/data/lanes/1381/35ff3ed1/source/xamarin-macios/src/Foundation/NSObject2.cs:476
    at Foundation.NSObject..ctor (Foundation.NSObjectFlag x) [0x00008] in /Users/builder/data/lanes/1381/35ff3ed1/source/xamarin-macios/src/Foundation/NSObject2.cs:116
    at Foundation.NSDictionary..ctor (Foundation.NSObjectFlag t) [0x00000] in /Users/builder/data/lanes/1381/35ff3ed1/source/xamarin-macios/src/build/mac/mobile/Foundation/NSDictionary.g.cs:149
    at Foundation.NSMutableDictionary..ctor () [0x00000] in /Users/builder/data/lanes/1381/35ff3ed1/source/xamarin-macios/src/build/mac/mobile/Foundation/NSMutableDictionary.g.cs:88
    at TestMap2.MainClass.MountUrl (System.String Url, System.String MountPath, System.String UserName, System.String Password, System.Collections.Specialized.StringDictionary OpenOptions, System.Collections.Specialized.StringDictionary MountOptions, System.Object MountPoints) [0x0001e] in /Users/tonyvalenti/Projects/TestMap2/TestMap2/Main.cs:36
    at TestMap2.MainClass.Main (System.String[] args) [0x0001c] in /Users/tonyvalenti/Projects/TestMap2/TestMap2/Main.cs:13

    using System;
    using System.Runtime.InteropServices;
    using AppKit;
    using CoreFoundation;
    using System.Collections.Specialized;
    using Foundation;
    
    namespace TestMap2 {
        static class MainClass {
            static void Main(string[] args) {
    
                var Test =
                    MountUrl(
                        "http://127.0.0.1:8881/",
                        "/Users/TonyValenti/AlphaDrive",
                        "NoUser",
                        "NoPass",
                        null,
                        null,
                        new object()
                    );
    
    
    
                NSApplication.Init();
                NSApplication.Main(args);
            }
    
    
            public static int MountUrl(string Url, string MountPath, string UserName, string Password, StringDictionary OpenOptions, StringDictionary MountOptions, object MountPoints) {
                var CUrl = CFUrl.FromUrlString(Url, null);
                var CMountPath = CFUrl.FromFile(MountPath);
                var CUserName = new CFString(UserName);
                var CPassword = new CFString(Password);
    
                var COpenOptions = new NSMutableDictionary();
                var CMountOptions = new NSMutableDictionary();
                var CMountPoints = MountPoints;
    
    
    
                return NetFSMountURLSync(
                    (IntPtr)GCHandle.Alloc(CUrl),
                    (IntPtr)GCHandle.Alloc(CMountPath),
                    (IntPtr)GCHandle.Alloc(CUserName),
                    (IntPtr)GCHandle.Alloc(CPassword),
                    (IntPtr)GCHandle.Alloc(COpenOptions),
                    (IntPtr)GCHandle.Alloc(CMountOptions),
                    (IntPtr)GCHandle.Alloc(CMountPoints)
    
                );
    
            }
    
    
            [DllImport("/System/Library/Frameworks/NetFS.framework/NetFS")]
            static extern int NetFSMountURLSync(IntPtr url, IntPtr mountpath, IntPtr user, IntPtr passwd, IntPtr open_options, IntPtr mount_options, IntPtr mountpoints);
    
    
    
    
    
        }
    }
    
    
  • TonyValentiTonyValenti USMember ✭✭
    edited March 2017

    @ChrisHamons
    Hey Chris,
    I wanted to let you know that I really appreciate all of your work helping me out with this. The API call works and my app is now going through approval on the App Store!

    I have a few follow up questions about this:

    Is there a way to request that Xamarin add it to MonoMac?

    • Is there a way to make info.plist automatically get its version number from AssemblyInfo?
  • IvanIcinIvanIcin USMember ✭✭✭

    @ChrisHamons I can't fix this problem.

    I've tried the following:

    Any help?

  • IvanIcinIvanIcin USMember ✭✭✭

    I understand the issue better now. I don't use full XM 4.5. However, I do use some libraries that don't support Xamarin Mac directly, but can be tricked to run on it properly by using their desktop binaries. I don't use linker and everything runs perfectly well.

    However, this combination likely causes the same QTKit error to appear when submitting to the App Store even with mmp arguments set.

    @ChrisHamons can you help with this new info?

  • ChrisHamonsChrisHamons USForum Administrator, Xamarin Team Xamurai

    @IvanIcin - Please, please consider making a new post when your question isn't directly related to the original. It spams everyone on the thread and makes reading things more difficult.

    The option in question is --registrar:dynamic The medium story had a typo (fixed). However, as you found if you misspell the option it will be a build error, so if things build then that option took effect.

    Are you certain you added it to the Release configuration and not just debug?

    Please create a new post, post a link to a pastebin/gist of your full release configuration, and someone on the forums may be able to help. You have not currently provided enough information for anyone to be able to solve your issue.

Sign In or Register to comment.