Recording audio on WatchOS 4 - swift

At WWDC17, new recording features were announced for WatchOS 4.
I've been trying to get a PCM array, but I was not successful.
Currently I tried to use the following code:
Check AudioSession:
override func awake(withContext context: Any?) {
super.awake(withContext: context)
let recordingSession = AVAudioSession.sharedInstance()
do {
try recordingSession.setCategory(AVAudioSessionCategoryPlayAndRecord)
try recordingSession.setActive(true)
recordingSession.requestRecordPermission() { [unowned self] allowed in
DispatchQueue.main.async {
if allowed {
self.recordingSessionSuccessfulPermission()
} else {
self.recordingSessionFailPermission()
}
}
}
} catch {
self.recordingSessionFailPermission()
}
}
Start Record:
#IBAction func startRecordButton() {
label.setText("start record")
let audioEngine = AVAudioEngine.init()
let inputNode = audioEngine.inputNode
let format = inputNode.inputFormat(forBus: 0)
inputNode.installTap(onBus: 0, bufferSize: 8000, format: format) { (buf, wheb) in
self.label.setText("It is working")
}
audioEngine.prepare()
do {
try audioEngine.start()
} catch {
// Handle error...
}
}
This code throws the error:
[audiocomp] 178: registration server connection invalidated
[audiocomp] 170: registration server proxy error: Error Domain=NSCocoaErrorDomain Code=4099 "The connection to service named com.apple.audio.AudioComponentRegistrar was invalidated." UserInfo={NSDebugDescription=The connection to service named com.apple.audio.AudioComponentRegistrar was invalidated.}

The AudioEngine object must be created outside Action.
For example:
class MyClass: WKInterfaceController {
private var audioEngine = AVAudioEngine()

Related

swift show loader while reading data from firebase

i have a list of music at my firebase real time database and i am retriving them but i have 1000 musics data and i want to show loader when i reading data and stop loader when if there is a error(internet connection, or something else) or reading completed.
when i turn off the internet i couldn't get the data and can't stop loader to show error alert like there is no internet connection.
please help me how to handle that problem.
here is my code
didload function called from viewdidload()
private var musicArray = [ItemModal]() {
didSet {
view?.updateTableView()
}
}
func didLoad() {
view?.showLoader()
getAllMusics { ItemModal in
self.musicArray = ItemModal
self.view?.hideLoader()
}
}
func getAllMusics(completion: #escaping ([ItemModal]) -> Void) {
var musicArray = [ItemModal]()
ref.child("music").observeSingleEvent(of: .value) { snapshot in
let enumerator = snapshot.children
while let rest = enumerator.nextObject() as? DataSnapshot {
guard let data = try? JSONSerialization.data(withJSONObject: rest.value as Any, options: []) else { return }
if let itemModal = try? JSONDecoder().decode(ItemModal.self, from: data) {
musicArray.append(itemModal)
}
}
completion(musicArray)
}
}
You can use reachability function by using https://github.com/ashleymills/Reachability.swift. To get to notify when the internet is turned off, you can implement reachabilityChanged Notification. In the selector method of reachabilityChanged, you can hide the loader.
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(reachabilityChanged), name: .reachabilityChanged)
}
#objc func changed() {
if reachability?.isReachable {
//Continue success implementation
} else {
view?.hideLoder
//Implement Error handling
}
}

Microphone not working with CallKit VoIP calls on iOS 13

I have implemented Web RTC and it's working perfectly. The problem is that when the application is not opened—or terminated state—and when I receive a call, I am not able to pass my voice to the other user, while I am able to listen. I have configured the AVAudioSession before CXAnswer call delegate.
func configureAudioSession() {
let sharedSession = AVAudioSession.sharedInstance()
do {
try sharedSession.setCategory(AVAudioSession.Category.playAndRecord)
try sharedSession.setMode(AVAudioSession.Mode.voiceChat)
try sharedSession.setPreferredIOBufferDuration(TimeInterval(0.005))
try sharedSession.setPreferredSampleRate(44100.0)
} catch {
debugPrint("Failed to configure `AVAudioSession`")
}
func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
guard let call = ProviderDelegate.callManager.callWithUUID(action.callUUID) else {
action.fail()
return
}
configureAudioSession()
call.answer { error in
if let error = error {
print("ERROR: failed to answer: \(error.localizedDescription)")
}
action.fulfill()
}
}

Give a new url to the browser with Swift macOS

I could find a way to open a specific browser (with macOS and Swift):
#IBAction func frx(_ sender: NSButton) {
NSWorkspace.shared.open(URL(fileURLWithPath: "/Applications/Firefox.app"))
}
Is it possible to give to that Firefox window a new url in a posterior moment and reload the page? (Give the address not when I launch the application but later)
struct Firefox {
static func open(path: String) {
let ff_url = NSURL(fileURLWithPath: "/Applications/Firefox.app", isDirectory: true) as URL
if let www_url = URL(string: path) {
NSWorkspace.shared.open([www_url], withApplicationAt: ff_url, configuration: NSWorkspace.OpenConfiguration()) { app, error in
if let error = error {
// handle error
}
if let _ = app {
// handle success
}
}
} else {
// handle error
}
}
}

Image capture is slower than flash in Iphone photo capture using AVFoundation

We are using latest Swift Code and AVFoundation API's to create a camera App which has Flash, Reverse camera and Capture photo functionality. Code needs to support ios10 onwards.
Issue we are having is Camera flash appears, but doesn't get captured in the photo(basically camera flash appears a bit sooner than the photo capture or photo capture is bit slower than the flash) Which is making our flash funcitonality useless.
here is the code for our Camera Capture:
//Function to capture the image from the camera session -> this gets called from the ViewController Outlet action OnCapture
func capture() throws {
guard captureSession.isRunning else {
throw CameraRuntimeError.captureSessionIsMissing
}
let settings = AVCapturePhotoSettings()
if getCurrentCamera().isFlashAvailable {
settings.flashMode = self.flashMode
}
self.photoOutput?.capturePhoto(with: settings, delegate: self)
}
And here is the delegate:
extension CameraFunctions: AVCapturePhotoCaptureDelegate {
private static let failedToConvertToJPEGErrorCode = "JPEGERROR"
private static let failedToCaptureImage = "CAMERROR"
public func photoOutput(_ captureOutput: AVCapturePhotoOutput,
didFinishProcessingPhoto photoSampleBuffer: CMSampleBuffer?,
previewPhoto previewPhotoSampleBuffer: CMSampleBuffer?,
resolvedSettings: AVCaptureResolvedPhotoSettings,
bracketSettings: AVCaptureBracketedStillImageSettings?, error: Swift.Error?) {
if error != nil {
onPhotoCaptured(StringResult(error: ServicesError(CameraFunctions.failedToCaptureImage, error!.localizedDescription)))
}
if let buffer = photoSampleBuffer, let data = AVCapturePhotoOutput.jpegPhotoDataRepresentation(forJPEGSampleBuffer: buffer, previewPhotoSampleBuffer: nil) {
let encodedString = //DO ENCODING OF THE PHOTO
onPhotoCaptured(encodedString)
} else {
onPhotoCaptured(StringResult(error: ServicesError(CameraFunctions.failedToConvertToJPEGErrorCode, CameraRuntimeError.failedToConvertImageToJPEG.localizedDescription)))
}
closeCaptureSession()
}
}
onPhotoCaptured is present in a ViewController.
please let us know if we are doing something wrong.
setting the prepared Settings for photo output solved this issue:
func capture(_ delegate: AVCapturePhotoCaptureDelegate, _ onError: #escaping (Error) -> Void) throws {
guard captureSession.isRunning else {
throw CameraRuntimeError.captureSessionIsMissing
}
let settings: AVCapturePhotoSettings
if #available(iOS 11.0, *) {
settings = AVCapturePhotoSettings(format: [AVVideoCodecKey: AVVideoCodecType.jpeg])
settings.isAutoStillImageStabilizationEnabled = true
} else {
settings = AVCapturePhotoSettings(format: [AVVideoCodecKey: AVVideoCodecJPEG])
}
if getCurrentCamera().isFlashAvailable {
settings.flashMode = self.flashMode
}
//This statement did the magic
self.photoOutput?.setPreparedPhotoSettingsArray([settings]) { (suc: Bool, err: Error?) -> Void in
if suc {
self.photoOutput?.capturePhoto(with: settings, delegate: delegate)
}
if err != nil {
onError(err!)
}
}
}

Debugging advice for WatchOS2

I've been going through the examples in WatchOS 2 By Tutorial book by the team over at RayWenderlich, specifically chapter 18. They all work fine. In my own App, I am trying to send a button press from the watch to fire a button on the iPhone App. Here's the relevant code in Swift from the Watch and the Phone:
Watch:
//
// InterfaceController.swift
// Wasted Time Extension
//
// Created by Michael Rowe on 7/21/15.
// Copyright © 2010-2015 Michael Rowe. All rights reserved.
//
import WatchKit
import WatchConnectivity
import Foundation
class InterfaceController: WKInterfaceController,WCSessionDelegate {
#IBOutlet var wasteLabel: WKInterfaceLabel!
#IBOutlet var costLabel: WKInterfaceLabel!
#IBOutlet var counter: WKInterfaceLabel!
#IBOutlet var statusButton: WKInterfaceButton!
// our watchconnective session
var session : WCSession?
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
}
override func willActivate() {
// This method is called when watch view controller is about to be visible to user
super.willActivate()
if(WCSession.isSupported()){
session = WCSession.defaultSession()
session!.delegate = self
session!.activateSession()
}
}
override func didDeactivate() {
// This method is called when watch view controller is no longer visible
super.didDeactivate()
}
func session(session: WCSession, didReceiveMessage message: [String: AnyObject], replyHandler: [String: AnyObject] -> Void) {
print("Did receive message Watch \(message)")
}
#IBAction func addButtonPressed() {
// Pull values from the Phone for current meeting cost, waste costs, and people in meeting
let prefs:NSUserDefaults = NSUserDefaults(suiteName: "a.b.c")!
var counterd = prefs.doubleForKey("keyPeopleInMeeting")
counterd++
counter.setText(String(format:"%9.0f", counterd))
// Sending data to iPhone via Interactive Messaging
if WCSession.isSupported(){
// we have a watch supporting iPhone
let session = WCSession.defaultSession()
// we can reach the watch
if session.reachable {
let message = ["add": "1"]
print("Message \(message)")
session.transferUserInfo(message)
print("Send Message Add - People \(counterd)")
}
}
if WCSession.isSupported() {
let session = WCSession.defaultSession()
if session.reachable {
let message = ["add":"1"]
session.sendMessage(message, replyHandler: { ( reply: [String: AnyObject]) -> Void in
print("Reply: \(reply)")
}, errorHandler: { (error: NSError) -> Void in
print("ERROR Watch: \(error.localizedDescription)")
})
} else { // reachable
self.showReachabilityError()
}
}
print("Watch Add Button Pressed \(counterd)")
}
#IBAction func minusButtonPressed() {
// Pull values from the Phone for current meeting cost, waste costs, and people in meeting
let prefs:NSUserDefaults = NSUserDefaults(suiteName: "a.b.c")!
var counterd = prefs.doubleForKey("keyPeopleInMeeting")
counterd--
if (counterd <= 1) {
counterd = 1
}
counter.setText(String(format:"%9.0f", counterd))
if WCSession.isSupported() {
let session = WCSession.defaultSession()
if session.reachable {
let message = ["minus":"1"]
session.sendMessage(message, replyHandler: { ( reply: [String: AnyObject]) -> Void in
print("Reply: \(reply)")
}, errorHandler: { (error: NSError) -> Void in
print("ERROR Watch: \(error.localizedDescription)")
})
} else { // reachable
self.showReachabilityError()
}
}
print("Watch Minus Button Pressed \(counterd)")
}
func statusButtonPressed() {
// Pull values from the Phone for current meeting cost, waste costs, and people in meeting
let prefs:NSUserDefaults = NSUserDefaults(suiteName: "a.b.c")!
let status = statusButton.description
if WCSession.isSupported() {
let session = WCSession.defaultSession()
if session.reachable {
let message = ["status":status]
session.sendMessage(message, replyHandler: { ( reply: [String: AnyObject]) -> Void in
print("Reply: \(reply)")
}, errorHandler: { (error: NSError) -> Void in
print("ERROR Watch: \(error.localizedDescription)")
})
} else { // reachable
self.showReachabilityError()
}
}
print("Watch Status Button Pressed - Status \(statusButton)")
}
func session(session: WCSession, didReceiveApplicationContext applicationContext: [String : AnyObject]){
let prefs:NSUserDefaults = NSUserDefaults(suiteName: "a.b.c")!
if let waste = applicationContext["waste"] as? Float {
print("Watch Receive - Waste \(waste)")
}
if let cost = applicationContext["cost"] as? Float {
print("Watch Receive - Cost \(cost)")
}
if let counternum = applicationContext["counter"] as? Float {
print("Watch Receive - Counter \(counternum)")
}
if let status = applicationContext["status"] as? String {
print("Watch Receive - Status \(status)")
statusButton.setTitle(status)
}
}
private func showReachabilityError() {
let tryAgain = WKAlertAction(title: "Try Again", style: .Default, handler: { () -> Void in })
let cancel = WKAlertAction(title: "Cancel", style: .Cancel, handler: { () -> Void in })
self.presentAlertControllerWithTitle("Your iPhone is not reachable.", message: "You cannot adjust the status or number of attendees Watch is not currently connected to your iPhone. Please ensure your iPhone is on and within range of your Watch.", preferredStyle: WKAlertControllerStyle.Alert, actions:[tryAgain, cancel])
}
func session(session: WCSession, didFinishUserInfoTransfer userInfoTransfer: WCSessionUserInfoTransfer, error: NSError?) {
print("Transfer User Info Error watch: \(error)")
}
}
And the receiving code on the
iPhone:CODE:
func session(session: WCSession,
didReceiveMessage message: [String : AnyObject],
replyHandler: ([String : AnyObject]) -> Void) {
if let counterd = message["add"] as? Float {
let reply = ["add":counterd]
print("iPhone Receive Add \(counterd)")
addButtonPressed(self)
replyHandler(reply)
}
if let counterd = message["minus"] as? Float {
let reply = ["minus":counterd]
print("iPhone Receive minus \(counterd)")
removeButtonPressed(self)
replyHandler(reply)
}
if let status = message["status"] as? String {
if status == "Start" {
let reply = ["status":"Quorum"]
meetingStartedButtonPressed(self)
replyHandler(reply)
}
if status == "Quorum" {
let reply = ["status": "Finish"]
quorumButtonPressed(self)
replyHandler(reply)
}
if status == "Finish" {
let reply = ["status": "Reset"]
meetingEndedButtonPressed(self)
replyHandler(reply)
}
if status == "Reset" {
let reply = ["status": "Start"]
resetButtonPressed(self)
replyHandler(reply)
}
print("iPhone Received Status Button \(status)")
}
}
I get the messages firing fine on the Watch and see them in the debug log... But they do not seem to fire on the Phone. The phone is successfully sending its messages to the watch.
I have tested this code both in the simulator and on my own watch and iPhone. Note that the messages from the iPhone to the Watch are done using the via updateApplicationContext vs. the send message I am trying to use to send messages from the watch to the iPhone. Here's a sample of the iPhone code for sending context:
if WCSession.isSupported() {
if session.watchAppInstalled {
let UserInfo = ["waste":Float((wastedAmount.text! as NSString).floatValue), "cost":Float((totalAmount.text! as NSString).floatValue), "counter":Float((peopleInMeeting.text! as NSString).floatValue), "status":"Start"]
do {
try session.updateApplicationContext(UserInfo as! [String : AnyObject])
} catch {
print("Updating the context failed: ")
}
}
}
More information is needed regarding specifically what you're actually seeing on the Watch, when you say:
I get the messages firing fine on the Watch and see them in the debug log... But they do not seem to fire on the Phone. The phone is successfully sending its messages to the watch.
However, one common occurrence is that the iPhone code is actually working correctly, and the only thing you are not seeing is the debug statements printed to the console. This seems likely to be the case since you say you are seeing the expected Watch messages, presumably including those from print("Reply: \(reply)"). This indicates the message is being handled by the iPhone.
When that's the case, it's often simply that you are expecting to see debug console messages from both the Watch and iOS simulator processes at the same time, but in fact you're only connected to one or the other. There are (at least) two things you can do here:
Run the WatchKit app from Xcode, but then change to attach to the iPhone process instead. In Xcode, go Debug > Attach to Process... and select the iPhone app under "Likely Targets".
Start by running the iPhone app, which will mean you are already attached to that process. In the Apple Watch simulator, run the Watch app. You'll then be able to debug the iPhone side of the communication.
To debug in Watch-OS while running iPhone app and vice versa in Xcode-8.1. Required running Process need to be attached .
Visually:-