From 758c5d518a605461c2dce40a49d3d0c215ab0916 Mon Sep 17 00:00:00 2001 From: EdgSgroi Date: Thu, 9 Jul 2020 15:28:38 -0300 Subject: [PATCH 1/2] Create tvos ideas list broadcast methods --- Blink/Blink.xcodeproj/project.pbxproj | 4 ++ .../ViewModels/BrainstormingViewModel.swift | 2 +- .../Blink/Menu/ViewModels/MenuViewModel.swift | 2 +- .../Voting/ViewModel/VotingViewModel.swift | 30 ++++++++--- .../Voting/ViewModels/VotingViewModel.swift | 54 +++++++++++++++++++ 5 files changed, 84 insertions(+), 8 deletions(-) create mode 100644 Blink/Blink_iOS/Voting/ViewModels/VotingViewModel.swift diff --git a/Blink/Blink.xcodeproj/project.pbxproj b/Blink/Blink.xcodeproj/project.pbxproj index 49257f8..7bcbb15 100644 --- a/Blink/Blink.xcodeproj/project.pbxproj +++ b/Blink/Blink.xcodeproj/project.pbxproj @@ -21,6 +21,7 @@ 4F5F130D24B6612E00A7D9E7 /* Multipeer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F5F130C24B6612E00A7D9E7 /* Multipeer.swift */; }; 4F5F130F24B661D700A7D9E7 /* MenuViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F5F130E24B661D700A7D9E7 /* MenuViewModel.swift */; }; 4F5F131124B6659A00A7D9E7 /* BrainstormingViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F5F131024B6659A00A7D9E7 /* BrainstormingViewModel.swift */; }; + 4F5F131324B78BCB00A7D9E7 /* VotingViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F5F131224B78BCB00A7D9E7 /* VotingViewModel.swift */; }; A418231A24B39DDF0082962F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A418231924B39DDF0082962F /* AppDelegate.swift */; }; A418231C24B39DDF0082962F /* MenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A418231B24B39DDF0082962F /* MenuView.swift */; }; A418231E24B39DE10082962F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A418231D24B39DE10082962F /* Assets.xcassets */; }; @@ -70,6 +71,7 @@ 4F5F130C24B6612E00A7D9E7 /* Multipeer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Multipeer.swift; sourceTree = ""; }; 4F5F130E24B661D700A7D9E7 /* MenuViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuViewModel.swift; sourceTree = ""; }; 4F5F131024B6659A00A7D9E7 /* BrainstormingViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrainstormingViewModel.swift; sourceTree = ""; }; + 4F5F131224B78BCB00A7D9E7 /* VotingViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VotingViewModel.swift; sourceTree = ""; }; A418231624B39DDF0082962F /* Blink.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Blink.app; sourceTree = BUILT_PRODUCTS_DIR; }; A418231924B39DDF0082962F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; A418231B24B39DDF0082962F /* MenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuView.swift; sourceTree = ""; }; @@ -286,6 +288,7 @@ 4F5F130A24B6610F00A7D9E7 /* ViewModels */ = { isa = PBXGroup; children = ( + 4F5F131224B78BCB00A7D9E7 /* VotingViewModel.swift */, ); path = ViewModels; sourceTree = ""; @@ -534,6 +537,7 @@ 4F5F12D424B639FA00A7D9E7 /* SceneDelegate.swift in Sources */, 4F5F130F24B661D700A7D9E7 /* MenuViewModel.swift in Sources */, 4F5F12D624B639FA00A7D9E7 /* ContentView.swift in Sources */, + 4F5F131324B78BCB00A7D9E7 /* VotingViewModel.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Blink/Blink/Brainstorming/ViewModels/BrainstormingViewModel.swift b/Blink/Blink/Brainstorming/ViewModels/BrainstormingViewModel.swift index b9b5ef0..8b482f0 100644 --- a/Blink/Blink/Brainstorming/ViewModels/BrainstormingViewModel.swift +++ b/Blink/Blink/Brainstorming/ViewModels/BrainstormingViewModel.swift @@ -9,7 +9,7 @@ import Foundation import MultipeerConnectivity -class BrainstormingViewModel: NSObject, MCSessionDelegate { +class BrainstormingViewModel: NSObject { let multipeerConnection = Multipeer.shared diff --git a/Blink/Blink/Menu/ViewModels/MenuViewModel.swift b/Blink/Blink/Menu/ViewModels/MenuViewModel.swift index d50023e..ef1cdae 100644 --- a/Blink/Blink/Menu/ViewModels/MenuViewModel.swift +++ b/Blink/Blink/Menu/ViewModels/MenuViewModel.swift @@ -9,7 +9,7 @@ import Foundation import MultipeerConnectivity -class MenuViewModel: NSObject, MCSessionDelegate { +class MenuViewModel: NSObject { let multipeerConnection = Multipeer.shared diff --git a/Blink/Blink/Voting/ViewModel/VotingViewModel.swift b/Blink/Blink/Voting/ViewModel/VotingViewModel.swift index f68865e..9724341 100644 --- a/Blink/Blink/Voting/ViewModel/VotingViewModel.swift +++ b/Blink/Blink/Voting/ViewModel/VotingViewModel.swift @@ -9,15 +9,33 @@ import Foundation import MultipeerConnectivity -class VotingViewModel: NSObject, MCSessionDelegate { - +class VotingViewModel: NSObject { let multipeerConnection = Multipeer.shared + var ideas: [String] = [] + override init() { super.init() multipeerConnection.delegate = self } + func sendIdeas() { + let mcSession = multipeerConnection.mcSession + if mcSession.connectedPeers.count > 0 { + if let ideasData = try? NSKeyedArchiver.archivedData(withRootObject: ideas, requiringSecureCoding: false) { + do { + try mcSession.send(ideasData, toPeers: mcSession.connectedPeers, with: .reliable) + } catch let error as NSError { + let ac = UIAlertController(title: "Send error", message: error.localizedDescription, preferredStyle: .alert) + ac.addAction(UIAlertAction(title: "Ok", style: .default)) +// present(ac, animated: true) + } + } + } + } +} + +extension VotingViewModel: MCSessionDelegate { func session(_ session: MCSession, peer peerID: MCPeerID, didChange state: MCSessionState) { switch state { case MCSessionState.connected: @@ -30,16 +48,16 @@ class VotingViewModel: NSObject, MCSessionDelegate { multipeerConnection.connectionStatus = .unknown } } - + func session(_ session: MCSession, didReceive data: Data, fromPeer peerID: MCPeerID) { } - + func session(_ session: MCSession, didReceive stream: InputStream, withName streamName: String, fromPeer peerID: MCPeerID) { } - + func session(_ session: MCSession, didStartReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, with progress: Progress) { } - + func session(_ session: MCSession, didFinishReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, at localURL: URL?, withError error: Error?) { } } diff --git a/Blink/Blink_iOS/Voting/ViewModels/VotingViewModel.swift b/Blink/Blink_iOS/Voting/ViewModels/VotingViewModel.swift new file mode 100644 index 0000000..c42e4ec --- /dev/null +++ b/Blink/Blink_iOS/Voting/ViewModels/VotingViewModel.swift @@ -0,0 +1,54 @@ +// +// VotingViewModel.swift +// Blink_iOS +// +// Created by Edgar Sgroi on 09/07/20. +// Copyright © 2020 Artur Carneiro. All rights reserved. +// + +import Foundation +import MultipeerConnectivity + +class VotingViewModel: NSObject { + + var multipeerConnection = Multipeer.shared + + var ideas: [String] = [] + + override init() { + super.init() + multipeerConnection.delegate = self + } +} + +extension VotingViewModel: MCSessionDelegate { + func session(_ session: MCSession, peer peerID: MCPeerID, didChange state: MCSessionState) { + switch state { + case MCSessionState.connected: + multipeerConnection.connectionStatus = .connected + case MCSessionState.connecting: + multipeerConnection.connectionStatus = .connecting + case MCSessionState.notConnected: + multipeerConnection.connectionStatus = .notConnected + @unknown default: + multipeerConnection.connectionStatus = .unknown + } + } + + func session(_ session: MCSession, didReceive data: Data, fromPeer peerID: MCPeerID) { + if let ideasList:[String] = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? [String] { + ideas = ideasList + } + } + + func session(_ session: MCSession, didReceive stream: InputStream, withName streamName: String, fromPeer peerID: MCPeerID) { + } + + func session(_ session: MCSession, didStartReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, with progress: Progress) { + } + + func session(_ session: MCSession, didFinishReceivingResourceWithName resourceName: String, fromPeer peerID: MCPeerID, at localURL: URL?, withError error: Error?) { + } + + +} From 8b72695f54242d04107dcdba6dc9c1516e8e62a7 Mon Sep 17 00:00:00 2001 From: EdgSgroi Date: Fri, 10 Jul 2020 14:56:53 -0300 Subject: [PATCH 2/2] Add tvOS broadcast and voting methods Unit Test of votes counting method was added too. --- Blink/Blink.xcodeproj/project.pbxproj | 4 +++ .../Voting/ViewModel/VotingViewModel.swift | 21 +++++++++++++ Blink/BlinkTests/VotingViewModelTests.swift | 30 +++++++++++++++++++ .../Voting/ViewModels/VotingViewModel.swift | 16 ++++++++++ 4 files changed, 71 insertions(+) create mode 100644 Blink/BlinkTests/VotingViewModelTests.swift diff --git a/Blink/Blink.xcodeproj/project.pbxproj b/Blink/Blink.xcodeproj/project.pbxproj index 7bcbb15..b4ff088 100644 --- a/Blink/Blink.xcodeproj/project.pbxproj +++ b/Blink/Blink.xcodeproj/project.pbxproj @@ -22,6 +22,7 @@ 4F5F130F24B661D700A7D9E7 /* MenuViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F5F130E24B661D700A7D9E7 /* MenuViewModel.swift */; }; 4F5F131124B6659A00A7D9E7 /* BrainstormingViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F5F131024B6659A00A7D9E7 /* BrainstormingViewModel.swift */; }; 4F5F131324B78BCB00A7D9E7 /* VotingViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F5F131224B78BCB00A7D9E7 /* VotingViewModel.swift */; }; + 4F5F131524B8DDB500A7D9E7 /* VotingViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4F5F131424B8DDB500A7D9E7 /* VotingViewModelTests.swift */; }; A418231A24B39DDF0082962F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A418231924B39DDF0082962F /* AppDelegate.swift */; }; A418231C24B39DDF0082962F /* MenuView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A418231B24B39DDF0082962F /* MenuView.swift */; }; A418231E24B39DE10082962F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A418231D24B39DE10082962F /* Assets.xcassets */; }; @@ -72,6 +73,7 @@ 4F5F130E24B661D700A7D9E7 /* MenuViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuViewModel.swift; sourceTree = ""; }; 4F5F131024B6659A00A7D9E7 /* BrainstormingViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrainstormingViewModel.swift; sourceTree = ""; }; 4F5F131224B78BCB00A7D9E7 /* VotingViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VotingViewModel.swift; sourceTree = ""; }; + 4F5F131424B8DDB500A7D9E7 /* VotingViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VotingViewModelTests.swift; sourceTree = ""; }; A418231624B39DDF0082962F /* Blink.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Blink.app; sourceTree = BUILT_PRODUCTS_DIR; }; A418231924B39DDF0082962F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; A418231B24B39DDF0082962F /* MenuView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuView.swift; sourceTree = ""; }; @@ -356,6 +358,7 @@ children = ( A418232E24B39DE10082962F /* BlinkTests.swift */, A418233024B39DE10082962F /* Info.plist */, + 4F5F131424B8DDB500A7D9E7 /* VotingViewModelTests.swift */, ); path = BlinkTests; sourceTree = ""; @@ -572,6 +575,7 @@ buildActionMask = 2147483647; files = ( A418232F24B39DE10082962F /* BlinkTests.swift in Sources */, + 4F5F131524B8DDB500A7D9E7 /* VotingViewModelTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Blink/Blink/Voting/ViewModel/VotingViewModel.swift b/Blink/Blink/Voting/ViewModel/VotingViewModel.swift index 9724341..d6e3c07 100644 --- a/Blink/Blink/Voting/ViewModel/VotingViewModel.swift +++ b/Blink/Blink/Voting/ViewModel/VotingViewModel.swift @@ -10,9 +10,12 @@ import Foundation import MultipeerConnectivity class VotingViewModel: NSObject { + typealias Ranking = [(key: String, value: Int)] let multipeerConnection = Multipeer.shared var ideas: [String] = [] + var votes: [String] = [] + var rank: Ranking = [] override init() { super.init() @@ -33,6 +36,22 @@ class VotingViewModel: NSObject { } } } + + func countVotes(votes: [String], ideas: [String]) -> Ranking { + let votedIdeas = Array(Set(votes)) + var nonVotedIdeas = [String]() + for i in ideas { + if !votedIdeas.contains(i) { + nonVotedIdeas.append(i) + } + } + let votedIdeasArray = votes.map { ($0, 1) } + let ideasFrequency = Dictionary(votedIdeasArray, uniquingKeysWith: +) + let nonVotedIdeasArray = nonVotedIdeas.map { ($0, 0) } + let ideasNonFrequency = Dictionary(nonVotedIdeasArray, uniquingKeysWith: +) + let votingResult = ideasFrequency.merging(ideasNonFrequency) { (_, new) in new } + return votingResult.sorted(by: {($0.value > $1.value)}) + } } extension VotingViewModel: MCSessionDelegate { @@ -50,6 +69,8 @@ extension VotingViewModel: MCSessionDelegate { } func session(_ session: MCSession, didReceive data: Data, fromPeer peerID: MCPeerID) { + if let votesList:[String] = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? [String] { + votes += votesList } } func session(_ session: MCSession, didReceive stream: InputStream, withName streamName: String, fromPeer peerID: MCPeerID) { diff --git a/Blink/BlinkTests/VotingViewModelTests.swift b/Blink/BlinkTests/VotingViewModelTests.swift new file mode 100644 index 0000000..303edd7 --- /dev/null +++ b/Blink/BlinkTests/VotingViewModelTests.swift @@ -0,0 +1,30 @@ +// +// VotingViewModelTests.swift +// BlinkTests +// +// Created by Edgar Sgroi on 10/07/20. +// Copyright © 2020 Artur Carneiro. All rights reserved. +// + +import XCTest +@testable import Blink + +class VotingViewModelTests: XCTestCase { + + func testCountVotes() { + let viewModel = VotingViewModel() + let ideas = ["preved", "poka", "hola", "yo"] + let votes = ["preved", "hola", "poka", "hola", "poka", "hola"] + + XCTAssertFalse(ideas.isEmpty) + XCTAssertFalse(votes.isEmpty) + + let result = viewModel.countVotes(votes: votes, ideas: ideas) + XCTAssertFalse(result.isEmpty) + let expectedResult = [(key: "hola", value: 3), (key: "poka", value: 2), (key: "preved", value: 1), (key: "yo", value: 0)] + for index in 0.. 0 { + if let votesData = try? NSKeyedArchiver.archivedData(withRootObject: votes, requiringSecureCoding: false) { + do { + try mcSession.send(votesData, toPeers: mcSession.connectedPeers, with: .reliable) + } catch let error as NSError { + let ac = UIAlertController(title: "Send error", message: error.localizedDescription, preferredStyle: .alert) + ac.addAction(UIAlertAction(title: "Ok", style: .default)) +// present(ac, animated: true) + } + } + } + } } extension VotingViewModel: MCSessionDelegate {