I am using SDKbox for Unity to add in app purchases to my app. The products (consumable virtual currency) are added in iTunes and in unity, and when compiled in Xcode, they will execute in sandbox successfully.
But I am having problems assigning the purchased coins to the dataGame file. When I use the code below (updated from SDKbox code) to assign the coins (Insulinium in this game) the app will compile in Xcode, successfully run one in-app purchase, and then freeze without giving the user the purchased coins.
public void onInitialized(bool status)
{
Debug.Log("PurchaseHandler.onInitialized " + status);
}
public void onSuccess(Product product)
{
Debug.Log("PurchaseHandler.onSuccess: " + product.name);
if (product.name == "Insulinium150")
{
dataGame.coin += 150;
}
if (product.name == "Insulinium450")
{
dataGame.coin += 450;
}
if (product.name == "Insulinium1500")
{
dataGame.coin += 1500;
}
uiManager.SetCoin ();
}
I've looked everywhere and the lack of answer makes me think I am missing something pretty easy. I understand the basics of C# and unity coding, but I could have easily overlooked something. Anyone have any thoughts?
What is the best way to add virtual currency to your game file after a successful in-app purchase on iOS?
Related
Now I am deveoping in app purchase in iOS using flutter, but the in app purchase did not support debugging on simulator, so I have to package my app and install in real device and test in app purchase, but the problem is the package take me so much time. And I could not see the log and code workflow so I did not know where is going wrong. So I have to tweak my code and package the apk file(the ci may take me hours) again and again. It make me crazy, is it possible to show the log or let me debbuging in simulator when develop in app purchase in iOS?
how to see the log? I write a rest api and send log info to the server side.
static Future<void> logger(String restLog) async {
RestLogModel restLogModel = RestLogModel();
restLogModel.message = restLog;
Map jsonMap = restLogModel.toMap();
try {
final response = await RestClient.postHttp( "/post/logger/v1/log", jsonMap);
if (response.statusCode == 200 &&
response.data["statusCode"] == "200") {
Map channelResult = response.data["result"];
if (channelResult != null) {
// Pay attention: channelResult would be null sometimes
String jsonContent = JsonEncoder().convert(channelResult);
}
} else {
AppLogHandler.logError(RestApiError('Item failed to fetch.'),
JsonEncoder().convert(response));
}
} on Exception catch (e) {
}
}
It works fine. But every time I change the code, I have to repackage the code and install in real device to verify the change, is there anyway to make it easy? like debugging in app purchase in an emulator?
You can use OS Logs to capture the log statements in the MAC Console app and filter the logs by your category
import os.log
extension OSLog {
private static var subsystem = Bundle.main.bundleIdentifier!
/// Logs the view cycles like viewDidLoad.
static let viewCycle = OSLog(subsystem: subsystem, category: "viewcycle") }
We are using CallKit framework to benefit native usage for Voip features. Users can make Voice and Video Calls in our Messenger App.
But Apple removing CallKit apps from China, because of Chinese government.
What is the best approach for CallKit apps like us for now?
We do not want to remove our app from China and we do not remove all CallKit functionality from our app because of China..
I agree with txulu that it seems that CallKit just needs to be disabled/not used for users in China - see this helpful response on the Apple Developer forums.
The general consensus seems to be that as long as you can explain to App Review how you’re disabling CallKit features for users in China, that should probably be acceptable unless/until Apple publishes specific guidelines.
For your particular problem Ahmet, it sounds like CallKit may provide some of the the core functionality of your app. If this is the case and you really need to support users in China, you might want to look at rebuilding your app using another VOIP framework to make calls (VOIP is still allowed in China...just not using CallKit). Or perhaps you could disable and hide the calling features in your app if the user is in China.
My app was only using CallKit to observe when a call initiated from my app ends, so I was able to devise a work around. For users in China I now observe for the UIApplicationDidBecomeActiveNotification and make my best guess about whether a phone call initiated from the app has ended based on how much time has elapsed since the call began. It's not as good as using CallKit's CXCallObserver, but it seems to work well enough for my purpose.
Update! My app passed App Store review with the fix described.
Submitted a new version yesterday.
Included a short message in the reviewer info section saying "In this version and onwards, we do not use CallKit features for users in China. We detect the user's region using NSLocale."
App was approved around 12hr later without any questions or comments from the App Review team.
Detecting users in China
To determine if a user is in China, I am using NSLocale to get the users' currentLocale and countryCode. If the countryCode contains one of the ISO codes for China (CN, CHN), I set a flag to note I cannot use CallKit and not initialize or use CallKit features in my app.
- (void)viewDidLoad {
[super viewDidLoad];
NSLocale *userLocale = [NSLocale currentLocale];
if ([userLocale.countryCode containsString: #"CN"] || [userLocale.countryCode containsString: #"CHN"]) {
NSLog(#"currentLocale is China so we cannot use CallKit.");
self.cannotUseCallKit = YES;
} else {
self.cannotUseCallKit = NO;
// setup CallKit observer
self.callObserver = [[CXCallObserver alloc] init];
[self.callObserver setDelegate:self queue:nil];
}
}
To test this, you can change the region in Settings > General > Language and Region > Region. When I set Region to 'China' but left language set as English, [NSLocale currentLocale] returned "en_CN".
Swift 5
Utility Functions
func isCallKitSupported() -> Bool {
let userLocale = NSLocale.current
guard let regionCode = userLocale.regionCode else { return false }
if regionCode.contains("CN") ||
regionCode.contains("CHN") {
return false
} else {
return true
}
}
MainViewController
class MainViewController: UIViewController {
...
var callObserver = CXCallObserver()
...
override func viewDidLoad() {
super.viewDidLoad()
if isCallKitSupported() {
callObserver.setDelegate(self, queue: nil)
}
...
}
...
}
Note: countryCode is now regionCode and only returns 'US', 'CN', etc. No language before country code like 'en_CN'.
Swift 5
func isCallKitSupport() -> Bool {
let userLocale = NSLocale.current
if userLocale.regionCode?.contains("CN") != nil ||
userLocale.regionCode?.contains("CHN") != nil {
return false
} else {
return true
}
}
One thing you could try, even though it may not work: disable callkit functionality based on the locale region. This may be enough "proof" that Callkit is disabled for China from the legal perspective in order to be approved for the Appstore. Then your Chinese customers could just switch the region in the settings to get Callkit. This would be already "their" problem so to speak.
Disclaimer: I'm by no means a lawyer or anything, follow this advice at your own risk.
Edit:
CXProvider.isSupported is no longer available: I keep the answer here hoping that it will be restored back on an upcoming iOS 13 release.
From iOS 13 onwards, the correct way to do this is to check the new CXProvider.isSupported property.
Here's the documentation (from Xcode, as the online documentation has not been updated yet):
Go to “Pricing and Availability” in iTunes Connect.
Availability” (Click blue button Edit).
Deselect China in the list “Deselect” button.
Click “Done”.
Wondering if someone could help me with this, or at least point me in the right direction.
I've been searching for documentation on how to get/set settings in a React Native iOS app so that those settings appear in the iOS Settings app listed under my app. I see that there is a Settings API, but it appears that the documentation is not complete. The function definitions are listed there, but that's it. No examples or anything.
Can anyone provide me with a simple example, or point me to a tutorial or something that will help me get going? I'm assuming I import Settings from react-native, just like I would do for other APIs, but beyond that I'm not sure where to go.
Thanks.
As stated in React Native documentation :
Settings serves as a wrapper for NSUserDefaults, a persistent
key-value store available only on iOS.
If you want to add iOS Settings bundle to your app you can use this.
As per Chris Sheffield's comment, here is what I have succeeded with so far:
Add a Settings.bundle to your Xcode project
Highlight the project > File > New > File
Choose "Settings Bundle" > Next
I just left the default name: Settings.bundle
Open the Root.plist file inside of the bundle
Make changes based on Apple's documentation (version I'm referencing is archived here: https://web.archive.org/...)
The important value to keep track of is the item's Identifier
Save, compile, and install the app
You can now use Settings.get('<identifier>') like either of these:
const varName = Settings.get('var_name')
const [ varName ] = useState(Settings.get('var_name'));
Notes
I suggest using some method of watching for changes so that your app updates when the user changes settings while it's running, but these are the only parts required.
I do not suggest letting the user also change those specific settings in-app since that goes against the principle of Single Source of Truth, but it's your app, you do what's best for you and your users.
Hope this plugin will help. react-native-permissions
export const _checkPermission = (permissionName) => {
return Permissions.check(permissionName).then(response => {
if (response === 'denied') {
return false
} else if (response === 'authorized') {
return true
} else if (response === 'restricted') {
return false
} else if (response === 'undetermined') {
return false
}
})
}
also, you can use this for asking permission
_requestPermission = (permissionName) => {
return Permissions.request(permissionName).then(response => {
return response
})
}
export const _alertForPermission = (permissionName) => {
return _requestPermission(permissionName)
}
I'm working on an iOS text to speech app and trying to add an option to use the Alex voice, which is new for iOS 9. I need to determine whether or not the user has downloaded the Alex voice in Settings -> Accessibility. I can't seem to find out how to do this.
if ([AVSpeechSynthesisVoice voiceWithIdentifier:AVSpeechSynthesisVoiceIdentifierAlex] == "Not Found" ) {
// Do something...
}
The reason is the other language voices that are standard, play back at a certain rate, different from the Alex voice. So I have a working app, but if the user hasn't downloaded the voice, iOS automatically defaults to a basic voice, but it plays back at the incorrect rate. If I can detect the voice hasn't been downloaded, I can compensate for the difference and / or advise the user.
OK, so I guess I was overthinking this and thought it was more complicated. The solution was simple.
if (![AVSpeechSynthesisVoice voiceWithIdentifier:AVSpeechSynthesisVoiceIdentifierAlex]) {
// Normalize the speech rate since the user hasn't downloaded the voice and/or trigger a notification that they need to go into settings and download the voice.
}
Thanks to everyone who looked at this and to #CeceXX for the edit. Hope this helps someone else.
Here's one way to do it. Let's stick with Alex as an example:
- (void)checkForAlex {
// is Alex installed?
BOOL alexInstalled = NO;
NSArray *voices = [AVSpeechSynthesisVoice speechVoices];
for (id voiceName in voices) {
if ([[voiceName valueForKey:#"name"] isEqualToString:#"Alex"]) {
alexInstalled = YES;
}
}
// react accordingly
if (alexInstalled) {
NSLog(#"Alex is installed on this device.");
} else {
NSLog(#"Alex is not installed on this device.");
}
}
This method loops through all installed voices and queries each voice's name. If Alex is among them, he's installed.
Other values you can query are "language" (returns a language code like en-US) and quality (1 = standard, 2 = enhanced).
I'm working on a BlackBerry application that uses a MapView.
At the moment, I'm only showing the MapView, nothing more.
This is a snippet from the code I use for it:
public class MapScreen extends MainScreen {
private MapField map;
public MapScreen() {
super(MainScreen.NO_VERTICAL_SCROLL);
map = new MapField();
map.moveTo(new Coordinates(50.847573,4.713135, 0));
add(map);
//...
}
//...
}
I'm using net.rim.device.api.lbs.MapField because I have to be compatible with OS 5.0
On the simulator, everything's fine and it's working.
But the moment I deploy it on the device, I see a white screen...
The device has an internet connection, but only over Wi-Fi. First I was thinking that that was the problem, but according to "Blackberry services that are available over Wi-Fi connections", it shouldn't be a problem.
So, does anybody know why it's not working on the device, and how I can solve this?
Thanks
You say "the device has an internet connection, but only over Wi-Fi" which makes me beleive you don't have the real device provisioned with a BlackBerry data plan. You need that plan in order to access any BlackBerry services, even over Wi-Fi.
To check for an appropriate connection you can use:
if (CoverageInfo.isCoverageSufficient(CoverageInfo.COVERAGE_BIS_B) {
// Connection will support BlackBerry services
} else if (CoverageInfo.isCoverageSufficient(CoverageInfo.COVERAGE_MDS) {
// Connection will support BlackBerry services if BES allows the connection to BIS servers.
}
A better way of checking for this is to check the ServiceBook entries for LBSConfig or variants thereof.
That allows devices that are no longer on a plan, but were once configured by one with LBS, to function properly.
private static final boolean have_lbs() {
ServiceBook sb = ServiceBook.getSB();
ServiceRecord[] records = sb.getRecords();
int count = records.length;
for (int ii = 0; ii < count; ++ii) {
if (records[ii].getCid().toUpperCase().startsWith("LBS"))
return true;
}
return false;
}