Share datas between two apps with iOS 8 App Groups (using NSUserDefaults) - ios

I wonder if we can share datas between apps with the new iOS 8 feature : App groups (using NSUserDefaults) - Or if App Groups only share datas between the main app and its extension?
I actually enabled the App Groups feature on both of the apps that should share datas between them (they belong the same company). They also have the same App Groups thing (like group.com.company.myApp).
Here's the code on the first one (in Swift)
NSUserDefaults.standardUserDefaults().setBool(true, forKey:"Bool")
NSUserDefaults.standardUserDefaults().synchronize()
And here's the code on the second one (in Objective-C)
NSUserDefaults *datas = [[NSUserDefaults alloc] initWithSuiteName:#"group.com.company.myApp"];
NSLog(#"%#", [datas boolForKey:#"Bool"]);
Sadly, the Bool always returns nil.
If anyone has a solution :)
Thanks

Check out this thread on the Apple Developer Forums:
https://devforums.apple.com/message/977151#977151
I believe that both instances need to use the group ID (initializing with initWithSuiteName:) or else they are reading/writing to different user defaults sets.
So your Swift code would change to:
var userDefaults = NSUserDefaults(suiteName: "group.com.company.myApp")
userDefaults.setBool(true, forKey: "Bool")
userDefaults.synchronize()

App Groups no longer work in WatchOS2. You must use the watch connectivity Framework.
in your iOS App:
import UIKit
import WatchConnectivity
class ViewController: UIViewController, WCSessionDelegate {
override func viewDidLoad() {
super.viewDidLoad()
if (WCSession.isSupported()) {
let session = WCSession.defaultSession()
session.delegate = self
session.activateSession()
}
do {
let applicationDict = ["key" : "value"]
try WCSession.defaultSession().updateApplicationContext(applicationDict)
} catch {
// Handle errors here
}
}
}
In your Watch OS2 App:
import WatchKit
import WatchConnectivity
import Foundation
class InterfaceController: WKInterfaceController, WCSessionDelegate {
func session(session: WCSession, didReceiveApplicationContext applicationContext: [String : AnyObject]) {
print(applicationContext)
}

Related

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

I am trying to add watch extension to my existing application. Login is compulsory for my application. How can i check from watch app if user logged-in or not... and as user gets loged-in, I want to pass that login-data from the application to the watchapplication. I don't know how to pass the login data to watch 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.
}
}
}
}
You can do this using Watch Connectivity. Specifically sending a boolean value with transferUserInfo(). This will allow you to send the data from the iPhone application when the user either logs in or out, and it will be delivered to the watch extension in the background.
Here is an example:
func updateUserLoggedIn(_ loggedInValue : Bool) {
if WCSession.isSupported() {
WCSession.default.transferUserInfo(["User Logged In" : loggedInValue])
}
}
Then you simply handle the transfer in your watch extension:
func session(_ session: WCSession, didReceiveUserInfo userInfo: [String : Any] = [:]) {
if let userInfoFromPhone = userInfo["User Logged In"] {
// Handle Result Accordingly
}
}

Passing Data from Watch to iPhone using Appgroups

I am working on a project to pass data from an Apple Watch App to the iPhone App using Appgroups. My code is not working and I am not sure why. Hopefully someone can help me out! :)
Sending Data Apple Watch
#IBAction func senddata() {
let group = "group.pairedapp"
let shared = UserDefaults(suiteName: group)
let ok = "works"
shared!.setValue(ok, forKey: "status")
shared!.synchronize()
}
Getting Data on iPhone
#IBAction func getWatchData(_ sender: Any) {
let group = "group.pairedapp"
let shared = UserDefaults(suiteName: group)
let get = shared!.value(forKey: "status")
if get != nil {
print("works")
}
else{
print("OO NO!")
}
}
Since the introduction of watchOS2, watchOS apps are no longer considered just extensions of their iOS counterpart and hence you cannot use AppGroups to share data between the two.
You should use the WatchConnectivity framework on watchOS2+ to share data between your watchOS and iOS apps.
For more information, see the Sharing Data part of the WatchKit Programming Guide.

Pass Variable from iOS to watchOS

In my viewController, I have a varible for an AVAudioPlayer
var audioPlayer = AVAudioPlayer()
I want to acess this varible in my watchKit app so that I can play and pause the AVAudioPlayer from the watchKit app. Like
audioPlayer.play()
audioPlayer.pause()
How can I acess this variable from my watchKit app? Thanks for the help! I'm using Swift 3 and Xcode 8.
Since watchOS 2, you can't use AppGroups to share data directly between your iOS app and the WatchKit app.
Your only option to communicate between the two is the WatchConnectivity framework. Using WatchConnectivity, you can signal the iOS app using instant messaging to start/stop playing. On iOS in your AppDelegate implement something like this:
func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: #escaping ([String : Any]) -> Void) {
if let content = message["play"] as? [String:Any] {
audioPlayer.play()
replyHandler(["startedPlaying":true])
} else if let content = message["pause"] as? [String:Any] {
audioPlayer.pause()
replyHandler(["pausedMusic":true])
}
}
And in your Watch app you need to send messages with the content specified in your AppDelegate's session(_:didReceiveMessage:replyHandler:). If you don't need to send a response back to the Watch app, you can just use session(_:didReceiveMessage:) and get rid of the replyHandler part.

How to reference non-supported frameworks in Watch OS 2

I updated my app to the latest swift 2.0 syntax. In doing so, My watchkit app has become broken. The issue is the watchkit app references a class that references the framework AVFoundation. WatchOS2 apparently now no longer supports some of the standard frameworks:
Support for network-based operations includes the following technologies:
WatchKit extensions can access the network directly through an
NSURLSession object. WatchKit extensions have full access to the
NSURLSession capabilities, including the ability to download files in
the background. For information on how to use this class, see URL
Loading System Programming Guide. The Watch Connectivity framework
supports bidirectional communication between your Watch app and iOS
app. Use this framework to coordinate activities between the two apps.
See Communicating with Your Companion iOS App.
Available System Technologies for WatchKit
So now I cannot compile the watch kit code as "no such module found" is an error message when trying to use the AVFoundation framework. How can I get around this and keep referencing that class and framework in my apple watch app. Should I be communicating data between the phone and the watch? Is there a way to link the framework to the extension?
What I am trying to do is the following, in my InterfaceController:
override func willActivate() {
super.willActivate()
let defaultsShared = NSUserDefaults(suiteName: "somesharedappgroup")
let defaults = NSUserDefaults.standardUserDefaults()
if let barcodeString = defaultsShared!.objectForKey("barcode") as? String {
if let barcodeContent = RSUnifiedCodeGenerator.shared.generateCode(barcodeString, machineReadableCodeObjectType: AVMetadataObjectTypeCode39Code) {
barcode.setImage(barcodeContent)
label.setText("ID: \(barcodeString)")
} else {
label.setText("Please setup extensions in the settings of SHPID.")
barcode.setImage(nil)
}
} else {
label.setText("Please setup extensions in the settings of SHPID.")
barcode.setImage(nil)
}
}
The RSUnifiedCodeGenerator being a class that utilizes AVFoundation to generate barcode images from strings. Furthermore, the type that generator takes is an AVObject: AVMetadataObjectTypeCode39Code. This solution worked well in the first WatchOS, but now remains broken in OS 2. I see that WatchConnectivity may be a solution, and have it just pass me the barcode from the phone itself, but that would require I stop supporting iOS 8. What is the best solution, if any, for using AVFoundation with WatchOS 2. If I can not do that, how else should I go about passing this image to the watch from the phone when called. Thanks.
This is an example on how you could use WatchConnectivity for your app.
Please not that this example is rough and does not handle error. The session management should also get some attention for a stable product.
iPhone AppDelegate
import UIKit
import WatchConnectivity
import AVFoundation
import RSBarcodes
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, WCSessionDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// Override point for customization after application launch.
if WCSession.isSupported() {
let session = WCSession.defaultSession()
session.delegate = self
session.activateSession()
}
return true
}
// On Watch sends the message.
// Will not reply as we will push a data message with image.
func session(session: WCSession, didReceiveMessage message: [String : AnyObject]) {
if "generateBarcode" == message["id"] as! String {
let code = message["code"] as! String
let barcodeImage = RSUnifiedCodeGenerator.shared.generateCode(code,
machineReadableCodeObjectType: AVMetadataObjectTypeCode39Code)!
if WCSession.isSupported() {
let session = WCSession.defaultSession()
session.delegate = self
session.activateSession()
session.sendMessageData(UIImagePNGRepresentation(barcodeImage)!,
replyHandler: nil, errorHandler: nil)
}
}
}
}
Watch InterfaceController
import WatchKit
import Foundation
import WatchConnectivity
class InterfaceController: WKInterfaceController, WCSessionDelegate {
#IBOutlet var barcodeImage: WKInterfaceImage!
override func willActivate() {
super.willActivate()
if WCSession.isSupported() {
let session = WCSession.defaultSession()
session.delegate = self
session.activateSession()
// Send a message requesting a barcode image
session.sendMessage(
["id": "generateBarcode", "code": "2166529V"],
replyHandler: nil, // Do not handle response, iPhone will push a data message
errorHandler: nil)
}
}
// On iPhone pushes a data message
func session(session: WCSession, didReceiveMessageData messageData: NSData) {
barcodeImage.setImage(UIImage(data: messageData))
}
}
I think that using WatchConnectivity is the right thing to do.
For previous version support if the only dealer breaker is AVMetadataObjectTypeCode39Code, maybe you can print its value and pass it to the function directly?

Is there a way to detect if an Apple Watch is paired with an iPhone?

I want to know if there's an API available that shows the availability of an Apple Watch. I don't want to write an Apple Watch App yet. I want to do some analysis to see what percentage of actual users have an Apple Watch, before investing time to develop a watch version (if possible, of course).
So on WatchOS 2 that is possible !
You have to do on iPhone side :
First :
import WatchConnectivity
Then :
if WCSession.isSupported() { // check if the device support to handle an Apple Watch
let session = WCSession.defaultSession()
session.delegate = self
session.activateSession() // activate the session
if session.paired { // Check if the iPhone is paired with the Apple Watch
// Do stuff
}
}
I hope It would help you :)
The answer is yes but you have to support watchOS 2 and iOS9 to do it.
You need to check the property pairedfrom WCSession class.
You can find all the information at Watch Connectivity Framework Reference. I also recommend to watch this video from WWDC 2015 and read this tutorial
Swift 5
import WatchConnectivity
Then:
final class WatchSessionManager: NSObject {
private override init() {}
static let shared = WatchSessionManager()
func setup() {
guard WCSession.isSupported() else {
return
}
let session = WCSession.default
session.delegate = self
session.activate()
}
}
extension WatchSessionManager: WCSessionDelegate {
func sessionDidBecomeInactive(_ session: WCSession) {}
func sessionDidDeactivate(_ session: WCSession) {}
func session(
_ session: WCSession,
activationDidCompleteWith activationState: WCSessionActivationState,
error: Error?
) {
print("Apple Watch is paired: \(session.isPaired)")
}
}

Resources