Repository: FlexMonkey/SwiftSpace
Branch: master
Commit: 957360de3289
Files: 18
Total size: 51.3 KB
Directory structure:
gitextract_mwztu8xg/
├── README.md
├── SwiftSpace/
│ ├── AppDelegate.swift
│ ├── Assets.xcassets/
│ │ └── AppIcon.appiconset/
│ │ └── Contents.json
│ ├── Base.lproj/
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ ├── Info.plist
│ ├── UIBezierPathExtension.swift
│ └── ViewController.swift
├── SwiftSpace.xcodeproj/
│ ├── project.pbxproj
│ ├── project.xcworkspace/
│ │ ├── contents.xcworkspacedata
│ │ └── xcuserdata/
│ │ └── simon_non_admin.xcuserdatad/
│ │ └── UserInterfaceState.xcuserstate
│ └── xcuserdata/
│ └── simon_non_admin.xcuserdatad/
│ ├── xcdebugger/
│ │ └── Breakpoints_v2.xcbkptlist
│ └── xcschemes/
│ ├── SwiftSpace.xcscheme
│ └── xcschememanagement.plist
├── SwiftSpaceTests/
│ ├── Info.plist
│ └── SwiftSpaceTests.swift
└── SwiftSpaceUITests/
├── Info.plist
└── SwiftSpaceUITests.swift
================================================
FILE CONTENTS
================================================
================================================
FILE: README.md
================================================
# SwiftSpace
Gyroscope Driven Drawing in 3D Space
Companion project to: http://flexmonkey.blogspot.co.uk/2015/08/coremotion-controlled-3d-sketching-on.html
I was really impressed by a demo of InkScape that I read about in Creative Applications recently. InkScape is an Android app which allows users to sketch in a 3D space that's controlled by the device's accelerometer. It's inspired by Rhonda which pre-dates accelerometers and uses a trackball instead.
Of course, my first thought was, "how can I do this in Swift?". I've never done any work with CoreMotion before, so this was a good opportunity to learn some new stuff. My first port of call was this excellent article on iOS motion at NSHipster.
My plan for the application was to have a SceneKit scene with a motion controlled camera rotating around an offset pivot point at the centre of the SceneKit world. With each touchesBegan(), I'd create a new flat box in the centre of the screen that aligned with the camera and on touchesMoved(), I'd use the touch location to append to a path that I'd draw onto a CAShapeLayer that I'd use as the diffuse material for the newly created geometry.
Easy! Let's break it down:
## Creating the Camera
I wanted the camera at to always point at and rotate around the centre of the world while being slightly offset from it. The two things to help this are the camera's pivot property and using a "look at constraint". First off, I create a node to represent the centre of the world and the camera itself:
```swift
let centreNode = SCNNode()
centreNode.position = SCNVector3(x: 0, y: 0, z: 0)
scene.rootNode.addChildNode(centreNode)
let camera = SCNCamera()
camera.xFov = 20
camera.yFov = 20
```
Next, an SCNLookAtConstraint means that however I translate the camera, it will always point at the centre:
```swift
let constraint = SCNLookAtConstraint(target: centreNode)
cameraNode.constraints = [constraint]
```
...and finally, setting the camera's pivot will reposition it but have it rotate around the centre of the world:
```swift
cameraNode.pivot = SCNMatrix4MakeTranslation(0, 0, -cameraDistance)
```
## Handling iPhone Motion
Next up is handling the iPhone's motion to rotate the camera. Remembering that the iPhone's roll is its rotation along the front-to-back axis and its pitch is its rotation along its side-to-side axis:
...I'll use those properties to control my camera's x and y Euler angles.
The first step is to create an instance of CMMotionManager and ensure it's available and working (so this code won't work on the simulator):
```swift
let motionManager = CMMotionManager()
guard motionManager.gyroAvailable else
{
fatalError("CMMotionManager not available.")
}
```
Next up, I start the motion manager with a little block of code that's invoked with each update. I use a tuple to store the initial attitude of the iPhone and simple use the difference between that initial value and the current attitude to set the camera's Euler angles:
```swift
let queue = NSOperationQueue.mainQueue
motionManager.deviceMotionUpdateInterval = 1 / 30
motionManager.startDeviceMotionUpdatesToQueue(queue())
{
(deviceMotionData: CMDeviceMotion?, error: NSError?) in
if let deviceMotionData = deviceMotionData
{
if (self.initialAttitude == nil)
{
self.initialAttitude = (deviceMotionData.attitude.roll,
deviceMotionData.attitude.pitch)
}
self.cameraNode.eulerAngles.y = Float(self.initialAttitude!.roll - deviceMotionData.attitude.roll)
self.cameraNode.eulerAngles.x = Float(self.initialAttitude!.pitch - deviceMotionData.attitude.pitch)
}
}
```
##Drawing in 3D
Since I know the angles of my camera, it's pretty simple to align the target geometry for drawing on the touchesBegan() method - it just shares the same attitude:
```swift
currentDrawingNode = SCNNode(geometry: SCNBox(width: 1, height: 1, length: 0, chamferRadius: 0))
currentDrawingNode.eulerAngles.x = self.cameraNode.eulerAngles.x
currentDrawingNode.eulerAngles.y = self.cameraNode.eulerAngles.y
```
At the same time, I create a new CAShapeLayer that will contain a stroked path that follows the user's finger:
```swift
currentDrawingLayer = CAShapeLayer()
let material = SCNMaterial()
material.diffuse.contents = currentDrawingLayer
material.lightingModelName = SCNLightingModelConstant
currentDrawingNode.geometry?.materials = [material]
```
On touchesMoved(), I need to convert the location in the main view to the location on the geometry. Since this geometry has a size of 1 x 1 (from -0.5 through 0.5 in both directions), I'll need to convert that to coordinates in my CAShapeLayer (arbitrarily set to 512 x 512) to add points its path.
There are a few steps to do this, taking the locationInView() of the first item in the touches set, I pass it into hitTest() on my SceneKit scene. This returns an array of SCNHitTestResults for all the geometries underneath the touch which I filter for the current geometry and then simply rescale the result's localCoordinates to find the coordinates on the current CAShapeLayer:
```swift
let locationInView = touches.first?.locationInView(view)
if let hitTestResult:SCNHitTestResult = sceneKitView.hitTest(locationInView!, options: nil).filter( { $0.node == currentDrawingNode }).first,
currentDrawingLayer = currentDrawingLayer
{
let drawPath = UIBezierPath(CGPath: currentDrawingLayer.path!)
let newX = CGFloat((hitTestResult.localCoordinates.x + 0.5) * Float(currentDrawingLayerSize))
let newY = CGFloat((hitTestResult.localCoordinates.y + 0.5) * Float(currentDrawingLayerSize))
drawPath.addLineToPoint(CGPoint(x: newX, y: newY))
currentDrawingLayer.path = drawPath.CGPath
}
```
...and that's kind of it!
The source code to this project is available here at my GitHub repository. It was developed under Xcode 7 beta 5 and tested on my iPhone 6 running iOS 8.4.1.
================================================
FILE: SwiftSpace/AppDelegate.swift
================================================
//
// AppDelegate.swift
// SwiftSpace
//
// Created by SIMON_NON_ADMIN on 20/08/2015.
// Copyright © 2015 Simon Gladman. All rights reserved.
//
import UIKit
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
return true
}
func applicationWillResignActive(application: UIApplication) {
// Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
// Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}
func applicationDidEnterBackground(application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
}
func applicationWillEnterForeground(application: UIApplication) {
// Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}
func applicationDidBecomeActive(application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
}
func applicationWillTerminate(application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
}
}
================================================
FILE: SwiftSpace/Assets.xcassets/AppIcon.appiconset/Contents.json
================================================
{
"images" : [
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "3x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "1x"
},
{
"idiom" : "ipad",
"size" : "76x76",
"scale" : "2x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
================================================
FILE: SwiftSpace/Base.lproj/LaunchScreen.storyboard
================================================
================================================
FILE: SwiftSpace/Base.lproj/Main.storyboard
================================================
================================================
FILE: SwiftSpace/Info.plist
================================================
CFBundleDevelopmentRegion
en
CFBundleExecutable
$(EXECUTABLE_NAME)
CFBundleIdentifier
$(PRODUCT_BUNDLE_IDENTIFIER)
CFBundleInfoDictionaryVersion
6.0
CFBundleName
$(PRODUCT_NAME)
CFBundlePackageType
APPL
CFBundleShortVersionString
1.0
CFBundleSignature
????
CFBundleVersion
1
LSRequiresIPhoneOS
UILaunchStoryboardName
LaunchScreen
UIMainStoryboardFile
Main
UIRequiredDeviceCapabilities
armv7
UISupportedInterfaceOrientations
UIInterfaceOrientationPortrait
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
UISupportedInterfaceOrientations~ipad
UIInterfaceOrientationPortrait
UIInterfaceOrientationPortraitUpsideDown
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
================================================
FILE: SwiftSpace/UIBezierPathExtension.swift
================================================
//
// UIBezierPathExtension.swift
// SwiftSpace
//
// Created by Simon Gladman on 07/11/2015.
// Copyright © 2015 Simon Gladman. All rights reserved.
//
import UIKit
extension UIBezierPath
{
func interpolatePointsWithHermite(interpolationPoints : [CGPoint])
{
let n = interpolationPoints.count - 1
for var ii = 0; ii < n; ++ii
{
var currentPoint = interpolationPoints[ii]
if ii == 0
{
self.moveToPoint(interpolationPoints[0])
}
var nextii = (ii + 1) % interpolationPoints.count
var previi = (ii - 1 < 0 ? interpolationPoints.count - 1 : ii-1);
var previousPoint = interpolationPoints[previi]
var nextPoint = interpolationPoints[nextii]
let endPoint = nextPoint;
var mx : CGFloat = 0.0
var my : CGFloat = 0.0
if ii > 0
{
mx = (nextPoint.x - currentPoint.x) * 0.5 + (currentPoint.x - previousPoint.x) * 0.5;
my = (nextPoint.y - currentPoint.y) * 0.5 + (currentPoint.y - previousPoint.y) * 0.5;
}
else
{
mx = (nextPoint.x - currentPoint.x) * 0.5;
my = (nextPoint.y - currentPoint.y) * 0.5;
}
let controlPoint1 = CGPoint(x: currentPoint.x + mx / 3.0, y: currentPoint.y + my / 3.0)
currentPoint = interpolationPoints[nextii]
nextii = (nextii + 1) % interpolationPoints.count
previi = ii;
previousPoint = interpolationPoints[previi]
nextPoint = interpolationPoints[nextii]
if ii < n - 1
{
mx = (nextPoint.x - currentPoint.x) * 0.5 + (currentPoint.x - previousPoint.x) * 0.5;
my = (nextPoint.y - currentPoint.y) * 0.5 + (currentPoint.y - previousPoint.y) * 0.5;
}
else
{
mx = (currentPoint.x - previousPoint.x) * 0.5;
my = (currentPoint.y - previousPoint.y) * 0.5;
}
let controlPoint2 = CGPoint(x: currentPoint.x - mx / 3.0, y: currentPoint.y - my / 3.0)
self.addCurveToPoint(endPoint, controlPoint1: controlPoint1, controlPoint2: controlPoint2)
}
}
}
================================================
FILE: SwiftSpace/ViewController.swift
================================================
//
// ViewController.swift
// SwiftSpace
//
// Created by Simon Gladman on 20/08/2015.
// Copyright © 2015 Simon Gladman. All rights reserved.
//
import UIKit
import SceneKit
import CoreMotion
class ViewController: UIViewController
{
let buttonBar = UIToolbar()
let cameraDistance: Float = 2
let sceneKitView = SCNView()
let cameraNode = SCNNode()
var initialAttitude: (roll: Double, pitch:Double)?
let motionManager = CMMotionManager()
let currentDrawingLayerSize = 512
var currentDrawingNode: SCNNode?
var currentDrawingLayer: CAShapeLayer?
let hermitePath = UIBezierPath()
var interpolationPoints = [CGPoint]()
override func viewDidLoad()
{
guard motionManager.gyroAvailable else
{
fatalError("CMMotionManager not available.")
}
super.viewDidLoad()
let title = UIBarButtonItem(title: "flexmonkey.co.uk", style: UIBarButtonItemStyle.Plain, target: nil, action: nil)
let spacer = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.FlexibleSpace, target: nil, action: nil)
let clearButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Trash, target: self, action: "clear")
buttonBar.items = [title, spacer, clearButton]
view.addSubview(sceneKitView)
view.addSubview(buttonBar)
sceneKitView.backgroundColor = UIColor.darkGrayColor()
sceneKitView.scene = SCNScene()
// centreNode
let centreNode = SCNNode()
centreNode.position = SCNVector3(x: 0, y: 0, z: 0)
scene.rootNode.addChildNode(centreNode)
// camera
let camera = SCNCamera()
camera.xFov = 20
camera.yFov = 20
cameraNode.camera = camera
scene.rootNode.addChildNode(cameraNode)
let constraint = SCNLookAtConstraint(target: centreNode)
cameraNode.constraints = [constraint]
cameraNode.pivot = SCNMatrix4MakeTranslation(0, 0, -cameraDistance)
// motion manager
let queue = NSOperationQueue.mainQueue
motionManager.deviceMotionUpdateInterval = 1 / 30
motionManager.startDeviceMotionUpdatesToQueue(queue())
{
(deviceMotionData: CMDeviceMotion?, error: NSError?) in
if let deviceMotionData = deviceMotionData
{
if (self.initialAttitude == nil)
{
self.initialAttitude = (deviceMotionData.attitude.roll,
deviceMotionData.attitude.pitch)
}
self.cameraNode.eulerAngles.y = Float(self.initialAttitude!.roll - deviceMotionData.attitude.roll)
self.cameraNode.eulerAngles.x = Float(self.initialAttitude!.pitch - deviceMotionData.attitude.pitch)
}
}
}
override func touchesBegan(touches: Set, withEvent event: UIEvent?)
{
super.touchesBegan(touches, withEvent: event)
currentDrawingNode = SCNNode(geometry: SCNBox(width: 1, height: 1, length: 0, chamferRadius: 0))
currentDrawingLayer = CAShapeLayer()
if let currentDrawingNode = currentDrawingNode, currentDrawingLayer = currentDrawingLayer
{
currentDrawingNode.position = SCNVector3(x: 0, y: 0, z: 0)
currentDrawingNode.eulerAngles.x = self.cameraNode.eulerAngles.x
currentDrawingNode.eulerAngles.y = self.cameraNode.eulerAngles.y
scene.rootNode.addChildNode(currentDrawingNode)
currentDrawingLayer.strokeColor = UIColor.whiteColor().CGColor
currentDrawingLayer.fillColor = nil
currentDrawingLayer.lineWidth = 10
currentDrawingLayer.lineJoin = kCALineJoinRound
currentDrawingLayer.lineCap = kCALineCapRound
currentDrawingLayer.frame = CGRect(x: 0, y: 0, width: currentDrawingLayerSize, height: currentDrawingLayerSize)
let material = SCNMaterial()
material.diffuse.contents = currentDrawingLayer
material.lightingModelName = SCNLightingModelConstant
currentDrawingNode.geometry?.materials = [material]
}
}
override func touchesMoved(touches: Set, withEvent event: UIEvent?)
{
super.touchesMoved(touches, withEvent: event)
let locationInView = touches.first?.locationInView(view)
if let hitTestResult:SCNHitTestResult = sceneKitView.hitTest(locationInView!, options: nil).filter( { $0.node == currentDrawingNode }).first,
currentDrawingLayer = currentDrawingLayer
{
if currentDrawingLayer.path == nil
{
let newX = CGFloat((hitTestResult.localCoordinates.x + 0.5) * Float(currentDrawingLayerSize))
let newY = CGFloat((hitTestResult.localCoordinates.y + 0.5) * Float(currentDrawingLayerSize))
interpolationPoints = [CGPoint(x: newX, y: newY)]
}
let newX = CGFloat((hitTestResult.localCoordinates.x + 0.5) * Float(currentDrawingLayerSize))
let newY = CGFloat((hitTestResult.localCoordinates.y + 0.5) * Float(currentDrawingLayerSize))
interpolationPoints.append(CGPoint(x: newX, y: newY))
hermitePath.removeAllPoints()
hermitePath.interpolatePointsWithHermite(interpolationPoints)
currentDrawingLayer.path = hermitePath.CGPath
}
}
override func touchesEnded(touches: Set, withEvent event: UIEvent?)
{
super.touchesEnded(touches, withEvent: event)
currentDrawingLayer = nil
currentDrawingNode = nil
hermitePath.removeAllPoints()
interpolationPoints.removeAll()
}
func clear()
{
scene.rootNode.childNodes.filter( {$0.geometry != nil} ).forEach
{
$0.removeFromParentNode()
}
}
var scene: SCNScene
{
return sceneKitView.scene!
}
override func viewDidLayoutSubviews()
{
super.viewDidLayoutSubviews()
let topMargin = topLayoutGuide.length
let toolbarHeight = buttonBar.intrinsicContentSize().height
sceneKitView.frame = CGRect(x: 0, y: topMargin, width: view.frame.width, height: view.frame.height - topMargin - toolbarHeight)
buttonBar.frame = CGRect(x: 0, y: view.frame.height - toolbarHeight, width: view.frame.width, height: toolbarHeight)
}
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask
{
return UIInterfaceOrientationMask.Portrait
}
}
================================================
FILE: SwiftSpace.xcodeproj/project.pbxproj
================================================
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
0530B0FE1B86517800E5D1DE /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0530B0FD1B86517800E5D1DE /* AppDelegate.swift */; };
0530B1001B86517800E5D1DE /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0530B0FF1B86517800E5D1DE /* ViewController.swift */; };
0530B1031B86517800E5D1DE /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0530B1011B86517800E5D1DE /* Main.storyboard */; };
0530B1051B86517800E5D1DE /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0530B1041B86517800E5D1DE /* Assets.xcassets */; };
0530B1081B86517800E5D1DE /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0530B1061B86517800E5D1DE /* LaunchScreen.storyboard */; };
0530B1131B86517800E5D1DE /* SwiftSpaceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0530B1121B86517800E5D1DE /* SwiftSpaceTests.swift */; };
0530B11E1B86517800E5D1DE /* SwiftSpaceUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0530B11D1B86517800E5D1DE /* SwiftSpaceUITests.swift */; };
3EA13AB11BEE002900E2B70F /* UIBezierPathExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3EA13AB01BEE002900E2B70F /* UIBezierPathExtension.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
0530B10F1B86517800E5D1DE /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 0530B0F21B86517800E5D1DE /* Project object */;
proxyType = 1;
remoteGlobalIDString = 0530B0F91B86517800E5D1DE;
remoteInfo = SwiftSpace;
};
0530B11A1B86517800E5D1DE /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 0530B0F21B86517800E5D1DE /* Project object */;
proxyType = 1;
remoteGlobalIDString = 0530B0F91B86517800E5D1DE;
remoteInfo = SwiftSpace;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
0530B0FA1B86517800E5D1DE /* SwiftSpace.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftSpace.app; sourceTree = BUILT_PRODUCTS_DIR; };
0530B0FD1B86517800E5D1DE /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
0530B0FF1B86517800E5D1DE /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; };
0530B1021B86517800E5D1DE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
0530B1041B86517800E5D1DE /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
0530B1071B86517800E5D1DE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
0530B1091B86517800E5D1DE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
0530B10E1B86517800E5D1DE /* SwiftSpaceTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftSpaceTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
0530B1121B86517800E5D1DE /* SwiftSpaceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftSpaceTests.swift; sourceTree = ""; };
0530B1141B86517800E5D1DE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
0530B1191B86517800E5D1DE /* SwiftSpaceUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftSpaceUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
0530B11D1B86517800E5D1DE /* SwiftSpaceUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftSpaceUITests.swift; sourceTree = ""; };
0530B11F1B86517800E5D1DE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
3EA13AB01BEE002900E2B70F /* UIBezierPathExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIBezierPathExtension.swift; sourceTree = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
0530B0F71B86517800E5D1DE /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
0530B10B1B86517800E5D1DE /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
0530B1161B86517800E5D1DE /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
0530B0F11B86517800E5D1DE = {
isa = PBXGroup;
children = (
0530B0FC1B86517800E5D1DE /* SwiftSpace */,
0530B1111B86517800E5D1DE /* SwiftSpaceTests */,
0530B11C1B86517800E5D1DE /* SwiftSpaceUITests */,
0530B0FB1B86517800E5D1DE /* Products */,
);
sourceTree = "";
};
0530B0FB1B86517800E5D1DE /* Products */ = {
isa = PBXGroup;
children = (
0530B0FA1B86517800E5D1DE /* SwiftSpace.app */,
0530B10E1B86517800E5D1DE /* SwiftSpaceTests.xctest */,
0530B1191B86517800E5D1DE /* SwiftSpaceUITests.xctest */,
);
name = Products;
sourceTree = "";
};
0530B0FC1B86517800E5D1DE /* SwiftSpace */ = {
isa = PBXGroup;
children = (
3EA13AAF1BEE001700E2B70F /* extensions */,
0530B0FD1B86517800E5D1DE /* AppDelegate.swift */,
0530B0FF1B86517800E5D1DE /* ViewController.swift */,
0530B1011B86517800E5D1DE /* Main.storyboard */,
0530B1041B86517800E5D1DE /* Assets.xcassets */,
0530B1061B86517800E5D1DE /* LaunchScreen.storyboard */,
0530B1091B86517800E5D1DE /* Info.plist */,
);
path = SwiftSpace;
sourceTree = "";
};
0530B1111B86517800E5D1DE /* SwiftSpaceTests */ = {
isa = PBXGroup;
children = (
0530B1121B86517800E5D1DE /* SwiftSpaceTests.swift */,
0530B1141B86517800E5D1DE /* Info.plist */,
);
path = SwiftSpaceTests;
sourceTree = "";
};
0530B11C1B86517800E5D1DE /* SwiftSpaceUITests */ = {
isa = PBXGroup;
children = (
0530B11D1B86517800E5D1DE /* SwiftSpaceUITests.swift */,
0530B11F1B86517800E5D1DE /* Info.plist */,
);
path = SwiftSpaceUITests;
sourceTree = "";
};
3EA13AAF1BEE001700E2B70F /* extensions */ = {
isa = PBXGroup;
children = (
3EA13AB01BEE002900E2B70F /* UIBezierPathExtension.swift */,
);
name = extensions;
sourceTree = "";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
0530B0F91B86517800E5D1DE /* SwiftSpace */ = {
isa = PBXNativeTarget;
buildConfigurationList = 0530B1221B86517800E5D1DE /* Build configuration list for PBXNativeTarget "SwiftSpace" */;
buildPhases = (
0530B0F61B86517800E5D1DE /* Sources */,
0530B0F71B86517800E5D1DE /* Frameworks */,
0530B0F81B86517800E5D1DE /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = SwiftSpace;
productName = SwiftSpace;
productReference = 0530B0FA1B86517800E5D1DE /* SwiftSpace.app */;
productType = "com.apple.product-type.application";
};
0530B10D1B86517800E5D1DE /* SwiftSpaceTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 0530B1251B86517800E5D1DE /* Build configuration list for PBXNativeTarget "SwiftSpaceTests" */;
buildPhases = (
0530B10A1B86517800E5D1DE /* Sources */,
0530B10B1B86517800E5D1DE /* Frameworks */,
0530B10C1B86517800E5D1DE /* Resources */,
);
buildRules = (
);
dependencies = (
0530B1101B86517800E5D1DE /* PBXTargetDependency */,
);
name = SwiftSpaceTests;
productName = SwiftSpaceTests;
productReference = 0530B10E1B86517800E5D1DE /* SwiftSpaceTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
0530B1181B86517800E5D1DE /* SwiftSpaceUITests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 0530B1281B86517800E5D1DE /* Build configuration list for PBXNativeTarget "SwiftSpaceUITests" */;
buildPhases = (
0530B1151B86517800E5D1DE /* Sources */,
0530B1161B86517800E5D1DE /* Frameworks */,
0530B1171B86517800E5D1DE /* Resources */,
);
buildRules = (
);
dependencies = (
0530B11B1B86517800E5D1DE /* PBXTargetDependency */,
);
name = SwiftSpaceUITests;
productName = SwiftSpaceUITests;
productReference = 0530B1191B86517800E5D1DE /* SwiftSpaceUITests.xctest */;
productType = "com.apple.product-type.bundle.ui-testing";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
0530B0F21B86517800E5D1DE /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0700;
ORGANIZATIONNAME = "Simon Gladman";
TargetAttributes = {
0530B0F91B86517800E5D1DE = {
CreatedOnToolsVersion = 7.0;
};
0530B10D1B86517800E5D1DE = {
CreatedOnToolsVersion = 7.0;
TestTargetID = 0530B0F91B86517800E5D1DE;
};
0530B1181B86517800E5D1DE = {
CreatedOnToolsVersion = 7.0;
TestTargetID = 0530B0F91B86517800E5D1DE;
};
};
};
buildConfigurationList = 0530B0F51B86517800E5D1DE /* Build configuration list for PBXProject "SwiftSpace" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 0530B0F11B86517800E5D1DE;
productRefGroup = 0530B0FB1B86517800E5D1DE /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
0530B0F91B86517800E5D1DE /* SwiftSpace */,
0530B10D1B86517800E5D1DE /* SwiftSpaceTests */,
0530B1181B86517800E5D1DE /* SwiftSpaceUITests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
0530B0F81B86517800E5D1DE /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
0530B1081B86517800E5D1DE /* LaunchScreen.storyboard in Resources */,
0530B1051B86517800E5D1DE /* Assets.xcassets in Resources */,
0530B1031B86517800E5D1DE /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
0530B10C1B86517800E5D1DE /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
0530B1171B86517800E5D1DE /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
0530B0F61B86517800E5D1DE /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
3EA13AB11BEE002900E2B70F /* UIBezierPathExtension.swift in Sources */,
0530B1001B86517800E5D1DE /* ViewController.swift in Sources */,
0530B0FE1B86517800E5D1DE /* AppDelegate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
0530B10A1B86517800E5D1DE /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
0530B1131B86517800E5D1DE /* SwiftSpaceTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
0530B1151B86517800E5D1DE /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
0530B11E1B86517800E5D1DE /* SwiftSpaceUITests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
0530B1101B86517800E5D1DE /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 0530B0F91B86517800E5D1DE /* SwiftSpace */;
targetProxy = 0530B10F1B86517800E5D1DE /* PBXContainerItemProxy */;
};
0530B11B1B86517800E5D1DE /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 0530B0F91B86517800E5D1DE /* SwiftSpace */;
targetProxy = 0530B11A1B86517800E5D1DE /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
0530B1011B86517800E5D1DE /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
0530B1021B86517800E5D1DE /* Base */,
);
name = Main.storyboard;
sourceTree = "";
};
0530B1061B86517800E5D1DE /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
0530B1071B86517800E5D1DE /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
0530B1201B86517800E5D1DE /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
0530B1211B86517800E5D1DE /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
0530B1231B86517800E5D1DE /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
INFOPLIST_FILE = SwiftSpace/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.4;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = uk.co.flexmonkey.SwiftSpace;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Debug;
};
0530B1241B86517800E5D1DE /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
INFOPLIST_FILE = SwiftSpace/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 8.4;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = uk.co.flexmonkey.SwiftSpace;
PRODUCT_NAME = "$(TARGET_NAME)";
};
name = Release;
};
0530B1261B86517800E5D1DE /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
INFOPLIST_FILE = SwiftSpaceTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = uk.co.flexmonkey.SwiftSpaceTests;
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SwiftSpace.app/SwiftSpace";
};
name = Debug;
};
0530B1271B86517800E5D1DE /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
INFOPLIST_FILE = SwiftSpaceTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = uk.co.flexmonkey.SwiftSpaceTests;
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SwiftSpace.app/SwiftSpace";
};
name = Release;
};
0530B1291B86517800E5D1DE /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
INFOPLIST_FILE = SwiftSpaceUITests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = uk.co.flexmonkey.SwiftSpaceUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_TARGET_NAME = SwiftSpace;
USES_XCTRUNNER = YES;
};
name = Debug;
};
0530B12A1B86517800E5D1DE /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
INFOPLIST_FILE = SwiftSpaceUITests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = uk.co.flexmonkey.SwiftSpaceUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
TEST_TARGET_NAME = SwiftSpace;
USES_XCTRUNNER = YES;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
0530B0F51B86517800E5D1DE /* Build configuration list for PBXProject "SwiftSpace" */ = {
isa = XCConfigurationList;
buildConfigurations = (
0530B1201B86517800E5D1DE /* Debug */,
0530B1211B86517800E5D1DE /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
0530B1221B86517800E5D1DE /* Build configuration list for PBXNativeTarget "SwiftSpace" */ = {
isa = XCConfigurationList;
buildConfigurations = (
0530B1231B86517800E5D1DE /* Debug */,
0530B1241B86517800E5D1DE /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
0530B1251B86517800E5D1DE /* Build configuration list for PBXNativeTarget "SwiftSpaceTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
0530B1261B86517800E5D1DE /* Debug */,
0530B1271B86517800E5D1DE /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
0530B1281B86517800E5D1DE /* Build configuration list for PBXNativeTarget "SwiftSpaceUITests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
0530B1291B86517800E5D1DE /* Debug */,
0530B12A1B86517800E5D1DE /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 0530B0F21B86517800E5D1DE /* Project object */;
}
================================================
FILE: SwiftSpace.xcodeproj/project.xcworkspace/contents.xcworkspacedata
================================================
================================================
FILE: SwiftSpace.xcodeproj/xcuserdata/simon_non_admin.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist
================================================
================================================
FILE: SwiftSpace.xcodeproj/xcuserdata/simon_non_admin.xcuserdatad/xcschemes/SwiftSpace.xcscheme
================================================
================================================
FILE: SwiftSpace.xcodeproj/xcuserdata/simon_non_admin.xcuserdatad/xcschemes/xcschememanagement.plist
================================================
SchemeUserState
SwiftSpace.xcscheme
orderHint
0
SuppressBuildableAutocreation
0530B0F91B86517800E5D1DE
primary
0530B10D1B86517800E5D1DE
primary
0530B1181B86517800E5D1DE
primary
================================================
FILE: SwiftSpaceTests/Info.plist
================================================
CFBundleDevelopmentRegion
en
CFBundleExecutable
$(EXECUTABLE_NAME)
CFBundleIdentifier
$(PRODUCT_BUNDLE_IDENTIFIER)
CFBundleInfoDictionaryVersion
6.0
CFBundleName
$(PRODUCT_NAME)
CFBundlePackageType
BNDL
CFBundleShortVersionString
1.0
CFBundleSignature
????
CFBundleVersion
1
================================================
FILE: SwiftSpaceTests/SwiftSpaceTests.swift
================================================
//
// SwiftSpaceTests.swift
// SwiftSpaceTests
//
// Created by SIMON_NON_ADMIN on 20/08/2015.
// Copyright © 2015 Simon Gladman. All rights reserved.
//
import XCTest
@testable import SwiftSpace
class SwiftSpaceTests: XCTestCase {
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 testExample() {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
}
func testPerformanceExample() {
// This is an example of a performance test case.
self.measureBlock {
// Put the code you want to measure the time of here.
}
}
}
================================================
FILE: SwiftSpaceUITests/Info.plist
================================================
CFBundleDevelopmentRegion
en
CFBundleExecutable
$(EXECUTABLE_NAME)
CFBundleIdentifier
$(PRODUCT_BUNDLE_IDENTIFIER)
CFBundleInfoDictionaryVersion
6.0
CFBundleName
$(PRODUCT_NAME)
CFBundlePackageType
BNDL
CFBundleShortVersionString
1.0
CFBundleSignature
????
CFBundleVersion
1
================================================
FILE: SwiftSpaceUITests/SwiftSpaceUITests.swift
================================================
//
// SwiftSpaceUITests.swift
// SwiftSpaceUITests
//
// Created by SIMON_NON_ADMIN on 20/08/2015.
// Copyright © 2015 Simon Gladman. All rights reserved.
//
import XCTest
class SwiftSpaceUITests: XCTestCase {
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
// In UI tests it is usually best to stop immediately when a failure occurs.
continueAfterFailure = false
// UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method.
XCUIApplication().launch()
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
func testExample() {
// Use recording to get started writing UI tests.
// Use XCTAssert and related functions to verify your tests produce the correct results.
}
}