Facebook Tweaks with Swift Tutorial

Learn how to use Facebook Tweaks, which allows you to modify parameters in your code whilst the app is running, and to enable or disable features on the fly. By .

Leave a rating/review
Save for later
Share
You are currently viewing page 3 of 4 of this article. Click here to view the first page.

More Complicated Tweaks

One of your testers said that the label text should be bigger. You could use the method you already have to add a tweak for the font size, but there’s a cooler way to go about it.

You’ll add a tweak that executes a closure when the tester changes a tweak value — a tweak with action, if you will.

Instances of FBTweak can have observers. When the value of a tweak changes, the method tweakDidChange(tweak: FBTweak) is called on all observers. You’ll use this to implement the tweak with action.

Open Tweaks.swift and change the following line:

class Tweaks {

To this:

class Tweaks: NSObject, FBTweakObserver {

With this change, you tell the compiler that Tweaks will implement tweakDidChange.

Now, add the following to the beginning of the Tweaks class in Tweaks.swift:

typealias ActionWithValue = ((currentValue: AnyObject) -> ())
var actionsWithValue = [String:ActionWithValue]()

The variable actionsWithValue is a dictionary that stores functions with a parameter of type AnyObject and a return value of ().

Now, add the following method to the class:

func tweakActionForCategory<T where T: AnyObject>(categoryName: String, collectionName: String, name: String, defaultValue: T, minimumValue: T? = nil, maximumValue: T? = nil, action: (currentValue: AnyObject) -> ()) {
    
  let identifier = categoryName.lowercaseString + "." + collectionName.lowercaseString + "." + name
    
  let collection = Tweaks.collectionWithName(collectionName, categoryName: categoryName)
    
  var tweak = collection.tweakWithIdentifier(identifier)
  if tweak == nil {
    tweak = FBTweak(identifier: identifier)
    tweak.name = name
        
    tweak.defaultValue = defaultValue
        
    if minimumValue != nil && maximumValue != nil {
      tweak.minimumValue = minimumValue
      tweak.maximumValue = maximumValue
    }
    tweak.addObserver(self)
        
    collection.addTweak(tweak)
  }
    
  actionsWithValue[identifier] = action
    
  action(currentValue: tweak.currentValue ?? tweak.defaultValue)
}

The method tweakActionForCategory(...) looks very similar to tweakValueForCategory(...). However, the difference is that it has an additional parameter, action: (currentValue: AnyObject) -> (), that is added to the dictionary actionsWithValue within the method body.

The instance of the Tweaks class (self) is added as an observer to the tweak. This means when a tweak that was added via this method is changed, the method tweakDidChange(...) on the instance is called.

Finally, add the following:

func tweakDidChange(tweak: FBTweak!) {
  let action = actionsWithValue[tweak.identifier]
  action?(currentValue: tweak.currentValue ??  tweak.defaultValue)
}

In tweakDidChange(...), you use the tweak identifier to retrieve the action for this tweak from the actionsWithValue dictionary. This action is then executed with the current value, provided it’s not nil, otherwise it’s set to the default value.

To get this tweak with action to work, you need an instance of the Tweaks class in ViewController.swift.

Go to the beginning of the class ViewController and add:

let tweaks = Tweaks()

Now add the following code in viewDidLoad:

tweaks.tweakActionForCategory("Jar View", collectionName: "Coin Label", name: "Text Size", defaultValue: 30, minimumValue: 20, maximumValue: 60, action: { (currentValue) -> () in
  self.coinLabel.font = self.coinLabel.font.fontWithSize(CGFloat(currentValue.floatValue))
})

This code adds a tweak with ab action to the tweak store. When you change the value of the tweak, the action executes. In this case, you’re changing the font size of the coinLabel to the current value of the tweak.

Build and run. Pull up the tweaks view controller. Change the font size to 60 and tap Done. You should see something like this:

Larger text size

Larger text size

Even More Tweaks

Add a few more tweaks, just to practice your new skills. Add the following code to viewDidLoad() in ViewController.swift:

tweaks.tweakActionForCategory("Jar View", collectionName: "Coin Label", name: "Orange Text", defaultValue: false, action: { (currentValue) -> () in
  if currentValue.boolValue == true {
    self.coinLabel.textColor = UIColor(red: 0.98, green: 0.58, blue: 0.13, alpha: 1.0)
  } else {
    self.coinLabel.textColor = UIColor.blackColor()
  }
})

This tweak lets you toggle the text color of the coinLabel.

How would the app look if you were to change the gravity? Well, add a tweak for it and find out. Back within viewDidLoad(), find the closure of the startAccelerometerUpdatesToQueue method of motionManager, and replace these two lines:

let y = CGFloat(data.acceleration.y)
let x = CGFloat(data.acceleration.x)

With the following:

let magnitude = CGFloat(Tweaks.tweakValueForCategory("Jar View", collectionName: "Dynamics", name: "Gravity Magnitude", defaultValue: 1.0).floatValue)
let y = magnitude * CGFloat(data.acceleration.y)
let x = magnitude * CGFloat(data.acceleration.x)

This tweak gives you and your testers control of a fundamental element — gravity! It’s like being a super hero! Unfortunately, this only works when you test on a device as the simulator doesn’t have an accelerometer and the iPhone doesn’t know how to defy gravity — yet.

Play around with the tweaks and find the values that make the app behave and look its best.

Production Code

I recommend you remove the tweaks code as soon as you settle on the right values. But just in case you forget or something weird happens, like you have one beer too many and decide to publish your app after letting your friends tweak it, you should add code to disable tweaks in your release builds.

Select your project in the Project Navigator and then open Build Settings. Search for other swift. Add the value -DDEBUG in Other Swift Flags to the Debug configuration, as shown here:

Adding a swift flag

Adding a swift flag

With this addition, you can use preprocessor directives to disable Tweaks in release builds.

Note: Preprocessor directives are like macros that tell the compiler which code to use when compiling. This allows you to conditionally execute parts of your code, so that not only will it not run in production, it is literally excluded from the build all together.

Open AppDelegate.swift and replace the existing definition of window with the following:

#if DEBUG
lazy var window: UIWindow? = {
  let window = FBTweakShakeWindow(frame: UIScreen.mainScreen().bounds)
  return window
}()
#else
var window: UIWindow?
#endif

Now go to Tweaks.swift and add #if DEBUG at the beginning of both tweakActionForCategory(...) and tweakValueForCategory(...). At the end of the method tweakActionForCategory(...), add the lines:

#else
action(currentValue: defaultValue)
#endif

And add this at the end of tweakValueForCategory(...):

#else
return defaultValue
#endif

Now, when you make a release build, the tweaks fall back to default values. So even if you do publish your app when you shouldn’t the worst you’ll do is publish it exactly how you developed it to be.