Created
May 4, 2015 16:16
-
-
Save broomburgo/ba8e5dd6cd509fdfd781 to your computer and use it in GitHub Desktop.
type-first-development-in-swift-code-for-blog-article
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* NOT QUITE FLEXIBLE | |
struct Field { | |
let id: String | |
let name: String | |
let value: String | |
let visible: Bool | |
} | |
*/ | |
typealias UniqueKey = String | |
enum FieldValue: Equatable { | |
case Text(String) | |
case Empty | |
} | |
func == (lhs: FieldValue, rhs: FieldValue) -> Bool { | |
switch (lhs, rhs) { | |
case (.Empty, .Empty): | |
return true | |
case (.Text(let lhsText), .Text(let rhsText)): | |
return lhsText == rhsText | |
default: | |
return false | |
} | |
} | |
enum FieldVisibility: Equatable { | |
case Visible | |
case Hidden | |
} | |
func == (lhs: FieldVisibility, rhs: FieldVisibility) -> Bool { | |
switch (lhs, rhs) { | |
case (.Visible, .Visible), (.Hidden, .Hidden): | |
return true | |
default: | |
return false | |
} | |
} | |
struct Field { | |
let id: UniqueKey | |
let name: String | |
let value: FieldValue | |
let visibility: FieldVisibility | |
init (_ id: UniqueKey, _ name: String, _ value: FieldValue, _ visibility: FieldVisibility) { | |
self.id = id | |
self.name = name | |
self.value = value | |
self.visibility = visibility | |
} | |
} | |
typealias FieldChange = Field -> Field | |
func setVisibility (visibility: FieldVisibility) -> FieldChange { | |
return { field in | |
return Field ( | |
field.id, | |
field.name, | |
field.value, | |
visibility | |
) | |
} | |
} | |
/// alcuni esempi | |
let setHidden = setVisibility(.Hidden) | |
let visibleField1 = Field ("","", .Empty, .Visible) | |
let isTrue1 = visibleField1.visibility == .Visible | |
let hiddenField1 = setHidden(visibleField1) | |
let isTrue2 = hiddenField1.visibility == .Hidden | |
typealias FieldChangeGenerator = Field -> FieldChange | |
/// una funzione che rende modifica la visibilità di un campo in base al valore di un altro campo | |
/* | |
typealias FieldCondition = Field -> FieldVisibility | |
func setVisibilityIfOrigin (condition: FieldCondition) -> FieldChangeGenerator { | |
return { origin in | |
let newVisibility = condition(origin) | |
return setVisibility(newVisibility) | |
} | |
} | |
/// alcuni esempi | |
let setVisibleIfNotEmpty = setVisibilityIfOrigin { field in | |
switch field.value { | |
case .Empty: | |
return .Hidden | |
default: | |
return .Visible | |
} | |
} | |
let nonEmptyField = Field ("", "", .Text("something"), .Visible) | |
let anotherVisibleField = setVisibleIfNotEmpty(nonEmptyField)(invisibleField) | |
let isTrue3 = anotherVisibleField.visibility == .Visible | |
*/ | |
/// rendiamo generica la condizione | |
struct FieldCondition<T> { | |
let apply: Field -> T | |
init(_ apply: Field -> T) { | |
self.apply = apply | |
} | |
} | |
func setTargetIfOrigin<T> (condition: FieldCondition<T>, generate: T -> FieldChange) -> FieldChangeGenerator { | |
return { field in | |
return generate(condition.apply(field)) | |
} | |
} | |
func setVisibilityIfOrigin (condition: FieldCondition<FieldVisibility>) -> FieldChangeGenerator { | |
return setTargetIfOrigin (condition) { visibility in setVisibility(visibility) } | |
} | |
let setVisibleIfNotEmpty = setVisibilityIfOrigin (FieldCondition { origin in | |
switch origin.value { | |
case .Empty: | |
return .Hidden | |
default: | |
return .Visible | |
} | |
}) | |
let copyValue = setTargetIfOrigin (FieldCondition { $0.value}) { value in | |
return { target in | |
return Field(target.id,target.name,value,target.visibility) | |
} | |
} | |
let field1 = Field("field1","",.Text("1"),.Visible) | |
let field2 = Field("field2","",.Text("2"),.Visible) | |
let newField1 = copyValue(field2)(field1) | |
let isTrue4 = newField1.value == .Text("2") | |
/// creiamo una condizione per il target | |
/// serve perché nel form avremmo tanti campi e noi vogliamo solo modificarne uno | |
typealias FieldCheck = FieldChange -> FieldChange | |
typealias FieldConditionBool = FieldCondition<Bool> | |
func checkTarget (condition: FieldConditionBool) -> FieldCheck { | |
return { change in | |
return { field in | |
if condition.apply(field) { | |
return change(field) | |
} | |
else { | |
return field | |
} | |
} | |
} | |
} | |
func ifTargetId (id: UniqueKey) -> FieldCheck { | |
return checkTarget (FieldCondition { $0.id == id }) | |
} | |
let visibleField = ifTargetId("field2")(setHidden)(field1) | |
let hiddenField = ifTargetId("field2")(setHidden)(field2) | |
let isTrue5 = visibleField.visibility == .Visible | |
let isTrue6 = hiddenField.visibility == .Hidden | |
let fields = [field1,field2] | |
let newFields = fields.map(ifTargetId("field2")(setHidden)) | |
let isTrue7 = newFields[0].visibility == .Visible | |
let isTrue8 = newFields[1].visibility == .Hidden | |
/// ok, da una lista di Field vogliamo realizzare un Form | |
/* BAD IDEA | |
struct Section { | |
let fields: [Field] | |
} | |
struct Step { | |
let sections: [Section] | |
} | |
struct Form { | |
let steps: [Step] | |
} | |
*/ | |
enum Node<T> { | |
case Branch([T]) | |
case Root([Node<T>]) | |
} | |
typealias Form = Node<Field> | |
func branch (fields: [Field]) -> Form { | |
return Node.Branch(fields) | |
} | |
func root (nodes: [Form]) -> Form { | |
return Node.Root(nodes) | |
} | |
typealias FormChange = Form -> Form | |
func changeFromFieldChange (fieldChange: FieldChange) -> FormChange { | |
return { form in | |
switch form { | |
case .Branch (let fields): | |
return branch(fields.map(fieldChange)) | |
case .Root (let subforms): | |
return root(subforms.map(changeFromFieldChange(fieldChange))) | |
} | |
} | |
} | |
let updateField = changeFromFieldChange | |
let form = branch([field1,field2]) | |
let newForm = updateField(ifTargetId("field1")(setHidden))(form) | |
typealias FieldGet = Form -> [Field] | |
func fieldsWithCondition (condition: FieldConditionBool) -> FieldGet { | |
return { form in | |
switch form { | |
case .Branch(let fields): | |
return fields.filter(condition.apply) | |
case .Root(let subforms): | |
return subforms.map(fieldsWithCondition(condition)).reduce([], combine: +) | |
} | |
} | |
} | |
func fieldsWithId (id: UniqueKey) -> FieldGet { | |
return fieldsWithCondition (FieldConditionBool { $0.id == id }) | |
} | |
let allFields: FieldGet = fieldsWithCondition (FieldConditionBool { _ in true }) | |
let twoFields = allFields(form) | |
let isTrue9 = count(twoFields) == 2 | |
let isTrue10 = fieldsWithId("field1")(form)[0].visibility == .Visible | |
let isTrue11 = fieldsWithId("field1")(newForm)[0].visibility == .Hidden |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Link all'articolo: http://engineering.facile.it/type-first-development-in-swift/