RevenueCat: How to get the purchaseToken in iOS - ios

I need the purchaseToken for our server. This field exsits in the Android API,
/**
* Token that uniquely identifies a purchase.
*/
val purchaseToken: String,
And it is received upon purchase. But iOS API of the purchase function below doesn't send it back
func purchase(package: Package) async throws -> PurchaseResultData
When I debug, I see a similar data inside this:
purchaseResultData.customerInfo.allPurchases.first?.value["id"]
But allPurchases is private so I can't get it this way.
Any ideas?

I notice that PurchaseResultData is a typealias for
typealias PurchaseResultData = (transaction: StoreTransaction?,
customerInfo: CustomerInfo,
userCancelled: Bool)
Instead of checking the customerInfo for the purchaseToken, maybe the transaction object has this information as I can see that the type StoreTransaction has a property called productIdentifier
So maybe try
purchaseResultData.transaction.productIdentifier
Update with some other ideas
Maybe try looking at the transaction identifier
Maybe exploring the store kit objects, namely SK1Transaction and SK2Transaction which are properties of StoreTransaction
I have not used this specific API, but it seems like allPurchasedProductIdentifiers is a public property within CustomerInfo so I wonder if this is different from the allPurchases you tried

Related

Swift: Using parsed JSON Data outside of a closure [duplicate]

This question already has answers here:
Returning data from async call in Swift function
(13 answers)
Closed last year.
I am building a mobile app with swift, and am having some syntax issues as I am not a developer. The structure and logic of the application is really rough and surely incorrect, however we just need something that functions. (It is a school project and my team got no devs).
Anyways, we have a MySQL database that will be used as a middleman between our badge server/admin app, and our mobile app. Currently when you go to https://gatekeeperapp.org/service.php , you will see the current database data, taken by a php script and hosted there as JSON. Currently in Swift I have a struct with a function that takes this JSON data, and maps it to variables. The idea is to then pass these pulled variables into a separate set of functions that will check the pulled long/lat against the mobile devices location, and then return whether they match or not. This value would be updated, re-encoded to JSON, and pushed to a web service that would go about changing the values in the database so the badge server could use them.
Where I am currently I can see that values are being pulled and mapped and I can set a variable in a separate function to the pulled value, but then I can only seem to output this value internally, rather than actually use it in the function. I get a type error saying that the pulled values are of type (). How can I properly use these values? Ultimately I think I would want to convert the () to a double, so I could properly compare it to the Long/Lat of the device, and then will need to re-encode the new values to JSON.
Swift Code -- struct function
Swift code -- JSON struct
Swift code -- using pulled data
Your closure is called asynchronously, which means that the outer function where you are expecting to use the values has already returned by the time the closure is called. Instead, you probably need to call some other function from the closure, passing the values you've received.
class MyClass {
func fetchUserData() {
UserData().fetchUser { [weak self] user, error in
DispatchQueue.main.async {
if let user = user {
self?.handleSuccess(userID: user)
} else if let error = error {
self?.handleError(error)
}
}
}
}
private func handleSuccess(userID: String) {
print(userID)
// Do something with userID. Maybe assign it to a property on the class?
}
private func handleError(_ error: Error) {
print(error)
// Handle the error. Maybe show an alert?
}
}

Manual logged screen_view events sometimes have screen_name - (not set)

My project consist of obj-c and swift classes. I use Firebase 7.3.0.
I manually log screen_view event for my screens. I call this method in viewWillAppear or viewDidAppear like this:
#objc class MyAnalyticConstants: NSObject {
static let myScreenName = "AwesomeScreen"
}
class MyViewController: UIViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
MyAnalyticsClass().logScreenViewEvent(name: MyAnalyticConstants.myScreenName)
}
}
class MyAnalyticsClass {
func logScreenViewEvent(name: String, parameters: [String: Any]? = nil) {
var param = [String: Any]()
if let parameters = parameters {
param = parameters
}
param[AnalyticsParameterScreenName] = name
logEvent(name: AnalyticsEventScreenView, parameters: param)
}
func logEvent(name: String, parameters: [String: Any]?) {
analytics.logEvent(name, parameters: parameters)
}
}
I turned off automatic screenview reporting by setting FirebaseAutomaticScreenReportingEnabled to NO (Boolean) in the Info.plist. I use struct with static names for my screens.
However, sometimes I see (not set) value for "screen_view" event inside my google analytics path exploration for production. I can't catch this while using DebugView.
screenshot
I would really appreciate it, if somebody could help me to fix or explain it.
EDIT:
I swizzled firebase method
+ (void)xxx_logEventWithName:(NSString *)name
parameters:(nullable NSDictionary<NSString *, id> *)parameters {
[self xxx_logEventWithName:name parameters:parameters];
if ([name isEqualToString:#"screen_view"] && [parameters[#"screen_name"] length] <= 3) {
NSLog(#"%#", #[][1]);
}
and jumped through app during 30 min. I didn't catch up crash. Any other ideas?
Ok, there are a few things you could typically do in this case.
I wrote it as an afterthought, but it's something you should make sure of before doing technical debugging that follows: you should go to your analytics property/view and debug the filters. Maybe you have replacing filters interfering with your values, but I presume you checked your data in a full and unfiltered view where your app is the sole "stream" of data. This is important. The bug can come from a different app to this property, so either make sure you're the only source, or make sure you filter out other sources/apps/platforms.
Check your logEvent function calls where you either send AnalyticsEventScreenView or just the "screen_view" string as the first parameter. You see how the Firebase lib uses one function to send all kinds of events? They now treat screenviews as events. Which has its elegancy, but also may lead to unintended mistakes. Check what the globals actually mean in here: https://github.com/firebase/firebase-cpp-sdk/blob/0c8c8b29bc2d62d66c6ac49ff2c3fb04f815a687/analytics/ios_headers/FIREventNames.h
Check your logScreenViewEvent function calls. Pay attention to cases when you pass the first parameter as a variable. Also make sure you're never setting the AnalyticsParameterScreenName, which is also known as a string "screen_name" from here: https://github.com/firebase/firebase-cpp-sdk/blob/0c8c8b29bc2d62d66c6ac49ff2c3fb04f815a687/analytics/ios_headers/FIRParameterNames.h in the parameters dictionary, the second attribute. Cuz setting it there will effectively overwrite whatever is the first attribute you're setting. I actually usually suggest having only one argument for the screenview function declaration, especially to avoid collisions like this.
Oh, almost forgot. Make sure you ALWAYS use your neat MyAnalyticsClass wrapper and never call the native logEvent(). I would just check all files where you include the Firebase sdk and see if it should be replaces with the wrapper.
Finally, if the above didn't help, you can insert the check in both your function wrappers to throw an error whenever the event name equals to "screen_view" and the "screen_name" parameter's length not more than 2 character (I'm just trying to include all falsy values, so null, undefined, nil, whatever). And run unit tests or even better - regression testing with things set like that. Well, or manually test it out, watching for the errors in the console rather than the web debugger.

HealthKit requestAuthorization for correlationType(forIdentifier: .bloodPressure)

I am trying to read bloodPressure data from HealthKit and I wonder why I have to ask for .bloodPressureSystolic and .bloodPressureDiastolic instead of .bloodPressure
What I am trying to do is asking
requestAuthorization(toShare: nil, read: dataTypesToRead, completion: { ... })
where dataTypesToRead = HKObjectType.correlationType(forIdentifier: .bloodPressure) (ps: this is just for simplicity, correlationType(forIdentifier: ) should be unwrapped)
and the app crashes. So I am guessing that one cannot requestAuthorization for correlationType(forIdentifier: )
if I use HKObjectType.quantityType(forIdentifier:) or .categoryType(forIdentifier:) then requestAuthorization works perfectly, also with .bloodPressureSystolic and .bloodPressureDiastolic
If my guess is correct then why Apple says:
typesToRead
A set containing the data types you want to read. This set can contain any concrete subclass of the HKObjectType class (any of the HKCharacteristicType , HKQuantityType, HKCategoryType, HKWorkoutType or HKCorrelationType classes). If the user grants permission, your app can read these data types from the HealthKit store.
https://developer.apple.com/documentation/healthkit/hkhealthstore/1614152-requestauthorization
is my guess correct? why I can't ask permission for HKObjectType.correlationType(forIdentifier: .bloodPressure) ??
HealthKit doesn't require authorization for correlation types because your app can only query for correlations with member objects that it is authorized to read. It seems like the documentation is just a little misleading when it mentions HKCorrelationType classes.

get mediaSessionID for the current media playing on chrome cast in Swift on iOS

I'm referring to https://developers.google.com/cast/docs/reference/ios/interface_g_c_k_media_status.html#a45e3eb39e674f5d0dbfd78deef77a1e6
that helps me with the api, but the initializer for the GCKMediaStatus class says:
- (instancetype) initWithSessionID: (NSInteger) mediaSessionID
mediaInformation: (GCKMediaInformation *) mediaInformation
note: this is in Objective-c syntax but Swift works just the same except in Swift language...
Nonetheless I can't seem to figure out how to retrieve the mediaSessionID to be able to initialize an instance of this class to a new variable.
I'm trying to do the following to get me eventually to the method within this class called streamPosition which would go like this:
var mediaStatus = GCKMediaStatus(sessionID: Int, mediaInformation: GCKMediaInformation!)
var currentStreamPosition = mediaStatus.streamPosition()
where Int would be the mediaSessionID NOT the sessionID of the chrome cast (read the additional section below!!) and GCKMediaInformation! would be an instance of the GCKMediaInformation class. (I think) correct me if I'm wrong on either of those parameters.
Then I could use this data. But when I do this the currentStreamPosition I suppose defaults to 0 and thats what I get when I print to the currentStreamPosition variable.
Note: I've already connected to the current playing media and I am able to pause, play, and seek to an arbitrary number within the stream. This all works. So I now I'm connected and everything else works.
use case: I want to be able skip ahead 15 seconds or rewind 15 seconds etc. with the use of this method, but I haven't found anything to help.
also - don't get sessionID confused with mediaSessionID!! I CAN get the sessionID successfully and print it out. My issue is with the mediaSessionID.
additional info: the autocomplete is Xcode says this is the parameters labeled names:
GCKMediaStatus(sessionID: Int, mediaInformation: GCKMediaInformation!)
note the first parameter says sessionID and it is of type int. But on https://developers.google.com/cast/docs/ios_sender if you notice sessionID is of type String! (an optional String).
I think this label was mis-named in Xcode for the autocomplete. I think it should be named mediaSessionID and NOT sessionID since this is what the documentation shows on the first link I provided.
Any help would be much appreciated.
Thanks!
To get the stream position, use the method approximateStreamPosition on GCKMediaControlChannel.

For plug in running on iOS

What I want to implement is as follow:
A-app (calling app) : request the return value of a-string sent as parameter : request(a-string) -> b-string.
B-app (plug-in installed separately by me or others, it plays the role of dictionary or database ) : search a-string from database and return the result (b-string).
With successful experiences of plug-in on android and with Apple's confident rhetoric of plug-in, I thought plug-in, of course, run on iOS. After a lot of hard work, however, I finally found out:
* Note : The creation and use of loadable bundles is not supported in iOS.*
Nonetheless, not giving up, I finally made it with custom URl and pasteboard:
A-app : write a-string and false state to pasteboard & call B-app via custom URL.
B-app : viewDidLoad runs following func and thereafter exit program ; func { read pasteboard and search from database & write the result(b-string) and true state to pasteboard }
A-app : while-loop detects whether state is false or true. if true, catch b-string from pasteboard.
Anyway it works but it's too long thus almost useless. Do you have any idea for better solutions? Why doesn't Apple allow plug-in for iOS? Any responses are welcome. Thank you.
I can't answer why Apple doesn't allow plug-ins, but I can offer some advice on what you're trying to achieve.
The common pattern for sending data back to your application is to implement a callback url, so the A-app would also implement a custom URI and add that to the uri sent to B-app.
B-app would then process the uri as you have already implemented, but then instead of exiting, it simply sends the data you requested in the uri passed to it.
See http://x-callback-url.com for more details and example implementations.

Resources