I have a variable. It contains telephone numbers.
I am using like this:
println(person.phoneNumbers!.map( {$0.value} ))
Output:
[555-478-7672, (408) 555-5270, (408) 555-3514]
You can see there are three phone numbers. How can I iterate this variable?
I need something like this:
Phone 1: 555-478-7672
Phone 2: (408) 555-5270
Phone 3: (408) 555-3514
This solution is for Swift 1.2
for (index, number) in enumerate(person.phoneNumbers!.map { $0.value }) {
println("Phone \(index): \(number)")
}
And since I am scared by this ! I would go with the following version
if let numbers = person.phoneNumbers {
for (index, number) in enumerate(numbers.map { $0.value }) {
println("Phone \(index): \(number)")
}
}
Update
The following code tries to answer the question added in the comments below.
if let numbers = person.phoneNumbers {
let list = numbers.map { $0.value }
let json : [String:AnyObject] = ["phoneNumbers": list]
}
Update #2
Please find below the second block of code, updated for Swift 2.0
if let numbers = person.phoneNumbers {
for (index, number) in (numbers.map { $0.value }).enumerate() {
print("Phone \(index): \(number)")
}
}
You can do it like this.
for number in person.phoneNumbers!.map( {$0.value} ) {
println(number)
}
Do it like this,
let extracted = person.phoneNumbers.enumerate().map { index, phone in
return "Phone \(index):\(phone.value)"
}
Related
Here is what my class looks like:
class Card : Object {
#objc dynamic var tags: String = ""
#objc dynamic var set_id: String = ""
}
I want to return number of tags from all Cards with forwarded set_id.
Here is the method:
func totalTags() -> String {
var tagCounter: Int = 0
let realm = try? Realm()
let totalCards = realm!.objects(Card.self).filter("set_id = '\(setId)'") //all Cards with selected set_id, set_id is global var.
for card in 0...totalCards.count {
//every 'card' has tags, but there there can me more tags,
//like : tags="one,twitter,world,Europe"...
//And I want to count all of them for every 'card'
let result = realm.objects(Card.self).filter() //How to filter?
tagCounter += //what? result.count or something?
}
return String(tagCounter)
}
I understand that tags: String contains comma separated elements and you want to find the number of elements.
You can do that by iterating over totalCards. For each card, split the tags into an array and count the number of elements.
for card in totalCards {
tagCounter += card.tags.components(separatedBy: ",").count
}
components(separatedBy:) documentation
I know this was already answered but I just want to share how to do it my own way.
let tagsCount = totalCards.map { $0.tags.components(separatedBy: ",") }.flatMap { $0 }.filter { !$0.isEmpty }.reduce(into: 0, { result, _ in
result += 1
})
Thanks. Happy coding :)
Saving data to Firebase and retrieving data to display in label is working but when I try to add an Int to the label it overwrites the label data.
I add to the label with
var pointsCount = 0
func addPoints(_ points: NSInteger) {
pointsCount += points
pointsLabel.text = "\(self.pointsCount)"
}
Then I save the label contents to Firebase as a string.
func saveToFirebase() {
let userID = Auth.auth().currentUser!.uid
let points: String = ("\(self.pointsCount)")
let savedScores = ["points": points,
"blah": blah,
"blahblah": blahblah]
Database.database().reference().child("users").child(userID).updateChildValues(savedScores, withCompletionBlock:
{
(error, ref) in
if let error = error
{
print(error.localizedDescription)
return
}
else
{
print("Data saved successfully!")
}
})
}
I retrieve the string from the Realtime Database and convert it to an Int.
func retrieveFromFirebase() {
guard let userID = Auth.auth().currentUser?.uid else { return }
Database.database().reference().child("users").child(userID).child("points").observeSingleEvent(of: .value) {
(snapshot)
in
guard let points = snapshot.value as? String else { return }
let pointsString = points
if let pointsInt = NumberFormatter().number(from: pointsString) {
let retrievedPoints = pointsInt.intValue
self.pointsLabel.text = "\(retrievedPoints)"
} else {
print("NOT WORKING")
}
}
The label displays the retrieved data from the database perfectly.
Then if I try to add more points to the label, it erases the retrieved data and starts adding from 0 as if the label displayed nil.
I've been searching for answers all day for what seems to be a rather simple problem but haven't been able to figure it out due to my lack of experience.
I have tried separating everything and saving the data as an integer and retrieving the data back as an integer but the issue seems to be from the addPoints function.
Please let me know what I'm doing wrong.
The solution ended up being as simple as adding an 'if' statement to the points function.
Instead of...
func addPoints(_ points: NSInteger) {
pointsCount += points
pointsLabel.text = "\(self.pointsCount)"
}
It needed to be...
func addPoints(_ points: NSInteger) {
if let text = self.pointsLabel.text, var pointsCount = Int(text)
{
pointsCount += points
pointsLabel.text = "\(pointsCount)"
}
}
I'm currently able to get a contact from the contacts app, but the problem I'm facing that I need to be able to select the contact I want to import to my app , if the contact have more than 1 phone number, I always get the first number, here is the code I'm using:
func contactPicker(_ picker: CNContactPickerViewController, didSelect contactProperty: CNContactProperty) {
let numbers = contactProperty.contact.phoneNumbers.first
let firstName = contactProperty.contact.givenName
let lastName = contactProperty.contact.familyName
let phoneNumber = (numbers?.value)?.stringValue ?? ""
/// Duplicate phone numbers will not be saved
if phoneNumbers.contains(phoneNumber) {
return
}
/// Saving selected contact in Core Data
CoreDataManager.sharedInstance.savePhoneNumberInCoreData(FirstName: firstName, LastName: lastName, PhoneNumber: phoneNumber)
DispatchQueue.main.async { [weak self] in
self?.tableView.reloadData()
}
}
The problem with line:
contactProperty.contact.phoneNumbers.first
There are two options only for contactProperty.contact.phoneNumbers .first or .last
If there is something like .selected, it would solve the problem.
There is something called Main telephone number that you could use
var phoneNumber: String?
if let mainNumber = numbers.first(where: { $0.label == CNLabelPhoneNumberMain }) {
phoneNumber = mainNumber.value.stringValue
} else {
phoneNumber = numbers.first?.value.stringValue //or some other default value
}
Note that I changed the definition of numbers to be the array of phone numbers
let numbers = contactProperty.contact.phoneNumbers
Full code:
func contactPicker(_ picker: CNContactPickerViewController, didSelect contactProperty: CNContactProperty) {
let numbers = contactProperty.contact.phoneNumbers
var phoneNumber: String?
if let mainNumber = numbers.first(where: { $0.label == CNLabelPhoneNumberMain }) {
phoneNumber = mainNumber.value.stringValue
} else {
phoneNumber = numbers.first?.value.stringValue //or some other default value
}
if phoneNumber == nil || phoneNumbers.contains(phoneNumber) {
return
}
let firstName = contactProperty.contact.givenName
let lastName = contactProperty.contact.familyName
CoreDataManager.sharedInstance.savePhoneNumberInCoreData(FirstName: firstName, LastName: lastName, PhoneNumber: phoneNumber)
DispatchQueue.main.async { [weak self] in
self?.tableView.reloadData()
}
}
I'm agree with solution of Joakim Danielson.
But there are one more solution to get specific phone number which is stored in mobile number like home, mobile, fax etc.
Get all numbers from contact and enumerate on every number and check labeled values. See following code.
let numbers = contact.phoneNumbers
numbers.forEach { (c) in
if let label = c.label {
let localizedLabel = CNLabeledValue<NSCopying & NSSecureCoding>.localizedString(forLabel: label)
print("\(localizedLabel)")
switch localizedLabel.lowercased() {
case "home":
let homeNumber = c.value
break
case "mobile":
let mobileNumber = c.value
break
default:
break
}
}
}
contactProperty.contacts is a back-reference to the CNContact the selected property lives in...
Each property is represented by an instance of CNContactProperty, which provides a tuple that can contain three or five values, depending on whether the property is a member of an array of labeled values.
CNContactProperty
So, you should use the property's Information vars directly:
For example, the phoneNumbers property is a member of an array of labeled values, so the CNContactProperty tuple contains the contact, key, value, identifier, and label.
CNContactProperty
NOTE: I learned this from reading another S-O answer, but I can't seem to find it right now. If appropriate, dupe or edit thus,
i need to check when i iterated through the last item. I cannot just put the line after my for loop because then i receive always an empty list. I tried the following but this one doesnt work:
.observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.exists(){
for rest in snapshot.children.allObjects.count as! [DataSnapshot] {
let refi = Database.database().reference().child("Users")
refi.observeSingleEvent(of: .value, with: { (snapshoti) in
if snapshoti.value as! String == "active"{
let userid = rest.key
self.someProtocol[rest.key] = self.checksum
if self.checksum == self.someProtocol.count-1 {
self.sortUsers()
}else{
}
self.checksum = self.checksum+1
}
})
}
The answer of dr_barto will work but needs the following adaptation:
for (idx, element) in array.enumerated() {
if idx == array.endIndex-1 {
// handling the last element
}
}
From the Apple documentation:
endIndex is the array’s “past the end” position—that is, the position one greater than the last valid subscript argument
If you don't want to use index, you can check element like this:
for element in array {
if element == array.first {
print("first")
} else if element == array.last {
print("last")
}
}
EDIT my answer won't work since (as pointed out in the comments) endIndex is never going to match any index value returned from enumerated because it denotes the index after the last element. See https://stackoverflow.com/a/53341276/5471218 for how it's done correctly :)
As pointed out in the comments, you should use enumerated; given an array, you'd use it like this:
for (idx, element) in array.enumerated() {
if idx == array.endIndex {
// handling the last element
}
}
Could be made into an extension also:
extension Array {
func lastIndex(index: Int) -> Bool {
index == endIndex-1
}
}
or
extension Array where Element : Equatable {
func isLast(element: Element) -> Bool {
last == element
}
}
for the newbies in swift like me..
I attached the full code.
ref.child("BookShelf").child(userID!).observeSingleEvent(of: .value, with: { (snapshot) in
if let datasnap = snapshot.value as? Dictionary<String, Any> {
for (index,data) in datasnap.enumerated() {
let book = Book(userid: data.key , dataSnap: data.value as! Dictionary<String, Any>)
bookShelf.append(book)
print("index",index)
print("datasnap.count-1",datasnap.count-1)
if(index == datasnap.count-1){
print("1 bookshelf count getFirebaseData()",bookShelf.count)
self.showDashBoard(bookShelf: bookShelf)
}
}
//if last data then trigger show
}
}) { (error) in
print(error.localizedDescription)
}
Try this solution
.observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.exists(){
for (index,rest) in snapshot.children.allObjects {
if index == (snapshot.children.allObjects.count - 1){
//yeah finally you are getting the last object :)
}
}
})
}
P.S: I Assume snapshot.children.allObjects is an array of [DataSnapshot]
Ok so lets say I have an custom object for vocabulary words, alternate way of being written, and their meaning.
class VocabEntry {
var kanji:String?
var kana:String?
var meaning:String?
}
Then I have an array comprised of these objects. Here's one for example.
let newVocabWord = VocabEntry()
newVocabWord.kanji = "下さい”
newVocabWord.kana = "ください”
newVocabWord.meaning = "please"
Now I have a string of text:
let testString = "すみません、十階のボタンを押して下さい"
How can I compare that string to my array of custom objects (that contain strings) and reference the matches?
I tried.
if vocabArray.contains( { $0.kanji == testString }) {
print("yes")
}
But that trying to match the entire string. If I change testString to "下さい" it works, but that's not what I'm looking for. What I want is for it to say "Here I found 下さい in xx object. Here's the index number."
You can use indexOf() with a predicate to find the index of a
matching entry, and containsString() to search for substrings.
Since the kanji property is optional, you have to check that via
optional binding:
if let index = vocabArray.indexOf({ entry in
if let kanji = entry.kanji {
// check if `testString` contains `kanji`:
return testString.containsString(kanji)
} else {
// `entry.kanji` is `nil`: no match
return false
}
}) {
print("Found at index:", index)
} else {
print("Not found")
}
This can be written more concise as
if let index = vocabArray.indexOf({
$0.kanji.flatMap { testString.containsString($0) } ?? false
}) {
print("Found at index:", index)
} else {
print("Not found")
}
To get the indices of all matching entries, the following would work:
let matchingIndices = vocabArray.enumerate().filter { (idx, entry) in
// filter matching entries
entry.kanji.flatMap { testString.containsString($0) } ?? false
}.map {
// reduce to index
(idx, entry) in idx
}
print("Found at indices:", matchingIndices)