Outside of asking the user to input their name, is there any way to get it off the device?
I tried this library, which attempts to extract the name from [UIDevice currentDevice] name], but that doesn't work in a lot of situations:
https://github.com/tiboll/TLLNameFromDevice
Is the user's name present in the phonebook or anywhere else that we have access to in iOS 6?
Well you could go through all the contacts in the AddressBook and see if any of them are marked with the owner flag.
Just be aware that doing this will popup the "this app wants access to the address book" message. Also Apple isn't very keen on these kind of things. In the app review guide it is specified that an app can not use personal information without the user's permission.
You could use Square's solution:
Get the device's name (e.g. "John Smith's iPhone").
Go through the contacts on the phone and look for a contact named "John Smith".
JBDeviceOwner and ABGetMe will both do this for you.
You could use CloudKit. Following a snippet in Swift (ignoring errors):
let container = CKContainer.defaultContainer()
container.fetchUserRecordIDWithCompletionHandler(
{
(recordID, error) in
container.requestApplicationPermission(
.PermissionUserDiscoverability,
{
(status, error2) in
if (status == CKApplicationPermissionStatus.Granted)
{
container.discoverUserInfoWithUserRecordID(
recordID,
completionHandler:
{
(info, error3) in
println("\(info.firstName) \(info.lastName)")
}
)
}
}
)
}
)
The above code was based on the code at http://www.snip2code.com/Snippet/109633/CloudKit-User-Info
to save folks time. in swift4:
let container = CKContainer.default()
container.fetchUserRecordID(
completionHandler: {
(recordID, error) in
guard let recordID = recordID else {
return
}
container.requestApplicationPermission(
.userDiscoverability,
completionHandler: {
(status, error2) in
if (status == CKContainer_Application_PermissionStatus.granted)
{
if #available(iOS 10.0, *) {
container.discoverUserIdentity(withUserRecordID:
recordID,
completionHandler:
{
(info, error3) in
guard let info = info else {
return
}
print("\(info.firstName) \(info.lastName)")
}
)
}
}
}
)
}
)
however: CKUserIdentity no longer exposes either first or last name
So this answer no longer works.
You can use:
NSLog(#"user == %#",[[[NSHost currentHost] names] objectAtIndex:0]);
I did receive compiler warnings that the methods +currentHost and -names were not found. Given the warning, I’m not sure of Apple’s intention to make this available (or not) as a publicly accessible API, however, everything seemed to work as expected without the need to include any additional header files or linking in additional libraries/frameworks.
Edit 1:
You may also take a look at this Link
Edit 2:
If you have integrated your app with Facebook you can easily retrieve the user info, see Facebook Fetch User Data
For SWIFT you can use
NSUserName() returns the logon name of the current user.
func NSUserName() -> String
Related
I have written a small app in Swift using Xcode 12.5 by following the information and code samples provided here ... https://github.com/square/SquarePointOfSaleSDK-iOS
The app polls a server to see if there is a charge to be made. The output from the server is in JSON format. When a charge comes in, the JSON results are providing a customer id, amount to be charged, and a note to the Square Point of Sale SDK.
Using the SCCAPIRequest example from the GitHub page ...
// Replace with your app's URL scheme.
let callbackURL = URL(string: "<#T##Your URL Scheme##String#>://")!
// Your client ID is the same as your Square Application ID.
// Note: You only need to set your client ID once, before creating your first request.
SCCAPIRequest.setApplicationID(<#T##Application ID##String#>)
do {
// Specify the amount of money to charge.
let money = try SCCMoney(amountCents: 100, currencyCode: "USD")
// Create the request.
let apiRequest =
try SCCAPIRequest(
callbackURL: callbackURL,
amount: money,
userInfoString: nil,
locationID: nil,
notes: "Coffee",
customerID: nil,
supportedTenderTypes: .all,
clearsDefaultFees: false,
returnsAutomaticallyAfterPayment: false,
disablesKeyedInCardEntry: false,
skipsReceipt: false
)
// Open Point of Sale to complete the payment.
try SCCAPIConnection.perform(apiRequest)
} catch let error as NSError {
print(error.localizedDescription)
}
The app successfully switches to Square POS, displays the amount due, and knows which customer I am wanting to charge (via customer id). I can process the payment and Square POS switches back to my app just fine.
This is where I am running in to trouble. I am also using the UIApplication delegate method example on that same page. Under the comment "Handle a successful request" ...
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
guard SCCAPIResponse.isSquareResponse(url) else {
return
}
do {
let response = try SCCAPIResponse(responseURL: url)
if let error = response.error {
// Handle a failed request.
print(error.localizedDescription)
} else {
// Handle a successful request.
}
} catch let error as NSError {
// Handle unexpected errors.
print(error.localizedDescription)
}
return true
}
I have added the following ...
print("Transaction successful: \(response)")
From what I understand, the response should include the transaction id, and anything that was passed along in the userInfoString. It appears that this code example isn't even firing when Square POS returns to my app. I cannot see anything in the Xcode console.
I have assigned a callback URL within Xcode using the documentation on the link above, and it's also added in the Square Developer Portal under the Point of Sale API.
What am I missing? Where should the UIApplication delegate method be placed, in AppDelegate.swift or should it reside in ViewController.swift, or somewhere else? Any insight would be greatly appreciated.
#ElTomato provided me with the hint that I needed to solve the problem I was having. I needed to delete SceneDelegate.swift, remove Application Scene Manifest from Info.plist, and remove some code from AppDelegate.swift
I found detailed instructions on THIS site ...
iOS 13: Swift - 'Set application root view controller programmatically' does not work
Thank you kindly for the fantastic help #ElTomato
When a user is signed up through my form, a document gets created associated with that user. My main goal is to create a global function that can recognize the user that is signed in and get their document ID. I have a function setup for adding documents to a subcollection of the user document which is perfectly setup, the only downfall is that when I'm testing with multiple accounts, I have to manually switch the collection path. Here is what I mean.
#IBAction public func createEventButton(_ sender: UIButton) {
let error = validateFields()
if error != nil {
showError(error!)
} else {
db.collection("school_users/\(stThomas)/events").addDocument(data: ["event_name": nameTextField.text, "event_date": dateTextField.text, "event_cost": costTextField.text, "for_grades": gradesTextField.text]) { (error) in
if error != nil {
self.showError("There was an error trying to add user data to the system.")
} else {
self.dismiss(animated: true, completion: nil)
}
}
}
So as you can see here, I am using string interpolation with the "stThomas" constant I used to store a document ID. I basically want to create a function that will recognize the document ID of the user signed in so I can use my Constants instead of string interpolation and having to manually switch the user collection path each time, which would be eventually impossible during production.
Not to mention, I do have a function to grab the document ID, say for instance an event is clicked, but as a beginner in Swift, I can't seem to connect the dots. I will also show this function for clarification.
func getDocID() {
db.collection("school_users/\(notreDame)/events").getDocuments() { (querySnapshot, error) in
if let error = error {
print("There was an error getting the documents: \(error)")
} else {
self.documentsID = querySnapshot!.documents.map { document in
return DocID(docID: (document.documentID))
}
self.tableView.reloadData()
}
}
}
And in this function you can see my other constant "notreDame" with another stored document ID. If anybody knows a simple way to do this that would be great. And yes, I checked the Firebase documents, thank you for asking.
I've did some extra research and realized that I can use User IDs in collection paths. My problem is now solved. Many more problems to come though.
It's really hard to find a proper title for this question. Please be easy on me.
The first part is a check to see if an account exists:
Auth.auth().fetchSignInMethods(forEmail: userEmail, completion: {
(providers, error) in
if error != nil {
self.displayAlertMessage(alertTitle: "Unhandled error", alertMessage: "Undefined error #SignUpViewController_0001");
return;
} else if providers != nil {
self.displayAlertMessage(alertTitle: "Error", alertMessage: "This account is not exist.");
return;
}
})
As you can see, I have something named Unhandled error with message Undefined error. I don't know how to name it properly. Can somebody explain that part to me?
The second one is about getting a localized string - any ideas to make it fancy?
Auth.auth().createUser(withEmail: userEmail, password: userPassword) { user, error in if error == nil && user != nil {
self.displayAlertMessage(alertTitle: "Success", alertMessage: "Your account created successfully. We send you a verification email.", dismiss: true);
} else {
self.displayAlertMessage(alertTitle: "Firebase error", alertMessage: "(error!.localizedDescription)");
}
}
Thanks for tips :)
You can handle the Errors this way:
Auth.auth().fetchSignInMethods(forEmail: email, completion: { (response, error) in
if let error = error, let errCode = AuthErrorCode(rawValue: error._code)
{
switch errCode {
case .emailAlreadyInUse:
GeneralHelper.sharedInstance.displayAlertMessage(titleStr: LocalizeConstant.CommonTitles.Alert.rawValue.localizedStr(), messageStr: LocalizeConstant.CommonTitles.Continue.rawValue.localizedStr())
case .accountExistsWithDifferentCredential:
GeneralHelper.sharedInstance.displayAlertMessage(titleStr: LocalizeConstant.CommonTitles.Alert.rawValue.localizedStr(), messageStr: LocalizeConstant.CommonTitles.Continue.rawValue.localizedStr())
default:
break
}
return
}
}
Here I am getting the errCode using AuthErrorCode provided by Firebase itself and then, I am passing in the received error code using error._code. So, now I can get the type of AuthErrorCode. Using this I am making cases like .emailAlreadyInUser, .accountExistsWithDifferentCredential etc. You can just type . and it will show you all the AuthErrorCodes. So, you can simply handle the error codes in this way.
Now, coming to the second part of the question, i.e. getting localized string. You can add localization to Firebase, for that you have to select the language code. Auth.auth().languageCode = "en" //For English. But, I do not think that it gives localized errors as there are many more languages than what Firebase supports. This mainly for sending localized emails.
To handle the localization, you have to create your own method as I did. You can see that I have called a function displayAlertMessage in which I am passing thetitleStr: LocalizeConstant.CommonTitles.Alert.rawValue.localizedStr(), which is a part of localization.
struct LocalizeConstant {
enum CommonTitles: String
{
case Alert = "common_alert"
}
}
This value designates to the key given by me in the localization file. If you do not know about localization, you have to do a Google search on it. Let's say I have two Localizable.strings one is in English and the other one is in French. In Localizable.strings(English), I've written Alert like this:
"common_alert" = "Alert";
And, In French:
"common_alert" = "Alerte!";
So, this is how I have manually added localization in my app. But, to achieve this you have to do two things. 1) You have to set up your appLanguage. 2) You have to call a method which will fetch the values from these keys defined in the Localizable.strings file.
To do this, I have created a method localizedStr(). It is an extension to String and you can use it as follows.
extension String{
func localizedStr() -> String
{
var finalRes = ""
if let path = Bundle.main.path(forResource: Constants.appLang, ofType: "lproj") //Constants.appLang is "en" here for "English", but you can set accordingly.
{
if let bundle = Bundle(path: path)
{
finalRes = NSLocalizedString(self, tableName: nil, bundle: bundle, value: " ", comment: " ")
}
}
return finalRes
}
}
Now, this method localizedStr() will give you a localized string according to your app language. Even, if Firebase provides localized error codes(which I think it does not), it is impossible to get the error description in each language. So this is the best way I came up with. It may not be the best method out there in the world, but it does the task.
P.S.: To optimize this throughout the app, either you can create an extension to AuthErrorCode or you can create a Helper function where you will just pass the error._code and it will return the localized string. I've added the long way so that you can understand everything in the best way.
My OSX app allows the user to select a contact from their contacts list and loads the details into a Customer record. I am using CNContactPicker to retrieve a contact into a CNContact record. One of the fields I need to retrieve is organizationName. This works perfectly in OS's prior to High Sierra, but upon upgrading to High Sierra it will crash. All other CNContact fields can be retrieved with no issue (e.g. names, email, address etc). I do have permissions requested in my info.plist file.
It makes no difference if the Contact does/does not have an Organization Name.
Not much to show in terms of code:
// This fails on 1st line - any reference to organizationName causes failure
if (contact.organizationName != "") {
self.name = contact.organizationName
}
// This works
if (contact.givenName != "") {
self.name = contact.givenName
}
// This works
if (contact.contactType == CNContactType.organization) {
// Do something
}
The actual error is: [General] A property was not requested when contact was fetched.
I would like to know what has changed in the OS to cause this error, and if there is a solution or workaround please.
I submitted a bug report with Apple and received the following response which fixes my issue. Essentially, even though I have retrieved a Contact that the user selected, I need to do a CNContactFetchRequest to fetch this specific contact again (using the identifier) with keys specified (e.g. organisation).
Here is their exact response:
If you want to make sure organizationName is available, execute a CNFetchRequest for a contact with the same identifier (as returned from CNContactPicker delegate method) and provide a set of keys to fetch containing CNContactOrganizationName.
Here is the code:
var validContacts: [CNContact] = []
let contactStore = CNContactStore()
do {
// Specify the key fields that you want to be fetched.
// Note: if you didn't specify your specific field request. your app will crash
let fetchRequest = CNContactFetchRequest(keysToFetch: [CNContactOrganizationNameKey as CNKeyDescriptor])
fetchRequest.predicate = CNContact.predicateForContacts(withIdentifiers: [contact.identifier])
try contactStore.enumerateContacts(with: fetchRequest, usingBlock: { (contact, error) -> Void in
validContacts.append(contact)
})
for validContact in validContacts {
// Do something with your contact, there should be only one.
}
} catch let e as NSError {
print(e)
}
I've just create a project in Xcode 9 beta 6 and add this code:
let privateDB = CKContainer.default().privateCloudDatabase
let greatID = CKRecordID(recordName: "GreatPlace")
let place = CKRecord(recordType: "Place", recordID: greatID)
privateDB.save(place) { (record, error) in
if error != nil {
let er = (error as! CKError).errorUserInfo
print("Error: \n")
print("CKErrorDescription: \(er["CKErrorDescription"]!)\n")
print("ContainerID: \(er["ContainerID"]!)\n")
print("NSDebugDescription: \(er["NSDebugDescription"]!)\n")
print("NSUnderlyingError: \(er["NSUnderlyingError"]!)\n")
print("NSLocalizedDescription: \(er["NSLocalizedDescription"]!)\n")
print("ServerErrorDescription: \(er["ServerErrorDescription"]!)\n")
}
if record != nil {
print("record: \(record!)")
}
}
and add this capabilities:
and when I run the code I receive this error message:
What I am doing wrong ?
There was a bug causing some associations to be missed. That bug has been fixed and we automatically fixed the container/app associations that were broken during that time.
If for some reason you still need to redo an association you can either use the Capabilities pane in Xcode or use developer.apple.com -> Certificates, Identifiers & Profiles -> App IDs -> pick the ID -> Edit -> Edit under iCloud -> check the box for the container to disassociate, save, then re-associate.
If you're still stuck please email cloudkit[at]apple.com
My friend and I are having the same issue. We made 2 different projects and both of them had the same error message "Invalid bundle ID for container" which is CKError case 10 .
We are calling our fetch function to get the default "Users" record in the viewDidLoad.
func fetchWorkoutCompleted(completion: #escaping (Error?) -> Void = { _ in }) {
cloudKitManager.fetchRecord(ofType: "Users", sortDescriptors: nil) { (records, error) in
if let error = error {
print(error.localizedDescription)
completion(error)
return
}
guard let records = records else { completion(nil); return }
completion(nil)
}
}
Using Xamarin.IOS, I had to select manual provisioning rather than automatic provisioning in the info.plist file.
Had the same issue. what worked for me was changing the iCloud group name.
Before it was something like this: iCloud.com.companyName.appName.randomString
After changing to: iCloud.com.companyName.randomString it started working and synchronising.
If after adding the new container it's red press the refresh button(from under the groups) and try a clean install on your phone and it should work
Thanks to Dave Browning, this is based on his answer.
Following worked for me:
Check the container id
Check the container id used for initialisation of CKContainer
Note: If you are using NSPersistentCloudKitContainer we wouldn't be using the container id directly would be picked automatically from the entitlements file.
Try to disable and enable iCloud on the App ID
Disable and Enable iCloud on App ID
Go to https://developer.apple.com and sign in
Select Certificates, Identifiers & Profiles
Select Identifiers (App IDs)
Edit App ID for the app
Uncheck iCloud
Save
Check iCloud
Quit Xcode and Clear DerivedData
Run app