I am attempting to pass an array between iPhone and Apple Watch Apps in Swift 4, thus far I have been unsuccessful. Here are a few examples I have tried to follow, unsuccessfully. In those examples they don't include many of the functions, however when I try and write it like that it gives the error does not conform to protocols
in addition when I declare the watch session It doesn't declare properly, also the didRecieveApplicationContext
is very different in swift 4 than as shown in the other questions/ examples Link Link2
Here is my iPhone ViewController
import UIKit
import WatchConnectivity
class watchtimetablemaker: UIViewController, WCSessionDelegate {
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
}
func sessionDidBecomeInactive(_ session: WCSession) {
}
func sessionDidDeactivate(_ session: WCSession) {
}
var watchSession: WCSession?
var initalArray = [String]()
var combination = ""
var vdlArray = [String]()
@IBAction func Save(_ sender: Any) {
vdlArray.removeAll()
//here I edit the arrays, add instances etc. cut out for fluency
self.view.endEditing(true)
sendToWatch()
UserDefaults.standard.set(vdlArray, forKey: "vdlarray")
UserDefaults.standard.synchronize()
print(initalArray)
print(vdlArray)
}
func sendToWatch(){
do{
let appDictionary = ["Array": initalArray]
try WCSession.default.updateApplicationContext(appDictionary)
}
catch {
print(error)
}
}
override func viewDidLoad() {
super.viewDidLoad()
if (WCSession.isSupported()){
watchSession = WCSession.default
watchSession?.delegate = self
watchSession?.activate()
}
vdlArray = UserDefaults.standard.array(forKey: "vdlarray") as! [String]
print(vdlArray)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Here is the Apple Watch Controller
import WatchKit
import Foundation
import WatchConnectivity
class TimetableWatch: WKInterfaceController, WCSessionDelegate {
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
load()
}
@IBOutlet var tabletitle: WKInterfaceTable!
var watchSession: WCSession?
var tableData = [String]()
override func awake(withContext context: Any?) {
super.awake(withContext: context)
if (WCSession.isSupported()){
watchSession = WCSession.default
watchSession?.delegate = self
watchSession?.activate()
}
load()
tabletitle.setNumberOfRows(tableData.count, withRowType: "Cell")
var i = 0
for item in tableData {
let row = tabletitle.rowController(at: i) as! TableCellRow
row.tbalecelllabel.setText(item)
i = i + 1
}
}
func load(){
func session(session: WCSession, didRecieveApplicationContext applicationContext: [String : Any]) {
DispatchQueue.main.async() { () -> Void in
if let retreivedArray = applicationContext["Array"] as? [String] {
self.tableData = retreivedArray
print(self.tableData)
}
}
}
}
override func willActivate() {
super.willActivate()
}
override func didDeactivate() {
super.didDeactivate()
}
}
I have been unable to find any documentation relating to swift4 so, any help would be much appreciated.
In WKInterfaceController
:
I agree with the comment from Jens Peter that you shouldn't wrap didReceiveApplicationContext
in the load() function.
Handle errors in activationDidCompleteWith
as such:
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
if let error = error {
print("Activation failed with error: \(error.localizedDescription)")
return
}
print("Watch activated with state: \(activationState.rawValue)")
}
That will also help determine if WatchConnectivity
is even becoming established.
Remove var watchSession: WCSession?
completely and declare WCSession
in awake(withContext)
as:
if WCSession.isSupported() {
let watchSession = WCSession.default
watchSession = self
watchSession()
}
Also remove the call to load()
in awake(withContext)
.
In InterfaceController
it's helpful to put some print
statements in your WCSession stubs even if you're not entirely using them. You can see if WatchConnectivity is activating.
func sessionDidBecomeInactive(_ session: WCSession) {
// To support multiple watches.
print("WC Session did become inactive.")
}
func sessionDidDeactivate(_ session: WCSession) {
// To support multiple watches.
WCSession.default.activate()
print("WC Session did deactivate.")
}
func session(_ session: WCSession, activationDidCompleteWith activationState: WCSessionActivationState, error: Error?) {
if let error = error {
print("WC Session activation failed with error: \(error.localizedDescription)")
return
}
print("Phone activated with state: \(activationState.rawValue)")
}
This line WCSession.default.activate()
as you see in sessionDidDeactivate
will allow for multiple watch support in the off case the user happens to have more than one watch.
Remove the var watchSession: WCSession?
as you did in WKInterfaceController
. Once again in viewDidLoad()
check for WCSession
support with:
if WCSession.isSupported() {
let watchSession = WCSession.default
watchSession = self
watchSession()
}
Update sendToWatch()
to:
func sendToWatch() {
let session = WCSession.default()
if session.activationState == .activated {
let appDictionary = ["Array": initalArray]
do {
try session.updateApplicationContext(appDictionary)
} catch {
print(error)
}
}
}
I think that cleans it up a bit and will hopefully work or at least move you forward. I didn't build this in a project however. Hope this helps.