Thanks again Rob! To follow up - I’ve found the store module works when I build to my device(s), but I’m not able to test in the Simulator as I had in previous builds.
Any luck yet? If you have any other ideas on how I can help debug the issue, please let me know.
I apologize guys. I’m very very confused about what your actual problem is now. You cannot test the store.* API calls in the simulator. OS-X/Windows does not have the requisite SDKs necessary to talk to the stores for devices. You must do this on device.
Now @-Garet-, your OP said you were getting an error on the device, which you should not be getting. Focusing on @-Garet-, you said above that our sample app does run on device, so this means the problem is somewhere in your code. Would it be possible for you to post your magstore.lua file?
You probably should also get a table printing function (like print_r) from the community code and print out (i.e. dump) your store table where you are getting that error and right after you do the require statement and see what messages show up in the console log.
Rob
Here’s the magstore.lua file: http://www.pasteall.org/47938/lua
I hope you can figure something out! This has been stumping me for days now.
I see several potential “gotchas”. First you are using a lot of global variables and functions. As a rule if there isn’t the word local in front of a variable or function the first time it’s used, its global. If you use the same names in multiple scenes they will stomp on each other. The exception to this is things like:
function scene:createScene( event )
Which is adding a function named createScene() to the table/object name scene. The object named “scene” is typically declared as a local at the top of a storyboard module. Which leads me to the next issue.
It appears you are using a storyboard scene, though I don’t see where you’re requiring the storyboard module, though it’s probably a global somewhere else.
Anyway, things that live outside of one of the scene functions (createScene, enterScene, etc. only execute once when the scene is loaded the first time. So your store.init() will only fire once and when you re-enter the scene it does not run again, unless you have called removeScene() somewhere. This itself may not be a big deal, but other blocks of code like this:
print("Active: "..tostring(store.isActive)) if ( store.isActive ) then print("Activated store!") if ( store.canLoadProducts ) then print("Can load products!") store.loadProducts( arrayOfProductIdentifiers, loadProductsCallback ) else --this store does not support an app fetching products end end
I appreciate you going over it. I understand the potential risk of using globals, but I’m never overwriting the store module. I use globals sometimes because the app has multiple views, in which I carry some values from one scene to another via globals. May not be the absolute most ideal solution, but it hasn’t given me problems so far. I also load all my modules in the main.lua file as globals, bringing them to all my other scenes. I’m an avid believer in DRY programming standards, so I avoid repeating myself if at all possible.
The thing I can’t explain, is why the store module is initializing inside the simulator (albeit returning that it “can’t be loaded in the simulator”), but as soon as it’s brought the the device, it returns errors saying it can’t find the library. *That* is what I believe the real issue is. What is causing it though, I can’t seem to figure out.
Well we should have a discussion about the benefits of DRY vs. the issues with Globals and if passing data is the main reason, we have a non-global way of dealing with it.
But this problem… On the simulator we have to support the API calls to a point your app won’t crash. So the calls are there and just print messages letting you know that you can’t do this on the sim. Now when you get on device, you are now actually using the libraries and if there are problems, they will now show up.
But as I said, your code is actually running store.* calls before you init it. Any way, you said the error was on line 10, but line 10 in what you posted can’t generate the error. So with the code you posted, where is the error?
I’ve been continually debugging my code since the original post, so the lines may of shifted around. Anyway, I did a fresh build. This time making sure that my init happens before any other declaration/call of store functions.
The top of my file is now:
local store = require( "store" ) store.init('apple', storeTransaction) downloading = false download\_queue = {} loadingIndex = false loadingViewer = false created = false downloadedStatus = false loadingFlags = false failedIssues = 0 --Create a storyboard scene for this module local scene = storyboard.newScene() local function loadProductsCallback( event ) local validProducts = event.products local invalidProducts = event.invalidProducts print( "Valid Products:", #validProducts ) for i = 1,#validProducts do local currentItem = validProducts[i] print( currentItem.title ) print( currentItem.description ) print( currentItem.price ) print( currentItem.productIdentifier ) native.showAlert("MSG", currentItem.title..currentItem.description..currentItem.price..currentItem.productIdentifier, { "Ok" }) end print( "Invalid Products:", #invalidProducts ) for i = 1,#invalidProducts do native.showAlert("MSG", "Invalid product!", { "Ok" }) end end local arrayOfProductIdentifiers = { "org.caringmagazine.salvnfcaringapp.caringvol19no1" } print("Active: "..tostring(store.isActive)) if ( store.isActive ) then print("Activated store!") if ( store.canLoadProducts ) then print("Can load products!") store.loadProducts( arrayOfProductIdentifiers, loadProductsCallback ) else --this store does not support an app fetching products end end local function storeTransaction( event ) local transaction = event.transaction if ( transaction.state == "purchased" ) then --handle a successful transaction here print( "productIdentifier", transaction.productIdentifier ) print( "receipt", transaction.receipt ) print( "signature:", transaction.signature ) print( "transactionIdentifier", transaction.identifier ) print( "date", transaction.date ) native.showAlert("MSG", "Transaction successful!", { "Ok" }) elseif ( transaction.state == "cancelled" ) then native.showAlert("MSG", "Cancelled transaction!", { "Ok" }) elseif ( transaction.state == "failed" ) then native.showAlert("MSG", "Failed transaction!", { "Ok" }) end --tell the store that the transaction is complete! --if you're providing downloadable content, do not call this until the download has completed store.finishTransaction( event.transaction ) end
and when build and placed on the device, the console reads:
Dec 11 16:38:32 iPad SA\_Prototype[422] \<Warning\>: Runtime error ...ts/SA\_App/SA\_Prototype/magstore.lua:2: attempt to call field 'init' (a nil value) stack traceback: [C]: ? [C]: in function 'error' ?: in function 'gotoScene' ...ojects/SA\_App/SA\_Prototype/main.lua:383: in main chunk Dec 11 16:38:32 iPad SA\_Prototype[422] \<Warning\>: Runtime error stack traceback: [C]: ? [C]: in function 'error' ?: in function 'gotoScene' ...ojects/SA\_App/SA\_Prototype/main.lua:383: in main chunk
So, still the same error. And as you can see, this is happening on line 2, where the init happens. It’s almost as if the library isn’t getting copied in the bundle for some reason.
Although I’m sure it would have no impact on the bug, would it be better practice to initialize the store in the initial loading of the scene, or is it just fine getting one-shot loaded at the top of the file?
If you create a new project with a single main.lua file with it’s contents being only the require and init functions… do you get the error?
If the answer is no, then the store variable must be getting overridden somewhere in your main project. I would strongly suggest using this: http://metalua.luaforge.net/src/lib/strict.lua.html
I’ll test it now, but I don’t see HOW that’s even possible… Did you see the code I posted? I’m literally declaring the store variable as the library on the first line, and calling init on the very second line. Now I’m not a lua expert, but in most programming languages, if there did happen to be a global variable named store (which there’s not…), the local declaration would override it in the local scope. Again, this may be different in lua than most languages.
I saw your code above, but wasn’t sure if it was your main.lua file or not. Of course if it was your main.lua file and you were doing the store require line at the very top, then on the next line calling init, there isn’t any chance of a variable overriding it at that stage.
I’ve only skimmed though the thread, so please accept my apologies if you have tried my advice already.
Sometimes it helps to take a step back and go back to basics in order to figure out what is going on sometimes, believe me, I know
It’s cool, heh. I’ve just been pulling my hair with this issue off and on for a little while now. And no, it wasn’t my main.lua file; but my main.lua is very short and definitely has no declaration of ‘store’. I feel like it’s gotta be something stupid that I did in my code somewhere, but the more I look, the less I find.
If thats the case then it’s possible it’s being overriden.
For example, this breaks the store module: (try it and see), it can’t find any table method named “init” because store was redeclared as a new table
local store = require( "store" ) store = {} store.init( "apple", function() end )
So it’s really worth trying out my idea, to rule out the possibility of this being the case once and for all…
Put the code in the example section here: http://docs.coronalabs.com/daily/api/library/store/init.html
Into a new folder and name it main.lua
Run it and let me know the outcome.
Keep in mind that if you require a module in one scene, Lua loads it. If you require it in another module, you just get a reference to what was originally loaded. If something has overwritten what was there before, you’re going to get a reference to whatever overwrote it.
You can do a:
print(type(store)) right before you call .init() and see what type it is. It should return “table”. If it returns table, you do a
for k, v in pairs(store) do
print(k,":",v)
end
and see if you see the rest of the object there (purchase, restore, loadProducts, etc). It would be help to search for “store” in your other .lua files and see if you’re referencing it somewhere else.
Rob
To add on to what Rob says, there is a quick and easy way to see every instance of “store” in your code.
-
Open a terminal window and cd to your projects directory.
-
Type in the following command: "grep -lr “store” *
It will show you every file in which it found an instance of “store”
Hope this helps
Are you overwriting the store variable anywhere along the way? Are there any other errors in the console log?
Thanks
Rob
Not being overridden anywhere, and if that were the case, it would definitely not get activated in the simulator.
In the simulator it outputs:
WARNING: The store API is not supported on this platform
and also when I run print("Active: "…tostring(store.isActive)), I get
Active: false
In the simulator. On the device it returns nil.
@Garet, I’ve been following this post and am wondering if you could a resolution/cause for this issue?
I actually did, and it’s something that was hidden in plain sight. In my original prototype for the app, the magstore.lua file was called store.lua. This file was left alone in my project while I migrated to a new store framework I developed for the store view. Because I was no longer using the file, I never had it opened inside my project editor. I didn’t even realize the file still existed till I was browsing through the hundred or so assets in my folder and stumbled across the “store.lua” file. I instantly let out the most epic of face-palms and quickly remove the file.
Low and behold, my project now builds and runs without a problem. I wish there could be more comprehensive error reporting saying exactly what library file was trying to be loaded (in this case project_dir/store.lua rather than the included store.lua library). It would have definitely saved a lot of head scratching and debugging in all the wrong places, but I understand that it’s hard to outline every possible cause for an error in the console. It was more my error than anything else, but I guess we just have to pick up and learn from mistakes.
Thanks everyone for the help and wise words to “step back and go back to basics”. Sometimes the most annoying of errors are hiding in plain sight.
Awesome! I’m glad my advice helped.
In future, you can code against such things. Generally these issues turn out to be so simple, that you want to bash your head against the desk for wasting hours on it. Believe me, i’ve been there more than I would like to admit.
This is the strategy I would employ from here on out. In your code that requires the store module (or better yet, create your own wrappers/libraries for this stuff) do something like this:
-- Module example local M = {} local store = require( "store" ) function M:init( store, options ) if "function" == type( store.init ) then -- do stuff else print( "Store: store.init function not found. Check for issues" ) end end return M -- End module example -- Your current code example: local store = require( "store" ) if "function" == type( store.init ) then -- do stuff else print( "Store: store.init function not found. Check for issues" ) end
At least that way you get a heads up
You could also create a text file of reserved file names and do a directory scan where your files reside (in your lua code) and check if you chose a file name that is on the reserved list.
There are plenty of approaches to safeguard you from this, the choice is yours.
Happy coding!