Swift 3 stream delegate even handler error - ios

I am writing a very, very simple TCP socket app that sends and receives data from a server; my app uses delegates; most of the code I found from online but none of the event handlers for the streams are working; any ideas on why the following method is never being called?
UPDATE: this question has been answered here:
Non-responsive stream delegate in Swift
However, the problem that I am facing is how do I flush the output stream after the buffer is full? I would like send data in real time to my server, but I am getting a "broken pipe" from stream output error
/**
NSStream Delegate Method where we handle errors, read and write data from input and output streams
:param: stream NStream that called delegate method
:param: eventCode Event Code
*/
final func stream(stream: Stream, handleEvent eventCode: Stream.Event) {
switch eventCode {
case Stream.Event.endEncountered:
endEncountered(stream: stream)
case Stream.Event.errorOccurred:
print("[SCKT]: ErrorOccurred: \(stream.streamError?.localizedDescription)")
case Stream.Event.openCompleted:
print("open completed")
openCompleted(stream: stream)
case Stream.Event.hasBytesAvailable:
handleIncommingStream(stream: stream)
case Stream.Event.hasSpaceAvailable:
print("space available")
writeToStream()
default:
print("default!")
}
}
UIViewController
import Darwin
import Foundation
import UIKit
import Dispatch
class ViewController: UIViewController {
#IBOutlet private weak var joystickMove: Joystick!
#IBOutlet private weak var joystickRotate: Joystick!
private var joystick = Joystick()
private var contour = Contours()
private var contour_index: Int = 0
private var ip_address = "000.000.00.0" as CFString
private var port: Int = 0000
private var control_socket: Socket
// private var control_socket: Socket = Socket()
private var toast_label: UILabel = UILabel()
// let dataProcessingQueue = DispatchQueue.main
// convenience init() {
// self.control_socket = Socket()
// }
// init(coder aDecoder: NSCoder!) {
// super.init(coder: aDecoder)
// }
// init(imageURL: NSURL?) {
// self.control_socket = Socket()
// self.control_socket.open(host: self.ip_address as String!, port: self.port)
// super.init(nibName: nil, bundle: nil)
// }
// required init?(coder aDecoder: NSCoder) {
// fatalError("init(coder:) has not been implemented")
// }
required init(coder aDecoder: NSCoder) {
print("here!")
self.control_socket = Socket()
// self.control_socket.open(host: self.ip_address as String!, port: self.port)
print("here(2)!")
super.init(coder: aDecoder)!
}
override func viewDidLoad() {
super.viewDidLoad()
self.control_socket.open(host: self.ip_address as String!, port: self.port)
createJoystick()
createContours()
createViewsButton()
recvLidarData()
}
private func requestIsComplete() -> Bool {
// This function should find out if all expected data was received and return 'true' if it did.
return true
}
private func processData(data: Data) {
// This function should do something with the received data
}
private func recvLidarData() {
let concurrentQueue = DispatchQueue(label: "queuename", attributes: .concurrent)
concurrentQueue.async {
// recieve the data here!
}
}
private func createToast() {
self.toast_label = UILabel(frame: CGRect(x: self.view.frame.size.width/2 - 150, y: self.view.frame.size.height-100, width: 300, height: 35))
self.toast_label.backgroundColor = UIColor.black
self.toast_label.textColor = UIColor.white
self.toast_label.textAlignment = NSTextAlignment.center;
self.view.addSubview(self.toast_label)
self.toast_label.text = "Could not connect to server"
self.toast_label.alpha = 1.0
self.toast_label.layer.cornerRadius = 10;
self.toast_label.clipsToBounds = true
UIView.animate(withDuration: 4.0, delay: 0.1, options: UIViewAnimationOptions.curveEaseOut, animations: {
self.toast_label.alpha = 0.0
})
}
private func createJoystick() {
let n: CGFloat = 100.0
let x: CGFloat = (UIScreen.main.bounds.width/2) - (n/2.0)
let y: CGFloat = UIScreen.main.bounds.height - (UIScreen.main.bounds.height/4.0)
self.joystick.frame = CGRect(x: x, y: y, width: n, height: n)
self.joystick.backgroundColor = UIColor.clear
self.joystick.substrateColor = UIColor.lightGray
self.joystick.substrateBorderColor = UIColor.gray
self.joystick.substrateBorderWidth = 1.0
self.joystick.stickSize = CGSize(width: 50.0, height: 50.0)
self.joystick.stickColor = UIColor.darkGray
self.joystick.stickBorderColor = UIColor.black
self.joystick.stickBorderWidth = 2.0
self.joystick.fade = 0.5
self.joystick.ip_address = self.ip_address
self.joystick.port = self.port
var packet = ""
DispatchQueue.global(qos: .userInitiated).async { // do some task
// self.control_socket = Socket()
// self.control_socket.open(host: self.ip_address as String!, port: self.port)
self.joystick.trackingHandler = { (data) -> () in
var power = sqrt(pow(Double(data.velocity.x), 2.0) + pow(Double(data.velocity.y), 2.0))
let theta = atan2(Double(-data.velocity.y), Double(data.velocity.x))
let degrees = theta * (180.0 / M_PI)
power = power/1.2
if degrees >= 55 && degrees <= 125 { // move forward
packet = "\(1) \(1) \(power) \(power)"
} else if degrees >= -125 && degrees <= -55 { // move backwards
packet = "\(-1) \(-1) \(power) \(power)"
} else if degrees >= -55 && degrees <= 55 { // turn right
packet = "\(1) \(-1) \(power) \(power)"
} else { // turn left
packet = "\(-1) \(1) \(power) \(power)"
}
}
print("packet: \(packet)")
self.control_socket.send(message: packet)
print("sent")
}
view.addSubview(joystick)
}
private func createContours() {
let n: CGFloat = 350.0
let x: CGFloat = (UIScreen.main.bounds.width/2.0) - (n/2.0)
let y: CGFloat = UIScreen.main.bounds.height - (UIScreen.main.bounds.height/4.0) - n - 100.0
self.contour.frame = CGRect(x: x, y: y, width: n, height: n)
self.contour.backgroundColor = UIColor.clear
view.addSubview(self.contour)
}
private func createViewsButton() {
let width: CGFloat = 150.0
let height: CGFloat = 75.0
let x: CGFloat = (UIScreen.main.bounds.width/2.0) - (width/2.0)
let y: CGFloat = UIScreen.main.bounds.height - (UIScreen.main.bounds.height/4.0) - width
let button: UIButton = UIButton(frame: CGRect(x: x, y: y, width: width, height: height))
button.backgroundColor = UIColor.blue
button.setTitle("Contour Views", for: .normal)
button.addTarget(self, action: #selector(self.buttonAction), for: .touchUpInside)
button.tag = 1
view.addSubview(button)
}
#objc private func buttonAction(sender: UIButton!) {
var btnsendtag: UIButton = sender
if btnsendtag.tag == 1 {
self.contour_index = (self.contour_index + 1) % 2
switch self.contour_index {
case 0:
for index in 0...356 {
if self.contour.distx[index] != -1 && self.contour.disty[index] != -1 {
self.contour.circles[index].alpha = 0
}
}
case 1:
for index in 0...356 {
if self.contour.distx[index] != -1 && self.contour.disty[index] != -1 {
self.contour.circles[index].alpha = 1
}
}
default:
for index in 0...356 {
if self.contour.distx[index] != -1 && self.contour.disty[index] != -1 {
self.contour.circles[index].alpha = 1
UIColor.cyan.setFill()
self.contour.lines[index].fill()
self.contour.lines[index].stroke()
}
}
}
}
}
override func viewDidAppear(_ animated: Bool) {
}
public func delayWithSeconds(_ seconds: Double, completion: #escaping () -> ()) {
DispatchQueue.main.asyncAfter(deadline: .now() + seconds) {
completion()
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillDisappear(_ animated: Bool) {
}
}
Socket class
import UIKit
import Foundation
#objc protocol SocketStreamDelegate{
func socketDidConnect(stream:Stream)
#objc optional func socketDidDisconnet(stream:Stream, message:String)
#objc optional func socketDidReceiveMessage(stream:Stream, message:String)
#objc optional func socketDidEndConnection()
}
class Socket: NSObject, StreamDelegate {
var delegate:SocketStreamDelegate?
private let bufferSize = 1024
private var _host:String?
private var _port:Int?
private var _messagesQueue:Array<String> = [String]()
private var _streamHasSpace:Bool = false
private var inputStream: InputStream?
private var outputStream: OutputStream?
var isClosed = false
var isOpen = false
var host:String?{
get{
return self._host
}
}
var port:Int?{
get{
return self._port
}
}
deinit{
if let inputStr = self.inputStream{
inputStr.close()
inputStr.remove(from: .main, forMode: RunLoopMode.defaultRunLoopMode)
}
if let outputStr = self.outputStream{
outputStr.close()
outputStr.remove(from: .main, forMode: RunLoopMode.defaultRunLoopMode)
}
}
/**
Opens streaming for both reading and writing, error will be thrown if you try to send a message and streaming hasn't been opened
:param: host String with host portion
:param: port Port
*/
final func open(host:String!, port:Int!){
self._host = host
self._port = port
var inStreamUnmanaged:Unmanaged<CFReadStream>?
var outStreamUnmanaged:Unmanaged<CFWriteStream>?
CFStreamCreatePairWithSocketToHost(nil, host as CFString!, UInt32(port), &inStreamUnmanaged, &outStreamUnmanaged)
inputStream = inStreamUnmanaged?.takeRetainedValue()
outputStream = outStreamUnmanaged?.takeRetainedValue()
if inputStream != nil && outputStream != nil {
inputStream!.delegate = self
outputStream!.delegate = self
// var myloop = RunLoop.current
// inputStream!.schedule(in: myloop, forMode: RunLoopMode.defaultRunLoopMode)
// outputStream!.schedule(in: myloop, forMode: RunLoopMode.defaultRunLoopMode)
inputStream!.schedule(in: .main, forMode: RunLoopMode.defaultRunLoopMode)
outputStream!.schedule(in: .main, forMode: RunLoopMode.defaultRunLoopMode)
print("[SCKT]: Open Stream")
self._messagesQueue = Array()
inputStream!.open()
outputStream!.open()
// myloop.run()
print("Run")
} else {
print("[SCKT]: Failed Getting Streams")
}
}
final func close(){
if let inputStr = self.inputStream {
inputStr.delegate = nil
inputStr.close()
inputStr.remove(from: .main, forMode: RunLoopMode.defaultRunLoopMode)
}
if let outputStr = self.outputStream {
outputStr.delegate = nil
outputStr.close()
outputStr.remove(from: .main, forMode: RunLoopMode.defaultRunLoopMode)
}
isClosed = true
}
/**
NSStream Delegate Method where we handle errors, read and write data from input and output streams
:param: stream NStream that called delegate method
:param: eventCode Event Code
*/
final func stream(stream: Stream, handleEvent eventCode: Stream.Event) {
switch eventCode {
case Stream.Event.endEncountered:
endEncountered(stream: stream)
case Stream.Event.errorOccurred:
print("[SCKT]: ErrorOccurred: \(stream.streamError?.localizedDescription)")
case Stream.Event.openCompleted:
print("open completed")
openCompleted(stream: stream)
case Stream.Event.hasBytesAvailable:
handleIncommingStream(stream: stream)
case Stream.Event.hasSpaceAvailable:
print("space available")
writeToStream()
default:
print("default!")
}
}
final func endEncountered(stream: Stream) {
}
final func openCompleted(stream: Stream){
if(self.inputStream!.streamStatus == .open && self.outputStream!.streamStatus == .open) {
let justAOneTimeThing: () = {
self.isOpen = true
self.delegate!.socketDidConnect(stream: stream)
}()
}
}
/**
Reads bytes asynchronously from incomming stream and calls delegate method socketDidReceiveMessage
:param: stream An NSInputStream
*/
final func handleIncommingStream(stream: Stream) {
if stream is InputStream {
var buffer = [UInt8](repeating: 0, count: bufferSize)
DispatchQueue.global(qos: .userInitiated).async {
let len = self.inputStream?.read(&buffer, maxLength: buffer.count)
if len! >= 0 {
if let output = NSString(bytes: &buffer, length: len!, encoding: String.Encoding.utf8.rawValue) {
self.delegate?.socketDidReceiveMessage!(stream: stream, message: output as String)
}
} else {
// Handle error
}
}
} else {
print("[SCKT]: \(#function) : Incorrect stream received")
}
}
/**
If messages exist in _messagesQueue it will remove and it and send it, if there is an error
it will return the message to the queue
*/
final func writeToStream() {
if _messagesQueue.count > 0 && self.outputStream!.hasSpaceAvailable {
DispatchQueue.global(qos: .userInitiated).async {
let message = self._messagesQueue.removeLast()
let buff = [UInt8](message.utf8)
if self.outputStream!.write(buff, maxLength: buff.count) == -1 {
self._messagesQueue.append(message)
}
}
}
}
final func send(message:String){
_messagesQueue.insert(message, at: 0)
writeToStream()
}
}

Maybe your stream function prototype is incorrect. Your prototype is
final func stream(stream: Stream, handleEvent eventCode: Stream.Event)
Try this:
func stream(_ aStream: Stream, handle eventCode: Stream.Event)
in your Socket class

Related

Swift: How can I store array data, so it can be accessed more than once after leaving class?

I'm currently trying to develop a prototype for a quiz application.
The questions for the quiz are read out of a JSON file and the answers are generated within the program. Those two parts are then matched to each other within a Mediator class.
Displaying the question in my ViewController works perfectly fine for the first question. When trying to retrieve the second question the array that stores the question in the Mediator class is empty. That obviously is the case, because I'm re-entering the Mediator after going to my ViewController.
How can I store the data more efficiently?
If you need more information, please let me know. I'm really grateful for every suggestion I can get. I'm totally stuck at right now!
ViewController:
[...]
override func viewDidLoad() {
super.viewDidLoad()
setup(categoryType)
navigationBar.title = barTitle
QAMatcher.delegate = self
QAMatcher.getCategory(categoryType)
}
#objc func didChooseAnswer(_ sender: UIButton){
QAMatcher.nextQuestion()
updateUI()
}
func updateUI(){
if let label = questionLabel {
label.text = QAMatcher.getQuestion()
}
if let type = AnswerType(rawValue: QAMatcher.getAnswerType()) {
addAnswerSection(type)
}
}
func addAnswerSection(_ type: AnswerType) {
if let stackView = quizStackView, let answerSection = uiBuilder.getAnswerSection(answerType: type) {
stackView.addArrangedSubview(answerSection)
}
}
[...]
Mediator Class:
import Foundation
protocol Mediator {
func sendRequest (_ request: Request, sender: Sender)
}
protocol MediatorDelegate {
func didUpdate(mediator: QuestionAnswerMediator, _ array: [QuestionData])
}
enum Sender {
case answer
case question
}
class QuestionAnswerMediator: Mediator {
var delegate: MediatorDelegate?
var questionArray = [QuestionData]()
var answerArray = [String]()
var request = Request()
var questionNum = 0
func getCategory(_ category: CategoryType) {
request.categoryType = category
matchQuestionAndAnswers()
}
func sendRequest (_ request: Request, sender: Sender){
if sender == .question {
let array = QuestionManager.shared.send(category: request.categoryType!)
questionArray.append(contentsOf: array)
}
else if sender == .answer {
let array = AnswerManager.shared.send(request: request)
if array?.isEmpty == false {
answerArray = []
answerArray.append(contentsOf: array ?? ["default"])
}
}
}
func fetchAnswers(for type: String, question: String) {
request.answerType = AnswerType(rawValue: type)
request.question = question
sendRequest(request, sender: .answer)
}
func fetchQuestions (for category: CategoryType){
request.categoryType = category
sendRequest(request, sender: .question)
}
func matchQuestionAndAnswers(){
var i = 0
fetchQuestions(for: request.categoryType!)
for question in questionArray {
fetchAnswers(for: question.answerType, question: question.question)
if answerArray.isEmpty != true {
questionArray[i].answers = answerArray
}
i += 1
}
self.delegate?.didUpdate(mediator: self, questionArray)
}
func getQuestion() -> String {
return questionArray[questionNum].question
}
func getAnswers() -> [String] {
return questionArray[questionNum].answers!
}
func getAnswerType() -> String {
return questionArray[questionNum].answerType
}
func nextQuestion () {
if questionNum + 1 < questionArray.count {
questionNum += 1
}
else {
questionNum = 0
}
}
}
ok, that was hard ;) i had to create my own project file, because yours was still missing. After doing that i tried to compile and run...but you did not fill the assets so it crashed. After faking your assets...it runs. ;)
You already analyzed your problem correctly. You should never create a viewcontroller mutliple times if you just call him once. So i deleted this and you now only have one instance.
i routed the "didchooseAnswer" through your classes...but you still have some work to do: i had to give a frame to your view class, so fill that frame you need, i don't know what frame you need.
I just corrected it for MultipleChoiceView...you have to do it for the other views as well like PolarView etc...
i hope i have everything copied what i changed, if not, just tell me. but maybe then you should give me rights on your github project so that i can commit it there...
Quizviewcontroller:
import UIKit
import CoreLocation
import os
class QuizViewController: UIViewController {
#IBOutlet weak var questionLabel: UILabel!
#IBOutlet weak var navigationBar: UINavigationItem!
#IBOutlet var mainView: UIView!
#IBOutlet weak var quizStackView: UIStackView!
var barTitle: String?
//variables for location services
var locationManager = CLLocationManager()
var locationData: LocationModel?
//variables for communication with Mediator
var QAMatcher = QuestionAnswerMediator()
var questions : [QuestionData]?
var categoryType: CategoryType!
var timer = Timer()
//Change UI depending on answer type
var uiBuilder = AnswerViewBuilder()
override func viewDidLoad() {
super.viewDidLoad()
setup(categoryType)
navigationBar.title = barTitle
QAMatcher.delegate = self
QAMatcher.getCategory(categoryType)
}
#objc func didChooseAnswer(_ sender: UIButton){
QAMatcher.nextQuestion()
updateUI()
}
}
//MARK: - Setup & updating UI
extension QuizViewController: MediatorDelegate {
func didUpdate(mediator: QuestionAnswerMediator, _: [QuestionData]) {
self.updateUI()
}
func setup(_ category: CategoryType?) {
switch category {
case .shortterm:
print("No function yet")
case .longterm:
print("No function yet")
case .problemsolving:
print("No function yet")
case .orientation:
locationManager.delegate = self
checkLocationServiceSupport()
locationManager.requestWhenInUseAuthorization()
gecodeCurrentLocation()
default:
let message: StaticString = M.Errors.invalidCategoryError
os_log(message, log: OSLog.default, type: .error)
}
}
func updateUI(){
if let label = questionLabel {
label.text = QAMatcher.getQuestion()
}
if let type = AnswerType(rawValue: QAMatcher.getAnswerType()) {
addAnswerSection(type)
}
}
func addAnswerSection(_ type: AnswerType) {
if let stackView = quizStackView, let answerSection = uiBuilder.getAnswerSection(answerType: type, didChooseAnswer: self.didChooseAnswer(_:)) {
stackView.addArrangedSubview(answerSection)
}
}
}
//MARK: - CLLocationManagerDelegate
extension QuizViewController: CLLocationManagerDelegate {
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
locationManager.stopUpdatingLocation()
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error)
{
let message: StaticString = M.Errors.locationDetectionError
os_log(message, log: OSLog.default, type: .error)
}
func checkLocationServiceSupport () {
if CLLocationManager.significantLocationChangeMonitoringAvailable() != true {
let alert = UIAlertController(title: M.Alters.locationAlertTitle, message: M.Alters.locationAlert, preferredStyle: .actionSheet)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { _ in
NSLog("The user acknowledge that location services are not available.")
}))
self.present(alert, animated: true, completion: nil)
self.dismiss(animated: true, completion: nil)
}
}
func gecodeCurrentLocation() {
locationManager.requestLocation()
if let lastLocation = self.locationManager.location {
let geocoder = CLGeocoder()
geocoder.reverseGeocodeLocation(lastLocation) { (placemarks, error) in
if error == nil {
if let placemark = placemarks?.first {
let city = placemark.locality!
let country = placemark.country!
self.locationData = LocationModel(city: city,country: country)
}
}
else {
let message: StaticString = M.Errors.locationDecodingError
os_log(message, log: OSLog.default, type: .error)
}
}
}
}
}
//MARK: - UITextFieldDelegate
//extension CategoryViewController: UITextFieldDelegate {
//
// func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
// if textField.text != "" {
// return true
// }
// else {
// textField.placeholder = "Enter your answer here"
// return false
// }
// }
//
// func textFieldDidEndEditing(_ textField: UITextField) {
// }
//
// func textFieldShouldReturn(_ textField: UITextField) -> Bool {
// textField.endEditing(true)
// return true
// }
//}
MulitpleChoiceView
import UIKit
class MultipleChoiceView: UIStackView {
var button1 = UIButton()
var button2 = UIButton()
var button3 = UIButton()
var didChooseAnswer : ((UIButton)->())?
init(frame: CGRect, didChooseAnswer: #escaping (UIButton)->()) {
super.init(frame: frame)
self.didChooseAnswer = didChooseAnswer
self.addAnswerView()
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func addAnswerView() {
setupStackView()
costumeButtons()
}
func setupStackView() {
self.alignment = .fill
self.distribution = .fillEqually
self.spacing = 10
self.axis = .vertical
}
func costumeButtons() {
let buttons = [button1 , button2, button3]
let dimensions = CGRect(x: 0, y: 0, width: 120, height: 20)
let color = #colorLiteral(red: 0.7294117647, green: 0.7450980392, blue: 1, alpha: 1)
for button in buttons {
button.frame = dimensions
button.backgroundColor = color
button.layer.cornerRadius = button.frame.size.height / 4
button.isUserInteractionEnabled = true
button.addTarget(self, action: M.Selector.buttonAction, for: .touchUpInside)
self.addArrangedSubview(button)
}
}
#objc func didChooseAnswer(_ sender: UIButton) {
if let didChooseAnswer = didChooseAnswer {
didChooseAnswer(sender)
}
}
}
AnswerViewBuilder
import UIKit
class AnswerViewBuilder {
func getAnswerSection(answerType: AnswerType, didChooseAnswer: #escaping (UIButton)->()) -> UIView?{
switch (answerType) {
case .multipleChoice:
return MultipleChoiceView(frame: CGRect(x: 0, y: 0, width: 100, height: 100), didChooseAnswer: didChooseAnswer)
case .textField:
return TextFieldView()
case .polarQuestion:
return PolarQuestionView()
case .ranking:
return MultipleChoiceView(frame: CGRect(x: 0, y: 0, width: 100, height: 100), didChooseAnswer: didChooseAnswer)
default:
print("Answer section could not be created.")
return nil
}
}
}

How to check if device is having poor internet connection in swift

I want to check if device is having very slow internet connection.
I have used Reachability class to check for internet connection is available or not.
But here i want to check after every few sec that internet speed is not poor.
Is this possible to find and yes than how can i do it.
I have founded solution and adding it here.
Simply create class and paste it and use it where you want.
protocol NetworkSpeedProviderDelegate: class {
func callWhileSpeedChange(networkStatus: NetworkStatus)
}
public enum NetworkStatus :String
{case poor; case good; case disConnected}
class NetworkSpeedTest: UIViewController {
weak var delegate: NetworkSpeedProviderDelegate?
var startTime = CFAbsoluteTime()
var stopTime = CFAbsoluteTime()
var bytesReceived: CGFloat = 0
var testURL:String?
var speedTestCompletionHandler: ((_ megabytesPerSecond: CGFloat, _ error: Error?) -> Void)? = nil
var timerForSpeedTest:Timer?
func networkSpeedTestStart(UrlForTestSpeed:String!){
testURL = UrlForTestSpeed
timerForSpeedTest = Timer.scheduledTimer(timeInterval: 60.0, target: self, selector: #selector(testForSpeed), userInfo: nil, repeats: true)
}
func networkSpeedTestStop(){
timerForSpeedTest?.invalidate()
}
#objc func testForSpeed()
{
testDownloadSpeed(withTimout: 2.0, completionHandler: {(_ megabytesPerSecond: CGFloat, _ error: Error?) -> Void in
print("%0.1f; KbPerSec = \(megabytesPerSecond)")
if (error as NSError?)?.code == -1009
{
self.delegate?.callWhileSpeedChange(networkStatus: .disConnected)
}
else if megabytesPerSecond == -1.0
{
self.delegate?.callWhileSpeedChange(networkStatus: .poor)
}
else
{
self.delegate?.callWhileSpeedChange(networkStatus: .good)
}
})
}
}
extension NetworkSpeedTest: URLSessionDataDelegate, URLSessionDelegate {
func testDownloadSpeed(withTimout timeout: TimeInterval, completionHandler: #escaping (_ megabytesPerSecond: CGFloat, _ error: Error?) -> Void) {
// you set any relevant string with any file
let urlForSpeedTest = URL(string: testURL!)
startTime = CFAbsoluteTimeGetCurrent()
stopTime = startTime
bytesReceived = 0
speedTestCompletionHandler = completionHandler
let configuration = URLSessionConfiguration.ephemeral
configuration.timeoutIntervalForResource = timeout
let session = URLSession(configuration: configuration, delegate: self, delegateQueue: nil)
guard let checkedUrl = urlForSpeedTest else { return }
session.dataTask(with: checkedUrl).resume()
}
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
bytesReceived += CGFloat(data.count)
stopTime = CFAbsoluteTimeGetCurrent()
}
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
let elapsed = (stopTime - startTime) //as? CFAbsoluteTime
let speed: CGFloat = elapsed != 0 ? bytesReceived / (CGFloat(CFAbsoluteTimeGetCurrent() - startTime)) / 1024.0 : -1.0
// treat timeout as no error (as we're testing speed, not worried about whether we got entire resource or not
if error == nil || ((((error as NSError?)?.domain) == NSURLErrorDomain) && (error as NSError?)?.code == NSURLErrorTimedOut) {
speedTestCompletionHandler?(speed, nil)
}
else {
speedTestCompletionHandler?(speed, error)
}
}
}
After That how to use it.So implement delegate and use it.
class ViewController: UIViewController, NetworkSpeedProviderDelegate {
func callWhileSpeedChange(networkStatus: NetworkStatus) {
switch networkStatus {
case .poor:
break
case .good:
break
case .disConnected:
break
}
}
let test = NetworkSpeedTest()
override func viewDidLoad() {
super.viewDidLoad()
test.delegate = self
test.networkSpeedTestStop()
test.networkSpeedTestStart(UrlForTestSpeed: "Paste Your Any Working URL ")
// Do any additional setup after loading the view.
}
}
You can call the Reachability class every time after a fixed interval by using method given below:
override func viewDidLoad() {
scheduledTimerWithTimeInterval()
}
func scheduledTimerWithTimeInterval(){
// Scheduling timer to Call the function "updateCounting" with the interval of 'x' seconds
timer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self.updateCounting), userInfo: nil, repeats: true)
}
#objc func updateCounting(){
\\Do your stuff here(Check Reachabilty Here)
}
EDIT:
This is how you can check signal strength for cellular networks.
func getSignalStrength() -> Int {
let application = UIApplication.shared
let statusBarView = application.value(forKey: "statusBar") as! UIView
let foregroundView = statusBarView.value(forKey: "foregroundView") as! UIView
let foregroundViewSubviews = foregroundView.subviews
var dataNetworkItemView:UIView? = nil
for subview in foregroundViewSubviews {
if subview.isKind(of: NSClassFromString("UIStatusBarSignalStrengthItemView")!) {
dataNetworkItemView = subview
break
}
}
if dataNetworkItemView == nil
{
return 0
}
return dataNetworkItemView?.value(forKey: "signalStrengthBars") as! Int
}
For Wifi Network this is how you can get signal strength
private func getWiFiRSSI() -> Int? {
let app = UIApplication.shared
var rssi: Int?
let exception = tryBlock {
guard let statusBar = app.value(forKey: "statusBar") as? UIView else { return }
if let statusBarMorden = NSClassFromString("UIStatusBar_Modern"), statusBar .isKind(of: statusBarMorden) { return }
guard let foregroundView = statusBar.value(forKey: "foregroundView") as? UIView else { return }
for view in foregroundView.subviews {
if let statusBarDataNetworkItemView = NSClassFromString("UIStatusBarDataNetworkItemView"), view .isKind(of: statusBarDataNetworkItemView) {
if let val = view.value(forKey: "wifiStrengthRaw") as? Int {
rssi = val
break
}
}
}
}
if let exception = exception {
print("getWiFiRSSI exception: \(exception)")
}
return rssi
}
EDIT 2: Add this extension to access your status bar view
extension UIApplication {
var statusBarUIView: UIView? {
if #available(iOS 13.0, *) {
let tag = 38482458385
if let statusBar = self.keyWindow?.viewWithTag(tag) {
return statusBar
} else {
let statusBarView = UIView(frame: UIApplication.shared.statusBarFrame)
statusBarView.tag = tag
self.keyWindow?.addSubview(statusBarView)
return statusBarView
}
} else {
if responds(to: Selector(("statusBar"))) {
return value(forKey: "statusBar") as? UIView
}
}
return nil
}
}

Is it possible to use AKAmplitudeTracker on AKAudioPlayer and measure the amplitude changes?

Hey I am working on a project(which is in swift) and it compares two audio signals and measure the correctness. AUDIOKIT pod is used to convert the audio from microphone(AKAmplitudeTracker) to float numbers. I am trying to implement the same method by applying the tracker on the AKAudioPlayer. What I am trying to do is performing sampling on the source signal and the reference signal and get it as amplitude data only, and then performing DTW(Dynamic time warping) algorithm.
Is there any means by which I can get the AKAudioPlayer music to be converted as amplitude data? Is it possible to add a tracker to the AKAudioPlayer currently playing music? Codes are given below. I need some expert advices, Thanks in advance and Happy coding.
//
// Conductor.swift
// AmplitudeTracker
//
// Created by Mark Jeschke on 10/3/17.
// Copyright © 2017 Mark Jeschke. All rights reserved.
//
import AudioKit
import AudioKitUI
// Treat the conductor like a manager for the audio engine.
class Conductor {
// Singleton of the Conductor class to avoid multiple instances of the audio engine
var url:URL?
var fileName:String?
var type:String?
static let sharedInstance = Conductor()
var isPlayingKit:Bool?
var micTracker: AKAmplitudeTracker!
var mp3Tracker: AKAmplitudeTracker!
var player:AKAudioPlayer!
var mic: AKMicrophone!
var delay: AKDelay!
var reverb: AKCostelloReverb!
// Balance between the delay and reverb mix.
var reverbAmountMixer = AKDryWetMixer()
func play(file: String, type: String) -> AKAudioPlayer? {
let url = Bundle.main.url(forResource: file, withExtension: type)
let file = try! AKAudioFile(forReading: url!)
player = try! AKAudioPlayer(file: file)
if self.isPlayingKit! {
player.play()
mp3Tracker = AKAmplitudeTracker(player)
delay = AKDelay(mp3Tracker)
delay.time = 0.0
delay.feedback = 0.0
delay.dryWetMix = 0.5
reverb = AKCostelloReverb(delay)
reverb.presetShortTailCostelloReverb()
reverbAmountMixer = AKDryWetMixer(delay, reverb, balance: 0.8)
AudioKit.output = reverbAmountMixer
}
else {
self.isPlayingKit = true
AudioKit.output = nil
player.stop()
}
return player
}
init() {
AKSettings.playbackWhileMuted = true
mic = AKMicrophone()
print("INIT CONDUCTOR")
micTracker = AKAmplitudeTracker(mic)
delay = AKDelay(micTracker)
delay.time = 0.5
delay.feedback = 0.1
delay.dryWetMix = 0.5
reverb = AKCostelloReverb(delay)
reverb.presetShortTailCostelloReverb()
reverbAmountMixer = AKDryWetMixer(delay, reverb, balance: 0.8)
AudioKit.output = reverbAmountMixer
isPlayingKit = true
startAudioEngine()
}
func startAudioEngine() {
AudioKit.start()
isPlayingKit = true
print("Audio engine started")
}
func stopAudioEngine() {
AudioKit.stop()
isPlayingKit = false
print("Audio engine stopped")
}
}
The above mentioned method captures the amplitude of the microphone.
The below given is the location where I tried to use AKAmplitudeTracker on AKAudioPlayer.
//
// ViewController.swift
// AudioBoom
//
// Created by Alex Babu on 20/06/18.
// Copyright © 2018 Naico. All rights reserved.
//
import AudioKit
class ViewController: UIViewController {
var instantanousAmplitudeData = [Double]()
var timer:Timer?
var timerCount:Int?
let conductor = Conductor.sharedInstance
var player:AKAudioPlayer?
#IBOutlet weak var holderView: UIView!
#IBOutlet weak var equalizer: UILabel!
#IBOutlet weak var percentageLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
timerCount = 0
playMusicOutlet.layer.cornerRadius = playMusicOutlet.frame.size.height/2
playMusicOutlet.layer.borderColor = UIColor.cyan.cgColor
playMusicOutlet.layer.borderWidth = 2.0
playMusicOutlet.clipsToBounds = true
musicDTW.layer.cornerRadius = musicDTW.frame.size.height/2
musicDTW.layer.borderColor = UIColor.cyan.cgColor
musicDTW.layer.borderWidth = 2.0
musicDTW.clipsToBounds = true
holderView.layer.cornerRadius = holderView.frame.size.width/2
holderView.clipsToBounds = true
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBOutlet weak var playMusicOutlet: UIButton!
#IBAction func playMusic(_ sender: Any) {
playMusicOutlet.setTitle("Talk now", for: .normal)
self.equalizer.isHidden = false
timerCount = 0
AVAudioSession.sharedInstance().requestRecordPermission({(_ granted: Bool) -> Void in
if granted {
print("Permission granted")
self.timer = Timer.scheduledTimer(withTimeInterval: 0.05, repeats: true) { [unowned self] (timer) in
if let count = self.timerCount {
DispatchQueue.main.async {
self.timerCount = count + 1
print("Amplitude of mic detected:\(self.conductor.micTracker.amplitude)")
print("Amplitude of mic counter:\(String(describing: count))")
print("Amplitude of mp3 detected:\(self.conductor.micTracker.amplitude)")
print("Amplitude of mp3 Counter:\(String(describing: count))")
self.instantanousAmplitudeData.append(self.conductor.micTracker.amplitude)
self.equalizer.frame.size.height = CGFloat(self.conductor.micTracker.amplitude * 500)
self.percentageLabel.text = String(Int(((self.conductor.micTracker.amplitude * 500)/500) * 100)) + "%"
if count == 10000 {
timer.invalidate()
self.equalizer.isHidden = true
}
}
}
}
}
else {
print("Permission denied")
}
})
}
#IBOutlet weak var musicDTW: UIButton!
#IBAction func musicDTWAction(_ sender: Any) {
let anotherConductor = Conductor.sharedInstance
if let ccc = anotherConductor.play(file: "Timebomb", type: "mp3") {
musicDTW.setTitle("Music DTW on", for: .normal)
if let mp3Tracker = conductor.mp3Tracker {
self.equalizer.frame.size.height = CGFloat(mp3Tracker.amplitude * 500)
}
}
else {
musicDTW.setTitle("Music DTW off", for: .normal)
}
}
}
There's a lot going on with all this code, so its hard to debug it but what you describe is definitely possible and you probably just have some small thing wrong. Perhaps you can share the repo with me privately and I can fix it for you.
Try these out!
Conductor Class
import AudioKit
import AudioKitUI
// Treat the conductor like a manager for the audio engine.
class Conductor {
// Singleton of the Conductor class to avoid multiple instances of the audio engine
var url:URL?
var fileName:String?
var type:String?
static let sharedInstance = Conductor()
var isPlayingKit:Bool?
var micTracker: AKAmplitudeTracker!
var mp3Tracker: AKAmplitudeTracker!
var player:AKAudioPlayer!
var mic: AKMicrophone!
var delay: AKDelay!
var reverb: AKCostelloReverb!
// Balance between the delay and reverb mix.
var reverbAmountMixer = AKDryWetMixer()
func play(file: String, type: String) -> AKAudioPlayer? {
let url = Bundle.main.url(forResource: file, withExtension: type)
let file = try! AKAudioFile(forReading: url!)
player = try! AKAudioPlayer(file: file)
if self.isPlayingKit! {
mp3Tracker = AKAmplitudeTracker(player)
delay = AKDelay(mp3Tracker)
delay.time = 0.0
delay.feedback = 0.0
delay.dryWetMix = 0.5
reverb = AKCostelloReverb(delay)
reverb.presetShortTailCostelloReverb()
reverbAmountMixer = AKDryWetMixer(delay, reverb, balance: 0.8)
AudioKit.output = reverbAmountMixer //#1
player.play() //#2
}
else {
self.isPlayingKit = true
AudioKit.output = nil
// player.stop()
stopAudioEngine()
}
return player
}
func isPlayingAudioKit() -> Bool {
return isPlayingKit!
}
init() {
self.isPlayingKit = false
}
func initMicrophone() {
AKSettings.playbackWhileMuted = true
mic = AKMicrophone()
print("INIT CONDUCTOR")
micTracker = AKAmplitudeTracker(mic)
delay = AKDelay(micTracker)
delay.time = 1.5
delay.feedback = 0.1
delay.dryWetMix = 1.0
reverb = AKCostelloReverb(delay)
reverb.presetShortTailCostelloReverb()
reverbAmountMixer = AKDryWetMixer(delay, reverb, balance: 0.5)
AudioKit.output = reverbAmountMixer
isPlayingKit = true
startAudioEngine()
}
func startAudioEngine() {
AudioKit.start()
isPlayingKit = true
print("Audio engine started")
}
func stopAudioEngine() {
AudioKit.stop()
isPlayingKit = false
print("Audio engine stopped")
}
}
ViewController
import AudioKit
class ViewController: UIViewController {
var instantanousUserAudioData = [Float]()
var referenceAudioData = [Float]()
var timer:Timer?
var timerCount:Int?
let conductor = Conductor.sharedInstance
#IBOutlet weak var holderView: UIView!
#IBOutlet weak var equalizer: UILabel!
#IBOutlet weak var percentageLabel: UILabel!
#IBOutlet weak var timerOutlet: UIButton!
#IBOutlet weak var micOutlet: UIButton!
#IBOutlet weak var DTWOutlet: UIButton!
#IBOutlet weak var musicOutlet: UIButton!
#IBOutlet weak var resultLabel: UILabel!
#IBAction func timerAction(_ sender: Any) {
self.timer?.invalidate()
}
override func viewDidLoad() {
super.viewDidLoad()
timerCount = 0
micOutlet.layer.cornerRadius = micOutlet.frame.size.height/2
micOutlet.layer.borderColor = UIColor.cyan.cgColor
micOutlet.layer.borderWidth = 2.0
micOutlet.clipsToBounds = true
musicOutlet.layer.cornerRadius = musicOutlet.frame.size.height/2
musicOutlet.layer.borderColor = UIColor.cyan.cgColor
musicOutlet.layer.borderWidth = 2.0
musicOutlet.clipsToBounds = true
DTWOutlet.layer.cornerRadius = DTWOutlet.frame.size.height/2
DTWOutlet.layer.borderColor = UIColor.cyan.cgColor
DTWOutlet.layer.borderWidth = 2.0
DTWOutlet.clipsToBounds = true
timerOutlet.layer.cornerRadius = timerOutlet.frame.size.height/2
timerOutlet.layer.borderColor = UIColor.cyan.cgColor
timerOutlet.layer.borderWidth = 2.0
timerOutlet.clipsToBounds = true
holderView.layer.cornerRadius = holderView.frame.size.width/2
holderView.clipsToBounds = true
self.micOutlet.isEnabled = false
self.musicOutlet.isEnabled = false
AVAudioSession.sharedInstance().requestRecordPermission({(_ granted: Bool) -> Void in
self.micOutlet.isEnabled = granted
self.musicOutlet.isEnabled = granted
})
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return .lightContent
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func micAction(_ sender: Any) {
conductor.initMicrophone()
self.timerCount = 0
self.equalizer.isHidden = false
self.timer = Timer.scheduledTimer(withTimeInterval: 0.05, repeats: true) { [unowned self] (timer) in
if let count = self.timerCount {
DispatchQueue.main.async {
self.timerCount = count + 1
print("Amplitude of mic detected:\(self.conductor.micTracker.amplitude)")
print("Amplitude of mp3 Counter:\(String(describing: count))")
self.instantanousUserAudioData.append(Float(self.conductor.micTracker.amplitude))
self.equalizer.frame.size.height = CGFloat(self.conductor.micTracker.amplitude * 500)
self.percentageLabel.text = String(Int(((self.conductor.micTracker.amplitude * 500)/500) * 100)) + "%"
if count > 10 && self.conductor.micTracker.amplitude == 0.0 && self.instantanousUserAudioData.last == 0.0 {
self.micOutlet.backgroundColor = .green
self.micOutlet.setTitleColor(.black, for: .normal)
self.micOutlet.layer.borderColor = UIColor.red.cgColor
timer.invalidate()
}
if count == 0 {
self.micOutlet.backgroundColor = .clear
self.micOutlet.setTitleColor(.cyan, for: .normal)
self.micOutlet.layer.borderColor = UIColor.cyan.cgColor
}
}
}
}
}
#IBAction func musicAction(_ sender: Any) {
self.timerCount = 0
if self.conductor.play(file: voiceReference, type: type_mp3) != nil {
self.timer?.invalidate()
self.timer = Timer.scheduledTimer(withTimeInterval: 0.05, repeats: true) { [unowned self] (timer) in
if let count = self.timerCount {
DispatchQueue.main.async {
self.timerCount = count + 1
print("Amplitude of mp3 detected:\(self.conductor.mp3Tracker.amplitude)")
print("Amplitude of mp3 Counter:\(String(describing: count))")
self.referenceAudioData.append(Float(self.conductor.mp3Tracker.amplitude))
self.equalizer.frame.size.height = CGFloat(self.conductor.mp3Tracker.amplitude * 500)
self.equalizer.isHidden = false
self.percentageLabel.text = String(Int(((self.conductor.mp3Tracker.amplitude * 500)/500) * 100)) + "%"
if count > 10 && self.conductor.mp3Tracker.amplitude == 0.0 && self.referenceAudioData.last == 0.0 {
self.musicOutlet.backgroundColor = .green
self.musicOutlet.setTitleColor(.black, for: .normal)
self.musicOutlet.layer.borderColor = UIColor.red.cgColor
timer.invalidate()
}
if count == 0 {
self.musicOutlet.backgroundColor = .clear
self.musicOutlet.setTitleColor(.cyan, for: .normal)
self.musicOutlet.layer.borderColor = UIColor.cyan.cgColor
}
}
}
}
}
else {
}
}
#IBAction func resultAction(_ sender: Any) {
print("mic array:\(instantanousUserAudioData)")
print("song array:\(referenceAudioData)")
self.timer?.invalidate()
if referenceAudioData.count > 0, instantanousUserAudioData.count > 0 {
let refData = knn_curve_label_pair(curve: referenceAudioData,label: "reference")
let userData = knn_curve_label_pair(curve: instantanousUserAudioData,label: "userData")
let attempt:KNNDTW = KNNDTW()
attempt.train(data_sets: [refData,userData])
let prediction: knn_certainty_label_pair = attempt.predict(curve_to_test: referenceAudioData)
print("predicted :" + prediction.label, "with ", prediction.probability * 100,"% certainty")
resultLabel.text = "DTW cost is " + String(attempt.dtw_cost(y: referenceAudioData, x: instantanousUserAudioData))
print("COST OF THE DTW ALGORITHM IS : \(String(attempt.dtw_cost(y: referenceAudioData, x: instantanousUserAudioData)))")
}
}
}

Trying to combine 2 Audiokit examples: MicrophoneAnalysis and Recorder: crashes when I hit record

I am trying to combine MicrophoneAnalysis and Recorder examples. It keeps crashing at the line try recorder.record.
2018-01-08 21:21:48.507019-0800 Music Practice[90266:18761122] [avae]
AVAEInternal.h:70:_AVAE_Check: required condition is false: [AVAEGraphNode.mm:804:CreateRecordingTap: (nullptr == Tap())]
2018-01-08 21:21:48.527443-0800 Music Practice[90266:18761122] * Terminating app due to uncaught exception 'com.apple.coreaudio.avfaudio', reason: 'required condition is false: nullptr == Tap()'
* First throw call stack:
// -----------
import AudioKit
import AudioKitUI
import UIKit
class SecondViewController: UIViewController {
#IBOutlet private var inputPlot: AKNodeOutputPlot!
#IBOutlet weak var tempViewForRecordingAndPlay: UIView!
#IBOutlet weak var outputWavePlot: AKOutputWaveformPlot!
// for microphone Analysis
#IBOutlet weak var frequencyLabel: UILabel!
#IBOutlet weak var amplitudeLabel: UILabel!
#IBOutlet weak var noteNameWithSharpsLabel: UILabel!
#IBOutlet weak var noteNameWithFlatsLabel: UILabel!
#IBOutlet private var audioInputPlot: EZAudioPlot!
var micMixer: AKMixer!
var recorder: AKNodeRecorder!
var player: AKAudioPlayer!
var tape: AKAudioFile!
var micBooster: AKBooster!
var moogLadder: AKMoogLadder!
var delay: AKDelay!
var mainMixer: AKMixer!
let mic = AKMicrophone()
var state = State.readyToRecord
#IBOutlet private weak var infoLabel: UILabel!
#IBOutlet private weak var resetButton: UIButton!
#IBOutlet private weak var RecordOrPlay_Btn: UIButton!
#IBOutlet private weak var frequencySlider: AKSlider!
#IBOutlet private weak var resonanceSlider: AKSlider!
#IBOutlet private weak var loopButton: UIButton!
#IBOutlet private weak var moogLadderTitle: UILabel!
enum State {
case readyToRecord
case recording
case readyToPlay
case playing
}
var plot: AKNodeOutputPlot!
var micNew: AKMicrophone!
var tracker: AKFrequencyTracker!
var silence: AKBooster!
let noteFrequencies = [16.35, 17.32, 18.35, 19.45, 20.6, 21.83, 23.12, 24.5, 25.96, 27.5, 29.14, 30.87]
let noteNamesWithSharps = ["C", "C♯", "D", "D♯", "E", "F", "F♯", "G", "G♯", "A", "A♯", "B"]
let noteNamesWithFlats = ["C", "D♭", "D", "E♭", "E", "F", "G♭", "G", "A♭", "A", "B♭", "B"]
#objc func updateUI() {
if tracker.amplitude > 0.1 {
frequencyLabel.text = String(format: "%0.1f", tracker.frequency)
var frequency = Float(tracker.frequency)
while frequency > Float(noteFrequencies[noteFrequencies.count - 1]) {
frequency /= 2.0
}
while frequency < Float(noteFrequencies[0]) {
frequency *= 2.0
}
var minDistance: Float = 10_000.0
var index = 0
for i in 0..<noteFrequencies.count {
let distance = fabsf(Float(noteFrequencies[i]) - frequency)
if distance < minDistance {
index = i
minDistance = distance
}
}
let octave = Int(log2f(Float(tracker.frequency) / frequency))
noteNameWithSharpsLabel.text = "\(noteNamesWithSharps[index])\(octave)"
noteNameWithFlatsLabel.text = "\(noteNamesWithFlats[index])\(octave)"
}
amplitudeLabel.text = String(format: "%0.2f", tracker.amplitude)
}
#if NOT_USED
func setupPlot() {
plot = AKNodeOutputPlot(micNew, frame: audioInputPlot.bounds)
plot.plotType = .rolling
plot.shouldFill = true
plot.shouldMirror = true
plot.color = UIColor.blue
audioInputPlot.addSubview(plot)
}
#endif
func setupPlot_forMic() {
plot = AKNodeOutputPlot(micMixer, frame: audioInputPlot.bounds)
plot.plotType = .rolling
plot.shouldFill = true
plot.shouldMirror = true
plot.color = UIColor.red
audioInputPlot.addSubview(plot)
}
func execute_viewDidAppear_micAnalysis() {
//AudioKit.output = silence
//AudioKit.start()
setupPlot_forMic()
Timer.scheduledTimer(timeInterval: 0.1,
target: self,
selector: #selector(SecondViewController.updateUI),
userInfo: nil,
repeats: true)
}
//view DID APPEAR
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
execute_viewDidAppear_micAnalysis()
}
// View DID DOWNLOAD
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.tempViewForRecordingAndPlay.addSubview(inputPlot);
self.tempViewForRecordingAndPlay.addSubview(outputWavePlot);
//parentView.bringSubview(toFront: childView)
tempViewForRecordingAndPlay.bringSubview(toFront: inputPlot);
tempViewForRecordingAndPlay.bringSubview(toFront: outputWavePlot);
recorderPlayerSettings()
#if TEMP
#else
micAnalysisSettings()
#endif
}
func recorderPlayerSettings() {
setupButtonNames()
// Clean tempFiles!
AKAudioFile.cleanTempDirectory()
// Session settings
AKSettings.bufferLength = .medium
do {
try AKSettings.setSession(category: .playAndRecord, with: .allowBluetoothA2DP)
} catch {
AKLog("Could not set session category.")
}
AKSettings.defaultToSpeaker = true
// Patching
inputPlot.node = mic
micMixer = AKMixer(mic)
micBooster = AKBooster(micMixer)
//play(from: innerTime, to: endTime, when: 0)
// passing 0 for endTime will use the duration.
// Will set the level of microphone monitoring
micBooster.gain = 0
recorder = try? AKNodeRecorder(node: micMixer)
if let file = recorder.audioFile {
player = try? AKAudioPlayer(file: file)
}
player.looping = true
player.completionHandler = playingEnded
moogLadder = AKMoogLadder(player)
mainMixer = AKMixer(moogLadder, micBooster)
AudioKit.output = mainMixer
AudioKit.start()
setupUIForRecording()
}
func micAnalysisSettings() {
AKSettings.audioInputEnabled = true
//micNew = AKMicrophone()
tracker = AKFrequencyTracker(micMixer)
//tracker = AKFrequencyTracker(mic)
silence = AKBooster(tracker, gain: 0)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// CallBack triggered when playing has ended
// Must be seipatched on the main queue as completionHandler
// will be triggered by a background thread
func playingEnded() {
DispatchQueue.main.async {
self.setupUIForPlaying ()
}
}
#IBAction func RecordOrPlay_BtnTouched(_ sender: UIButton) {
switch state {
case .readyToRecord :
infoLabel.text = "Recording"
RecordOrPlay_Btn.setTitle("Stop", for: .normal)
state = .recording
// microphone will be monitored while recording
// only if headphones are plugged
if AKSettings.headPhonesPlugged {
micBooster.gain = 1
}
do {
try recorder.record()
} catch { print("Errored recording.") }
case .recording :
// Microphone monitoring is muted
micBooster.gain = 0
do {
try player.reloadFile()
} catch { print("Errored reloading.") }
let recordedDuration = player != nil ? player.audioFile.duration : 0
if recordedDuration > 0.0 {
recorder.stop()
player.audioFile.exportAsynchronously(name: "TempTestFile.m4a",
baseDir: .documents,
exportFormat: .m4a) {_, exportError in
if let error = exportError {
print("Export Failed \(error)")
} else {
print("Export succeeded")
}
}
setupUIForPlaying ()
}
case .readyToPlay :
player.play()
infoLabel.text = "Playing..."
RecordOrPlay_Btn.setTitle("Stop", for: .normal)
state = .playing
case .playing :
player.stop()
setupUIForPlaying()
}
}
struct Constants {
static let empty = ""
}
func setupButtonNames() {
resetButton.setTitle(Constants.empty, for: UIControlState.disabled)
RecordOrPlay_Btn.setTitle(Constants.empty, for: UIControlState.disabled)
loopButton.setTitle(Constants.empty, for: UIControlState.disabled)
}
func setupUIForRecording () {
state = .readyToRecord
infoLabel.text = "Ready to record"
RecordOrPlay_Btn.setTitle("Record", for: .normal)
resetButton.isEnabled = false
resetButton.isHidden = true
micBooster.gain = 0
setSliders(active: false)
}
func setupUIForPlaying () {
let recordedDuration = player != nil ? player.audioFile.duration : 0
infoLabel.text = "Recorded: \(String(format: "%0.1f", recordedDuration)) seconds"
RecordOrPlay_Btn.setTitle("Play", for: .normal)
state = .readyToPlay
resetButton.isHidden = false
resetButton.isEnabled = true
setSliders(active: true)
frequencySlider.value = moogLadder.cutoffFrequency
resonanceSlider.value = moogLadder.resonance
}
func setSliders(active: Bool) {
loopButton.isEnabled = active
moogLadderTitle.isEnabled = active
frequencySlider.callback = updateFrequency
frequencySlider.isHidden = !active
resonanceSlider.callback = updateResonance
resonanceSlider.isHidden = !active
frequencySlider.range = 10 ... 2_000
moogLadderTitle.text = active ? "Moog Ladder Filter" : Constants.empty
}
#IBAction func loopButtonTouched(_ sender: UIButton) {
if player.looping {
player.looping = false
sender.setTitle("Loop is Off", for: .normal)
} else {
player.looping = true
sender.setTitle("Loop is On", for: .normal)
}
}
func updateFrequency(value: Double) {
moogLadder.cutoffFrequency = value
frequencySlider.property = "Frequency"
frequencySlider.format = "%0.0f"
}
func updateResonance(value: Double) {
moogLadder.resonance = value
resonanceSlider.property = "Resonance"
resonanceSlider.format = "%0.3f"
}
#IBAction func resetEverything(_ sender: Any) {
player.stop()
do {
try recorder.reset()
} catch { print("Errored resetting.") }
//try? player.replaceFile((recorder.audioFile)!)
setupUIForRecording()
}
// convert to a generic view animate function and dissociate from the button
#IBAction func animateButton(_ sender: UIButton) {
UIView.animate(withDuration: 0.5, delay: 0.0, options: UIViewAnimationOptions.curveEaseIn, animations: {
//Frame Option 1:
self.tempViewForRecordingAndPlay.frame = CGRect(x: self.tempViewForRecordingAndPlay.frame.origin.x, y: 20, width: self.tempViewForRecordingAndPlay.frame.width, height: self.tempViewForRecordingAndPlay.frame.height)
//Frame Option 2:
self.tempViewForRecordingAndPlay.center = CGPoint(x: self.view.frame.width / 2, y: self.view.frame.height / 4)
//self.tempViewForRecordingAndPlay.backgroundColor = .blue
},completion: { finish in
/*
UIView.animate(withDuration: 1, delay: 0.25,options: UIViewAnimationOptions.curveEaseOut,animations: {
self.tempViewForRecordingAndPlay.backgroundColor = .orange
self.tempViewForRecordingAndPlay.transform = CGAffineTransform(scaleX: 0.25, y: 0.25)
//self.animationButton.isEnabled = false // If you want to restrict the button not to repeat animation..You can enable by setting into true
},completion: nil)})
*/
UIView.animate(withDuration: 1.0, delay: 0.25, usingSpringWithDamping:
0.6, initialSpringVelocity: 0.3, options:
UIViewAnimationOptions.allowAnimatedContent, animations: { () -> Void in
//do actual move
self.tempViewForRecordingAndPlay.center = self.tempViewForRecordingAndPlay.center
}, completion: nil)})
}
You probably already have a tap on the bus and you can not have another on the same bus.
Try to micMixer.outputNode.removeTap(onBus: 0) before You calling recorder.record().

Save highscore in swift

I gave this code, and I want to add highscore to this project. I wa trying to do this on my own but can't get this to working. Can somebody help me with achieve that ?
I was trying to manipulate with timeInterval but with no luck, this is my learning project from GitHub, I Was trying to ask author to help me but he didn't answer.
fileprivate extension ViewController {
func setupPlayerView() {
playerView.bounds.size = CGSize(width: radius * 2, height: radius * 2)
playerView.layer.cornerRadius = radius
playerView.backgroundColor = #colorLiteral(red: 1, green: 1, blue: 1, alpha: 1)
view.addSubview(playerView)
}
func startEnemyTimer() {
enemyTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(generateEnemy(timer:)), userInfo: nil, repeats: true)
}
func stopEnemyTimer() {
guard let enemyTimer = enemyTimer,
enemyTimer.isValid else {
return
}
enemyTimer.invalidate()
}
func startDisplayLink() {
displayLink = CADisplayLink(target: self, selector: #selector(tick(sender:)))
displayLink?.add(to: RunLoop.main, forMode: RunLoopMode.defaultRunLoopMode)
}
func stopDisplayLink() {
displayLink?.isPaused = true
displayLink?.remove(from: RunLoop.main, forMode: RunLoopMode.defaultRunLoopMode)
displayLink = nil
}
func getRandomColor() -> UIColor {
let index = arc4random_uniform(UInt32(colors.count))
return colors[Int(index)]
}
func getEnemyDuration(enemyView: UIView) -> TimeInterval {
let dx = playerView.center.x - enemyView.center.x
let dy = playerView.center.y - enemyView.center.y
return TimeInterval(sqrt(dx * dx + dy * dy) / enemySpeed)
}
func gameOver() {
stopGame()
displayGameOverAlert()
}
func stopGame() {
stopEnemyTimer()
stopDisplayLink()
stopAnimators()
gameState = .gameOver
}
func prepareGame() {
removeEnemies()
centerPlayerView()
popPlayerView()
startLabel.isHidden = false
clockLabel.text = "00:00.000"
gameState = .ready
}
func startGame() {
startEnemyTimer()
startDisplayLink()
startLabel.isHidden = true
beginTimestamp = 0
gameState = .playing
}
func removeEnemies() {
enemyViews.forEach {
$0.removeFromSuperview()
}
enemyViews = []
}
func stopAnimators() {
playerAnimator?.stopAnimation(true)
playerAnimator = nil
enemyAnimators.forEach {
$0.stopAnimation(true)
}
enemyAnimators = []
}
func updateCountUpTimer(timestamp: TimeInterval) {
if beginTimestamp == 0 {
beginTimestamp = timestamp
}
elapsedTime = timestamp - beginTimestamp
clockLabel.text = format(timeInterval: elapsedTime)
}
func highscore(timestamp: TimeInterval) {
if beginTimestamp == 0 {
beginTimestamp = timestamp
}
elapsedTime = timestamp - beginTimestamp
highscoreLabel.text = format(timeInterval: elapsedTime)
}
func format(timeInterval: TimeInterval) -> String {
let interval = Int(timeInterval)
let seconds = interval % 60
let minutes = (interval / 60) % 60
let milliseconds = Int(timeInterval * 1000) % 1000
return String(format: "%02d:%02d.%03d", minutes, seconds, milliseconds)
}
func checkCollision() {
enemyViews.forEach {
guard let playerFrame = playerView.layer.presentation()?.frame,
let enemyFrame = $0.layer.presentation()?.frame,
playerFrame.intersects(enemyFrame) else {
return
}
gameOver()
}
}
func movePlayer(to touchLocation: CGPoint) {
playerAnimator = UIViewPropertyAnimator(duration: playerAnimationDuration,
dampingRatio: 0.5,
animations: { [weak self] in
self?.playerView.center = touchLocation
})
playerAnimator?.startAnimation()
}
func moveEnemies(to touchLocation: CGPoint) {
for (index, enemyView) in enemyViews.enumerated() {
let duration = getEnemyDuration(enemyView: enemyView)
enemyAnimators[index] = UIViewPropertyAnimator(duration: duration,
curve: .linear,
animations: {
enemyView.center = touchLocation
})
enemyAnimators[index].startAnimation()
}
}
func displayGameOverAlert() {
let (title, message) = getGameOverTitleAndMessage()
let alert = UIAlertController(title: "Game Over", message: message, preferredStyle: .alert)
let action = UIAlertAction(title: title, style: .default,
handler: { _ in
self.prepareGame()
}
)
alert.addAction(action)
self.present(alert, animated: true, completion: nil)
}
func getGameOverTitleAndMessage() -> (String, String) {
let elapsedSeconds = Int(elapsedTime) % 60
switch elapsedSeconds {
case 0..<10: return ("I try again", "You need more practice")
case 10..<30: return ("Maybe Another try?", "No bad, just play more")
case 30..<60: return ("Play again", "You are like Ninja")
default:
return ("WOW", "You are simply the best")
}
}
right know I managed to have state of score but I can't to save high score whats wrong with that
func updateBest(){
var defaults=UserDefaults()
var highscore=defaults.integer(forKey: "highscore")
if(Int(elapsedTime)) > highscore
{
defaults.set(Int(self.elapsedTime), forKey: "highscore")
}
var highscoreshow=defaults.integer(forKey: "highscore")
highscoreLabel.text = "\(highscoreshow)"
print("hhScore reported: \(highscoreLabel.text)")
// clockLabel.text = "\(elapsedTime)"
// print("play reported: \(clockLabel.text)")
}
For saving highscore to userdefaults :
let highscore : String = "\(Int(self.elapsedTime))"
UserDefaults.standard.set(highscore, forKey: "highscore")
For retrieving highscore from userdefaults :
highscoreLabel.text = (UserDefaults.standard.value(forKey: "highscore") as? String)
I hope it helps. Cheers. :]

Resources