Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 67 additions & 54 deletions config/ConfigExample/RemoteConfigViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@

import UIKit
import FirebaseRemoteConfig
import FirebaseRemoteConfigSwift

class RemoteConfigViewController: UIViewController {
private var remoteConfig: RemoteConfig!
private var remoteConfigView: RemoteConfigView { view as! RemoteConfigView }

private let topLabelKey = "topLabelKey"
private let recipeKey = "recipeKey"
private let typedRecipeKey = "typedRecipeKey"
private let bottomLabelKey = "bottomLabelKey"

override func loadView() {
Expand Down Expand Up @@ -48,41 +49,42 @@ class RemoteConfigViewController: UIViewController {

// MARK: - Firebase 🔥

// Add additional keys that are in the console to see them in the log after fetching the config.
struct QSConfig: Codable {
let topLabelKey: String
let bottomLabelKey: String
let freeCount: Int
}

/// Initializes defaults from `RemoteConfigDefaults.plist` and sets config's settings to developer mode
private func setupRemoteConfig() {
remoteConfig = RemoteConfig.remoteConfig()
remoteConfig.setDefaults(fromPlist: "RemoteConfigDefaults")

// This is an alternative to remoteConfig.setDefaults(fromPlist: "RemoteConfigDefaults"))
do {
try remoteConfig.setDefaults(from: QSConfig(topLabelKey: "myTopLabel",
bottomLabelKey: "Buy one get one free!",
freeCount: 4))
} catch {
print("Failed to set Defaults.")
}
let settings = RemoteConfigSettings()
settings.minimumFetchInterval = 0
remoteConfig.configSettings = settings
}

/// Fetches remote config values from the server
private func fetchRemoteConfig() {
remoteConfig.fetch { status, error in
guard error == nil else { return self.displayError(error) }
print("Remote config successfully fetched!")
}
}

/// Activates remote config values, making them available to use in your app
private func activateRemoteConfig() {
remoteConfig.activate { success, error in
guard error == nil else { return self.displayError(error) }
print("Remote config successfully activated!")
DispatchQueue.main.async {
self.updateUI()
}
}
}

/// Fetches and activates remote config values
@objc
private func fetchAndActivateRemoteConfig() {
remoteConfig.fetchAndActivate { status, error in
guard error == nil else { return self.displayError(error) }
print("Remote config successfully fetched & activated!")
do {
let qsConfig: QSConfig = try self.remoteConfig.decoded()
print(qsConfig)
} catch {
self.displayError(error)
return
}
DispatchQueue.main.async {
self.updateUI()
}
Expand All @@ -91,9 +93,19 @@ class RemoteConfigViewController: UIViewController {

/// This method applies our remote config values to our UI
private func updateUI() {
remoteConfigView.topLabel.text = remoteConfig["topLabelKey"].stringValue
remoteConfigView.topLabel.text = remoteConfig[decodedValue: "topLabelKey"]
updateJSONView()
remoteConfigView.bottomLabel.text = remoteConfig["bottomLabelKey"].stringValue
if var bottomLabel: String = remoteConfig[decodedValue: "bottomLabelKey"],
let freeCount: Int = remoteConfig[decodedValue: "freeCount"],
freeCount > 1,
bottomLabel.contains("one") {
let formatter = NumberFormatter()
formatter.numberStyle = .spellOut
if let english = formatter.string(from: NSNumber(value: freeCount)) {
bottomLabel = bottomLabel.replacingOccurrences(of: "one free", with: "\(english) free")
}
remoteConfigView.bottomLabel.text = bottomLabel
}
}

// MARK: - Private Helpers
Expand Down Expand Up @@ -125,35 +137,36 @@ class RemoteConfigViewController: UIViewController {
}
}

// Specify keys in the order we would like to display the information in
let keys = [
"recipe_name",
"ingredients",
"prep_time",
"cook_time",
"instructions",
"yield",
"serving_size",
"notes",
]
guard let recipeDictionary = remoteConfig[recipeKey].jsonValue as? [String: Any] else { return }

keys.enumerated().forEach { index, key in
guard var value = recipeDictionary[key] else { return }

if let list = value as? [String] {
let joinedValue = list.joined(separator: " • ")
value = joinedValue
} else if let stringValue = value as? String {
value = stringValue
struct Recipe: Decodable, CustomStringConvertible {
var recipe_name: String
var ingredients: [String]
var prep_time: Int
var cook_time: Int
var instructions: [String]
var yield: String
var serving_size: Int
var notes: String

var description: String {
return "Recipe Name: \(recipe_name)\n" +
"Ingredients: \(ingredients)\n" +
"Prep Time: \(prep_time)\n" +
"Cook Time: \(cook_time)\n" +
"Instructions: \(instructions)\n" +
"Yield: \(yield)\n" +
"Serving Size: \(serving_size)\n" +
"Notes: \(notes)"
}
}

guard let stringValue = value as? String else { return }

let formattedKey = key
.capitalized
.replacingOccurrences(of: "_", with: " ")
.appending(": ")
guard let recipe: Recipe = try? remoteConfig[typedRecipeKey].decoded() else {
fatalError("Failed to decode JSON for \(typedRecipeKey)")
}
let lines = recipe.description.split(separator: "\n")
for (index, line) in lines.enumerated() {
let lineSplit = line.split(separator: ":")
let formattedKey = String(lineSplit[0])
let stringValue = String(lineSplit[1])

let attributedKey = NSAttributedString(
string: formattedKey,
Expand All @@ -175,8 +188,8 @@ class RemoteConfigViewController: UIViewController {
animateFadeIn(for: label, duration: 0.3)

let height = jsonView.frame.height
let step = height / CGFloat(keys.count)
let offset = height * 0.2 * 1 / CGFloat(keys.count)
let step = height / CGFloat(lines.count)
let offset = height * 0.2 * 1 / CGFloat(lines.count)

let x: CGFloat = jsonView.frame.width * 0.05
let y: CGFloat = step * CGFloat(index) + offset
Expand All @@ -196,7 +209,7 @@ extension UIViewController {
public func displayError(_ error: Error?, from function: StaticString = #function) {
Copy link
Member

@ncooke3 ncooke3 Feb 14, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the error you were running into had to do with how this method was being called within the async contexts. It was then being called on a background thread causing the crash (because it does UI work). Weird how it wasn't straightforward to reproduce though. I think it would work if the implementation of this method was wrapped in DispatchQueue.main.async { ... }. That way, you could call it from whatever context and it would put itself on the main queue by itself.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Nick! If you have the bandwidth, would you see if you can get the async/await variation to work? Otherwise I should be able to get it to work later this week.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure thing, I'll take a closer look later today.

guard let error = error else { return }
print("🚨 Error in \(function): \(error.localizedDescription)")
let message = "\(error.localizedDescription)\n\n Ocurred in \(function)"
let message = "\(error.localizedDescription)\n\n Occurred in \(function)"
let errorAlertController = UIAlertController(
title: "Error",
message: message,
Expand Down
1 change: 1 addition & 0 deletions config/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ target 'ConfigExample' do

# Pods for ConfigExample
pod 'FirebaseRemoteConfig'
pod 'FirebaseRemoteConfigSwift', "> 8.12-beta"

target 'ConfigExampleTests' do
inherit! :search_paths
Expand Down
11 changes: 10 additions & 1 deletion config/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ PODS:
- FirebaseInstallations (~> 8.0)
- GoogleUtilities/Environment (~> 7.7)
- "GoogleUtilities/NSData+zlib (~> 7.7)"
- FirebaseRemoteConfigSwift (8.12.0-beta):
- FirebaseRemoteConfig (~> 8.11)
- FirebaseSharedSwift (~> 8.11-beta)
- FirebaseSharedSwift (8.12.0)
- GoogleDataTransport (9.1.2):
- GoogleUtilities/Environment (~> 7.2)
- nanopb (~> 2.30908.0)
Expand All @@ -41,6 +45,7 @@ PODS:

DEPENDENCIES:
- FirebaseRemoteConfig
- FirebaseRemoteConfigSwift (> 8.12-beta)

SPEC REPOS:
trunk:
Expand All @@ -49,6 +54,8 @@ SPEC REPOS:
- FirebaseCoreDiagnostics
- FirebaseInstallations
- FirebaseRemoteConfig
- FirebaseRemoteConfigSwift
- FirebaseSharedSwift
- GoogleDataTransport
- GoogleUtilities
- nanopb
Expand All @@ -60,11 +67,13 @@ SPEC CHECKSUMS:
FirebaseCoreDiagnostics: 3b40dfadef5b90433a60ae01f01e90fe87aa76aa
FirebaseInstallations: 25764cf322e77f99449395870a65b2bef88e1545
FirebaseRemoteConfig: f174a7091ac8e69bf6d0de2bae2212edb922a0ab
FirebaseRemoteConfigSwift: 55de35ecdfed3758d69c0faa04c460989efd63db
FirebaseSharedSwift: baa1fb7870eb8589d6107946da9d2bf050939b03
GoogleDataTransport: 629c20a4d363167143f30ea78320d5a7eb8bd940
GoogleUtilities: e0913149f6b0625b553d70dae12b49fc62914fd1
nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96
PromisesObjC: 68159ce6952d93e17b2dfe273b8c40907db5ba58

PODFILE CHECKSUM: 508a02096afdac3e2d945a0ec8c5748484e5097f
PODFILE CHECKSUM: 257ef6d5d16a32786088ce5e498068af81e1a28b

COCOAPODS: 1.11.2