We have learned that in order to get our Mac app into the App Store we need to check that it's has a valid receipt and that iTunes user obtained it from the App Store.
We have also learned that this is not a common topic and is not really documented by Xamarin.
I did find this topic:
https://forums.xamarin.com/discussion/29794/how-can-i-validate-mac-app-store-receipt-locally
Which got me pointed in the right direction. However Xamarin.Mac by default does not have an "int main()" function. The main function is void. It's unclear how to handle the case that the receipt is invalid in Xamarins C# version of this structure.
Also Xamarin.Mac does not contain a binding project like Xamarin.iOS, so I can not just drop this Objective-C code in. It's really hard to believe that up until now no one has submitted a Xamarin.Mac app to the app store. You'd think this would be a pretty important topic to cover in the online documentation. Especially given the premium cost to have individual Mac developer licenses.
I would love to know if there is a simple solution to getting this validation in our code as all the solutions seem to be complex.
Things I am considering:
1. Writing this big Obj-C class in C# which somehow can call "exit(173)" by thumbing through the code line by line
2. Creating a binding somehow
Can anyone out there save me some time?
Posts
I've never done this, at all, but a glance suggests:
@ChrisHamons
Thanks for the link to the binding solution. I also found this:
http://brendanzagaeski.appspot.com/xamarin/0002.html
I was going to go down that path and assume the binding translates the "exit()" function.
@ChrisHamons
Need some help with the clang command. I modified the downloaded zip so that "SimpleClass" was "RVNReceiptValidation" but when I run the following:
clang -dynamiclib -std=gnu99 RVNReceiptValidation.m -current_version 1.0 -compatibility_version 1.0 -fvisibility=hidden -framework Cocoa -o bin/RVNReceiptValidation.dylib
I get a huge list of errors:
Undefined symbols for architecture x86_64:
"_CMSDecoderCopyContent", referenced from:
_RVNDecodeReceiptData in RVNReceiptValidation-1084f1.o
"_CMSDecoderCopySignerStatus", referenced from:
_RVNDecodeReceiptData in RVNReceiptValidation-1084f1.o
"_CMSDecoderCreate", referenced from:
_RVNDecodeReceiptData in RVNReceiptValidation-1084f1.o
...
"_kSecAsn1UTF8StringTemplate", referenced from:
_RVNDecodeUTF8StringFromASN1Data in RVNReceiptValidation-1dd8e6.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
I am not an expert on compilers so I am pretty confused. My guess was I need to reference the Security framework? But it seems like it's included in Cocoa?
Yeah, it appears to live there:
https://developer.apple.com/library/mac/documentation/Security/Reference/CryptoMessageRef/#//apple_ref/c/func/CMSDecoderCopyContent
Often, an easy way to figure out the command line arguments to, is create a new project in Xcode. Add the frameworks you need and a reference to each symbol. Then build and click the "Show report navigator on the left pane (thought bubble)", then click build, and expand the build lines to see what clang was passed.
I did have an Xcode project. Here is what I am seeing in the section you mentioned:
clang -arch x86_64 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.10.sdk -L/Users/Paul/Library/Developer/Xcode/DerivedData/AppStoreReceipt-cogqxtnqhvabmlfrmweqndbywcxl/Build/Products/Debug -F/Users/Paul/Library/Developer/Xcode/DerivedData/AppStoreReceipt-cogqxtnqhvabmlfrmweqndbywcxl/Build/Products/Debug -filelist /Users/Paul/Library/Developer/Xcode/DerivedData/AppStoreReceipt-cogqxtnqhvabmlfrmweqndbywcxl/Build/Intermediates/AppStoreReceipt.build/Debug/AppStoreReceipt.build/Objects-normal/x86_64/AppStoreReceipt.LinkFileList -Xlinker -rpath -Xlinker @executable_path/../Frameworks -mmacosx-version-min=10.10 -fobjc-arc -fobjc-link-runtime -Xlinker -dependency_info -Xlinker /Users/Paul/Library/Developer/Xcode/DerivedData/AppStoreReceipt-cogqxtnqhvabmlfrmweqndbywcxl/Build/Intermediates/AppStoreReceipt.build/Debug/AppStoreReceipt.build/Objects-normal/x86_64/AppStoreReceipt_dependency_info.dat -o /Users/Paul/Library/Developer/Xcode/DerivedData/AppStoreReceipt-cogqxtnqhvabmlfrmweqndbywcxl/Build/Products/Debug/AppStoreReceipt.app/Contents/MacOS/AppStoreReceipt
Hmm. Did you check the link step? They are calling clang a few time, for the o files and then the link.
I see -framework Security
Yes, that was from the link step. I finally got it, was a matter of figuring out the command line syntax. I was trying to do it with a pipe, but you just need to repeat -framework:
clang -dynamiclib -arch x86_64 -mmacosx-version-min=10.10 -std=gnu99 RVNReceiptValidation.m -current_version 1.0 -compatibility_version 1.0 -fvisibility=hidden -framework Security -framework Cocoa -framework IOKit -o bin/RVNReceiptValidation.dylib
I now have a dylib file! Lets see how much further I can get haha. Thanks.
@ChrisHamons
I am now able to create a .dll file for this. I am so close! Yet when I create an instance using:
RVNReceiptValidation v = new RVNReceiptValidation();
The app quits saying:
Debugger operation failed
Argument cannot be null
Paramter name: attributeType
Here is some of the code included in my .dll:
@interface RVNReceiptValidation : NSObject
bool ValidateAppStoreReceipt();
@end
RNVRecepitValidation.m (note this is the exact same as git hub except I created this function)
bool ValidateAppStoreReceipt()
{
}
RVNReceiptValidation.cs:
using Foundation;
namespace Musicnotes.Mac {
[BaseType (typeof (NSObject))]
interface RVNReceiptValidation {
}
}
I am getting lost somewhere having to create an instance of the object vs having a static function. I will need to read up on binding a static C function.
Note, sorry the code thing in this forum doesn't look right.
Using the make file to determine if I got this working or not, I am very close but just missing something:
$ make
mkdir -p bin tmp
clang -dynamiclib -arch x86_64 -std=gnu99 RVNReceiptValidation.m -current_version 1.0 -compatibility_version 1.0 -fvisibility=hidden -framework Security -framework Cocoa -framework IOKit -o bin/RVNReceiptValidation.dylib
MONO_PATH=/Library/Frameworks/Xamarin.Mac.framework/Versions/Current/lib/mono/Xamarin.Mac /Library/Frameworks/Xamarin.Mac.framework/Versions/Current/bin/bmac-mobile-mono /Library/Frameworks/Xamarin.Mac.framework/Versions/Current/lib/bmac/bmac-mobile.exe -baselib:/Library/Frameworks/Xamarin.Mac.framework/Versions/Current/lib/reference/mobile/Xamarin.Mac.dll --api=RVNReceiptValidation.cs -o:bin/RVNReceiptValidation.dll --tmpdir=tmp --ns=Musicnotes.Mac
WARNING: The runtime version supported by this application is unavailable.
Using default runtime: v4.0.30319
mcs /out:bin/RVNReceiptValidationTest.exe RVNReceiptValidationTest.cs /target:exe /nostdlib /reference:/Library/Frameworks/Xamarin.Mac.framework/Versions/Current/lib/mono/Xamarin.Mac/System.dll /reference:/Library/Frameworks/Xamarin.Mac.framework/Versions/Current/lib/mono/Xamarin.Mac/System.Core.dll /reference:/Library/Frameworks/Xamarin.Mac.framework/Versions/Current/lib/mono/Xamarin.Mac/Xamarin.Mac.dll /reference:bin/RVNReceiptValidation.dll /reference:/Library/Frameworks/Xamarin.Mac.framework/Versions/Current/lib/mono/Xamarin.Mac/mscorlib.dll
RVNReceiptValidationTest.cs(23,8): warning CS0219: The variable `v' is assigned but its value is never used
Compilation succeeded - 1 warning(s)
/Library/Frameworks/Xamarin.Mac.framework/Versions/Current/bin/mmp /output:bin /name:RVNReceiptValidationTest /profile:Xamarin.Mac /arch:x86_64 /sgen /new-refcount /nolink /assembly:/Library/Frameworks/Xamarin.Mac.framework/Versions/Current/lib/mono/Xamarin.Mac/System.dll /assembly:/Library/Frameworks/Xamarin.Mac.framework/Versions/Current/lib/mono/Xamarin.Mac/System.Core.dll /assembly:/Library/Frameworks/Xamarin.Mac.framework/Versions/Current/lib/mono/Xamarin.Mac/Xamarin.Mac.dll /assembly:bin/RVNReceiptValidation.dll /assembly:/Library/Frameworks/Xamarin.Mac.framework/Versions/Current/lib/mono/Xamarin.Mac/mscorlib.dll bin/RVNReceiptValidationTest.exe
Xamarin.Mac 2.0.1 Business Edition
warning MM2006: Native library 'liboleaut32.dylib' was referenced but could not be found.
bundling complete
cp bin/RVNReceiptValidation.dylib bin/RVNReceiptValidationTest.app/Contents/MonoBundle/
./bin/RVNReceiptValidationTest.app/Contents/MacOS/RVNReceiptValidationTest
Xamarin.Mac: Could not load machine.config: /Users/Paul/Downloads/XMBindingExample/bin/RVNReceiptValidationTest.app/Contents/MonoBundle/machine.config
WARNING: The runtime version supported by this application is unavailable.
Using default runtime: v4.0.30319
ObjCRuntime.Dlfcn.dlopen (/Users/Paul/Downloads/XMBindingExample/bin/RVNReceiptValidationTest.app/Contents/MonoBundle/RVNReceiptValidation.dylib)
Unhandled Exception:
System.Exception: Could not create an native instance of the type 'Musicnotes.Mac.RVNReceiptValidation': the native class hasn't been loaded.
It is possible to ignore this condition by setting ObjCRuntime.Class.ThrowOnInitFailure to false.
at Foundation.NSObject.InitializeObject (Boolean alloced) [0x00000] in :0
at Foundation.NSObject..ctor (Foundation.NSObjectFlag x) [0x00000] in :0
at Musicnotes.Mac.RVNReceiptValidation..ctor () [0x00000] in :0
at Musicnotes.Mac.MainClass.Main (System.String[] args) [0x00000] in :0
[ERROR] FATAL UNHANDLED EXCEPTION: System.Exception: Could not create an native instance of the type 'Musicnotes.Mac.RVNReceiptValidation': the native class hasn't been loaded.
It is possible to ignore this condition by setting ObjCRuntime.Class.ThrowOnInitFailure to false.
at Foundation.NSObject.InitializeObject (Boolean alloced) [0x00000] in :0
at Foundation.NSObject..ctor (Foundation.NSObjectFlag x) [0x00000] in :0
at Musicnotes.Mac.RVNReceiptValidation..ctor () [0x00000] in :0
at Musicnotes.Mac.MainClass.Main (System.String[] args) [0x00000] in :0
make: *** [all] Error 1
I am curious about:
warning MM2006: Native library 'liboleaut32.dylib' was referenced but could not be found.
Are you loading the native library before NSApplication.Init (). Aka the ObjCRuntime.Dlfcn.dlopen dance?
Here is my "test" code:
`using System;
using System.Reflection;
using System.IO;
using AppKit;
namespace Musicnotes.Mac
{
static class MainClass
{
}`
OK, so after more digging around and comparing to example. I finally got the make file to work!!!
It was because I needed to use @implement RVNReceiptValidation around my instance code. Makes perfect since.
Could you consider packaging up your example for others to use? As you pointed out, you aren't the first or the last to run into this pain point.
@ChrisHamons
Hopefully the last thing I will bug you about on this. I included the DLL created by my make file in Xamarin (note the make file executes a test which I see the code is being run), but I am still seeing:
Debugger operation failed
Argument cannot be null.
Parameter name: attributeType
Sure, I am still not done yet ... At the end of the article you showed me, it states:
I am not seeing the dylib in the .app file.
Unlike Xamarin.iOS, we don't have baked in support for binding projects / native references. I'm not sure what the article is referencing to be honest.
Right now, you need to make a post-build step to copy the dylib into your app bundle.
We are working on improving the integration with native libraries / bindings in a future version, so it will be closer to how things are with Xamarin.iOS.
I finally got it by manually copying the dylib!!!! Oh man this was complex. Thanks for your help. I will come up with a way to copy the dylib with your suggestion.
I will create a github with my solution.
@ChrisHamons
Here is the solution you had asked for on my github:
https://github.com/pmhart83/xamarin-mac-RVNReceiptValidation-binding
Good stuff. Thanks for packaging everything up for others!
All,
Sorry for the late response, I've been tied up documenting iOS 9 support. I'll add this to the list of Xamarin.Mac documentation requests and see if I can get @pmhart83's solution included in the docs/samples here shortly.
Please let me know if you need anything else.
Thanks,
Kevin
Hi Chris,
I'm trying to build the same but got stuck on the part where I need to copy the dylib in the package - what exactly am I supposed to type in post build so that it gets copied to the correct path in the package?
Another issue - when you actually create the example for the app purchase you may consider to sign the ReceiptValidation.dll as it cannot be used in signed .exe currently.
I ran into problems with our first submission. The dll / dylib have to be read only.
I solved the issue of the dylib not being in the bundle folder by moving it from resources to monobundle using System.IO.
You can include a reference to the dll but it will not work until you copy the dylib to MonoBundle.
Also I had to change the original code by moving the static NSString config vars to an instance var then pass the receipt object to the static functions and see what the instance of bundleId is. Somehow it was crashing when included in the mac project otherwise.
@BobMilanov You'll need to create a Custom Command / Post build that copies you file into the right directory.
You can play around with the various environmental variables by doing things like:
echo ${TargetDir}
and building.
Roughly, it'll be something like cp ${ProjectDir}/WHEREVER_YOU_KEEP_THE_DYLIB ${TargetDir}/Foo.app/Contents/MonoBundle/
Thanks Chris,
Managed to get past this step but the problem with the signing remained (could not find a switch to sign the generated dll) so I switched to fully managed code to check the receipt that does not use cryptography - hopefully that will get me through the app review
. In case somebody else bumps into this it is important to call NSApplication.Init(), before accessing the NSBundle.MainBundle.AppStoreReceiptUrl property - otherwise the app simply crashes. In case the receipt verification fails simply exit with Environment.Exit(173);. When I finish the whole code I'll post it in the forum as well...
@KMullins Hi Kevin, from apple's documentation. there shouldn't be much difference validating receipts between mac ox and iOS. Do you think this method discussed in this thread can be applied to iOS app? i.e. Package RVNReceiptValidation into static library then import it in Xamarin.iOS project?
thanks,
Amy