I am getting started with RemoteConfig on iOS with Swift and followed the tutorial to get started. I have developer mode enabled and tested the updating of config values via the Firebase console. However, the update values never get synced with the local config values.
Code:
override func viewDidLoad() {
super.viewDidLoad()
syncRemoteConfig()
}
fileprivate func syncRemoteConfig() {
let remoteConfig = RemoteConfig.remoteConfig()
#if DEBUG
let settings = RemoteConfigSettings()
settings.minimumFetchInterval = 0
remoteConfig.configSettings = settings
#endif
remoteConfig.fetchAndActivate { (status, error) in
let posts = remoteConfig.configValue(forKey: "posts").jsonValue as! [[String: AnyObject]]
print(posts) // <== Always print the previous data
if let error = error {
print(error.localizedDescription)
}
//status always prints status.successUsingPreFetchedData
}
}
Run pod update to at least Firebase 6.25.0.
It fixed a race condition bug in which the minimumFetchInterval might not have been applied before the fetch.
Related
I have a problem with a FireStore, hope some of you could help me.
I'm trying to create a document this way:
class FireStoreUtils: NSObject {
static let defaultUtils = FireStoreUtils()
var db = Firestore.firestore()
var fireStoreSettings = FirestoreSettings.init()
override init() {
super.init()
fireStoreSettings.host = FireStoreParameters.host
fireStoreSettings.isSSLEnabled = FireStoreParameters.sslEnabled
fireStoreSettings.isPersistenceEnabled = FireStoreParameters.persistenceEnabled
fireStoreSettings.cacheSizeBytes = Int64(FireStoreParameters.cacheSizeBytes)
db.settings = fireStoreSettings
}
weak var delegate:FireStoreUtilsDelegate?
func addNewUser(userData userDictionary: [String: Any],userId uId: String) {
do {
let table = self.db.collection("Collection_name")
let documentRef = table.document(uId)
try? documentRef.setData(userDictionary, completion: { (error) in
if (error != nil) {
self.delegate?.didFailToPerformOperation(errorMessage: error?.localizedDescription)
}
else {
self.delegate?.didSucceedToPerformOperation(message: "Success")
}
})
}
catch let error {
self.delegate?.didFailToPerformOperation(errorMessage: error.localizedDescription)
}
}
But from some reason executing setData I get the following crash
assertion failed: 0 == pthread_setspecific(tls->key, (void)value)*
I tried to use FireStore logs but the last log message I got is
2021-01-17 18:55:12.114707+0200 7.3.0 - [Firebase/Firestore][I-FST000001] Creating Firestore stub.
I added Firestore manually and din't use nor Swift PM or Pods, because both of the didn't work with FirebaseAuth in pod file.
It starts to look like everything is dead end.
If somebody knows what's going on or got similar problem, please,help!
Thank you,
Maria.
I have this code, which should append messages with a new message:
func addMessage(_ message: Message) {
do {
try Realm().write {
self.messages.append(message)
}
} catch let error {
print("could not add message due to error:\n\(error)")
}
}
However, I get an exception Cannot modify managed RLMArray outside of a write transaction It doesn't make any sense to me, because I'm already in a write transaction...
You need to create a Realm object before applying the write module.
According to the GitHub documentation, you can try code like this:
func addMessage(_ message: Message) {
do {
let realm = try! Realm()
try! realm.write {
self.messages.append(message)
}
} catch let error {
print("Could not add message due to error:\n\(error)")
}
}
Hope it helps!
You can use let realm = try! Realm() with a custom default configuration by setting a default configuration see here:
var config = Realm.Configuration()
// Set this as the configuration used for the default Realm
Realm.Configuration.defaultConfiguration = config
The trouble was I was using a plain Realm object with no special configuration. Since I am using Realm Mobile Platform, I needed to create a Realm object with the same config each time I want to write to that DB:
let configuration = Realm.Configuration(
syncConfiguration: SyncConfiguration(user: user, realmURL: URL(string: "realm://127.0.0.1:9080/~/speciail")!)
)
self.realm = try! Realm(configuration: configuration)
//now do the write transaction!
It took a bit of refactoring, but I have it now. My thanks to those of you who took the time to help me.
For some reason the upload to S3 does work not whereas I was able to get the download function to work just fine.
Here is my upload code:
#IBAction func uploadFile(_ sender: UIButton) {
let CognitoRegionType = AWSRegionType.USWest2 // e.g. AWSRegionType.USEast1
let CognitoIdentityPoolId = "us-west-2:3c00122a-866c-4ce4-9dd3-ee23c16e58f3"
let DefaultServiceRegionType = AWSRegionType.USWest1 // e.g. AWSRegionType.USEast1
let S3BucketName = "snappcastphotos"
let credentialsProvider = AWSCognitoCredentialsProvider(regionType:CognitoRegionType, identityPoolId: CognitoIdentityPoolId)
let configuration = AWSServiceConfiguration(region: DefaultServiceRegionType , credentialsProvider:credentialsProvider)
AWSServiceManager.default().defaultServiceConfiguration = configuration
let uploadRequest = AWSS3TransferManagerUploadRequest()
uploadRequest?.bucket = S3BucketName
uploadRequest?.key = "bingo"
uploadRequest?.body = URL(fileURLWithPath: "/users/rhom/desktop/test.rtf")
let transferManager = AWSS3TransferManager.default()
transferManager.upload(uploadRequest!).continueWith(executor: AWSExecutor.mainThread(), block: { (task:AWSTask<AnyObject>) -> Any? in
if let error = task.error as? NSError {
if error.domain == AWSS3TransferManagerErrorDomain, let code = AWSS3TransferManagerErrorType(rawValue: error.code) {
switch code {
case .cancelled, .paused:
break
default:
print("Error uploading: \(uploadRequest?.key) Error: \(error)")
}
} else {
print("Error uploading: \(uploadRequest?.key) Error: \(error)")
}
return nil
}
let uploadOutput = task.result
print("Upload complete for: \(uploadRequest?.key)")
return nil
})
}
and this code produces the following error/output:
Error uploading: Optional("bingo") Error: Error Domain=com.amazonaws.AWSS3ErrorDomain Code=0 "(null)" UserInfo={HostId=KAsbvIqiY67dr/64f3uvZPB1Lr5Vj7eNNV198DLai/RG/tA+v3To8CBbnbFSM00V2COZnzebx/M=, Message=The request body terminated unexpectedly, Code=IncompleteBody, RequestId=A95236E53A1B8FE8}
Anyone have any ideas? I can't find any clue as to why the download works using Cognito and S3 but the upload doesn't work....
It looks more of an authorization error. Check if you are having the write permissions for this particular bucket and also check the path you are giving is correct, as if there is a mismatch it wont work.
I finally figured this problem out :)
It turns out that I had installed an older version of AWS Frameworks as I was following an older tutorial to get started... and had loaded the frameworks using Cocoapods specifying in the podfile for IOS 8.0 but I am IOS 10.0 now!!!! so changing the podfile to version 10.0 fixed the problem.
I ended up reinstalling AWS Frameworks using Cocoapods and the correct podfile with 10.0... but I think there is a way to just update the pod but I will look into that later
Hope this helps anyone who has this problem too :)
I set breakpoints all throughout this query to Firebase, and all that's happening is the breakpoint for the following line gets hit (which is the very first line), and then no others.
_CHAT_REF.observeEventType(.Value, withBlock: { snapshot in
Any idea why this would be happening? Even if there is no data, the breakpoints inside the query block should be still be getting hit, but they aren't. What I've done: I've uninstalled and reinstalled Firebase at least 10 times, using both CocoaPods and not CocoaPods, following directions to the T. I'm not getting any kind of compile error, and I'm running FIRApp.configure() in my app delegate.
Full Code (breakpoints on each line, none called only _CHAT_REF.observe line):
private var _CHAT_REF = FIRDatabase.database().reference().child("chats")
_CHAT_REF.observeEventType(.Value, withBlock: { snapshot in
self.individualMessages = []
if snapshot.hasChildren() {
// Found chats
for snap in snapshot.children {
let theChat = Chat(snapshot: snap as! FIRDataSnapshot)
// The current user belongs to this chat, so add it to individual messages.
if theChat.sender_id == GlobalEnv.currentUser.id || theChat.receiver_id == GlobalEnv.currentUser.id {
self.individualMessages.append(theChat)
}
}
} else {
// No Children
print("No children found.")
}
self.tvContacts.reloadData()
})
DB Structure:
DB Structure on Firebase
I ran into a similar problem. It turned out that I wasn't able to read/write the database from behind my organization's proxy server. I built to a device using open wifi, and it worked.
Try this and let me know if it makes a difference. It's very simplified but the variable assignments are handled differently.
assume you are in a ViewController class...
class ViewController: UIViewController {
var ref: FIRDatabaseReference!
override func viewDidLoad() {
super.viewDidLoad()
ref = FIRDatabase.database().reference()
}
func clickAction() { //just tied to an action in the UI
let chatsRef = ref.child("chats")
chatsRef.observeEventType(.Value, withBlock: { snapshot in
print("Hello, World")
})
}
}
I need to test some features of my app with just a previously selected group of users.
I created an Audience where user_id exactly matches 123456. 123456 being my own ID.
In Remote Config I created a Condition that matches users in the Audience above.
Then I created a parameter in Remote Config called feature_available and for the condition, I set a return value of true. The Default value is false.
In my app I set up Firebase:
FIRApp.configure()
let remoteConfig = FIRRemoteConfig.remoteConfig()
if let remoteConfigSettings = FIRRemoteConfigSettings(developerModeEnabled: false) {
remoteConfig.configSettings = remoteConfigSettings
}
remoteConfig.setDefaultsFromPlistFileName("FirebaseRemoteConfigDefaults")
And set the user ID:
FIRAnalytics.setUserID("123456")
Then I fetch from Firebase:
var expirationDuration: Double
// If in developer mode cacheExpiration is set to 0 so each fetch will retrieve values from the server.
expirationDuration = remoteConfig.configSettings.isDeveloperModeEnabled ? 0 : 3600
remoteConfig.fetch(withExpirationDuration: TimeInterval(expirationDuration)) { (status, error) -> Void in
if status == .success {
remoteConfig.activateFetched()
} else {
assertionFailure("Firebase config not fetched. Error \(error!.localizedDescription)")
}
}
The last thing I do is to get the value from Firebase and check if I have the feature enable:
let featureIsAvailable = remoteConfig["feature_available"].boolValue
if featureIsAvailable { ... }
The problem is that every single time the value returns from Firebase it is false and I can't manage to get it to return the correct value that matches that Audience I created.
I also tried to do it setting a user property instead of using setUserID() and had the same result.
Any suggestions?
I've run into similar issues before, sometimes it can take a while for the fetch to finish. The check if the feature is available needs to be done when the config is successfully fixed. Something like this hopefully works for you as well:
var featureIsAvailable : Bool?
override func viewDidLoad() {
super.viewDidLoad()
configureRemoteConfig()
fetchConfig()
}
func configureRemoteConfig() {
remoteConfig = FIRRemoteConfig.remoteConfig()
// Create Remote Config Setting to enable developer mode.
// Fetching configs from the server is normally limited to 5 requests per hour.
// Enabling developer mode allows many more requests to be made per hour, so developers
// can test different config values during development.
let remoteConfigSettings = FIRRemoteConfigSettings(developerModeEnabled: true)
remoteConfig.configSettings = remoteConfigSettings!
remoteConfig.setDefaultsFromPlistFileName("RemoteConfigDefaults")
}
func fetchConfig() {
var expirationDuration: Double = 3600
// If in developer mode cacheExpiration is set to 0 so each fetch will retrieve values from
// the server.
if (self.remoteConfig.configSettings.isDeveloperModeEnabled) {
expirationDuration = 0
}
// cacheExpirationSeconds is set to cacheExpiration here, indicating that any previously
// fetched and cached config would be considered expired because it would have been fetched
// more than cacheExpiration seconds ago. Thus the next fetch would go to the server unless
// throttling is in progress. The default expiration duration is 43200 (12 hours).
remoteConfig.fetch(withExpirationDuration: expirationDuration) { (status, error) in
if (status == .success) {
print("Config fetched!")
self.remoteConfig.activateFetched()
let featureIsAvailable = self.remoteConfig["feature_available"]
if (featureIsAvailable.source != .static) {
self.featureIsAvailable = featureIsAvailable.boolValue
print("should the feature be available?", featureIsAvailable!)
}
} else {
print("Config not fetched")
print("Error \(error)")
}
self.checkIfFeatureIsAvailable()
}
}
func checkIfFeatureIsAvailable() {
if featureIsAvailable == false {
// Don't show new feature
} else {
// Show new feature
}
}
I sent an email to the Firebase team requesting support and they told me that the issue was a bug in Xcode 8(.0 and .1) using Swift.
Updating to the latest version released today, 8.2, fixed the issue.