Contact Us

Use the form on the right to contact us.

You can edit the text in this area, and change where the contact form on the right submits to, by entering edit mode using the modes on the bottom right. 


Oak Ridge, TN, 37830
United States

Swift-Snips

How to filter an array of dictionaries with NSPredicate : Swift 3

Wade Cantley

This uses a real-world example where beacons are being passed, and I am selectively putting close beacons, which are a dictionary of data, into an array.

The notification that contains all the beacon data is repeated over and over so I don't want duplicates making it into my list, so I use a filter to determine if the Beacon data already exists before appending it to the array.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
// I am including lots of "stuff" so that it is clear how this came together in a real example.  
// The code won't work without something feeding becaons to the notification system.  But that doesn't matter.
// The important bits will have /* # */ next to it.
import UIKit
import CoreLocation

class ListBeacons2: UITableViewController {

    // /* 1 */ - Create a varriable that will act as your array container of dictionaries.
    // /* 2 */ - Filter array to see if the new dictionary exists
    // /* 3 */ - If it doesn't append the Dictionary to the array.
    
    
    
    // Setup the variable that will hold the array [] of objects of the type CLBeacon
    /* 1 */ var nearestBeacons = [CLBeacon]()
    
    //....later in code
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //Listen for a message containing an array of beacons
        // This comes from another page that sends the package of beacons every time CoreLocation refreshes its search.
        NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ListBeacons.getAllBeacons),  name: "sendAllBeacons", object: nil)
        
    } //viewDidLoad

    
    func getAllBeacons(notification: NSNotification){
        
        //clear the array
        nearestBeacons = []
        
        // Put the package (that contains the beacons) from the notification into a variable
        var packageOfBeacons = Array(arrayLiteral: notification.object!)
        
        //Put the array of beacons into a variable called "beacons"
        let beacons = packageOfBeacons[0] as! [CLBeacon]
        
        // loop over that array with the contents of type "CLBeacon"
        for eachBeacon in beacons {
            
            // Check the proximity, and if it is close move into the IF
            if (eachBeacon.proximity as CLProximity).rawValue == 1 {
                
                // Filter to see if the beacon already exists in this list.
                // NOTE : We have cast the info coming across as type "CLBeacon" which means that the name of the original key, 
                // such as "uuid" will actually need to be referenced by the object's variable name "proximityUUID".  
                // The casting to a system object of CLBeacon, in line 40, is what does this. 
                /* 2 */let predicate = NSPredicate(format: "%K == %@ AND %K == %@ AND %K == %@" ,
                                            "proximityUUID", eachBeacon.proximityUUID,
                                            "major", eachBeacon.major,
                                            "minor", eachBeacon.minor)
                
                
                let sorted = self.nearestBeacons.filter({
                    return predicate.evaluateWithObject($0)
                })
                
                
                // If there are no beacons like it in the array, append it.
                if sorted.count == 0 {
                    /* 3 */ self.nearestBeacons.append(eachBeacon)
                    print("Added new one")
                    print(eachBeacon)
                }
                
            }
        }
        
        
        
    } // getAllBeacons
}

Here are the contents of the notification... (like 25)

Printing description of notification:
NSConcreteNotification 0x17005ca40 {name = sendAllBeacons; object = (
    "CLBeacon (uuid:F7826DA6-4FA2-4E98-8024-BC5B71E0893E, major:59400, minor:4950, proximity:2 +/- 1.00m, rssi:-77)",
    "CLBeacon (uuid:F7826DA6-4FA2-4E98-8024-BC5B71E0893E, major:44736, minor:11067, proximity:2 +/- 1.14m, rssi:-85)",
    "CLBeacon (uuid:F7826DA6-4FA2-4E98-8024-BC5B71E0893E, major:43597, minor:7697, proximity:2 +/- 1.14m, rssi:-85)",
    "CLBeacon (uuid:F7826DA6-4FA2-4E98-8024-BC5B71E0893E, major:2, minor:24564, proximity:2 +/- 1.67m, rssi:-80)",
    "CLBeacon (uuid:F7826DA6-4FA2-4E98-8024-BC5B71E0893E, major:1, minor:13719, proximity:2 +/- 2.78m, rssi:-84)",
    "CLBeacon (uuid:F7826DA6-4FA2-4E98-8024-BC5B71E0893E, major:3, minor:20, proximity:2 +/- 4.08m, rssi:-78)",
    "CLBeacon (uuid:F7826DA6-4FA2-4E98-8024-BC5B71E0893E, major:3, minor:47301, proximity:2 +/- 4.08m, rssi:-87)",
    "CLBeacon (uuid:F7826DA6-4FA2-4E98-8024-BC5B71E0893E, major:1, minor:20, proximity:3 +/- 3.16m, rssi:-76)"
)}

Here is an example of the contents of "eachBeacon" (line 39)

CLBeacon (uuid:F7826DA6-4FA2-4E98-8024-BC5B71E0893E, major:1, minor:13719, proximity:2 +/- 0.84m, rssi:-72)