Giter Club home page Giter Club logo

swifttweaks's Introduction

SwiftTweaks

Adjust your iOS app on the fly without waiting to re-compile!

SwiftTweaks Icon

Your users won’t see your animation study, Sketch comps, or prototypes. What they will see is the finished product - so it’s really important to make sure that your app feels right on a real device!

Animations that look great on your laptop often feel too slow when in-hand. Layouts that looks perfect on a 27-inch display might be too cramped on a 4-inch device. Light gray text may look subtle in Sketch, but it’s downright illegible when you’re outside on a sunny day.

These animation timings, font sizes, and color choices are all examples of “magic numbers” - the constants that give your app its usability and identity. The goal of SwiftTweaks: allow you to fine-tune these magic numbers in the debug builds of your Swift project, without having to wait for Xcode to rebuild the app.

Tweaks

SwiftPM compatible Carthage compatible Version GitHub release Swift 5.0 platforms Build Status

Overview

Use a Tweak in place of a boolean, number, or color in your code. You can adjust that Tweak without having to recompile, which means you can play with animation timings, colors, and layouts without needing Xcode!

Currently, you can tweak the following types:

  • Bool
  • Int
  • CGFloat
  • Double
  • UIColor
  • TweakAction
  • String
  • StringOption

A Tweak looks like this:

public static let colorTint = Tweak("General", "Colors", "Tint", UIColor.blueColor())

There are also helpful TweakGroupTemplate types, so you can quickly declare commonly-used-together combos. They all have sensible defaults, but of course, you can set your own!

// Controls delay and duration for UIView.animate
// Use it with UIView.animate(basicTweakTemplate:...)
public static let basicAnimation = BasicAnimationTweakTemplate("Animation", "Basic Animation")

// Controls delay, duration, damping, and initial spring velocity for UIView.animate
// Use it with UIView.animate(springTweakTemplate:...)
public static let springAnimation = SpringAnimationTweakTemplate("Animation", "Spring Animation")

// Controls shadow color, radius, offset, and opacity for CALayer
// Use it with CALayer.apply(shadowTweakTemplate:...)
public static let shadowTweak = ShadowTweakTemplate("Shadows", "Button Shadow")

// Controls top/right/bottom/left for UIEdgeInsets
// Use it with UIEdgeInsets.init(edgeInsetsTweakTemplate)
public static let edgeInsets = EdgeInsetsTweakTemplate("Layout", "Screen Edge Insets")

Of course, you can create your own TweakGroupTemplate type if you'd like - they're handy whenever you have a cluster of tweaks that need to be used together to get a desired effect. They can be built out of any combination of Tweaks.

Tweaks

Actions

SwiftTweaks now supports closures, so you can perform actions that do not depend on data from SwiftTweaks. To do this, use TweakAction as a type in your TweakStore to create a Tweak that executes your custom closures:

public static let action = Tweak<TweakAction>("Actions", "Action", "Perform some action")

Later in the code you can add closures to that tweak, which are executed when a button in Tweaks window is pressed.

let idenfitier = ExampleTweaks.action.addClosure {
	/// Some complicated action happens here
	print("We're all done!")
}

If you want to, you can also always remove closure using unique idenfitier that addClosure method provides.

ExampleTweaks.action.removeClosure(with: idenfitier)

Wait, what about Facebook Tweaks?

Good question! I’m glad you asked. The whole reason SwiftTweaks exists is because we love the stuffing out of FBTweaks. We’re long-time fans of FBTweaks in our Objective-C projects: Replace the magic numbers with an FBTweak macro, and you’re all set! You can leave an FBTweak macro in your production code, because it’s replaced at compile-time with the tweak’s default value.

But Swift doesn’t support this macro-wizardry, so FBTweaks is burdensome to use in Swift code. Our app is nearly all Swift, so we wanted to see if we could make something that was a little easier!

Steps to Tweaking

There are three steps to add SwiftTweaks to your project:

  1. Create a TweakLibraryType, which contains a set of Tweaks and a TweakStore to persist them.
  2. Reference that TweakLibraryType in your code to use a Tweak.
  3. In your AppDelegate, make the TweakWindow the window of your app (there are other options, but this is the most straightforward! More on that later.)

Now build-and-run, then shake your phone to bring up the Tweaks UI! Adjust tweaks, and when you’re satisfied with what you’ve got, share your tweaks with others from within the Tweaks UI.

Step One: Make your TweakLibrary

A tweak library is responsible for listing out a bunch of public static tweaks, and building a TweakStore. A tweak library looks like this:

public struct ExampleTweaks: TweakLibraryType {
	public static let colorTint = Tweak("General", "Colors", "Tint", UIColor.blue)
	public static let marginHorizontal = Tweak<CGFloat>("General", "Layout", "H. Margins", defaultValue: 15, min: 0)
	public static let marginVertical = Tweak<CGFloat>("General", "Layout", "V. Margins", defaultValue: 10, min: 0)
	public static let font = Tweak<StringOption>("General", "Layout", "Font", options: ["AvenirNext", "Helvetica", "SanFrancisco"])
	public static let featureFlagMainScreenHelperText = Tweak<Bool>("Feature Flags", "Main Screen", "Show Body Text", true)

	public static let buttonAnimation = SpringAnimationTweakTemplate("Animation", "Button Animation")

	public static let defaultStore: TweakStore = {
		let allTweaks: [TweakClusterType] = [colorTint, marginHorizontal, marginVertical, featureFlagMainScreenHelperText, buttonAnimation]

		let tweaksEnabled = TweakDebug.isActive

		return TweakStore(
			tweaks: allTweaks,
			enabled: tweaksEnabled
		)
	}()
}

Let’s break down what happened here:

  • We have five tweaks in ExampleTweaks: a tint color, two CGFloats for layout, a StringOption for font choice, and a Bool that toggles an in-development feature.
  • The compiler can get confused between Int, CGFloat, and Double - so you might find it necessary to tell the Tweak<T> what type its T is - as we do here with our margin tweaks.
  • We create a defaultStore by creating a TweakStore, which needs to know whether tweaks are enabled, and a list of all tweaks.
  • The enabled flag on TweakStore exists so that SwiftTweaks isn’t accessible by your users in production. You can set it however you like; we enjoy using the DEBUG flag from our project’s Build Settings.

Step Two: Using Your TweakLibrary

To use a tweak, you replace a number or UIColors in your code with a Tweak reference, like this:

Here’s our original code:

button.tintColor = UIColor.green

assign returns the current value of the tweak:

button.tintColor = ExampleTweaks.assign(ExampleTweaks.colorTint)

bind calls its closure immediately, and again each time the tweak changes:

ExampleTweaks.bind(ExampleTweaks.colorTint) { button.tintColor = $0 }

bindMultiple calls its closure immediately, and again each time any of its tweaks change:

// A "multipleBind" is called initially, and each time _any_ of the included tweaks change:
let tweaksToWatch: [TweakType] = [ExampleTweaks.marginHorizontal, ExampleTweaks.marginVertical]
ExampleTweaks.bindMultiple(tweaksToWatch) {
	let horizontal = ExampleTweaks.assign(ExampleTweaks.marginHorizontal)
	let vertical = ExampleTweaks.assign(ExampleTweaks.marginVertical)
	scrollView.contentInset = UIEdgeInsets(top: vertical, right: horizontal, bottom: vertical, left: horizontal)
}

For more examples, check out the example project’s ViewController.swift file.

Step Three: Set TweakWindow as your Root View Controller

By default, SwiftTweaks uses a shake gesture to bring up the UI, but you can also use a custom gesture!

Installation

Swift Package Manager

SwiftTweaks is available via the Swift Package Manager; add it to Xcode with this URL:

https://github.com/Khan/SwiftTweaks

It's also listed in the (excellent) Swift Package Index!

To add SwiftTweaks to your application, add it to your Cartfile:

github "Khan/SwiftTweaks"

In addition, add -DDEBUG to Other Swift Flags in your project's Build Settings for your Debug configuration.

pod 'SwiftTweaks'

# Enable DEBUG flag in Swift for SwiftTweaks
post_install do |installer|
    installer.pods_project.targets.each do |target|
        if target.name == 'SwiftTweaks'
            target.build_configurations.each do |config|
                if config.name == 'Debug'
                    config.build_settings['OTHER_SWIFT_FLAGS'] = '-DDEBUG'
                end
            end
        end
    end
end

FAQ

Do I have to set TweakWindow as the root of my app?

Nope! Wherever/however you prefer, just create a TweaksViewController like so:

let tweaksVC = TweaksViewController(tweakStore: ExampleTweaks.defaultStore, delegate: self)

Can I have multiple TweakLibraryTypes in my app?

Sure! You’d initialize their defaultStores with a unique storeName identifier, like so:

public struct FirstTweaksLibrary: TweakLibraryType {
	// ...

	public static let defaultStore: TweakStore = {
		let allTweaks: [TweakClusterType] = //...

		return TweakStore(
			tweaks: allTweaks,
			storeName: "FirstTweaksLibrary", 	// Here's the identifier
			enabled: tweaksEnabled
		)
	}()
}

Why can’t any type be used for a Tweak?

While Tweak<T> is generic, we have to restrict T to be TweakableType so that we can guarantee that each kind of T can be represented in our editing interface and persisted on disk. More types would be awesome, though! It’d be neat to support dictionaries, closures, and other things.

If you’d like to extend TweakableType, you’ll need to extend some internal components, like TweakViewDataType, TweakDefaultData, TweakViewData, and TweakPersistency. Feel free to open a pull request if you’d like to add a new type!

How do I create a new TweakGroupTemplate?

Maybe you’re using a different animation framework, or want a template for CGRect or something like that - great! As long as the tweakable “components” of your template conform to TweakableType then you’re all set. Create a new TweakGroupTemplateType, and take a look at the existing templates for implementation suggestions. (You’ll probably want to use SignedNumberTweakDefaultParameters too - they’re very helpful!)

If you think your TweakGroupTemplateType would help out others, please make a pull request!

swifttweaks's People

Contributors

albinekcom avatar andymatuschak avatar atom-wintermute avatar aymericio avatar bryanjclark avatar ejensen avatar ericbuehl avatar erikpoort avatar hfossli avatar hs-abekert avatar ignazioc avatar ikesyo avatar jaredly avatar johntmcintosh avatar mac-cain13 avatar marmelroy avatar msmollin avatar nachosoto avatar pendowski avatar pyckamil avatar richy486 avatar sendyhalim avatar shivahuang avatar warpling avatar yutailang0119 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

swifttweaks's Issues

View Controller

I would love to be able to push on a custom view controller for Swift Tweaks.

Are bound Tweaks leaking the observation blocks?

We're using SwiftTweaks in a project and while using bind for a few Tweaks I was wondering when those observer closures are released. I skimmed through the source code and it appears to me that the closure given to bind is saved in a dictionary where it is strongly references forever.

If I'm not mistaken these bindings dictionaries are never cleaned up neither there is any API to unregister the binding. So everytime my ViewController is started I bind a new closure that will be around forever, this looks like a memory leak to me.

Is there anything I'm missing that resolved this memory issue? Or is this an issue and do we need to add some kind of unbind call to remove the closure from the list.

Text

I would love to have custom text to show information like API URL.

BindMultiple: pass current value of tweaks into the closure

Currently, if you're using bindMultiple, you need to do something like this:

let tweaksToWatch: [TweakType] = [ExampleTweaks.marginHorizontal, ExampleTweaks.marginVertical]
ExampleTweaks.bindMultiple(tweaksToWatch) {
    let horizontal = ExampleTweaks.assign(ExampleTweaks.marginHorizontal)
    let vertical = ExampleTweaks.assign(ExampleTweaks.marginVertical)
    scrollView.contentInset = UIEdgeInsets(top: vertical, right: horizontal, bottom: vertical, left: horizontal)
 }

It'd be neat to be able to do something like this instead:

let tweaksToWatch: [TweakType] = [ExampleTweaks.marginHorizontal, ExampleTweaks.marginVertical]
ExampleTweaks.bindMultiple(tweaksToWatch) { horizontal, vertical in
    scrollView.contentInset = UIEdgeInsets(top: vertical, right: horizontal, bottom: vertical, left: horizontal)
 }

I've found this helpful in http://github.com/robb/Cartography/

Add a "closure" Tweak Type

public struct TweakClosure {
    public let collectionName: String
    public let groupName: String
    public let tweakName: String
    internal let closure: () -> ()
}

The idea here: when you want to perform an action in your app only in certain debug builds, you could put it in a TweakClosure, and then call something like this:

MyTweakLibrary.evaluate(MyTweakLibrary.someTweakClosure)

You'd also want to have that TweakClosure conform to AnyTweak, so that it can be included in a TweakGroup, and displayed in a table cell with a little [RUN] button (I'm thinking it'd look like a little App Store [BUY] button)

Consider changing TweaksViewController.delegate from `unowned` to `weak`

In order to integrate with RxSwift's DelegateProxy mechanism, TweaksViewController.delegate could be changed from unowned to weak.

This gist shows the required DelegateProxy implementation and an example call to rx_pressedDone.

No dependency on RxSwift is required of the SwiftTweaks project

The required change to SwiftTweaks can be seen in this commit.

Please let me know if you would like me to submit a pull request.

Add a TwoFingerTripleTap as a default option for TweakWindow.GestureType

A two-finger triple-tap is a commonly-used gesture when the shake gesture's already used for something else - let's make it super-easy for devs to use that instead of the shake gesture!

(Of course, devs can always use whatever custom gesture they'd want.)

Instead of:

public enum GestureType {
        case Shake
        case Gesture(UIGestureRecognizer)
    }

We'd have:

public enum GestureType {
        case Shake
        case TwoFingerTripleTap
        case CustomGesture(UIGestureRecognizer)
    }

"Backups" feature

Often, when I've dialed in an animation, I want to be able to create a "backup" so I can go experiment more, knowing that I have a save point if I want to go back.

It'd be neat if we had the ability to create/update/read Backups. I would like that they use the same visual format as the email template we use when sharing a set of tweaks.

One interesting bit that'll arise from this: what happens if the underlying TweakLibraryType changes? Do we need some kind of randomly-generated-from-a-deterministically-derived-seed thing (where TweakStore creates a "version name" to ensure that a Backup can only be restored if the underlying version name is the same?)

Feature: long-press on cell to view "hint"

Sometimes, the collection + group + tweak names aren't enough to really communicate the effect of a tweak.

Maybe it'd be neat to optionally allow a longer "hint" text, to be displayed on long-pressing in the cell?

Would need to design the affordance for the long press, because it'll be rarely used, but I think it'd be helpful.

The hint would be presented in a UIAlertController.Alert

Add a "list" TweakType

Sometimes, you want to have a feature flag that has multiple options - for example, you might have 3 environments to point against (e.g. test, stage, production) and want to switch between them.

A couple of ideas for implementing this:

  1. A protocol, TweakableEnum, that can be used in a tweak easily. The protocol would probably require a static var caseNames: [String] to list out the options in the Tweaks UI. We'd also probably want a failable-init that returns the correct enum case for a given String (so that the enum can "look up" a persisted stringly-typed value.) This might depend on us building #14 first (adding String as a TweakableType).
  2. Use a tweak template to support lists.
  3. Make a tweak that simply holds a [String] and optionally has a default value (asserting if the default value doesn't exist in the array, and if no default given, uses the first value in the array). Leave it to the implementer to switch on the current value of the string - they can always build a string-based enum and init with rawValue: to get away from stringly-based stuff.

Anyhow, I've not yet tried building this out yet, so I dunno what will be easy-to-understand-and-implemented here. One goal for SwiftTweaks is to keep tweak libraries simple and straightforward - so while I'm inclined to do the "most precise and Swifty thing", it's even more important that SwiftTweaks remains easy-to-use for new folks, so that'll be a big criteria in evaluating which one of these will work!

Floating TweakGroup window

Any TweakGroup (that is, the sections of the tableview inside a TweakCollection) should be "floatable" - that is, you could tap the little (+) button, and get a floating, minimizable pane that still allows for interaction in the view controller:

screen shot 2016-04-04 at 8 00 23 pm

Write value for tweak programmatically for unit tests

I need the ability to test behavior when a tweak is enabled/disabled. We are using Tweaks for feature flagging, false turns off in-development features for production.

Because I am scared new development features may affect production in an unexpected way, I would like to be able to unit test various modules which use tweak. Is there not a way to write a different value programmatically?

EXC_BAD_ACCESS in testHexToColor

Not sure what's going on in testHexToColor, but there's an EXC_BAD_ACCESS for this test case:

HexToColorTestCase(string: "Hello", expectedColor: nil),

Any ideas?

"TweakGroup" templates (e.g. for animations)

So you can more easily create templates (think: the parameters of a spring animation) rather than having to create 4 separate tweak values.

The idea would be, you could create a struct that conforms to TweakGroupType.

Layout bug in TweaksRootViewController

The tableviews in TweaksRootViewController often don't get the topLayoutGuide update (so their content scrolls under the navigation bar)

Doesn't reproduce now that we don't have the segmented controller in the nav bar (because you can't switch the tab, there's not a path to reproduce the error) but the underlying bug in the layout logic is still there!

"Umbrella header 'SwiftTweaks.h' not found"

HI!
Downloaded SwiftTweaks from Github
make no alterations in the project
try to build framework and get "Umbrella header 'SwiftTweaks.h' not found"
Search in Stackoverflow and it says that
"This usually happens after a project rename or something like that. The problem is that the umbrella header is no longer listed as a Public header."
(http://stackoverflow.com/questions/30355133/swift-framework-umbrella-header-h-not-found)
but I checked and the headers SwiftTweaks.h is already on the "public" headers section
Thanks in advance for any help!

Support String as a TweakViewDataType

We didn't need this for our initial use of SwiftTweaks, but it'd be helpful to have!

Things to consider:

  • How would this support NSLocalizedString?
  • Need to design & build out UI for it (with nice keyboard support, dismiss on scrolling the tableview, etc).

Manual Installation

CocoaPods and Carthage are awesome tools and make our life really easier, but there are some devs who still don't know how to use them.

It would be cool to add the Manual installation guide in your README.md. You can take a look at my iOS Readme Template to see how you can do it.

Consider making TweakWindow.tweaksViewController `public` accessible

I would like to present the TweaksViewController from a button press, which works well, however, the FloatingTweakGroupViewController does not then display (as TweaksViewController.floatingTweaksWindowPresenter == nil in this case)

Making TweakWindow.tweaksViewController public allows for all the benefits of using TweakWindow triggered from any application event.

You can see a simple example on this fork: https://github.com/DanielAsher/SwiftTweaks/commits/fix/floating-window-on-tweak-manual-present

cheers,

Daniel

Make FloatingTweakGroupViewController public

Original discussion here: #44

Much like TweaksViewController is public, we could probably do some investigation and make FloatingTweakGroupViewController public as well, so developers can use that component (without mucking around in TweakWindow's child objects).

For developers who aren't using TweakWindow, let's make the floating UI publicly usable - and include an optional init Bool for TweakViewController that hides the (+) buttons when not in use.

Allow FloatingTweakGroupViewController to be vertically dragged

I love the FloatingTweakGroupViewController - but sometimes the very part of the current view I'm trying to tweak is obscured by the floating window. Enabling a drag gesture on the floating window would allow us to reposition it and expose the hidden parts of the underlying view.

Swift 3 migration

Hi,

are there any plans to support swift-3? I see a stale PR #63 and it looks like @richy486 fork is deleted. Should I submit a PR?

thanks,

Daniel

Add Tweak.valueInStore(TweakLibraryType) in a protocol extension

Something like:

public extension Tweak {
    func currentValueInStore<S: TweakLibraryType.class>(store: S) -> T {
        return store.assign(self)
    }
}

This would simplify getting values from tweaks, from:

let value = TweakStore.assign(TweakStore.some.tweak)

to

let value = TweakStore.some.tweak.currentValueInStore(TweakStore.self)

TweakViewController misaligned layout when Personal Hotspot Blue Bar showing

Hi,

Firstly thanks so much for this project! As a user of FBTweaks I've been waiting for a swift replacement, and I look forward to contributing if possible :)

I have noticed that the Personal Hotspot Blue Bar pushes the TweakViewController beyond the bottom of the window bounds. Screenshot attached:
img_4319

Cheers,

Daniel

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.