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

Sending data through Notifications : Swift 3

Wade Cantley

Here I am using the real-world example of sending data through a notification by sending beacon data to a listener every time the CoreLocation refreshes.  This is intended to demonstrate how the notification services can transmit data to a listener.

// /* 1 */ - In the AppDelegate I am sending a notification 
//every time CoreLocation hears a set of beacons.

// /* 2 */ - In another class, I am listing for the 
// Notice and pass along the data to a method.

// /* 3 */ - Do somethign with the data when the 
// method receives it.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// APPDELEGATE.SWIFT
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

// Later in code a delegate fires off with beacon data...
    
func beaconManager(manager: KTKBeaconManager, didRangeBeacons beacons: [CLBeacon], inRegion region: KTKBeaconRegion) {
    
    //Sends out a notification with a beacon array.
    /* 1 */ NSNotificationCenter.defaultCenter().postNotificationName("sendAllBeacons", object: beacons)
    
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
// LISTBEACONS2.SWIFT
class ListBeacons2: UITableViewController {

    // When the view loads, it starts listening for a notificaiton.
    override func viewDidLoad() {
        super.viewDidLoad()
        
        //Listen for a message containing an array of beacons
        /* 2 */ NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(ListBeacons.getAllBeacons),  name: "sendAllBeacons", object: nil)
    }

    
    func getAllBeacons(notification: NSNotification){
        
        //Do something with the notification data which, in this example,
        // is cast into an array.
        /* 3 */ var beacons = Array(arrayLiteral: notification.object!)
        beacons = beacons[0] as! [CLBeacon]
    }
}

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)

 

 

Adding/Appending Objects to an Array : Swift 3

Wade Cantley

The goal here is just to demonstrate how to add an object to an array using a real-world example.  The code is commented and includes an example of a NSPredicate filter.

Look for /* # */ to indicate where that happens.


// 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 ListBeacons: UITableViewController {

    // /* 1 */ - Create a varriable that will act as your storage container.
    // /* 2 */ - Append CLBeacon objects 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.
                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 {
                    /* 2 */ self.nearestBeacons.append(eachBeacon)
                    print("Added new one")
                    print(eachBeacon)
                }
                
            }
        }
        
        
        
    } // getAllBeacons
}

Commenting and Annotating your code

Wade Cantley

//MARK: - Indicates an annotation with a line above.(dash is optional)
//TODO: Indicates items that need to be addressed
//FIXME: Indicates items that needs to be fixed.

Applying these pragma marks allow the user to see how the page is divided when reviewing the dropdown list of methods.

Nesting a pragma mark in a function also shows the comment as nested in the drop-down method review.

HTTP URL Request In WebView doesn't work

Wade Cantley

If your phone is online and your WebView isn't loading, there is a good chance you are getting a transport security error.  (check the console)

If so, the below could be added to your info.plist file.  This is a pretty blanket permission grant though.


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>NSAppTransportSecurity</key>
    <dict>
        <key>NSAllowsArbitraryLoads</key>
        <true/>
    </dict>
  
  ... other plist settings ...
  </dict>
</plist>

Or, you can get pretty specific about which domains have exceptions and what those exceptions would be.


<key>NSAppTransportSecurity</key>
<dict>
    <key>NSExceptionDomains</key>
    <dict>
        <key>testdomain.com</key>
        <dict>
            <key>NSIncludesSubdomains</key>
            <true/>
            <key>NSExceptionAllowsInsecureHTTPLoads</key>
            <true/>
            <key>NSExceptionRequiresForwardSecrecy</key>
            <true/>
            <key>NSExceptionMinimumTLSVersion</key>
            <string>TLSv1.2</string>
            <key>NSThirdPartyExceptionAllowsInsecureHTTPLoads</key>
            <false/>
            <key>NSThirdPartyExceptionRequiresForwardSecrecy</key>
            <true/>
            <key>NSThirdPartyExceptionMinimumTLSVersion</key>
            <string>TLSv1.2</string>
            <key>NSRequiresCertificateTransparency</key>
            <false/>
        </dict>
    </dict>
</dict>

Notes on Creating Unit Tests in XCode

Wade Cantley

Common Shortcuts for running a build.

  • CMD + u = Build and run entire suite
  • CTRL + CMD + u = Run Without compiling
  • CTRL + CMD + OPT + u = Build and run tests under cursor (focusses on single test or single file)
  • CMD + SHFT + u = Compile the test suite

Create A new Test file

  • Inherit from XCTestCase
  • Append "test" or "Tests" to the filename.
  • File should ONLY be included in the test target
  • Should move the file under tests.
  • Your test file and functions should show up under the test area.

Using XCTestCase


As mentioned before, "testExample" and "testPerformanceExample" below that would run.

import XCTest

//Added as a reference to the primary target.
@testable import Swift3_TestingExamples

class utilMethodsTests: XCTestCase {
    
    let subject = NSObject() //instantiated for teach 'test' method
    
    override func setUp() {
        super.setUp()
        // Put setup code here. This method is called before the invocation of each test method in the class.
        
        print("setup")
    }
    
    override func tearDown() {
        // Put teardown code here. This method is called after the invocation of each test method in the class.
        super.tearDown()
        print("teardown")
    }
    
    func testExample() {
        // This is an example of a functional test case.
        // Use XCTAssert and related functions to verify your tests produce the correct results.
        
        print("textExample")
        
    }
    
    func testPerformanceExample() {
        // This is an example of a performance test case.
        self.measure {
            // Put the code you want to measure the time of here.
            
            print("Measure")
            
        }
        print("Done measuring")
    }
    
    func test_assertion() {
        
        //subject hasn't had anything added to it so it is indeed Nil and will pass.
        XCTAssertNotNil(subject)
    }
    
    func test_assertion2() {
        
        //Subject will equal itself so this will pass
        XCTAssertEqual(subject, subject)
        
    }
    
}

So, the code "@testable import Swift3_TestingExamples" references classes in the primary build target.


When run, the below is output to the console.

Click on the image to expand it.

Let's break down the results.
Here is the load up of the suite data.

2016-10-19 13:47:02.649662 Swift3_TestingExamples[48598:16495047] subsystem: com.apple.UIKit, category: HIDEventFiltered, enable_level: 0, persist_level: 0, default_ttl: 0, info_ttl: 0, debug_ttl: 0, generate_symptoms: 0, enable_oversize: 1, privacy_setting: 2, enable_private_data: 0

2016-10-19 13:47:02.725485 Swift3_TestingExamples[48598:16495047] subsystem: com.apple.UIKit, category: HIDEventIncoming, enable_level: 0, persist_level: 0, default_ttl: 0, info_ttl: 0, debug_ttl: 0, generate_symptoms: 0, enable_oversize: 1, privacy_setting: 2, enable_private_data: 0

2016-10-19 13:47:02.818649 Swift3_TestingExamples[48598:16495046] subsystem: com.apple.BaseBoard, category: MachPort, enable_level: 1, persist_level: 0, default_ttl: 0, info_ttl: 0, debug_ttl: 0, generate_symptoms: 0, enable_oversize: 0, privacy_setting: 0, enable_private_data: 0

2016-10-19 13:47:02.908909 Swift3_TestingExamples[48598:16494963] subsystem: com.apple.UIKit, category: StatusBar, enable_level: 0, persist_level: 0, default_ttl: 0, info_ttl: 0, debug_ttl: 0, generate_symptoms: 0, enable_oversize: 1, privacy_setting: 2, enable_private_data: 0

2016-10-19 13:47:03.068141 Swift3_TestingExamples[48598:16494963] subsystem: com.apple.BackBoardServices.fence, category: App, enable_level: 1, persist_level: 0, default_ttl: 0, info_ttl: 0, debug_ttl: 0, generate_symptoms: 0, enable_oversize: 0, privacy_setting: 0, enable_private_data: 0

Test Suite 'Selected tests' started at 2016-10-19 13:47:03.426

Test Suite 'Swift3_TestingExamplesTests.xctest' started at 2016-10-19 13:47:03.427

 

Test Suite 'utilMethodsTests' started at 2016-10-19 13:47:03.427

Then the first method to run is "testPerformanceExample"
This seemed to loop over the self.measure several times.

Test Case '-[Swift3_TestingExamplesTests.utilMethodsTests testPerformanceExample]' started.
setup

Measure
Measure
Measure
Measure
Measure
Measure
Measure
Measure
Measure
Measure

/Users/chriscantley/Documents/Swift Tutorials/Swift 3/Swift3_TestingExamples/Swift3_TestingExamplesTests/utilMethodsTests.swift:46: Test Case '-[Swift3_TestingExamplesTests.utilMethodsTests testPerformanceExample]' measured [Time, seconds] average: 0.000, relative standard deviation: 119.370%, values: [0.000027, 0.000005, 0.000002, 0.000004, 0.000003, 0.000011, 0.000002, 0.000003, 0.000002, 0.000002], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100

Done measuring
teardown

Test Case '-[Swift3_TestingExamplesTests.utilMethodsTests testPerformanceExample]' passed (0.298 seconds).

The next method to run was "testExample".
As expected, the "Setup" and "teardown" execute around each function. 

Test Case '-[Swift3_TestingExamplesTests.utilMethodsTests testExample]' started.
setup
textExample
teardown
Test Case '-[Swift3_TestingExamplesTests.utilMethodsTests testExample]' passed (0.000 seconds).

Next up was "test_assertion2"

Test Case '-[Swift3_TestingExamplesTests.utilMethodsTests test_assertion2]' started.
setup
teardown
Test Case '-[Swift3_TestingExamplesTests.utilMethodsTests test_assertion2]' passed (0.000 seconds).

Then the last method run was "test_assertion"

Test Case '-[Swift3_TestingExamplesTests.utilMethodsTests test_assertion]' started.
setup
teardown
Test Case '-[Swift3_TestingExamplesTests.utilMethodsTests test_assertion]' passed (0.000 seconds).

Lastly, the suite wrapps up with a total time report and where it saved the test log.

Test Suite 'utilMethodsTests' passed at 2016-10-19 13:47:03.726.
     Executed 4 tests, with 0 failures (0 unexpected) in 0.298 (0.299) seconds

Test Suite 'Swift3_TestingExamplesTests.xctest' passed at 2016-10-19 13:47:03.727.
     Executed 4 tests, with 0 failures (0 unexpected) in 0.298 (0.300) seconds

Test Suite 'Selected tests' passed at 2016-10-19 13:47:03.727.
     Executed 4 tests, with 0 failures (0 unexpected) in 0.298 (0.301) seconds

Test session log:

    /Users/chriscantley/Library/Developer/Xcode/DerivedData/Swift3_TestingExamples-cskiyeuhijxakjepughtltgevngj/Logs/Test/9CC4B557-2C0D-4782-8DA6-854733A07F3C/Session-Swift3_TestingExamplesTests-2016-10-19_134648-IvrJpT.log


Okay, So here is an example of how to connect a Unit Test page to a class.
Below are highlighted pages that we are working with.  

UtilMethods.swift : This has the target method we are testing.
The Unit Test will target "doTheMath()" which is just a simple adding function that spits out an integer.


import Foundation
import UIKit

class UtilMethods: NSObject{
    
    func doTheMath(firstNumber: Int, secondNumber: Int) -> Int {
        
        let addResult =  firstNumber + secondNumber
        return addResult
    }
    
}

UtilMethodsTest.swift : This has the unit test that will evaluate the target method we are testing.


import XCTest
@testable import Swift3_TestingExamples

class UtilMethodsTest: XCTestCase {
    
    //Bring in the target class
    let utilMethods = UtilMethods()
    
    override func setUp() {
        super.setUp()
        // Put setup code here. This method is called before the invocation of each test method in the class.
    }
    
    override func tearDown() {
        // Put teardown code here. This method is called after the invocation of each test method in the class.
        super.tearDown()
    }

    
    func test_doTheMath() {
        
        let firstInt = 2
        let secondInt = 3
        
        let finalAnswer = 5
        
        //Pass the test data to the method we are testing
        let getResultingNumber = utilMethods.doTheMath(firstNumber: firstInt, secondNumber: secondInt)
        
        //Check that it isn't nil
        XCTAssertNotNil(getResultingNumber)
        
        //Does it give the correct answer
        XCTAssertEqual(finalAnswer, getResultingNumber)
        
        //is the final answer greater than its parts?
        XCTAssertGreaterThan(getResultingNumber, firstInt)
        XCTAssertGreaterThan(getResultingNumber, secondInt)
        
        
        
    }
    
    
}

In this example, all the assertions come back true but once you remove the numbers or remove the calculation within the function, errors start to come back. 

Migrating code to Swift 3.0 XCode 8

Wade Cantley

So here are my fixes and their sources just in case you need further explanation as to why it works.  Chances are I won't fully understand all of the changes but first things first - get the code to build.

Read More

Cannot Convert value of type ... to type '[ANyHashable : Any]' in Coercion

Wade Cantley

The ongoing effort to convert my app after an upgrade to XCode 8 and Swift 3.

Just when I thought I was error free, a new set of errors popped up and this is one that is pretty frequent.

I have already run the Swift 3.0 update on the code so this is part of the group of errors that are remaining.  

Here is a link to where I found the answer.

 

http://stackoverflow.com/questions/39630247/swift-3-cannot-convert-value-of-type-nsmutabledictionary-to-expected-argument

The gist is that using "AnyHashable: Any" just doesn't like a dictionary to be mutable.  So I changed the target code to look like this. 

From 

        // [START GOOGLE ANALYTICS]
        let tracker = GAI.sharedInstance().defaultTracker
        tracker?.set(kGAIScreenName, value: storyboardViewName)
        let builder = GAIDictionaryBuilder.createScreenView()
        tracker?.send(builder?.build() as [AnyHashable: Any])
        // [END GOOGLE ANALYTICS]

To

        // [START GOOGLE ANALYTICS]
        let tracker = GAI.sharedInstance().defaultTracker
        tracker?.set(kGAIScreenName, value: storyboardViewName)
        let builder = GAIDictionaryBuilder.createScreenView()
        tracker?.send(builder?.build() as NSDictionary? as? [AnyHashable: Any] ?? [:])
        // [END GOOGLE ANALYTICS]

This seems to have solved the issue.

Use Legacy Language Version

Wade Cantley

“Use Legacy Swift Language Version” (SWIFT_VERSION) is required to be configured correctly for targets which use Swift. Use the [Edit > Convert > To Current Swift Syntax…] menu to choose a Swift version or use the Build Settings editor to configure the build setting directly.

What's going on?

This relates to an imported framework, in this situation it is HanekeSwift, and it would appear there is a version dependency difference that doesn't quite match up.

That said, the app has been updated to Swift 3.0 and it would appear that the app is only compatible with 2.3. 

As always, it is best to look at whether others have addressed the issue and in this situation it looks like someone has created a branch that is updated to the most recent 3.0

https://github.com/Haneke/HanekeSwift/issues/348

But, nothing is ever that simple.  Just copying-&-pasting the update into the terminal doesn't seem to work.  

[computername:ProjectFolderName chriscantley$ pod update"HanekeSwift", :git => 'https://github.com/Haneke/HanekeSwift.git', :branch => 'feature/swift-3'
-bash: https://github.com/Haneke/HanekeSwift.git,: No such file or directory 

And now off to StackExchange to figure out what is going on. There I found a suggestion to update Cocoa Pods.

sudo gem update cocoapods

nope...
So I downloaded the Branch and performed a compare using BeyondCompare and everything was different in the primary directory.  So I just moved everything over from the 3.0 branch to my copy.

After the migration...

After the migration...

Other errors came up, however on removing the derived data (remove or rename the folder for your project at /Users/chriscantley/Library/Developer/Xcode/DerivedData), cleaning the project, executing it to build, cleaning again and then building... a Whole new set of errors came up.

But this one is resolved.