Compare commits

..

7 commits

Author SHA1 Message Date
TrevKnows
7e2ee8733b First Commit 2023-12-11 15:38:22 -05:00
TrevKnows
51e7ef0573 Testing 2023-08-08 06:55:08 -04:00
TrevKnows
2ecc9a2ec9 Added PTR to Bluetooth Module 2023-06-27 12:48:46 -04:00
TrevKnows
0646453175 Added PTR to Main and WiFi Modules 2023-06-27 12:33:40 -04:00
TrevKnows
a34e2bf6d3 Board ID Fix - Pass 1
BLE boards running CircuitPython 7+ are now compatible with PyLeap.

It was tested using Circuit Playground Bluefruit Express, CLUE nRF52840 Express, and Feather nRF52840.

I also added a placeholder for boards without specific images.
2023-05-24 00:59:59 -04:00
TrevKnows
e5bb6df771 First Commit 2023-05-17 12:08:13 -04:00
TrevKnows
976bf512fa Removed local reference from source file 2023-03-29 15:34:07 -04:00
80 changed files with 976 additions and 1297 deletions

View file

@ -98,7 +98,7 @@ public class FileTransferClient {
// Set current peripheral // Set current peripheral
self.blePeripheral = blePeripheral self.blePeripheral = blePeripheral
// Setup services // Setup servic*es
let servicesGroup = DispatchGroup() let servicesGroup = DispatchGroup()
var error: Error? = nil var error: Error? = nil

View file

@ -3,19 +3,19 @@
archiveVersion = 1; archiveVersion = 1;
classes = { classes = {
}; };
objectVersion = 52; objectVersion = 54;
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
D505B99C2755323C00386E9F /* NetworkMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D505B99B2755323C00386E9F /* NetworkMonitor.swift */; }; D505B99C2755323C00386E9F /* NetworkMonitor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D505B99B2755323C00386E9F /* NetworkMonitor.swift */; };
D505B99E2756894300386E9F /* ViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = D505B99D2756894300386E9F /* ViewModifier.swift */; }; D505B99E2756894300386E9F /* ViewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = D505B99D2756894300386E9F /* ViewModifier.swift */; };
D50887632A2E35510002B798 /* Wifi_ifaddrs.m in Sources */ = {isa = PBXBuildFile; fileRef = D50887622A2E35510002B798 /* Wifi_ifaddrs.m */; };
D517F68126C5771D002996E8 /* FillerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D517F68026C5771D002996E8 /* FillerView.swift */; }; D517F68126C5771D002996E8 /* FillerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D517F68026C5771D002996E8 /* FillerView.swift */; };
D5199A2F28DD16F100ACC34C /* BleContentTransfer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5199A2E28DD16F100ACC34C /* BleContentTransfer.swift */; }; D5199A2F28DD16F100ACC34C /* BleContentTransfer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5199A2E28DD16F100ACC34C /* BleContentTransfer.swift */; };
D51D1413293A53BD0028AEDD /* WifiCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D51D1412293A53BD0028AEDD /* WifiCellViewModel.swift */; }; D51D1413293A53BD0028AEDD /* WifiCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D51D1412293A53BD0028AEDD /* WifiCellViewModel.swift */; };
D520D69029D4C9380022048D /* WifiServiceCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D520D68F29D4C9380022048D /* WifiServiceCellView.swift */; };
D520D69229D4C9900022048D /* WifiServiceCellSubView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D520D69129D4C9900022048D /* WifiServiceCellSubView.swift */; };
D5267411292E902700D4C79E /* Networking.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5267410292E902700D4C79E /* Networking.swift */; }; D5267411292E902700D4C79E /* Networking.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5267410292E902700D4C79E /* Networking.swift */; };
D5269C00291960A300C0CE4B /* WifiSelection.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5269BFF291960A300C0CE4B /* WifiSelection.swift */; };
D5269C02291997DE00C0CE4B /* WifiServiceCellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5269C01291997DE00C0CE4B /* WifiServiceCellView.swift */; };
D5269C042919985400C0CE4B /* WifiServiceCellSubView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5269C032919985400C0CE4B /* WifiServiceCellSubView.swift */; };
D5269C08291AB75800C0CE4B /* WifiPairingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5269C07291AB75800C0CE4B /* WifiPairingView.swift */; }; D5269C08291AB75800C0CE4B /* WifiPairingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5269C07291AB75800C0CE4B /* WifiPairingView.swift */; };
D52A926D29071DF400973B6B /* SelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D52A926C29071DF400973B6B /* SelectionView.swift */; }; D52A926D29071DF400973B6B /* SelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D52A926C29071DF400973B6B /* SelectionView.swift */; };
D52A926F29078E0A00973B6B /* WifiServiceSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D52A926E29078E0A00973B6B /* WifiServiceSelectionView.swift */; }; D52A926F29078E0A00973B6B /* WifiServiceSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D52A926E29078E0A00973B6B /* WifiServiceSelectionView.swift */; };
@ -68,7 +68,6 @@
D58D887B26CC02B60085604A /* OnboardingViewPure.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58D887A26CC02B60085604A /* OnboardingViewPure.swift */; }; D58D887B26CC02B60085604A /* OnboardingViewPure.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58D887A26CC02B60085604A /* OnboardingViewPure.swift */; };
D58E1C8828A2B10B00AB683E /* WifiView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58E1C8728A2B10B00AB683E /* WifiView.swift */; }; D58E1C8828A2B10B00AB683E /* WifiView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58E1C8728A2B10B00AB683E /* WifiView.swift */; };
D58E1C8A28A2B15E00AB683E /* WifiViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58E1C8928A2B15E00AB683E /* WifiViewModel.swift */; }; D58E1C8A28A2B15E00AB683E /* WifiViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58E1C8928A2B15E00AB683E /* WifiViewModel.swift */; };
D58E1C8D28A2B32C00AB683E /* Wifi_ifaddrs.m in Sources */ = {isa = PBXBuildFile; fileRef = D58E1C8C28A2B32C00AB683E /* Wifi_ifaddrs.m */; };
D58E1C8F28A30A5300AB683E /* WifiNetworkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58E1C8E28A30A5300AB683E /* WifiNetworkService.swift */; }; D58E1C8F28A30A5300AB683E /* WifiNetworkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D58E1C8E28A30A5300AB683E /* WifiNetworkService.swift */; };
D595FC2E2812C23D00569D8C /* Image Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D595FC2D2812C23D00569D8C /* Image Extension.swift */; }; D595FC2E2812C23D00569D8C /* Image Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D595FC2D2812C23D00569D8C /* Image Extension.swift */; };
D59DFD8F268A4A4D001737F6 /* BTConnectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D59DFD8E268A4A4D001737F6 /* BTConnectionView.swift */; }; D59DFD8F268A4A4D001737F6 /* BTConnectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D59DFD8E268A4A4D001737F6 /* BTConnectionView.swift */; };
@ -84,6 +83,8 @@
D5BA1F7F28B66F280012FC62 /* WifiServiceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5BA1F7E28B66F280012FC62 /* WifiServiceManager.swift */; }; D5BA1F7F28B66F280012FC62 /* WifiServiceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5BA1F7E28B66F280012FC62 /* WifiServiceManager.swift */; };
D5BA1F8128B66F920012FC62 /* CircuitPythonService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5BA1F8028B66F920012FC62 /* CircuitPythonService.swift */; }; D5BA1F8128B66F920012FC62 /* CircuitPythonService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5BA1F8028B66F920012FC62 /* CircuitPythonService.swift */; };
D5BA1F8328B68ED40012FC62 /* NetworkPeripheral.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5BA1F8228B68ED40012FC62 /* NetworkPeripheral.swift */; }; D5BA1F8328B68ED40012FC62 /* NetworkPeripheral.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5BA1F8228B68ED40012FC62 /* NetworkPeripheral.swift */; };
D5BBD12C2A1538C100961B68 /* BoardDataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5BBD12B2A1538C100961B68 /* BoardDataProvider.swift */; };
D5BBD12E2A1C6AB300961B68 /* BleBannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5BBD12D2A1C6AB300961B68 /* BleBannerView.swift */; };
D5C474AC27E174A5002DD160 /* WebView Content.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5C474AB27E174A5002DD160 /* WebView Content.swift */; }; D5C474AC27E174A5002DD160 /* WebView Content.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5C474AB27E174A5002DD160 /* WebView Content.swift */; };
D5C474C827E39FD7002DD160 /* ReadexPro-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D5C474C627E39FC8002DD160 /* ReadexPro-Medium.ttf */; }; D5C474C827E39FD7002DD160 /* ReadexPro-Medium.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D5C474C627E39FC8002DD160 /* ReadexPro-Medium.ttf */; };
D5C474C927E39FDA002DD160 /* ReadexPro-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D5C474C727E39FC8002DD160 /* ReadexPro-Regular.ttf */; }; D5C474C927E39FDA002DD160 /* ReadexPro-Regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = D5C474C727E39FC8002DD160 /* ReadexPro-Regular.ttf */; };
@ -116,13 +117,14 @@
D50237E3280994F900F1EE8A /* PyLeap.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = PyLeap.entitlements; path = "../../../../Desktop/Copy Of PyLeap Entitlement/PyLeap.entitlements"; sourceTree = "<group>"; }; D50237E3280994F900F1EE8A /* PyLeap.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; name = PyLeap.entitlements; path = "../../../../Desktop/Copy Of PyLeap Entitlement/PyLeap.entitlements"; sourceTree = "<group>"; };
D505B99B2755323C00386E9F /* NetworkMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkMonitor.swift; sourceTree = "<group>"; }; D505B99B2755323C00386E9F /* NetworkMonitor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkMonitor.swift; sourceTree = "<group>"; };
D505B99D2756894300386E9F /* ViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModifier.swift; sourceTree = "<group>"; }; D505B99D2756894300386E9F /* ViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModifier.swift; sourceTree = "<group>"; };
D50887612A2E35490002B798 /* PyLeap-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "PyLeap-Bridging-Header.h"; sourceTree = "<group>"; };
D50887622A2E35510002B798 /* Wifi_ifaddrs.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Wifi_ifaddrs.m; sourceTree = "<group>"; };
D517F68026C5771D002996E8 /* FillerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FillerView.swift; sourceTree = "<group>"; }; D517F68026C5771D002996E8 /* FillerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FillerView.swift; sourceTree = "<group>"; };
D5199A2E28DD16F100ACC34C /* BleContentTransfer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BleContentTransfer.swift; sourceTree = "<group>"; }; D5199A2E28DD16F100ACC34C /* BleContentTransfer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BleContentTransfer.swift; sourceTree = "<group>"; };
D51D1412293A53BD0028AEDD /* WifiCellViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WifiCellViewModel.swift; sourceTree = "<group>"; }; D51D1412293A53BD0028AEDD /* WifiCellViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WifiCellViewModel.swift; sourceTree = "<group>"; };
D520D68F29D4C9380022048D /* WifiServiceCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WifiServiceCellView.swift; sourceTree = "<group>"; };
D520D69129D4C9900022048D /* WifiServiceCellSubView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WifiServiceCellSubView.swift; sourceTree = "<group>"; };
D5267410292E902700D4C79E /* Networking.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Networking.swift; sourceTree = "<group>"; }; D5267410292E902700D4C79E /* Networking.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Networking.swift; sourceTree = "<group>"; };
D5269BFF291960A300C0CE4B /* WifiSelection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = WifiSelection.swift; path = "PyLeap/Views/Unpaired View/WifiSelection.swift"; sourceTree = SOURCE_ROOT; };
D5269C01291997DE00C0CE4B /* WifiServiceCellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = WifiServiceCellView.swift; path = ../../../../../../Desktop/WifiServiceCellView.swift; sourceTree = "<group>"; };
D5269C032919985400C0CE4B /* WifiServiceCellSubView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = WifiServiceCellSubView.swift; path = ../../../../../../Desktop/WifiServiceCellSubView.swift; sourceTree = "<group>"; };
D5269C07291AB75800C0CE4B /* WifiPairingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WifiPairingView.swift; sourceTree = "<group>"; }; D5269C07291AB75800C0CE4B /* WifiPairingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WifiPairingView.swift; sourceTree = "<group>"; };
D52A926C29071DF400973B6B /* SelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectionView.swift; sourceTree = "<group>"; }; D52A926C29071DF400973B6B /* SelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectionView.swift; sourceTree = "<group>"; };
D52A926E29078E0A00973B6B /* WifiServiceSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WifiServiceSelectionView.swift; sourceTree = "<group>"; }; D52A926E29078E0A00973B6B /* WifiServiceSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WifiServiceSelectionView.swift; sourceTree = "<group>"; };
@ -177,7 +179,6 @@
D58D887A26CC02B60085604A /* OnboardingViewPure.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingViewPure.swift; sourceTree = "<group>"; }; D58D887A26CC02B60085604A /* OnboardingViewPure.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingViewPure.swift; sourceTree = "<group>"; };
D58E1C8728A2B10B00AB683E /* WifiView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WifiView.swift; sourceTree = "<group>"; }; D58E1C8728A2B10B00AB683E /* WifiView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WifiView.swift; sourceTree = "<group>"; };
D58E1C8928A2B15E00AB683E /* WifiViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WifiViewModel.swift; sourceTree = "<group>"; }; D58E1C8928A2B15E00AB683E /* WifiViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WifiViewModel.swift; sourceTree = "<group>"; };
D58E1C8C28A2B32C00AB683E /* Wifi_ifaddrs.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Wifi_ifaddrs.m; sourceTree = "<group>"; };
D58E1C8E28A30A5300AB683E /* WifiNetworkService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WifiNetworkService.swift; sourceTree = "<group>"; }; D58E1C8E28A30A5300AB683E /* WifiNetworkService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WifiNetworkService.swift; sourceTree = "<group>"; };
D595FC2D2812C23D00569D8C /* Image Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Image Extension.swift"; sourceTree = "<group>"; }; D595FC2D2812C23D00569D8C /* Image Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Image Extension.swift"; sourceTree = "<group>"; };
D59DFD8E268A4A4D001737F6 /* BTConnectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTConnectionView.swift; sourceTree = "<group>"; }; D59DFD8E268A4A4D001737F6 /* BTConnectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTConnectionView.swift; sourceTree = "<group>"; };
@ -194,6 +195,8 @@
D5BA1F7E28B66F280012FC62 /* WifiServiceManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WifiServiceManager.swift; sourceTree = "<group>"; }; D5BA1F7E28B66F280012FC62 /* WifiServiceManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WifiServiceManager.swift; sourceTree = "<group>"; };
D5BA1F8028B66F920012FC62 /* CircuitPythonService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircuitPythonService.swift; sourceTree = "<group>"; }; D5BA1F8028B66F920012FC62 /* CircuitPythonService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircuitPythonService.swift; sourceTree = "<group>"; };
D5BA1F8228B68ED40012FC62 /* NetworkPeripheral.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkPeripheral.swift; sourceTree = "<group>"; }; D5BA1F8228B68ED40012FC62 /* NetworkPeripheral.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkPeripheral.swift; sourceTree = "<group>"; };
D5BBD12B2A1538C100961B68 /* BoardDataProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoardDataProvider.swift; sourceTree = "<group>"; };
D5BBD12D2A1C6AB300961B68 /* BleBannerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BleBannerView.swift; sourceTree = "<group>"; };
D5C474AB27E174A5002DD160 /* WebView Content.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WebView Content.swift"; sourceTree = "<group>"; }; D5C474AB27E174A5002DD160 /* WebView Content.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WebView Content.swift"; sourceTree = "<group>"; };
D5C474C227E39FAD002DD160 /* ReadexPro-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "ReadexPro-Bold.ttf"; sourceTree = "<group>"; }; D5C474C227E39FAD002DD160 /* ReadexPro-Bold.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "ReadexPro-Bold.ttf"; sourceTree = "<group>"; };
D5C474C527E39FC8002DD160 /* ReadexPro-Light.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "ReadexPro-Light.ttf"; sourceTree = "<group>"; }; D5C474C527E39FC8002DD160 /* ReadexPro-Light.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "ReadexPro-Light.ttf"; sourceTree = "<group>"; };
@ -218,7 +221,6 @@
D5DD39A628D11817000FAEB8 /* WifiFileTransfer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WifiFileTransfer.swift; sourceTree = "<group>"; }; D5DD39A628D11817000FAEB8 /* WifiFileTransfer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WifiFileTransfer.swift; sourceTree = "<group>"; };
D5DD39A828D11962000FAEB8 /* WifiTransferService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WifiTransferService.swift; sourceTree = "<group>"; }; D5DD39A828D11962000FAEB8 /* WifiTransferService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WifiTransferService.swift; sourceTree = "<group>"; };
D5DD39AA28D234C3000FAEB8 /* SettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewModel.swift; sourceTree = "<group>"; }; D5DD39AA28D234C3000FAEB8 /* SettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewModel.swift; sourceTree = "<group>"; };
D5ECF4B828C8E3C600FBF93D /* PyLeap-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "PyLeap-Bridging-Header.h"; sourceTree = "<group>"; };
D5F53CEA2694B524007634C2 /* Blinka Animation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Blinka Animation.swift"; sourceTree = "<group>"; }; D5F53CEA2694B524007634C2 /* Blinka Animation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Blinka Animation.swift"; sourceTree = "<group>"; };
D5F53CEC2694B7A9007634C2 /* OnboardingBackgroundView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingBackgroundView.swift; sourceTree = "<group>"; }; D5F53CEC2694B7A9007634C2 /* OnboardingBackgroundView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingBackgroundView.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
@ -278,9 +280,11 @@
D59DFDB5268CD052001737F6 /* AppEnvironment.swift */, D59DFDB5268CD052001737F6 /* AppEnvironment.swift */,
D534F3FB280B59090053699C /* ExampleView.swift */, D534F3FB280B59090053699C /* ExampleView.swift */,
D544A2502822D4730038D483 /* Spotlight Extension.swift */, D544A2502822D4730038D483 /* Spotlight Extension.swift */,
D5BBD12A2A15389400961B68 /* Helpers */,
D567E2B428B80DC10009F768 /* Outline.md */, D567E2B428B80DC10009F768 /* Outline.md */,
D567E2DD28C8D3E20009F768 /* SettingsView */,
D5C74DF027EB92E900730505 /* Model */, D5C74DF027EB92E900730505 /* Model */,
D59DFDB2268CCEAC001737F6 /* Views */, D59DFDB2268CCEAC001737F6 /* Features */,
D5C74DEE27EB91D600730505 /* Networking */, D5C74DEE27EB91D600730505 /* Networking */,
D5C74DED27EB919200730505 /* Resource */, D5C74DED27EB919200730505 /* Resource */,
D5C41D6726C5F509004C38E3 /* Download View */, D5C41D6726C5F509004C38E3 /* Download View */,
@ -376,23 +380,29 @@
path = BLESetttings; path = BLESetttings;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
D58E1C8628A2B0DE00AB683E /* Wifi View */ = { D56FFE1C2A2A4ED500EF1E3B /* Views */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
D567E2DD28C8D3E20009F768 /* SettingsView */,
D58E1C8728A2B10B00AB683E /* WifiView.swift */, D58E1C8728A2B10B00AB683E /* WifiView.swift */,
D567E2B728C137880009F768 /* WifiCell.swift */, D567E2B728C137880009F768 /* WifiCell.swift */,
D51D1412293A53BD0028AEDD /* WifiCellViewModel.swift */,
D567E2B928C1382E0009F768 /* WifiSubViewCell.swift */, D567E2B928C1382E0009F768 /* WifiSubViewCell.swift */,
);
path = Views;
sourceTree = "<group>";
};
D58E1C8628A2B0DE00AB683E /* Wifi Module */ = {
isa = PBXGroup;
children = (
D56FFE1C2A2A4ED500EF1E3B /* Views */,
D51D1412293A53BD0028AEDD /* WifiCellViewModel.swift */,
D58E1C8928A2B15E00AB683E /* WifiViewModel.swift */, D58E1C8928A2B15E00AB683E /* WifiViewModel.swift */,
D5269C07291AB75800C0CE4B /* WifiPairingView.swift */, D5269C07291AB75800C0CE4B /* WifiPairingView.swift */,
D52A926E29078E0A00973B6B /* WifiServiceSelectionView.swift */, D52A926E29078E0A00973B6B /* WifiServiceSelectionView.swift */,
D5269BFF291960A300C0CE4B /* WifiSelection.swift */, D520D68F29D4C9380022048D /* WifiServiceCellView.swift */,
D5269C01291997DE00C0CE4B /* WifiServiceCellView.swift */,
D5269C032919985400C0CE4B /* WifiServiceCellSubView.swift */,
D5BA1F7E28B66F280012FC62 /* WifiServiceManager.swift */, D5BA1F7E28B66F280012FC62 /* WifiServiceManager.swift */,
D5DD39A628D11817000FAEB8 /* WifiFileTransfer.swift */, D5DD39A628D11817000FAEB8 /* WifiFileTransfer.swift */,
D5DD39A828D11962000FAEB8 /* WifiTransferService.swift */, D5DD39A828D11962000FAEB8 /* WifiTransferService.swift */,
D520D69129D4C9900022048D /* WifiServiceCellSubView.swift */,
D5AA27F728CA785B001CCE25 /* CircuitPythonType.swift */, D5AA27F728CA785B001CCE25 /* CircuitPythonType.swift */,
D5482F4A28E75053000B0C8E /* LocalNetworkAuth.swift */, D5482F4A28E75053000B0C8E /* LocalNetworkAuth.swift */,
D5AA27F928CA8D46001CCE25 /* WifiStatusHeaderBarView.swift */, D5AA27F928CA8D46001CCE25 /* WifiStatusHeaderBarView.swift */,
@ -402,17 +412,17 @@
D5BA1F8028B66F920012FC62 /* CircuitPythonService.swift */, D5BA1F8028B66F920012FC62 /* CircuitPythonService.swift */,
D58E1C8E28A30A5300AB683E /* WifiNetworkService.swift */, D58E1C8E28A30A5300AB683E /* WifiNetworkService.swift */,
D5BA1F8228B68ED40012FC62 /* NetworkPeripheral.swift */, D5BA1F8228B68ED40012FC62 /* NetworkPeripheral.swift */,
D50887622A2E35510002B798 /* Wifi_ifaddrs.m */,
D50887612A2E35490002B798 /* PyLeap-Bridging-Header.h */,
D5D7DF2A28B3E321008552D1 /* BasicCredentials.swift */, D5D7DF2A28B3E321008552D1 /* BasicCredentials.swift */,
D58E1C8C28A2B32C00AB683E /* Wifi_ifaddrs.m */,
D5ECF4B828C8E3C600FBF93D /* PyLeap-Bridging-Header.h */,
); );
path = "Wifi View"; path = "Wifi Module";
sourceTree = "<group>"; sourceTree = "<group>";
}; };
D59DFDB2268CCEAC001737F6 /* Views */ = { D59DFDB2268CCEAC001737F6 /* Features */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
D58E1C8628A2B0DE00AB683E /* Wifi View */, D58E1C8628A2B0DE00AB683E /* Wifi Module */,
D5507ACE26C668BC00512BAA /* UI Components */, D5507ACE26C668BC00512BAA /* UI Components */,
D59DFDB3268CCEB9001737F6 /* Onboarding Views */, D59DFDB3268CCEB9001737F6 /* Onboarding Views */,
D5C474AA27E1746F002DD160 /* WebView */, D5C474AA27E1746F002DD160 /* WebView */,
@ -424,7 +434,7 @@
D517F68026C5771D002996E8 /* FillerView.swift */, D517F68026C5771D002996E8 /* FillerView.swift */,
D5D1F4AC27ECFD200040E2BF /* Startup View */, D5D1F4AC27ECFD200040E2BF /* Startup View */,
); );
path = Views; path = Features;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
D59DFDB3268CCEB9001737F6 /* Onboarding Views */ = { D59DFDB3268CCEB9001737F6 /* Onboarding Views */ = {
@ -438,6 +448,14 @@
path = "Onboarding Views"; path = "Onboarding Views";
sourceTree = "<group>"; sourceTree = "<group>";
}; };
D5BBD12A2A15389400961B68 /* Helpers */ = {
isa = PBXGroup;
children = (
D5BBD12B2A1538C100961B68 /* BoardDataProvider.swift */,
);
path = Helpers;
sourceTree = "<group>";
};
D5C41D6726C5F509004C38E3 /* Download View */ = { D5C41D6726C5F509004C38E3 /* Download View */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -535,6 +553,7 @@
children = ( children = (
D59E31A9281B8DD300D24211 /* DownloadState.swift */, D59E31A9281B8DD300D24211 /* DownloadState.swift */,
D5507ACB26C668BC00512BAA /* BleModuleView.swift */, D5507ACB26C668BC00512BAA /* BleModuleView.swift */,
D5BBD12D2A1C6AB300961B68 /* BleBannerView.swift */,
D5507ACC26C668BC00512BAA /* BleModuleViewModel.swift */, D5507ACC26C668BC00512BAA /* BleModuleViewModel.swift */,
D5199A2E28DD16F100ACC34C /* BleContentTransfer.swift */, D5199A2E28DD16F100ACC34C /* BleContentTransfer.swift */,
D5D5BB3828DD19F000E5D93F /* BleContentCommands.swift */, D5D5BB3828DD19F000E5D93F /* BleContentCommands.swift */,
@ -605,8 +624,9 @@
D52F7E682672F4C400911D43 /* Project object */ = { D52F7E682672F4C400911D43 /* Project object */ = {
isa = PBXProject; isa = PBXProject;
attributes = { attributes = {
BuildIndependentTargetsInParallel = YES;
LastSwiftUpdateCheck = 1250; LastSwiftUpdateCheck = 1250;
LastUpgradeCheck = 1250; LastUpgradeCheck = 1500;
TargetAttributes = { TargetAttributes = {
D52F7E6F2672F4C400911D43 = { D52F7E6F2672F4C400911D43 = {
CreatedOnToolsVersion = 12.5; CreatedOnToolsVersion = 12.5;
@ -659,6 +679,7 @@
D5D1F4B827ED2F4F0040E2BF /* FileManagerCheck.swift in Sources */, D5D1F4B827ED2F4F0040E2BF /* FileManagerCheck.swift in Sources */,
D52BE82A26A0660200630900 /* KeyboardUtils.swift in Sources */, D52BE82A26A0660200630900 /* KeyboardUtils.swift in Sources */,
D58182FB27F732E40091C43B /* SubCellViewModel.swift in Sources */, D58182FB27F732E40091C43B /* SubCellViewModel.swift in Sources */,
D520D69229D4C9900022048D /* WifiServiceCellSubView.swift in Sources */,
D5D5BB3928DD19F000E5D93F /* BleContentCommands.swift in Sources */, D5D5BB3928DD19F000E5D93F /* BleContentCommands.swift in Sources */,
D544A1D2281B9BB70038D483 /* Buttons.swift in Sources */, D544A1D2281B9BB70038D483 /* Buttons.swift in Sources */,
D5D1F4AE27ECFDA10040E2BF /* GifImage.swift in Sources */, D5D1F4AE27ECFDA10040E2BF /* GifImage.swift in Sources */,
@ -678,9 +699,9 @@
D56F640C270242CA000E5975 /* UIColor+LightAndDark.swift in Sources */, D56F640C270242CA000E5975 /* UIColor+LightAndDark.swift in Sources */,
D5F53CED2694B7A9007634C2 /* OnboardingBackgroundView.swift in Sources */, D5F53CED2694B7A9007634C2 /* OnboardingBackgroundView.swift in Sources */,
D5D1F4B227ECFF760040E2BF /* ProjectsModel.swift in Sources */, D5D1F4B227ECFF760040E2BF /* ProjectsModel.swift in Sources */,
D5269C02291997DE00C0CE4B /* WifiServiceCellView.swift in Sources */,
D5BA1F7A28B52A490012FC62 /* WifiListDetailView.swift in Sources */, D5BA1F7A28B52A490012FC62 /* WifiListDetailView.swift in Sources */,
D52F7E742672F4C400911D43 /* PyLeapApp.swift in Sources */, D52F7E742672F4C400911D43 /* PyLeapApp.swift in Sources */,
D5BBD12E2A1C6AB300961B68 /* BleBannerView.swift in Sources */,
D567E2B628B81B730009F768 /* Queue.swift in Sources */, D567E2B628B81B730009F768 /* Queue.swift in Sources */,
D534F3FC280B59090053699C /* ExampleView.swift in Sources */, D534F3FC280B59090053699C /* ExampleView.swift in Sources */,
D57858ED28327E18008E8BE4 /* PairingTutorialView.swift in Sources */, D57858ED28327E18008E8BE4 /* PairingTutorialView.swift in Sources */,
@ -702,7 +723,6 @@
D5DD39A728D11817000FAEB8 /* WifiFileTransfer.swift in Sources */, D5DD39A728D11817000FAEB8 /* WifiFileTransfer.swift in Sources */,
D5AA27FA28CA8D46001CCE25 /* WifiStatusHeaderBarView.swift in Sources */, D5AA27FA28CA8D46001CCE25 /* WifiStatusHeaderBarView.swift in Sources */,
D5F53CEB2694B524007634C2 /* Blinka Animation.swift in Sources */, D5F53CEB2694B524007634C2 /* Blinka Animation.swift in Sources */,
D5269C00291960A300C0CE4B /* WifiSelection.swift in Sources */,
D5597BF826A9E14B00DF17C0 /* AppDelegate.swift in Sources */, D5597BF826A9E14B00DF17C0 /* AppDelegate.swift in Sources */,
D56F640E270242CA000E5975 /* String+DeletingPrefix.swift in Sources */, D56F640E270242CA000E5975 /* String+DeletingPrefix.swift in Sources */,
D57858F328333CBC008E8BE4 /* TroubleshootView.swift in Sources */, D57858F328333CBC008E8BE4 /* TroubleshootView.swift in Sources */,
@ -739,18 +759,19 @@
D567E2B828C137880009F768 /* WifiCell.swift in Sources */, D567E2B828C137880009F768 /* WifiCell.swift in Sources */,
D5D1F4B027ECFDE00040E2BF /* NavBarModifier.swift in Sources */, D5D1F4B027ECFDE00040E2BF /* NavBarModifier.swift in Sources */,
D5597C0C26AF018800DF17C0 /* View+If.swift in Sources */, D5597C0C26AF018800DF17C0 /* View+If.swift in Sources */,
D58E1C8D28A2B32C00AB683E /* Wifi_ifaddrs.m in Sources */,
D56B75D4294BAAB400D008E7 /* BLESettingsView.swift in Sources */, D56B75D4294BAAB400D008E7 /* BLESettingsView.swift in Sources */,
D50887632A2E35510002B798 /* Wifi_ifaddrs.m in Sources */,
D52BE7EE269DF36E00630900 /* DownloadViewModel.swift in Sources */, D52BE7EE269DF36E00630900 /* DownloadViewModel.swift in Sources */,
D59E31AA281B8DD300D24211 /* DownloadState.swift in Sources */, D59E31AA281B8DD300D24211 /* DownloadState.swift in Sources */,
D520D69029D4C9380022048D /* WifiServiceCellView.swift in Sources */,
D5BA1F7F28B66F280012FC62 /* WifiServiceManager.swift in Sources */, D5BA1F7F28B66F280012FC62 /* WifiServiceManager.swift in Sources */,
D5C74DF327EB92FA00730505 /* View+Extensions.swift in Sources */, D5C74DF327EB92FA00730505 /* View+Extensions.swift in Sources */,
D5AA27F828CA785B001CCE25 /* CircuitPythonType.swift in Sources */, D5AA27F828CA785B001CCE25 /* CircuitPythonType.swift in Sources */,
D5199A2F28DD16F100ACC34C /* BleContentTransfer.swift in Sources */, D5199A2F28DD16F100ACC34C /* BleContentTransfer.swift in Sources */,
D535E21628E1FA910096E548 /* ScrollRefreshableView.swift in Sources */, D535E21628E1FA910096E548 /* ScrollRefreshableView.swift in Sources */,
D5640216271B54BF00AE1519 /* MainSelectionView.swift in Sources */, D5640216271B54BF00AE1519 /* MainSelectionView.swift in Sources */,
D5269C042919985400C0CE4B /* WifiServiceCellSubView.swift in Sources */,
D505B99C2755323C00386E9F /* NetworkMonitor.swift in Sources */, D505B99C2755323C00386E9F /* NetworkMonitor.swift in Sources */,
D5BBD12C2A1538C100961B68 /* BoardDataProvider.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -794,6 +815,7 @@
DEBUG_INFORMATION_FORMAT = dwarf; DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES; ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_DYNAMIC_NO_PIC = NO; GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES; GCC_NO_COMMON_BLOCKS = YES;
@ -808,7 +830,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0; IPHONEOS_DEPLOYMENT_TARGET = 17.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES; ONLY_ACTIVE_ARCH = YES;
@ -855,6 +877,7 @@
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO; ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
GCC_NO_COMMON_BLOCKS = YES; GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
@ -863,7 +886,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0; IPHONEOS_DEPLOYMENT_TARGET = 17.0;
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
SDKROOT = iphoneos; SDKROOT = iphoneos;
@ -881,22 +904,22 @@
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = PyLeap/PyLeap.entitlements; CODE_SIGN_ENTITLEMENTS = PyLeap/PyLeap.entitlements;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 0; CURRENT_PROJECT_VERSION = 2;
DEVELOPMENT_ASSET_PATHS = "\"PyLeap/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"PyLeap/Preview Content\"";
DEVELOPMENT_TEAM = 2X94RM7457; DEVELOPMENT_TEAM = 2X94RM7457;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = PyLeap/Info.plist; INFOPLIST_FILE = PyLeap/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.5; IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 2.1.1; MARKETING_VERSION = 2.2;
PRODUCT_BUNDLE_IDENTIFIER = com.adafruit.PyLeap; PRODUCT_BUNDLE_IDENTIFIER = com.adafruit.PyLeap;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx";
SUPPORTS_MACCATALYST = NO; SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_OBJC_BRIDGING_HEADER = "PyLeap/Views/Wifi View/PyLeap-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
@ -911,22 +934,22 @@
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = PyLeap/PyLeap.entitlements; CODE_SIGN_ENTITLEMENTS = PyLeap/PyLeap.entitlements;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 0; CURRENT_PROJECT_VERSION = 2;
DEVELOPMENT_ASSET_PATHS = "\"PyLeap/Preview Content\""; DEVELOPMENT_ASSET_PATHS = "\"PyLeap/Preview Content\"";
DEVELOPMENT_TEAM = 2X94RM7457; DEVELOPMENT_TEAM = 2X94RM7457;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
INFOPLIST_FILE = PyLeap/Info.plist; INFOPLIST_FILE = PyLeap/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.5; IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = ( LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 2.1.1; MARKETING_VERSION = 2.2;
PRODUCT_BUNDLE_IDENTIFIER = com.adafruit.PyLeap; PRODUCT_BUNDLE_IDENTIFIER = com.adafruit.PyLeap;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator macosx";
SUPPORTS_MACCATALYST = NO; SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_OBJC_BRIDGING_HEADER = "PyLeap/Views/Wifi View/PyLeap-Bridging-Header.h";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
}; };

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<Scheme <Scheme
LastUpgradeVersion = "1320" LastUpgradeVersion = "1500"
version = "1.3"> version = "1.3">
<BuildAction <BuildAction
parallelizeBuildables = "YES" parallelizeBuildables = "YES"

View file

@ -0,0 +1,21 @@
{
"images" : [
{
"filename" : "Default device image.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View file

@ -0,0 +1,47 @@
//
// BleBannerView.swift
// PyLeap
//
// Created by Trevor Beaton on 5/22/23.
//
import SwiftUI
struct BleBannerView: View {
var deviceName: String
var disconnectAction: () -> Void
var body: some View {
VStack {
HStack {
Image("bluetoothLogo")
.resizable()
.scaledToFit()
.frame(width: 16, height: 16)
Text(deviceName)
.font(Font.custom("ReadexPro-Regular", size: 14))
Button(action: disconnectAction) {
Text("Disconnect")
.font(Font.custom("ReadexPro-Bold", size: 14))
.underline()
}
}
// .background(GeometryReader {
// Color.clear.preference(key: ViewHeightKey.self,
// value: $0.frame(in: .local).size.height)
// })
}
}
}
struct BleBannerView_Previews: PreviewProvider {
static var previews: some View {
BleBannerView(deviceName: "Test", disconnectAction: {
print("Dismiss Action")
})
}
}

View file

@ -13,8 +13,8 @@ class BleContentCommands: ObservableObject {
private weak var fileTransferClient: FileTransferClient? private weak var fileTransferClient: FileTransferClient?
@Published var transmissionProgress: TransmissionProgress? @Published var transmissionProgress: TransmissionProgress?
@Published var isTransmiting = false @Published var isTransmiting = false
@Published var bootUpInfo = String()
@Published var counter = 0 @Published var counter = 0
enum ProjectViewError: LocalizedError { enum ProjectViewError: LocalizedError {
@ -63,7 +63,6 @@ class BleContentCommands: ObservableObject {
@Published var lastTransmit: TransmissionLog? = TransmissionLog(type: .write(size: 334)) @Published var lastTransmit: TransmissionLog? = TransmissionLog(type: .write(size: 334))
@Published var activeAlert: ActiveAlert?
// Data // Data
private let bleManager = BleManager.shared private let bleManager = BleManager.shared
@ -103,9 +102,6 @@ class BleContentCommands: ObservableObject {
self.lastTransmit = TransmissionLog(type: .read(data: data)) self.lastTransmit = TransmissionLog(type: .read(data: data))
let str = String(decoding: data, as: UTF8.self) let str = String(decoding: data, as: UTF8.self)
print("Read: \(str)") print("Read: \(str)")
self.bootUpInfo = str
sharedBootinfo = str
case .failure(let error): case .failure(let error):
self.lastTransmit = TransmissionLog(type: .error(message: error.localizedDescription)) self.lastTransmit = TransmissionLog(type: .error(message: error.localizedDescription))

View file

@ -8,7 +8,24 @@
import SwiftUI import SwiftUI
import FileTransferClient import FileTransferClient
class BleContentTransfer: ObservableObject { protocol BoardInfoDelegate: AnyObject {
func boardInfoDidUpdate(to newBoard: Board?)
}
enum ListCommandError: Error {
case belowMinimum
case isPrime
}
class BleContentTransfer: ObservableObject, BoardInfoDelegate {
@Published var currentBoard: Board? {
didSet {
}
}
static let shared = BleContentTransfer()
private weak var fileTransferClient: FileTransferClient? private weak var fileTransferClient: FileTransferClient?
@ -22,7 +39,6 @@ class BleContentTransfer: ObservableObject {
@Published var downloadState: DownloadState = .idle @Published var downloadState: DownloadState = .idle
@State var circuitPythonVersion = String()
@Published var isTransmiting = false @Published var isTransmiting = false
@ -32,18 +48,13 @@ class BleContentTransfer: ObservableObject {
@Published var downloaderror = false @Published var downloaderror = false
@Published var bootUpInfo = ""
@Published var contentCommands = BleContentCommands() @Published var contentCommands = BleContentCommands()
// CLEAN UP // CLEAN UP
var projectDirectories: [URL] = [] var projectDirectories: [URL] = []
var projectFiles: [URL] = [] var projectFiles: [URL] = []
var returnedArray = [[String]]()
var fileTransferArray : [URL] = []
var filesReadyForTransfer : [URL] = []
@Published var sendingBundle = false @Published var sendingBundle = false
@Published var didCompleteTranfer = false @Published var didCompleteTranfer = false
@ -58,10 +69,12 @@ class BleContentTransfer: ObservableObject {
@Published var isConnectedToInternet = false @Published var isConnectedToInternet = false
@Published var showAlert = false @Published var showAlert = false
var downloadPhases: String = ""
let directoryPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] let directoryPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
func boardInfoDidUpdate(to newBoard: Board?) {
self.currentBoard = newBoard
// Add any other logic you need to handle the new board here.
}
enum ProjectViewError: LocalizedError { enum ProjectViewError: LocalizedError {
case fileTransferUndefined case fileTransferUndefined
@ -77,25 +90,9 @@ class BleContentTransfer: ObservableObject {
} }
init() { init() {
getCPVersion()
registerNotification(enabled: true) registerNotification(enabled: true)
} }
func getCPVersion() {
if sharedBootinfo.contains("CircuitPython 7") {
print("circuitPythonVersion = 7")
circuitPythonVersion = "7"
print(circuitPythonVersion)
}
if sharedBootinfo.contains("CircuitPython 8") {
print("circuitPythonVersion = 8")
circuitPythonVersion = "8"
print(circuitPythonVersion)
}
}
private weak var didCompleteZip: NSObjectProtocol? private weak var didCompleteZip: NSObjectProtocol?
private weak var didEncounterTransferError: NSObjectProtocol? private weak var didEncounterTransferError: NSObjectProtocol?
private weak var downloadErrorDidOccur: NSObjectProtocol? private weak var downloadErrorDidOccur: NSObjectProtocol?
@ -311,13 +308,6 @@ class BleContentTransfer: ObservableObject {
} }
} }
// filterCPVersion(incomingArray: regularFileUrls)
// filterCPFiles(filesArray: regularFileUrls)
// pathManipulation(arrayOfAny: filterCPVersion(incomingArray: projectDirectories), regularArray: filterCPVersion(incomingArray: projectFiles))
projectDirectories.removeFirst() projectDirectories.removeFirst()
DispatchQueue.main.async { DispatchQueue.main.async {
@ -336,57 +326,19 @@ class BleContentTransfer: ObservableObject {
func filterCPVersion(incomingArray: [URL]) -> [URL] { func filterCPVersion(incomingArray: [URL]) -> [URL] {
print("project name \(projectName)")
let filteredList = incomingArray.filter {
for i in incomingArray { let lastPathComponent = $0.lastPathComponent
print("incoming Array \(i.absoluteString)") return lastPathComponent != "CircuitPython 8.x"
&& lastPathComponent != "CircuitPython 7.x"
&& lastPathComponent != "CircuitPython_Templates"
} }
let listForCurrentCPVersion = filteredList.filter {
$0.absoluteString.contains("CircuitPython%20\(Board.shared.versionNumber).x")
let listWithoutCP8Folder = incomingArray.filter {
$0.lastPathComponent != ("CircuitPython 8.x")
}
let listWithoutCP7Folder = listWithoutCP8Folder.filter {
$0.lastPathComponent != ("CircuitPython 7.x")
}
//CircuitPython_Templates
let removedCPTemplates = listWithoutCP7Folder.filter {
$0.lastPathComponent != ("CircuitPython_Templates")
}
if sharedBootinfo.contains("CircuitPython 8") {
let listForCurrentCPVersion = removedCPTemplates.filter {
!$0.absoluteString.contains("CircuitPython%207.x")
}
for i in listForCurrentCPVersion {
print("listForCurrentCPVersion :- \(i.absoluteString)")
} }
return listForCurrentCPVersion return listForCurrentCPVersion
}
if sharedBootinfo.contains("CircuitPython 7") {
let listForCurrentCPVersion = listWithoutCP7Folder.filter {
!$0.absoluteString.contains("CircuitPython%208.x")
}
for i in listForCurrentCPVersion {
print("listForCurrentCPVersion :- \(i.absoluteString)")
}
return listForCurrentCPVersion
}
return listWithoutCP7Folder
} }
@ -394,12 +346,8 @@ class BleContentTransfer: ObservableObject {
print("\(#function) @Line: \(#line)") print("\(#function) @Line: \(#line)")
var recursiveArray = directoryArray var recursiveArray = directoryArray
for i in recursiveArray {
print("recursiveArray \(i.absoluteString)")
}
if directoryArray.isEmpty { if directoryArray.isEmpty {
print("Array is empty. makeDirectory is done - Ready for file transfer!")
newTransfer(listOf: regularFilesArray) newTransfer(listOf: regularFilesArray)
} else { } else {
@ -490,10 +438,7 @@ class BleContentTransfer: ObservableObject {
} }
enum ListCommandError: Error {
case belowMinimum
case isPrime
}
func checkIfFilesExistOnBoard(url: URL) { func checkIfFilesExistOnBoard(url: URL) {
@ -527,29 +472,16 @@ class BleContentTransfer: ObservableObject {
var tempPathComponents = url.pathComponents var tempPathComponents = url.pathComponents
print("Incoming URL for makeFileString: \(url.absoluteString)") print("Incoming URL for makeFileString: \(url.absoluteString)")
indexOfCP = tempPathComponents.firstIndex(of: "CircuitPython \(Board.shared.versionNumber).x")!
if sharedBootinfo.contains("CircuitPython 7") {
indexOfCP = tempPathComponents.firstIndex(of: "CircuitPython 7.x")!
tempPathComponents.removeSubrange(0...indexOfCP)
tempPathComponents.removeLast()
var joinedArrayPath = tempPathComponents.joined(separator: "/")
print("\(#function) @Line: \(#line)")
print("Outgoing makeFileString: \(joinedArrayPath) for CP 7")
return joinedArrayPath
}
if sharedBootinfo.contains("CircuitPython 8") {
indexOfCP = tempPathComponents.firstIndex(of: "CircuitPython 8.x")!
tempPathComponents.removeSubrange(0...indexOfCP) tempPathComponents.removeSubrange(0...indexOfCP)
tempPathComponents.removeLast() tempPathComponents.removeLast()
var joinedArrayPath = tempPathComponents.joined(separator: "/") var joinedArrayPath = tempPathComponents.joined(separator: "/")
print("\(#function) @Line: \(#line)") print("\(#function) @Line: \(#line)")
print("Outgoing makeFileString: \(joinedArrayPath) for CP 8") print("Outgoing makeFileString: \(joinedArrayPath) for CP 8")
return joinedArrayPath return joinedArrayPath
}
return ""
} }
@ -561,256 +493,81 @@ class BleContentTransfer: ObservableObject {
print("Incoming URL: \(url.absoluteString) ") print("Incoming URL: \(url.absoluteString) ")
if sharedBootinfo.contains("CircuitPython 7") {
print(tempPathComponents) print(tempPathComponents)
indexOfCP = tempPathComponents.firstIndex(of: "CircuitPython 7.x")! indexOfCP = tempPathComponents.firstIndex(of: "CircuitPython \(Board.shared.versionNumber).x")!
tempPathComponents.removeSubrange(0...indexOfCP) tempPathComponents.removeSubrange(0...indexOfCP)
var joinedArrayPath = tempPathComponents.joined(separator: "/") var joinedArrayPath = tempPathComponents.joined(separator: "/")
print("\(#function) @Line: \(#line)") print("\(#function) @Line: \(#line)")
print("Outgoing String: \(joinedArrayPath) for CP 7") print("Outgoing Directory String: \(joinedArrayPath) for CP \(Board.shared.versionNumber)")
return joinedArrayPath return joinedArrayPath
}
if sharedBootinfo.contains("CircuitPython 8") {
indexOfCP = tempPathComponents.firstIndex(of: "CircuitPython 8.x")!
tempPathComponents.removeSubrange(0...indexOfCP)
var joinedArrayPath = tempPathComponents.joined(separator: "/")
print("\(#function) @Line: \(#line)")
print("Outgoing String: \(joinedArrayPath) for CP 8")
return joinedArrayPath
} else {
guard let projectName = projectName else {
return "Unknown"
}
indexOfCP = tempPathComponents.firstIndex(of: projectName)!
tempPathComponents.removeSubrange(0...indexOfCP)
var joinedArrayPath = tempPathComponents.joined(separator: "/")
print("\(#function) @Line: \(#line)")
print("Outgoing String: \(joinedArrayPath) for CP 7")
return joinedArrayPath
}
return ""
} }
func newTransfer(listOf urls: [URL]) { func newTransfer(listOf urls: [URL]) {
print("\(#function) @Line: \(#line)") guard !urls.isEmpty else {
var copiedFiles = urls
print("Files left for transfer: \(urls.count)")
print("Attempting to transfer... \(urls.first?.lastPathComponent ?? "No file found")")
DispatchQueue.main.async {
self.counter += 1
}
if copiedFiles.isEmpty {
print("All Files Transferred! 👍") print("All Files Transferred! 👍")
self.completedTransfer() completedTransfer()
DispatchQueue.main.asyncAfter(deadline: .now() + 2){ DispatchQueue.main.asyncAfter(deadline: .now() + 2){
self.sendingBundle = false self.resetTransferParameters()
self.completedTransfer() }
self.numOfFiles = 0 return
self.counter = 0
self.contentList.removeAll()
} }
} else { print("\(#function) @Line: \(#line)")
print("Files left for transfer: \(urls.count)")
print("Attempting to transfer... \(urls.first?.lastPathComponent ?? "No file found")")
DispatchQueue.main.async { self.counter += 1 }
guard let data = try? Data(contentsOf: URL(string: copiedFiles.first!.absoluteString)!) else { guard let url = urls.first,
let data = try? Data(contentsOf: url) else {
print("File not found") print("File not found")
return return
} }
let path = isDirectFile(name: url.lastPathComponent) ? url.lastPathComponent : makeDirectoryString(url: url)
if copiedFiles.first?.lastPathComponent == "code.py" || copiedFiles.first?.lastPathComponent == "README.txt" { contentCommands.writeFileCommand(path: path, data: data) { result in
print("Input for writeFileCommand: \(copiedFiles.first?.absoluteString)")
self.contentCommands.writeFileCommand(path: copiedFiles.first!.lastPathComponent, data: data) { result in
switch result { switch result {
case .success:
case .success(_):
print("Success ✅") print("Success ✅")
copiedFiles.removeFirst() self.newTransfer(listOf: Array(urls.dropFirst()))
self.newTransfer(listOf: copiedFiles)
case .failure(let error): case .failure(let error):
print("Transfer Failure \(error)")
self.handleTransferError(error)
}
}
}
private func handleTransferError(_ error: Error) {
DispatchQueue.main.async {
print("\(#function) @Line: \(#line)") print("\(#function) @Line: \(#line)")
print(error) print(error)
DispatchQueue.main.async {
print("Transfer Failure \(error)")
self.downloadState = .failed self.downloadState = .failed
DispatchQueue.main.asyncAfter(deadline: .now() + 1) { DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
self.downloadState = .idle self.downloadState = .idle
} }
NotificationCenter.default.post(name: .didEncounterTransferError, object: nil, userInfo: nil) NotificationCenter.default.post(name: .didEncounterTransferError, object: nil, userInfo: nil)
} }
} }
}
} else {
print("Checking... 🫥") private func resetTransferParameters() {
// print("makeDirectoryString transfer \(makeDirectoryString(url: copiedFiles.first!))") sendingBundle = false
completedTransfer()
let directoryPath = makeDirectoryString(url: copiedFiles.first!) numOfFiles = 0
counter = 0
print("Input writeFileCommand: \(directoryPath)") contentList.removeAll()
self.contentCommands.writeFileCommand(path: directoryPath, data: data) { result in
switch result {
case .success(_):
print("Success ✅")
copiedFiles.removeFirst()
self.newTransfer(listOf: copiedFiles)
case .failure(let error):
print("\(#function) @Line: \(#line)")
print(error)
DispatchQueue.main.async {
print("Transfer Failure \(error)")
self.downloadState = .failed
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
self.downloadState = .idle
}
NotificationCenter.default.post(name: .didEncounterTransferError, object: nil, userInfo: nil)
}
}
}
} }
private func isDirectFile(name: String) -> Bool {
return name == "code.py" || name == "README.txt"
} }
}
func filePathMod(listOf files: [URL]) {
var indexOfCP = 0
for i in files {
print("-\(i.absoluteString)-\n")
}
var tempArray = files
if files.isEmpty {
// Continue
print("\(#function) @Line: \(#line)")
print("Done!")
// print("RETURNED PATHS: \(returnedArray)\n")
for i in filesReadyForTransfer {
print("\(i)")
}
// self.transferFiles(files: fileArray)
// validateDirectory(directoryArray: returnedArray, fileArray: fileArray)
} else {
var tempPath = files[0].pathComponents
print("temp path \(tempPath)")
if tempPath.contains("CircuitPython 7.x") {
print("\(#function) @Line: \(#line)")
indexOfCP = tempPath.firstIndex(of: "CircuitPython 7.x")!
tempPath.removeSubrange(0...indexOfCP)
print("removeSubrange temp path - \(tempPath)")
tempArray.removeFirst()
var joinedArrayPath = tempPath.joined(separator: "/")
filesReadyForTransfer.append(URL(string: joinedArrayPath)!)
filePathMod(listOf: tempArray)
}
if tempPath.contains("CircuitPython 8.x") {
print("\(#function) @Line: \(#line)")
indexOfCP = tempPath.firstIndex(of: "CircuitPython 8.x")!
tempPath.removeSubrange(0...indexOfCP)
print("removeSubrange temp path - \(tempPath)")
tempArray.removeFirst()
var joinedArrayPath = tempPath.joined(separator: "/")
filesReadyForTransfer.append(URL(string: joinedArrayPath)!)
filePathMod(listOf: tempArray)
}
if tempPath.contains(projectName ?? "unknown") {
print("\(#function) @Line: \(#line)")
indexOfCP = tempPath.firstIndex(of: projectName!)!
tempPath.removeSubrange(0...indexOfCP+1)
print("removeSubrange temp path - \(tempPath)")
tempArray.removeFirst()
var joinedArrayPath = tempPath.joined(separator: "/")
filesReadyForTransfer.append(URL(string: joinedArrayPath)!)
filePathMod(listOf: tempArray)
}
}
}
func completedTransfer() { func completedTransfer() {
DispatchQueue.main.async { DispatchQueue.main.async {
self.downloadState = .complete self.downloadState = .complete
self.didCompleteTranfer = true self.didCompleteTranfer = true
@ -825,185 +582,6 @@ class BleContentTransfer: ObservableObject {
} }
} }
func transferFiles(files: [URL]) {
print(#function)
var copiedFiles = files
print("Number of files in filesArray \(files.count)")
print(files)
if files.isEmpty {
print("Array of contents empty - Check other directories")
self.completedTransfer()
DispatchQueue.main.asyncAfter(deadline: .now() + 2){
self.sendingBundle = false
self.completedTransfer()
self.numOfFiles = 0
self.contentList.removeAll()
}
} else {
guard let selectedUrl = files.first else {
print("No such file exist here")
return
}
guard let data = try? Data(contentsOf: URL(fileURLWithPath: selectedUrl.deletingPathExtension().lastPathComponent, relativeTo: selectedUrl).appendingPathExtension(selectedUrl.pathExtension)) else {
print("File not found")
return
}
if selectedUrl.deletingLastPathComponent().lastPathComponent == "CircuitPython 7.x"{
print("Selected Path: \(selectedUrl.path)")
var tempURL = selectedUrl.pathComponents
tempURL.removeFirst(12)
let joined = tempURL.joined(separator: "/")
var newModPath = tempURL
newModPath.removeLast()
print("Test file path: \(tempURL)")
print("File transfer modified path xx: \(joined)")
self.contentCommands.writeFileCommand(path: joined, data: data) { result in
switch result {
case .success(_):
copiedFiles.removeFirst()
self.transferFiles(files: copiedFiles)
case .failure(_):
DispatchQueue.main.async {
print("Transfer Failure")
print("\(joined)")
self.downloadState = .failed
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
self.downloadState = .idle
}
}
NotificationCenter.default.post(name: .didEncounterTransferError, object: nil, userInfo: nil)
}
}
}
else if selectedUrl.deletingLastPathComponent().lastPathComponent == "lib" {
var tempURL = selectedUrl.pathComponents
tempURL.removeFirst(12)
let joined = tempURL.joined(separator: "/")
print("File transfer modified path 11:\(joined)")
print("Updated Path:\(joined)")
contentCommands.writeFileCommand(path: joined, data: data) { result in
switch result {
case .success(_):
copiedFiles.removeFirst()
self.transferFiles(files: copiedFiles)
case .failure(_):
print("Transfer Failure - 2")
self.downloadState = .failed
NotificationCenter.default.post(name: .didEncounterTransferError, object: nil, userInfo: nil)
}
}
} else {
if selectedUrl.lastPathComponent == "README.txt" {
print("Got one")
copiedFiles.removeFirst()
self.transferFiles(files: copiedFiles)
} else {
var tempURL = selectedUrl.pathComponents
tempURL.removeFirst(12)
let joined = tempURL.joined(separator: "/")
print("File transfer modified path: \(joined)")
print("Updated Path:\(joined)")
contentCommands.writeFileCommand(path: joined, data: data) { result in
switch result {
case .success(_):
copiedFiles.removeFirst()
self.transferFiles(files: copiedFiles)
case .failure(let error):
print("Failed: \(error): \(result)")
NotificationCenter.default.post(name: .didEncounterTransferError, object: nil, userInfo: nil)
}
}
}
}
}
DispatchQueue.main.async {
self.sendingBundle = true
}
}
func readMyStatus() {
///model.readFile(filename: "boot_out.txt")
print(#function)
print("BOOT INFO: \(bootUpInfo)")
switch bootUpInfo.description {
case let str where str.contains("CircuitPython 7"):
print("CircuitPython 7")
circuitPythonVersion = "CircuitPython 7"
case let str where str.contains("CircuitPython 8"):
print("CircuitPython 8")
circuitPythonVersion = "CircuitPython 8"
default:
print("Unknown Device")
}
}
// MARK: System // MARK: System
struct TransmissionProgress { struct TransmissionProgress {
@ -1047,7 +625,6 @@ class BleContentTransfer: ObservableObject {
@Published var lastTransmit: TransmissionLog? = TransmissionLog(type: .write(size: 334)) @Published var lastTransmit: TransmissionLog? = TransmissionLog(type: .write(size: 334))
@Published var activeAlert: ActiveAlert?
// Data // Data
private let bleManager = BleManager.shared private let bleManager = BleManager.shared
@ -1088,8 +665,7 @@ class BleContentTransfer: ObservableObject {
let str = String(decoding: data, as: UTF8.self) let str = String(decoding: data, as: UTF8.self)
print("\(#function) @Line: \(#line)") print("\(#function) @Line: \(#line)")
print("Read: \(str)") print("Read: \(str)")
//self.readMyStatus()
self.bootUpInfo = str

View file

@ -12,28 +12,36 @@ class ExpandedBLECellState: ObservableObject {
@Published var currentCell = "" @Published var currentCell = ""
} }
class Board: Equatable {
static func == (lhs: Board, rhs: Board) -> Bool {
return lhs.name == rhs.name && lhs.versionNumber == rhs.versionNumber
}
static let shared = Board(name: "Unrecognized Board", versionNumber: "8")
var name: String
var versionNumber: String
private init(name: String, versionNumber: String) {
self.name = name
self.versionNumber = versionNumber
}
}
struct BleModuleView: View { struct BleModuleView: View {
// Data @State var boardInfoForView: Board?
enum ActiveAlert: Identifiable {
case confirmUnpair(blePeripheral: BlePeripheral)
var id: Int {
switch self {
case .confirmUnpair: return 1
}
}
}
@Environment(\.presentationMode) var presentationMode @Environment(\.presentationMode) var presentationMode
@EnvironmentObject var expandedState : ExpandedBLECellState @EnvironmentObject var expandedState : ExpandedBLECellState
@ObservedObject var connectionManager = FileTransferConnectionManager.shared @ObservedObject var connectionManager = FileTransferConnectionManager.shared
@State var unknownBoardName: String?
let selectedPeripheral = FileTransferConnectionManager.shared.selectedPeripheral let selectedPeripheral = FileTransferConnectionManager.shared.selectedPeripheral
@StateObject var viewModel = BleModuleViewModel() @StateObject var vm = BleModuleViewModel()
@EnvironmentObject var rootViewModel: RootViewModel @EnvironmentObject var rootViewModel: RootViewModel
@ -44,7 +52,6 @@ struct BleModuleView: View {
@State var isExpanded = true @State var isExpanded = true
@State private var scrollViewID = UUID() @State private var scrollViewID = UUID()
@State private var activeAlert: ActiveAlert?
@State private var boardBootInfo = "" @State private var boardBootInfo = ""
@State private var inConnectedInSelectionView = true @State private var inConnectedInSelectionView = true
@ -118,12 +125,7 @@ struct BleModuleView: View {
.lineLimit(2) .lineLimit(2)
} else { } else if boardBootInfo == "clue_nrf52840_express" {
}
if boardBootInfo == "clue_nrf52840_express" {
Image("clue") Image("clue")
.resizable() .resizable()
.aspectRatio(contentMode: .fit) .aspectRatio(contentMode: .fit)
@ -138,6 +140,20 @@ struct BleModuleView: View {
.lineLimit(2) .lineLimit(2)
.padding(.horizontal, 20) .padding(.horizontal, 20)
} else {
Image("Placeholder Board Image")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 200, height: 200)
.padding(.horizontal, 60)
.fixedSize(horizontal: false, vertical: true)
Text(unknownBoardName ?? "")
.font(Font.custom("ReadexPro-Regular", size: 30))
.minimumScaleFactor(0.01)
.multilineTextAlignment(.center)
.lineLimit(2)
.padding(.horizontal, 20)
} }
@ -173,86 +189,11 @@ struct BleModuleView: View {
VStack { VStack {
BleBannerView(deviceName: boardInfoForView?.name ?? "Unknown Device", disconnectAction: {
if boardBootInfo == "circuitplayground_bluefruit" {
HStack {
Image("bluetoothLogo")
.resizable()
.scaledToFit()
.frame(width: 16, height: 16)
Text("Circuit Playground Bluefruit.")
.font(Font.custom("ReadexPro-Regular", size: 14))
.minimumScaleFactor(0.1)
Button {
showConfirmationPrompt() showConfirmationPrompt()
} label: {
Text("Disconnect")
.font(Font.custom("ReadexPro-Bold", size: 14))
.underline()
.minimumScaleFactor(0.1)
}
}
}
if boardBootInfo == "clue_nrf52840_express" {
VStack {
HStack {
Image("bluetoothLogo")
.resizable()
.scaledToFit()
.frame(width: 16, height: 16)
Text("Adafruit CLUE.")
.font(Font.custom("ReadexPro-Regular", size: 14))
Button {
showConfirmationPrompt()
} label: {
Text("Disconnect")
.font(Font.custom("ReadexPro-Bold", size: 14))
.underline()
//.minimumScaleFactor(0.1)
}
}
// Expandable
VStack {
Text("More Info")
}
.background(GeometryReader {
Color.clear.preference(key: ViewHeightKey.self,
value: $0.frame(in: .local).size.height)
}) })
}
.onPreferenceChange(ViewHeightKey.self) { subviewHeight = $0 }
.frame(height: isExpanded ? subviewHeight : 50, alignment: .top)
.clipped()
.frame(maxWidth: .infinity)
.transition(.move(edge: .bottom))
.onTapGesture {
withAnimation(.easeIn(duration: 0.5)) {
isExpanded.toggle()
}
}
}
} }
.padding(.all, 0.0) .padding(.all, 0.0)
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
@ -270,14 +211,32 @@ struct BleModuleView: View {
MainSubHeaderView(device: "Adafruit CLUE") MainSubHeaderView(device: "Adafruit CLUE")
} }
if boardBootInfo == "circuitplayground_bluefruit" { else if boardInfoForView?.name == "Circuitplayground Bluefruit" {
MainSubHeaderView(device: "Circuit Playground") MainSubHeaderView(device: "Circuit Playground")
} }
else {
MainSubHeaderView(device: unknownBoardName ?? "device")
}
let check = vm.pdemos.filter {
if boardInfoForView?.name == "Circuitplayground Bluefruit" {
let cpbProjects = $0.compatibility.contains("circuitplayground_bluefruit")
print("Returned \(cpbProjects) for circuitplayground_bluefruit")
return cpbProjects
}
else if boardInfoForView?.name == "Clue Nrf52840 Express" {
let clueProjects = $0.compatibility.contains("clue_nrf52840_express")
return clueProjects
}
else {
return true
}
let check = viewModel.pdemos.filter {
$0.compatibility.contains(boardBootInfo)
} }
@ -311,6 +270,9 @@ struct BleModuleView: View {
.id(self.scrollViewID) .id(self.scrollViewID)
} }
.environmentObject(expandedState) .environmentObject(expandedState)
.refreshable {
vm.fetchAndLoadProjectsFromStorage()
}
} }
} }
@ -318,49 +280,36 @@ struct BleModuleView: View {
.background(Color.white) .background(Color.white)
.onChange(of: viewModel.bootUpInfo, perform: { newValue in .onChange(of: vm.bootUpInfo, perform: { newValue in
viewModel.readMyStatus() vm.readMyStatus()
print("newValue \(newValue)") print("newValue \(newValue)")
boardBootInfo = newValue boardBootInfo = newValue
}) })
.onChange(of: connectionManager.selectedClient) { selectedClient in .onChange(of: connectionManager.selectedClient) { selectedClient in
viewModel.setup(fileTransferClient: selectedClient) vm.setup(fileTransferClient: selectedClient)
} }
.onAppear(){ .onChange(of: vm.connectedBoard, perform: { newValue in
print("Opened BleModuleView") dump(newValue)
// networkServiceModel.fetch() boardInfoForView = newValue
unknownBoardName = newValue?.name
})
viewModel.setup(fileTransferClient:connectionManager.selectedClient) .onAppear(){
vm.setup(fileTransferClient:connectionManager.selectedClient)
connectionManager.isSelectedPeripheralReconnecting = true connectionManager.isSelectedPeripheralReconnecting = true
viewModel.readFile(filename: "boot_out.txt") vm.readFile(filename: "boot_out.txt")
}
print("x\(boardBootInfo)")
} }
struct Alerts: ViewModifier {
@Binding var activeAlert: ActiveAlert?
func body(content: Content) -> some View {
content
.alert(item: $activeAlert, content: { alert in
switch alert {
case .confirmUnpair(let blePeripheral):
return Alert(
title: Text("Confirm disconnect \(blePeripheral.name ?? "")"),
message: nil,
primaryButton: .destructive(Text("Disconnect")) {
//BleAutoReconnect.clearAutoconnectPeripheral()
BleManager.shared.disconnect(from: blePeripheral)
},
secondaryButton: .cancel(Text("Cancel")) {})
}
})
}
} }
} }

View file

@ -11,19 +11,46 @@ import FileTransferClient
class BleModuleViewModel: ObservableObject { class BleModuleViewModel: ObservableObject {
weak var delegate: BoardInfoDelegate?
@Published var boardInfoForView: Board? {
didSet {
print("Changed")
delegate?.boardInfoDidUpdate(to: boardInfoForView)
}
}
private weak var fileTransferClient: FileTransferClient? private weak var fileTransferClient: FileTransferClient?
@StateObject var contentTransfer = BleContentTransfer() @State var contentTransfer = BleContentTransfer()
@Published var entries = [BlePeripheral.DirectoryEntry]() @Published var entries = [BlePeripheral.DirectoryEntry]()
@Published var isTransmiting = false @Published var isTransmiting = false
@Published var bootUpInfo = "" @Published var bootUpInfo = ""
let dataStore = DataStore()
@Published var pdemos : [ResultItem] = [] var boardDataProvider = BoardDataProvider()
var connectedBoard: Board?
let dataStore = DataStore()
@ObservedObject var networkModel = NetworkService()
@Published var pdemos : [PyProject] = []
func loadProjectsFromStorage() {
self.pdemos = self.dataStore.loadDefaultList()
}
func fetchAndLoadProjectsFromStorage() {
self.networkModel.fetch {
self.pdemos = self.dataStore.loadDefaultList()
}
}
init() { init() {
pdemos = dataStore.loadDefaultList() loadProjectsFromStorage()
self.delegate = contentTransfer
} }
@ -71,7 +98,6 @@ class BleModuleViewModel: ObservableObject {
@Published var transmissionProgress: TransmissionProgress? @Published var transmissionProgress: TransmissionProgress?
@Published var lastTransmit: TransmissionLog? = TransmissionLog(type: .write(size: 334)) @Published var lastTransmit: TransmissionLog? = TransmissionLog(type: .write(size: 334))
@Published var activeAlert: ActiveAlert?
// Data // Data
private let bleManager = BleManager.shared private let bleManager = BleManager.shared
@ -121,6 +147,28 @@ class BleModuleViewModel: ObservableObject {
} }
func setupBoardInfoForDisplay(_ boardInfo: String?) -> Board {
// First, safely unwrap the optional 'boardInfo' string
guard let info = boardInfo else {
print("boardInfo is nil")
return Board.shared
}
let boardID = boardDataProvider.getBoardID(from: info) ?? "Unrecognized Board"
// Board default version is set to 8
let boardVersion = boardDataProvider.getCircuitPythonMajorVersion(from: info) ?? "8"
Board.shared.name = boardID
Board.shared.versionNumber = boardVersion
// Create a new Board object with the acquired name and version
let board = Board.shared
return board
}
// MARK: - Actions // MARK: - Actions
func readFile(filename: String) { func readFile(filename: String) {
@ -135,8 +183,10 @@ class BleModuleViewModel: ObservableObject {
let str = String(decoding: data, as: UTF8.self) let str = String(decoding: data, as: UTF8.self)
print("Read: \(str)") print("Read: \(str)")
self.bootUpInfo = str
sharedBootinfo = str self.connectedBoard = self.setupBoardInfoForDisplay(str)
self.boardInfoForView = self.connectedBoard
case .failure(let error): case .failure(let error):
self.lastTransmit = TransmissionLog(type: .error(message: error.localizedDescription)) self.lastTransmit = TransmissionLog(type: .error(message: error.localizedDescription))
@ -344,18 +394,4 @@ class BleModuleViewModel: ObservableObject {
completion?(result) completion?(result)
} }
} }
}
public var sharedBootinfo = ""
enum ActiveAlert: Identifiable {
case error(error: Error)
var id: Int {
switch self {
case .error: return 1
}
}
} }

View file

@ -10,13 +10,13 @@ import FileTransferClient
struct DemoSubview: View { struct DemoSubview: View {
@Binding var bindingString: String let result: PyProject
let result: ResultItem
@EnvironmentObject var rootViewModel: RootViewModel @EnvironmentObject var rootViewModel: RootViewModel
@StateObject var viewModel = SubCellViewModel() @StateObject var viewModel = SubCellViewModel()
@StateObject var contentTransfer = BleContentTransfer() //@StateObject var contentTransfer = BleContentTransfer()
@StateObject var contentTransfer = BleContentTransfer.shared
@ObservedObject var connectionManager = FileTransferConnectionManager.shared @ObservedObject var connectionManager = FileTransferConnectionManager.shared
@ -150,15 +150,6 @@ Try again later
if isConnected { if isConnected {
if result.compatibility.contains(bindingString) {
// Button {
// viewModel.deleteStoredFilesInFM()
// } label: {
// Text("Delete File Manager Contents")
// .bold()
// .padding(12)
// }
if contentTransfer.downloadState == .idle { if contentTransfer.downloadState == .idle {
@ -210,7 +201,7 @@ Try again later
CompleteButton() CompleteButton()
.padding(.top, 20) .padding(.top, 20)
} }
}
} else { } else {
@ -231,8 +222,6 @@ Try again later
print("On Appear") print("On Appear")
contentTransfer.contentCommands.setup(fileTransferClient: connectionManager.selectedClient) contentTransfer.contentCommands.setup(fileTransferClient: connectionManager.selectedClient)
// viewModel.readFile(filename: "boot_out.txt")
} }
.onChange(of: contentTransfer.transferError, perform: { newValue in .onChange(of: contentTransfer.transferError, perform: { newValue in
@ -247,14 +236,8 @@ Try again later
} }
}) })
// .onChange(of: connectionManager.selectedClient) { selectedClient in
// viewModel.setup(fileTransferClient: selectedClient)
// }
.onAppear(perform: { .onAppear(perform: {
contentTransfer.readMyStatus()
viewModel.searchPathForProject(nameOf: result.projectName) viewModel.searchPathForProject(nameOf: result.projectName)
if viewModel.projectDownloaded { if viewModel.projectDownloaded {

View file

@ -11,7 +11,7 @@ struct DemoViewCell: View {
@EnvironmentObject var expandedState : ExpandedBLECellState @EnvironmentObject var expandedState : ExpandedBLECellState
let result : ResultItem let result : PyProject
@State var isExpanded: Bool = false { @State var isExpanded: Bool = false {
didSet { didSet {
@ -36,7 +36,7 @@ struct DemoViewCell: View {
if isExpanded { if isExpanded {
Group { Group {
DemoSubview(bindingString: $deviceInfo, result: result, isConnected: $isConnected) DemoSubview(result: result, isConnected: $isConnected)
} }
} }

View file

@ -54,8 +54,7 @@ struct RootView: View {
case .selection: case .selection:
SelectionView() SelectionView()
case .wifiSelection:
WifiSelection()
case .wifiPairingTutorial: case .wifiPairingTutorial:
WifiPairingView() WifiPairingView()

View file

@ -47,7 +47,7 @@ public class RootViewModel: ObservableObject {
} }
func goToWiFiSelection() { func goToWiFiSelection() {
destination = .wifiSelection destination = .wifiServiceSelection
} }
func goToWifiView() { func goToWifiView() {

View file

@ -16,8 +16,6 @@ struct WifiHeaderView: View {
VStack { VStack {
HStack (alignment: .center, spacing: 0) { HStack (alignment: .center, spacing: 0) {
Image(systemName: "gearshape") Image(systemName: "gearshape")

View file

@ -0,0 +1,143 @@
//
// MainSelectionView.swift
// PyLeap
//
// Created by Trevor Beaton on 10/16/21.
//
import SwiftUI
import FileTransferClient
enum AdafruitDevices {
case clue_nrf52840_express
case circuitplayground_bluefruit
case esp32s2
}
struct MainSelectionView: View {
@State private var showWebViewPopover: Bool = false
@State private var inConnectedInSelectionView = false
@State private var boardBootInfo = ""
@EnvironmentObject var expandedState : ExpandedBLECellState
@ObservedObject var vm = MainSelectionViewModel()
@State private var isConnected = false
@State private var test = ""
@State private var nilBinder = DownloadState.idle
@EnvironmentObject var rootViewModel: RootViewModel
@AppStorage("shouldShowOnboarding") var shouldShowOnboarding: Bool = true
var body: some View {
VStack(alignment: .center, spacing: 0) {
MainHeaderView()
HStack(alignment: .center, spacing: 8, content: {
Text("Not Connected to a Device.")
.font(Font.custom("ReadexPro-Regular", size: 16))
Button {
rootViewModel.goToSelection()
} label: {
Text("Connect Now")
.font(Font.custom("ReadexPro-Regular", size: 16))
.underline()
}
})
.padding(.all, 0.0)
.frame(maxWidth: .infinity)
.frame(maxHeight: 40)
.background(Color("pyleap_burg"))
.foregroundColor(.white)
ScrollView {
MainSubHeaderView(device: "Adafruit device")
if vm.pdemos.isEmpty {
HStack{
Spacer()
ProgressView()
.scaleEffect(2)
Spacer()
}
.padding(0)
}
ScrollViewReader { scroll in
ForEach(vm.pdemos) { demo in
if demo.bundleLink == expandedState.currentCell {
DemoViewCell(result: demo, isExpanded: true, isConnected: $inConnectedInSelectionView, deviceInfo: $boardBootInfo, onViewGeometryChanged: {
})
.onAppear(){
print("Cell Appeared")
withAnimation {
scroll.scrollTo(demo.id)
}
}
} else {
DemoViewCell(result: demo, isExpanded: false, isConnected: $inConnectedInSelectionView, deviceInfo: $boardBootInfo, onViewGeometryChanged: {
})
}
}
}
}
.refreshable {
vm.pdemos = []
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
vm.networkModel.fetch {
vm.loadProjectsFromStorage()
}
}
}
}
.onAppear() {
print("Opened MainSelectionView")
}
.fullScreenCover(isPresented: $shouldShowOnboarding, content: {
ExampleView(shouldShowOnboarding: $shouldShowOnboarding)
})
.preferredColorScheme(.light)
.background(Color.white)
.navigationBarColor(UIColor(named: "pyleap_gray"))
.navigationBarTitleDisplayMode(.inline)
}
}

View file

@ -57,32 +57,43 @@ class MainSelectionViewModel: ObservableObject {
let dataStore = DataStore() let dataStore = DataStore()
@Published var pdemos : [ResultItem] = [] @Published var pdemos: [PyProject] = []
var networkMonitorCancellable: AnyCancellable? var networkMonitorCancellable: AnyCancellable?
init() { init() {
let fileURL = documentsDirectory.appendingPathComponent("StandardPyLeapProjects.json") startUp()
}
networkMonitorCancellable = networkMonitor.$isConnected.sink { isConnected in func loadProjectsFromStorage() {
if isConnected { self.pdemos = self.dataStore.loadDefaultList()
print("The device is currently connected to the internet.") }
// Perform some action when the device is connected to the internet.
func fetchAndLoadProjectsFromStorage() {
self.networkModel.fetch { self.networkModel.fetch {
self.pdemos = self.dataStore.loadDefaultList() self.pdemos = self.dataStore.loadDefaultList()
} }
}
func startUp(){
networkMonitorCancellable = networkMonitor.$isConnected.sink { isConnected in
if isConnected {
// Perform some action when the device is connected to the internet.
self.fetchAndLoadProjectsFromStorage()
print("The device is currently connected to the internet.")
} else { } else {
print("The device is not currently connected to the internet.") print("The device is not currently connected to the internet.")
// Perform some action when the device is not connected to the internet. // Perform some action when the device is not connected to the internet.
print("Loading cached remote data.") print("Loading cached remote data.")
self.pdemos = self.dataStore.loadDefaultList() self.loadProjectsFromStorage()
} }
} }
} }
} }

View file

@ -54,7 +54,7 @@ struct SelectionView: View {
Button { Button {
rootViewModel.goToWiFiSelection() rootViewModel.goToWiFiSelection()
} label: { } label: {
Text("Wifi") Text("WiFi")
.font(Font.custom("ReadexPro-Regular", size: 25)) .font(Font.custom("ReadexPro-Regular", size: 25))
.foregroundColor(Color.white) .foregroundColor(Color.white)
.frame(width: 270, height: 50, alignment: .center) .frame(width: 270, height: 50, alignment: .center)
@ -110,6 +110,9 @@ struct SelectionView: View {
} }
.padding(.bottom, 60) .padding(.bottom, 60)
.onAppear() {
print("In Selection view")
}
} }
} }

View file

@ -19,7 +19,7 @@ struct WifiCell: View {
@EnvironmentObject var expandedState : ExpandedState @EnvironmentObject var expandedState : ExpandedState
let result : ResultItem let result : PyProject
@State var isExpanded: Bool = false { @State var isExpanded: Bool = false {
didSet { didSet {

View file

@ -15,7 +15,7 @@ struct WifiSubViewCell: View {
@StateObject var wifiFileTransfer = WifiFileTransfer() @StateObject var wifiFileTransfer = WifiFileTransfer()
@StateObject var wifiTransferService = WifiTransferService() @StateObject var wifiTransferService = WifiTransferService()
let result : ResultItem let result : PyProject
@Binding var bindingString: String @Binding var bindingString: String
@ -85,7 +85,7 @@ Remove device from USB. Press "Reset" on the device.
func testOperation() { func startOperationQueue() {
let operationQueue = OperationQueue() let operationQueue = OperationQueue()
let operation1 = BlockOperation { let operation1 = BlockOperation {
@ -158,48 +158,17 @@ Remove device from USB. Press "Reset" on the device.
.font(Font.custom("ReadexPro-Bold", size: 18)) .font(Font.custom("ReadexPro-Bold", size: 18))
.padding(.top, 5) .padding(.top, 5)
ForEach(result.compatibility, id: \.self) { device in
HStack { HStack {
Image(systemName: "checkmark") Image(systemName: "checkmark")
.resizable() .resizable()
.frame(width: 25, height: 22, alignment: .center) .frame(width: 25, height: 22, alignment: .center)
.foregroundColor(.green) .foregroundColor(.green)
Text("ESP32-S2") Text(formatDeviceName(device))
.font(Font.custom("ReadexPro-Regular", size: 18)) .font(Font.custom("ReadexPro-Regular", size: 18))
.foregroundColor(.black) .foregroundColor(.black)
} }
.padding(.top, 10) .padding(.top, 10)
ForEach(result.compatibility, id: \.self) { string in
if string == "circuitplayground_bluefruit" {
HStack {
Image(systemName: "checkmark")
.resizable()
.frame(width: 25, height: 22, alignment: .center)
.foregroundColor(.green)
Text("Circuit Playground Bluefruit")
.font(Font.custom("ReadexPro-Regular", size: 18))
.foregroundColor(.black)
}
.padding(.top, 10)
}
if string == "clue_nrf52840_express" {
HStack {
Image(systemName: "checkmark")
.resizable()
.frame(width: 25, height: 22, alignment: .center)
.foregroundColor(.green)
Text("Adafruit CLUE")
.font(Font.custom("ReadexPro-Regular", size: 18))
.foregroundColor(.black)
}
.padding(.top, 10)
}
} }
}) })
.ignoresSafeArea(.all) .ignoresSafeArea(.all)
@ -219,18 +188,15 @@ Remove device from USB. Press "Reset" on the device.
if isConnected {
if result.compatibility.contains(bindingString) {
if wifiFileTransfer.testIndex.downloadState == .idle { if wifiFileTransfer.testIndex.downloadState == .idle {
Button { Button {
// NotificationCenter.default.post(name: .didCompleteZip, object: nil, userInfo: projectResponse)
testOperation() startOperationQueue()
} label: { } label: {
RunItButton() RunItButton()
@ -274,18 +240,6 @@ Remove device from USB. Press "Reset" on the device.
} }
} else {
Button {
rootViewModel.goTobluetoothPairing()
} label: {
ConnectButton()
.padding(.top, 20)
}
}
}
Spacer() Spacer()
.frame(height: 30) .frame(height: 30)
.ignoresSafeArea(.all) .ignoresSafeArea(.all)

View file

@ -11,7 +11,7 @@ import Combine
struct WifiView: View { struct WifiView: View {
@StateObject var viewModel = WifiViewModel() @StateObject var vm = WifiViewModel()
private let kPrefix = Bundle.main.bundleIdentifier! private let kPrefix = Bundle.main.bundleIdentifier!
// User Defaults // User Defaults
@ -38,12 +38,12 @@ struct WifiView: View {
@State private var showPopover: Bool = false @State private var showPopover: Bool = false
func toggleViewModelIP() { func toggleViewModelIP() {
viewModel.isInvalidIP.toggle() vm.isInvalidIP.toggle()
} }
func scanNetworkWifi() { func scanNetworkWifi() {
viewModel.wifiServiceManager.findService() vm.wifiServiceManager.findService()
} }
func printArray(array: [Any]) { func printArray(array: [Any]) {
@ -59,9 +59,9 @@ struct WifiView: View {
} else { } else {
hostName = userDefaults.object(forKey: kPrefix+".storeResolvedAddress.hostName") as! String hostName = userDefaults.object(forKey: kPrefix+".storeResolvedAddress.hostName") as! String
viewModel.ipAddressStored = true vm.ipAddressStored = true
print("storeResolvedAddress - is stored") print("storeResolvedAddress - is stored")
viewModel.connectionStatus = .connected vm.connectionStatus = .connected
} }
} }
@ -71,28 +71,40 @@ struct WifiView: View {
hintText: "IP Address...", hintText: "IP Address...",
primaryTitle: "Done", primaryTitle: "Done",
secondaryTitle: "Cancel") { text in secondaryTitle: "Cancel") { text in
viewModel.checkServices(ip: text) vm.checkServices(ip: text)
} secondaryAction: { } secondaryAction: {
print("Cancel") print("Cancel")
} }
} }
func showConfirmationPrompt() {
comfirmationAlertMessage(title: "Are you sure you want to disconnect?", exitTitle: "Cancel", primaryTitle: "Disconnect") {
rootViewModel.goToSelection()
} cancel: {
}
}
func showAlertMessage() { func showAlertMessage() {
alertMessage(title: "IP address Not Found", exitTitle: "Ok") { alertMessage(title: "IP address Not Found", exitTitle: "Ok") {
showValidationPrompt() showValidationPrompt()
} }
} }
@State var boardInfoForView = Board.shared
var body: some View { var body: some View {
VStack(spacing: 0) { VStack(spacing: 0) {
WifiHeaderView() WifiHeaderView()
Group{ Group{
switch viewModel.connectionStatus { switch vm.connectionStatus {
case .connected: case .connected:
WifiStatusConnectedView(hostName: $hostName) WifiStatusConnectedView(hostName: $hostName, disconnectAction: {
showConfirmationPrompt()
})
case .noConnection: case .noConnection:
WifiStatusNoConnectionView() WifiStatusNoConnectionView()
case .connecting: case .connecting:
@ -100,16 +112,14 @@ struct WifiView: View {
} }
} }
ScrollView(.vertical, showsIndicators: false) { ScrollView(.vertical, showsIndicators: false) {
ScrollViewReader { scroll in ScrollViewReader { scroll in
SubHeaderView() SubHeaderView()
let check = vm.pdemos.filter {
let check = viewModel.pdemos.filter { $0.wifiCompatible
$0.compatibility.contains(boardBootInfo)
} }
ForEach(check) { demo in ForEach(check) { demo in
@ -117,48 +127,43 @@ struct WifiView: View {
if demo.bundleLink == test.currentCell { if demo.bundleLink == test.currentCell {
WifiCell(result: demo,isExpanded: trueTog, isConnected: $inConnectedInWifiView, bootOne: $boardBootInfo, stateBinder: $downloadState, onViewGeometryChanged: { WifiCell(result: demo,isExpanded: trueTog, isConnected: $inConnectedInWifiView, bootOne: $boardBootInfo, stateBinder: $downloadState, onViewGeometryChanged: {
}) })
.onAppear(){ .onAppear(){
withAnimation { withAnimation {
scroll.scrollTo(demo.id) scroll.scrollTo(demo.id)
} }
} }
} else { } else {
WifiCell(result: demo, isExpanded: falseTog, isConnected: $inConnectedInWifiView, bootOne: $boardBootInfo, stateBinder: $downloadState, onViewGeometryChanged: { WifiCell(result: demo, isExpanded: falseTog, isConnected: $inConnectedInWifiView, bootOne: $boardBootInfo, stateBinder: $downloadState, onViewGeometryChanged: {
withAnimation { withAnimation {
// scroll.scrollTo(demo.id)
} }
}) })
} }
} }
} }
.id(self.scrollViewID) .id(self.scrollViewID)
} }
.foregroundColor(.black) .foregroundColor(.black)
.environmentObject(test) .environmentObject(test)
.refreshable {
vm.fetchAndLoadProjectsFromStorage()
}
} }
.onChange(of: viewModel.connectionStatus, perform: { newValue in .onChange(of: vm.connectionStatus, perform: { newValue in
if newValue == .connected { if newValue == .connected {
hostName = userDefaults.object(forKey: kPrefix+".storeResolvedAddress.hostName") as! String hostName = userDefaults.object(forKey: kPrefix+".storeResolvedAddress.hostName") as! String
} }
}) })
.onChange(of: viewModel.wifiServiceManager.resolvedServices, perform: { newValue in .onChange(of: vm.wifiServiceManager.resolvedServices, perform: { newValue in
print("Credential Check!") print("Credential Check!")
print(newValue) print(newValue)
@ -173,7 +178,7 @@ struct WifiView: View {
}) })
.onChange(of: viewModel.isInvalidIP, perform: { newValue in .onChange(of: vm.isInvalidIP, perform: { newValue in
print("viewModel.isInvalidIP .onChange") print("viewModel.isInvalidIP .onChange")
if newValue { if newValue {
showAlertMessage() showAlertMessage()
@ -185,8 +190,8 @@ struct WifiView: View {
.onAppear(){ .onAppear(){
checkForStoredIPAddress() checkForStoredIPAddress()
viewModel.printStoredInfo() vm.printStoredInfo()
viewModel.read() vm.read()
} }
} }

View file

@ -18,51 +18,6 @@ struct TestIndex {
class WifiFileTransfer: ObservableObject { class WifiFileTransfer: ObservableObject {
// func fetchDocuments<T: Sequence>(in sequence: T) where T.Element == Int {
// var documentNumbers = sequence.map { String($0) }
//
// let timer = Timer.scheduledTimer(withTimeInterval: 2, repeats: true) { [weak self] timer in
// guard
// let self = self,
// let documentNumber = documentNumbers.first
// else {
// timer.invalidate()
// return
// }
//
// self.fetchDocument(byNumber: documentNumber)
// documentNumbers.removeLast()
// }
// timer.fire() // if you don't want to wait 2 seconds for the first one to fire, go ahead and fire it manually
// }
func fetchDocumentsq<T: Sequence>(in sequence: T) where T.Element == URL {
print(sequence)
guard let value = sequence.first(where: { _ in true }) else {
print("Complete - fetchDocumentsq")
return
}
// let docNumber = String(value)
// fetchDocument(byNumber: docNumber)
DispatchQueue.main.asyncAfter(deadline: .now() + 2) { [weak self] in
// self?.fetchDocuments(in: sequence.dropFirst())
print(value)
self?.fetchDocumentsq(in: sequence.dropFirst())
}
}
struct TestIndex { struct TestIndex {
var count = 0 var count = 0
var numberOfFiles = 0 var numberOfFiles = 0
@ -74,13 +29,10 @@ class WifiFileTransfer: ObservableObject {
downloadState = .idle downloadState = .idle
} }
} }
var testIndex = TestIndex() var testIndex = TestIndex()
func copy(with zone: NSZone? = nil) -> Any { func copy(with zone: NSZone? = nil) -> Any {
let copy = WifiFileTransfer() let copy = WifiFileTransfer()
copy.counter = counter copy.counter = counter
@ -125,6 +77,7 @@ class WifiFileTransfer: ObservableObject {
func printArray(array: [Any]) { func printArray(array: [Any]) {
print("From print array:")
for i in array { for i in array {
print("\(i)") print("\(i)")
} }
@ -339,34 +292,20 @@ class WifiFileTransfer: ObservableObject {
func filterOutCPDirectories(urls: [URL]) -> [URL] { func filterOutCPDirectories(urls: [URL]) -> [URL] {
// Removes - CircuitPython 8.x directory at the lastPathComponent // Removes - CircuitPython 8.x directory at the lastPathComponent
let removingCP8FromArray = urls.filter { let filteredList = urls.filter {
$0.lastPathComponent != ("CircuitPython 8.x") let lastPathComponent = $0.lastPathComponent
return lastPathComponent != "CircuitPython 8.x"
&& lastPathComponent != "CircuitPython 7.x"
&& lastPathComponent != "CircuitPython_Templates"
} }
// Removes - CircuitPython 7.x directory at the lastPathComponent let listForCurrentCPVersion = filteredList.filter {
let removingCP7FromArray = removingCP8FromArray.filter { $0.absoluteString.contains("CircuitPython%20\(Board.shared.versionNumber).x")
$0.lastPathComponent != ("CircuitPython 7.x")
} }
if WifiCPVersion.versionNumber == 8 { printArray(array: listForCurrentCPVersion)
let listForCurrentCPVersion = removingCP7FromArray.filter {
!$0.absoluteString.contains("CircuitPython%207.x")
}
return listForCurrentCPVersion return listForCurrentCPVersion
}
if WifiCPVersion.versionNumber == 7 {
let listForCurrentCPVersion = removingCP7FromArray.filter {
!$0.absoluteString.contains("CircuitPython%208.x")
}
return listForCurrentCPVersion
}
return removingCP7FromArray
} }
@ -419,8 +358,6 @@ class WifiFileTransfer: ObservableObject {
DispatchQueue.main.async { DispatchQueue.main.async {
self.numOfFiles = tempArray.count self.numOfFiles = tempArray.count
self.makeFile(files: tempArray) self.makeFile(files: tempArray)
// self.testIndex.numberOfFiles = self.numOfFiles
} }
@ -618,7 +555,6 @@ class WifiFileTransfer: ObservableObject {
printArray(array: files) printArray(array: files)
var copiedArray = files var copiedArray = files
// self.fetchDocumentsq(in: files)
DispatchQueue.main.async { DispatchQueue.main.async {
self.counter += 1 self.counter += 1

View file

@ -0,0 +1,99 @@
//
// WifiServiceCellSubView.swift
// PyLeap
//
// Created by Trevor Beaton on 3/29/23.
//
import SwiftUI
import Foundation
struct WifiServiceCellSubView: View {
let resolvedService: ResolvedService
@EnvironmentObject var rootViewModel: RootViewModel
let userDefaults = UserDefaults.standard
private let kPrefix = Bundle.main.bundleIdentifier!
func storeResolvedAddress(service: ResolvedService) {
print("Storing resolved address")
userDefaults.set(service.ipAddress, forKey: kPrefix+".storeResolvedAddress.ipAddress" )
userDefaults.set(service.hostName, forKey: kPrefix+".storeResolvedAddress.hostName" )
userDefaults.set(service.device, forKey: kPrefix+".storeResolvedAddress.device" )
print("Stored UserDefaults")
print(userDefaults.object(forKey: kPrefix+".storeResolvedAddress.ipAddress"))
print(userDefaults.object(forKey: kPrefix+".storeResolvedAddress.hostName"))
print(userDefaults.object(forKey: kPrefix+".storeResolvedAddress.device"))
}
func showConfirmationPrompt(service: ResolvedService, hostName: String) {
comfirmationAlertMessage(title: "Would you like to connect to \(hostName)?", exitTitle: "Cancel", primaryTitle: "Connect") {
storeResolvedAddress(service: service)
rootViewModel.goToWifiView()
} cancel: {
}
}
var body: some View {
VStack {
HStack {
VStack {
Text("Device ID: \(resolvedService.hostName)")
.font(Font.custom("ReadexPro-Regular", size: 18))
.multilineTextAlignment(.leading)
.minimumScaleFactor(0.1)
Text("Device IP: \(resolvedService.ipAddress)")
.font(Font.custom("ReadexPro-Regular", size: 18))
.multilineTextAlignment(.leading)
.minimumScaleFactor(0.1)
}
Spacer()
}
.padding(.horizontal, 30)
.padding(.vertical, 30)
HStack (
alignment: .center,
spacing: 0
) {
Spacer()
Button {
showConfirmationPrompt(service: resolvedService, hostName: resolvedService.hostName)
} label: {
Text("Connect")
.font(Font.custom("ReadexPro-Regular", size: 25))
.foregroundColor(Color.white)
.frame(width: 270, height: 50, alignment: .center)
.background(Color("pyleap_pink"))
.clipShape(Capsule())
.padding(.bottom, 30)
}
Spacer()
}
}
}
}
//struct WifiServiceCellSubView_Previews: PreviewProvider {
// static var previews: some View {
// WifiServiceCellSubView(resolvedService: .con)
// }
//}

View file

@ -0,0 +1,81 @@
//
// WifiServiceCellView.swift
// PyLeap
//
// Created by Trevor Beaton on 11/7/22.
//
import SwiftUI
struct WifiServiceCellView: View {
let resolvedService: ResolvedService
@State private var isExpanded: Bool = false {
didSet {
onViewGeometryChanged()
}
}
let onViewGeometryChanged: ()->Void
var body: some View {
content
.frame(maxWidth: .infinity)
}
private var content: some View {
VStack(alignment: .leading, spacing: 8) {
header
if isExpanded {
Group {
WifiServiceCellSubView(resolvedService: resolvedService)
}
}
}
}
func removeAdafruitString(text: String) -> String {
if text.contains("Adafruit") {
let parsed = text.replacingOccurrences(of: "Adafruit", with: "")
return parsed
} else {
return text
}
}
private var header: some View {
HStack {
Text(removeAdafruitString(text:resolvedService.device))
.font(Font.custom("ReadexPro-Regular", size: 24))
.minimumScaleFactor(0.1)
.lineLimit(1)
// .padding(.horizontal, 30)
.foregroundColor(.white)
Spacer()
Image(systemName: "chevron.down")
.resizable()
.frame(width: 30, height: 15, alignment: .center)
.foregroundColor(.white)
.padding(.trailing, 30)
}
//.padding(.vertical, 5)
.padding(.leading)
.frame(maxWidth: .infinity)
.frame(height: 50)
.background(Color("alt-gray"))
.onTapGesture { isExpanded.toggle() }
}
}

View file

@ -3,7 +3,7 @@
// PyLeap // PyLeap
// //
// Created by Trevor Beaton on 10/24/22. // Created by Trevor Beaton on 10/24/22.
// // Testing
import SwiftUI import SwiftUI

View file

@ -10,18 +10,15 @@ import Foundation
struct WifiStatusConnectedView: View { struct WifiStatusConnectedView: View {
let userDefaults = UserDefaults.standard
private let kPrefix = Bundle.main.bundleIdentifier!
@EnvironmentObject var rootViewModel: RootViewModel
@Binding var hostName: String @Binding var hostName: String
func showConfirmationPrompt() { var disconnectAction: () -> Void
comfirmationAlertMessage(title: "Are you sure you want to disconnect?", exitTitle: "Cancel", primaryTitle: "Disconnect") {
rootViewModel.goToSelection()
} cancel: {
} func removeAdafruitString(from boardName: String) -> String {
let removeString = "Adafruit"
let updatedText = boardName.replacingOccurrences(of: removeString, with: "")
return updatedText
} }
var body: some View { var body: some View {
@ -33,16 +30,13 @@ struct WifiStatusConnectedView: View {
.frame(width: 20, height: 20) .frame(width: 20, height: 20)
.padding(5) .padding(5)
Text("Connected to \(hostName). ") Text("Connected to \(hostName) ")
.font(Font.custom("ReadexPro-Regular", size: 14)) .font(Font.custom("ReadexPro-Regular", size: 14))
Button { Button(action: disconnectAction) {
showConfirmationPrompt()
} label: {
Text("Disconnect") Text("Disconnect")
.font(Font.custom("ReadexPro-Bold", size: 14)) .font(Font.custom("ReadexPro-Bold", size: 14))
.underline() .underline()
.minimumScaleFactor(0.1)
} }
}) })

View file

@ -71,31 +71,6 @@ class WifiSubViewCellModel: ObservableObject {
// if success.contains("GET, OPTIONS, PUT, DELETE, MOVE") {
//
// print("USB not in use.")
// DispatchQueue.main.async {
// self.usbInUse = false
// }
//
// // DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
// // if wifiFileTransfer.projectDownloaded {
// //
// // wifiFileTransfer.testFileExistance(for: result.projectName, bundleLink: result.bundleLink)
// //
// // } else {
// // downloadModel.trueDownload(useProject: result.bundleLink, projectName: result.projectName)
// // }
// // }
//
// } else {
// DispatchQueue.main.async {
// self.usbInUse = true
// }
// print("USB in use - files cannot be tranferred or moved while USB is in use. Show Error")
// }
}) })
} }

View file

@ -173,7 +173,7 @@ class WifiTransferService: ObservableObject {
} }
let base64LoginString = loginData.base64EncodedString() let base64LoginString = loginData.base64EncodedString()
// var request = URLRequest(url: URL(string: "http://cpy-9cbe10.local/fs/")!,timeoutInterval: Double.infinity)
var request = URLRequest(url: URL(string: "http://\(hostName).local/fs/")!,timeoutInterval: Double.infinity) var request = URLRequest(url: URL(string: "http://\(hostName).local/fs/")!,timeoutInterval: Double.infinity)
request.addValue("application/json", forHTTPHeaderField: "Accept") request.addValue("application/json", forHTTPHeaderField: "Accept")
@ -226,7 +226,6 @@ class WifiTransferService: ObservableObject {
let loginData = loginString.data(using: String.Encoding.utf8) let loginData = loginString.data(using: String.Encoding.utf8)
let base64LoginString = loginData!.base64EncodedString() let base64LoginString = loginData!.base64EncodedString()
print("Host Name: \(hostName)") print("Host Name: \(hostName)")

View file

@ -27,6 +27,7 @@ class WifiViewModel: ObservableObject {
@Published var ipInputValidation = false @Published var ipInputValidation = false
//Dependencies //Dependencies
var networkMonitor = NetworkMonitor() var networkMonitor = NetworkMonitor()
var networkAuth = LocalNetworkAuthorization() var networkAuth = LocalNetworkAuthorization()
public var wifiNetworkService = WifiNetworkService() public var wifiNetworkService = WifiNetworkService()
@ -34,7 +35,7 @@ class WifiViewModel: ObservableObject {
@Published var wifiTransferService = WifiTransferService() @Published var wifiTransferService = WifiTransferService()
@Published var wifiServiceManager = WifiServiceManager() @Published var wifiServiceManager = WifiServiceManager()
@ObservedObject var networkModel = NetworkService()
var circuitPythonVersion = Int() var circuitPythonVersion = Int()
@Published var webDirectoryInfo = [WebDirectoryModel]() @Published var webDirectoryInfo = [WebDirectoryModel]()
@ -45,9 +46,19 @@ class WifiViewModel: ObservableObject {
let dataStore = DataStore() let dataStore = DataStore()
@Published var pdemos : [ResultItem] = [] @Published var pdemos : [PyProject] = []
func loadProjectsFromStorage() {
self.pdemos = self.dataStore.loadDefaultList()
}
func fetchAndLoadProjectsFromStorage() {
self.networkModel.fetch {
self.pdemos = self.dataStore.loadDefaultList()
}
}
// File Manager Data // File Manager Data
let directoryPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] let directoryPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
@ -59,11 +70,10 @@ class WifiViewModel: ObservableObject {
var ipAddressStored = false var ipAddressStored = false
init() { init() {
pdemos = dataStore.loadDefaultList() loadProjectsFromStorage()
checkIP() checkIP()
registerNotifications(enabled: true) registerNotifications(enabled: true)
wifiServiceManager.findService() wifiServiceManager.findService()
//read()
} }
/// Makes a network call to populate our project list /// Makes a network call to populate our project list
@ -71,29 +81,39 @@ class WifiViewModel: ObservableObject {
// networkModel.fetch() // networkModel.fetch()
} }
@Published var pyleapProjects = [ResultItem]() @Published var pyleapProjects = [PyProject]()
var boardDataProvider = BoardDataProvider()
// This function reads the boards the boot_out.txt file to then set the current board's name and version number for file transfer.
func setBoardToDefault() {
Board.shared.name = "Unrecognized Board"
Board.shared.versionNumber = "8"
}
func read() { func read() {
setBoardToDefault()
// This method can't be used until the device has permission to communicate. // This method can't be used until the device has permission to communicate.
print("READING CP Vers.") print("READING CP Vers.")
wifiTransferService.getRequest(read: "boot_out.txt") { result in wifiTransferService.getRequest(read: "boot_out.txt") { result in
if result.contains("CircuitPython 7") { let boardID = self.boardDataProvider.getBoardID(from: result) ?? "Unrecognized Board"
WifiCPVersion.versionNumber = 7 // Board default version is set to 8
print("WifiCPVersion.versionNumber set to: \(WifiCPVersion.versionNumber)") let boardVersion = self.boardDataProvider.getCircuitPythonMajorVersion(from: result) ?? "8"
}
if result.contains("CircuitPython 8") { Board.shared.name = boardID
WifiCPVersion.versionNumber = 8 Board.shared.versionNumber = boardVersion
print("WifiCPVersion.versionNumber set to: \(WifiCPVersion.versionNumber)") dump(Board.shared)
}
} }
} }
private weak var invalidIPObserver: NSObjectProtocol? private weak var invalidIPObserver: NSObjectProtocol?
private weak var testObserver: NSObjectProtocol? private weak var testObserver: NSObjectProtocol?
@ -146,11 +166,6 @@ class WifiViewModel: ObservableObject {
connectionStatus = .connected connectionStatus = .connected
} }
// @Published var connectionStatus: ConnectionStatus = AppEnvironment.isRunningTests ? .connected : .noConnection
func printStoredInfo() { func printStoredInfo() {
print("======Stored UserDefaults======") print("======Stored UserDefaults======")

View file

@ -0,0 +1,53 @@
//
// BoardDataProvider.swift
// PyLeap
//
// Created by Trevor Beaton on 5/17/23.
//
import Foundation
final class BoardDataProvider {
func getBoardID(from prompt: String) -> String? {
print("getBoardID function: \(prompt)")
let boardIDPrefix = "Board ID:"
let uidSuffix = "UID"
if let startRange = prompt.range(of: boardIDPrefix) {
let start = startRange.upperBound
var end = prompt.endIndex
if let endRange = prompt.range(of: uidSuffix) {
end = endRange.lowerBound
}
let range = start..<end
let boardID = prompt[range].trimmingCharacters(in: .whitespacesAndNewlines)
let boardIDWithSpaces = boardID.replacingOccurrences(of: "_", with: " ")
let capitalizedBoardID = boardIDWithSpaces.capitalized
print("Outgoing: \(capitalizedBoardID)")
return capitalizedBoardID
}
return nil
}
func getCircuitPythonMajorVersion(from prompt: String) -> String? {
print("From getCircuitPythonMajorVersion function: \(prompt)")
let regexPattern = "CircuitPython (\\d+)"
if let regex = try? NSRegularExpression(pattern: regexPattern) {
let range = NSRange(location: 0, length: prompt.utf16.count)
if let match = regex.firstMatch(in: prompt, options: [], range: range) {
if let range = Range(match.range(at: 1), in: prompt) {
return String(prompt[range])
}
}
}
return nil
}
}

View file

@ -11,7 +11,7 @@ import Foundation
/// This is a DataStore class that is used for saving and loading data to/from the file system using the FileManager class. /// This is a DataStore class that is used for saving and loading data to/from the file system using the FileManager class.
*/ */
public class DataStore { public class DataStore: ObservableObject {
let fileManager = FileManager.default let fileManager = FileManager.default
@ -27,8 +27,8 @@ public class DataStore {
/// This method writes it to a file named "StandardPyLeapProjects.json" in the documents directory. /// This method writes it to a file named "StandardPyLeapProjects.json" in the documents directory.
*/ */
func save(content: [ResultItem], completion: @escaping () -> Void) { func save(content: [PyProject], completion: @escaping () -> Void) {
print(#function)
let encoder = JSONEncoder() let encoder = JSONEncoder()
if let encodedProjectData = try? encoder.encode(content) { if let encodedProjectData = try? encoder.encode(content) {
@ -47,7 +47,7 @@ public class DataStore {
/// This method reads the "StandardPyLeapProjects.json" file in the documents directory and decodes it as an array of ResultItem objects, and then appends the customProjects array to it and saves it back to the file. /// This method reads the "StandardPyLeapProjects.json" file in the documents directory and decodes it as an array of ResultItem objects, and then appends the customProjects array to it and saves it back to the file.
*/ */
func save(customProjects: [ResultItem], completion: @escaping () -> Void) { func save(customProjects: [PyProject], completion: @escaping () -> Void) {
var temp = customProjects var temp = customProjects
@ -55,7 +55,7 @@ public class DataStore {
let savedData = try? Data(contentsOf: fileURL) let savedData = try? Data(contentsOf: fileURL)
if let savedData = savedData, if let savedData = savedData,
let savedProjects = try? JSONDecoder().decode([ResultItem].self, from: savedData) { let savedProjects = try? JSONDecoder().decode([PyProject].self, from: savedData) {
NotificationCenter.default.post(name: .didCollectCustomProject, object: nil, userInfo: nil) NotificationCenter.default.post(name: .didCollectCustomProject, object: nil, userInfo: nil)
temp.append(contentsOf: savedProjects) temp.append(contentsOf: savedProjects)
@ -76,24 +76,24 @@ public class DataStore {
let savedData = try? Data(contentsOf: fileURL) let savedData = try? Data(contentsOf: fileURL)
if let savedData = savedData, if let savedData = savedData,
let savedProjects = try? JSONDecoder().decode([ResultItem].self, from: savedData) { let savedProjects = try? JSONDecoder().decode([PyProject].self, from: savedData) {
loadCustomProjectList(contents: savedProjects) loadCustomProjectList(contents: savedProjects)
} }
} }
/** /**
/// : This method reads the "StandardPyLeapProjects.json" file in the documents directory, decodes it as an array of ResultItem objects, and returns it. /// This method reads the "StandardPyLeapProjects.json" file in the documents directory, decodes it as an array of ResultItem objects, and returns it.
*/ */
func loadDefaultList() -> [ResultItem] { func loadDefaultList() -> [PyProject] {
var result = [ResultItem]() var result = [PyProject]()
let fileURL = documentsDirectory.appendingPathComponent("StandardPyLeapProjects.json") let fileURL = documentsDirectory.appendingPathComponent("StandardPyLeapProjects.json")
let savedData = try? Data(contentsOf: fileURL) let savedData = try? Data(contentsOf: fileURL)
if let savedData = savedData, if let savedData = savedData,
let savedProjects = try? JSONDecoder().decode([ResultItem].self, from: savedData) { let savedProjects = try? JSONDecoder().decode([PyProject].self, from: savedData) {
result = savedProjects result = savedProjects
} }
return result return result
@ -107,14 +107,14 @@ public class DataStore {
/// This method reads the "CustomProjects.json" file in the documents directory, decodes it as an array of ResultItem objects, appends it to the input array, and then calls /// This method reads the "CustomProjects.json" file in the documents directory, decodes it as an array of ResultItem objects, appends it to the input array, and then calls
*/ */
func loadCustomProjectList(contents: [ResultItem]) { func loadCustomProjectList(contents: [PyProject]) {
var temp = contents var temp = contents
let fileURL = documentsDirectory.appendingPathComponent("CustomProjects.json") let fileURL = documentsDirectory.appendingPathComponent("CustomProjects.json")
let savedData = try? Data(contentsOf: fileURL) let savedData = try? Data(contentsOf: fileURL)
if let savedData = savedData, if let savedData = savedData,
let savedProjects = try? JSONDecoder().decode([ResultItem].self, from: savedData) { let savedProjects = try? JSONDecoder().decode([PyProject].self, from: savedData) {
temp.append(contentsOf: savedProjects) temp.append(contentsOf: savedProjects)
removeDuplicates(projectList: temp) removeDuplicates(projectList: temp)
@ -129,9 +129,9 @@ public class DataStore {
/// This method uses the reduce(into:_:) method to iterate over the array, and it builds a new array that only contains unique ResultItem objects based on their bundleLink property. It then calls the save(content:completion:) method to save the new array to "StandardPyLeapProjects.json" file. /// This method uses the reduce(into:_:) method to iterate over the array, and it builds a new array that only contains unique ResultItem objects based on their bundleLink property. It then calls the save(content:completion:) method to save the new array to "StandardPyLeapProjects.json" file.
*/ */
func removeDuplicates(projectList: [ResultItem]) { func removeDuplicates(projectList: [PyProject]) {
let combinedLists = projectList.reduce(into: [ResultItem]()) { (result, projectList) in let combinedLists = projectList.reduce(into: [PyProject]()) { (result, projectList) in
if !result.contains(where: { $0.bundleLink == projectList.bundleLink }) { if !result.contains(where: { $0.bundleLink == projectList.bundleLink }) {
result.append(projectList) result.append(projectList)
@ -146,7 +146,7 @@ public class DataStore {
let savedData = try? Data(contentsOf: fileURL) let savedData = try? Data(contentsOf: fileURL)
if let savedData = savedData, if let savedData = savedData,
let savedProjects = try? JSONDecoder().decode([ResultItem].self, from: savedData) { let savedProjects = try? JSONDecoder().decode([PyProject].self, from: savedData) {
for project in savedProjects { for project in savedProjects {
print("CustomProjects name: \(project.projectName)") print("CustomProjects name: \(project.projectName)")

View file

@ -8,11 +8,24 @@
import Foundation import Foundation
struct RootResults: Decodable {
let projects: [ResultItem] struct RootResults: Codable {
let formatVersion: Int
let fileVersion: Int
let projects: [PyProject]
} }
struct ResultItem: Codable, Identifiable, Equatable { struct PyProject: Codable, Identifiable, Equatable {
var id: UUID = UUID()
let projectName: String
let projectImage: String
let description: String
let bundleLink: String
let learnGuideLink: String
let compatibility: [String]
let bluetoothCompatible: Bool
let wifiCompatible: Bool
enum CodingKeys: CodingKey { enum CodingKeys: CodingKey {
case projectName case projectName
case projectImage case projectImage
@ -20,15 +33,21 @@ struct ResultItem: Codable, Identifiable, Equatable {
case bundleLink case bundleLink
case learnGuideLink case learnGuideLink
case compatibility case compatibility
case bluetoothCompatible
case wifiCompatible
} }
var id = UUID() init(from decoder: Decoder) throws {
let projectName: String let container = try decoder.container(keyedBy: CodingKeys.self)
let projectImage: String self.projectName = try container.decode(String.self, forKey: .projectName)
let description: String self.projectImage = try container.decode(String.self, forKey: .projectImage)
let bundleLink: String self.description = try container.decode(String.self, forKey: .description)
let learnGuideLink: String self.bundleLink = try container.decode(String.self, forKey: .bundleLink)
let compatibility: [String] self.learnGuideLink = try container.decode(String.self, forKey: .learnGuideLink)
self.compatibility = try container.decode([String].self, forKey: .compatibility)
self.bluetoothCompatible = try container.decode(Bool.self, forKey: .bluetoothCompatible)
self.wifiCompatible = try container.decode(Bool.self, forKey: .wifiCompatible)
}
} }

View file

@ -14,6 +14,7 @@ import Foundation
import SwiftUI import SwiftUI
class NetworkService: ObservableObject { class NetworkService: ObservableObject {
let dataStore = DataStore() let dataStore = DataStore()
let thirdPartyBackgroundQueue = DispatchQueue(label: "com.PyLeap.thirdPartyBackgroundQueue", qos: .background, attributes: .concurrent) let thirdPartyBackgroundQueue = DispatchQueue(label: "com.PyLeap.thirdPartyBackgroundQueue", qos: .background, attributes: .concurrent)
@ -34,39 +35,40 @@ class NetworkService: ObservableObject {
func fetch(completion: @escaping() -> Void) { func fetch(completion: @escaping() -> Void) {
print("Attempting Network Request") print("Attempting Network Request")
let request = URLRequest(url: URL(string: AdafruitInfo.baseURL)!, cachePolicy: URLRequest.CachePolicy.returnCacheDataElseLoad, timeoutInterval: 60.0) let request = URLRequest(url: URL(string: AdafruitInfo.baseURL)!)
let task = session.dataTask(with: request) { data, response, error in let task = session.dataTask(with: request) { data, response, error in
if let error = error { if let error = error {
print("error: \(error)") print("error: \(error)")
}
if let data = data {
print("Updating UIList with new data...")
if let projectData = try? JSONDecoder().decode(RootResults.self, from: data) {
DispatchQueue.main.async {
self.dataStore.save(content: projectData.projects, completion: self.dataStore.loadDefaultProjectList)
completion()
}
} else {
print("No data found")
}
} else {
print("Updating UIList with Cached data...") print("Updating UIList with Cached data...")
DispatchQueue.main.async { DispatchQueue.main.async {
self.dataStore.loadDefaultProjectList() self.dataStore.loadDefaultProjectList()
completion() completion()
} }
return
}
if let data = data {
print("Updating storage with new data.")
do {
let projectData = try JSONDecoder().decode(RootResults.self, from: data)
dump(projectData.projects)
DispatchQueue.main.async {
self.dataStore.save(content: projectData.projects, completion: self.dataStore.loadDefaultProjectList)
completion()
}
} catch {
print("Decoding error: \(error)")
}
} }
} }
task.resume() task.resume()
} }
func fetchThirdPartyProject(urlString: String?) { func fetchThirdPartyProject(urlString: String?) {
thirdPartyBackgroundQueue.async { thirdPartyBackgroundQueue.async {

View file

@ -175,6 +175,9 @@ Find more information on adding your own project here:
} }
} }
extension View { extension View {
func comfirmationAlertMessage(title: String, exitTitle: String, primaryTitle: String,disconnect: @escaping() -> (),cancel: @escaping() -> ()){ func comfirmationAlertMessage(title: String, exitTitle: String, primaryTitle: String,disconnect: @escaping() -> (),cancel: @escaping() -> ()){

View file

@ -31,5 +31,10 @@ extension View {
} }
} }
func formatDeviceName(_ name: String) -> String {
let replaced = name.replacingOccurrences(of: "_", with: " ").replacingOccurrences(of: "-", with: " ")
let formatted = replaced.capitalized
return formatted
}
} }

BIN
PyLeap/Views/.DS_Store vendored

Binary file not shown.

View file

@ -1,246 +0,0 @@
//
// MainSelectionView.swift
// PyLeap
//
// Created by Trevor Beaton on 10/16/21.
//
import SwiftUI
import FileTransferClient
enum AdafruitDevices {
case clue_nrf52840_express
case circuitplayground_bluefruit
case esp32s2
}
struct MainSelectionView: View {
@State private var showWebViewPopover: Bool = false
@State private var inConnectedInSelectionView = true
@State private var boardBootInfo = ""
@EnvironmentObject var expandedState : ExpandedBLECellState
@ObservedObject var viewModel = MainSelectionViewModel()
@State private var isConnected = false
@State private var test = ""
@State private var nilBinder = DownloadState.idle
@EnvironmentObject var rootViewModel: RootViewModel
@AppStorage("shouldShowOnboarding") var shouldShowOnboarding: Bool = true
var body: some View {
VStack(alignment: .center, spacing: 0) {
MainHeaderView()
HStack(alignment: .center, spacing: 8, content: {
Text("Not Connected to a Device.")
.font(Font.custom("ReadexPro-Regular", size: 16))
Button {
rootViewModel.goToSelection()
} label: {
Text("Connect Now")
.font(Font.custom("ReadexPro-Regular", size: 16))
.underline()
}
})
.padding(.all, 0.0)
.frame(maxWidth: .infinity)
.frame(maxHeight: 40)
.background(Color("pyleap_burg"))
.foregroundColor(.white)
ScrollView {
MainSubHeaderView(device: "Adafruit device")
if viewModel.pdemos.isEmpty {
HStack{
Spacer()
ProgressView()
.scaleEffect(2)
Spacer()
}
.padding(0)
}
ScrollViewReader { scroll in
ForEach(viewModel.pdemos) { demo in
if demo.bundleLink == expandedState.currentCell {
DemoViewCell(result: demo, isExpanded: true, isConnected: $inConnectedInSelectionView, deviceInfo: $boardBootInfo, onViewGeometryChanged: {
})
.onAppear(){
print("Cell Appeared")
withAnimation {
scroll.scrollTo(demo.id)
}
}
} else {
DemoViewCell(result: demo, isExpanded: false, isConnected: $inConnectedInSelectionView, deviceInfo: $boardBootInfo, onViewGeometryChanged: {
})
}
}
}
}
}
.onDisappear() {
}
/// **Pull down to Refresh feature**
// ScrollRefreshableView(title: "Refresh", tintColor: .purple) {
// HStack{
// Spacer()
// MainSubHeaderView()
// Spacer()
// }
// .padding(0)
//
// if networkModel.pdemos.isEmpty {
// HStack{
// Spacer()
// ProgressView()
// .scaleEffect(2)
// Spacer()
// }
// .padding(0)
//
// }
//
// }// } onRefresh: {
// self.networkModel.fetch()
// }
//
// }
.onChange(of: viewModel.pdemos, perform: { newValue in
print("Update")
})
.onAppear() {
print("Opened MainSelectionView")
}
.fullScreenCover(isPresented: $shouldShowOnboarding, content: {
ExampleView(shouldShowOnboarding: $shouldShowOnboarding)
})
.preferredColorScheme(.light)
.background(Color.white)
.navigationBarColor(UIColor(named: "pyleap_gray"))
.navigationBarTitleDisplayMode(.inline)
}
}
//struct MainSelectionView_Previews: PreviewProvider {
// static var previews: some View {
// MainSelectionView()
// }
//}
//
//struct MainSelectionView: View {
//
// @State private var showWebViewPopover: Bool = false
// @ObservedObject var networkModel = NetworkService()
// @ObservedObject var viewModel = MainSelectionViewModel()
// @State private var test = ""
// @State private var nilBinder = DownloadState.idle
// @EnvironmentObject var rootViewModel: RootViewModel
// @AppStorage("shouldShowOnboarding") var shouldShowOnboarding: Bool = true
//
// var body: some View {
// VStack(alignment: .center, spacing: 0) {
// MainHeaderView()
// connectionMessageView()
// ScrollView {
// MainSubHeaderView(device: "Adafruit device")
// if networkModel.pdemos.isEmpty {
// loadingIndicatorView()
// }
// ScrollViewReader { scroll in
// ForEach(networkModel.pdemos) { demo in
// demoViewCell(demo: demo, scroll: scroll)
// }
// }
// }
// }
// }
//
// private func connectionMessageView() -> some View {
// HStack(alignment: .center, spacing: 8) {
// Text("Not Connected to a Device.")
// .font(Font.custom("ReadexPro-Regular", size: 16))
// Button {
// rootViewModel.goToSelection()
// } label: {
// Text("Connect Now")
// .font(Font.custom("ReadexPro-Regular", size: 16))
// .underline()
// }
// }
// .padding(.all, 0.0)
// .frame(maxWidth: .infinity)
// .frame(maxHeight: 40)
// .background(Color("pyleap_burg"))
// .foregroundColor(.white)
// }
//
// private func loadingIndicatorView() -> some View {
// HStack{
// Spacer()
// ProgressView()
// .scaleEffect(2)
// Spacer()
// }
// .padding(0)
// }
//
// private func demoViewCell(demo: Demo, scroll: ScrollViewProxy) -> some View {
// if demo.bundleLink == expandedState.currentCell {
// DemoViewCell(result: demo, isExpanded: true, isConnected: $inConnectedInSelectionView, deviceInfo: $boardBootInfo, onViewGeometryChanged: {
// })
// .onAppear(){
// print("Cell Appeared")
// withAnimation {
// scroll.scrollTo(demo.id)
// }
// }
// } else {
// DemoViewCell(result: demo, isExpanded: false, isConnected: $inConnectedInSelectionView, deviceInfo: $boardBootInfo, onViewGeometryChanged: {
// })
// }
// }
//}