An eject button for Interface Builder to generate swift code

Related tags

Utility Eject
Overview

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
Comments
  • Model child node properties better

    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.

    enhancement 
    opened by KingOfBrian 7
  • UIDatePicker minuteInterval error

    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

    bug 
    opened by transitmark 2
  • dyld: Library not loaded: EjectKit

    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.

    opened by edwardloveall 2
  • LayoutMargins not generated

    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.

    opened by mergesort 1
  • UIStackView may out of order

    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.

    opened by mergesort 1
  • States!

    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.
    opened by BrianSemiglia 1
  • Disable frame generation properly

    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.

    Needs Investigation 
    opened by KingOfBrian 0
  • highlightedColor should be highlightedTextColor

    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.

    opened by KingOfBrian 0
  • Anchorage argument

    Anchorage argument

    I saw there was Anchorage "support" but short of hard coding it, no way to enable it. Appending a trailing --anchorage will now toggle the anchoring mode.

    Requires 41 to merge first.

    opened by kalkwarf 0
  • Add skip logic

    Add skip logic

    IB leaves "Stuff" in the xibs that aren't necessary to port, and generate mysterious warnings when Ejecting. Centralized the logic to suppress them both to keep the real logic clean, and because I anticipate finding more elements that need attention.

    Requires 40 to merge first.

    opened by kalkwarf 0
  • eject fails to build with Xcode 11 (swift 3 is unsupported)

    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')
    
    opened by fxcoudert 0
  • Support Safe Area Layout Guide

    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.

    opened by mattwyskiel 1
  • Fix UIVisualEffectView

    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.

    bug 
    opened by KingOfBrian 0
Owner
Rightpoint
Rightpoint is an independent customer experience agency with technology at our core.
Rightpoint
Async+ for Swift provides a simple chainable interface for your async and throwing code, similar to promises and futures

Async+ for Swift provides a simple chainable interface for your async and throwing code, similar to promises and futures. Have the best of both worlds

async_plus 132 Jan 6, 2023
🗃 Powerful and easy to use Swift Query Builder for Vapor 3.

⚠️ This lib is DEPRECATED ⚠️ please use SwifQL with Bridges Quick Intro struct PublicUser: Codable { var name: String var petName: String

iMike 145 Sep 10, 2022
RResultBuilder is DSL library based on Result Builder

RResultBuilder is DSL library based on Result Builder Features Requirements Installation Usage Screenshot Example Contribute Meta Feat

Rakuten, Inc. 24 Sep 28, 2022
A result builder that allows to define shape building closures

ShapeBuilder A result builder implementation that allows to define shape building closures and variables. Problem In SwiftUI, you can end up in a situ

Daniel Peter 47 Dec 2, 2022
A result builder that build HTML parser and transform HTML elements to strongly-typed result, inspired by RegexBuilder.

HTMLParserBuilder A result builder that build HTML parser and transform HTML elements to strongly-typed result, inspired by RegexBuilder. Note: Captur

null 4 Aug 25, 2022
Generate protobuf message definitions from Swift structs

SwiftProtobufGen Generates protobuf definitions from Swift structs Building For some reason Xcode builds don't work at all but swiftpm command line bu

null 3 Nov 1, 2021
Swift Xid - Xid uses MongoDB Object ID algorighm1 to generate globally unique ids with base32 serialzation to produce shorter strings

Swift Xid - Xid uses MongoDB Object ID algorighm1 to generate globally unique ids with base32 serialzation to produce shorter strings

Uditha Atukorala 0 Jun 13, 2022
Generate a privacy policy for your iOS app

PrivacyFlash Pro To easily run PrivacyFlash Pro get the latest packaged release. Learn more about PrivacyFlash Pro in our research paper. PrivacyFlash

privacy-tech-lab 141 Dec 22, 2022
A utility to generate PreviewDevice presets from the available devices

SwiftUIGen A utility to generate PreviewDevice presets from the available devices Installation Manual Go to the GitHub page for the latest release Dow

Timberlane Labs 29 Nov 14, 2022
An extension for Xcode to generate builders from structs

Swift Struct Builder Generator Xcode Source Editor Extension An Xcode extension (plugin) to generate struct builders automatically. Install Swift Stru

Marius Wichtner 1 Nov 24, 2021
Minecraft-silicon - Generate Apple Silicon-native versions of Minecraft

Minecraft Silicon A dead simple utility to generate Apple Silicon-native Minecra

Cole Feuer 4 Jun 21, 2022
Generate and Preview Passbook Passes

Note: This project is no longer being maintained. Passbook is an iOS 6 feature that manages boarding passes, movie tickets, retail coupons, & loyalty

Nomad CLI 324 Dec 21, 2022
Generates a random photo after you click the button

Swift Random Photo Generator Generates a random photo after you click the button! Things you need to do ?? clone this repository git clone https://git

Tsuen Hsueh 2 Aug 16, 2022
This is a app developed in Swift, using Object Oriented Programing, UIKit user interface programmatically, API Request and Kingfisher to load remote images

iOS NOW ⭐ This is a app developed in Swift, using Object Oriented Programing, UIKit user interface programmatically, API Request and Kingfisher to loa

William Tristão de Paula 1 Dec 7, 2021
⏲ A tiny package to measure code execution time. Only 20 lines of code.

Measure ⏲ A tiny package to measure code execution time. Measure.start("create-user") let user = User() Measure.finish("create-user") Console // ⏲ Mea

Mezhevikin Alexey 3 Oct 1, 2022
Swift code to programmatically execute local or hosted JXA payloads without using the on-disk osascript binary

Swift code to programmatically execute local or hosted JXA payloads without using the on-disk osascript binary. This is helpful when you have Terminal access to a macOS host and want to launch a JXA .js payload without using on-disk osascript commands.

Cedric Owens 20 Sep 27, 2022
Useful Swift code samples, extensions, functionalities and scripts to cherry-pick and use in your projects

SwiftyPick ?? ?? Useful Swift code samples, extensions, functionalities and scripts to cherry-pick and use in your projects. Purpose The idea behind t

Manu Herrera 19 May 12, 2022
GenStore is a lightweight swift code generator for your resources.

GenStore is a lightweight swift code generator for your resources. GenStore can create classes for your images, colors and localized strings.

null 11 Oct 23, 2021
Useful extensions for my Swift code

UIViewController extensions presentAlert(withTitle title: String, message : String) presentAlertDialog(withTitle title: String, message : String, acti

Bogdan Grafonsky 1 Oct 17, 2021