Hello!
As the title says, how do I make the transition from the old Parse framework to Parse Swift seamlessly (Authentication wise)?
As of now, the only way I see possible is force users to re-login and is not really a viable solution, since we have a lot of users. Is there any way to either link the old user with a Parse Swift one?
Thereās currently no conversion from PFUser (objective-c SDK) to ParseUser (Swift SDK) in the Keychain as well as most of PFInstallation. The Swift SDK can get installationId from the Objective-C SDK. If you or someone else would like to build this feature feel free to have the discussion and eventually open a PR.
A number of developers have switched to the Swift SDK. Maybe they can chime in with how they converted over and if they created custom solutions.
1 Like
enodev
August 1, 2022, 11:22am
3
Given the fact that legacy Objective-C SDK is not actively maintained and is only accumulating problems over time, it may be really a good time to start thinking how to provide seamless transition into the Swift based SDK.
I am one of the guys still actively shipping apps with Objective-C SDK, now with a custom Swift Package Manager support built into my forked copy (https://github.com/mman/Parse-SDK-iOS-OSX/tree/spm ). But in order to keep the build clean I am simply removing broken features from the SDK instead of fixing them properly - and Iād love to switch to Swift SDK, but until the migration path is clean for already signed in users this will not be possibleā¦
So it will be a good story for Swift SDK to provide clean migration path. I will be happy to help from September!
Martin
danipralea:
how do I make the transition from the old Parse framework to Parse Swift seamlessly (Authentication wise)?
As of now, the only way I see possible is force users to re-login and is not really a viable solution, since we have a lot of users. Is there any way to either link the old user with a Parse Swift one?
Iāve opened a PR that will allow developers to login their users using the sessionToken
from the Objective-C SDK. The added methods will retrieve the sessionToken automatically from the Objective-C SDK Keychain:
parse-community:main
ā cbaker6:migrateObjC
opened 09:52PM - 17 Aug 22 UTC
### New Pull Request Checklist
<!--
Please check the following boxes [x] bā¦ efore submitting your issue.
Click the "Preview" tab for better readability.
Thanks for contributing to Parse Platform!
-->
- [x] I am not disclosing a [vulnerability](https://github.com/parse-community/Parse-Swift/security/policy).
- [x] I am creating this PR in reference to an [issue](https://github.com/parse-community/Parse-Swift/issues?q=is%3Aissue).
### Issue Description
Currently there's no way to migrate from the [Objective-C SDK](https://github.com/parse-community/Parse-SDK-iOS-OSX) to the Swift SDK. This enforces developers who convert to have to ask users to log into applications again after migration. More discussion in [here](https://community.parseplatform.org/t/how-to-transition-from-the-old-parse-framework/2807).
Related issue: #n/a
### Approach
Use the session token (assuming it's still valid) from an already logged in user in the Objective-C SDK Keychain to **become/login** into the Swift SDK. This allows a seamless migration/login without interrupting the application flow and does not modify the original Objective-C Keychain or the original session token. There is no need to have the Parse Objective-C SDK as a framework in your project as the Swift SDK doesn't need it to access the keychain since the keychain belongs to the respective app and can be access via the `bundleId`. The methods introduced to allow this are:
#### ParseUser
```swift
/**
Logs in a `ParseUser` *asynchronously* using the session token from the Parse Objective-C SDK Keychain.
Returns an instance of the successfully logged in `ParseUser`. The Parse Objective-C SDK Keychain is not
modified in any way when calling this method; allowing developers to revert their applications back to the older
SDK if desired.
- parameter options: A set of header options sent to the server. Defaults to an empty set.
- parameter callbackQueue: The queue to return to after completion. Default
value of .main.
- parameter completion: The block to execute when completed.
It should have the following argument signature: `(Result<Self, ParseError>)`.
- note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer
desires a different policy, it should be inserted in `options`.
- warning: When initializing the Swift SDK, `migratingFromObjcSDK` should be set to **false**
when calling this method.
*/
public func loginUsingObjCKeychain(options: API.Options = [],
callbackQueue: DispatchQueue = .main,
completion: @escaping (Result<Self, ParseError>) -> Void)
/**
Logs in a `ParseUser` *asynchronously* using the session token from the Parse Objective-C SDK Keychain.
Returns an instance of the successfully logged in `ParseUser`. The Parse Objective-C SDK Keychain is not
modified in any way when calling this method; allowing developers to revert their applications back to the older
SDK if desired.
- parameter options: A set of header options sent to the server. Defaults to an empty set.
- returns: Returns the logged in `ParseUser`.
- note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer
desires a different policy, it should be inserted in `options`.
- warning: When initializing the Swift SDK, `migratingFromObjcSDK` should be set to **false**
when calling this method.
*/
@discardableResult func loginUsingObjCKeychain(options: API.Options = []) async throws -> Self
/**
Logs in a `ParseUser` *asynchronously* using the session token from the Parse Objective-C SDK Keychain.
Publishes an instance of the successfully logged in `ParseUser`. The Parse Objective-C SDK Keychain is not
modified in any way when calling this method; allowing developers to revert their applications back to the older
SDK if desired.
- parameter options: A set of header options sent to the server. Defaults to an empty set.
- returns: A publisher that eventually produces a single value and then finishes or fails.
- note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer
desires a different policy, it should be inserted in `options`.
- warning: When initializing the Swift SDK, `migratingFromObjcSDK` should be set to **false**
when calling this method.
*/
func loginUsingObjCKeychainPublisher(options: API.Options = []) -> Future<Self, ParseError>
```
#### ParseInstallation
```swift
/**
Migrates the `ParseInstallation` *asynchronously* from the Objective-C SDK Keychain.
- parameter copyEntireInstallation: When **true**, copies the
entire `ParseInstallation` from the Objective-C SDK Keychain to the Swift SDK. When
**false**, only the `channels` and `deviceToken` are copied from the Objective-C
SDK Keychain; resulting in a new `ParseInstallation` for original `sessionToken`.
Defaults to **true**.
- parameter options: A set of header options sent to the server. Defaults to an empty set.
- parameter callbackQueue: The queue to return to after completion. Default value of .main.
- parameter completion: The block to execute.
It should have the following argument signature: `(Result<Self, ParseError>)`.
- note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer
desires a different policy, it should be inserted in `options`.
*/
func migrateFromObjCKeychain(copyEntireInstallation: Bool = true,
options: API.Options = [],
callbackQueue: DispatchQueue = .main,
completion: @escaping (Result<Self, ParseError>) -> Void)
/**
Deletes the Objective-C Keychain along with the Objective-C `ParseInstallation`
from the Parse Server *asynchronously* and executes the given callback block.
- parameter options: A set of header options sent to the server. Defaults to an empty set.
- parameter callbackQueue: The queue to return to after completion. Default
value of .main.
- parameter completion: The block to execute when completed.
It should have the following argument signature: `(Result<Void, ParseError>)`.
- warning: It is recommended to only use this method after a succesfful migration. Calling this
method will destroy the entire Objective-C Keychain and `ParseInstallation` on the Parse
Server.
- note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer
desires a different policy, it should be inserted in `options`.
*/
func deleteObjCKeychain(options: API.Options = [],
callbackQueue: DispatchQueue = .main,
completion: @escaping (Result<Void, ParseError>) -> Void)
/**
Migrates the `ParseInstallation` *asynchronously* from the Objective-C SDK Keychain.
- parameter copyEntireInstallation: When **true**, copies the
entire `ParseInstallation` from the Objective-C SDK Keychain to the Swift SDK. When
**false**, only the `channels` and `deviceToken` are copied from the Objective-C
SDK Keychain; resulting in a new `ParseInstallation` for original `sessionToken`.
Defaults to **true**.
- parameter options: A set of header options sent to the server. Defaults to an empty set.
- returns: Returns saved `ParseInstallation`.
- throws: An error of type `ParseError`.
- note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer
desires a different policy, it should be inserted in `options`.
*/
@discardableResult func migrateFromObjCKeychain(copyEntireInstallation: Bool = true,
deleteObjectiveCKeychain: Bool = false,
options: API.Options = []) async throws -> Self
/**
Deletes the Objective-C Keychain along with the Objective-C `ParseInstallation`
from the Parse Server *asynchronously*.
- parameter options: A set of header options sent to the server. Defaults to an empty set.
- returns: Returns saved `ParseInstallation`.
- throws: An error of type `ParseError`.
- warning: It is recommended to only use this method after a succesfful migration. Calling this
method will destroy the entire Objective-C Keychain and `ParseInstallation` on the Parse
Server.
*/
func deleteObjCKeychain(options: API.Options = []) async throws
/**
Migrates the `ParseInstallation` *asynchronously* from the Objective-C SDK Keychain
and publishes when complete.
- parameter copyEntireInstallation: When **true**, copies the
entire `ParseInstallation` from the Objective-C SDK Keychain to the Swift SDK. When
**false**, only the `channels` and `deviceToken` are copied from the Objective-C
SDK Keychain; resulting in a new `ParseInstallation` for original `sessionToken`.
Defaults to **true**.
- parameter options: A set of header options sent to the server. Defaults to an empty set.
- returns: A publisher that eventually produces a single value and then finishes or fails.
- note: The default cache policy for this method is `.reloadIgnoringLocalCacheData`. If a developer
desires a different policy, it should be inserted in `options`.
*/
func migrateFromObjCKeychainPublisher(copyEntireInstallation: Bool = true,
options: API.Options = []) -> Future<Self, ParseError>
/**
Deletes the Objective-C Keychain along with the Objective-C `ParseInstallation`
from the Parse Server *asynchronously* and publishes when complete.
- parameter options: A set of header options sent to the server. Defaults to an empty set.
- returns: A publisher that eventually produces a single value and then finishes or fails.
- warning: It is recommended to only use this method after a succesfful migration. Calling this
method will destroy the entire Objective-C Keychain and `ParseInstallation` on the Parse
Server.
*/
func deleteObjCKeychainPublisher(options: API.Options = []) -> Future<Void, ParseError>
```
Note that logging in via the linked methods is the proper way to migrate to the Swift SDK as the SDK's themselves are entirely different and the Swift SDK should hydrate the `_User` and `_Installation` `ParseObject`'s from the server instead of relying on the Objective-C SDK.
Developers should also ensure the latest `PFUser` and `PFInstallation` is saved to their Parse Server before migrating a user using these new methods. After initializing the SDK, migration should look something like:
```swift
struct User: ParseUser {
//: These are required by `ParseObject`.
var objectId: String?
var createdAt: Date?
var updatedAt: Date?
var ACL: ParseACL?
var originalData: Data?
//: These are required by `ParseUser`.
var username: String?
var email: String?
var emailVerified: Bool?
var password: String?
var authData: [String: [String: String]?]?
//: Your custom keys.
var customKey: String?
//: Implement your own version of merge
func merge(with object: Self) throws -> Self {
var updated = try mergeParse(with: object)
if updated.shouldRestoreKey(\.customKey,
original: object) {
updated.customKey = object.customKey
}
return updated
}
}
struct Installation: ParseInstallation {
//: These are required by `ParseObject`.
var objectId: String?
var createdAt: Date?
var updatedAt: Date?
var ACL: ParseACL?
var originalData: Data?
//: These are required by `ParseInstallation`.
var installationId: String?
var deviceType: String?
var deviceToken: String?
var badge: Int?
var timeZone: String?
var channels: [String]?
var appName: String?
var appIdentifier: String?
var appVersion: String?
var parseVersion: String?
var localeIdentifier: String?
//: Your custom keys
var customKey: String?
//: Implement your own version of merge
func merge(with object: Self) throws -> Self {
var updated = try mergeParse(with: object)
if updated.shouldRestoreKey(\.customKey,
original: object) {
updated.customKey = object.customKey
}
return updated
}
}
// Initialize Swift SDK in the same place you use to initialize the Objective-C SDK
// ....
// Place this code in the entry point of your app where you typically check if the Parse User is logged in
if User.current == nil {
do {
// Automatically migrate the user
let migratedUser = try await User.loginUsingObjCKeychain()
// Automatically migrate the installation
let migratedInstallation = try await Installation.migrateFromObjCKeychain()
// Sometime after you verified Migrations were successful, can delete old Keychain
// Note, you don't need to delete the Keychain.
let migratedInstallation = try await Installation.deleteObjCKeychain()
} catch {
print("Migration unsuccessful: \(error.localizedDescription)")
}
}
```
### TODOs before merging
<!--
Add TODOs that need to be completed before merging this PR.
Delete TODOs that do not apply to this PR.
-->
- [x] Add tests
- [x] Add entry to changelog
- [x] Add changes to documentation (guides, repository pages, in-code descriptions)
Depending how a developer implements the added methods into their app, the migration can happen without requiring the user to input or change anything assuming an internet connection is available.
Manuel
August 18, 2022, 9:45pm
6
I think what developers are mostly looking for is a single, clear step-by-step migration instruction article.
The article should contain:
Clarify the future plans regarding the ObjC SDK (i.e. deprecation); explain why we are currently proving two Parse SDKs for the same ecosystem and why migration should be done
Which benefits are there when migrating to the Swift SDK, i.e. some marketing to encourage migration
Which caveats are there when using the Swift SDK, e.g. not feature but performance related
Which features are available in the ObjC SDK but unavailable in the Swift SDK, i.e. the developer is required to code around a non-Parse API to achieve the same functionality; with code examples for how to achieve the same functionality; a simple SDK feature comparison table would be very useful
Which features work out-of-the-box in the ObjC SDK but require a manual workaround / coding / setup in the Swift SDK; with detailed code examples on how to duplicate a default implement of the existing functionality in ObjC SDK
Hopefully you find people to work on the tasks youāve listed. In addition, maybe you can get them to also work on a āFirebase to ParseSwiftā article, similar to:
https://firebase.google.com/support/guides/parse-ios
Along with:
opened 05:51PM - 21 Jan 22 UTC
type:docs
### New Feature / Enhancement Checklist
<!--
Check all of the following boā¦ xes [x] before submitting your issue.
Click the "Preview" tab for better readability.
Thanks for contributing to Parse Swift!
-->
- [x] I am not disclosing a [vulnerability](https://github.com/parse-community/Parse-Swift/security/policy).
- [x] I am not just asking a [question](https://github.com/parse-community/.github/blob/main/SUPPORT.md).
- [x] I have searched through [existing issues](https://github.com/parse-community/Parse-Swift/issues?q=is%3Aissue).
### Current Limitation
The SDK supports [DocC Interactive Tutorials](https://developer.apple.com/documentation/docc/tutorial-syntax), but currently has no interactive tutorials. These tutorials can be used to teach developers how to use the SDK.
All of the current tutorials are in [Swift Playgrounds](https://github.com/parse-community/Parse-Swift/tree/main/ParseSwift.playground/Pages).
### Feature / Enhancement Description
Use the [Swift Playgrounds](https://github.com/parse-community/Parse-Swift/tree/main/ParseSwift.playground/Pages) as a baseline to make Interactive tutorials. These tutorials should include images/screenshots. This is useful when:
1. A developer wants view our tutorials online at https://parseplatform.org/Parse-Swift/release/tutorials/parseswift/
2. A developer depends on the Parse-Swift SDK using SPM and goes to "Xcode->Product->Build Documentation" in their project locally
![image](https://user-images.githubusercontent.com/8621344/151708437-0dda21cd-76b7-4de3-91d9-8220404a6756.png)
The initial tutorial file that should be edited is here: https://github.com/parse-community/Parse-Swift/blob/main/Sources/ParseSwift/Documentation.docc/Your%20First%20Object.tutorial
The Swift Playgrounds should be left intact as they give developers a real way to test the SDK against a Parse Server. Feel free to tackle individual tutorials in separate PR's. You can leverage text from the original [Objective-C SDK guide](https://docs.parseplatform.org/ios/guide/). Much of the Swift Playground code uses completion handlers for asynchronous code. It will be beneficial to write most of the code in the tutorial using `try await` instead.
Tutorials with a checkbox have already been converted from their [Swift Playgrounds counterparts](https://github.com/parse-community/Parse-Swift/tree/main/ParseSwift.playground/Pages):
- [ ] 1 - Your first Object
- [ ] 2 - Finding Objects
- [ ] 3 - User - Sign Up
- [ ] 4 - User - Continued
- [ ] 5 - ACL
- [ ] 6 - Installation
- [ ] 7 - GeoPoint
- [ ] 8 - Pointers
- [ ] 9 - Files
- [ ] 10 - Cloud Code
- [ ] 11 - LiveQuery
- [ ] 12 - Roles and Relations
- [ ] 13 - Operations
- [ ] 14 - Config
- [ ] 15 - Custom ObjectId
- [ ] 16 - Analytics
- [ ] 17 - SwiftUI - Finding Objects
- [ ] 18 - SwiftUI - Finding Objects With Custom ViewModel
- [ ] 19 - SwiftUI - LiveQuery
### Example Use Case
Completed tutorials will look like: https://developer.apple.com/tutorials/swiftui
### Videos for Designing DocC Tutorials
- [Build interactive tutorials using DocC](https://developer.apple.com/videos/play/wwdc2021/10235/)
### Alternatives / Workarounds
Solely depend on Playground implementations for demonstrating how to use SDK.
### 3rd Party References
https://developer.apple.com/tutorials/swiftui
Manuel
August 18, 2022, 11:04pm
8
These are challenges and questions developers face when migrating, so maybe we can encourage people who are asking for migration advice (and the people who provide it) to add some bits to these instructions.
I just took a look at the Swift SDK docs (which just link to README ) and couldnāt find any section about migration.
You may have the best overview of what GitHub issues / forum threads there are already regarding that topic. What we can use as a basis?
Manuel
August 19, 2022, 12:08am
9
Iāve started an a migration guide in https://github.com/parse-community/Parse-Swift/pull/392 . The purpose is to publish it in an early (explicitly incomplete) version ASAP to make the document available for others to contribute.
cbaker6
August 26, 2022, 12:58am
10
@danipralea @enodev released some features that should help in the latest version:
bcbeta
September 7, 2022, 2:52am
11
When migrating the user from objC I am getting the following error:
ParseError code=-1 error=Could not find a session token in the Parse Objective-C SDK Keychain.
Code should be pretty straightforward?
User.loginUsingObjCKeychain(completion: { result in
print(result)
})
Any tips on troubleshooting this?
cbaker6
September 7, 2022, 3:08am
12
Most likely a bug in the Swift SDK as I never tried to migrate a real Objc Keychain before. I simply looked at the code. I havenāt used that SDK in yearsā¦ Attempt to set a breakpoint here:
- warning: When initializing the Swift SDK, `migratingFromObjcSDK` should be set to **false**
when calling this method.
- warning: The latest **PFUser** from the Objective-C SDK should be saved to your
Parse Server before calling this method.
*/
public static func loginUsingObjCKeychain(options: API.Options = [],
callbackQueue: DispatchQueue = .main,
completion: @escaping (Result<Self, ParseError>) -> Void) {
let objcParseKeychain = KeychainStore.objectiveC
let objcParseSessionToken: String? = objcParseKeychain?.object(forKey: "sessionToken") ??
objcParseKeychain?.object(forKey: "session_token")
guard let sessionToken = objcParseSessionToken else {
let error = ParseError(code: .unknownError,
message: "Could not find a session token in the Parse Objective-C SDK Keychain.")
callbackQueue.async {
completion(.failure(error))
}
return
}
Print out the info in your console log, does it show real data? If so, what do the keys look like? If you post the data (omitting the actual sessionToken and private data) it will help to understand the structure of Obj-C Keychain.
bcbeta
September 7, 2022, 4:33pm
13
Printing the objcParseKeychain doesnāt give helpful info. I donāt know how to print the keys?
Printing description of objcParseKeychain:
āæ Optional
āæ some : KeychainStore
- synchronizationQueue : <OS_dispatch_queue_concurrent: com.dave.BackcountrySkiReporter.com.parse.sdk.keychain[0x280bef680] = { xref = 13, ref = 1, sref = 1, target = com.apple.root.default-qos[0x107dd92c0], width = 0xffe, state = 0x0000041000000000, in-flight = 0}>
- service : ācom.dave.BackcountrySkiReporter.com.parse.sdkā
cbaker6
September 8, 2022, 1:35pm
14
Are you running your Swift SDK in the exact same app and device you were logged in with the Objective-C SDK? If not, you are using a different device, you need to use the other login methods.
If you are using the same app and device, you can try to debug the Objective-C version of your app, find the Keychain code, and compare the difference in the Swift SDK Keychain. Note, you or someone who has an app using the Obj-C SDK will need to do this as I donāt use that SDK anymore.
Worst case is you can login with the user credentials in the Swift SDK.
bcbeta
September 10, 2022, 6:44pm
15
Yes I am using the same app and device. Hereās a screenshot of the objC code. I tried to update the swift package to reflect this but couldnāt get it to work. My understanding of keychain is rudimentary at best.
cbaker6
September 11, 2022, 3:55pm
16
I opened a new PR which should address your issue:
parse-community:main
ā cbaker6:fixObjC
opened 03:46PM - 11 Sep 22 UTC
### New Pull Request Checklist
<!--
Please check the following boxes [x] bā¦ efore submitting your issue.
Click the "Preview" tab for better readability.
Thanks for contributing to Parse Platform!
-->
- [x] I am not disclosing a [vulnerability](https://github.com/parse-community/Parse-Swift/security/policy).
- [x] I am creating this PR in reference to an [issue](https://github.com/parse-community/Parse-Swift/issues?q=is%3Aissue).
### Issue Description
#391 added some migration methods but they currently have some issues:
- There's no method that easily allows the current `ParseInstallation` to become another `ParseInstallation`. Adding a method like this allows developers to use their own ways to get a particular `objectId` and use it to migrate to the Swift SDK.
- `ParseUser.loginUsingObjCKeychain` does not properly get the session token from an Objective-C Keychain.
Related issue: #n/a
### Approach
- Add the `become` method which allows the current installation to become any installation based on the `objectId`.
- Search the Objective-C Keychain for `currentUser->sessionToken` to get the `sessionToken` properly.
### TODOs before merging
<!--
Add TODOs that need to be completed before merging this PR.
Delete TODOs that do not apply to this PR.
-->
- [x] Add tests
- [x] Add entry to changelog
- [x] Add changes to documentation (guides, repository pages, in-code descriptions)
I suspect you will still have an issue with PFInstallation
if used it in your Objective-C SDK based app. This is because the Objective-C SDK appears to save the installationId
to disk as oppose to Keychain. If you did the information from PFInstallation, I recommend using the new one that the Swift SDK generates, query for all ParseInstallation
ās related to the current user, copy the deviceToken
, channels
, and any custom keys from the newest installation that used the Objective-C SDK to the ParseInstallation.current
, save the current, then delete the old Objective-C Installations.
bcbeta
September 11, 2022, 7:45pm
18
This is similar to what I had tried. Using this commit still not finding a value for ācurrrentUserā key.
cbaker6
September 12, 2022, 4:50am
19
The merged updates in PR 407 fixes the problem. If you need help with using the new methods, see my sample app:
Sample app that demonstrates how to migrate a Parse User and Installation from the Parse Objective-C SDK to Swift SDK
When migrating the installation, you should use the Objective-C PFInstallation.current().objectId
and Parse-Swift ParseInstallation.become()
:
let installation = PFInstallation.current()
installation?.channels = dummyChannels
installation?.saveInBackground { (success, error) in
guard success,
error == nil,
let currentInstallation = PFInstallation.current(),
let objectId = currentInstallation.objectId else {
self.errorMessage = error?.localizedDescription ?? "Could not save installation"
return
}
self.objCInstallation = currentInstallation
Task {
do {
self.swiftInstallation = try await Installation.become(objectId)
Most of the example logic is in ContentViewModel.swift
bcbeta
September 13, 2022, 2:09am
20
Unfortunately the ācurrentUserā key is coming up empty for me as well when migrating. Hopefully someone else who is able to successfully migrate can offer some insight.
cbaker6
September 13, 2022, 3:41am
21
I recommend checking to see if you are using the last commit from the PR. The updates are released in 4.12.0
, so just use the latest release:
If 4.12.0
doesnāt work, maybe you werenāt logged on in the Objective-SDK on your testing device or you changed something that modified the Keychain. You can also try the sample app I linked in the previous comment to verify. The sample app uses both SDKs and migrating PFUser->ParseUser
and PFInstallation->ParseInstallation
works as expected as User.loginUsingObjCKeychain()
is the only way the app is logging into the Swift SDK:
Screen shots below:
Before Migration and Logging into Objective-C SDK
After Logging into Objective-C SDK and Migrating
bcbeta
September 13, 2022, 11:35pm
22
Yes the latest release works! Thank you so much for all your work on this.
1 Like