Adafruit Subscription

This commit is contained in:
TrevKnows 2018-02-13 16:52:37 -05:00
parent 00eb36497c
commit c3b5ab6465
30 changed files with 1643 additions and 0 deletions

View file

@ -0,0 +1,14 @@
<?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>Version</key>
<string>1.0</string>
<key>Name</key>
<string>Chapter 1</string>
<key>Pages</key>
<array>
<string>Page1.playgroundpage</string>
</array>
</dict>
</plist>

View file

@ -0,0 +1,109 @@
/*:#localized(key: "FirstProseBlock")
**Goal:** Learn how to use basic movement functions.
Its time to race! Start your engines!
Use your Swift programming skills to control the Robot Rover with **basic motion** function calls like `moveForward()`, `turnRight()`, `turnLeft()`, and `moveBack()`.
*/
//#-code-completion(everything, hide)
//#-code-completion(identifier, show, moveForward(), turnLeft(), moveBack(), turnRight(), wait(), dance())
//#-hidden-code
import Foundation
import PlaygroundSupport
setup()
//var rcCommand: RCCommand = RCCommand()
//var commandManager: CommandManager = CommandManager()
//func testFw(_ time:) {
// rcCommand.duration = time
//}
//func moveForward(_ item: Int) {
// let page = PlaygroundPage.current
// if let proxy = page.liveView as? PlaygroundRemoteLiveViewProxy {
// let message = PlaygroundValue.integer(item)
// // let message2: PlaygroundValue = .string(CommandType.COMMAND_MOVE_FORWARD.rawValue)
//
// proxy.send(message)
// commandManager.moveForward()
// }
//}
//func moveBack(_ item: Int) {
// let page = PlaygroundPage.current
// if let proxy = page.liveView as? PlaygroundRemoteLiveViewProxy {
// let message = PlaygroundValue.integer(item)
// // let message2: PlaygroundValue = .string(CommandType.COMMAND_MOVE_BACKWARD.rawValue)
//
// proxy.send(message)
// commandManager.moveBack()
// }
//}
//func turnLeft(_ item: Int) {
// let page = PlaygroundPage.current
// if let proxy = page.liveView as? PlaygroundRemoteLiveViewProxy {
// let message = PlaygroundValue.integer(item)
// //let message2: PlaygroundValue = .string(CommandType.COMMAND_TURN_LEFT.rawValue)
//
// proxy.send(message)
// commandManager.turnLeft()
// }
//}
//
//
//func turnRight(_ item: Int) {
// let page = PlaygroundPage.current
// if let proxy = page.liveView as? PlaygroundRemoteLiveViewProxy {
// let message = PlaygroundValue.integer(item)
// //let message2: PlaygroundValue = .string(CommandType.COMMAND_TURN_RIGHT.rawValue)
//
// proxy.send(message)
// commandManager.turnRight()
// }
//}
//func testFw(_ item: Int) {
// let page = PlaygroundPage.current
// if let proxy = page.liveView as? PlaygroundRemoteLiveViewProxy {
// let message: PlaygroundValue = .integer(item)
// proxy.send(message)
// }
//}
//#-end-hidden-code
//#-editable-code
moveForward()
//#-end-editable-code
//#-hidden-code
/*
turnRight()
moveForward()
turnLeft()
moveForward()
turnLeft()
moveForward()
moveForward()
turnRight()
moveForward()
turnRight()
moveForward()
turnLeft()
pause()
*/
//#-end-hidden-code

View file

@ -0,0 +1,11 @@
// Created by Trevor Beaton on 12/14/17.
// Copyright © 2017 Vanguard Logic LLC. All rights reserved.
import UIKit
import Foundation
import PlaygroundSupport
import PlaygroundBluetooth
let rcViewController: RCViewController = RCViewController(1)
PlaygroundPage.current.liveView = rcViewController

View file

@ -0,0 +1,14 @@
<?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>Name</key>
<string>Page 1</string>
<key>LiveViewMode</key>
<string>VisibleByDefault</string>
<key>LiveViewEdgeToEdge</key>
<true/>
<key>PlaygroundLoggingMode</key>
<string>Off</string>
</dict>
</plist>

View file

@ -0,0 +1,8 @@
<?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>Hints</key>
<array/>
</dict>
</plist>

View file

@ -0,0 +1,90 @@
//
// CommandList.swift
//
// Created by Trevor Beaton on 12/14/17.
// Copyright © 2017 Vanguard Logic LLC. All rights reserved.
import Foundation
import PlaygroundSupport
public func moveForward(){
commandManager.moveForward()
}
public func moveBack(){
commandManager.moveBack()
}
public func turnRight(){
commandManager.turnRight()
}
public func turnLeft(){
commandManager.turnLeft()
}
public func wait() {
commandManager.pause()
}
//public func wheelie(){
// commandManager.moveBack()
// commandManager.moveForward()
//}
public func dance(){
commandManager.turnLeft()
commandManager.turnLeft()
commandManager.turnRight()
commandManager.turnRight()
commandManager.turnLeft()
commandManager.turnLeft()
commandManager.turnRight()
commandManager.turnRight()
commandManager.pause()
commandManager.moveBack()
commandManager.moveForward()
commandManager.turnRight()
commandManager.turnRight()
}
/*
create a function reference that takes the duration time and sends it to the command manager function that will be sent to the RCCommand.Duration
*/
public func moveForward(_ seconds: Int) {
let page = PlaygroundPage.current
if let proxy = page.liveView as? PlaygroundRemoteLiveViewProxy {
let message = PlaygroundValue.integer(seconds)
proxy.send(message)
commandManager.moveForward()
}
}
public func moveBack(_ seconds: Int) {
let page = PlaygroundPage.current
if let proxy = page.liveView as? PlaygroundRemoteLiveViewProxy {
let message = PlaygroundValue.integer(seconds)
proxy.send(message)
commandManager.moveBack()
}
}
public func turnLeft(_ seconds: Int) {
let page = PlaygroundPage.current
if let proxy = page.liveView as? PlaygroundRemoteLiveViewProxy {
let message = PlaygroundValue.integer(seconds)
proxy.send(message)
commandManager.turnLeft()
}
}
public func turnRight(_ seconds: Int) {
let page = PlaygroundPage.current
if let proxy = page.liveView as? PlaygroundRemoteLiveViewProxy {
let message = PlaygroundValue.integer(seconds)
proxy.send(message)
commandManager.turnRight()
}
}

View file

@ -0,0 +1,52 @@
//Newer
// Created by Trevor Beaton on 12/14/17.
// Copyright © 2017 Vanguard Logic LLC. All rights reserved.
import Foundation
import PlaygroundSupport
import PlaygroundBluetooth
var delegate: UserProcessDelegate?
let rcBluetooth: RCBluetooth = RCBluetooth()
let commandManager: CommandManager = CommandManager()
public func setup(){
printLog(newString: #function)
let page = PlaygroundPage.current
page.needsIndefiniteExecution = true
let proxy = page.liveView as? PlaygroundRemoteLiveViewProxy
delegate = UserProcessDelegate(pauseHandler: commandManager)
delegate?.onAssessment = assessment
proxy?.delegate = delegate
}
public func assessment(_ playgroundValue:PlaygroundValue)->Bool{
// Assessment
var correctSolution:[CommandType] = [ CommandType.COMMAND_MOVE_FORWARD ]
var commands:[PlaygroundValue] = [PlaygroundValue]()
if case let .array(values) = playgroundValue {
commands = values
}
let result:Bool = RCCommand.solutionChecker(commands, correctSolution)
var failureHints = [NSLocalizedString("Fail Hint.Test.", comment: "")]
//Update assessment status
PlaygroundPage.current.assessmentStatus = .pass(message: NSLocalizedString("### Pass! \n Now thats what I call a victory dance!\n\n[**Next Page**](@next)", comment: ""))
if(result){
printLog(newString: "PASS!")
PlaygroundPage.current.assessmentStatus = .pass(message: NSLocalizedString("### Pass Message. \n\n[**Next Page**](@next)", comment: ""))
}
else{
printLog(newString: "Fail")
PlaygroundPage.current.assessmentStatus = .fail(hints: failureHints, solution: nil)
}
return result
}

View file

@ -0,0 +1,26 @@
<?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>SwiftVersion</key>
<string>3.0</string>
<key>ImageReference</key>
<string>icon.png</string>
<key>Version</key>
<string>3.0</string>
<key>ContentVersion</key>
<string>1.0</string>
<key>Name</key>
<string>Empty</string>
<key>ContentIdentifier</key>
<string>com.example.adafruitswiftplayground</string>
<key>DeploymentTarget</key>
<string>ios10.0</string>
<key>DevelopmentRegion</key>
<string>en</string>
<key>Chapters</key>
<array>
<string>Chapter1.playgroundchapter</string>
</array>
</dict>
</plist>

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

View file

@ -0,0 +1,102 @@
//
// CommandManager.swift
//
//
// Created by Trevor Beaton on 12/14/17.
// Copyright © 2017 Vanguard Logic LLC. All rights reserved.
import Foundation
import PlaygroundSupport
public enum CommandType: String {
case COMMAND_MOVE_FORWARD = "command_moveForward"
case COMMAND_MOVE_BACKWARD = "command_moveBackward"
case COMMAND_TURN_RIGHT = "command_turnRight"
case COMMAND_TURN_LEFT = "command_turnLeft"
case COMMAND_PAUSE = "command_pause"
case COMMAND_EXIT_PROGRAM = "ExitProgram"
}
public protocol CommandPauseDelegate {
var isReadyForMoreCommands: Bool { get set }
var returnValue: Bool { get set }
}
extension CommandPauseDelegate {
/// Waits until `isReadyForMoreCommands` is set to true.
func wait() {
repeat {
RunLoop.main.run(mode: .defaultRunLoopMode, before: Date(timeIntervalSinceNow: 0.1))
} while !isReadyForMoreCommands
}
}
public class CommandManager: CommandPauseDelegate {
public var isReadyForMoreCommands = true
public var returnValue = false
var command: PlaygroundValue = .string("")
var userInt: Int?
public init(){
}
public func sendCommand(_ commandData:PlaygroundValue) {
let page = PlaygroundPage.current
if let proxy = page.liveView as? PlaygroundRemoteLiveViewProxy {
proxy.send(commandData)
}
// Spin the runloop until the LiveView process has completed the current command.
isReadyForMoreCommands = false
wait()
}
public func moveForward(){
command = .string(CommandType.COMMAND_MOVE_FORWARD.rawValue)
sendCommand(command)
printLog(newString: #function)
}
public func moveBack(){
command = .string(CommandType.COMMAND_MOVE_BACKWARD.rawValue)
sendCommand(command)
printLog(newString: #function)
}
public func turnRight(){
command = .string(CommandType.COMMAND_TURN_RIGHT.rawValue)
sendCommand(command)
printLog(newString: #function)
}
public func turnLeft(){
command = .string(CommandType.COMMAND_TURN_LEFT.rawValue)
sendCommand(command)
printLog(newString: #function)
}
public func exitProgram(){
command = .string(CommandType.COMMAND_EXIT_PROGRAM.rawValue)
sendCommand(command)
}
public func pause(){
command = .string(CommandType.COMMAND_PAUSE.rawValue)
sendCommand(command)
}
public func testForward(){
command = .string(CommandType.COMMAND_MOVE_FORWARD.rawValue)
sendCommand(command)
printLog(newString: #function)
}
public func intTaker(){
printLog(newString: "Hello")
}
}

View file

@ -0,0 +1,64 @@
// Created by Trevor Beaton on 12/14/17.
// Copyright © 2017 Vanguard Logic LLC. All rights reserved.
import UIKit
import CoreBluetooth
import PlaygroundSupport
import Foundation
let kBLEService_UUID = "6e400001-b5a3-f393-e0a9-e50e24dcca9e"
let kBLE_Characteristic_uuid_Tx = "6e400002-b5a3-f393-e0a9-e50e24dcca9e"
let kBLE_Characteristic_uuid_Rx = "6e400003-b5a3-f393-e0a9-e50e24dcca9e"
let BLEService_UUID = CBUUID(string: kBLEService_UUID)
let BLE_Characteristic_uuid_Tx = CBUUID(string: kBLE_Characteristic_uuid_Tx)//(Property = Write without response)
let BLE_Characteristic_uuid_Rx = CBUUID(string: kBLE_Characteristic_uuid_Rx)// (Property = Read/Notify)
public class Constants: NSObject {
public static let COMMAND_FINISHED = "CommandFinishedtestS"
public static let PROGRAM_FINISHED = "ProgramFinished"
public static func commandTextToLocalizedText(_ text:String)->String{
if(text == ""){
return ""
}
var type:CommandType = CommandType(rawValue: text)!
return self.commandTypeToLocalizedText(type)
}
public static func commandTypeToLocalizedText(_ type:CommandType)->String{
var text:String = ""
switch(type) {
case .COMMAND_MOVE_FORWARD:
text = NSLocalizedString("I'm moving forward!", comment: "")
case .COMMAND_MOVE_BACKWARD:
text = NSLocalizedString("Backing up!", comment: "")
case .COMMAND_TURN_RIGHT:
text = NSLocalizedString("And a quick turn to the right!", comment: "")
case .COMMAND_TURN_LEFT:
text = NSLocalizedString("And a quick turn to the left!", comment: "")
default:
break
}
return text
}
}

View file

@ -0,0 +1,275 @@
//
// RCBluetooth.swift
//
// Created by Trevor Beaton on 12/14/17.
// Copyright © 2017 Vanguard Logic LLC. All rights reserved.
//
import Foundation
import CoreBluetooth
import PlaygroundSupport
import PlaygroundBluetooth
//Global variables and print funtion for debugging
public var printString = ""
public var bleStatus: String?
public var bleStatus2: String?
public func printLog(newString: String) {
let appendString = "\n"
printString = newString + appendString
NotificationCenter.default.post(name:NSNotification.Name(rawValue: "Print"), object: nil)
}
public class RCBluetooth: NSObject, PlaygroundBluetoothCentralManagerDelegate, CBPeripheralDelegate {
// Mark:- Data
var timer = Timer()
var duration: Int = 2000
var txCharacteristic : CBCharacteristic?
var rxCharacteristic : CBCharacteristic?
var characteristicASCIIValue = NSString()
//Public variables
public var onDataWritten:(()->Void)?
public var isConnected: Bool = false
public var onCharacteristicsUpdated:((Data)->Void)?
public var centralManager: PlaygroundBluetoothCentralManager?
public var onCharacteristicsDiscovered:((CBPeripheral)->Void)?
//Private variables
private var blePeripheral: CBPeripheral?
private let data = NSMutableData()
//Motion String Commands
var forwardString = "!B516"
var stopString = "!B507"
var backString = "!B615"
var backStopString = "!B606"
var rightString = "!B813"
var rightStop = "!B804"
var leftString = "!B714"
var leftStop = "!B705"
let txCharMax: Int = 20
public override init() {
super.init()
//Creates a central manager that supports communicating with Bluetooth peripherals
centralManager = PlaygroundBluetoothCentralManager(services: [BLEService_UUID], queue: .global())
centralManager!.delegate = self
}
//:- Methods for Connectivity
//Tells the delegate that the state of the central manager has changed.
public func centralManagerStateDidChange(_ centralManager: PlaygroundBluetoothCentralManager) {
if centralManager.state == .poweredOn {
isConnected = true
printLog(newString: "Bluetooth is Enabled.")
}else {
printLog(newString: "Bluetooth is Disabled. Turn On Bluetooth in Control Panel.")
}
}
// Tells the delegate that a peripheral has been discovered during scanning.
public func centralManager(_ centralManager: PlaygroundBluetoothCentralManager, didDiscover peripheral: CBPeripheral, withAdvertisementData advertisementData: [String : Any]?, rssi: Double) {
}
// Tells the delegate that the central manager is about to attempt to establish a connection with a peripheral.
public func centralManager(_ centralManager: PlaygroundBluetoothCentralManager, willConnectTo peripheral: CBPeripheral) {
}
// Tells the delegate that the central manager established a connection with a peripheral.
public func centralManager(_ centralManager: PlaygroundBluetoothCentralManager, didConnectTo peripheral: CBPeripheral) {
peripheral.delegate = self
// Make sure we get the discovery callbacks
peripheral.discoverServices([BLEService_UUID])
blePeripheral = peripheral
}
// Tells the delegate that the central manager failed to establish a connection with a peripheral.
public func centralManager(_ centralManager: PlaygroundBluetoothCentralManager, didFailToConnectTo peripheral: CBPeripheral, error: Error?) {
}
// Tells the delegate that the central manager disconnected from a peripheral.
public func centralManager(_ centralManager: PlaygroundBluetoothCentralManager, didDisconnectFrom peripheral: CBPeripheral, error: Error?) {
blePeripheral = nil
}
// Invoked when you discover the peripherals available services.
public func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
guard error == nil else {
print("Error discovering services: \(error!.localizedDescription)")
return
}
guard let services = peripheral.services else {
return
}
for service in services {
// Find the characteristic we want in services
peripheral.discoverCharacteristics([BLE_Characteristic_uuid_Tx, BLE_Characteristic_uuid_Rx], for: service)
}
}
// Invoked when you discover the characteristics of a specified service.
public func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
if ((error) != nil) {
print("Error discovering services: \(error!.localizedDescription)")
return
}
guard let characteristics = service.characteristics else {
return
}
for characteristic in characteristics {
//looks for the right characteristic
if characteristic.uuid.isEqual(BLE_Characteristic_uuid_Tx) {
txCharacteristic = characteristic
isConnected = true
onCharacteristicsDiscovered?(peripheral)
}
if characteristic.uuid.isEqual(BLE_Characteristic_uuid_Rx) {
peripheral.setNotifyValue(true, for: characteristic)
}
peripheral.discoverDescriptors(for: characteristic)
}
}
// Invoked when you write data to a characteristic descriptors value.
public func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?){
guard error == nil else {
return
}
//onDataWritten?()
}
// Callback on data arrival via notification on the characteristic
public func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
guard error == nil else {
printLog(newString: "Error discovering services: \(error!.localizedDescription)")
return
}
if characteristic.uuid.isEqual(BLE_Characteristic_uuid_Rx) {
onCharacteristicsUpdated?(characteristic.value!)
}
}
// The peripheral checks whether our subscribe/unsubscribe happened or not
public func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
guard error == nil else {
// myPrint("Error changing notification state: \(error!.localizedDescription)")
return
}
if (characteristic.isNotifying) {
print("Notification began on \(characteristic)")
} else {
print("Notification stopped on (\(characteristic)) Disconnecting")
}
}
// Sends our motion commands and duration time to bluetooth device using a write characteristic.
public func sendRcData(_ data: Data, _ duration: Int){
blePeripheral!.writeValue(data, for: txCharacteristic!, type: CBCharacteristicWriteType.withResponse)
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(duration) ) {
self.onDataWritten?()
self.stopForward()
}
}
// Write function
public func writeValue22(data: String){
let data = (data as NSString).data(using: String.Encoding.utf8.rawValue)
if let blePeripheral = blePeripheral{
if let txCharacteristic = txCharacteristic {
blePeripheral.writeValue(data!, for: txCharacteristic, type: CBCharacteristicWriteType.withResponse)
}
}
}
func stringToData(string: String) {
let data = string.data(using: String.Encoding.utf8,allowLossyConversion: true)
sendDataWithCrc(data!)
}
func sendDataWithCrc(_ data : Data) {
let len = data.count
var dataBytes = [UInt8](repeating: 0, count: len)
var crc: UInt8 = 0
(data as NSData).getBytes(&dataBytes, length: len)
for i in dataBytes { //add all bytes
crc = crc &+ i
}
crc = ~crc //invert
var dataWithChecksum = NSData(data: data) as Data
dataWithChecksum.append(&crc, count: 1)
sendCommand = "\(dataWithChecksum)"
}
// Mark:- Motion Functions
public func moveForward(){
printLog(newString: "<Forward>")
writeValue22(data: forwardString)
}
public func stopForward(){
// printLog(newString: "<Forward Stopped>")
writeValue22(data: stopString)
}
public func moveBack(){
printLog(newString: "<Back>")
writeValue22(data: backString)
}
public func stopBack(){
// printLog(newString: "<Back Stopped>")
writeValue22(data: backStopString)
}
public func turnRight(){
printLog(newString: "<Turning Right>")
writeValue22(data: rightString)
}
public func stopRight(){
// printLog(newString: "<Right Stopped>")
writeValue22(data: rightStop)
}
public func turnLeft(){
printLog(newString: "<Turning Left>")
writeValue22(data: leftString)
}
public func stopLeft(){
// printLog(newString: "<Left Stopped>")
writeValue22(data: leftStop)
}
func sendTouchEvent(_ tag: Int, isPressed: Bool) {
let message = "!B\(tag)\(isPressed ? "1" : "0")"
if let data = message.data(using: String.Encoding.utf8) {
sendDataWithCrc(data)
}
}
}
extension RCBluetooth : ControllerPadViewControllerDelegate {
func onSendControllerPadButtonStatus(tag: Int, isPressed: Bool) {
sendTouchEvent(tag, isPressed: isPressed)
}
}

View file

@ -0,0 +1,116 @@
//
// RcCommand.swift
//
//
// Created by Trevor Beaton on 9/5/17.
// Copyright © 2017 Vanguard Logic LLC. All rights reserved.
import Foundation
import PlaygroundSupport
public var durationTimer: Int?
public class RCCommand: NSObject {
//:- Variables
public var duration: Int = 750
public var sensorType:String?
public var commandArray : [String] = []
let rcBluetooth: RCBluetooth = RCBluetooth()
let commandManager: CommandManager = CommandManager()
//:- Motion String Commands
let MOVE_BACKWARD : [Int8] = [ 0x21, 0x42, 0x36, 0x31, 0x35 ] //!B615
let MOVE_FORWARD : [Int8] = [ 0x21, 0x42, 0x35, 0x31, 0x36 ] //!B516
let TURN_RIGHT : [Int8] = [ 0x21, 0x42, 0x37, 0x31, 0x34 ] //!B714
let TURN_LEFT : [Int8] = [ 0x21, 0x42, 0x38, 0x31, 0x33 ] //!B813
let PAUSE : [Int8] = [ 0x21, 0x42, 0x35, 0x30, 0x37 ] //!B507
//:- Functions
public func sendRobotDuration(_ command: PlaygroundValue){
var testString = "\(command)"
let result = testString.trimmingCharacters(in: CharacterSet(charactersIn: "01234567890.").inverted)
duration = Int(result)!
printLog(newString: "Result: \(result)")
}
public func durationReset(){
printLog(newString: "Duration Has Been Reset to 300 milliseconds.")
duration = 750
}
public func sendRobotCommand(_ rcBluetooth: RCBluetooth, _ command: PlaygroundValue){
switch command {
case let .string(text):
var fourBytes : [Int8] = []
sensorType = nil
switch(text) {
case CommandType.COMMAND_PAUSE.rawValue:
fourBytes = PAUSE
// duration = 1
case CommandType.COMMAND_MOVE_FORWARD.rawValue:
fourBytes = MOVE_FORWARD
// duration = 800
printLog(newString: "Forward Duration: \(duration)")
break
case CommandType.COMMAND_MOVE_BACKWARD.rawValue:
fourBytes = MOVE_BACKWARD
// duration = 800
printLog(newString: "Backward Duration: \(duration)")
break
case CommandType.COMMAND_TURN_RIGHT.rawValue:
fourBytes = TURN_LEFT
// duration = 600
printLog(newString: "Right Duration: \(duration)")
break
case CommandType.COMMAND_TURN_LEFT.rawValue:
fourBytes = TURN_RIGHT
// duration = 600
printLog(newString: "Left Duration: \(duration)")
default:
break
}
if sensorType == nil {
let data : Data = NSData(bytes: fourBytes, length: fourBytes.count*4) as Data
rcBluetooth.sendRcData(data, duration)
}
else{
printLog(newString: "Data was not sent.")
}
break
default:
break
}
}
func delay(_ delay:Double, closure:@escaping ()->()) {
DispatchQueue.main.asyncAfter(
deadline: DispatchTime.now() + Double(Int64(delay * Double(NSEC_PER_SEC))) / Double(NSEC_PER_SEC), execute: closure)
}
public static func solutionChecker(_ commands:[PlaygroundValue], _ correctSolution:[CommandType])->Bool{ // Don't pass in robot connect command into this
var result:Bool = false
if commands.count == correctSolution.count{
for index in 0...(commands.count-1) {
let command:PlaygroundValue = commands[index]
if case let .string(text) = command {
if !text.isEqual(correctSolution[index].rawValue){
break
}
}
if index==(commands.count-1){
result = true
}
}
}
return result
}
}

View file

@ -0,0 +1,73 @@
//
// RCView.swift
//
//
// Created by Trevor Beaton on 8/23/17.
// Copyright © 2017 Vanguard Logic LLC. All rights reserved.
import UIKit
import Foundation
import CoreBluetooth
public class RobotView:UIView{
var scrollView:UIScrollView!
var buttons = [UIButton]()
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
let icon = UIImageView(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
var robotName:String = ""
override init (frame : CGRect) {
super.init(frame : frame)
setupView()
}
required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func setupView(){
icon.image = #imageLiteral(resourceName: "robotAvatar.png")
icon.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(icon)
label.numberOfLines = 0
label.text = NSLocalizedString("Robots\nAvailable", comment: "")
label.textAlignment = NSTextAlignment.left;
label.textColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
label.font = UIFont.boldSystemFont(ofSize: 12)
label.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(label)
scrollView = UIScrollView(frame: CGRect(x: 0, y: 0, width: 200, height: 50))
scrollView.backgroundColor = #colorLiteral(red: 0.9688462615, green: 0.9830557704, blue: 1, alpha: 0)
scrollView.contentSize = CGSize(width:200, height:50)
scrollView.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(scrollView)
}
public func addDiscoveredRobot(_ name:String){
print("addDiscoveredRobot: \(name)")
let button = UIButton(frame: CGRect(x:self.buttons.count*100 , y: 0, width: 150, height: 50))
// button.backgroundColor = UIColor(patternImage: #imageLiteral(resourceName: "robotNameBg.png")) // # imageLiteral(resourceName: "robotNameBg.png")
button.setTitle(name, for: UIControlState.normal)
button.setTitleColor(#colorLiteral(red: 1, green: 1, blue: 1, alpha: 1), for: UIControlState.normal)
button.titleLabel!.font = UIFont.boldSystemFont(ofSize: 15)
button.titleLabel!.textAlignment = .left
button.titleLabel!.lineBreakMode = .byWordWrapping
button.titleLabel!.numberOfLines = 2
scrollView.addSubview(button)
self.buttons.append(button)
}
}

View file

@ -0,0 +1,523 @@
//
// RCViewController.swift
//
// Copyright © 2017 Vanguard Logic LLC. All rights reserved.
// Created by Trevor Beaton on 8/22/17.
//
//
import Foundation
import UIKit
import WebKit
import CoreBluetooth
import PlaygroundSupport
import PlaygroundBluetooth
protocol ControllerPadViewControllerDelegate: class {
func onSendControllerPadButtonStatus(tag: Int, isPressed: Bool)
}
var sendCommand : String?
public class RCViewController: UIViewController, UITextViewDelegate {
//Data
//Page ID
var page:Int = 1
//Data
var bleView: PlaygroundBluetoothConnectionView!
let bleViewDelegate = ConnectionViewDelegate()
var btViewConstraints = [NSLayoutConstraint]()
var isLandscape:Bool = true
var isPortraitMode:Bool = true
var rcBluetooth: RCBluetooth = RCBluetooth()
var rcCommand: RCCommand = RCCommand()
var commandsForAssessment:[PlaygroundValue] = [PlaygroundValue]()
private let buttonPrefix = "!B"
//Button Setup
public var commentText:UITextView!
var forwardButton : UIButton!
var backButton: UIButton!
var leftButton: UIButton!
var rightButton: UIButton!
var blePeripheral: CBPeripheral!
// Data
weak var delegate: ControllerPadViewControllerDelegate?
func updateTextView() {
let newLine = "\n"
var newText = commentText.text!
newText += printString
commentText.text = newText
}
public required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)!
}
public override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
public convenience init(_ page:Int = 1) {
self.init(nibName: nil, bundle: nil)
self.page = page
}
public override func viewDidLoad() {
super.viewDidLoad()
rcBluetooth.onDataWritten = onCommandCompleted
UISetup()
self.commentText.delegate = self
NotificationCenter.default.addObserver(self, selector: #selector(updateTextView),name:NSNotification.Name(rawValue: "Print"), object: nil)
}
public override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
public override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
}
public override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator){
super.viewWillTransition(to: size, with: coordinator)
// printLog(newString: "\(size)")
if(size.width>size.height){
setupPortraitView(size)
}
else{
setupLandscapeView(size)
}
}
fileprivate func sendTouchEvent(_ tag: Int, isPressed: Bool) {
if let delegate = delegate {
delegate.onSendControllerPadButtonStatus(tag: tag, isPressed: isPressed)
}
}
// MARK: - Actions
public func scrollToBottom() {
let stringLength:Int = self.commentText.text.count
self.commentText.scrollRangeToVisible(NSMakeRange(stringLength-1, 0))
}
func onTouchDownForward(_ sender: UIButton) {
sendTouchEvent(sender.tag, isPressed: true)
let isPressed = true
rcBluetooth.moveForward()
scrollToBottom()
}
func onTouchUpForward(_ sender: UIButton) {
let isPressed = false
sendTouchEvent(sender.tag, isPressed: true)
rcBluetooth.stopForward()
}
func onTouchDownBack(_ sender: UIButton) {
sendTouchEvent(sender.tag, isPressed: true)
let isPressed = true
rcBluetooth.moveBack()
scrollToBottom()
}
func onTouchUpBack(_ sender: UIButton) {
sendTouchEvent(sender.tag, isPressed: true)
let isPressed = false
rcBluetooth.stopBack()
//scrollToBottom()
}
func onTouchDownRight(_ sender: UIButton) {
sendTouchEvent(sender.tag, isPressed: true)
let isPressed = true
rcBluetooth.turnRight()
scrollToBottom()
}
func onTouchUpRight(_ sender: UIButton) {
let isPressed = false
rcBluetooth.stopRight()
// scrollToBottom()
}
func onTouchDownLeft(_ sender: UIButton) {
sendTouchEvent(sender.tag, isPressed: true)
let isPressed = true
rcBluetooth.turnLeft()
scrollToBottom()
}
func onTouchUpLeft(_ sender: UIButton) {
let isPressed = false
rcBluetooth.stopLeft()
// scrollToBottom()
}
func addCommandToAssessmentArray(_ command:PlaygroundValue){
// printLog(newString: " addCommandToAssessmentArray: Phase #1 - Function is called")
if(self.commandsForAssessment.count <= 30){
self.commandsForAssessment.append(command)
//printLog(newString: " addCommandToAssessmentArray: Phase #2")
}
// printLog(newString: " addCommandToAssessmentArray: Phase #3")
}
func processCommand(_ command:PlaygroundValue){
// printLog(newString: #function)
rcCommand.sendRobotCommand(rcBluetooth, command)
}
func processCommandForDuration(_ item:PlaygroundValue){
// printLog(newString: #function)
rcCommand.sendRobotDuration(item)
}
func onCommandCompleted(){
// printLog(newString: "Command Completed")
self.sendMessage(.string(Constants.COMMAND_FINISHED))
}
func onCommandCompleted2(){
commentText.text = "Test"
}
func UISetup() {
isLandscape = (self.view.frame.width > self.view.frame.height)
//Connecting Bluetooth View
bleView = PlaygroundBluetoothConnectionView(centralManager: rcBluetooth.centralManager!)
bleView.delegate = bleViewDelegate
bleView.dataSource = bleViewDelegate
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(1000)) {
self.rcBluetooth.centralManager!.connectToLastConnectedPeripheral()
}
self.view.addSubview(bleView)
// Setup debug log
commentText = UITextView(frame: CGRect(x: self.view.frame.width*5/100, y: self.view.frame.height*68/100, width: self.view.frame.width*89/200, height: self.view.frame.height*13/100))
commentText.isEditable = false
commentText.backgroundColor = #colorLiteral(red: 0.2549019754, green: 0.2745098174, blue: 0.3019607961, alpha: 1)
commentText.textColor = #colorLiteral(red: 0.9254902005, green: 0.2352941185, blue: 0.1019607857, alpha: 1)
commentText.font = UIFont.init(name: "Avenir", size: 15)
commentText.textAlignment = .left
commentText.textContainer.lineBreakMode = .byWordWrapping
commentText.layer.borderWidth = 0
commentText.layer.cornerRadius = 18
view.addSubview(commentText)
forwardButton = UIButton(frame: CGRect(x: 320, y: 70, width: 83, height: 60))
forwardButton.setTitle("Forward", for: .normal)
forwardButton.setTitleColor(UIColor.white, for: .normal)
forwardButton.backgroundColor = #colorLiteral(red: 0.1764705926, green: 0.4980392158, blue: 0.7568627596, alpha: 1)
// Rounded button
forwardButton.layer.borderWidth = 0
forwardButton.layer.cornerRadius = 18
forwardButton.tag = 5
forwardButton.addTarget(self, action: #selector(onTouchDownForward(_:)), for: .touchDown)
forwardButton.addTarget(self, action: #selector(onTouchUpForward(_:)), for: .touchUpInside)
forwardButton.addTarget(self, action: #selector(onTouchUpForward(_:)), for: .touchDragExit)
forwardButton.addTarget(self, action: #selector(onTouchUpForward(_:)), for: .touchCancel)
view.addSubview(forwardButton)
backButton = UIButton(frame: CGRect(x: 320, y: 210, width: 83, height: 60))
backButton.setTitle("Back", for: .normal)
backButton.setTitleColor(UIColor.white, for: .normal)
backButton.backgroundColor = #colorLiteral(red: 0.1764705926, green: 0.4980392158, blue: 0.7568627596, alpha: 1)
// Rounded button
backButton.layer.borderWidth = 0
backButton.layer.cornerRadius = 18
backButton.tag = 6
backButton.addTarget(self, action: #selector(onTouchDownBack(_:)), for: .touchDown)
backButton.addTarget(self, action: #selector(onTouchUpBack(_:)), for: .touchUpInside)
backButton.addTarget(self, action: #selector(onTouchUpBack(_:)), for: .touchDragExit)
backButton.addTarget(self, action: #selector(onTouchUpBack(_:)), for: .touchCancel)
view.addSubview(backButton)
//
//
leftButton = UIButton(frame: CGRect(x: 270, y: 140, width: 83, height: 60))
leftButton.setTitle("Left", for: .normal)
leftButton.setTitleColor(UIColor.white, for: .normal)
leftButton.backgroundColor = #colorLiteral(red: 0.1764705926, green: 0.4980392158, blue: 0.7568627596, alpha: 1)
leftButton.tag = 7
// Rounded button
leftButton.layer.borderWidth = 0
leftButton.layer.cornerRadius = 18
leftButton.addTarget(self, action: #selector(onTouchDownLeft(_:)), for: .touchDown)
leftButton.addTarget(self, action: #selector(onTouchUpLeft(_:)), for: .touchUpInside)
leftButton.addTarget(self, action: #selector(onTouchUpLeft(_:)), for: .touchDragExit)
leftButton.addTarget(self, action: #selector(onTouchUpLeft(_:)), for: .touchCancel)
view.addSubview(leftButton)
rightButton = UIButton(frame: CGRect(x: 375, y: 140, width: 83, height: 60))
rightButton.setTitle("Right", for: .normal)
rightButton.setTitleColor(UIColor.white, for: .normal)
rightButton.backgroundColor = #colorLiteral(red: 0.1764705926, green: 0.4980392158, blue: 0.7568627596, alpha: 1)
// Rounded button
rightButton.layer.borderWidth = 0
rightButton.layer.cornerRadius = 18
rightButton.tag = 8
rightButton.addTarget(self, action: #selector(onTouchDownRight(_:)), for: .touchDown)
rightButton.addTarget(self, action: #selector(onTouchUpRight(_:)), for: .touchUpInside)
rightButton.addTarget(self, action: #selector(onTouchUpRight(_:)), for: .touchDragExit)
rightButton.addTarget(self, action: #selector(onTouchUpRight(_:)), for: .touchCancel)
view.addSubview(rightButton)
if(isLandscape){
setupLandscapeView(CGSize(width: self.view.frame.width/2, height: self.view.frame.height))
}
else{
setupPortraitView(CGSize(width: self.view.frame.width, height: self.view.frame.height/2))
}
}
//- LANDSCAPE ORIENTATION
func setupLandscapeView(_ size:CGSize){
isLandscape = true
NSLayoutConstraint.deactivate(btViewConstraints)
btViewConstraints.removeAll()
btViewConstraints.append(bleView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 20))
btViewConstraints.append(bleView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 20))
NSLayoutConstraint.activate(btViewConstraints)
//- Comment Log mark
scrollToBottom()
commentText.frame = CGRect(x: 12, y: 390, width: 490, height: 300)
commentText.font = UIFont.init(name: "Avenir Next", size: 17)
// scrollToBottom()
//-Button Frame Update For Landscape Mode
forwardButton.frame = CGRect(x: 320, y: 70, width: 83, height: 60)
backButton.frame = CGRect(x: 320, y: 210, width: 83, height: 60)
leftButton.frame = CGRect(x: 270, y: 140, width: 83, height: 60)
rightButton.frame = CGRect(x: 375, y: 140, width: 83, height: 60)
}
func setupPortraitView(_ size:CGSize){
isLandscape = false
NSLayoutConstraint.deactivate(btViewConstraints)
btViewConstraints.removeAll()
btViewConstraints.append(bleView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 75))
btViewConstraints.append(bleView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 20))
NSLayoutConstraint.activate(btViewConstraints)
scrollToBottom()
//- Comment Log
commentText.frame = CGRect(x: 190, y: 300, width: 400, height: 140)
commentText.font = UIFont.init(name: "Avenir Next", size: 17)
// scrollToBottom()
//- Button Frame Update For Portrait Mode
forwardButton.frame = CGRect(x: 570, y: 85, width: 83, height: 60)
backButton.frame = CGRect(x: 570, y: 215, width: 83, height: 60)
leftButton.frame = CGRect(x: 520, y: 150, width: 83, height: 60)
rightButton.frame = CGRect(x: 620, y: 150, width: 83, height: 60)
}
func exitProgram(){
// All commands executed
let message: PlaygroundValue = .array(commandsForAssessment)
sendMessage(message)
commandsForAssessment.removeAll()
}
class ConnectionViewDelegate: PlaygroundBluetoothConnectionViewDelegate, PlaygroundBluetoothConnectionViewDataSource {
//PlaygroundBluetoothConnectionViewDataSource
public func connectionView(_ connectionView: PlaygroundBluetoothConnectionView, itemForPeripheral peripheral: CBPeripheral, withAdvertisementData advertisementData: [String : Any]?) -> PlaygroundBluetoothConnectionView.Item {
// Displays UI elements for connectivity
let name = peripheral.name ?? NSLocalizedString("Unknown Device", comment: "")
let icon = UIImage(imageLiteralResourceName:"Images/adafruit_logo_small copy.png")
let issueIcon = icon
return PlaygroundBluetoothConnectionView.Item(name: name, icon: icon, issueIcon: issueIcon, firmwareStatus: nil, batteryLevel: nil)
}
// MARK: PlaygroundBluetoothConnectionView Delegate
public func connectionView(_ connectionView: PlaygroundBluetoothConnectionView, shouldDisplayDiscovered peripheral: CBPeripheral, withAdvertisementData advertisementData: [String : Any]?, rssi: Double) -> Bool {
// Filter out peripheral items (optional)
return true
}
public func connectionView(_ connectionView: PlaygroundBluetoothConnectionView, titleFor state: PlaygroundBluetoothConnectionView.State) -> String {
// Provide a localized title for the given state of the connection view.
switch state {
case .noConnection:
return NSLocalizedString("Connect RC", comment: "")
case .connecting:
return NSLocalizedString("Connecting to RC", comment: "")
case .searchingForPeripherals:
return NSLocalizedString("Searching for RC", comment: "")
case .selectingPeripherals:
return NSLocalizedString("Select your RC", comment: "")
case .connectedPeripheralFirmwareOutOfDate:
return NSLocalizedString("Connect to a Different RC", comment: "")
}
}
public func connectionView(_ connectionView: PlaygroundBluetoothConnectionView, firmwareUpdateInstructionsFor peripheral: CBPeripheral) -> String {
// Provide firmware update instructions.
return "Firmware update instructions here."
}
func connectionView(_ connectionView: PlaygroundBluetoothConnectionView,
willDisconnectFrom peripheral: CBPeripheral) {
}
func connectionView(_ connectionView: PlaygroundBluetoothConnectionView,
shouldConnectTo peripheral: CBPeripheral,
withAdvertisementData advertisementData: [String: Any]?,
rssi: Double) -> Bool {
return true
}
}
}
extension RCViewController: PlaygroundLiveViewMessageHandler {
public func liveViewMessageConnectionOpened(){
// printLog(newString: "<Live View Message Connection Made> ")
}
public func liveViewMessageConnectionClosed() {
commandsForAssessment.removeAll()
//PlaygroundPage.current.finishExecution()
}
//Recieve message from Constant.swift
public func receive(_ message: PlaygroundValue) {
printLog(newString: #function)
if case let .string(command) = message {
if command.isEqual(CommandType.COMMAND_EXIT_PROGRAM.rawValue){
exitProgram()
}
if rcBluetooth.isConnected {
// printLog(newString: "Bluetooth is Online.")
addCommandToAssessmentArray(message)
processCommand(message)
}
else{ // Connection not ready
printLog(newString: "Connect To RC Before Sending Commands.")
}
}
else if case let .dictionary(dict) = message { // Connect to robot
printLog(newString: "Send message; Command String attempt made")
processCommand(message)
addCommandToAssessmentArray(message)
}
else if case let .boolean(result) = message { // Program Results
// programStateImage.stopAnimating()
//isShowingResult = true
// if result{
// myPrint("Receive result from Constants.swift: feedback_success")
// setCommandAnimationAsync(CommandType.FEEDBACK_SUCCESS)
// if robotConnection.isConnected {
// robotCommand.sendRobotCommand(robotConnection, PlaygroundValue.string(CommandType.COMMAND_SOUND_AWESOME.rawValue))
// }
// }
// else{
// myPrint("Receive result from Constants.swift: feedback_fail")
// setCommandAnimationAsync(CommandType.FEEDBACK_FAIL)
// if robotConnection.isConnected {
// robotCommand.sendRobotCommand(robotConnection, PlaygroundValue.string(CommandType.COMMAND_SOUND_WHA.rawValue))
// }
// }
sendMessage(.string(Constants.PROGRAM_FINISHED))
}
else if case let item = message {
if rcBluetooth.isConnected {
printLog(newString: "Integer Sent: \(message)")
addCommandToAssessmentArray(message)
processCommandForDuration(message)
// printLog(newString: "Array count \(commandsForAssessment.count)")
// printLog(newString: "First in the arrray is... \(commandsForAssessment[0])")
} else{ // Connection not ready
printLog(newString: "Send message; (PROGRAM_FINISHED) attempt made")
sendMessage(.string(Constants.PROGRAM_FINISHED))
}
}
}
public func sendMessage(_ message: PlaygroundValue) {
// printLog(newString: "<Send message to Contants.swift>")
rcCommand.durationReset()
send(message)
}
}

View file

@ -0,0 +1,70 @@
//
// UserProcessDelegate.swift
//
//
// Created by Trevor Beaton on 9/20/17.
// Copyright © 2017 Vanguard Logic LLC. All rights reserved.
import Foundation
import PlaygroundSupport
public class UserProcessDelegate: PlaygroundRemoteLiveViewProxyDelegate {
public var pauseHandler: CommandPauseDelegate?
public init(pauseHandler: CommandPauseDelegate?) {
self.pauseHandler = pauseHandler
}
public var onAssessment:((PlaygroundValue)->Bool)?
public init() {
}
//On live view connection closed
public func remoteLiveViewProxyConnectionClosed(_ remoteLiveViewProxy: PlaygroundRemoteLiveViewProxy) {
// Kill user process if LiveView process closed.
//PlaygroundPage.current.finishExecution()
}
//Receive message from live view
public func remoteLiveViewProxy(_ remoteLiveViewProxy: PlaygroundRemoteLiveViewProxy, received message: PlaygroundValue) {
if case let .string(text) = message {
if text.isEqual(Constants.COMMAND_FINISHED){
// Indicate that the handler is ready for more commands.
pauseHandler?.isReadyForMoreCommands = true
// printLog(newString: "UserProcess-remoteLiveViewProxy-TEST")
}
}
if case let item = message {
pauseHandler?.isReadyForMoreCommands = true
// printLog(newString: "UserProcess-remoteLiveViewProxy-TEST")
}
// Update Hints and Assessments
if case let .array(commands) = message {
let result:Bool = (onAssessment?(message))!
//Send Result to live view
let resultValue: PlaygroundValue = .boolean(result)
remoteLiveViewProxy.send(resultValue)
}
// Kill user process if LiveView process closed.
if case let .string(text) = message {
if text.isEqual(Constants.PROGRAM_FINISHED){
PlaygroundPage.current.finishExecution()
}
}
}
}

View file

@ -0,0 +1,21 @@
<?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>Diff</key>
<array>
<dict>
<key>ModifiedContent</key>
<string>3</string>
<key>ModifiedRange</key>
<string>{1274, 1}</string>
<key>OriginalContent</key>
<string></string>
<key>OriginalRange</key>
<string>{1274, 0}</string>
</dict>
</array>
<key>File</key>
<string>Chapters/Chapter1.playgroundchapter/Pages/Page1.playgroundpage/Contents.swift</string>
</dict>
</plist>

View file

@ -0,0 +1,34 @@
<?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>BaseVersion</key>
<string>1.0</string>
<key>DiffPackType</key>
<string>UserEdit</string>
<key>DocumentType</key>
<string>PlaygroundBook</string>
<key>Name</key>
<string>com.razeware.empty</string>
<key>UpdateVersion</key>
<string>1.0</string>
<key>Updates</key>
<dict>
<key>Added</key>
<array/>
<key>Modified</key>
<array>
<dict>
<key>Diff</key>
<string>Chapters/Chapter1.playgroundchapter/Pages/Page1.playgroundpage/Contents.swift.delta</string>
<key>File</key>
<string>Chapters/Chapter1.playgroundchapter/Pages/Page1.playgroundpage/Contents.swift</string>
</dict>
</array>
<key>Removed</key>
<array/>
<key>Replaced</key>
<array/>
</dict>
</dict>
</plist>

BIN
Bluefruit/banner.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

BIN
Bluefruit/preview-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 786 KiB

BIN
Bluefruit/thumbnail.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

41
feed.json Normal file
View file

@ -0,0 +1,41 @@
{
"documents": [
{
"previewImageURLs": [],
"bannerImageURL": "Bluefruit/banner.png",
"thumbnailURL": "Bluefruit/thumbnail.png",
"subtitle": "Learn Swift with Adafruit",
"description": "Learn Swift with Adafruit",
"publishedDate": "2017-05-29T12:00:00+00:00",
"url": "Bluefruit/Swift-Playground-for-Bluefruit.playgroundbook.zip",
"additionalInformation": [
{
"name": "Publisher:",
"value": "Adafruit Industries"
},
{
"name": "Version:",
"value": "1.0"
},
{
"type": "date",
"name": "Released:",
"value": "2017-05-29T12:00:00+00:00"
},
{
"name": "Languages:",
"value": "English"
}
],
"title": "Adafruit Bluefruit",
"contentVersion": "1.0",
"lastUpdatedDate": "2017-05-29T12:00:00+00:00",
"contentIdentifier": "com.adafruit.swiftplaygrounds.bluefruit"
}
],
"publisherName": "Adafruit Industries",
"title": "Adafruit",
"formatVersion": "1.0",
"feedIdentifier": "com.adafruit.swiftplaygrounds",
"contactURL": "support.adafruit.com"
}