Reading and Writing to multiple Firestore Databases - ios

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?

Related

Can OSLogStore access an app's extension's logging?

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?

How To Use Shared Container/App Groups To Access Files From Other Targets in Swift

I am new to Swift.
I am trying to make a two-app project, where one app contains some data files and the other accesses those files.
The solution, I think, has been to use the App Groups entitlements to allow for this, and access the files through those means. I have been able to follow along with the example here: Communicating and persisting data between apps with App Groups. In particular, the 2nd answer, which is Swift-ish (maybe an older version I guess?). It does seem to work with the right entitlements. So, now the question is how can I access the file from one app, with it being apart of the another? I'm not familiar with the API's and correct functions that I can use (new to Swift, as I said).
The apps are basic. Setup as single view applications, with everything defaulted except the ViewController's, entitlements, and one has the test data. The Xcode project structure is:
testingData/
testingData/
testingData.entitlements
TestData/
testdata.txt
AppDelegate.swift
ViewController.swift
Main.storyboard
Assets.xcassets
LaunchScreen.storyboard
Info.plist
sharedContainerTest/
sharedContainerTest.entitlements
AppDelegate.swift
ViewController.swift
Main.storyboard
Assets.xcassets
LaunchScreen.storyboard
Info.plist
Products/
testingData.app
sharedContainerTest.app
The entitlements are both the same. They each have App Groups enabled, with the same string: group.com.example.name. On the testingData target, the ViewController has the following chunk in the viewDidLoad function from that example (modified for Swift 4.x):
var userDefaults = UserDefaults(suiteName: "group.com.example.name")!
userDefaults.set("user12345", forKey: "userId")
userDefaults.synchronize()
On the sharedContainerTest target, its ViewContoller has
var userDefaults = UserDefaults(suiteName: "group.com.example.name")
if let testUserId = userDefaults?.object(forKey: "userId") as? String {
print("User Id: \(testUserId)")
}
in its viewDidLoad function. As I said, this seems to work, but now what do I need to add to this to access the testdata.txt file from the testingData app? Does it need to be stored as a variable, perhaps? Or is there a Bundle object that can do this?
If this has been answered elsewhere, please kindly point me to it and I'll take this down.
After some trial and error, the answer is as follows:
Instead of passing in the string "user12345", you need to pass in the URL to the file you want to read for the userDefaults.set method as follows:
var userDefaults = UserDefaults(suiteName: "group.com.example.name")!
userDefaults.set(Bundle.main.url(forResource: "testdata", withExtension: ".txt"), forKey: "userId")
userDefaults.synchronize()
Then in the receiver app, you call that object and set the URL:
let userDefaults = UserDefaults(suiteName: "group.com.example.name")
let test = userDefaults?.object(forKey: "userId") as? String
let testURL = URL(fileURLWithPath: test!)
From here you can read in the contents as normal.

Is it possible to load data from app's local database in app extension?

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.

New Firebase - Cannot query data. What is the new syntax?

I am trying to move to the new Firebase from the old one (launched today, May 18th, 2016), but the query syntax is unclear to me. The only example I can find to make a query is via a listener.
I've followed the docs (https://firebase.google.com/docs/database/ios/retrieve-data), but they are very unclear as to how to simply query one piece of data in the Firebase realtime DB.
The old docs were fine, but the new ones are terrible, imo. Also, their examples on github, where I got some of the code I'm now trying to use, are overly complex (https://github.com/firebase/quickstart-ios/tree/master/database/DatabaseExampleSwift).
The code runs fine, I have a new project set up in the Firebase admin console online, I have the new plist file set up and in xcode, and everything compiles fine. I just need code to easily access one measly piece of data! Plz help.
Thanks.
Here's my code (runs fine / is configured fine), but can't display the data:
class LatestNewsTableViewController: UITableViewController {
// get the Feed URL from Firebase:
lazy var FireBaseRef: FIRDatabaseReference = FIRDatabase.database().reference()
var FeedURLRef: FIRDatabaseReference!
var refHandle: FIRDatabaseHandle?
Then, in viewDidLoad(), I'm doing this:
FeedURLRef = FireBaseRef.child("AppGlobals").child("FeedURL")
refHandle = FireBaseRef.observeEventType(FIRDataEventType.Value, withBlock: { (snapshot) in
let FirebaseFeedURL = snapshot.value as! [String : AnyObject]
print("FirebaseFeedURL = " + String(FirebaseFeedURL))
})

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