Code Monkey home page Code Monkey logo

eject's Introduction

Eject

Eject is a utility to transition from Interface Builder to programatic view layout. This is done by using code generation to create a .swift file to replace the view hierarchy managed by the .xib file.

Why?

One common pain point with Interface Builder is that as a view becomes more dynamic and is managed more programatically, Interface Builder becomes less helpful. This tool lets developers use Interface Builder without that concern, giving them an Eject button to hit when Interace Builder starts getting in the way, and provides an easy path to transition to full programatic view layout.

But But

Yes, I understand that this is probably a bad idea. But it might not be.

Usage

Install with homebrew:

brew install eject

Use on the command line:

eject --file /path/to/MassiveViewController.xib

Copy and paste code into .swift file, and remove the .xib:

rm /path/to/MassiveViewController.xib

Or to see what changed in a xib file by looking at the changes in generated code:

TMP=`mktemp` && git show HEAD:$XIB > $TMP && diff <(eject --file $XIB ) <(eject --file $TMP)

eject will generate code for everything it can in the .xib file. If there is any XML that eject does not understand, it will print out a warning message. Open an Issue with any warnings, bugs or ideas you may have.

Features

  • UIKit .xib support
  • Constraints (using Anchorage)
  • Outlet and OutletCollection support
  • Good variable names
    • Use the user entered "user label" if present
    • Snake case of the className with the namespace removed
    • Constraint variable names are long, but descriptive, (labelBottomEqualToButtonTop)
  • Code that compiles out of the box is a non-goal
    • Will not generate view1, view2 variable names to avoid compile errors. Supply user labels and re-generate.

Does it work?

The Unit Tests show how much work is done. UIKit coverage is configured by the CocoaTouchBuilder using various Builders. Some configuration is generated from Interface Builder .inspector files.

This should still be considered an Alpha quality tool.

Todo

  • Enhance code generation approaches
  • AppKit support
  • Storyboard support?
  • Use default values to remove un-needed code
  • Better error reporting of un-interpreted flags
  • Explore generating code as a method of diffing .xib files

eject's People

Contributors

edwardloveall avatar jdhealy avatar kingofbrian avatar squaredtiki 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

eject's Issues

UIDatePicker minuteInterval error

When a UIDatePicker is created the minuteInterval is incorrect. It should be in Int.

open var minuteInterval: Int // display minutes wheel with interval. interval must be evenly divided into 60. default is 1. min is 1, max is 30

let timePicker = UIDatePicker()
timePicker.datePickerMode = .dateAndTime
timePicker.minuteInterval = .1
timePicker.translatesAutoresizingMaskIntoConstraints = false

Download button

There should be a button for one-click download of the generated Swift code as a .swift file.

highlightedColor should be highlightedTextColor

The color node doesn't have any context, it just uses the configured keyPath, which is wrong.

We can pretty easily override a universal keyPath mapping which would change all highlightedColor keys in a color node to highlightedTextColor. This would be more explicit ideally (ie: only do the mapping inside of labels), but in reality I think this simple hack should work fine.

States!

Just a thought but what if, as a user, I could:

  1. Create a layout in interface builder.
  2. Duplicate that view and create second, third, nth variations/states.
  3. Eject xibs into view-decorating extensions and playground file that demos animated/sudden state changes.

Code like this:

let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = UIColor(white: 1, alpha: 1)

let button = UIButton(type: .roundedRect)
button.titleLabel?.lineBreakMode = .byTruncatingMiddle
button.isOpaque = false
button.translatesAutoresizingMaskIntoConstraints = false
button.clipsToBounds = true
button.setContentCompressionResistancePriority(1, for: .vertical)
button.setTitle("Button", for: .normal)

view.addSubview(button)
contentView.addSubview(view)

would become...

extension UITableViewCell {
    func decorate() {
        let view = UIView()
        view.setState_XcodeSpecificLabel_()
        let button = UIButton()
        button.setState_XcodeSpecificLabel1_()
        view.addSubview(button)
        contentView.addSubview(view)
        button.setState_XcodeSpecificLabel2_()
    }
}

extension UIView {
    func setState_XcodeSpecificLabel_() {
        translatesAutoresizingMaskIntoConstraints = false
        backgroundColor = UIColor(white: 1, alpha: 1)
    }
}

extension UIButton {
    func setState_XcodeSpecificLabel1_() {
        type = .roundedRect
        titleLabel?.lineBreakMode = .byTruncatingMiddle
        isOpaque = false
        translatesAutoresizingMaskIntoConstraints = false
        clipsToBounds = true
        setContentCompressionResistancePriority(1, for: .vertical)
        setTitle("Button", for: .normal)
    }
}

extension UIButton {
    func setState_XcodeSpecificLabel2_() {
         type = .roundedRect
         titleLabel?.lineBreakMode = .byTruncatingMiddle
         isOpaque = false
         translatesAutoresizingMaskIntoConstraints = false
         clipsToBounds = true
         setContentCompressionResistancePriority(1, for: .vertical)
         setTitle("Button", for: .normal)
    }
}

Pros

  1. Compose states visually

Cons

  1. Extensions approach requires concrete types
  2. Changes to XIB-states would sometimes require changes to sibling states, which would be error-prone.

dyld: Library not loaded: EjectKit

So I build the project, navigate to the build directory in the terminal, then run ./eject. This message spits out at me:

dyld: Library not loaded: @rpath/EjectKit.framework/Versions/A/EjectKit
  Referenced from: /Users/edwardloveall/Library/Developer/Xcode/DerivedData/Eject-gcyocjdokcfabqevoqmiqqzavoxx/Build/Products/Debug/eject
  Reason: image not found
zsh: abort

This is opposed to the generic help message that spits out into the xcode log when I build and run.

Add support for Trait Collections

The IB trait collection exclusion mechanism isn't implemented. This is mainly because I don't know how to generate code that wouldn't be terrible. Generating Relayout code is probably the current best option.

Figure out `placeholder` in constraint.

There is a placeholder key on constraint. I'm not sure what this actually does. Very often it won't have enough information to generate a valid constraint and can cause failures.

Disable frame generation properly

I currently disable frame generation all together. The choice should be deferred to the .xib file instead of a configuration option since it's a flag on the document.

eject fails to build with Xcode 11 (swift 3 is unsupported)

==> xcodebuild SYMROOT=build
Build settings from command line:
    SYMROOT = build

note: Using new build system
note: Planning build
note: Constructing build description
error: SWIFT_VERSION '3.0' is unsupported, supported versions are: 4.0, 4.2, 5.0. (in target 'eject' from project 'Eject')
error: SWIFT_VERSION '3.0' is unsupported, supported versions are: 4.0, 4.2, 5.0. (in target 'EjectKit' from project 'Eject')

Syntax Highlighting

It would be nice to syntax-highlight the generated Swift code on the page.

Enhance Code Generation Options

The current code generation is as simplistic as I could get away with. It should:

  • Add support for Stencil to manage the high-level code structure.
    • Move statements out of the document and onto Reference
    • Add properties to access different configuration phases
  • Support for blocks beyond what a template will provide
    • Add support for nested statements
    • Variable name override

License?

What's the license for this project?

Add support for attributedString

This will probably be one of the more complicated builders. I'd prefer we generate BonMot code.

<attributedString key="attributedText">
     <fragment content="I agree that I have read and understand the ">
        <attributes>
             <color key="NSColor" red="0.58823529411764708" green="0.58823529411764708" blue="0.58823529411764708" alpha="1" colorSpace="calibratedRGB"/>
             <font key="NSFont" size="12" name="HelveticaNeue"/>
             <paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural"/>
         </attributes>
     </fragment>
     <fragment content="terms &amp; conditions">
         <attributes>
             <color key="NSColor" red="0.14117647058823529" green="0.59607843137254901" blue="0.54509803921568623" alpha="1" colorSpace="calibratedRGB"/>
             <font key="NSFont" size="12" name="HelveticaNeue-Bold"/>
             <paragraphStyle key="NSParagraphStyle" alignment="left" lineBreakMode="wordWrapping" baseWritingDirection="natural"/>
         </attributes>
     </fragment>
 </attributedString>

LayoutMargins not generated

I had a view that had layout margins as such:

<edgeInsets key="layoutMargins" top="0.0" left="10" bottom="0.0" right="10"/>

Unfortunately the layoutMargins property was skipped over when generating the view's code.

Fix UIVisualEffectView

Code generation is not correct. There's also some curious double-nesting in xib files when created fresh, ie:

        <visualEffectView opaque="NO" contentMode="scaleToFill" id="JqZ-8z-GcL">
            <rect key="frame" x="0.0" y="0.0" width="240" height="128"/>
            <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
            <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" id="qar-2Q-g4b">
                <frame key="frameInset"/>
                <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                <subviews>
                    <visualEffectView opaque="NO" contentMode="scaleToFill" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="oio-Xx-v59">
                        <frame key="frameInset"/>
                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        <view key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" ambiguous="YES" id="AIi-sS-Vsr">
                            <frame key="frameInset"/>
                            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
                        </view>
                        <vibrancyEffect>
                            <blurEffect style="light"/>
                        </vibrancyEffect>
                    </visualEffectView>
                </subviews>
            </view>
            <vibrancyEffect>
                <blurEffect style="light"/>
            </vibrancyEffect>
            <point key="canvasLocation" x="326" y="485"/>
        </visualEffectView>

Wonder if the view hierarchy that's created is as strange as the XML looks.

UIStackView may out of order

UIStackView is not always generating code in an order that is the same as represented in Interface Builder, causing some views to be misplaced after running through Eject.

Support Safe Area Layout Guide

So I ran Eject on my .xib and got this output:

document.objects.view.subviews.view: insetsLayoutMarginsFromSafeArea='NO'                                                                                                         
document.objects.view.subviews.view: insetsLayoutMarginsFromSafeArea='NO'                                                                                                         
Can not configure XML nodes 'viewLayoutGuide'                                                                                                                                     
Can not configure XML nodes 'point'                                                                                                                                               
Variable 'imageView: UIImageView' was generated 2 times. // <-- specific to my project                                                                                                                          
Error: invalidReference("2kL-IL-xdO")   

It appears that the program is unable to support constraints against the safe area layout guide, added in iOS 11.

Here is the relevant source from the xib file:

// ...
<constraints>
     <constraint firstItem="2kL-IL-xdO" firstAttribute="bottom" secondItem="Zi3-cc-4t5" secondAttribute="bottom" constant="20" id="8U4-yK-lhp"/>
     <constraint firstItem="2kL-IL-xdO" firstAttribute="bottom" secondItem="3i5-rT-P9f" secondAttribute="bottom" constant="20" id="8Wl-FC-8aX"/>
     <constraint firstItem="tBV-wJ-bHs" firstAttribute="leading" secondItem="2kL-IL-xdO" secondAttribute="leading" constant="16" id="DYw-JW-42g"/>
     <constraint firstItem="2kL-IL-xdO" firstAttribute="trailing" secondItem="3i5-rT-P9f" secondAttribute="trailing" constant="20" id="FKk-5J-rwC"/>
     <constraint firstItem="2kL-IL-xdO" firstAttribute="trailing" secondItem="iI4-J5-UpK" secondAttribute="trailing" constant="89" id="G2d-aF-bYk"/>
     <constraint firstItem="Zi3-cc-4t5" firstAttribute="leading" secondItem="2kL-IL-xdO" secondAttribute="leading" constant="20" id="Kas-ql-tKM"/>
     <constraint firstItem="tBV-wJ-bHs" firstAttribute="top" secondItem="5C9-QY-uP2" secondAttribute="bottom" constant="-1" id="KoW-CI-mIs"/>
     <constraint firstItem="W73-W8-5S3" firstAttribute="centerX" secondItem="2kL-IL-xdO" secondAttribute="centerX" id="KrG-m9-qvT"/>
     <constraint firstAttribute="trailing" secondItem="YHR-GC-tiW" secondAttribute="trailing" id="LdH-1r-g0g"/>
     <constraint firstItem="5C9-QY-uP2" firstAttribute="top" secondItem="ADU-wZ-6Mx" secondAttribute="bottom" constant="14" id="QjB-0e-Cdv"/>
     <constraint firstItem="ADU-wZ-6Mx" firstAttribute="leading" secondItem="2kL-IL-xdO" secondAttribute="leading" id="R8u-cw-6KD"/>
     <constraint firstAttribute="bottom" secondItem="YHR-GC-tiW" secondAttribute="bottom" id="Tf1-zr-ncU"/>
     <constraint firstItem="iI4-J5-UpK" firstAttribute="top" secondItem="2kL-IL-xdO" secondAttribute="top" constant="45" id="UQW-Lt-gTE"/>
     <constraint firstItem="ADU-wZ-6Mx" firstAttribute="top" secondItem="W73-W8-5S3" secondAttribute="bottom" constant="8" id="YKH-bV-GFb"/>
     <constraint firstItem="YHR-GC-tiW" firstAttribute="top" secondItem="977-Kl-DqP" secondAttribute="top" id="YaX-ba-BQb"/>
     <constraint firstItem="2kL-IL-xdO" firstAttribute="trailing" secondItem="tBV-wJ-bHs" secondAttribute="trailing" constant="16" id="Ycl-6J-UBO"/>
     <constraint firstItem="iI4-J5-UpK" firstAttribute="leading" secondItem="2kL-IL-xdO" secondAttribute="leading" constant="90" id="e8a-kC-VxY"/>
     <constraint firstItem="YHR-GC-tiW" firstAttribute="leading" secondItem="977-Kl-DqP" secondAttribute="leading" id="h0W-Wq-OnG"/>
     <constraint firstItem="2kL-IL-xdO" firstAttribute="trailing" secondItem="ADU-wZ-6Mx" secondAttribute="trailing" id="klO-yT-vIA"/>
     <constraint firstItem="W73-W8-5S3" firstAttribute="top" secondItem="iI4-J5-UpK" secondAttribute="bottom" constant="27" id="lAV-H5-8eZ"/>
     <constraint firstItem="3i5-rT-P9f" firstAttribute="top" relation="greaterThanOrEqual" secondItem="tBV-wJ-bHs" secondAttribute="bottom" id="pJ9-Cb-772"/>
     <constraint firstItem="2kL-IL-xdO" firstAttribute="trailing" secondItem="5C9-QY-uP2" secondAttribute="trailing" constant="16" id="pUr-Fw-BIK"/>
     <constraint firstItem="iI4-J5-UpK" firstAttribute="centerX" secondItem="2kL-IL-xdO" secondAttribute="centerX" id="rbI-JP-9gZ"/>
     <constraint firstItem="5C9-QY-uP2" firstAttribute="leading" secondItem="2kL-IL-xdO" secondAttribute="leading" constant="16" id="wE1-MW-VkO"/>
</constraints>
// ...
<viewLayoutGuide key="safeArea" id="2kL-IL-xdO"/>
// ...

This is all under the root view in the hierarchy.

Model child node properties better

Eject models XML attributes to properties pretty well, but it does not model XML elements to properties very well. This usually just works, so it's not a big deal, but currently there's no way to inject a node value into the constructor. This is mostly causing issues for frame, but also for passing in target actions into UIBarButtonItem.

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.