This method is called twice in a row, once for Emails, and again for Phone numbers, in an attempt to save that info to a separate object for each contact as I couldn't figure out how to save nested arrays to a single Parse object.
The baffling thing is that if I only do the method for emails, all save fine. If I only do it for Phone numbers, all save fine. When I do emails and then phones, only phones save. When I do phones and then emails, emails save.
The exception is that if a given contact only has an email or phone it always succeeds. Commenting out the pointer to Contacts didn't do anything to help, so it appears there's some race condition or locking error that's going on here with Parse.
Any ideas? I'd love to learn how to do nested arrays to parse too if that's possible, but I tried a few things and couldn't figure out how to get that to work.
func updateMultiField(person: ABRecord, parseObject: PFObject, fieldToGrab: ABPropertyID, contact: PFObject){
var multiArray:ABMultiValueRef = extractABMultiRef(ABRecordCopyValue(person, fieldToGrab))!
var parseObjects: [PFObject] = [PFObject]()
for (var j = 0; j < ABMultiValueGetCount(multiArray); ++j){
var multi = MultiRef()
var multiValueRaw = ABMultiValueCopyValueAtIndex(multiArray, j)
multi.value = extractMultiValue(multiValueRaw)
if (multi.value != nil) {
//get type
multi.type = getMultiType(fieldToGrab)
//get label
multi.label = extractMultiLabel(ABMultiValueCopyLabelAtIndex(multiArray, j))
//get id
multi.id = String(Int(ABMultiValueGetIdentifierAtIndex(multiArray, j)))
parseObject[parseContactIdFieldName] = contact
parseObject[labelFieldName] = multi.label
parseObject[valueFieldName] = multi.value
parseObject[multiIdFieldName] = multi.id
parseObject[typeFieldName] = multi.type
println("\(multi.type) multi about to be saved with value of \(multi.value)")
parseObjects.insert(parseObject, atIndex: j)
//save
}//if
}//for
PFObject.saveAll(parseObjects)
}//updateField
An earlier method calls this method twice:
updateMultiField(person, parseObject: multis, fieldToGrab: kABPersonPhoneProperty, contact: contact)
updateMultiField(person, parseObject: multis, fieldToGrab: kABPersonEmailProperty, contact: contact)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
UPDATE:
Here's the fixed code, which no longer passes a PFObject into the method and tries to save the same object over and over, but rather instantiates it in the for loop (5th line), which resolves the problem:
func updateMultiField(person: ABRecord, fieldToGrab: ABPropertyID, contact: PFObject){
var multiArray:ABMultiValueRef = extractABMultiRef(ABRecordCopyValue(person, fieldToGrab))!
var parseObjects: [PFObject] = [PFObject]()
for (var j = 0; j < ABMultiValueGetCount(multiArray); ++j){
var parseObject = PFObject(className: multisObjectName)
var multi = MultiRef()
var multiValueRaw = ABMultiValueCopyValueAtIndex(multiArray, j)
multi.value = extractMultiValue(multiValueRaw)
if (multi.value != nil) {
//get type
multi.type = getMultiType(fieldToGrab)
//get label
multi.label = extractMultiLabel(ABMultiValueCopyLabelAtIndex(multiArray, j))
//get id
multi.id = String(Int(ABMultiValueGetIdentifierAtIndex(multiArray, j)))
parseObject[parseContactIdFieldName] = contact
parseObject[labelFieldName] = multi.label
parseObject[valueFieldName] = multi.value
parseObject[multiIdFieldName] = multi.id
parseObject[typeFieldName] = multi.type
println("\(multi.type) multi about to be saved with value of \(multi.value)")
parseObjects.insert(parseObject, atIndex: j)
}//if
}//for
PFObject.saveAll(parseObjects)
}//updateMultiField
You seems trying to save the same parseObject 2 times without retrieving it in between. the last save will be always ignored since the object you are trying to save is outdated (a newest version is already on server).
you should retrieve the parseObject from server and then change and save again.
Related
i have this in EF4:
SomeClass ObjectX;
using (MyBDcontext ctx = ModelHelper.GetObjectContext())
ObjectX = ctx.GetObjectByKey(Entity.EntityKey);
ObjectX.SomeProperty = StoreValue + 1;
using (MyBDcontext ctx = ModelHelper.GetObjectContext()){
//I get the same object
SomeClass AnotherObjectX = (SomeClass)ctx.GetObjectByKey(ObjectX.EntityKey);
//I do a DeepCopy(copyFrom, copyTo)
ModelHelper.DeepCopy(ObjectX, AnotherObjectX);
var objects = (from entry in ctx.ObjectStateManager.GetObjectStateEntries(
EntityState.Modified)
where entry.EntityKey != null
select entry.Entity);
ctx.Refresh(RefreshMode.StoreWins, objects);
}
My problem is that the object ObjectX after run Refresh() is with the same values, not the store values. why?
in var objects exist an instance of ObjectX and is refresh with the store values but my intance not change.
sorry about my english, thanks for the help...
Edit:
thank for the help Gert, i will try to explain my self:
//Store values
ObjectX.SomeProperty = 0
//Edit in memory
ObjectX.SomeProperty = 1
//For some reason i wanna cancel all change and return store values ctx.Refresh(RefreshMode.StoreWins, objects);
//ObjectX.SomeProperty have the value = 1, it's supust to be 0 (store value)
might sound like a basic question--but I'm not seeing where I am going wrong..
I end up with either of these two scenarios:
I keep getting the error "Could not cast value of type __NSCFNumber to NSSTring". if I use extractedSku = skuList[i]!.value["sku"] as! String
If I remove as! String it saves it, but it isn't saved as a string. How do I get this to be saved as a string?
I have appended data from firebase into an array
skuArray = [AnyObject?]()
in viewDidLoad, I am iterating skuArray to extract the 'sku' and store into a variable.
var skuArray = [AnyObject?]()
var productDetailArray = [AnyObject?]()
data stored in Sku Array is:
[Optional(Snap (aRandomKey) {
active = 1;
sku = 888888;
})]
viewDidLoad:
let skuList = self.skuArray
for var i = 0; i < skuList.count ; ++i{
let extractedSku = skuList[i]!.value["sku"] as! String
// go into database and extract "products" details by sku
self.databaseRef.child("products/\(extractedSku)").observeEventType(.ChildAdded, withBlock: { (snapshot:FIRDataSnapshot) in
self.productDetailArray.append(snapshot)
})
Since the underlying type is NSNumber, use the stringValue property to get a String:
if let extractedSku = (skuList[i]?.value["sku"] as? NSNumber)?.stringValue {
// use extractedSku which is of type String
}
I have 2 Realm Models:
class CourseModel: Object {
dynamic var coursename = ""
dynamic var par3field = 0
dynamic var par4field = 0
dynamic var par5field = 0
let scoreModels: List<ScoresModel> = List<ScoresModel>()
override internal static func primaryKey() -> String? { return "coursename" }
}
class ScoresModel: Object {
dynamic var dateplayed = ""
var courseModel: CourseModel? {
return linkingObjects(CourseModel.self, forProperty: "scoreModels").first
}
}
The app user will first add a new course for which I use CourseModel. As the user plays a course they enter scores for that course, for which I use ScoresModel, hence the primary key 'coursename'.
I query the CourseModel with
let realm = try Realm()
let results = realm.objects(CourseModel)
return results
and it produces the following result
Results<CourseModel> (
[0] CourseModel {
coursename = First Course;
par3field = 4;
par4field = 10;
par5field = 4;
scoreModels = RLMArray <0x797a36d0> (
[0] ScoresModel {
dateplayed = Apr 5, 2016; },
[1] ScoresModel {
dateplayed = Mar 3, 2016; }
);
},
[1] CourseModel {
coursename = Second Course;
par3field = 4;
par4field = 10;
par5field = 4;
scoreModels = RLMArray <0x7a046f40> (
[0] ScoresModel {
dateplayed = Apr 5, 2016; }
);
}
)
The ScoresModel produces a similar result but without the CourseModel data.
The ScoresModel has a lot of data in it, I only showed 'dateplayed' here to keep it short.
My question is this; when I've extracted the data from Realm how can I access a particular field to work with that data, i.e. how do I get the par5field data to do calculations with it, and also the 2nd question how do I get to the scoreModels data, for example 'dateplayed' to list the dates in a table for example?
When you perform a query against Realm, the results are returned in a Results object that behaves exactly like an array. So you need to iterate through each object to access the properties you want for each one.
To answer your first question, to access the par5field property (From just the first object):
let firstObject? = results.first
let par5field = firstObject.par5field
// Do calculations with it
For your second question, scoreModels is just a standard array object, so you can just insert the values it into a table view as you would a standard Array object.
If you wanted to list ALL of the ScoreModel objects, regardless of which CourseModel objects they belong to, you can perform a Realm query to get them directly.
let realm = try! Realm()
let results = realm.objects(ScoreModel)
return results
I am trying to get duplicate CNContacts inside an Array:
Code:
func fetchDuplicateContacts(){
self.didFinished_getDuplicateContacts = false;
self.arr_duplicateContacts.removeAllObjects()
NSLog("Start fetch duplicate", "")
self.duplicateContactsCount = 0;
for (var i: Int = 0; i < self.arr_allContacts.count; i++){
let contact1 = self.arr_allContacts[i]
var hasDuplicate: Bool = false
let arr_childs: NSMutableArray = NSMutableArray()
for (var j: Int = 0; j < self.arr_allContacts.count; j++){
let contact2 = self.arr_allContacts[j]
if(contact1 != contact2){
if CNContactFormatter.stringFromContact(contact1, style: .FullName) == CNContactFormatter.stringFromContact(contact2, style: .FullName){
if(self.checkIfContactsHaveSamePhones(contact1, contact2: contact2)){
print("Move on cuz duplicate: \(CNContactFormatter.stringFromContact(contact1, style: .FullName))")
duplicateContactsCount += 1;
arr_childs.addObject(NSDictionary(objects: [contact2, false], forKeys: ["object", "checked"]))
hasDuplicate = true
}
}
}
}
// This is for adding first contact to main array to be the first. It's important to be like this
if hasDuplicate == true{
arr_childs.insertObject(NSDictionary(objects: [contact1, false], forKeys: ["object", "checked"]), atIndex: 0)
var key: NSString? = CNContactFormatter.stringFromContact(contact1, style: .FullName)
if key == nil {
key = "no name \(i)"
}
arr_duplicateContacts.addObject([key! : arr_childs])
}
}
NSLog("End fetch duplicate w results \(self.duplicateContactsCount)", "")
self.didFinished_getDuplicateContacts = true;
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.mytable.reloadData()
})
}
I loop trough the array, check each 2 contacts if have same name and same numbers and if true, add them to an Array of NSDictionary (whose key is contact name, and object is an NSDictionary which contain CNContact and a bool "checked").. A little bit messy, I know.
*The problem: I will get duplicates inside main array *
Let's say that I have Contact1["bob", "07100"], Contact2["bob","07100"]. When "j" loop will check if Contact1 == Contact1 which is true and skips adding object to array and after that checks Contact1 == Contact2(false and then it sees that Contact2 is a duplicate and ads to main array). After i++ it does same thing with Contact2 and this is the problem (try figure out for 3 objects if is not clear).
Tried solving it by asuming that duplicate contacts are one after another and used that i = j but if Contact1 == Contact3, Contact2 will be skiped from verification
Any idea how to solve this problem?
I thought I would propose another way of achieving what you are trying to do leveraging some of Swift's built in functions. One thing to note about this solution is that it will not help you determine which is the duplicate and which is the contact the user wants to keep. This is actually an issue with your approach because what happens if both contacts have the same name but a different number? This is why in my solution I group the duplicates and you (or your user) can decide what to do.
Get an array of the all the full names:
let fullNames = self.arr_allContacts.map(CNContactFormatter.stringFromContact($0, style: .FullName))
Make that array unique
let uniqueArray = Array(Set(fullNames))
This is the step that I will be doing differently than what you are doing. I will build an array of arrays because I think it will get to where you want to go.
var contactGroupedByUnique = [Array]()
for (fullName in uniqueArray) {
var group = self.arr_allContacts.filter {
CNContactFormatter.stringFromContact($0, style: .FullName) == fullName
}
contactGroupedByUnique.append(group)
}
Now you can do the following:
contactGroupedByUnique.count = //number of unique contact
contactGroupedByUnique[index] = //number of duplicates of that contact
I don't have time to test the code but this should get you there.
I have two collections of phone numbers that I want to compare to see if any match. In other languages, I'd loop through one collection, add it to a collection var type that requires uniqueness, loop through the other and check for matches such as:
var phones = ["1","2","3"]
var phones2 = ["2","5","6"]
var uniqueCollection: Set = Set()
for var i = 0; i < phones.count; i++ {
if (uniqueCollection.containsKey(phones[i]) == false){
uniqueCollection.add(phones[i])
}
}
var anyMatch = false
for var j = 0; j < phones2.count; j++{
if uniqueCollection.containsKey(phones2[j]) {
anyMatch = true
}
}
So far I haven't found any way to do this as Swift Maps seem to be a transform, Dictionaries require a value to go with the keys, and don't have an explicit "containsKey()" type function, and it doesn't seem there's another collection like "a hash table" with a method to see if a var is in there.
http://www.weheartswift.com/higher-order-functions-map-filter-reduce-and-more/
http://nshipster.com/swift-comparison-protocols/
Assuming this doesn't exist, I'm planning to just go the long way of double loops, which will suck for performance if there's ever two large collections.
func checkMultisAnyMatch(personMultis: [[AnyObject]], parseMultis: [[AnyObject]]) -> Bool{
//Put all the phone #'s or emails for the person into an Array
//Put all the phone #'s or emails for the PF contact into an array
//Loop through the phones in the parseContact
//if any match, break the loop, and set anyPhoneMatch = true
var anyMatch = false
for var i = 0; i < personMultis.count; i++ {
//order is Id, label, value, type
//that means it's in the 3rd column, or #2 subscript
var personMulti:AnyObject? = personMultis[i][2]
if (personMulti != nil) {
for var j = 0; j < parseMultis.count; j++ {
//order is Id, label, value, type
var parseMulti:AnyObject? = parseMultis[j][2]
if parseMulti != nil {
if parseMulti! as NSString == personMulti! as NSString {
anyMatch = true
}//if 4
}//if 3
}//for 2
}//if
}//for
return anyMatch
}
Would NSSet work for you?
func intersectsSet(_ otherSet: NSSet) -> Bool
Returns a Boolean value that indicates whether at least one object in the receiving set is also present in another given set.
You can create an NSSet from an NSArray.
var set1 = NSSet(array:["number1", "number2", "number3"])
var set2 = NSSet(array:["number4", "number2", "number5"])
var set3 = NSSet(array:["number4", "number5", "number6"])
let contains1 = set1.intersectsSet(set2) // true
let contains2 = set1.intersectsSet(set3) // false