First time I am building iOS application, I got stuck in saving data securely. How to save sensitive data like username and password in keychain
in app I am using UserDefaults to store and retrieve like below
UserDefaults.standard.set([unameTextfield.text], forKey: "userName")
UserDefaults.standard.set([passwordTextfield.text], forKey: "userPassword")
for retrieving:
let uName = UserDefaults.standard.string(forKey: "userName")
let uPassword = UserDefaults.standard.string(forKey: "userPassword")
but I want to save data securely in Keychain, how to do that?
You are correct, UserDefault is not a good solution when it comes to store sensitive information. Keychain is what you need; however, the problem is that keychain native API is not as easy/straighforward as Userdefault Api.
So, I think a great solution is to use KeychainWrapper.
First, install it. Make your cocaopod file look like this
use_frameworks!
platform :ios, '8.0'
target 'target_name' do
pod 'SwiftKeychainWrapper'
end
Then, install it from the terminal with pod install and import it. (This is from the docs)
import SwiftKeychainWrapper
//Set value
let saveSuccessful: Bool = KeychainWrapper.standard.set("Some String", forKey: "myKey")
//get value
let retrievedString: String? = KeychainWrapper.standard.string(forKey: "myKey")
Related
We are exploring the use of Realm DB in Flutter.
We tried to initialize an existing encrypted realm file via the configuration details provided at https://pub.dev/packages/realm
var config = Configuration.local([Car.schema], disableFormatUpgrade: true,
path: 'assets/myfile.realm');
return Realm(config);
However, we could not find an option to specify the encryption key while opening the file.
Can anyone please help?
Thank you.
At this time, there is limited support for Realm in Flutter - although the beta package exists, it's a work in progress. You can still call native Realm functions from Flutter and get back access codes. See Flutter: Write platform specific code
Generally speaking, to encrypt a Realm, generate a random encryption key and pass it to the Realm configuration object. Keeping in mind that to unencrypt it, you need to use the same key so it will need to be stored.
Here's a quick Swift example how how to generate a key but the technique applies across the board to all platforms
var key = Data(count: 64)
_ = key.withUnsafeMutableBytes { (pointer: UnsafeMutableRawBufferPointer) in
SecRandomCopyBytes(kSecRandomDefault, 64, pointer.baseAddress!) }
Then to initially create that encrypted Realm, pass the key in the encryptionKey parameter in the Configuration object:
var config = Realm.Configuration(encryptionKey: key)
let realm = try Realm(configuration: config)
//write some data to Realm etc
Then later, get the key from wherever it was stored, typically in a keychain on macOS/iOS - or whatever secure way you choose to store it:
var key = //get the key
var config = Realm.Configuration(encryptionKey: key)
let realm = try Realm(configuration: config)
//read, write data from the encrypted Realm
So, say user registration is done (this code is already correct) and the next window asks users to enter personal info (education etc), how do you code that this information goes to Firebase under the user's profile.
I have the registration page done and users created there already go to firebase.
This is what I have for the page after registration. No errors but obviously incomplete.
The below code is what I found, but it is only for entering data in to database. It doesn't work on 2 points:
it it not dynamic data entered by user, it is simply the description of what the user is asked,
it doesn't go under the user's profile in firebase.
func post() {
let MainFunInterest = "MainFunInterest"
let SomethingInterestingIhaveRead = "SomethingInterestingIhaveRead"
let JobOrEducation = "JobOrEducation"
let WhatIamConsideringBuying = "WhatIamConsideringBuying"
let post : [String : AnyObject] = ["MainFunInterest" : MainFunInterest as AnyObject,
"SomethingInterestingIhaveRead" : SomethingInterestingIhaveRead as AnyObject,
"JobOrEducation" : JobOrEducation as AnyObject,
"WhatIamConsideringBuying" : WhatIamConsideringBuying as AnyObject]
let databaseRef = Database.database().reference()
databaseRef.child("personal info").childByAutoId().setValue(post)
}
I need the user's entry to go under his profile in firebase.
You can add a ‘users’ node through .child() and then set their personal information by their personal UID from .getUID
So the database could look like this:
Users -
(Their personal UID) -
Name - ‘Terry’
Email - ‘terry#email.com’
Phone - ‘0978364727’
When you call databaseRef.childByAutoId() Firebase generates a new child node under databaseRef. So if you call that multiple times, you get multiple new child nodes, even when the calls are for the same user.
To store data for users, you'll want to (as Nathan answered) store that data under that user's unique UID. That way you can update it later if needed, and easily find the data for a user without having to query for it.
To store the data under the user's UID, do something like this:
let uid = Auth.auth().currentUser.uid
databaseRef.child("personal info").child(uid).setValue(post)
For more on this, see:
the Firebase documentation on basic write operations.
the Firebase documentation on getting the currently signed in user.
Adding data to a specific UID in firebase
How to save to Firebase - Swift
I successfully saved a token using Locksmith, but now I want to delete it when the user logs out.
Here is the code that I tried to use to delete the token, but this did not work:
let _ = try? Locksmith.deleteDataForUserAccount(userAccount: "github")
The equivalent using KeychainWrapper would look like this:
let keychainResult = KeychainWrapper.defaultKeychainWrapper.remove(key: KEY_UID)
The API that I was using required a call in order to delete the token.
I am trying to delete an identity from my Keychain in Swift 3.0. My code is currently:
var idCert: SecCertificate? = nil
var idKey: SecKey? = nil
SecIdentityCopyCertificate(id, &idCert)
SecIdentityCopyPrivateKey(id, &idKey)
SecKeychainItemDelete(idCert as! SecKeychainItem)
SecKeychainItemDelete(idKey as! SecKeychainItem)
"id" is a SecIdentity object that I got by using SecItemCopyMatching to get all of my identities, and then used that list to find the identity associated with a specific email address.
My code fails when I try to cast idCert to SecKeychainItem. Apple's Documentation says:
A SecCertificate object for a certificate that is stored in a keychain
can be safely cast to a SecKeychainItem for manipulation as a keychain
item. On the other hand, if the SecCertificate is not stored in a
keychain, casting the object to a SecKeychainItem and passing it to
Keychain Services functions returns errors.
but upon checking Keychain Access I can plainly see that the certificate is in the Keychain. What is going on? Is it possible for a certificate to appear to be in my Keychain but actually isn't or can't be found for some reason? Is there a way for me to programmatically check if the SecCertificate object is in my Keychain?
Is there any way to use the iTunes API to lookup information based on the unique app bundleId? I have an iphone app, I want to use the API to check to see if the user has the newest version. Using the search sucks, cuz its fuzzy (can return lots of results). I'd prefer not to have to iterate over the result set looking for my bundleId..
I'm not looking for a way to do this on the device side (not objective c). I'd like to make server side code on my server that hides when/if apple makes API change.
Apple has changed their API, and removed the language code from the URL, so you should only the bundleId for the app you are looking for.
For example:
http://itunes.apple.com/lookup?bundleId=com.yelp.yelpiphone
In addition, you can add the country parameter to the query, to get results for a specific country App Store.
For example:
http://itunes.apple.com/lookup?bundleId=com.yelp.yelpiphone&country=de
The description, user rating and other fields might change between different App Store countries.
Turns out you can use the 'Apple ID' instead of the bundle ID as it is also unique per app. The 'Apple ID' maps to 'trackId' in http://itunes.apple.com/lookup service.
You can use a bundle id with Search API. Just replace id with bundleId, like following:
http://itunes.apple.com/lookup?bundleId=com.facebook.Facebook
EDIT:
As #Roman Blachman stated, the country code logic has changed. If you want to limit your results to a specific country, use the following example.
http://itunes.apple.com/lookup?bundleId=com.facebook.Facebook&country=us
You can use the library, iVersion, to see if the user has the latest version of the app from within the app.
iVersion
Library for dynamically checking for updates to Mac/iPhone App Store
apps from within the application and notifying users about the new
release. Can also notify users about new features in the app the first
time they launch after an upgrade.
https://github.com/nicklockwood/iVersion
Thanks to all above answers. Here is code in swift 4.2
guard let info = Bundle.main.infoDictionary,
let identifier = info["CFBundleIdentifier"] as? String,
let url = URL(string: "http://itunes.apple.com/lookup?bundleId=\(identifier)") else { return }
do {
let data = try Data(contentsOf: url)
guard let json = try JSONSerialization.jsonObject(with: data, options: [.allowFragments]) as? [String: Any] else {
return
}
if let result = (json["results"] as? [Any])?.first as? [String: Any],
let version = result["version"] as? String {
print("version in app store", version)
}
} catch let erro as NSError {
print(erro.description)
}