UIPasteboard not giving value - ios

An object that helps a user share data from one place to another within your app, and from your app to other apps.
This is the statement written at the very beginning of UIPasteboard docs. But when I try to use it in two different apps accessing data set by other app I am getting nil everytime
DispatchQueue.global(qos: .background).async {
var i = 1
while(i > 0) {
let v = UIPasteboard.general.string
sleep(1)
print("Task : \(i)")
print("Value: \(v)")
i = i + 1
}
}
I am fetching data in above code and setting data as in below code.
UIPasteboard.general.string = "Hello"
NB: I have tested locally in this app it is setting data

Are you running iOS >=10? There was a privacy change regarding passing value between apps. Try reading the UIPasteBoard api doc : (https://developer.apple.com/documentation/uikit/uipasteboard).
Tl:dr You need to have both apps to be in the same app group (Communicating and persisting data between apps with App Groups)
To note: iOS apps are sandboxed. So the change in iOS 10 just enforces that feature.
EDITED: Since you can't use App Groups (different developer and/or products), you have to send data via a different channel. Try searching urlSchemes or store/fetch through a common server(tedious tho)

Related

Swift - UserDefaults setting not getting saved inside framework

I have a framework that is generating a device UUID once and saving it using UserDefaults. The app has access to the UserDefaults and everything works as expected. However, the framework is not accessing UserDefaults in some cases.
I sorted this out on an iPhone 8 using the synchronize() method:
func getDeviceID() -> String {
if let device = UserDefaults.standard.object(forKey: "DeviceID") as? String {
return device
} else {
let device = UUID().uuidString
UserDefaults.standard.set(device, forKey: "DeviceID")
UserDefaults.standard.synchronize() // this line helped with an iPhone 8
return device
}
}
However, on an older iPhone SE 1st generation the issue comes back.
First, why is this happening at all, and why is the synchronize() method seemingly helping in a newer device? (Both phones are running iOS 13)
Are there any known limitations when accessing UserDefaults from within a framework?
If it is failing when you're reading data right after writing the deviceId to UserDefaults
Then it could be related to how UserDefaults actually stores the data to disk.
The actual write to disk is asynchronous and batched automatically by NSUserDefaults.
Check this
So there's a chance that it is slower for older devices running new iOS versions.

Is sharing UserDefaults between iOS and tvOS still possible?

Is sharing UserDefaults between iOS and tvOS still possible?
In my Xcode project I use UserDefaults to share data between my iOS target and my tvOS target. I always receive nil values from my UserDefaults when I try to get data back from my tvOS app.
These are the steps I took to share data:
1: Add App Groups for both targets. Both targets use the same App Group ID:
group.nl.mycompany.myappname.
I use the .nl domain but this should be fine since this also worked for my other projects.
2: Confirm both targets have the same deployment target. I tried using 10.0 and 11.0.
3: Validate the myproject.entitlements that everything is set OK.
4: Validate that on developer.apple.com the App Group is enabled for my bundle identifier.
5: Both targets have the same bundle ID. I also tried using 2 different bundle identifiers.
6: The way I write to UserDefaults from my iOS app:
guard let defaults = UserDefaults(suiteName: "group.nl.mycompany.myappname") else { return }
defaults.set("Apple", forKey: "username")
defaults.synchronize()
I confirm this works in my iOS app by getting the value like so:
guard let defaults = UserDefaults(suiteName: "group.nl.mycompany.myappname") else { return nil }
defaults.synchronize()
let name = defaults.string(forKey: "username")
This indeed returns "Apple".
7: Opening my tvOS app and calling this code returns nil:
guard let defaults = UserDefaults(suiteName: "group.nl.mycompany.myappname") else { return nil }
defaults.synchronize()
let name = defaults.string(forKey: "username")
Is it possible that UserDefaults sharing has been removed? Something similar happened to sharing UserDefaults between your phone and watch link here. I also read that the maximum size of UserDefaults is 500kb for the AppleTV but saving this simple string should be fine.
Apple clearly states in the UserDefaults documentation that
With the exception of managed devices in educational institutions, a
userโ€™s defaults are stored locally on a single device, and persisted
for backup and restore. To synchronize preferences and other data
across a userโ€™s connected devices, use NSUbiquitousKeyValueStore
instead.
As it says, you should use iCloud-based NSUbiquitousKeyValueStore for synchronized data storage.
As for its (NSUbiquitousKeyValueStore) limits, the documentation says
The total amount of space available in your appโ€™s key-value store, for a given user, is 1 MB. There is a per-key value size limit of 1 MB, and a maximum of 1024 keys.

iOS Notification Content Extension - How to pass data to app?

I wrote a custom Notification Content Extension for my Push Notifications like this:
The thing is, whenever the user is on a certain item in the carousel, I want the GO TO APP button to send a String to the app when it's opening, and from there, handle that string to move the user to the correct ViewController.
I already have the handling part inside the app, I just need to know how to pass that String from the Notification Content Extension to the container app.
Thanks! :)
Enable app groups in capabilities and use suite userDefaults to write the key and read it in the app
NSUserDefaults*defaults= [[NSUserDefaults alloc] initWithSuiteName:#"group.com.company.appName"];
// Write in extension
[defaults setObject:#"anyThing" forKey:#"sharedContent"];
// Read in app
[defaults objectForKey:#"sharedContent"];
If your app is configured for Universal Links or you have defined a Custom URL Scheme for your app, you can also open your app's URL (e.g. with data in query parameters) by calling
extensionContext?.open(url)
in your NotificationViewController.
iOS 13, Swift 5.
Based on the answer by Sh_Khan, here is some Swift Syntax. Obviously I have added App Group as a capability to the target of the main app + the target of the extension, naming the group as "group.ch.Blah" for this example.
Setting your app group, saving a string in our case, needed to set the type as Any cause strings not a type that is available in groups.
let localK = getPrivateKey64() as Any
let defaults = UserDefaults.init(suiteName: "group.ch.Blah")
defaults?.set(localK, forKey: "privateK")
Setting your app group, and reading the string back, needed to recast it back to string.
let defaults = UserDefaults.init(suiteName: "group.ch.Blah")
let localK = defaults?.object(forKey: "privateK") as? String
Worked perfectly with a notification service extension.

Access most recently used emojis list in iOS

In iOS, in the native Emoji keyboard, you can see your most recently used Emojis. I would like to know if it is possible to get the data on those Emojis (which is app-independent) from inside my app.
My goal is to display the most used emoji, given a user, in my app.
If you just want an Emoji selector you could use/modify libraries like SYEmojiPopover or AGEmojiKeyboard which allows you to have full control on the output without messing with the iOS internals (albeit the "recents" list will be app-specific).
On iOS 9 the preferences are stored in the com.apple.EmojiPreferences suite, which you could extract the list of most recently used emoji by:
// swift 3:
let prefs = UserDefaults(suiteName: "com.apple.EmojiPreferences")!
let defaults = prefs.dictionary(forKey: "EmojiDefaultsKey")!["EmojiRecentsDefaultsKey"]! as! [String: Any]
let recents = defaults["RecentsKey"]! as! [String]
print(recents)
// swift 2:
let prefs = NSUserDefaults(suiteName: "com.apple.EmojiPreferences")!
let recents = prefs.dictionaryForKey("EmojiDefaultsKey")!["EmojiRecentsDefaultsKey"]!["RecentsKey"]! as! [String]
print(recents)
// prints e.g. ["๐Ÿ”", "๐Ÿšณ", "๐Ÿšฟ", "โŒ›", "๐Ÿ‘ถ", "๐Ÿ‡ฟ๐Ÿ‡ฆ", "โ›ช", "๐Ÿš†", "๐Ÿš…"]
Note that this is UNDOCUMENTED, and I have only checked it works on iOS 9 when deployed via Xcode. There is no guarantee that the App Store reviewers will allow this usage, nor there is any guarantee that it will work in the past or future versions.

Notification of changes in Core Data SQLite store between iPhone and Apple Watch app

I have an iPhone (iOS 8) and Apple Watch (watchOS 1) apps that share their data using Core Data (SQLite store, placed in shared app group). Both apps are using the same data access code that is placed in shared framework. NSPersistentStoreCoordinator is being set up in the following way:
lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
let sharedContainerURL = NSFileManager.defaultManager().containerURLForSecurityApplicationGroupIdentifier(self.sharedAppGroup)!
let storeURL = sharedContainerURL.URLByAppendingPathComponent(self.databaseName)
let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
var error: NSError? = nil
if coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeURL, options: nil, error: &error) == nil {
fatalError("Unable to add persistent store: \(error)")
}
return coordinator
}()
From my understanding, in runtime each app has its own NSPersistenStoreCoordinator instance (as iPhone apps and WatchKit extensions do have completely separate address space), but these two connect to exactly the same SQLite database file.
How can an iPhone app be notified when Watch app changes some data in the common SQLite store and the other way around: how can a Watch app be notified when the iPhone app changes some data in the common persistent store?
The solution that I've found quite satisfactory was to use MMWormhole library.
It works by using CFNotificationCenter Darwin Notifications and writing/reading data files in the shared app group, which results in instant communication between an iOS app and an app extension (Watch app, today's widget, etc).
Basic code goes like this:
Wormhole initialization
wormhole = MMWormhole(applicationGroupIdentifier: appGroup, optionalDirectory: nil)
Passing data object to a wormhole
let payload = ["Key": "Value"]
wormhole.passMessageObject(payload, identifier: theSameMessageIdentifier)
Listening for incoming object from a wormhole
wormhole.listenForMessageWithIdentifier(theSameMessageIdentifier) { message -> Void in
if let payloadDictionary = message as? Dictionary<String, String> {
// Do some work
}
}
It's as simple as that.
Not easily. There is no way to send a direct communication between the two applications.
My current recommendation in this regard is to use files on disk that include the objectIDs of anything that has changed from one app to the other.
When you detect a save to disk you write a file that, for example, is JSON and includes up to three arrays: update, insert, delete. The file name should be some form of timestamp.
Separately you should be watching the directory for any files created by the other app and consume them. Load the ObjectIDs and then create a notification out of the ala iCloud or in iOS 9 a remote notification. Then delete the file after process.
On launch, remove all files from the other store since you will automatically be aware of anything that happened pre-launch.
Not simple but fairly straight forward.

Resources