iOS Swift: Firebase Remote Config fetch values - ios

Is there a way or delegate to catch the updated values while the app is running without terminating the app. I am using this method to fetch the values and update.
RemoteConfig.remoteConfig().fetch(withExpirationDuration: duration) { [weak self] (status, error) in
guard error == nil else {
print("Got an error fetching remote values \(error!)")
return
}
print ("Retrieved values from the cloud!")
RemoteConfig.remoteConfig().activateFetched()
guard let strongSelf = self else { return }
strongSelf.updateWithRomteConfigValues()
}

The activateFetched() call will make sure to get the latest update data (either from the defaults or the remote config) without needing to terminate the app.
I think the problem in your case come from the duration.
Try setting the duration to 0 (make sure to only do it if the developer mode is enabled )

Related

In which situation adding a new video asset to the Photos library fails?

I'm working on an iOS app that allows users to save the captured video on their iOS devices. After some research, now I'm able to save the video on the device, but I was wondering what kind of situation the app fail to save the video. In the completion block of performChages, it takes two parameters, saved and error in the code below. Since the first parameter is a type of Bool, I think there are some cases when fails to save the video on the device. But it also has an error, so I was wondering what situation could return an error.
success
true if Photos successfully applied the changes requested in the block; otherwise, false.
error
If an error occurs, an NSError object describing the error; otherwise,
nil.
Another question is how can I handle when I get success = false in the completion handler. Is it possible to try save the video once more when I get success as false?
guard let writer: AVAssetWriter = recorder.writer else { return }
PHPhotoLibrary.shared().performChanges({() -> Void in
PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: writer.outputURL)
}, completionHandler: {saved, error -> Void in
if let error = error {
print("error: \(error)")
} else if saved {
print("saved")
} else {
print("failed")
}
})

MPMusicPlayerControllerMutableQueue insert an Apple Music song not working

So I have been trying to use the MusicKit APIs for a few days now. I have been attempting to use the MPMusicPlayerApplicationController and MutableQueue APIs.
I have queue initialized already using setQueue(with: [String]) with an array of store identifiers for Apple Music songs. Then I want to allow the user to reorder the songs in the queue. I use the following code to attempt that.
let musicPlayerController = MPMusicPlayerController.applicationQueuePlayer
musicPlayerController.perform(queueTransaction: { queue in
let afterItem = queue.items.first(where: { $0.playbackStoreID == predecessorId })
let descriptor = MPMusicPlayerStoreQueueDescriptor(storeIDs: [newItemId])
queue.insert(descriptor, after: afterItem)
}) { (queue, error) in
// Completion for when items' position update
if error != nil {
print(error!)
}
}
The code above works as expected if afterItem is nil (i.e. the song is correctly inserted at the front of the queue). However, if afterItem is not nil, nothing happens. The queue stays the exact same as if no insert happened and there is no error provided in the completion handler. This problem happens regardless of whether the song being inserted is already in the queue or not.
Am I attempting modifying the queue incorrectly?
Ok, I found the solution.
If you want the queue to be mutated.
You need to return the query
let musicPlayerController = MPMusicPlayerController.applicationQueuePlayer
musicPlayerController.perform(queueTransaction: { queue in
let afterItem = queue.items.first(where: { $0.playbackStoreID == predecessorId })
let descriptor = MPMusicPlayerStoreQueueDescriptor(storeIDs: [newItemId])
//return the modification here.
return queue.insert(descriptor, after: afterItem)
}) { (queue, error) in
// Completion for when items' position update
if error != nil {
print(error!)
}
}

How to use observeSingleEvent in Firebase with callback when not connected?

I am building iOS App using Firebase, I found that the method observeSingleEvent(with or without cancel block) in Firebase would not even fire if the app is not connected to the network and there is no cached value for the location.
I need to show messages to users when the App lose connection to internet but no need to do it if there is cached value for the location when use keepSynced to it.
How can I do if the API without an error that can detect connect or not in return? Since the document says the cancelBlock will be called if you don't have permission to access this data, but it even not callback when the App without connection.
if and else statetment check your problem ? If it is connected, you will be call the observeSingleEvent function
let connectedRef = Database.database().reference(withPath: ".info/connected")
connectedRef.observe(.value, with: { snapshot in
if snapshot.value as? Bool ?? false {
print("Connected")
} else {
print("Not connected")
}
})
More detail : Detecting Connection State

HKObserverQuery in Background mode

I have an application that need to track user heart rate readings from apple watch, so I did all the required steps that I found on apple guides, and here is the code that I am using:
static var query: HKObserverQuery?
func startObservingHeartRate() {
guard let heartRateSampleType = HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate) else {
fatalError("Unable to create a step count sample type")
}
AppDelegate.query = HKObserverQuery(sampleType: heartRateSampleType, predicate: nil, updateHandler: { (query, completionHandler, error) in
if error != nil {
// Perform Proper Error Handling Here...
print("An error occured while setting up the Heart Rate observer.")
}
//Read the last strored heatt rate in add it to the DB
//Add last fetched Heart Rate reading to DB and send it to clips
HealthKitManager().fetchLastStoredHeartRate(completion: { (lastReading, error) in
guard let lastReading = lastReading else {
//There is no heart readings in HealthKit
return
}
//Check if Last HR value is Abnormal
if lastReading.doubleValue > 60 {
//TODO: - Schedule notification
if UIApplication.shared.applicationState == .background {
} else {
//TODO: - Show popup to the user
}
}
})
completionHandler()
})
healthKitStore.execute(AppDelegate.query!)
configureHeartRateObserver()
}
func configureHeartRateObserver() {
guard let heartRateSampleType = HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.heartRate) else {
fatalError("Unable to create a step count sample type")
}
healthKitStore.enableBackgroundDelivery(for: heartRateSampleType, frequency: HKUpdateFrequency.immediate) { (success, error) in
if success {
print("Enabled background delivery of Heart Rate changes")
} else {
print("Failed to enable background delivery of weight changes. ")
}
}
}
and I am calling "startObservingHeartRate" in didFinishLaunchingWithOptions in AppDelegate, assuming that this query should be executed once a new reading added or deleted from the health kit store, every thing is fine, if app is in background or killed the handler wake up my app and it do the updates.
But whenever I put the app in background then put it in foreground again it execute the observer query for many times even if there is no new readings added to the HealthKit store and in this case I am getting the same last heart rate for many times for no reason.
Please any recommendation on how to use this types of query or any changes I need to do with my current implementation.
If you want to track added and removed heart rate samples more precisely, you should use an HKAnchoredObjectQuery. HKObserverQuery does not guarantee that its update handler will only be called when a sample is added or removed. Note that you must continue executing an HKObserverQuery in addition to HKAnchoredObjectQuery since you are also using enableBackgroundDelivery(for:frequency:completion:).

Firebase Storage Upload Fails in Adverse Network Conditions iOS

Firebase Storage claims here in its iOS documentation that it
performs uploads and downloads regardless of network quality. Uploads and downloads are robust, meaning they restart where they stopped
so one would expect it to handle a loss of connection when uploading, but it doesn't seem to.
With the following Swift code in iOS, I am able to perform an upload just fine when there is a connection, but if the device doesn't have a connection or if it is ever disconnected from the network it goes to the failure condition.
let storage = FIRStorage.storage().referenceForURL("VALID_URL_REMOVED")
let imagesRef = storage.child("images/test.jpg")
let data = UIImageJPEGRepresentation(observationImage!, 0.7);
let uploadTask = imagesRef.putData(data!, metadata: nil)
uploadTask.observeStatus(.Progress) { snapshot in
// Upload reported progress
if let progress = snapshot.progress {
let percentComplete = 100.0 * Double(progress.completedUnitCount) / Double(progress.totalUnitCount)
print("percent \(percentComplete)")
}
}
uploadTask.observeStatus(.Success) { snapshot in
// Upload completed successfully
print("success")
}
uploadTask.observeStatus(.Failure) { snapshot in
print("error")
print(snapshot.error?.localizedDescription)
}
The debug output for this code is as follows.
/*
percent 0.0
percent 0.0044084949781492
2016-06-30 11:49:16.480 Removed[5020:] <FIRAnalytics/DEBUG> Network status has changed. Code, status: 1, Disconnected
percent 0.0044084949781492
error
Optional("An unknown error occurred, please check the server response.")
*/
Firebase's Real Time Database offline storage is also set up with the following code, but I'm unsure of whether this is related.
FIRDatabase.database().persistenceEnabled = true
I have also tried manually setting the timeout as mentioned in the answers to this question using the following lines, with no change.
let config = FIRStorage()
config.maxUploadRetryTime = 1000000
Is there a way to have it handle these disconnects without implementing the functionality from scratch? Am I missing something?
You are missing observers. Right now you only observe .success and .failure events. Try add observers for .resume, .pause, .progress to handle different events.
// Listen for state changes, errors, and completion of the upload.
uploadTask.observe(.resume) { snapshot in
// Upload resumed, also fires when the upload starts
}
uploadTask.observe(.pause) { snapshot in
// Upload paused
}
uploadTask.observe(.progress) { snapshot in
// Upload reported progress
let percentComplete = 100.0 * Double(snapshot.progress!.completedUnitCount)
/ Double(snapshot.progress!.totalUnitCount)
}
uploadTask.observe(.failure) { snapshot in
if let error = snapshot.error as? NSError {
switch (FIRStorageErrorCode(rawValue: error.code)!) {
case .objectNotFound:
// File doesn't exist
break
case .unauthorized:
// User doesn't have permission to access file
break
case .cancelled:
// User canceled the upload
break
/* ... */
case .unknown:
// Unknown error occurred, inspect the server response
break
default:
// A separate error occurred. This is a good place to retry the upload.
break
}
}
}

Resources