Call External Function Using Watchkit Force Touch Menuitem

Call external function using WatchKit force touch MenuItem

The fatal error is (or was) caused by this line:

let wSM = WorkoutSessionManager()

That line creates a new instance of WorkoutSessionManager and calls init() on it.

Swift provides a default initializer called init() for any structure or class that provides default values for all of its properties and does not provide at least one initializer itself. But WorkoutSessionManager does not provide default values for the healthStore and workoutSession properties (and those properties are not optionals), and it provides its own initializer named init(context:), so it has no default initializer.

You need to either create your instance of WorkoutSessionManager using the designated initializer init(context:) (passing an appropriate instance of WorkoutSessionContext) or provide a default initializer for WorkoutSessionManager named init().

The precise manner in which you should do the former depends on the implementation of the rest of your app and the presentation of your DashboardController. I assume you are trying to recreate the "Fit" app shown in WWDC 2015 Session 203.

In that demonstration, the initial controller is an instance of ActivityInterfaceController, and that controller is responsible for presenting the next interface (via segues created in the storyboard). You can see the following code in the ActivityInterfaceController class:

override func contextForSegueWithIdentifier(segueIdentifier: String) -> AnyObject? {
let activityType: HKWorkoutActivityType

switch segueIdentifier {
case "Running":
activityType = .Running

case "Walking":
activityType = .Walking

case "Cycling":
activityType = .Cycling

default:
activityType = .Other
}

return WorkoutSessionContext(healthStore: self.healthStore, activityType: activityType)
}

The function above creates and returns a new instance of WorkoutSessionContext using an instance of HKHealthStore held by the initial controller. The context returned by that function is passed to the destination interface controller for the relevant segue through awakeWithContext.

For transitions in code, you can pass a context instance using equivalent functions such as pushControllerWithName(context:) which also lead to awakeWithContext.

If your initial controller is similar to the above, you can access the passed context in awakeWithContext in your DashboardController class and use it to configure a new instance of WorkoutSessionManager:

class DashboardController: WKInterfaceController
{
// ...

var wSM: WorkoutSessionManager?

override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)

if context is WorkoutSessionContext {
wSM = WorkoutSessionManager(context: context as! WorkoutSessionContext)
}

addMenuItemWithItemIcon(.Accept, title: "Save", action: #selector(DashboardController.saveSession))
}

// ...
}

Creating an instance of WorkoutSessionManager in that way avoids calling the (non-existent) init() initializer and permits reuse of the HKHealthStore instance. Whether that approach is open to you depends on the rest of your code and the way you are presenting your DashboardController.

Note that you should avoid creating multiple instances of WorkoutSessionManager. Use a singleton to provide a single instance of WorkoutSessionManager that is shared across your extension.

Use menu item for open another interface controller

Yes, it is possible. You need to create an IBAction for your Menu Item and call the desired navigation function inside that IBAction. I will show you code for using presentController, but you can use any other available navigation function from WatchKit.

@IBAction func presentOtherInterfaceController() {
self.presentController(withName: "MyOtherInterfaceController", context: nil)
}

Selector not called on selecting menu item after force touch

Solved.

Though its the result of a silly mistake (aren't most problems?), I think this is something a lot of people will run into, so I'll keep it here with my answer.

I enabled force touch on the simulator, so I could show the menu. When I'm tapping again on the button, force touch is still enabled, so I'm force touching, thus dismissing the menu.

Solution: Disable force touch before tapping a menu button.

WatchKit menu item not showing on real device

This is usually because the app can't find the custom image for your button. Is your image included in the WatchKit App bundle? Unfortunately, Interface Builder allows you to use an image from any bundle, but it won't work on a real device.

Any way to disable the force touch detection for my Apple WatchKit 2 app

Unfortunately there is no way to disable the force touch attempt at this time. Even Apple's own watch apps that don't have menus still show the force touch "bouce" effect if there is no menu items.

Is it possible to query live HKUnit data directly from another class without passing data?

If I understand correctly, you want to query the HealthKit store for certain kinds of samples, and update your UI every time new samples are saved to the store.

You can do this in several ways, including by using delegation, closures, or notifications. See below for sample code using delegation.

You've already defined a WorkoutSessionManagerDelegate protocol in your WorkoutSessionManager class (shown here). If that protocol is the same as that used in WWDC 2015 Session 203, it provides methods such as didUpdateActiveEnergyQuantity, didUpdateDistanceQuantity, and didUpdateHeartRateSample. If you give WorkoutSessionManager an object that acts as a delegate, the manager can delegate UI management to that object using the methods provided by the delegation protocol.

In the principal class, WorkoutSessionManager, define a property to hold a weak reference to the delegate: weak var delegate: WorkoutSessionManagerDelegate?

Then, whenever new samples become available, call the corresponding delegate method. So, for example, in the addActiveEnergySamples function, you already have the following line: self.delegate?.workoutSessionManager(self, didUpdateActiveEnergyQuantity: self.currentActiveEnergyQuantity)

In the delegate class, DashboardController, adopt the WorkoutSessionManagerDelegate protocol: class DashboardController: WKInterfaceController, WorkoutSessionManagerDelegate

And in awakeWithContext, assign yourself as the manager's delegate: wSM?.delegate = self

Finally, in the delegate class, implement the methods provided by the delegation protocol, and make such changes to your UI as necessary based on the data passed through those methods.

Call a method from another UIViewController in swift 2.0

You can create a global method. Create a empty swift file and add this code in it:

import Foundation
import AVFoundation


var backgroundMusicPlayer = AVAudioPlayer()

func playBackgroundMusic(filename: String) {
let url = NSBundle.mainBundle().URLForResource(filename, withExtension: nil)
guard let newURL = url else {
print("Could not find file: \(filename)")
return
}
do {
backgroundMusicPlayer = try AVAudioPlayer(contentsOfURL: newURL)
backgroundMusicPlayer.numberOfLoops = -1
backgroundMusicPlayer.prepareToPlay()
backgroundMusicPlayer.play()
} catch let error as NSError {
print(error.description)
}
}

Now when ever you want to play any song just call that method this way:

playBackgroundMusic("yourSong.mp3")

And if you want to go with your class then you can do it this way:

import Foundation
import AVFoundation

var audioPlayer: AVAudioPlayer!
var deviceCurrentTime: NSTimeInterval!

class Song: NSObject {

func playSong() {

let sound = NSBundle.mainBundle().pathForResource("We_Are_One_Ole_Ola_", ofType: "mp3")
let soundData = NSData(contentsOfFile: sound!)
// self.audioPlayer = AVAudioPlayer(data: soundData!) throws

audioPlayer = nil
do {
audioPlayer = try AVAudioPlayer(data: soundData!)
}
catch {
print("Handle \(error) here")
}

let delay:NSTimeInterval = 0.0//100ms
var playtime:NSTimeInterval
playtime = audioPlayer.deviceCurrentTime + delay

audioPlayer?.playAtTime(playtime)
audioPlayer?.play()

}
}

In other class you can use it this way:

override func viewDidLoad() {
super.viewDidLoad()

let temp = Song()
temp.playSong()
}


Related Topics



Leave a reply



Submit