How to Callback from Swift Framework to Flutter project? [duplicate] - ios

This question already has answers here:
How do I use a Flutter MethodChannel to invoke a method in dart code from the native swift code?
(3 answers)
Closed 29 days ago.
I have the framework written in Swift 5 language and now i need to integrate that framework into the new flutter project and there is more number of callbacks to notify from Swift Framework to Flutter project. In that I don't know how to do that, what is the way to callback/delegate to the flutter project from Swift Framework.

Try to use Method channels. You can call Flutter methods from Swift code and vise versa.
iOS:
#UIApplicationMain
#objc class AppDelegate: FlutterAppDelegate {
private var flutterMethodChannel: FlutterMethodChannel?
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
flutterMethodChannel = FlutterMethodChannel(
name: "packageOfYourApp",
binaryMessenger: controller.binaryMessenger
)
flutterMethodChannel?.invokeMethod(
"methodName",
arguments: nil
)
}
}
Flutter:
_someMethodChannel.setMethodCallHandler((handler) async {
if (handler.method == 'methodName') {
// Do your logic here.
} else {
print('Unknown method from MethodChannel: ${handler.method}');
}
});

Related

Flutter IOS Workmanager Background Fetch Never Called in Production

I'm seemingly unable to get background fetch working with Flutter workmanager on IOS.
I can confirm that it is working when called within xcode through debug. Just never when deployed to the device.
I've got my workmanager initialised and callback setup in main.dart
...
Workmanager().initialize(
callbackDispatcher,
isInDebugMode: true
);
}
void callbackDispatcher()
{
Workmanager().executeTask((task, inputData) async
{
function()
return Future.value(true);
});
}
I've added fetch background mode to info.plist
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
</array>
I've added system capabilities to project.pbxproj
SystemCapabilities = {
com.apple.BackgroundModes = {
enabled = 1;
};
};
I've added the plugin to appdelegate.swift
import UIKit
import Flutter
import workmanager
#UIApplicationMain
#objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
UIApplication.shared.setMinimumBackgroundFetchInterval(TimeInterval(60*15))
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
Be much appreciated if anyone has a working setup.
Ok. For future people reading this with the same issue, this does 'eventually' work.
After having the app open in the background for ~48hrs, I finally got a background task to run. This is by no means a complete success as it is not really at the frequency desired, but this implementation does 'work' none the less.
Just run in debug mode to confirm and be very patient.

How can I set up Amazon Amplify iOS in Objective-C?

The docs show Swift code only. When trying to use Objective-C, I can't access any of the Amplify libraries. Am I missing an installation step?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
do {
try Amplify.add(plugin: AWSCognitoAuthPlugin())
try Amplify.add(plugin: AWSPinpointAnalyticsPlugin())
try Amplify.configure()
print("Amplify configured with Auth and Analytics plugins")
} catch {
print("Failed to initialize Amplify with \(error)")
}
return true
}
How to do the equivalent in Objective-C?
Yes, crusty old Objective C apps still exist. And they have to be maintained. It really isn't reasonable to expect developers to rewrite them in Swift just so they can use Amplify. I was recently asked to add Amplify to an app built in early 2015 before Swift existed. (Gosh -- are there really apps out there that are over 5 years old?!)
Fortunately, if you can bite the bullet and add Swift support to your Objective C project, it isn't so hard to make a Swift wrapper class that you use from Objective C. Here's one I created for free. It would be nice if the highly paid folks at Amazon would be kind enough to help us out with examples like this.
import Amplify
import AmplifyPlugins
#objc
class AmplifyWrapper: NSObject {
override init() {
super.init()
}
#objc
public func initialize() {
do {
try Amplify.add(plugin: AWSCognitoAuthPlugin())
try Amplify.add(plugin: AWSPinpointAnalyticsPlugin())
try Amplify.configure()
print("Amplify configured with Auth and Analytics plugins")
} catch {
print("Failed to initialize Amplify with \(error)")
}
}
#objc
public func recordEvent(name: String, category: String, accountId: String) {
let properties: AnalyticsProperties = [ "category": category, "accountId": accountId]
let event = BasicAnalyticsEvent(name: name, properties: properties)
Amplify.Analytics.record(event: event)
}
}
Use form Objective C like this:
#import "PutYourAppNameHere-Swift.h"
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[[[AmplifyWrapper alloc] init] initialize];
...
}
Then later on you can do this:
[[[AmplifyWrapper alloc] init] recordEventWithName:#"App Opened" category:#"Counts" accountId:""];
Am I missing an installation step?
You're not missing anything. Unfortunately there's no Objective-C support on Amplify for iOS. Amplify was built in Swift using its full capabilities and Objective-C support is not currently being considered unless there's a strong demand from the community.
Out of curiosity: are you starting a new app in Objective-C? If so I would be curious to understand why not going with Swift given Apple's investment on Swift lately (Combine, SwiftUI, Swift language updates, etc).
If you're trying to integrate Amplify in an existing Objective-C app, then I'm afraid it won't be possible.

Flutter: disable screenshot capture for app

I am making a Flutter app and I need to make sure the user is not able to capture screenshot of the app (any screen). Is there any way to achieve this in Flutter or do I need to write native code for both Android and IOS?
Android
Method 1 (all app screens):
Locate your MainActivity (.java or .kt) class inside the embedded android project dir in your Flutter Project,
Add the following import to your main activity class:
import android.view.WindowManager.LayoutParams;
Add the following line to your MainActivity's onCreate method:
getWindow().addFlags(LayoutParams.FLAG_SECURE);
Method 2 (for specific screens):
Use FlutterWindowManagerPlugin:
https://pub.dev/packages/flutter_windowmanager
Thanks, #Kamlesh!
It's only in iOS,just modify in AppDelegate.
And no more plugins
import UIKit
import Flutter
import Firebase
#UIApplicationMain
#objc class AppDelegate: FlutterAppDelegate {
override func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions[UIApplication.LaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
self.window.makeSecure() //Add this line
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
//And this extension
extension UIWindow {
func makeSecure() {
let field = UITextField()
field.isSecureTextEntry = true
self.addSubview(field)
field.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
field.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
self.layer.superlayer?.addSublayer(field.layer)
field.layer.sublayers?.first?.addSublayer(self.layer)
}
}
See image here
For Flutter2 project
Method 1 : using package flutter_windowmanager
Method 2 :
in Android with kotlin
Step 1 Open the file "mainActivity.kt" using the path
android\app\src\main\kotlin\com\example\auth_email\MainActivity.kt
Step 2 Import Library
import android.view.WindowManager.LayoutParams
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
Step 3 In main activity class
class MainActivity: FlutterActivity() {
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
window.addFlags(LayoutParams.FLAG_SECURE)
super.configureFlutterEngine(flutterEngine)
}
}
In iOS Swift : AppDelegate.swift file
import UIKit
import Flutter
#UIApplicationMain
#objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
// <Add>
override func applicationWillResignActive(
_ application: UIApplication
) {
self.window.isHidden = true;
}
override func applicationDidBecomeActive(
_ application: UIApplication
) {
self.window.isHidden = false;
}
}
The simplest way to do this is to use a flutter package called flutter_windowmanager
Works only for Android, not for IOS!
First import its latest version in pubspec.yaml file of your Flutter project and run pub get. Then add the below code inside the widget's initState() method for which you want to disable screenshot and screen recording.
Future<void> secureScreen() async {
await FlutterWindowManager.addFlags(FlutterWindowManager.FLAG_SECURE);
}
#override
void initState() {
secureScreen();
super.initState();
}
#override
void dispose(){
super.dispose();
await FlutterWindowManager.clearFlags(FlutterWindowManager.FLAG_SECURE);
}
If you want to make your whole app screenshot disable just call securescreen() method (defined above) inside your main() function in main.dart file.
What worked for me was writing the below code in MainActivity.java file.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
}
and importing these packages!
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.os.Bundle; // required for onCreate parameter
On iOS I have disabled taking of screenshots with the help of extension https://stackoverflow.com/a/67054892/4899849. Follow next steps:
Add property in AppDelegate:
var field = UITextField()
in didFinishLaunchingWithOptions call next method: addSecuredView()
private func addSecuredView() {
if (!window.subviews.contains(field)) {
window.addSubview(field)
field.centerYAnchor.constraint(equalTo: window.centerYAnchor).isActive = true
field.centerXAnchor.constraint(equalTo: window.centerXAnchor).isActive = true
window.layer.superlayer?.addSublayer(field.layer)
field.layer.sublayers?.first?.addSublayer(window.layer)
}
}
override delegate methods:
override func applicationWillResignActive(_ application: UIApplication) {
field.isSecureTextEntry = false
}
override func applicationDidBecomeActive(_ application: UIApplication) {
field.isSecureTextEntry = true
}
Now, when you make a screenshot in the app or record a screen video you will see a black image or video. Hope, it will help cuz I spent 2 days trying to make it work)
Flutter
Method 1: Using this package screen_protector
Method 2:
In Whole your Application
Open AppDelegate file and add UITextField variable.
private var textField = UITextField()
Create a function in AppDelegate file.
// Screenshot Prevent Functions
private func makeSecureYourScreen() {
if (!self.window.subviews.contains(textField)) {
self.window.addSubview(textField)
textField.centerYAnchor.constraint(equalTo: self.window.centerYAnchor).isActive = true
textField.centerXAnchor.constraint(equalTo: self.window.centerXAnchor).isActive = true
self.window.layer.superlayer?.addSublayer(textField.layer)
textField.layer.sublayers?.first?.addSublayer(self.window.layer)
}
}
Call this method into the didFinishLaunchingWithOptions function.
makeSecureYourScreen()
Code Screenshot
In Specific Screen - Using Method Channel
Open AppDelegate file and add UITextField variable.
private var textField = UITextField()
Create a function in AppDelegate file.
// Screenshot Prevent Functions
private func makeSecureYourScreen() {
if (!self.window.subviews.contains(textField)) {
self.window.addSubview(textField)
textField.centerYAnchor.constraint(equalTo: self.window.centerYAnchor).isActive = true
textField.centerXAnchor.constraint(equalTo: self.window.centerXAnchor).isActive = true
self.window.layer.superlayer?.addSublayer(textField.layer)
textField.layer.sublayers?.first?.addSublayer(self.window.layer)
}
}
Call this method into the didFinishLaunchingWithOptions function.
makeSecureYourScreen()
Also, add your method channel code in the didFinishLaunchingWithOptions function.
let controller : FlutterViewController = self.window?.rootViewController as! FlutterViewController
let securityChannel = FlutterMethodChannel(name: "secureScreenshotChannel", binaryMessenger: controller.binaryMessenger)
securityChannel.setMethodCallHandler({
(call: FlutterMethodCall, result: #escaping FlutterResult) -> Void in
if call.method == "secureiOS" {
self.textField.isSecureTextEntry = true
} else if call.method == "unSecureiOS" {
self.textField.isSecureTextEntry = false
}
})
Add your code below code to your flutter files to disable the screenshot on a specific screen.
// Declare your method channel varibale here
var iosSecureScreenShotChannel = const MethodChannel('secureScreenshotChannel');
Now add code to initState to prevent screenshot
#override
void initState() {
// this method to user can't take screenshots of your application
iosSecureScreenShotChannel.invokeMethod("secureiOS");
// TODO: implement initState
super.initState();
}
For add code to dispose to allow screenshots on another screen.
#override
void dispose() {
// this method to the user can take screenshots of your application
iosSecureScreenShotChannel.invokeMethod("unSecureiOS");
// TODO: implement dispose
super.dispose();
}
Code Screenshot in Xcode
Code Screenshot in Flutter
You can disable screenshots and video captures like the Netflix app and Disney Hotstar app.
I have tried it in my application and it works fine. 😊
Screenshots can be prevented very easily by following below two steps.
I am using VS code.
Step 1 Open the file "mainActivity.kt" using the path android\app\src\main\kotlin\com\example\auth_email\MainActivity.kt
Step 2 Add the two lines
(a) import android.view.WindowManager.LayoutParams;
(b) getWindow().addFlags(LayoutParams.FLAG_SECURE); in MainActivity: FlutterActivity() section
Restart the app
enter image description here
This works for iOS. In your Runner > AppDelegate.m:
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[GeneratedPluginRegistrant registerWithRegistry:self];
// Override point for customization after application launch.
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
- (void)applicationWillResignActive:(UIApplication *)application{
self.window.hidden = YES;
}
- (void)applicationDidBecomeActive:(UIApplication *)application{
self.window.hidden = NO;
}
#end
If you are using kotlin
open MainActivity.kt
Add below code at end of imports
import android.view.WindowManager.LayoutParams
Add below code at end of super.onCreate(savedInstanceState)
window.addFlags(LayoutParams.FLAG_SECURE)
Its done.
try to use
for android edit MainActivity.kt
package com.package.app_name
import android.view.WindowManager.LayoutParams
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
class MainActivity: FlutterActivity() {
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
window.addFlags(LayoutParams.FLAG_SECURE)
super.configureFlutterEngine(flutterEngine)
}
}
define this package inside pubspec.yaml file
flutter_windowmanager: ^0.0.1+1
get dependencies
flutter pub get
You need to call a method of FlutterWindowManager using await and async.
You have to add a few lines of code in your StatefulWidget.
Future<void> secureScreen() async {
await FlutterWindowManager.addFlags(FlutterWindowManager.FLAG_SECURE);
}
#override
void initState() {
secureScreen();
super.initState();
}
https://pub.dev/packages/screen_protector
use this one. works for Android, iOS both.
In iOS, screenshot will be captured but output will be black screen.
You can use the flutter_windowmanager: ^0.2.0 package to disable screenshot capture in a Flutter app. To do this, add the following code in your main.dart file:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await FlutterWindowManager.addFlags(FlutterWindowManager.FLAG_SECURE);
runApp(MyApp());
}
This will add the FLAG_SECURE flag to your app, which will prevent
screenshots from being captured. Note that this will only work on
Android devices.
You could maybe listen for the screenshot keys on iOS and when the combination is being pressed blacken the screen.

How to clear NSUserDefaults programmatically in XCUITest using Simulator

I've read several answers related to this and they suggest doing one of the following, but these options are not working for me. I have an XCUITest and I'm trying to clear the standard user defaults before before running the rest of my XCUITest. Currently my test app has a button that calls this code. I've also tried calling this code directly from within the XCUITest (I'm not sure if this is expected to work or if it needs to be run from within the app).
NSString *appDomain = [[NSBundle mainBundle] bundleIdentifier];
[[NSUserDefaults standardUserDefaults] removePersistentDomainForName:appDomain];
I've also tried removing each individually:
[[NSUserDefaults standardUserDefaults] removeObjectForKey:#"MyKey1"];
[[NSUserDefaults standardUserDefaults] removeObjectForKey:#"MyKey2"];
I also tried each of the above methods followed by a synchronize call:
[[NSUserDefaults standardUserDefaults] synchronize];
The next time I read #"MyKey1" from the NSUserDefaults its still has the old value and has not been deleted.
Is there any way to remove an object from the NSUserDefaults programmatically when running an XCUITest in the simulator? These are automated tests, so I can't always manually click on "Reset Contents and Settings" in xcode.
Thanks!
There are couple of ways to solve your issues by setting the UserDefaults values before your tests run the app (not after).
Solution #1
Mock UserDefaults values for certain keys using launchArguments:
func testExample() {
let app = XCUIApplication()
app.launchArguments += ["-keepScreenOnKey", "YES"]
app.launch()
}
Note the minus sign before the keepScreenOnKey key. The minus sign indicates that it should take the next launch argument value as a value for that UserDefaults key.
Solution #2 (if solution #1 doesn’t work)
The previous solution might not work if you’re using the SwiftyUserDefaults library. In this case, there is one elegant solution: if the application is running under UI tests, erase all UserDefaults data from the app. How does the app know if it is being run under UI tests? By passing the launch arguments message, like this:
override func setUp() {
super.setUp()
app.launchArguments += ["UI-Testing"]
}
Then, in AppDelegate.swift check if the app is running under UI-Testing and remove all UserDefaults data (like you do):
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
setStateForUITesting()
return true
}
static var isUITestingEnabled: Bool {
get {
return ProcessInfo.processInfo.arguments.contains("UI-Testing")
}
}
private func setStateForUITesting() {
if AppDelegate.isUITestingEnabled {
UserDefaults.standard.removePersistentDomain(forName: Bundle.main.bundleIdentifier!)
}
}
}
Solution #3 (if solution #2 is not enough)
But what if the UI test expects states other than the default state set by solution #2? Bools default to false, Integers to 0 and Strings to "", but what if you need true for the Bool key?
Do something like this:
func testExample() {
app.launchEnvironment["UI-TestingKey_keepScreenOn"] = "YES"
app.launch()
}
And then in AppDelegate.swift file:
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
static let uiTestingKeyPrefix = "UI-TestingKey_"
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
if AppDelegate.isUITestingEnabled {
setUserDefaults()
}
return true
}
static var isUITestingEnabled: Bool {
get {
return ProcessInfo.processInfo.arguments.contains("UI-Testing")
}
}
private func setUserDefaults() {
for (key, value)
in ProcessInfo.processInfo.environment
where key.hasPrefix(AppDelegate.uiTestingKeyPrefix) {
// Truncate "UI-TestingKey_" part
let userDefaultsKey = key.truncateUITestingKey()
switch value {
case "YES":
UserDefaults.standard.set(true, forKey: userDefaultsKey)
case "NO":
UserDefaults.standard.set(false, forKey: userDefaultsKey)
default:
UserDefaults.standard.set(value, forKey: userDefaultsKey)
}
}
}
}
extension String {
func truncateUITestingKey() -> String {
if let range = self.range(of: AppDelegate.uiTestingKeyPrefix) {
let userDefaultsKey = self[range.upperBound...]
return String(userDefaultsKey)
}
return self
}
}
Please note that this example only works for Bool and String keys. If you need more scalability, the switch command should be modified to somehow check if the value is Integer or Double or Any other value, but the general idea is here.
EDIT: It looks like the reason for using solutions #2 and #3 is not valid anymore as of SwiftyUserDefaults version 4.0.0-beta.1 as they've added support for setting values through launch arguments. But, I have to admit that I have not tested SwiftyUserDefaults library from this version onward, so I'll keep both solutions here.
With UI tests, the app runs as a separate process. You would need to call the methods to clear NSUserDefaults from within the app itself.
We have our UI Test pass a resetNSUserDefaults flag to the app when it launches the app. The app then clears the NSUserDefaults early in the launch process.

Sending notification to Apple Watch extension

I have got an iPhone 8.2 app that communicates with a bluetooth accessory during background mode (I enabled it on the capabilities tab). Whenever I receive a message from the accessory (handled in the iPhone app bundle) I'd like to send a notification to the Apple Watch extension (so that the user can visualise the updated state of the accessory).
How can I do this?
Additional sub-questions:
Ideally I'd like the user to see the notification also if the Apple
Watch app extension is in background mode (question 2: can apple
watch extension go in background mode?).
I am also unsure if I can send a notification without turning that
Apple watch app on. question 3: Is this possible?
You can send that notification using MMWormhole.
You send it using:
[self.wormhole passMessageObject:#{#"titleString" : title}
identifier:#"messageIdentifier"];
and you receive it using:
[self.wormhole listenForMessageWithIdentifier:#"messageIdentifier"
listener:^(id messageObject) {
// Do Something
}];
Note that wormhole uses app groups to communicate, so you need to enable it.
What MMWormhole uses, under the hood, is CFNotificationCenterGetDarwinNotifyCenter and you have more info about that in this medium post.
As for the sub-questions I am afraid I don't have 100% certain, but I believe that yes, you the apple watch extension also works in background mode. As for the third question, I didn't understand it.
An update for iOS 9.0 and above.
While MMWormhole works on watchOS 2 as well, it would be preferable to use Apple's WatchConnectivity framework on watchOS 2.0 and above. MMWormhole requires the use of App Groups, while WatchConnectivity does not. WatchConnectivity does indeed require iOS 9.0 and above.
Below is a quick example of how to send a simple String from an iOS app to a WatchKit Extension. First let's set up a helper class.
class WatchConnectivityPhoneHelper : NSObject, WCSessionDelegate
{
static let sharedInstance:WatchConnectivityPhoneHelper = WatchConnectivityPhoneHelper()
let theSession:WCSession = WCSession.defaultSession()
override init()
{
super.init()
// Set the delegate of the WCSession
theSession.delegate = self
// Activate the session so that it will start receiving delegate callbacks
theSession.activateSession()
}
// Used solely to initialize the singleton for the class, which calls the constructor and activates the event listeners
func start()
{
}
// Both apps must be active to send a message
func sendStringToWatch(message:String, callback:[String : AnyObject] -> ())
{
// Send the message to the Watch
theSession.sendMessage(["testString" : message], replyHandler:
{ (reply: [String : AnyObject]) -> Void in
// Handle the reply here
// Send the reply in the callback
callback(reply)
},
errorHandler:
{ (error:NSError) -> Void in
// Handle the error here
})
}
// A message was sent by the Watch and received by the iOS app. This does NOT handle replies to messages sent from the iOS app
func session(session: WCSession, didReceiveMessage message: [String : AnyObject], replyHandler: ([String : AnyObject]) -> Void)
{
// Handle received message logic here
if (message["testString"] != nil)
{
replyHandler(["testString" : "Received Message!"])
}
}
}
This helper class would be almost identical for the WatchKit Extension. I've named the WatchKit Extension version of this class WatchConnectivityExtensionHelper. I won't paste it because, again, it is just about identical to the helper class above.
Usage
We need to start the iOS and WatchKit Extension message listeners by instantiating the singleton helper class. All we need to do is call some function or refer to some variable within the singleton to initialize it. Otherwise, iOS or the WatchKit Extension will be sending out messages but the other might not receive them.
iOS - AppDelegate.swift
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate
{
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool
{
// Start the event listener for communications between the iOS app and the Apple Watch
WatchConnectivityPhoneHelper.sharedInstance().start()
...
}
...
}
WatchKit Extension - ExtensionDelegate.swift
class ExtensionDelegate: NSObject, WKExtensionDelegate
{
func applicationDidFinishLaunching()
{
// Start the event listener for communications between the iOS app and the Apple Watch
WatchConnectivityExtensionHelper.sharedInstance.start()
...
}
...
}
Then anywhere in your iOS app, call sendStringToWatch to send a String to the WatchKit Extension:
WatchConnectivityPhoneHelper.sharedInstance.sendStringToWatch("Test message!")
{ (reply:[String : AnyObject]) -> Void in
if (reply["testString"] != nil)
{
let receivedString:String = reply["testString"] as! String
print(receivedString)
}
}

Resources