How does this function/variable work? - ios

I'm using the following code to retrieve NSURLCredentials:
let credentials: NSURLCredential? = {
let loginProtectionSpace = NSURLProtectionSpace(host: host, port: 0, protocol: NSURLProtectionSpaceHTTP, realm: nil, authenticationMethod: NSURLAuthenticationMethodDefault)
let credentials = NSURLCredentialStorage.sharedCredentialStorage().defaultCredentialForProtectionSpace(loginProtectionSpace)
return credentials
}()
This is called when the user opens the app. The credentials returned are nil. Then I set these credentials and try to print credentials out again and it's still nil. However, if I restart the app, the printed credentials are there. What's going on here?

This is a lazy variable. The code gets executed once when you first access the property. After that the initially returned value is "remembered" and returned on future calls.
If you set the credentials yourself in the NSURLCredentialStorage then on the next start of the app the first access of the property once again executes the code and retrieves the stored credentials. Note that during the run where you first set the credentials up the actual 3 lines of code retrieving the credentials from the storage are not executed a second time and therefore during that run of the app the property still is nil while there actually is a value in the storage. A similar thing will happen if you modify the existing credentials - during the run where you change them, the credentials will still hold a reference to the previous ones.
If you want to be able to requery the store, you should either
create a function for this instead of a lazy variable
or take a look at this answer: Re-initialize a lazy initialized variable in Swift

I suspect it's because credentials is immutable. The code block gets executed once to assign credentials its value. Which is why it has the credentials after you restarted the app.

Related

GetStream iOS native - How to update Client token after first initialization

I'm looking for an API to update the Client.Config.token value for the GetStream Library on iOS. Seems like this token is the only way for the library to parse the user_id from the JWT, however it is only parsed at init time of the singleton, Client.shared.
What happens if we need to update the token if the user has logged out of one account and into a different account?
Thank you for your question. For now, the shared Client doesn't support Token update, but you can use a not shared instance of the Client. So, when another user logged in you can create a new Client instance with another token instead of the existing one.
To finish an instance client setup you need to create the current user like this:
if let currentUserId = client.currentUserId {
client.create(user: User(id: currentUserId)) { [weak client] result in
client?.currentUser = try? result.get()
}
}
We'll add a token update for the shared Client in future releases.

How can I use keychain when I close my ios app?

I'm developing an iOS app and want user only type username:password pair only once (unless he logs out). Currently I use keychain-swift framework for storing emails/passwords.
I basically run:
let keychain = KeychainSwift()
keychain.set("johndoe", forKey: "my key") // on a successful login
keychain.get("my key")
When I run my app in a simulator I have to type password all the time (i.e., it doesn't look like it saves the password in keychain between the sessions).
Is it expected? What framework will allow me to save the data even when I close the app such that a user won't have to type username:password pairs every time to sign in?
I have never used KeychainSwift but at a guess you could do something like:
let keychain = KeychainSwift(keyPrefix: "com.Daniel.myIOSapp.")
keychain.set("johndoe", forKey: "username")
keychain.set("where is jane", forKey: "password")
which will create two "generic password" keychain items com.Daniel.myIOSapp.username and com.Daniel.myIOSapp.password and the associated values.
You normally store the username/password pair as a single keychain item. You can do that with KeychainSwift using something like:
keychain.set("where is jane", forKey: "johndoe")
which creates a single generic password item in the keychain and you probably want to store "johndoe" in your preferences under a suitable key.
HTH
Haven't used this framework myself, but from looking at the code it appears that it will save, and therefore be accessible after app is restarted.
You might want to confirm the set function is working properly. From the readme here:
https://github.com/evgenyneu/keychain-swift/blob/master/README.md
...it states:
Check if operation was successful
One can verify if set, delete and clear methods finished successfully
by checking their return values. Those methods return true on success
and false on error.
if keychain.set("hello world", forKey: "my key") { // Keychain item
is saved successfully } else { // Report error }
Also, just to confirm, the get function should read:
let myKey = keychain.get("my key")
...where myKey is put into your text field(s).

Cannot set PubNub auth key Swift iOS

I have a webapp and the iOS app built in Swift. The thing is I don't know Swift and I'm trying to modify the iOS app in order to add the authorization key to the PubNub client before subscribing/publishing to channels.
Link to PubNub docs
PRE:
Access Manager is enabled
my_auth_key is hardcoded and already enabled form the server for the corresponding channel I want to subscribe.
Here is the code
What's the correct way to set the auth key?
Thanks in advance
Polak, mentioned docs code snippet refer to previously created instance to get it's pre-configured PNConfiguration instance and change required field. This practice can be used in case if you need change something at run-time.
If you have data for authKey at the moment of client initialization, you can set it:
var pubnubClient: PubNub = {
let configuration = PNConfiguration(publishKey: UCConstants.PubNub.publishKey, subscribeKey: UCConstants.PubNub.subscribeKey)
configuration.authKey = "my_auth_key"
return PubNub.clientWithConfiguration(configuration)
}()
Also, I've tried exact your code and don't have any issues with setting of authKey, because I can see it with subscribe request.
If you still will have troubles with PAM and auth key usage, please contact us on support#pubnub.com
Looks like you can:
let configuration = PNConfiguration(
authKey: "super_secret",
publishKey: UCConstants.PubNub.publishKey,
subscribeKey: UCConstants.PubNub.subscribeKey
)
Based on the Obj-C code: https://github.com/pubnub/objective-c/blob/8c1f0876b5b34176f33681d22844e8d763019635/PubNub/Data/PNConfiguration.m#L174-L181

Is is normal to end up with a LOT of changes on CKFetchRecordChangesOperation?

I use CloudKit in my app, which seems to be working well. However when I initialise the app on a new device, using
CKFetchRecordChangesOperation *fetchRecordChangesOperation = [[CKFetchRecordChangesOperation alloc] initWithRecordZoneID:zoneID previousServerChangeToken:NIL];
I get a LOT of changes, as all previous deletions and changes appear to be synched across.
Is there a better way? for example to just download a full set of current data and set the serverChangeToken to the current value.
When looking at the documentation of the CKServerChangeToken it looks like you can only get it when using the CKFetchRecordChangesOperation. See: https://developer.apple.com/library/prerelease/ios/documentation/CloudKit/Reference/CKServerChangeToken_class/index.html
So now how could you get a hold on that token:
Usually you will do something like this: As you can see in the CKFetchRecordChangesOperation you can initialize it with a previousServerChangeToken. This token works like a timestamp. When the operation completes, you get this token back in the fetchRecordChangesCompletionBlock. You have to save that token in for instance the user defaults. Then the next time you start a CKFetchRecordChangesOperation, you can use that token to start reading the changes since the last time you called it.
Actually saving the token can be a little tricky. I can suggest adding a property like this:
private var previousChangeToken: CKServerChangeToken? {
get {
let encodedObjectData = NSUserDefaults.standardUserDefaults().objectForKey("\(container.containerIdentifier)_lastFetchNotificationId") as? NSData
var decodedData: CKServerChangeToken? = nil
if encodedObjectData != nil {
decodedData = NSKeyedUnarchiver.unarchiveObjectWithData(encodedObjectData!) as? CKServerChangeToken
}
return decodedData
}
set(newToken) {
if newToken != nil {
NSUserDefaults.standardUserDefaults().setObject(NSKeyedArchiver.archivedDataWithRootObject(newToken!), forKey:"\(container.containerIdentifier)_lastFetchNotificationId")
}
}
}
In your case you want a new application to start with a change token that could only have been created by an other running app. So besides saving the change token to the NSUserDefaults, you should also save it in CloudKit in the public Database in one specific settings recordType record. A newly installed app that does not have a token in it's NSUserDefaults can then read the token from your CloudKit settings record.

Restore Access Token in hybridauth

I saved the Access Token (using this method: getAccessToken ()) in my database, but now I would like to restore this value to an object.
How can I do this?
This is explained in hybridauth user manual with below code :
// get the stored hybridauth data from your storage system
$hybridauth_session_data = get_sorted_hybridauth_session( $current_user_id );
Get_sorted_hybridauth_session is your internal function to get the stored data.
It doesnt matter whether you store the data in a table in a field named 'external_token' or something, get it through a normal sql query, and then just feed it to below function :
// then call Hybrid_Auth::restoreSessionData() to get stored data
$hybridauth->restoreSessionData( $hybridauth_session_data );
// call back an instance of Twitter adapter
$twitter = $hybridauth->getAdapter( "Twitter" );
// regrab te user profile
$user_profile = $twitter->getUserProfile();
$hybridauth->restoreSessionData( $hybridauth_session_data ); will restore the serialized session object, and then it will get an adapter for whichever provider it was saved for. Its best that you also save the provider name (Twitter in this case) in the same database table with something like external_provider , and then you can get it through a sql auery and feed it to getAdapter function. That should do what you need to do.
The manual example is below :
http://hybridauth.sourceforge.net/userguide/HybridAuth_Sessions.html
=============
As an added info - what i saw in my tests was, saving session in this way does not prevent hybridauth from logging the user in, even if the user has revoked access from the app in the meantime. Ie, if user is already logged in and authorized, but, went to the app separately and revoked the access (google for example), hybridauth will still log in the user to your system. Im currently trying to find a way to make sure the user is logged to the remote system too.
Late, but I thought this would help:
The following code verifies and removes those providers from HybridAuth that the user is not truly logged into:
$providers = $this->hybridauthlib->getConnectedProviders();
foreach( $providers as $connectedWith ){
$p = $this->hybridauthlib->getAdapter( $connectedWith );
try {
$p->getUserProfile();
} catch (Exception $e) {
$p->logout();
}
}

Resources