Is there a way of using Logger/OSLogStore such that logging performed within an extension can later be retrieved by the application?
For example suppose an app is logging like this:
let logger = Logger(subsystem: "com.mysubsystem.log", category: "app")
logger.debug("app stuff")
and suppose an extension (such as a notification service extension for example) logs like this:
let logger = Logger(subsystem: "com.mysubsystem.log", category: "ext")
logger.debug("ext stuff")
Then next time the app runs, if it attempts to retrieves the log with code such as:
let logStore = try! OSLogStore(scope: .currentProcessIdentifier)
let oneHourAgo = logStore.position(date: Date().addingTimeInterval(-3600))
let allEntries = try! logStore.getEntries(at: oneHourAgo)
for entry in allEntries {
look at the content of the entry
Then within the for loop, there's lots and lots of stuff retrieved, including "app stuff", but there is no "ext stuff".
Is there anyway the application can retrospectively retrieve logging performed within the extension?
Related
I'm using 4 extensions within my app and use a group in combination with UserDefaults.init(suiteName:) to share settings between the extensions and the app.
However I've just tried adding an unwanted communications extension and found that data writing to the defaults, using the exact same way as its written in the other extensions, isn't saved.
At first I noticed data written by the UCE wasn't present when the app tried to read it, so performed an experiment and found that while the extension is running it can write data to user defaults and read it back, but the next time the extension runs, all that data has gone.
I've tried using the old UserDefaults.synchronize() method after writing the data but that makes no difference.
Why is the UC extension different from every other extension? Is it possible to write and persist data from within it?
let groupName = "group.com.mycompany.appName"
let sharedDefaults = UserDefaults.init(suiteName: groupName)
var theValue = sharedDefaults!.value(forKey: "some key")
NSLog("\(theValue)") // prints nothing, despite the extension having previously run
sharedDefaults!.set("some value", forKey: "some key"))
sharedDefaults!.synchronize()
theValue = sharedDefaults!.value(forKey: "some key")
NSLog("\(theValue)") // prints "some value"
I have a iOS app written in swift, and I've run into a problem. I have the data storage for my app on one firebase/cloud firestore project, and the licensing for this app (among others) on another firebase/cloud firestore server. I'm able to get the iOS app working with my data storage firebase project, but cannot for the life of me access my licensing server to validate logins and check if products are licensed. Is there a "correct" way of doing this/"proper" way to do this? Thanks!
edit: My data firebase project is working fine, in my appdelegate I have this code (keys changed obv):
let secondaryOptions = FirebaseOptions(googleAppID: "appIDKEY",
gcmSenderID: "senderID")
secondaryOptions.apiKey = "apiKey"
secondaryOptions.projectID = "project"
secondaryOptions.bundleID = "com.bundle.id"
secondaryOptions.clientID = "clientID.apps.googleusercontent.com"
secondaryOptions.databaseURL = "https://databaseurl.firebaseio.com"
secondaryOptions.storageBucket = "url.appspot.com"
secondaryOptions.appGroupID = nil
FirebaseApp.configure()
FirebaseApp.configure(name: "licensing", options: secondaryOptions)
then, in my Global Environment, I have this code:
let licensingApp = FirebaseApp.app(name: "licensing")
let licensingDB = Firestore.firestore(app: licensingApp)
let db = Firestore.firestore()
On the "let licensingDB = Firestore.firestore(app: licensingApp)" line, I get the error:
Cannot use instance member 'licensingApp' within property initializer; property initializers run before 'self' is available
What am I doing wrong?
Is it possible to load data from app's local database in app extension?
I have one requirement to use app's local database in app extension.
Is it possible? if yes then give some sources.
The only way you can share data between the main app and the extension is through userDefaults with suiteName by using app groups , so if you can write all your data from database to defaults then it'll be shared , but the problem is that it's not a recommended way as always defaults holds configuration data , also extension must be launched first so app can read data from it
You have to use Application group identifier, the you can share data with the NSUserDefaults. https://developer.apple.com/library/content/documentation/General/Conceptual/ExtensibilityPG/ExtensionScenarios.html
You can use the same Application group identifier to acces a shared directory:
NSURL *groupURL = [[NSFileManager defaultManager]
containerURLForSecurityApplicationGroupIdentifier:
#"com.yourapp.domain"];
Extension can use the network to fetch data.
You can share everything between a host app and its app extensions:
Keychain via keychain sharing
UserDefaults via app groups
Files via app groups
Database via SQLite file and app groups
To share a database between host app and extension you simply put the database file into the shared storage of the app group. This way, any change in the database of the host app is instantly available in the database of the extension and vice versa. With the CoreStore framework it is very easy to set up a database in the app group storage:
import CoreStore
let dataStack: DataStack = {
let dataStack = DataStack(xcodeModelName: "App")
let storagePathUrl = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.company.App")!.appendingPathComponent("App.sqlite")
do {
try dataStack.addStorageAndWait(SQLiteStore(fileURL: storagePathUrl, configuration: "Default", localStorageOptions: .
recreateStoreOnModelMismatch))
} catch let error {
print("Cannot set up database storage: \(error)")
}
return dataStack
}()
An example to insert a user object (User must be definded in your App.xcdatamodeld) into the database would then be:
dataStack.perform(asynchronous: { transaction in
let user = transaction.create(Into<User>())
user.email = "user#test.com"
user.name = "test"
}, completion: { _ in })
Please note that you must handle locking by yourself if host app and extension run at the same time and write to the database at the same time.
So, I have been coming across a problem where my Firebase app does not update user values when a user makes an update. To be more clear: Lets say user 1 has a photo of a dog and then changes it to a cat.
Once they change it to a cat, my node value in Firebase is successfully updated but the user themselves won't be able to see the change in other previously loaded areas in the app (other places with the dog picture) until they log out and then log back in.
For this reason I was wondering if there was any way to conduct a background app refresh that way all previous dog values in the app are changed to cat values without the user having to log out and then log back in. Please note that this same problem occurs not only with my user's profile picture but also any other user field I have setup.
Here is how I am updating a node value for my user in Firebase:
let storageRef = FIRStorage.storage().reference()
_ = FIRStorageMetadata()
let filePath = "\(FIRAuth.auth()?.currentUser?.uid)/\("userPhoto")"
let profileImageData = UIImageJPEGRepresentation(self.profilePicture.image!, 1.0)
if let data = profileImageData {
storageRef.child(filePath).put(data, metadata: nil){(metaData,error) in
if let error = error {
print(error.localizedDescription)
return
} else {
let downloadURL = metaData!.downloadURL()!.absoluteString
let userPhotoUpdateRef = FIRDatabase.database().reference().child("users").child(self.currentUser).child("userPhoto")
userPhotoUpdateRef.setValue(downloadURL)
}
}
}
If you have any questions please ask! Any help would be appreciated!
The Firebase SDK for Cloud Storage provides an easy way to read file from and write files to cloud storage. It does not provide a way to monitor those files.
The easiest way to provide a monitoring approach is to write the metadata of the files to the Firebase Realtime Database. See this short section in the Storage docs for a brief mention of that: https://firebase.google.com/docs/storage/ios/file-metadata#custom_metadata
When you write data to a location in the Firebase Database, all apps that are actively monitoring that location will be instantly updated. When they get that update, you can reload the image from Cloud Storage for Firebase.
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.