Apple Watch, Watchkit Extension and Main Application

Apple Watch, WatchKit Extension and main application

To communicate to the containing iPhone app you can use

(BOOL)openParentApplication:(NSDictionary *)userInfo
reply:(void (^)(NSDictionary *replyInfo,
NSError *error))reply

In your WKInterfaceController

From the Apple Docs

Use this method to communicate with your containing iOS app. Calling
the method causes iOS to launch the app in the background (as needed)
and call the application:handleWatchKitExtensionRequest:reply: method
of its app delegate. That method has the following signature:

- (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo
reply:(void(^)(NSDictionary *replyInfo))reply

The app delegate receives the dictionary you pass into the userInfo
parameter and uses it to process whatever request you made. If it
provides a reply, WatchKit executes the block you provided in the
reply parameter of this method.

iOS WatchOS - what is the difference between WatchKit App and WatchKit App extension?

Current answer (valid from Xcode 14): the separate 2 targets don't exist anymore, the new default watchOS app template combines the WatchKit App and WatchKit App Extension targets into a single Watch App target, which can store both code and resources, similar to iOS app targets.

See the Xcode 14 release notes (relevant excerpt below).

Xcode 14 includes a new default template for watchOS apps that
combines the WatchKit App and WatchKit App Extension targets into a
single Watch App target, simplifying code, asset, and localization
management. You can deploy single-target watchOS apps to watchOS 7 and
later. (83222217)


Historical answer (valid up until Xcode 13): The two come hand-in-hand, both are needed to create a watchOS application and you cannot use one without the other.

The main difference is that a WatchKit App is responsible for displaying the UI, so this is where you store the storyboard(s) and all assets (images, etc) used from storyboards. On the other hand, your WatchKit App Extension is responsible for everything done programatically, so this is where all your interface controllers and other classes should reside. You should also store all assets that you access from code in your App Extension target.

You'll need to use the WatchConnectivity framework from code as part of your App Extension target, however, as already stated, all watchOS applications need to have a WatchKit App target as well.

Apple watch multiple Assets.xcassets folder purpose

This seems to be a holdover from watchOS 1 when the WatchKit Extension was actually running on the iPhone and sending information and resources to the WatchKit App running on the watch.

There are some calls which will work only with the WatchKit App's Assets.xcassets folder such as setImageNamed: or setBackgroundImageNamed:

There are several ways to change the current image of an interface object:

  • Use the setImageNamed: or setBackgroundImageNamed: methods to assign an image that is already in the Watch app bundle.

  • Use the setImage:, setImageData:, setBackgroundImage:, or setBackgroundImageData: >methods to transfer image data from your WatchKit extension to your Watch app.

Specifying images by name is more efficient because only the name string must be transferred to your Watch app. watchOS searches your Watch app bundle for an image file with the name you specified. The most efficient way to specify images efficiently is to store them in your Watch app bundle and use the setImageNamed: or setBackgroundImageNamed: as appropriate to configure the corresponding object.

Images created in your WatchKit extension must be transferred to the Watch app before they can be used. For example, using the imageNamed: method in your extension loads the image from your WatchKit extension’s bundle, not from your Watch app’s bundle. You can then call the setImage: method, passing in the image object. The WatchKit extension automatically transfers the image to the Watch app for display. While this has some additional overhead compared to loading images directly from the Watch app bundle, It should not have a significant impact on either performance or battery life.

App Programming Guide for watchOS / Images

Personally I would not place images in both folders as this will increase the size of your App. I tend to place images that will be set by Storyboard in the WatchKit App's folder and all the images that will change programmatically in the Extension.

Detect WatchKit extension presence in app

I'll reiterate the answer I gave here. There's no way to detect whether or not a Watch has been paired with the phone programmatically.

Remove WatchKit App From Deployed App - iOS WatchOS Swift

You can remove the WatchKit Extension from your iOS app even after you deployed the watchOS app to the AppStore. You simply need to remove the Apple Watch targets from your Xcode project and then upload an update to the AppStore.

WatchOS: Should UI updates from the extension be called on the main thread?

After contacting Apple through a Technical Support Incident, the received answer and explanation is below.

TLDR: use the main thread.

All updates should be done from the main thread. This has always
been the general recommendation for UIKit and that recommendation
extends to watchOS.

It might be helpful to understand the underlying reason for this
requirement. Keep in mind that, even with a centralized communication
channel to serialize changes, many problems arise when you attempt to
manipulate UI state from background threads. For example, while the
serialization channel can prevent multiple UI commands from attempting
to simultaneously execute, it can’t control the order in which
unrelated commands will execute. Consider the following 2 blocks:

block 1 {     
DoUIChange1
DoUIChange2
}

block 2 {
DoUIChange3
DoUIChange4
}

If both blocks are executed on the main thread, then the actual
command stream is either:

DoUIChange1   
DoUIChange2
DoUIChange3
DoUIChange4

or…

DoUIChange3   
DoUIChange4
DoUIChange1
DoUIChange2

However, if both blocks are executed on their own threads, even more
possibilities open up:

DoUIChange3   
DoUIChange1
DoUIChange2
DoUIChange4

or..

DoUIChange1   
DoUIChange3
DoUIChange2
DoUIChange4

or..

DoUIChange1   
DoUIChange3
DoUIChange4
DoUIChange2

etc…

Needless to say, if the UI code is at all complex the number of
combinations quickly becomes enormous, making unexpected UI bugs
basically unavoidable.

Can I build Apple Watch app without iPhone companion app

You can starting with watchOS 6.

How can i check from watch app that if user logged-in or not into the phone application

Prior to WatchOS 2, you could share data between iOS companion app and watchOS app by using shared group container or iCloud to exchange data.

From WatchOS 2, since WatchKit extension now runs on Apple Watch itself, the extension must exchange data with the iOS app wirelessly. You will have to use WCSession class which is part of the WatchConnectivity framework. The framework is used to implement two-way communication between an iOS app and its paired watchOS app.

In iPhone companion app. Implement the following:

import WatchConnectivity

class LoginViewController: UIViewController {

override func viewDidLoad() {
super.viewDidLoad()

if WCSession.isSupported() {
WCSession.default().activate()
}
}

@IBAction func loginAction(_ sender: Any) {

// In this case ApiHandler is just a class which performs REST call to authenticate login credentials.
ApiHandler.login(username: txtUsername.text!, password: txtPassword.text!, { (loginSuccess) in
if loginSuccess {

let dict = ["statusLogin": "success"]
WCSession.default().sendMessage(dict, replyHandler: { (replyMessage) in
print(replyMessage)
}) { (error) in
print(error.localizedDescription)
}
}

})
}

}

In watchOS app.

import WatchConnectivity

class BaseInterfaceController: WKInterfaceController {
override func willActivate() {
super.willActivate()

if WCSession.isSupported() {
WCSession.default().delegate = self
WCSession.default().activate()
}
}

}

extension BaseInterfaceController: WCSessionDelegate {
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {

}

func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: @escaping ([String : Any]) -> Swift.Void) {
if let loginStatus = message["statusLogin"] as? String {
if loginStatus == "success" {
// login has been success in iOS app
// perform further tasks, like saving in Userdefaults etc.
}
}
}
}


Related Topics



Leave a reply



Submit