I have a string value in a WatchOS 2 app, and want to send an iMessage text using the value. An example of this is in the Workflow watch app, where the send message action results in the following screenshot:
I can't find any frameworks or url schemes that will work on WatchOS, so how do I do something like this?
You can send your "string" by enabling the Capabilites of group on both iMessage and watch target and sharing by the Userdefaults among the Targets(iMessagge and Watch OS).
//Watch OS sharing **String**
func sharedUserInfo() {
if let userDefaults = UserDefaults(suiteName: "group.watch.app.com" ) {
userDefaults.set( stringObj as AnyObject, forKey: "string")
userDefaults.synchronize()
}
}
//iMessage extracting the info
func sharedInfo() {
if let userDefaults = UserDefaults(suiteName: "group.watch.app.com") {
let stringObj = userDefaults.string(forKey: "string")
}
}
So you can send the string from watch OS to iMessage
Related
I'm working on a macOS cocoa-app in Swift where I import several different file types into the app for the user to interact with.
I'm currently trying to determine if it's possible to implement the "Open file with" feature, so that the user could open those files in a different program if they wanted to:
I've found a few different SO questions that seem tangentially related to what I'm trying to do:
Swift: How to open file with associated application?
Launch OSX Finder window with specific files selected
...but so far nothing to indicate if it's possible to implement right-click Finder/file (?) access in the way I had in mind.
Apologies if this is too vague of a question; any help / guidance appreciated!
Without going into details, it's pretty straight forward:
Get the list of all known applications that can open a specific file type (see LSCopyApplicationURLsForURL, a Core Foundation C function).
Build the menu. You can use NSWorkspace (and probably URL) to get the application icons.
Use NSWorkspace.openFile(_:withApplication:) to tell the application to open the given document.
2022, Swift 5
Get app list associated with local file:
func getAppsAssociatedWith(_ url: URL?) {
guard let url = localFileURL,
let retainedArr = LSCopyApplicationURLsForURL( url as CFURL, .all)?.takeRetainedValue(),
let listOfRelatedApps = retainedArr as? Array<URL>
else {
return []
}
return listOfRelatedApps
}
Getting thumbnail for app:
let singleAppIcon = NSWorkspace.shared
.icon(forFile: appUrl.path)
.scaledCopy(sizeOfLargerSide: 17)
Open url with app:
#available(macOS 10.15, iOS 9.0, *)
public class func openUrlWithApp(_ urls: [URL], appUrl: URL) {
NSWorkspace.shared.open(urls, withApplicationAt: appUrl, configuration: NSWorkspace.OpenConfiguration())
}
In my app I'm cashing all apps icons in dictionary.
[someFile localURL : app icon]
If I have already got icon earlier - no need to get it once more
var relatedAppsThumbnails: [URL: Image] = [:]
func updateRelatedApps() {
guard let url = currImgUrl, // file url to get icons from related apps
let retainedArr = LSCopyApplicationURLsForURL( url as CFURL, .all)?.takeRetainedValue(),
let listOfRelatedApps = retainedArr as? Array<URL>
else {
relatedApps = []
return
}
self.relatedApps = listOfRelatedApps
// add app icon in case of it wasn't added yet
for appUrl in listOfRelatedApps {
if relatedAppsThumbnails[appUrl] == nil {
let nsImg = NSWorkspace.shared.icon(forFile: appUrl.path)
.scaledCopy(sizeOfLargerSide: 17)
relatedAppsThumbnails[appUrl] = Image(nsImage: nsImg)
}
}
}
LSCopyApplicationURLsForURL is deprecated. You can use this alternative:
func getListOfExternalApps(forURL url: URL) -> [(URL, Image)] {
let listOfExternalApps = NSWorkspace.shared.urlsForApplications(toOpen: url)
let icons = listOfExternalApps.map {
let nsimage = NSWorkspace.shared.icon(forFile: $0.path())
nsimage.size = CGSize(width: .s16, height: .s16)
return Image(nsImage: nsimage)
}
return Array(zip(listOfExternalApps, icons))
}
I am trying to open up the iMessage app with a default message from my app. The default message contains a link to the app in the app store. This is used as a way for users to invite people to download the app.
The user types in a number and then hits a submit button and then it opens up the iMessage app with that number and a refilled message. However, for some reason, Swift won't generate the URL. Here is what I have
let body = "Download SomeApp by clicking the link below:\n\nhttps://appsto.re/us/someapp.i"
guard let phoneUrl = URL(string: "sms:\(numberTextField.text!)&body=\(body)") else {
return
}
if UIApplication.shared.canOpenURL(phoneUrl) {
UIApplication.shared.open(phoneUrl, options: [:], completionHandler: nil)
}
Right now its not even getting past the guard statement.
All I want to do is open iMessage with a link to my app in the body.
You need to escape the content passed into the &body= parameter. You can do this with addingPercentEncoding.
For example:
let body = "Download SomeApp by clicking the link below:\n\nhttps://appsto.re/us/someapp.i"
guard let escapedBody = body.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) else {
return
}
guard let phoneUrl = URL(string: "sms:\(numberTextField.text!)&body=\(escapedBody)") else {
return
}
On Watch, I'm able to pass a saved workout from the WorkoutInterfaceController to the SummaryInterfaceController. But I was wondering how to pass the saved workout from the Watch to the iPhone (so I can display it in a Summary View Controller too).
Do you know? Or is there a better way I'm supposed to do this?
Here's what I use to pass the saved workout from WorkoutInterfaceController to the SummaryInterfaceController:
private func saveWorkout() {
// Create and save a workout sample
let configuration = workoutSession!.workoutConfiguration
let isIndoor = (configuration.locationType == .indoor) as NSNumber
print("locationType: \(configuration)")
let workout = HKWorkout(activityType: configuration.activityType,
start: workoutStartDate!,
end: workoutEndDate!,
workoutEvents: workoutEvents,
totalEnergyBurned: totalEnergyBurned,
totalDistance: totalDistance,
metadata: [HKMetadataKeyIndoorWorkout:isIndoor]);
healthStore.save(workout) { success, _ in
if success {
self.addSamples(toWorkout: workout)
}
}
WKInterfaceController.reloadRootControllers(withNames: ["SummaryInterfaceController"], contexts: [workout])
}
private func addSamples(toWorkout workout: HKWorkout) {
// Create energy and distance samples
let totalEnergyBurnedSample = HKQuantitySample(type: HKQuantityType.activeEnergyBurned(),
quantity: totalEnergyBurned,
start: workoutStartDate!,
end: workoutEndDate!)
// Add samples to workout
healthStore.add([totalEnergyBurnedSample], to: workout) { (success: Bool, error: Error?) in
if success {
// Samples have been added
}
}
}
Let me know if any questions or information needed, thanks!
As a part of my research and development,I discovered how the pairing of the iPhone and the Apple Watch has the potential to be useful.
In this case, tapping on a button on the Watch app will send text on the iPhone.
To make a simple demo of this functionality, place a button on the WatchKit interface and a label on the iOS app’s storyboard.
Now, hook up the button to the WatchKit Interface Controller as an IBAction in order to respond to button tap events. Also hook up the Label to the UI View Controller as an IBOutlet.
In the Interface Controller, we make up a string variable to send to the label and in the button’s IBAction method, make a dictionary that includes the string variable you made. This dictionary is what is passed to the iPhone app.
class InterfaceController: WKInterfaceController {
Var str: String = "Hello Phone"
#IBAction func button() {
let dict: Dictionary = ["message": str]
}
Use the following method to send the dictionary to the iPhone.
WKInterfaceController.openParentApplication(dict, reply: {(reply, error) -> void in
print("Reply receive from iPhone app")
})
In the AppDelegate of the iOS app, add the following application method. This is what will handle the previous methods communication from the Watch. Also we can use a notification to notify a view controller that data has been received and to pass it along.
func application(application: UIApplication, handleWatchkitExtensionRequest userInfo: [NSObject : AnyObject]?, reply:(([NSObject : AnyObject]!) {
NSNotificationCenter.defaultCenter().postNotificationName("WatchKitReq", object: userInfo)
}
Finally in the view controller, make a listener for the notification that will update the label’s text to the string that was sent with the data.
class ViewController: UIViewController {
#IBOutlet weak var label: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector("handleWatchKitNotification:"), name: "WatchKitReq", object: nil)
}
func handleWatchKitNotification(notification: NSNotification) {
if let userInfo = notification.object as? [String : String] {
label.text = userInfo["message"]
}
}
Hope this will help you to understand. For more concerns you can look on this,
Delegate Method
To do this you have to create an App Group which is essentially a space which both apps can use. It was brought in with the exetension framework in iOS8 so apps can communicate with their Today widgets, or custom keyboards, and amongst other applications.
ADD CAPABILITIES
The first thing we have to do is add the app group capability to both our iPhone and Watch Watch Extension targets.
To do this open up the project settings (blue icon at the top of the list of files) and select the iPhone target. You will need to select the “capabilities” tab at the top of the page and scroll down to turn on app groups.
This requires a connected developer profile, and will take a while to enable. You’ll need to do the same steps to switch on app groups for the watch kit extension also.
Next you need to ensure that the app group string is an identifier string you want and that makes sense for your app, it must start with the word group or it complains. You can also add multiple groups if you wish. Whatever you pick they must be enabled with a blue tick (again this might take a while) and are exactly the same for both the iPhone and Watch extension targets!
To use App Groups, it’s not that different or difficult to use than NSUserDefaults:
var userDefaults = NSUserDefaults(suiteName: "group.com.example.My-App")
userDefaults.setObject(true, forKey: "isDarkModeEnabled")
userDefaults.synchronize()
The only differences here are how NSUserDefaults is instantiated and calling synchronize at the end. You feed it the container ID to the constructor parameter called “suiteName”, then call “synchronize()”, and your data flies to the cloud for other apps and devices to consume.
Taking It to the Next Level
You can take this one step further by creating a class for your app and abstract the underlying storage for your properties. Here’s an example:
public class ConfigurationModel: NSObject {
public static let storageKey = "group.com.example.My-App"
public let userStorage = NSUserDefaults(suiteName: storageKey)
public var isDarkModeEnabled: Bool {
get {
// Get setting from storage or default
if userStorage?.objectForKey("isDarkModeEnabled") == nil {
userStorage?.setObject(false, forKey: "isDarkModeEnabled")
userStorage?.synchronize()
}
return userStorage?.objectForKey("isDarkModeEnabled")
}
set {
// Set new value in storage
userStorage?.setObject(newValue, forKey: "isDarkModeEnabled")
userStorage?.synchronize()
}
}
At the top of the class, I am declaring my group container ID and creating the NSUserDefault object out of it. Then my properties for the class have getters and setters to store the data to the App Group. If the key doesn’t exist, it creates it with a default value and synchronizes it. Using the class from this point forward is simple:
var configModel = ConfigurationModel()
configModel.isDarkModeEnabled = true
This property is stored in the cloud! Everything is abstracted away for you. You don’t have to be bothered about storing and synchronizing it into the App Group. It’s all done automatically for you in the getters and setters!
Hope, this will help you to understand how you can share data between the iPhone and Apple Watch app.
Recently some of our users were complaining about contacts not syncing to their iCloud account. It was working in iOS 8 and mysteriously stopped in one of the updates in iOS 9. With iOS 10 around the corner I thought it might be linked to deprecation of AddressBook.framework to Contacts.framework. However, even moving to new Contacts.framework it didn't fix the problem.
There are no error logs on console of the device and neither of the frameworks generate any errors when contacts are being created/updated on the device.
Contacts are visible and available on the device just not syncing to iCloud and other devices attached to the iCloud account.
After a lot of debugging what I was able to isolate the issue to imageData property. Contacts which had imageData populated were not synced and the few which had no images were synced. This lead me to look at the code for imageData. Turns out I had been using UIImagePNGRepresentation to convert UIImage to NSData for imageData. Moving to UIImageJPEGRepresentation fixed the issue. The day was saved and iCloud accounts are synced.
Thank you Apple for not documenting this change. (it might be the image size even that is not documented)
CNContact(s) created locally are not synced to Third party application.
I have crated contacts in iphone contacts page.Those contacts are not synching inside my application.I have used below method to fetch contacs from my contacts framework.
There are no error logs on console of the device and neither of the frameworks generate any errors when contacts are being created/updated on the device.
Contacts are visible and available on the device just not syncing to my app.I have given permission to my application to access my contacts.
#available(iOS 10.0, *)
func retrieveContacts(_ completion: (_ success: Bool, _ contacts: [ContactEntry]?) -> Void) {
var contacts = [ContactEntry]()
do {
let contactsFetchRequest = CNContactFetchRequest(keysToFetch: [CNContactGivenNameKey as CNKeyDescriptor, CNContactFamilyNameKey as CNKeyDescriptor, CNContactImageDataKey as CNKeyDescriptor, CNContactImageDataAvailableKey as CNKeyDescriptor, CNContactPhoneNumbersKey as CNKeyDescriptor, CNContactEmailAddressesKey as CNKeyDescriptor])
try contactStore.enumerateContacts(with: contactsFetchRequest, usingBlock: { (cnContact, error) in
if let contact = ContactEntry(cnContact: cnContact) {
contacts.append(contact)
print(contact,contacts.count)
}
})
completion(true, contacts)
} catch {
completion(false, nil)
}
let keysToFetch = [CNContactFormatter.descriptorForRequiredKeys(for: .fullName),CNContactPhoneNumbersKey] as [Any]
let fetchRequest = CNContactFetchRequest( keysToFetch: keysToFetch as! [CNKeyDescriptor])
var contacts1 = [CNContact]()
if #available(iOS 10.0, *) {
fetchRequest.mutableObjects = false
} else {
// Fallback on earlier versions
}
fetchRequest.unifyResults = true
fetchRequest.sortOrder = .userDefault
let contactStoreID = CNContactStore().defaultContainerIdentifier()
print("\(contactStoreID)")
do {
try CNContactStore().enumerateContacts(with: fetchRequest) { (contact, stop) -> Void in
contacts1.append(contact)
print(contact)
}
} catch let e as NSError {
print(e.localizedDescription)
}
I have implemenetd to play iTunes songs from my tvOS application. I wants to give the feature to purchase songs from my application for this i have following code -
var iTunesLink = "https://itunes.apple.com/us/album/crazy-naked-girls/id310568758?i=310568759&uo=4&at=xxxxxxWa&app=itunes";
if let appStoreURL = NSURL(string: iTunesLink as String) {
if (UIApplication.sharedApplication().canOpenURL(appStoreURL) == true) {
UIApplication.sharedApplication().openURL(appStoreURL)
}
}
but this line UIApplication.sharedApplication().canOpenURL(appStoreURL) always giving false value.