Skip to content

Instantly share code, notes, and snippets.

@chuganzy
Last active August 29, 2015 14:19
Show Gist options
  • Save chuganzy/1f74224d7c62f01654ec to your computer and use it in GitHub Desktop.
Save chuganzy/1f74224d7c62f01654ec to your computer and use it in GitHub Desktop.
xcdatamodeldからSwiftのコードをいい感じに吐く
#!/usr/bin/env ruby
require 'nokogiri'
require 'colorize'
require 'erubis'
class Manager
class SwiftConverter
def to_swift
raise 'Error: method `to_swift` is not implemented'
end
end
class SwiftType < SwiftConverter
def self.is_optional(xml)
xml['optional'] == 'YES'
end
def initialize(model_type, is_optional, should_convert = true)
@is_optional = is_optional
if !should_convert
@type = model_type
return
end
case model_type
when /^Integer (\d+)/
type = "Int#{$1}"
when 'Decimal'
type = 'NSDecimal'
when 'Boolean'
type = 'Bool'
when 'Date'
type = 'NSTimeInterval'
when 'Binary'
type = 'NSData'
when 'Transformable'
type = 'AnyObject'
else
type = model_type
end
@type = type
end
def to_swift
if !@is_optional
return @type
end
@type + '?'
end
end
class SwiftXmlConverter < SwiftConverter
attr_reader :xml
def initialize(xml)
@xml = xml
end
end
class Entity < SwiftXmlConverter
attr_reader :class_name, :parent_class_name
def initialize(xml)
super(xml)
@attributes = []
@class_name = xml['representedClassName']
xml.xpath('attribute').each do |xml|
attribute = Attribute.new(xml)
@attributes.push(attribute)
end
end
def setup_relationships(entities)
entities_type_dictionary = {}
entities.each do |entity|
entities_type_dictionary[entity.xml['name']] = entity.class_name
end
if @xml['parentEntity']
@parent_class_name = entities_type_dictionary[@xml['parentEntity']]
else
@parent_class_name = 'NSManagedObject'
end
@relationships = []
@xml.xpath('relationship').each do |xml|
relationship = Relationship.new(xml, entities_type_dictionary)
@relationships.push(relationship)
end
end
def to_swift
attribute_swift = ''
template = <<"SWIFT"
//
// THIS IS GENERATED CODE.
// DO NOT MAKE ANY CHANGES.
//
import CoreData
@objc(<%= @entity.class_name %>)
class <%= @entity.class_name %>: <%= @entity.parent_class_name %> {
<% @attributes.each do |attribute| %>
<%= attribute.to_swift %>
<% end %>
<% @relationships.each do |relationship| %>
<%= relationship.to_swift %>
<% if relationship.type.start_with?('NSOrderedSet') %>
func <%= relationship.name %>Set() -> NSMutableOrderedSet {
self.willAccessValueForKey("<%= relationship.name %>")
let result = self.mutableOrderedSetValueForKey("<%= relationship.name %>")
self.didAccessValueForKey("<%= relationship.name %>")
return result
}
<% end %>
<% end %>
}
SWIFT
erb = Erubis::Eruby.new(template)
data = {
'entity' => self,
'attributes' => @attributes,
'relationships' => @relationships
}
erb.evaluate(data)
end
end
class Attribute < SwiftXmlConverter
def initialize(xml)
super(xml)
end
def to_swift
type = SwiftType.new(@xml['attributeType'], SwiftType.is_optional(@xml))
"@NSManaged var #{@xml['name']}: #{type.to_swift}"
end
end
class Relationship < SwiftXmlConverter
attr_reader :name, :type
def initialize(xml, entities_type_dictionary)
super(xml)
@entities_type_dictionary = entities_type_dictionary
if @xml['toMany'] == 'YES'
if @xml['ordered'] == 'YES'
type = 'NSOrderedSet'
else
type = 'NSSet'
end
else
type = @entities_type_dictionary[@xml['destinationEntity']]
end
type = SwiftType.new(type, SwiftType.is_optional(@xml), false)
@name = @xml['name']
@type = type.to_swift
end
def to_swift
"@NSManaged var #{@name}: #{@type}"
end
end
def initialize(model_base_path, model_name)
@model_base_path = model_base_path
path = @model_base_path + "/#{model_name}.xcdatamodeld/#{model_name}.xcdatamodel/contents"
f = File.open(path, 'r') do |file|
@xml = Nokogiri::XML(file)
end
end
def update_model
entities = []
@xml.xpath('/model/entity').each do |entity|
entity = Entity.new(entity)
entities.push(entity)
end
entities.each do |entity|
entity.setup_relationships(entities)
file_name = "#{entity.class_name}.swift"
puts "Updating: #{file_name}".bold
File.open("#{@model_base_path}/#{file_name}", 'w') do |file|
file.puts(entity.to_swift)
end
end
end
end
if __FILE__ == $PROGRAM_NAME
base_path = File.expand_path("#{__dir__}/../Sumally/Model") # absolute path to your .xcdatamodeld base path
name = 'Model' # name of your .xcdatamodeld
manager = Manager.new(base_path, name)
manager.update_model()
puts "Update Complete!".green
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment