Eric58
Members-
Content Count
13 -
Joined
-
Last visited
Community Reputation
6 Neutral-
Recent crashes with macOS StoreKit that never occured previously
Eric58 replied to Eric58's topic in Cross-platform
A bit more concrete info, my IAP code is based on the one shared by Hans a few years ago, which I believe quite a few of us are now familiar with: In my case, the specific call where the sporadic crash occurs is the last line of my adaptation of Han's code: procedure TMacOSAppPurchaseService.QueryProducts(const ProductIDs: TStrings); var ProductIDsArray: NSMutableArray; ProductIDsSet: NSSet; ProductID: string; begin ProductIDsArray := TNSMutableArray.Create; for ProductID in ProductIDs do ProductIDsArray.addObject(PStrToNSStr(ProductID)); ProductIDsSet := TNSSet.Wrap(TNSSet.OCClass.setWithArray(ProductIDsArray)); FProductsRequest := TSKProductsRequest.Wrap(TSKProductsRequest.Alloc.initWithProductIdentifiers(ProductIDsSet)); ... FProductsRequest.setDelegate(FProductsRequestDelegate.GetObjectID); FProductsRequest.start end; -
Recent crashes with macOS StoreKit that never occured previously
Eric58 posted a topic in Cross-platform
For those who have apps on macOS App Store, have you encountered this situation I've been facing in recent months? : The app review team started rejecting my submissions recently because whenever they tried to execute the IAP code on my app, the app just crashed. The specific StoreKit API that led to the crash is the one to do with ProductRequests.start. My app has been on the store for 3 years, and there has been no change to my IAP-related code all this while. I experimented with putting a Sleep(1000) just before calling that API, and the incidence of crash was somewhat reduced (and I managed to sneak in a submission once using this temporary hack). However, this is still not a solution since it still crashes sporadically, and later submissions were rejected because the crash happened during app review. The scant information I got from attempting to debug indicated that the symbol "StoreKit" couldn't be found. This led me to suspect that Apple's deprecation of StoreKit, switching to StoreKit 2, a few months ago may have something to do with this. But I'm just not sure, because the API call still works occasionally. Has anyone any insight about this? Also, is anyone aware of a StoreKit 2 (which is based on Swift) binding for Delphi available somewhere that I can perhaps explore as a solution? -
How to get TOpenDialog on sandboxed MacOS app to work
Eric58 replied to Eric58's topic in Cross-platform
Problem solved, after a bit of googling. Need to set the following item in the Entitlements List of Project Options to true for sandbox applications using TOpenDialog: "Read/write access to files selected with the Open or Save" -
Has anyone solved the problem of getting TOpenDialog on a MacOS application that is sandboxed to work? Sandboxed means the application build type is set to "Application Store" as opposed to setting it to "Normal" or "Developer ID". On MacOS, TOpenDialog is ultimately implemented as NSOpenPanel (which is MacOS's native method for letting the user select a file from the file system), and it works fine as long as my application is deployed as a Normal build. But the minute I set the build type to Application Store, the generated entitlements file will include a sandbox key with value set to true (as it should), making the application a sandboxed app, and that’s when TOpenDialog fails to run. Hope someone out there has a solution for this problem.
-
Hi Francisco, I faced the same problem a few months ago, and I solved it this way: Start with the the sample code MacInAppTest.zip posted by Hans, then modify it as described further below; the modifications are necessary to account for these reasons that caused the AV crashes reported by Hans: Unlike for iOS, the DispatchToDelphi for MacOS is called outside of the mainthread. This is why the callback to TiOSProductsRequestDelegate.productsRequest behaves erratically - it is no longer threadsafe. This can be fixed using the treatment shown in the code I attach below. This treatment needs to be applied for all other callback procedures that also need to be threadsafe. Unlike for the iOS compiler, the OSX64 compiler does not adopt the ARC management model. This difference requires that the field FProductsRequestDelegate in the TiOSInAppPurchaseService class be accompanied by another IInterface field which I've named FHoldProductsRequestDelegate. See my comment in the code snippet below for the detailed explanation. My original goal was to create a workable FMX.InAppPurchase.Mac, but I no longer have that module as my goal then expanded to incorporate my custom licensing requirements over the innate differences of Mac's and Window's IAP, and I ended up regutting what I needed from both platform's working IAP code into my cross-platform IAP+Licensing module. Modifications Needed on Han's code: procedure TiOSProductsRequestDelegate.productsRequest(request: SKProductsRequest; didReceiveResponse: SKProductsResponse); var Product: SKProduct; LocalProduct: TProduct; Products: NSArray; InvalidProducts: NSArray; ProductID: NSString; I: Integer; InvalidProductIDList: TStrings; _retainedProducts, _retainedInvalidProducts: NSArray; begin if TThread.Current.ThreadID <> MainThreadID then if TThread.Current.ThreadID <> MainThreadID then //breakpoint here to prove that we are not in mainthread ; /// DispatchToDelphi call this through a thread that is not the mainthread. /// We need to route this to the mainthread via Synchronize, but first we /// extract the data needed and hold them as captured variables for use during /// mainthread processng: _retainedProducts := didReceiveResponse.products; _retainedProducts.retain; _retainedInvalidProducts := didReceiveResponse.invalidProductIdentifiers; _retainedInvalidProducts.retain; TThread.Synchronize(nil, procedure var I: Integer; begin FIAPService.FProductList.Clear; InvalidProductIDList := TStringList.Create; if FIAPService.FProducts <> nil then FIAPService.FProducts.release; Products := _retainedProducts; //instead of didReceiveResponse.Products; FIAPService.FProducts := Products; FIAPService.FProducts.retain; InvalidProducts := _retainedInvalidProducts; //instead of didReceiveResponse.invalidProductIdentifiers; if (Products <> nil) and (Products.count > 0) then for I := 0 to Pred(Products.count) do begin Product := TSKProduct.Wrap(Products.objectAtIndex(I)); LocalProduct := SKProductToProduct(Product); FIAPService.FProductList.Add(LocalProduct); end; if (InvalidProducts <> nil) and (InvalidProducts.count > 0) then for I := 0 to Pred(InvalidProducts.count) do begin ProductID := TNSString.Wrap(InvalidProducts.objectAtIndex(I)); InvalidProductIDList.Add(NSStrToStr(ProductID)); end; if FIAPService <> nil then FIAPService.DoProductsRequestResponse(FIAPService.FProductList, InvalidProductIDList); // Finally balance the retains with releases: _retainedProducts.release; _retainedInvalidProducts.release; end) end; TiOSInAppPurchaseService = class(TInterfacedObject, IFMXInAppPurchaseService) private FProductsRequest: SKProductsRequest; /// FProductsRequestDelegate implements IInterface, so when QueryProducts access /// it via the construct FProductsRequestDelegate as ILocalObject, it will trigger /// an AddRef followed by a Release when QueryProducts goes out of scope. This /// will cause FProductsRequestDelegate to be prematurely freed. To prevent this /// add a new field FHoldProductsRequestDelegate and assign it to FProductsRequestDelegate /// in the constructor. Note that the iOS compiler does not face this problem because /// its ARC memory management will automatically do an AddRef on FProductsRequestDelegate FProductsRequestDelegate: TiOSProductsRequestDelegate; FHoldProductsRequestDelegate: IInterface; ...... end;
-
MacOS debugging: "Invalid process during debug session"
Eric58 replied to Eric58's topic in Cross-platform
Issue resolved. The cause of my problem is this: Myxcode-select command-line tool was not pointing to my main Xcode installation folder. This must have happened when I used brew to download the latest Xcode command line tools, and brew then set xcode-selectto point to a different path (/Library/Developer/CommandLineTools). I repaired this by issuing this command at the terminal, following the instruction from http://docwiki.embarcadero.com/RADStudio/Sydney/en/SDK_Manager#Troubleshooting_.28macOS_and_iOS.29 sudo xcode-select -s /Applications/Xcode.app/Contents/Developer -
Previously I had no problem debugging my OSX64 program over PAServer from Windows 10 running under VMWare. However, after I installed Homebrew and MacPort (these are tool installers), and used brew to install some tools on my Mac, any attempt to start debugging causes this error message to pop up immediately after the Deploying/Signing phase: If I turn off Integrated Debugging via Tools/Options/Debugger, then I can deploy and run the program, but of course I wouldn’t be able to debug. So it seems that I have somehow screwed up the ability of the toolchain to invoke the macOS debugger. Does anyone know how to repair the problem? My environment: MacOS Mojave 10.14.6, with Xcode 11.3.1 RAD Studio 10.4
-
Advice on debugging Windows Store app while minimising submissions
Eric58 replied to Eric58's topic in General Help
OK, I found the solution of how to associate an app in Rad Studio to the store. The links you posted gave me an idea of what is needed, although it was addressing Visual Studio environment. The solution is to ensure that the manifest file generated for adhoc appx file is identical to the manifest file generated for the store appx. The reason why they are different is due to the different parameter-gathering approach of Rad Studio: 1. When building a Store appx file, Rad Studio prompts for these 4 parameter values that will be eventually substituted into the manifest file. Package Name Package Display Name Publisher Publisher Display Name 2. When building an Adhoc appx, Rad Studio only prompts you to supply the certificate file, whose Subject value will be eventually substituted as the Publisher value in the manifest file. So there are two steps to take if you want the adhoc manifest file to be identical to the store manifest file for the above 4 values: Step 1: Create a new certificate file that has the desired Subject value (ie, Publisher value), and use this new certificate file for adhoc building Step 2: Edit the manifest.template.xml file to change the remaining 3 parameters' values accordingly. With these steps, my problem is solved. Maybe Embarcadero should consider incorporating this as an automated facility within Rad Studio, to save others facing the same problem. -
Advice on debugging Windows Store app while minimising submissions
Eric58 replied to Eric58's topic in General Help
Thanks again for your reply. I still could not solve my problem. To help explain my situation better, I will differentiate between the two distribution types that an appx file can be generated for: Adhoc Store I am able to generate an Adhoc appx file that is signed using my trusted local certificate, and so installing that is not a problem. However if I generate a Store appx file, it will be left unsigned (the signing would be done by Windows Store upon submission). Also, the Adhoc appx’s manifest is always different from the Store appx’s manifest file (due to different certificate issuer-ID, among others). Ignoring the difference in manifest (which may or may not be the cause of my problem), I can therefore re-install a local copy of an Adhoc appx file (since it is a trusted app) to override the store-installed app, in order to debug using the local copy of my source code. And here is the exact description of my problem when I debug with this local copy - the local copy’s query to the Store’s API returns different results from the store-installed copy’s query: When calling the store API GetAssociatedStoreProductsAsync, the store-installed copy returns 0 in the ExtendedError value (as it should), while the local copy returns some non-zero value (indicating some kind of error). Consequently, the store-installed copy is able to return the list of add-on products, while the local copy cannot. Note: For those familiar with the TWindowsStore component, the ExtendedError information isn’t exposed, and I had to modify the dependent unit Winapi.WindowsStore to expose that value. For some reason, when the local copy calls the store APIs, the store is unable to recognise it as coming from a valid app. Maybe it is the fact that the app was not signed by Windows Store, and so the expected credential isn’t passed along in the API calls. -
Advice on debugging Windows Store app while minimising submissions
Eric58 replied to Eric58's topic in General Help
Thanks for the reply, but how do you "run" the local generated .appx file? It is an installer not an exe - when I double-click on it, a dialog box pops up saying that the app "is already installed", and containing the two buttons "Reinstall" and "Launch". When I click the Launch button, it launches the store-downloaded version, not the exe file embedded in that local .appx file (I verified this by rebuilding it to have a different mainform caption in order to differentiate). Then I click the Reinstall button (hoping that I could then run the local copy and attach its process to the debugger), but it fails with the message "Ask the developer for a new package. This one isn't signed with a trusted certificate." -
Advice on debugging Windows Store app while minimising submissions
Eric58 posted a topic in General Help
I’ve submitted an app to Windows Store that uses the TWindowsStore component to query add-ons the user can buy via In-App purchase. The app was submitted as “available for private audience only” so that I can continue to debug it before making it accessible to the public. In order to ensure that the app’s license is downloaded to my computer’s cache, I installed the app from the Windows store (via the private link given to me), run it once, then closed it. This is following the instruction here https://docs.microsoft.com/en-sg/windows/uwp/monetize/in-app-purchases-and-trials under the heading "Test your in-app purchase or trial implementation". Of course, that instruction is for Visual Studio and not Rad Studio, but the principle should be the same. To debug it, I then started the installed app again, and used Rad Studio’s Run/AttachToProcess to attach to that app’s process. Because the app was built with debug information, I was able to step through the debugger quite well to see where things went wrong. Before continuing I would like to clarify this: I cannot use the local copy of the app on my computer (built for Normal configuration) to do the debugging; I tried doing this, but I can confirm that the error code returned when querying the store is different from that returned the store-installed app; also the local copy is not able to return any of the submitted add-ons when queried, while the store-installed copy can. I’m not surprised by this because the local copy isn’t somehow associated with information submitted to the Windows Store. Just to be sure, I also tried to run the local copy built under the Application Store configuration (that had the same manifest submitted to the store), then attached the debugger to its process, but again that local copy also doesn’t return the right error code. Anyway, after fixing errors that the debugging showed up, I could make another submission of the changes to the Store, then continue testing. But I would like to minimise such submissions as much as possible while I am still working on the app. So the question is: Has anyone found a way to associate their local copy of the app to the Store, so that debugging can continue after making changes to the local copy? The link provided above did say it is something you can do with Visual Studio, but I’ve no idea how this can be achieved with Rad Studio.