How to run background process in watchOS? - swift

I have made my first app for apple watch and faced with impossible to send data every 10 seconds to my backend .
Application is frozen when display turns off and running again from scratch when display is on.
I tryed to run workout session like in this example.
But it doesn't help, app is frozen after 10 seconds of inactivity.
I try to send accelerometer data to my backend every 10 seconds in InterfaceController.swift:
//...
override func willActivate() {
super.willActivate()
startWorkout() //it's copied from example
startAnglesTracker()
}
func startAnglesTracker() {
if motion.isAccelerometerAvailable && !motion.isAccelerometerActive {
motion.accelerometerUpdateInterval = 10
motion.startAccelerometerUpdates(to: OperationQueue.main) {
//sending here
}
}
}
private func startWorkout() {
let workoutConfiguration = HKWorkoutConfiguration()
workoutConfiguration.activityType = .crossTraining
workoutConfiguration.locationType = .indoor
do {
workoutSession = try HKWorkoutSession(configuration: workoutConfiguration)
workoutSession?.delegate = self
healthStore.start(workoutSession!)
vibrate()
} catch {
serverData.setText("workoutSession error")
}
}
//...
extension InterfaceController: HKWorkoutSessionDelegate {
func workoutSession(_ workoutSession: HKWorkoutSession, didFailWithError error: Error) {
}
func workoutSession(_ workoutSession: HKWorkoutSession, didGenerate event: HKWorkoutEvent) {
}
func workoutSession(_ workoutSession: HKWorkoutSession, didChangeTo toState: HKWorkoutSessionState, from fromState: HKWorkoutSessionState, date: Date) {
}
}
It works only when display is active.

Related

watchOS didUpdateLocation freezes during location change

I am developing a running app in SwiftUI for watchOS. The problem is that when I start the run, the measuring works fine as 0.55 0.56 0.57 km, etc. Then it randomly freezes, and jumps from 0.57 straight to 0.71. Even the total distance is sometimes inaccurate. When I look at the map of the run, I can sometimes see sharp corners and straight lines in curves, implying very inaccurate measurement. The OS is watchOS 8.0. I have tried kCLLocationAccuracyBest to no avail.
I have used CLLocationManager in an ObservableObject called DistanceObservable, and I have this exact code in iOS app that measures the distance perfectly.
... #Published var distance: Double = 0.0
func startUpdate() {
self.manager = CLLocationManager()
self.manager?.delegate = self
self.manager?.desiredAccuracy = kCLLocationAccuracyNearestTenMeters
self.manager?.requestWhenInUseAuthorization()
self.manager?.distanceFilter = 10.0
self.manager?.allowsBackgroundLocationUpdates = true
self.manager?.startUpdatingLocation()
}
Here is my watchOS didUpdateLocations method.
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
var locations = locations
for newLocation in locations {
let howRecent = newLocation.timestamp.timeIntervalSinceNow
guard newLocation.horizontalAccuracy < 20 && abs(howRecent) < 10 else { continue }
if let lastLocation = locationList.last {
let delta = newLocation.distance(from: lastLocation)
if (delta < 60) { // To prevent inaccurate "jumps" in distance
distance = distance + delta
if UserDefaults.standard.value(forKey: "hasSet") as!Bool == false {
distance = 0
locations.removeAll()
UserDefaults.standard.setValue(true, forKey: "hasSet")
}
latPoints.append(Double(locationList.last!.coordinate.latitude))
lonPoints.append(Double(locationList.last!.coordinate.longitude))
}
}
locationList.append(newLocation)
}
}
Finally I use the #EnvironmentObject in a SwiftUI View.
struct MyView: View {
#EnvironmentObject var loc: DistanceObservable
var body: some View {
Text("\(loc.distance)")
}
}
I have solved the issue by starting new HealthKit workout.
import HealthKit
Add conformance to ObservableObject and its methods
class SwiftUILocation: NSObject, CLLocationManagerDelegate, ObservableObject, HKWorkoutSessionDelegate, HKLiveWorkoutBuilderDelegate {
func workoutBuilder(_ workoutBuilder: HKLiveWorkoutBuilder, didCollectDataOf collectedTypes: Set<HKSampleType>) {
print("A")
}
func workoutBuilderDidCollectEvent(_ workoutBuilder: HKLiveWorkoutBuilder) {
print("B")
}
func workoutSession(_ workoutSession: HKWorkoutSession, didChangeTo toState: HKWorkoutSessionState, from fromState: HKWorkoutSessionState, date: Date) {
print("C")
}
func workoutSession(_ workoutSession: HKWorkoutSession, didFailWithError error: Error) {
print("D")
}
//.. rest of the code
}
Start HealthKit at the start of the workout
func startHealtKit(){
let configuration = HKWorkoutConfiguration()
configuration.activityType = .running
configuration.locationType = .outdoor
do {
self.workoutSession = try HKWorkoutSession(healthStore: healthStore,
configuration:configuration)
self.builder = self.workoutSession?.associatedWorkoutBuilder()
self.builder?.dataSource = HKLiveWorkoutDataSource(healthStore: healthStore,
workoutConfiguration: configuration)
self.workoutSession?.delegate = self
self.builder?.delegate = self
self.workoutSession?.startActivity(with: Date())
self.builder?.beginCollection(withStart: Date()) { (success, error) in
// Indicate that the session has started.
}
}
catch {
print("HealthKit problem \(error.localizedDescription)")
}
}
Stop HealtkKit at the end of the workout
func stopHealthKit(){
self.builder?.workoutSession?.stopActivity(with: Date())
self.builder?.endCollection(withEnd: Date()) { (success, error) in
// Indicate that the session has started.
}
}

"Unsupported action method signature". FolioReader Swift , Error

please help me to solve this error using FolioReaderKit Swift
enter image description here
I was also facing same crash while using Folioreader in my app. I have fixed it by doing below changes in my code.
In FolioReaderAudioPlayer.swift file. Update these 5 methods.
#objc func pause() -> MPRemoteCommandHandlerStatus {
playing = false
if !isTextToSpeech {
if let player = player , player.isPlaying {
player.pause()
}
} else {
if synthesizer.isSpeaking {
synthesizer.pauseSpeaking(at: .word)
}
}
return .success
}
#objc func togglePlay() -> MPRemoteCommandHandlerStatus {
isPlaying() ? pause() : play()
return .success
}
#objc func play() -> MPRemoteCommandHandlerStatus {
if book.hasAudio {
guard let currentPage = self.folioReader.readerCenter?.currentPage else { return .commandFailed }
currentPage.webView?.js("playAudio()")
} else {
self.readCurrentSentence()
}
return .success
}
#objc func playPrevChapter() -> MPRemoteCommandHandlerStatus {
stopPlayerTimer()
// Wait for "currentPage" to update, then request to play audio
self.folioReader.readerCenter?.changePageToPrevious {
if self.isPlaying() {
self.play()
} else {
self.pause()
}
}
return .success
}
#objc func playNextChapter() -> MPRemoteCommandHandlerStatus {
stopPlayerTimer()
// Wait for "currentPage" to update, then request to play audio
self.folioReader.readerCenter?.changePageToNext {
if self.isPlaying() {
self.play()
}
}
return .success
}

One to many webrtc

I want to create an "one to many" (with the max of 3 devices) webrtc setup. I have one device that is my main device. Other devices are connecting to that device. You can think about an walky talky. With one device who they are connecting to.
I have this code that works with an one to one connection.
import AVFoundation
import UIKit
import WebRTC
import SocketIO
import CoreTelephony
import ReachabilitySwift
let TAG = "ViewController"
let AUDIO_TRACK_ID = TAG + "AUDIO"
let LOCAL_MEDIA_STREAM_ID = TAG + "STREAM"
class ViewController: UIViewController, RTCPeerConnectionDelegate, RTCDataChannelDelegate {
var mediaStream: RTCMediaStream!
var localAudioTrack: RTCAudioTrack!
var remoteAudioTrack: RTCAudioTrack!
var dataChannel: RTCDataChannel!
var dataChannelRemote: RTCDataChannel!
var roomName: String!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
initWebRTC();
sigConnect(wsUrl: "http://192.168.1.69:3000");
localAudioTrack = peerConnectionFactory.audioTrack(withTrackId: AUDIO_TRACK_ID)
mediaStream = peerConnectionFactory.mediaStream(withStreamId: LOCAL_MEDIA_STREAM_ID)
mediaStream.addAudioTrack(localAudioTrack)
}
func getRoomName() -> String {
return (roomName == nil || roomName.isEmpty) ? "_defaultroom": roomName;
}
// webrtc
var peerConnectionFactory: RTCPeerConnectionFactory! = nil
var peerConnection: RTCPeerConnection! = nil
var mediaConstraints: RTCMediaConstraints! = nil
var socket: SocketIOClient! = nil
var wsServerUrl: String! = nil
var peerStarted: Bool = false
func initWebRTC() {
RTCInitializeSSL()
peerConnectionFactory = RTCPeerConnectionFactory()
let mandatoryConstraints = ["OfferToReceiveAudio": "true", "OfferToReceiveVideo": "false"]
let optionalConstraints = [ "DtlsSrtpKeyAgreement": "true", "RtpDataChannels" : "true", "internalSctpDataChannels" : "true"]
mediaConstraints = RTCMediaConstraints.init(mandatoryConstraints: mandatoryConstraints, optionalConstraints: optionalConstraints)
}
func connect() {
if (!peerStarted) {
sendOffer()
peerStarted = true
}
}
func hangUp() {
sendDisconnect()
stop()
}
func stop() {
if (peerConnection != nil) {
peerConnection.close()
peerConnection = nil
peerStarted = false
}
}
func prepareNewConnection() -> RTCPeerConnection {
var icsServers: [RTCIceServer] = []
icsServers.append(RTCIceServer(urlStrings: ["stun:stun.l.google.com:19302"], username:"",credential: ""))
let rtcConfig: RTCConfiguration = RTCConfiguration()
rtcConfig.tcpCandidatePolicy = RTCTcpCandidatePolicy.disabled
rtcConfig.bundlePolicy = RTCBundlePolicy.maxBundle
rtcConfig.rtcpMuxPolicy = RTCRtcpMuxPolicy.require
rtcConfig.iceServers = icsServers;
peerConnection = peerConnectionFactory.peerConnection(with: rtcConfig, constraints: mediaConstraints, delegate: self)
peerConnection.add(mediaStream);
let tt = RTCDataChannelConfiguration();
tt.isOrdered = false;
self.dataChannel = peerConnection.dataChannel(forLabel: "testt", configuration: tt)
self.dataChannel.delegate = self
print("Make datachannel")
return peerConnection;
}
// RTCPeerConnectionDelegate - begin [ ///////////////////////////////////////////////////////////////////////////////
/** Called when the SignalingState changed. */
public func peerConnection(_ peerConnection: RTCPeerConnection, didChange stateChanged: RTCSignalingState){
print("signal state: \(stateChanged.rawValue)")
}
/** Called when media is received on a new stream from remote peer. */
public func peerConnection(_ peerConnection: RTCPeerConnection, didAdd stream: RTCMediaStream){
if (peerConnection == nil) {
return
}
if (stream.audioTracks.count > 1) {
print("Weird-looking stream: " + stream.description)
return
}
}
/** Called when a remote peer closes a stream. */
public func peerConnection(_ peerConnection: RTCPeerConnection, didRemove stream: RTCMediaStream){}
/** Called when negotiation is needed, for example ICE has restarted. */
public func peerConnectionShouldNegotiate(_ peerConnection: RTCPeerConnection){}
/** Called any time the IceConnectionState changes. */
public func peerConnection(_ peerConnection: RTCPeerConnection, didChange newState: RTCIceConnectionState){}
/** Called any time the IceGatheringState changes. */
public func peerConnection(_ peerConnection: RTCPeerConnection, didChange newState: RTCIceGatheringState){}
/** New ice candidate has been found. */
public func peerConnection(_ peerConnection: RTCPeerConnection, didGenerate candidate: RTCIceCandidate){
print("iceCandidate: " + candidate.description)
let json:[String: AnyObject] = [
"type" : "candidate" as AnyObject,
"sdpMLineIndex" : candidate.sdpMLineIndex as AnyObject,
"sdpMid" : candidate.sdpMid as AnyObject,
"candidate" : candidate.sdp as AnyObject
]
sigSendIce(msg: json as NSDictionary)
}
/** Called when a group of local Ice candidates have been removed. */
public func peerConnection(_ peerConnection: RTCPeerConnection, didRemove candidates: [RTCIceCandidate]){}
/** New data channel has been opened. */
public func peerConnection(_ peerConnection: RTCPeerConnection, didOpen dataChannel: RTCDataChannel){
print("Datachannel is open, name: \(dataChannel.label)")
dataChannel.delegate = self
self.dataChannelRemote = dataChannel
}
// RTCPeerConnectionDelegate - end ]/////////////////////////////////////////////////////////////////////////////////
public func dataChannel(_ dataChannel: RTCDataChannel, didReceiveMessageWith buffer: RTCDataBuffer){
print("iets ontvangen");
}
public func dataChannelDidChangeState(_ dataChannel: RTCDataChannel){
print("channel.state \(dataChannel.readyState.rawValue)");
}
func sendData(message: String) {
let newData = message.data(using: String.Encoding.utf8)
let dataBuff = RTCDataBuffer(data: newData!, isBinary: false)
self.dataChannel.sendData(dataBuff)
}
func onOffer(sdp:RTCSessionDescription) {
print("on offer shizzle")
setOffer(sdp: sdp)
sendAnswer()
peerStarted = true;
}
func onAnswer(sdp:RTCSessionDescription) {
setAnswer(sdp: sdp)
}
func onCandidate(candidate:RTCIceCandidate) {
peerConnection.add(candidate)
}
func sendSDP(sdp:RTCSessionDescription) {
print("Converting sdp...")
let json:[String: AnyObject] = [
"type" : sdp.type.rawValue as AnyObject,
"sdp" : sdp.sdp.description as AnyObject
]
sigSend(msg: json as NSDictionary);
}
func sendOffer() {
peerConnection = prepareNewConnection();
peerConnection.offer(for: mediaConstraints) { (RTCSessionDescription, Error) in
if(Error == nil){
print("send offer")
self.peerConnection.setLocalDescription(RTCSessionDescription!, completionHandler: { (Error) in
print("Sending: SDP")
print(RTCSessionDescription as Any)
self.sendSDP(sdp: RTCSessionDescription!)
})
} else {
print("sdp creation error: \(Error)")
}
}
}
func setOffer(sdp:RTCSessionDescription) {
if (peerConnection != nil) {
print("peer connection already exists")
}
peerConnection = prepareNewConnection();
peerConnection.setRemoteDescription(sdp) { (Error) in
}
}
func sendAnswer() {
print("sending Answer. Creating remote session description...")
if (peerConnection == nil) {
print("peerConnection NOT exist!")
return
}
peerConnection.answer(for: mediaConstraints) { (RTCSessionDescription, Error) in
print("ice shizzle")
if(Error == nil){
self.peerConnection.setLocalDescription(RTCSessionDescription!, completionHandler: { (Error) in
print("Sending: SDP")
print(RTCSessionDescription as Any)
self.sendSDP(sdp: RTCSessionDescription!)
})
} else {
print("sdp creation error: \(Error)")
}
}
}
func setAnswer(sdp:RTCSessionDescription) {
if (peerConnection == nil) {
print("peerConnection NOT exist!")
return
}
peerConnection.setRemoteDescription(sdp) { (Error) in
print("remote description")
}
}
func sendDisconnect() {
let json:[String: AnyObject] = [
"type" : "user disconnected" as AnyObject
]
sigSend(msg: json as NSDictionary);
}
// websocket related operations
func sigConnect(wsUrl:String) {
wsServerUrl = wsUrl;
print("connecting to " + wsServerUrl)
socket = SocketIOClient(socketURL: NSURL(string: wsServerUrl)! as URL)
socket.on("connect") { data in
print("WebSocket connection opened to: " + self.wsServerUrl);
self.sigEnter();
}
socket.on("disconnect") { data in
print("WebSocket connection closed.")
}
socket.on("message") { (data, emitter) in
if (data.count == 0) {
return
}
let json = data[0] as! NSDictionary
print("WSS->C: " + json.description);
let type = json["type"] as! Int
if (type == RTCSdpType.offer.rawValue) {
print("Received offer, set offer, sending answer....");
let sdp = RTCSessionDescription(type: RTCSdpType(rawValue: type)!, sdp: json["sdp"] as! String)
self.onOffer(sdp: sdp);
} else if (type == RTCSdpType.answer.rawValue && self.peerStarted) {
print("Received answer, setting answer SDP");
let sdp = RTCSessionDescription(type: RTCSdpType(rawValue: type)!, sdp: json["sdp"] as! String)
self.onAnswer(sdp: sdp);
} else {
print("Unexpected websocket message");
}
}
socket.on("ice") { (data, emitter) in
if (data.count == 0) {
return
}
let json = data[0] as! NSDictionary
print("WSS->C: " + json.description);
let type = json["type"] as! String
if (type == "candidate" && self.peerStarted) {
print("Received ICE candidate...");
let candidate = RTCIceCandidate(
sdp: json["candidate"] as! String,
sdpMLineIndex: Int32(json["sdpMLineIndex"] as! Int),
sdpMid: json["sdpMid"] as? String)
self.onCandidate(candidate: candidate);
} else {
print("Unexpected websocket message");
}
}
socket.connect();
}
func sigRecoonect() {
socket.disconnect();
socket.connect();
}
func sigEnter() {
let roomName = getRoomName();
print("Entering room: " + roomName);
socket.emit("enter", roomName);
}
func sigSend(msg:NSDictionary) {
socket.emit("message", msg)
}
func sigSendIce(msg:NSDictionary) {
socket.emit("ice", msg)
}
}
So I thought that I need an array with the peers. And the mediaStream, localAudioTrack and the dataChannel needs to be one object because the local audio is the same? Are there good solutions for this? Because I don't know how to properly implement this.
I am investigating different questions and project referencing to an multi call webrtc setup.
I saw this (website) webrtc setup at GitHub:
https://github.com/anoek/webrtc-group-chat-example/blob/master/client.html
I'm going to try to reverse engineer this to swift:). Any help is really appreciated.
I would suggest against a one-to-many architecture where a single device needs to send its media to all others. This breaks awfully fast (like after 2-3 devices it needs to connect to).
The reason for that is that uplinks are usually limited in capacity and even when they aren't, devices aren't really geared to streaming so much data to many other devices.
To do what you want at "scale", use a server component that routes media to the other devices. Look at https://jitsi.org/ and http://www.kurento.org/ for starting points.
What you're trying to achieve can be achieved by multi-peer connection. Where one creates multiple peer connection with others which is a mesh topology from application level of view. This is most straight forward way and if it's the case that you're recently learning WebRTC and learnt how to implement a peer-connection you may try how to handle multiple peer connections. Look at this for more details on it click. But in practical where you will have dozens of clients, it will become hard because it will eats up the hardware resources and bandwidth. In that case what people do is: they maintain a shared server where all the client connects to and that server mixes the individual streams and distribute it. Which is a start topology. There are some well-known services for this like- tokbox, jitsi-meet.
You may also like to look into SFU model- https://webrtcglossary.com/sfu/

How can I get my iAd interstitial to work

I've looked everywhere for this but I can't find it. I've checked the code and it works in the simulator but not on the real app on the App Store. I launched the app which used this ad code way back in January and the adverts are still not working, all the billing and stuff is filled in on iTunes connect, I don't know how to fix it.
This is my code for the ads:
func loadInterstitialAd() {
interstitialAd = ADInterstitialAd()
interstitialAd.delegate = self
}
func interstitialAdWillLoad(interstitialAd: ADInterstitialAd!) {
}
func interstitialAdDidLoad(interstitialAd: ADInterstitialAd!) {
interstitialAdView = UIView()
interstitialAdView.frame = self.view!.bounds
view!.addSubview(interstitialAdView)
interstitialAd.presentInView(interstitialAdView)
UIViewController.prepareInterstitialAds()
}
func interstitialAdActionDidFinish(interstitialAd: ADInterstitialAd!) {
interstitialAdView.removeFromSuperview()
}
func interstitialAdActionShouldBegin(interstitialAd: ADInterstitialAd!, willLeaveApplication willLeave: Bool) -> Bool {
return true
}
func interstitialAd(interstitialAd: ADInterstitialAd!, didFailWithError error: NSError!) {
}
func interstitialAdDidUnload(interstitialAd: ADInterstitialAd!) {
interstitialAdView.removeFromSuperview()
}

Use arrow key input for SceneKit game in Swift

I am trying to create a swift file that will detect keyboard input from MacOS for a SceneKit game in swift. I am using the NSEvent to handle key events but I keep getting the error 'use of undeclared type NSEvent. Is there an easier way to achieve this?
I have tried the following code:
override func keyDown(event: NSEvent) {
interpretKeyEvents([event]) // calls insertText(_:), moveUp(_:), etc.
}
override func insertText(insertString: AnyObject) {
let str = insertString as! String
switch str {
case " ":
println("User hit the spacebar.")
default:
println("Unrecognized input: \(str)")
}
}
override func moveUp(sender: AnyObject?) {
println("Up arrow.")
}
override func moveLeft(sender: AnyObject?) {
println("Left arrow.")
}
override func deleteBackward(sender: AnyObject?) {
println("Delete.")
}
This will work:
override func keyDown(theEvent: NSEvent) {
let keyCode = theEvent.keyCode if(keyCode == 123)//Left{
}
if(keyCode == 124)//Right
{
}
if(keyCode == 125)//Down
{
}
if(keyCode == 126)//Up
{
}
This is an update (2020) to a QA asked in (2015).
override func pressesBegan(_ presses: Set<UIPress>, with event: UIPressesEvent?) {
guard let key = presses.first?.key else { return }
switch key.keyCode {
case .keyboardR:
print("Roll dice")
case .keyboardH:
print("Show help")
default:
super.pressesBegan(presses, with: event)
}
}