Turn your Swift data model into a working CRUD app.

Overview

Model2App: Turn your Swift data model into a working CRUD app.

CI Status Platform iOS Version Carthage License: MIT Twitter: @karolkulesza

Model2App is a simple library that lets you quickly generate a CRUD iOS app based on just a data model defined in Swift. (CRUD - Create Read Update Delete). Ever wanted to quickly validate a data model for your next awesome iOS app? Model2App lets you save hours/days by generating a fully working app with persistence layer, validation and many more features. Just define your model, hit ⌘ + R and enjoy your app. 😎

Model2App uses Realm ❤️ under the hood and can be treated as its extension in development activities, especially in the phase of defining or validating a data model for a bigger project.

🔷 Features

✴️ Automatically generate:

      App menu based on a list of classes defined by your app
      Objects list views, per each model class
      Dynamic object view for creating, updating and viewing objects of a given class, based on list of model properties
      Object property cells, based either on property type or on declared control type (see supported control types below)
      Logic to handle different control types to change the values of object properties
      Validation logic for creating/updating objects using set of predefined rules or custom one using a closure
      Logic for persisting created objects in local storage (Realm)
      Logic for invoking object update session and deleting objects
      Logic for setting relationships between objects, in case of Object properties
      Related objects' sections for objects which are referenced by other objects (inverse relationships)
      Logic for creating a related object from a given object view
      Logic to traverse (infinitely) between related objects
      Out of the box zoom-in & zoom-out navigation animations
      & a bunch of many more small features

✴️ Customize default app configuration:

      Adjust app menu (layout, order, background, menu items' icons/layout/alphas, font names/sizes/colors, animations and more)
      Pick any menu item icon from provided bundle (MenuIcons), provide your own, or let Model2App pick one for you
      Adjust objects list views (cell layout/background, displayed object properties, images layout, animations and more)
      Adjust object view property cells (cell layout/background, font names/sizes/colors, images layout, placeholders and more)
      Adjust object view Related Objects' headers (header layout/background, font names/sizes/colors)
      Adjust picker list views (cell layout/background, font names/sizes/colors)
      Hide a specific class from app menu or hide a specific property of a given class from object view
      Adjust default animation configuration: presentation/dismissal animation duration, damping ratio or initial spring velocity
      Specify whether image views presented in cells should be rounded or not

✴️ Other features:

      Supports both iPhones and iPads
      Supports both portrait and landscape orientations
      Validates your data model for declared relationships and declared control types for properties
      Enables using emoji character for menu icon image
      Flexibility & Extensibility: Apart from configuration parameters defined in M2AConfig class which can be overridden, most of the classes & methods used for core app features have open access modifier, so you can customize or extend selected parts of Model2App framework in your app

✴️ Supported control types:

      ✏️ TextField
      ✏️ NumberField
      ✏️ FloatDecimalField
      ✏️ DoubleDecimalField
      ✏️ CurrencyField
      ✏️ PhoneField
      ✏️ EmailField
      ✏️ PasswordField
      ✏️ URLField
      ✏️ ZIPField
      ✏️ Switch
      ✏️ DatePicker
      ✏️ TimePicker
      ✏️ DateTimePicker
      ✏️ TextPicker
      ✏️ ObjectPicker
      ✏️ ImagePicker

🔷 Requirements

      Xcode 10.1+
      Swift 4.2+

🔷 Installation

Model2App is available through both CocoaPods and Carthage.

✴️ CocoaPods

In order to install Model2App via CocoaPods, simply add the following line to your Podfile:

pod 'Model2App'

Then run the following command:

$ pod install

✴️ Carthage

In order to install Model2App via Carthage, simply add the following line to your Cartfile:

github "Q-Mobile/Model2App" ~> 0.1.0

Then run the following command:

$ carthage update

Please remember to add all *.framework files from Carthage/Build/* to your project (Not only Model2App.framework), apart from other standard steps for Carthage

🔷 Usage

✴️ Model definition:

After installing Model2App, simply define your data model by subclassing ModelClass, as in example below or as in example app available in this repo (Model2AppTestApp) and hit ⌘ + R. (NOTE: Sample data model visible below is just a small excerpt from the example app, please refer to Model2AppTestApp source for a more extended model)

@objcMembers class Company : ModelClass {
    dynamic var name : String?
    dynamic var phoneNumber : String?
    dynamic var industry : String?
}

@objcMembers class Person : ModelClass {
    dynamic var firstName : String?
    dynamic var lastName : String?
    dynamic var salutation : String?
    dynamic var phoneNumber : String?
    dynamic var privateEmail : String?
    dynamic var workEmail : String?
    let isKeyOpinionLeader = OptionalProperty<Bool>()
    dynamic var birthday : Date?
    dynamic var website : String?
    dynamic var note : String?
    dynamic var picture : Data?
    dynamic var company : Company?
}

@objcMembers class Deal : ModelClass {
    dynamic var name : String?
    let value = OptionalProperty<Int>()
    dynamic var stage : String?
    dynamic var closingDate : Date?
    dynamic var company : Company?
}      

✴️ Customizing default model configuration:

If you'd like to customize the default class/property configuration, simply override some or all of the computed type properties defined by ModelClass:

@objcMembers class Company : ModelClass {
    // (model properties defined earlier)

    override class var pluralName: String { return "Companies" }
    override class var menuIconFileName: String { return "users" }
    override class var menuOrder: Int { return 2 }
    override class var inverseRelationships: [InverseRelationship] {
        return [
            InverseRelationship("employees", sourceType: Person.self, sourceProperty: #keyPath(Person.company)),
            InverseRelationship("deals", sourceType: Deal.self, sourceProperty: #keyPath(Deal.company))
        ]
    }

    override class var propertyConfigurations: [String: PropertyConfiguration] {
        return [
            #keyPath(name) : PropertyConfiguration(
                placeholder: "Enter company name",
                validationRules: [.Required]
            ),
            #keyPath(phoneNumber) : PropertyConfiguration(
                placeholder: "Enter phone number"
            ),
            #keyPath(industry) : PropertyConfiguration(
                controlType: .TextPicker,
                pickerValues: ["Consulting", "Education", "Financial Services", "Government", "Manufacturing", "Real Estate", "Technology", "Other"]
            )
        ]
    }
}

@objcMembers class Person : ModelClass {
    // (model properties defined earlier)

    override class var pluralName: String { return "People" }
    override class var menuIconFileName: String { return "user-1" }
    override class var menuIconIsFromAppBundle: Bool { return true }
    override class var menuOrder: Int { return 1 }

    override class var listViewCellProperties: [String] {
        return [#keyPath(picture), #keyPath(firstName), #keyPath(lastName)]
    }

    override class var listViewCellLayoutVisualFormats: [String] {
        return [
            "H:|-10-[picture]-[firstName]-5-[lastName(>=50)]-|" // OR: (with slightly weaker readability but more safe): "H:|-10-[#keyPath(picture)]-[#keyPath(firstName)]-5-[#keyPath(lastName)(>=50)]"
        ]
    }

    override class var propertyConfigurations: [String: PropertyConfiguration] {
        return [
            #keyPath(firstName) : PropertyConfiguration(
                controlType: .TextField,
                placeholder: "Enter first name",
                validationRules: [.Required]
            ),
            #keyPath(lastName) : PropertyConfiguration(
                controlType: .TextField,
                placeholder: "Enter last name",
                validationRules: [.Required]
            ),
            #keyPath(salutation) : PropertyConfiguration(
                controlType: .TextPicker,
                pickerValues: ["Mr.", "Ms.", "Mrs.", "Dr.", "Prof."],
                validationRules: [.Required]
            ),
            #keyPath(phoneNumber) : PropertyConfiguration(
                controlType: .PhoneField,
                placeholder: "Enter phone number",
                validationRules: [.MinLength(length: 9), .MaxLength(length: 12)]
            ),
            #keyPath(privateEmail) : PropertyConfiguration(
                controlType: .EmailField,
                placeholder: "Enter email address",
                validationRules: [.Email]
            ),
            #keyPath(workEmail) : PropertyConfiguration(
                controlType: .EmailField,
                placeholder: "Enter email address",
                validationRules: [.Required, .Email, .Custom(isValid: { object in
                    if let workEmail = object[#keyPath(workEmail)] as? String,
                       let privateEmail = object[#keyPath(privateEmail)] as? String,
                       workEmail == privateEmail {
                            UIUtilities.showValidationAlert("Work Email cannot be the same as Private Email.")
                            return false
                    }
                    return true
                })]
            ),
            #keyPath(birthday) : PropertyConfiguration(
                controlType: .DatePicker,
                validationRules: [.Required]
            ),
            #keyPath(website) : PropertyConfiguration(
                controlType: .URLField,
                placeholder: "Enter URL",
                validationRules: [.URL]
            ),
            #keyPath(note) : PropertyConfiguration(
                controlType: .TextField,
                placeholder: "Enter note",
                validationRules: [.MaxLength(length: 1000)]
            ),
            #keyPath(company) : PropertyConfiguration(
                validationRules: [.Required]
            ),
            #keyPath(picture) : PropertyConfiguration(
                controlType: .ImagePicker
            )
        ]
    }
}

@objcMembers class Deal : ModelClass {
    // (model properties defined earlier)

    override class var pluralName: String { return "Deals" }
    override class var menuIconFileName: String { return "money" }

    override class var listViewCellProperties: [String] {
        return [#keyPath(name), "value", #keyPath(stage)]
    }

    override class var listViewCellLayoutVisualFormats: [String] {
        return [
            "H:|-10@750-[name(>=50)]-(>=10)-[value(>=50)]-|",
            "H:|-10@750-[stage]-(>=10)-[value]",
            "V:|-10@750-[value]-10@750-|",
            "V:|-10@750-[name]-[stage]-|"
        ]
    }

    override class var propertyConfigurations: [String: PropertyConfiguration] {
        return [
            #keyPath(name) : PropertyConfiguration(
                controlType: .TextField,
                placeholder: "Enter deal name",
                validationRules: [.Required]
            ),
            "value" : PropertyConfiguration(
                controlType: .CurrencyField,
                placeholder: "Enter deal value",
                validationRules: [.Required]
            ),
            #keyPath(stage) : PropertyConfiguration(
                controlType: .TextPicker,
                pickerValues: ["Prospecting", "Qualified", "Reviewed", "Quote", "Won", "Lost"],
                validationRules: [.Required]
            ),
            #keyPath(company) : PropertyConfiguration(
                validationRules: [.Required]
            )
        ]
    }
}      

✴️ Customizable ModelClass type properties:

✏️ displayName - Display name of this class. If not provided, inferred from the class name
✏️ pluralName - Plural name of this class. Used to name list of objects or menu items. If not provided, <ClassName> - List is used
✏️ menuIconFileName - Name of the image file used for menu icon in root menu of the app
✏️ menuIconIsFromAppBundle - Specifies whether Model2App should look for menu icon file in main app bundle. If false, Model2App's bundle will be used
✏️ menuOrder - Order of menu item for this class in root menu of the app
✏️ propertyConfigurations - Dictionary of property configurations for this class
✏️ inverseRelationships - List of inverse relationships for this class (Should be defined if there are any to-one relationships from other classes and if you would like to present a section of related objects)
✏️ listViewCellProperties - List of properties used in list view cell's for this class. Should contain all properties specified in listViewCellLayoutVisualFormats
✏️ listViewCellLayoutVisualFormats - List of visual formats for list view cell layout, using Apple's Auto Layout Visual Format Language
✏️ isHiddenInRootView - Specifies whether a given model class should be hidden in root menu of the app (Useful in case of child entities that should only be displayed in related objects section, for a given object)

✴️ PropertyConfiguration's properties:

✏️ controlType - Specifies the type of UI control used for this property
✏️ placeholder - Specifies the placeholder value used when no value is provided for this property
✏️ pickerValues - Specifies the list of potential picker values for this property. Valid only for TextPicker ControlType
✏️ validationRules - Specifies the list of validation rules for this property (evaluated when creating a new object of this class)
✏️ isHidden - Specifies whether this property should be hidden on UI

✴️ Supported validation rules (ValidationRule):

✏️ Required
✏️ MinLength(length: Int)
✏️ MaxLength(length: Int)
✏️ MinValue(value: Double)
✏️ MaxValue(value: Double)
✏️ Email
✏️ URL
✏️ Custom(isValid: (ModelClass) -> Bool)

✴️ Customizing default app configuration:

M2AConfig class defines default app configuration that can be optionally subclassed by the app. Please refer to both M2AConfig class source and AppConfig.swift file in Model2AppTestApp example app.

✴️ Remarks for model definition:

  • As highlighted above, Model2App uses Realm under the hood, so it has similar considerations as for the model definition:
    • All property attributes must follow the rules specified in Realm documentation: https://realm.io/docs/swift/latest#property-cheatsheet. In a nutshell, all model properties should be declared as @objc dynamic var (or just dynamic var if the class itself is declared using objcMembers), except for the OptionalProperty (used for numbers/bool), which should be declared using just let.
    • String, Date and Data properties can be optional. Object properties (defining relationships) must be optional. Storing optional numbers is done using OptionalProperty (alias for Realm's RealmOptional).

🔷 Example App

📱 Model2AppTestApp directory in this repo contains an example app that defines a very simple CRM-related data model. Open Model2AppTestApp/Model2AppTestApp.xcworkspace and run this test app to see what are the effects of applying Model2App library to a sample data model.

🔷 Limitations / Known Issues

      ⚠️ Version 0.1.0 of Model2App does not handle data model migrations, so if you change your data model after the initial app launch, you'll get an error and will have to remove the app, prior the next launch, in order to see the updated model. Handling model migrations is planned in Roadmap for future releases.

      ⚠️ In case of OptionalProperty properties you cannot use #keyPath to safely reference a given property (for example from propertyConfigurations or listViewCellProperties definition)

🔷 Roadmap / Features for Future Releases

Version 0.1.0 of Model2App contains a limited set of features. There are many functionalities that could extend its value:

      ☘️ Searching on object list views
      ☘️ Filtering on object list views
      ☘️ Sorting on object list views
      ☘️ Handling cascade deletions
      ☘️ Handling model migrations
      ☘️ Support for control type: “Slider"
      ☘️ Support for control type: “TextView"
      ☘️ Support for control type: “Button"
      ☘️ Support for one-to-many relationships (so far only inverse one-to-many relationships are supported)
      ☘️ Option to use emoji as menu item icon, instead of images
      ☘️ ... and many more! Stay tuned! ❤️

🔷 Contributing

👨🏻‍🔧 Feel free to contribute to Model2App by creating a pull request, following these guidelines:

  1. Fork Model2App
  2. Create your feature branch
  3. Commit your changes, along with unit tests
  4. Push to the branch
  5. Create pull request

🔷 Credits / Acknowledgments

      🎨 Icons used by Model2App were designed by Lucy G from Flaticon
      💚 Special thanks to all the people behind Realm

🔷 Author

      👨‍💻 Karol Kulesza (@karolkulesza)

🔷 License

      📄 Model2App is available under the MIT license. See the LICENSE file for more info.

You might also like...
An iOS app to turn typed text into images of handwritten text in your own handwriting style.
An iOS app to turn typed text into images of handwritten text in your own handwriting style.

Text-to-Handwritting © 2021 by Daniel Christopher Long An iOS app to turn typed text into images of handwritten text in your own handwriting style. ht

🗣Voice overlay helps you turn your user's voice into text, providing a polished UX while handling for you the necessary permissions
🗣Voice overlay helps you turn your user's voice into text, providing a polished UX while handling for you the necessary permissions

Voice overlay helps you turn your user's voice into text, providing a polished UX while handling for you the necessary permissions. It uses i

Turn your TV into a dashboard displaying any webpage!
Turn your TV into a dashboard displaying any webpage!

🚨 WORK IN PROGRESS 🚨 Turn your TV into a dashboard displaying any webpage! Usage Currently it is not possible to interact with webpages on TV. Use

Turn a photo of your food into a face
Turn a photo of your food into a face

Megabite Megabite is a mobile app that automatically turns a photo of your food into a face. Read more about it here. Installation This project uses C

JSONHelper - ✌ Convert anything into anything in one operation; JSON data into class instances, hex strings into UIColor/NSColor, y/n strings to booleans, arrays and dictionaries of these; anything you can make sense of!

JSONHelper Convert anything into anything in one operation; hex strings into UIColor/NSColor, JSON strings into class instances, y/n strings to boolea

A library to turn dictionary into object and vice versa for iOS. Designed for speed!

WAMapping Developed and Maintained by ipodishima Founder & CTO at Wasappli Inc. Sponsored by Wisembly A fast mapper from JSON to NSObject Fast Simple

In this mini app covered the concepts like basics of SwiftUI and Navigations and Animations and List with CRUD functions and MVVM and App Launch and App icons adding and also applied persistence using UserDefaults Concept.
In this mini app covered the concepts like basics of SwiftUI and Navigations and Animations and List with CRUD functions and MVVM and App Launch and App icons adding and also applied persistence using UserDefaults Concept.

TodoList In this application used the concepts from the beginner level project of SwiftUI_Evolve_1 The following concepts covered in this mini app Swi

Passing data from a couple of screen into a Model
Passing data from a couple of screen into a Model

Passing-Data Passing data from a couple of screen into a Model Introduction Hi, this video is just a representation project from this Video by Essenti

C4 is an open-source creative coding framework that harnesses the power of native iOS programming with a simplified API that gets you working with media right away. Build artworks, design interfaces and explore new possibilities working with media and interaction. CRUD App for iOS with Swift
CRUD App for iOS with Swift

Todoey ✓ Our Goal The objective of this tutorial is to understand how to save data in iOS. We'll look at various choices and learn to use UserDefaults

CRUD - A simple iOS App to manage tasks

CRUD App ⭐ The App 💡 A simple iOS App to manage tasks Setup 👨‍💻 Clone the rep

CoreMLSample - CoreML Example for in app model and download model

CoreMLSample Sample for CoreML This project is CoreML Example for in app model a

CRUD is an object-relational mapping (ORM) system for Swift 4+.

CRUD is an object-relational mapping (ORM) system for Swift 4+. CRUD takes Swift 4 Codable types and maps them to SQL database tables. CRUD can create tables based on Codable types and perform inserts and updates of objects in those tables. CRUD can also perform selects and joins of tables, all in a type-safe manner.

Modulo4NativoIOS - Atividade Modulo 4 Nativo IOS - CRUD com SireStore
Modulo4NativoIOS - Atividade Modulo 4 Nativo IOS - CRUD com SireStore

Modulo4NativoIOS Atividade Modulo 4 Nativo IOS - CRUD com Cloud Firebase Aluno:

Aplikasi iOS Simulasi CRUD Stok Barang dengan SwiftUI, Firebase CLI & Firestore
Aplikasi iOS Simulasi CRUD Stok Barang dengan SwiftUI, Firebase CLI & Firestore

iStockery Aplikasi iStockery adalah aplikasi stok inventory berbasis iOS yang dibuat menggunakan Firebase (Firestore) secara Local dengan fitur CRUD d

List tree data souce to display hierachical data structures in lists-like way. It's UI agnostic, just like view-model and doesn't depend on UI framework
List tree data souce to display hierachical data structures in lists-like way. It's UI agnostic, just like view-model and doesn't depend on UI framework

SwiftListTreeDataSource List tree data souce to display hierachical data structures in lists-like way. It's UI agnostic, just like view-model, so can

Floating indicator, mimicrate to indicator which appear when silent mode turn on / off. Support large texts.
Floating indicator, mimicrate to indicator which appear when silent mode turn on / off. Support large texts.

SPIndicator About Mimicrate to indicator which appear when silent mode turn on / off. Availalbe 2 animated presets: done & error. Also support custom

🦁 🃏 📱 An animal matching puzzle card game– built with turn-based game engine boardgame.io and React-Native + React-Native-Web
🦁 🃏 📱 An animal matching puzzle card game– built with turn-based game engine boardgame.io and React-Native + React-Native-Web

Matchimals.fun an animal matching puzzle card game 🦁 🃏 🍎 Download for iOS from the App Store 🤖 Download for Android from the Google Play Store 🖥

Recording Indicator Utility lets you turn off the orange microphone recording indicator light for live events and screencasts.
Recording Indicator Utility lets you turn off the orange microphone recording indicator light for live events and screencasts.

Recording Indicator Utility Recording Indicator Utility lets you turn off the orange microphone recording indicator light, making it ideal for profess

Owner
Q Mobile
We build custom-tailored mobile apps.
Q Mobile
Sample app to demonstrate data sharing between a WatchKit app and its main app using Realm

#Done! A sample app demonstrating how to share data between an app an its Watch extension using Realm. You can read more about it here. ##Screenshot #

Fancy Pixel 147 Dec 8, 2022
Realm is a mobile database: a replacement for Core Data & SQLite

Realm is a mobile database that runs directly inside phones, tablets or wearables. This repository holds the source code for the iOS, macOS, tvOS & wa

Realm 15.7k Jan 7, 2023
Face detection and recognition iOS app with OpenCV

Facemotion Facemotion it's an iOS app, allowing you to find easily a contact by face recognition. Scan the face of a person, whether the contact is in

Remi ROBERT 170 Nov 15, 2022
iOS price list app using Firebase, Realm & more

how-much iOS app to record how much things cost using various data persistence implementations. The basic data unit is an item, a simple dictionary: {

null 22 Aug 15, 2022
iOS and watchOS app for try! NYC

trySwiftApp try! Swift Conference App 2016 presentations You can find an overview of speakers and their presentations, including slides and video here

try! Swift 140 Feb 8, 2022
Unrealm is an extension on RealmCocoa, which enables Swift native types to be saved in Realm.

Unrealm enables you to easily store Swift native Classes, Structs and Enums into Realm . Stop inheriting from Object! Go for Protocol-Oriented program

Artur  Mkrtchyan 518 Dec 13, 2022
try! Swift Tokyo

trySwiftApp try! Swift Conference App try! Swift Presentations You can find an overview of speakers and their presentations, including slides and vide

try! Swift 251 Dec 18, 2022
Turn your Swift data model into a working CRUD app.

Model2App is a simple library that lets you quickly generate a CRUD iOS app based on just a data model defined in Swift. (CRUD - Create Read Update De

Q Mobile 132 Dec 22, 2022
Model2App - Turn your Swift data model into a working CRUD app.

Model2App is a simple library that lets you quickly generate a CRUD iOS app based on just a data model defined in Swift. (CRUD - Create Read Update De

Q Mobile 132 Dec 22, 2022
A fast, convenient and nonintrusive conversion framework between JSON and model. Your model class doesn't need to extend any base class. You don't need to modify any model file.

MJExtension A fast, convenient and nonintrusive conversion framework between JSON and model. 转换速度快、使用简单方便的字典转模型框架 ?? ✍??Release Notes: more details Co

M了个J 8.5k Jan 3, 2023