diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 00000000..aed16c3a --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,90 @@ +stages: +# - test + - download + - build + - deploy + +#test: +# stage: test +# script: +# - echo "This job tests something" +# tags: +# - xcode_12_2 + +download_artifacts: + stage: download + script: + - ./Scripts/download_dependencies.sh + only: + - branches + - develop + tags: + - bash_shell + environment: + name: oneartifactory + url: https://oneartifactoryprod.verizon.com/artifactory + variables: + ARTIFACTORY_URL: https://oneartifactoryprod.verizon.com/artifactory + +build_project: + stage: build + script: + - ./Scripts/build_aggregate.sh + only: + - branches + - develop + tags: + - xcode_12_2 + +deploy_snapshot: + stage: deploy + script: + - cd Scripts && ./upload_vds_frameworks.sh + only: + - branches + - develop + tags: + - bash_shell + environment: + name: oneartifactory + url: https://oneartifactoryprod.verizon.com/artifactory + variables: + ARTIFACTORY_URL: https://oneartifactoryprod.verizon.com/artifactory + +#promote_snapshot: +# stage: go live +# # Prevent artifacts from needing to re-download. Everything we need is in Artifactory. +# dependencies: [] +# script: +# # Grab the framework version from the xcode project. +# - framework_ver=$(cd RemoteView && agvtool vers -terse) +# - cd Scripts && ./promote_remote_view_frameworks.sh $framework_ver +# only: +# - tags +# tags: +# - bash_shell +# environment: +# name: oneartifactory +# url: https://oneartifactoryprod.verizon.com/artifactory +# variables: +# ARTIFACTORY_URL: https://oneartifactoryprod.verizon.com/artifactory +# +#create_version_tag: +# stage: tag +# when: manual +# # Prevent artifacts from needing to re-download. +# dependencies: [] +# script: +# # Grab the framework version from the xcode project and create a tag of the version. +# - framework_ver=$(cd RemoteView && agvtool vers -terse) +# - git tag -a "v${framework_ver}" -m "Version ${framework_ver} created by gitlab-ci Build" +# # Extract the git repo url to ssh version (git@gitlab.verizon.com) +# - ci_push_repo="git@${CI_SERVER_HOST}:${CI_PROJECT_PATH}.git" +# - echo $ci_push_repo +# # Set the remote url for pushing assuming the gitlab runner has SSH access to the repo. +# - git remote set-url --push origin $ci_push_repo +# - git push origin "v${framework_ver}" +# only: +# - develop +# tags: +# - bash_shell diff --git a/Scripts/build_aggregate.sh b/Scripts/build_aggregate.sh new file mode 100755 index 00000000..b4183ead --- /dev/null +++ b/Scripts/build_aggregate.sh @@ -0,0 +1,55 @@ +#!/bin/bash -e + +unset TOOLCHAINS #Xcode 7.3 BUG FIX http://stackoverflow.com/questions/36184930/xcodebuild-7-3-cant-enable-bitcode + +# define variables +C_PROJECT_NAME="VDS" +PHONE_CONFIGURATION="Release" +BUILD_DIR=$(xcodebuild -showBuildSettings -project ./${C_PROJECT_NAME}.xcodeproj | grep -w -o 'BUILD_DIR = .*' | cut -d\ -f3-) +PHONE_ARCHIVE_PATH="${BUILD_DIR}/${PHONE_CONFIGURATION}-iphoneos/${C_PROJECT_NAME}.xcarchive" +SIMULATOR_CONFIGURATION="Debug" +SIMULATOR_ARCHIVE_PATH="${BUILD_DIR}/${SIMULATOR_CONFIGURATION}-iphonesimulator/${C_PROJECT_NAME}.xcarchive" +FRAMEWORKS_DIR=$BUILD_DIR/../../../Frameworks/VDS +UNIVERSAL_OUTPUTFOLDER="${BUILD_DIR}/universal" + +sed -i '' "s|path = \.\.\/SharedFrameworks|path = ${FRAMEWORKS_DIR}|g" ./VDS.xcodeproj/project.pbxproj + +# Build device archive +xcodebuild archive \ + -scheme "${C_PROJECT_NAME}" \ + -configuration ${PHONE_CONFIGURATION} \ + -archivePath "${PHONE_ARCHIVE_PATH}" \ + -sdk iphoneos \ + -destination 'generic/platform=iOS' \ + -quiet \ + SKIP_INSTALL=NO \ + ONLY_ACTIVE_ARCH=NO \ + FRAMEWORK_SEARCH_PATHS="$FRAMEWORKS_DIR" \ + ALWAYS_SEARCH_USER_PATHS=true + +# Build simulator archive +xcodebuild archive \ + -scheme "${C_PROJECT_NAME}" \ + -configuration ${SIMULATOR_CONFIGURATION} \ + -archivePath "${SIMULATOR_ARCHIVE_PATH}" \ + -sdk iphonesimulator \ + -destination 'generic/platform=iOS Simulator' \ + -quiet \ + SKIP_INSTALL=NO \ + ONLY_ACTIVE_ARCH=NO \ + FRAMEWORK_SEARCH_PATHS=$FRAMEWORKS_DIR \ + ALWAYS_SEARCH_USER_PATHS=true + +mkdir -p "${UNIVERSAL_OUTPUTFOLDER}" + +# Remove any existing xc framework. +rm -rf ${UNIVERSAL_OUTPUTFOLDER}/${C_PROJECT_NAME}.xcframework + +# Create the xcframework in the universal output folder. +xcodebuild -create-xcframework \ + -framework "${PHONE_ARCHIVE_PATH}/Products/Library/Frameworks/${C_PROJECT_NAME}.framework" \ + -framework "${SIMULATOR_ARCHIVE_PATH}/Products/Library/Frameworks/${C_PROJECT_NAME}.framework" \ + -output ${UNIVERSAL_OUTPUTFOLDER}/${C_PROJECT_NAME}.xcframework + +# Copy the dSYM file as well. +ditto "${PHONE_ARCHIVE_PATH}/dSYMs/${C_PROJECT_NAME}.framework.dSYM" "${UNIVERSAL_OUTPUTFOLDER}/${C_PROJECT_NAME}.xcframework.dSYM" diff --git a/Scripts/download_dependencies.sh b/Scripts/download_dependencies.sh new file mode 100755 index 00000000..ccc388a1 --- /dev/null +++ b/Scripts/download_dependencies.sh @@ -0,0 +1,25 @@ +#!/bin/sh -e + +# download_dependencies.sh +# +# Downloads all compiled framework flavors in from Artifactory. +# + +# Create new aggregate builds + +if [ -z $ARTIFACTORY_URL ]; then + ARTIFACTORY_URL="https://oneartifactoryprod.verizon.com/artifactory" +fi + +BUILD_DIR=$(xcodebuild -showBuildSettings -project ./VDS.xcodeproj | grep -w -o 'BUILD_DIR = .*' | cut -d\ -f3-) +FRAMEWORKS_DIR=$BUILD_DIR/../../../Frameworks/VDS + +if [ ! -d $FRAMEWORKS_DIR ]; then + mkdir -p $FRAMEWORKS_DIR +fi + +./Scripts/download_framework.sh $ARTIFACTORY_URL "$FRAMEWORKS_DIR/VDSColorTokens.xcframework" GVJV_VDS_Maven/@vds-tokens/ios/VDSColorTokens.1.0.6.xcframework.zip + +./Scripts/download_framework.sh $ARTIFACTORY_URL "$FRAMEWORKS_DIR/VDSFormControlsTokens.xcframework" GVJV_VDS_Maven/@vds-tokens/ios/VDSFormControlsTokens.1.0.7.xcframework.zip + +./Scripts/download_framework.sh $ARTIFACTORY_URL "$FRAMEWORKS_DIR/VDSTypographyTokens.xcframework" GVJV_VDS_Maven/@vds-tokens/ios/VDSTypographyTokens.2.0.0.xcframework.zip diff --git a/Scripts/download_framework.sh b/Scripts/download_framework.sh new file mode 100755 index 00000000..791e9ead --- /dev/null +++ b/Scripts/download_framework.sh @@ -0,0 +1,86 @@ +#!/bin/bash -e + +# upload_framework.sh +# +# Downloads an iOS framework from Artificatory. +# +# An API key from Artifcatory is required in the api_key.private file before uploading. +# + +URL=$1 +LOCALPATH="${2}" +REMOTEPATH="${3}" +LOGFILE=$3 +LOCALDIR=$(dirname "${LOCALPATH}") +LOCALBASE=$(basename "${LOCALPATH}") +NEWFILEPATH="${LOCALDIR}"/$(basename "${REMOTEPATH}") +VERSIONFILE=$LOCALDIR/Checksums/"${LOCALBASE}".txt + +if [ -z $URL ]; then + echo "The artifactory instance url must be specified as the first argument!" + exit 1 +fi + +#first argument is error message. +exit_with_error () { +echo "Error: $1" +if [ -f "${NEWFILEPATH}" ]; then + rm -rf "${NEWFILEPATH}" 2 +fi +exit 1 +} + +echo "----------------------------------------------------------" +echo "Logs for ${LOCALBASE}" + +echo -e "Local Target: ${LOCALPATH}" +echo -e "Remote Source: ${REMOTEPATH}" + +if [ -z "$LOCALPATH" ]; then + exit_with_error "Missing local path argument" +fi + +if [ -z "$REMOTEPATH" ]; then + exit_with_error "Missing filename path argument" +fi + +#get local and remote checksums for comparison +echo -e "Getting checksums..." +echo -e "URL: ${URL}/api/storage/${REMOTEPATH}" +JSON=$(curl --header "X-JFrog-Art-Api: ${ARTIFACTORY_APIKEY}" -X GET "${URL}/api/storage/${REMOTEPATH}") +CHECKSUM=$(echo "$JSON" | python3 -c 'import sys, json; print (json.load(sys.stdin)["checksums"]["sha1"])') +if [[ -z "$CHECKSUM" ]]; then + exit_with_error "No Checksum found in json: ${JSON}" +fi +echo "Remote checksum ${CHECKSUM}" +if [[ -f "${VERSIONFILE}" ]]; then + OLDCHECKSUM=$(cat "${VERSIONFILE}") +else + OLDCHECKSUM="none" + mkdir -p $(dirname ${VERSIONFILE}) && touch "$VERSIONFILE" +fi +echo "Local checksum ${OLDCHECKSUM}" + +#get new framework if no original framework, no local checksum, or remote checksum is different from local. +if [ ! -e "${LOCALPATH}" ] || [ -z "$OLDCHECKSUM" ] || [ "$CHECKSUM" != "$OLDCHECKSUM" ]; then + echo "Downloading..." + echo -e "URL: ${URL}/${REMOTEPATH}" + curl --header "X-JFrog-Art-Api: ${ARTIFACTORY_APIKEY}" -f -X GET "$URL/$REMOTEPATH" --output "${NEWFILEPATH}" + if [ $? -eq 0 ] && [ -e "${NEWFILEPATH}" ]; then + echo "Finished Downloading, begin unzip" + unzip -q -o "${NEWFILEPATH}" -d "${LOCALDIR}" + if [ $? -eq 0 ]; then + echo "Finished unzipping, remove zip" + rm -rf "${NEWFILEPATH}" + echo "Writing new checksum to file" + echo "${CHECKSUM}" > "${VERSIONFILE}" + echo "Successfully downloaded and unzipped archive." + else + exit_with_error "Error unzipping" + fi + else + exit_with_error "Failed to download" + fi +else + echo "Successful, No New Version" +fi diff --git a/Scripts/upload_framework.sh b/Scripts/upload_framework.sh new file mode 100755 index 00000000..8793960e --- /dev/null +++ b/Scripts/upload_framework.sh @@ -0,0 +1,86 @@ +#!/bin/bash -e + +# upload_framework.sh +# +# Uploads an iOS framework to Artificatory given the local path as the first argument and the remote project name in Verizon OneArtifactory for the second argument. +# +# An API key from Artifcatory is required in the api_key.private file before uploading. +# +# The script will replace [VER] in the provided remote path with the version found in the framework bundle. +# +# Copied from Hedden, Kyle Matthew on 3/2/18. +# Copyright © 2018 Verizon. All rights reserved. + +URL=$1 +LOCALPATH=$2 +REMOTEPATH=$3 +FRAMEWORKVER=$4 + +if [ -z $URL ]; then + echo "The artifactory instance url must be specified as the first argument!" + exit 1 +fi + +echo ">>> UPLOAD START <<<" +echo "Local path: ${LOCALPATH}" +echo "Remote path: ${REMOTEPATH}" + +cat "${LOCALPATH}/Info.plist" + +LOCALBASE=$(basename "${LOCALPATH}") +LOCALDIR=$(dirname "${LOCALPATH}") + +# Replace the [VER] placeholders with the found version. +REMOTEPATH="${REMOTEPATH//\[VER\]/$FRAMEWORKVER}" +echo -e "Resolved path: \t\t${REMOTEPATH}" + +if [ -z $ARTIFACTORY_APIKEY ]; then + # Read the API key from a private file. + read -r APIKEY < "api_key.private" +else + APIKEY=$ARTIFACTORY_APIKEY +fi + +if [ -z $APIKEY ]; then + read -p "Artifactory API Key:" APIKEY + echo $APIKEY >> api_key.private +fi + +echo -e "API Key: \t\t${APIKEY}" + +# Zip the framework & DSYM for uploading. +pushd $LOCALDIR +echo -e "---------\nZipping: \t\t${LOCALBASE}.zip" +zip -r -X "${LOCALBASE}.zip" $LOCALBASE +# Generate framework's SHA-1 checksum. +CHECKSUM=$(shasum -a 1 "${LOCALBASE}.zip" | cut -d " " -f 1) +echo -e "SHA-1 Checksum: \t${CHECKSUM}" +if [ -e ${LOCALBASE}.dSYM ]; then + echo -e "---------\nZipping: \t\t${LOCALBASE}.dSYM.zip" + zip -r -X "${LOCALBASE}.dSYM.zip" $LOCALBASE.dSYM + # Generate its SHA-1 checksum for dsym. + DSYM_CHECKSUM=$(shasum -a 1 "${LOCALBASE}.dSYM.zip" | cut -d " " -f 1) + echo -e "SHA-1 Checksum: \t${DSYM_CHECKSUM}" +fi +popd +mv ${LOCALPATH}.zip . +if [ -e ${LOCALPATH}.dSYM.zip ]; then + mv ${LOCALPATH}.dSYM.zip . +fi + +# Upload framework to Artifactory. +echo -e "---------\nUploading to: \t\t${URL}/${REMOTEPATH}.zip" +curl --header "X-JFrog-Art-Api: ${APIKEY}" --header "X-Checksum-Sha1: ${CHECKSUM}" -X PUT "${URL}/${REMOTEPATH}.zip" -T "${LOCALBASE}.zip" + +# Cleanup. +rm "${LOCALBASE}.zip" + +if [ -e ${LOCALBASE}.dSYM.zip ]; then + # Upload dsym Artifactory. + echo -e "---------\nUploading to: \t\t${URL}/${REMOTEPATH}.dSYM.zip" + curl --header "X-JFrog-Art-Api: ${APIKEY}" --header "X-Checksum-Sha1: ${DSYM_CHECKSUM}" -X PUT "${URL}/${REMOTEPATH}.dSYM.zip" -T "${LOCALBASE}.dSYM.zip" + # Cleanup dsym. + rm ${LOCALBASE}.dSYM.zip +fi + +echo -e "\n\n<<< UPLOAD COMPLETE >>>\n\n" diff --git a/Scripts/upload_vds_frameworks.sh b/Scripts/upload_vds_frameworks.sh new file mode 100755 index 00000000..bc89f5c6 --- /dev/null +++ b/Scripts/upload_vds_frameworks.sh @@ -0,0 +1,25 @@ +#!/bin/sh -e + +# upload_core_ui_frameworks.sh +# +# Uploads all compiled framework flavors in MVMCoreUI to Artifactory with the SNAPSHOT classifier. This is to avoid accidently clobbering a release build of a particular version. Remove the classifier in Artificatory to make a release. +# +# Copied from Hedden, Kyle Matthew on 3/2/18. +# + +BUILD_SETTINGS=$(xcodebuild -showBuildSettings -project ../VDS.xcodeproj) +FRAMEWORK_VERSION=$(echo "$BUILD_SETTINGS" | grep -w -o 'MARKETING_VERSION = .*' | cut -d\ -f3-) +if [ $(git tag --list | grep "v${FRAMEWORK_VERSION}") ]; then + echo This version tag has already been committed! Aborting! + exit 1 +fi + +# Create new aggregate builds + +if [ -z $ARTIFACTORY_URL ]; then + ARTIFACTORY_URL="https://oneartifactoryprod.verizon.com/artifactory" +fi + +# Upload +BUILD_DIR=$(echo "$BUILD_SETTINGS" | grep -w -o 'BUILD_DIR = .*' | cut -d\ -f3-) +./upload_framework.sh $ARTIFACTORY_URL "${BUILD_DIR}/universal/VDS.xcframework" BPHV_MobileFirst_IOS/com/vzw/hss/myverizon/VDS/[VER]/VDS-[VER]-Debug-SNAPSHOT $FRAMEWORK_VERSION diff --git a/VDS.xcodeproj/project.pbxproj b/VDS.xcodeproj/project.pbxproj index 54e81d29..ba4fe7f6 100644 --- a/VDS.xcodeproj/project.pbxproj +++ b/VDS.xcodeproj/project.pbxproj @@ -9,18 +9,12 @@ /* Begin PBXBuildFile section */ 5F21D7BF28DCEB3D003E7CD6 /* Useable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F21D7BE28DCEB3D003E7CD6 /* Useable.swift */; }; 5FC35BE328D51405004EBEAC /* Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FC35BE228D51405004EBEAC /* Button.swift */; }; - 5FC35BE528D51414004EBEAC /* ButtonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FC35BE428D51413004EBEAC /* ButtonModel.swift */; }; - EA1F265D28B944F00033E859 /* CollectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA1F265B28B944F00033E859 /* CollectionView.swift */; }; - EA1F265E28B944F00033E859 /* CollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA1F265C28B944F00033E859 /* CollectionViewCell.swift */; }; - EA1F266428B945070033E859 /* RadioSwatchGroupModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA1F266028B945070033E859 /* RadioSwatchGroupModel.swift */; }; EA1F266528B945070033E859 /* RadioSwatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA1F266128B945070033E859 /* RadioSwatch.swift */; }; EA1F266628B945070033E859 /* RadioSwatchGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA1F266228B945070033E859 /* RadioSwatchGroup.swift */; }; - EA1F266728B945070033E859 /* RadioSwatchModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA1F266328B945070033E859 /* RadioSwatchModel.swift */; }; EA336171288B19200071C351 /* VDS.docc in Sources */ = {isa = PBXBuildFile; fileRef = EA336170288B19200071C351 /* VDS.docc */; }; EA336177288B19210071C351 /* VDS.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA33616C288B19200071C351 /* VDS.framework */; }; EA33617C288B19210071C351 /* VDSTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA33617B288B19210071C351 /* VDSTests.swift */; }; EA33617D288B19210071C351 /* VDS.h in Headers */ = {isa = PBXBuildFile; fileRef = EA33616F288B19200071C351 /* VDS.h */; settings = {ATTRIBUTES = (Public, ); }; }; - EA3361A2288B1E840071C351 /* ToggleModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3361A1288B1E840071C351 /* ToggleModel.swift */; }; EA3361A8288B23300071C351 /* UIColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3361A7288B23300071C351 /* UIColor.swift */; }; EA3361AA288B25E40071C351 /* Disabling.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3361A9288B25E40071C351 /* Disabling.swift */; }; EA3361AD288B26190071C351 /* DataTrackable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3361AC288B26190071C351 /* DataTrackable.swift */; }; @@ -28,7 +22,7 @@ EA3361B6288B2A410071C351 /* Control.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3361B5288B2A410071C351 /* Control.swift */; }; EA3361B8288B2AAA0071C351 /* ViewProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3361B7288B2AAA0071C351 /* ViewProtocol.swift */; }; EA3361BD288B2C760071C351 /* TypeAlias.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3361BC288B2C760071C351 /* TypeAlias.swift */; }; - EA3361BF288B2EA60071C351 /* ModelHandlerable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3361BE288B2EA60071C351 /* ModelHandlerable.swift */; }; + EA3361BF288B2EA60071C351 /* Handlerable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3361BE288B2EA60071C351 /* Handlerable.swift */; }; EA3361C328902D960071C351 /* Toggle.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3361C228902D960071C351 /* Toggle.swift */; }; EA3361C5289030FC0071C351 /* Accessable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3361C4289030FC0071C351 /* Accessable.swift */; }; EA3361C9289054C50071C351 /* Surfaceable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3361C8289054C50071C351 /* Surfaceable.swift */; }; @@ -41,45 +35,34 @@ EA3362302891EB4A0071C351 /* Fonts.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA33622F2891EB4A0071C351 /* Fonts.swift */; }; EA33623E2892EE950071C351 /* UIDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA33623D2892EE950071C351 /* UIDevice.swift */; }; EA3362402892EF6C0071C351 /* Label.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA33623F2892EF6B0071C351 /* Label.swift */; }; - EA3362432892EFF20071C351 /* LabelModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3362422892EFF20071C351 /* LabelModel.swift */; }; - EA3362452892F9130071C351 /* Labelable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3362442892F9130071C351 /* Labelable.swift */; }; EA33624728931B050071C351 /* Initable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA33624628931B050071C351 /* Initable.swift */; }; - EA3C3B4C2894823E000CA526 /* AnyProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA3C3B4B2894823E000CA526 /* AnyProxy.swift */; }; EA4DB18528CA967F00103EE3 /* SelectorGroupHandlerBase.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA4DB18428CA967F00103EE3 /* SelectorGroupHandlerBase.swift */; }; EA4DB2FD28D3D0CA00103EE3 /* AnyEquatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA4DB2FC28D3D0CA00103EE3 /* AnyEquatable.swift */; }; - EA4DB30028DCBC9900103EE3 /* BadgeModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA4DB2FF28DCBC9900103EE3 /* BadgeModel.swift */; }; EA4DB30228DCBCA500103EE3 /* Badge.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA4DB30128DCBCA500103EE3 /* Badge.swift */; }; - EA84F6B128B94A2500D67ABC /* CodableColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA84F6B028B94A2500D67ABC /* CodableColor.swift */; }; EA89200228AECF2A006B9984 /* UIButton+Publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA89200128AECF2A006B9984 /* UIButton+Publisher.swift */; }; EA89200428AECF4B006B9984 /* UITextField+Publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA89200328AECF4B006B9984 /* UITextField+Publisher.swift */; }; EA89200628B526D6006B9984 /* CheckboxGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA89200528B526D6006B9984 /* CheckboxGroup.swift */; }; - EA89200828B526E0006B9984 /* CheckboxGroupModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA89200728B526E0006B9984 /* CheckboxGroupModel.swift */; }; - EA89200D28B530FD006B9984 /* RadioBoxModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA89200C28B530FD006B9984 /* RadioBoxModel.swift */; }; - EA89200F28B53921006B9984 /* Selectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA89200E28B53921006B9984 /* Selectable.swift */; }; EA89201328B568D8006B9984 /* RadioBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA89201228B568D8006B9984 /* RadioBox.swift */; }; EA89201528B56CF4006B9984 /* RadioBoxGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA89201428B56CF4006B9984 /* RadioBoxGroup.swift */; }; - EA89201728B56CFF006B9984 /* RadioBoxGroupModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA89201628B56CFF006B9984 /* RadioBoxGroupModel.swift */; }; - EAA5EEA928EB3ED0003B3210 /* EntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA5EEA828EB3ED0003B3210 /* EntryField.swift */; }; - EAA5EEAB28EB3ED9003B3210 /* EntryFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA5EEAA28EB3ED9003B3210 /* EntryFieldModel.swift */; }; - EAA5EEB128EB6A5A003B3210 /* TextEntryFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA5EEB028EB6A5A003B3210 /* TextEntryFieldModel.swift */; }; - EAA5EEB328EB6A66003B3210 /* TextEntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA5EEB228EB6A66003B3210 /* TextEntryField.swift */; }; EAA5EEB528ECBFB4003B3210 /* ImageLabelAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA5EEB428ECBFB4003B3210 /* ImageLabelAttribute.swift */; }; EAA5EEB728ECC03A003B3210 /* ToolTipLabelAttribute.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA5EEB628ECC03A003B3210 /* ToolTipLabelAttribute.swift */; }; EAA5EEB928ECD24B003B3210 /* Icons.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = EAA5EEB828ECD24B003B3210 /* Icons.xcassets */; }; - EAA5EEE028F49DB3003B3210 /* Colorable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA5EEDF28F49DB3003B3210 /* Colorable.swift */; }; EAA5EEE428F5B855003B3210 /* VerizonNHGDS-Light.otf in Resources */ = {isa = PBXBuildFile; fileRef = EAA5EEE328F5B855003B3210 /* VerizonNHGDS-Light.otf */; }; - EAB1D29A28A5611D00DAE764 /* SelectorGroupModelable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D29928A5611D00DAE764 /* SelectorGroupModelable.swift */; }; + EAA5EEEF28F5C908003B3210 /* VDSTypographyTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EAA5EEEC28F5C908003B3210 /* VDSTypographyTokens.xcframework */; }; + EAA5EEF128F5C909003B3210 /* VDSColorTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EAA5EEED28F5C908003B3210 /* VDSColorTokens.xcframework */; }; + EAA5EEF328F5C909003B3210 /* VDSFormControlsTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EAA5EEEE28F5C908003B3210 /* VDSFormControlsTokens.xcframework */; }; EAB1D29C28A5618900DAE764 /* RadioButtonGroup.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D29B28A5618900DAE764 /* RadioButtonGroup.swift */; }; - EAB1D29E28A5619500DAE764 /* RadioButtonGroupModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D29D28A5619500DAE764 /* RadioButtonGroupModel.swift */; }; - EAB1D2A128A598FE00DAE764 /* UsesAutoLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D2A028A598FE00DAE764 /* UsesAutoLayout.swift */; }; - EAB1D2A328A5994800DAE764 /* Debuggable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D2A228A5994800DAE764 /* Debuggable.swift */; }; EAB1D2CD28ABE76100DAE764 /* Withable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D2CC28ABE76000DAE764 /* Withable.swift */; }; EAB1D2CF28ABEF2B00DAE764 /* Typography.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D2CE28ABEF2B00DAE764 /* Typography.swift */; }; EAB1D2E628AE842000DAE764 /* Publisher+Bind.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D2E328AE842000DAE764 /* Publisher+Bind.swift */; }; EAB1D2EA28AE84AA00DAE764 /* UIControlPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAB1D2E928AE84AA00DAE764 /* UIControlPublisher.swift */; }; + EAC925842911C63100091998 /* Colorable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAA5EEDF28F49DB3003B3210 /* Colorable.swift */; }; + EAC9258C2911C9DE00091998 /* TextEntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC925872911C9DE00091998 /* TextEntryField.swift */; }; + EAC9258D2911C9DE00091998 /* TextEntryFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC925882911C9DE00091998 /* TextEntryFieldModel.swift */; }; + EAC9258E2911C9DE00091998 /* EntryFieldModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC9258A2911C9DE00091998 /* EntryFieldModel.swift */; }; + EAC9258F2911C9DE00091998 /* EntryField.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAC9258B2911C9DE00091998 /* EntryField.swift */; }; EAD8D2C128BFDE8B006EB6A6 /* UIGestureRecognizer+Publisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAD8D2C028BFDE8B006EB6A6 /* UIGestureRecognizer+Publisher.swift */; }; EAF7F0952899861000B287F5 /* Checkbox.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F0932899861000B287F5 /* Checkbox.swift */; }; - EAF7F0962899861000B287F5 /* CheckboxModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F0942899861000B287F5 /* CheckboxModel.swift */; }; EAF7F09A2899B17200B287F5 /* CATransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F0992899B17200B287F5 /* CATransaction.swift */; }; EAF7F09E289AAEC000B287F5 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F09D289AAEC000B287F5 /* Constants.swift */; }; EAF7F0A0289AB7EC00B287F5 /* View.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F09F289AB7EC00B287F5 /* View.swift */; }; @@ -94,12 +77,7 @@ EAF7F0B5289C126F00B287F5 /* UILabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F0B4289C126F00B287F5 /* UILabel.swift */; }; EAF7F0B7289C12A600B287F5 /* UITapGestureRecognizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F0B6289C12A600B287F5 /* UITapGestureRecognizer.swift */; }; EAF7F0B9289C139800B287F5 /* ColorConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F0B8289C139800B287F5 /* ColorConfiguration.swift */; }; - EAF7F0BB289D80ED00B287F5 /* Modelable.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F0BA289D80ED00B287F5 /* Modelable.swift */; }; - EAF7F0F8289DB1AC00B287F5 /* VDSFormControlsTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EAF7F0E8289DB0DA00B287F5 /* VDSFormControlsTokens.xcframework */; }; - EAF7F0F9289DB1AC00B287F5 /* VDSTypographyTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EA3361F32891D5290071C351 /* VDSTypographyTokens.xcframework */; }; - EAF7F0FB289DB1AC00B287F5 /* VDSColorTokens.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = EAF7F0E9289DB0DA00B287F5 /* VDSColorTokens.xcframework */; }; EAF7F11728A1475A00B287F5 /* RadioButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F11528A1475A00B287F5 /* RadioButton.swift */; }; - EAF7F11828A1475A00B287F5 /* RadioButtonModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F11628A1475A00B287F5 /* RadioButtonModel.swift */; }; EAF7F13328A2A16500B287F5 /* AttachmentLabelAttributeModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EAF7F13228A2A16500B287F5 /* AttachmentLabelAttributeModel.swift */; }; /* End PBXBuildFile section */ @@ -116,21 +94,13 @@ /* Begin PBXFileReference section */ 5F21D7BE28DCEB3D003E7CD6 /* Useable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Useable.swift; sourceTree = ""; }; 5FC35BE228D51405004EBEAC /* Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = ""; }; - 5FC35BE428D51413004EBEAC /* ButtonModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ButtonModel.swift; sourceTree = ""; }; - EA1F265B28B944F00033E859 /* CollectionView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionView.swift; sourceTree = ""; }; - EA1F265C28B944F00033E859 /* CollectionViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CollectionViewCell.swift; sourceTree = ""; }; - EA1F266028B945070033E859 /* RadioSwatchGroupModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadioSwatchGroupModel.swift; sourceTree = ""; }; EA1F266128B945070033E859 /* RadioSwatch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadioSwatch.swift; sourceTree = ""; }; EA1F266228B945070033E859 /* RadioSwatchGroup.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadioSwatchGroup.swift; sourceTree = ""; }; - EA1F266328B945070033E859 /* RadioSwatchModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadioSwatchModel.swift; sourceTree = ""; }; EA33616C288B19200071C351 /* VDS.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = VDS.framework; sourceTree = BUILT_PRODUCTS_DIR; }; EA33616F288B19200071C351 /* VDS.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VDS.h; sourceTree = ""; }; EA336170288B19200071C351 /* VDS.docc */ = {isa = PBXFileReference; lastKnownFileType = folder.documentationcatalog; path = VDS.docc; sourceTree = ""; }; EA336176288B19210071C351 /* VDSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = VDSTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; EA33617B288B19210071C351 /* VDSTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VDSTests.swift; sourceTree = ""; }; - EA33618E288B1C0C0071C351 /* VDSColorTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSColorTokens.xcframework; path = "/Users/mattbruce/Documents/Projects/iPhone/Frameworks/MVA-JSONCreator/JSONCreator_iOS/../SharedFrameworks/VDSColorTokens.xcframework"; sourceTree = ""; }; - EA33618F288B1C0C0071C351 /* VDSFormControlsTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSFormControlsTokens.xcframework; path = "/Users/mattbruce/Documents/Projects/iPhone/Frameworks/MVA-JSONCreator/JSONCreator_iOS/../SharedFrameworks/VDSFormControlsTokens.xcframework"; sourceTree = ""; }; - EA3361A1288B1E840071C351 /* ToggleModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToggleModel.swift; sourceTree = ""; }; EA3361A7288B23300071C351 /* UIColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIColor.swift; sourceTree = ""; }; EA3361A9288B25E40071C351 /* Disabling.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Disabling.swift; sourceTree = ""; }; EA3361AC288B26190071C351 /* DataTrackable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataTrackable.swift; sourceTree = ""; }; @@ -138,11 +108,10 @@ EA3361B5288B2A410071C351 /* Control.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Control.swift; sourceTree = ""; }; EA3361B7288B2AAA0071C351 /* ViewProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewProtocol.swift; sourceTree = ""; }; EA3361BC288B2C760071C351 /* TypeAlias.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypeAlias.swift; sourceTree = ""; }; - EA3361BE288B2EA60071C351 /* ModelHandlerable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModelHandlerable.swift; sourceTree = ""; }; + EA3361BE288B2EA60071C351 /* Handlerable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Handlerable.swift; sourceTree = ""; }; EA3361C228902D960071C351 /* Toggle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Toggle.swift; sourceTree = ""; }; EA3361C4289030FC0071C351 /* Accessable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Accessable.swift; sourceTree = ""; }; EA3361C8289054C50071C351 /* Surfaceable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Surfaceable.swift; sourceTree = ""; }; - EA3361F32891D5290071C351 /* VDSTypographyTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSTypographyTokens.xcframework; path = ../SharedFrameworks/VDSTypographyTokens.xcframework; sourceTree = ""; }; EA3362002891E14C0071C351 /* VerizonNHGeTX-Bold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "VerizonNHGeTX-Bold.otf"; sourceTree = ""; }; EA3362012891E14D0071C351 /* VerizonNHGeDS-Bold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "VerizonNHGeDS-Bold.otf"; sourceTree = ""; }; EA3362022891E14D0071C351 /* VerizonNHGeTX-Regular.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "VerizonNHGeTX-Regular.otf"; sourceTree = ""; }; @@ -152,45 +121,34 @@ EA33622F2891EB4A0071C351 /* Fonts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Fonts.swift; sourceTree = ""; }; EA33623D2892EE950071C351 /* UIDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIDevice.swift; sourceTree = ""; }; EA33623F2892EF6B0071C351 /* Label.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Label.swift; sourceTree = ""; }; - EA3362422892EFF20071C351 /* LabelModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelModel.swift; sourceTree = ""; }; - EA3362442892F9130071C351 /* Labelable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Labelable.swift; sourceTree = ""; }; EA33624628931B050071C351 /* Initable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Initable.swift; sourceTree = ""; }; - EA3C3B4B2894823E000CA526 /* AnyProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyProxy.swift; sourceTree = ""; }; EA4DB18428CA967F00103EE3 /* SelectorGroupHandlerBase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectorGroupHandlerBase.swift; sourceTree = ""; }; EA4DB2FC28D3D0CA00103EE3 /* AnyEquatable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnyEquatable.swift; sourceTree = ""; }; - EA4DB2FF28DCBC9900103EE3 /* BadgeModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BadgeModel.swift; sourceTree = ""; }; EA4DB30128DCBCA500103EE3 /* Badge.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Badge.swift; sourceTree = ""; }; - EA84F6B028B94A2500D67ABC /* CodableColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodableColor.swift; sourceTree = ""; }; EA89200128AECF2A006B9984 /* UIButton+Publisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIButton+Publisher.swift"; sourceTree = ""; }; EA89200328AECF4B006B9984 /* UITextField+Publisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITextField+Publisher.swift"; sourceTree = ""; }; EA89200528B526D6006B9984 /* CheckboxGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxGroup.swift; sourceTree = ""; }; - EA89200728B526E0006B9984 /* CheckboxGroupModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckboxGroupModel.swift; sourceTree = ""; }; - EA89200C28B530FD006B9984 /* RadioBoxModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioBoxModel.swift; sourceTree = ""; }; - EA89200E28B53921006B9984 /* Selectable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Selectable.swift; sourceTree = ""; }; EA89201228B568D8006B9984 /* RadioBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioBox.swift; sourceTree = ""; }; EA89201428B56CF4006B9984 /* RadioBoxGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioBoxGroup.swift; sourceTree = ""; }; - EA89201628B56CFF006B9984 /* RadioBoxGroupModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioBoxGroupModel.swift; sourceTree = ""; }; - EAA5EEA828EB3ED0003B3210 /* EntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntryField.swift; sourceTree = ""; }; - EAA5EEAA28EB3ED9003B3210 /* EntryFieldModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EntryFieldModel.swift; sourceTree = ""; }; - EAA5EEB028EB6A5A003B3210 /* TextEntryFieldModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextEntryFieldModel.swift; sourceTree = ""; }; - EAA5EEB228EB6A66003B3210 /* TextEntryField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextEntryField.swift; sourceTree = ""; }; EAA5EEB428ECBFB4003B3210 /* ImageLabelAttribute.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageLabelAttribute.swift; sourceTree = ""; }; EAA5EEB628ECC03A003B3210 /* ToolTipLabelAttribute.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToolTipLabelAttribute.swift; sourceTree = ""; }; EAA5EEB828ECD24B003B3210 /* Icons.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Icons.xcassets; sourceTree = ""; }; EAA5EEDF28F49DB3003B3210 /* Colorable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Colorable.swift; sourceTree = ""; }; EAA5EEE328F5B855003B3210 /* VerizonNHGDS-Light.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "VerizonNHGDS-Light.otf"; sourceTree = ""; }; - EAB1D29928A5611D00DAE764 /* SelectorGroupModelable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectorGroupModelable.swift; sourceTree = ""; }; + EAA5EEEC28F5C908003B3210 /* VDSTypographyTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSTypographyTokens.xcframework; path = ../SharedFrameworks/VDSTypographyTokens.xcframework; sourceTree = ""; }; + EAA5EEED28F5C908003B3210 /* VDSColorTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSColorTokens.xcframework; path = ../SharedFrameworks/VDSColorTokens.xcframework; sourceTree = ""; }; + EAA5EEEE28F5C908003B3210 /* VDSFormControlsTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSFormControlsTokens.xcframework; path = ../SharedFrameworks/VDSFormControlsTokens.xcframework; sourceTree = ""; }; EAB1D29B28A5618900DAE764 /* RadioButtonGroup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioButtonGroup.swift; sourceTree = ""; }; - EAB1D29D28A5619500DAE764 /* RadioButtonGroupModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RadioButtonGroupModel.swift; sourceTree = ""; }; - EAB1D2A028A598FE00DAE764 /* UsesAutoLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UsesAutoLayout.swift; sourceTree = ""; }; - EAB1D2A228A5994800DAE764 /* Debuggable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Debuggable.swift; sourceTree = ""; }; EAB1D2CC28ABE76000DAE764 /* Withable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Withable.swift; sourceTree = ""; }; EAB1D2CE28ABEF2B00DAE764 /* Typography.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Typography.swift; sourceTree = ""; }; EAB1D2E328AE842000DAE764 /* Publisher+Bind.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Publisher+Bind.swift"; sourceTree = ""; }; EAB1D2E928AE84AA00DAE764 /* UIControlPublisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIControlPublisher.swift; sourceTree = ""; }; + EAC925872911C9DE00091998 /* TextEntryField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextEntryField.swift; sourceTree = ""; }; + EAC925882911C9DE00091998 /* TextEntryFieldModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TextEntryFieldModel.swift; sourceTree = ""; }; + EAC9258A2911C9DE00091998 /* EntryFieldModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EntryFieldModel.swift; sourceTree = ""; }; + EAC9258B2911C9DE00091998 /* EntryField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EntryField.swift; sourceTree = ""; }; EAD8D2C028BFDE8B006EB6A6 /* UIGestureRecognizer+Publisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIGestureRecognizer+Publisher.swift"; sourceTree = ""; }; EAF7F0932899861000B287F5 /* Checkbox.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Checkbox.swift; sourceTree = ""; }; - EAF7F0942899861000B287F5 /* CheckboxModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CheckboxModel.swift; sourceTree = ""; }; EAF7F0992899B17200B287F5 /* CATransaction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CATransaction.swift; sourceTree = ""; }; EAF7F09D289AAEC000B287F5 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; EAF7F09F289AB7EC00B287F5 /* View.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = View.swift; sourceTree = ""; }; @@ -205,13 +163,7 @@ EAF7F0B4289C126F00B287F5 /* UILabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UILabel.swift; sourceTree = ""; }; EAF7F0B6289C12A600B287F5 /* UITapGestureRecognizer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITapGestureRecognizer.swift; sourceTree = ""; }; EAF7F0B8289C139800B287F5 /* ColorConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorConfiguration.swift; sourceTree = ""; }; - EAF7F0BA289D80ED00B287F5 /* Modelable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Modelable.swift; sourceTree = ""; }; - EAF7F0E8289DB0DA00B287F5 /* VDSFormControlsTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSFormControlsTokens.xcframework; path = ../SharedFrameworks/VDSFormControlsTokens.xcframework; sourceTree = ""; }; - EAF7F0E9289DB0DA00B287F5 /* VDSColorTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSColorTokens.xcframework; path = ../SharedFrameworks/VDSColorTokens.xcframework; sourceTree = ""; }; - EAF7F0EA289DB0DA00B287F5 /* VDSAccessibilityTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSAccessibilityTokens.xcframework; path = ../SharedFrameworks/VDSAccessibilityTokens.xcframework; sourceTree = ""; }; - EAF7F0EB289DB0DA00B287F5 /* VDSLayoutTokens.xcframework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcframework; name = VDSLayoutTokens.xcframework; path = ../SharedFrameworks/VDSLayoutTokens.xcframework; sourceTree = ""; }; EAF7F11528A1475A00B287F5 /* RadioButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadioButton.swift; sourceTree = ""; }; - EAF7F11628A1475A00B287F5 /* RadioButtonModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RadioButtonModel.swift; sourceTree = ""; }; EAF7F13228A2A16500B287F5 /* AttachmentLabelAttributeModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachmentLabelAttributeModel.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -220,9 +172,9 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - EAF7F0F8289DB1AC00B287F5 /* VDSFormControlsTokens.xcframework in Frameworks */, - EAF7F0F9289DB1AC00B287F5 /* VDSTypographyTokens.xcframework in Frameworks */, - EAF7F0FB289DB1AC00B287F5 /* VDSColorTokens.xcframework in Frameworks */, + EAA5EEF328F5C909003B3210 /* VDSFormControlsTokens.xcframework in Frameworks */, + EAA5EEEF28F5C908003B3210 /* VDSTypographyTokens.xcframework in Frameworks */, + EAA5EEF128F5C909003B3210 /* VDSColorTokens.xcframework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -241,7 +193,6 @@ isa = PBXGroup; children = ( 5FC35BE228D51405004EBEAC /* Button.swift */, - 5FC35BE428D51413004EBEAC /* ButtonModel.swift */, ); path = Button; sourceTree = ""; @@ -250,9 +201,7 @@ isa = PBXGroup; children = ( EA1F266128B945070033E859 /* RadioSwatch.swift */, - EA1F266328B945070033E859 /* RadioSwatchModel.swift */, EA1F266228B945070033E859 /* RadioSwatchGroup.swift */, - EA1F266028B945070033E859 /* RadioSwatchGroupModel.swift */, ); path = RadioSwatch; sourceTree = ""; @@ -284,7 +233,6 @@ EA3361A6288B23240071C351 /* Extensions */, EA3361DF2891D0F10071C351 /* Fonts */, EA3361AB288B25EC0071C351 /* Protocols */, - EAB1D29F28A598D000DAE764 /* PropertyWrappers */, EAB1D2E228AE842000DAE764 /* Publishers */, EAB1D2D028ABEF3100DAE764 /* Typography */, EA3361B9288B2BE30071C351 /* Utilities */, @@ -306,13 +254,9 @@ EA33618D288B1C0C0071C351 /* Frameworks */ = { isa = PBXGroup; children = ( - EAF7F0EA289DB0DA00B287F5 /* VDSAccessibilityTokens.xcframework */, - EAF7F0E9289DB0DA00B287F5 /* VDSColorTokens.xcframework */, - EAF7F0E8289DB0DA00B287F5 /* VDSFormControlsTokens.xcframework */, - EAF7F0EB289DB0DA00B287F5 /* VDSLayoutTokens.xcframework */, - EA3361F32891D5290071C351 /* VDSTypographyTokens.xcframework */, - EA33618E288B1C0C0071C351 /* VDSColorTokens.xcframework */, - EA33618F288B1C0C0071C351 /* VDSFormControlsTokens.xcframework */, + EAA5EEED28F5C908003B3210 /* VDSColorTokens.xcframework */, + EAA5EEEE28F5C908003B3210 /* VDSFormControlsTokens.xcframework */, + EAA5EEEC28F5C908003B3210 /* VDSTypographyTokens.xcframework */, ); name = Frameworks; sourceTree = ""; @@ -327,7 +271,7 @@ EA89200B28B530F0006B9984 /* RadioBox */, EAF7F11428A1470D00B287F5 /* RadioButton */, EA1F265F28B945070033E859 /* RadioSwatch */, - EAA5EEA728EB3EBC003B3210 /* TextFields */, + EAC925852911C9DE00091998 /* TextFields */, EA3361A0288B1E6F0071C351 /* Toggle */, ); path = Components; @@ -337,7 +281,6 @@ isa = PBXGroup; children = ( EA3361C228902D960071C351 /* Toggle.swift */, - EA3361A1288B1E840071C351 /* ToggleModel.swift */, ); path = Toggle; sourceTree = ""; @@ -365,13 +308,9 @@ EA3361A9288B25E40071C351 /* Disabling.swift */, EAF7F0A1289AFB3900B287F5 /* Errorable.swift */, EA3361AE288B26310071C351 /* FormFieldable.swift */, + EA3361BE288B2EA60071C351 /* Handlerable.swift */, EA33624628931B050071C351 /* Initable.swift */, - EA3362442892F9130071C351 /* Labelable.swift */, - EAF7F0BA289D80ED00B287F5 /* Modelable.swift */, - EA3361BE288B2EA60071C351 /* ModelHandlerable.swift */, EAF7F0A5289B0CE000B287F5 /* Resetable.swift */, - EA89200E28B53921006B9984 /* Selectable.swift */, - EAB1D29928A5611D00DAE764 /* SelectorGroupModelable.swift */, EA3361C8289054C50071C351 /* Surfaceable.swift */, EA3361B7288B2AAA0071C351 /* ViewProtocol.swift */, EAB1D2CC28ABE76000DAE764 /* Withable.swift */, @@ -383,8 +322,6 @@ EA3361B4288B2A360071C351 /* Classes */ = { isa = PBXGroup; children = ( - EA1F265B28B944F00033E859 /* CollectionView.swift */, - EA1F265C28B944F00033E859 /* CollectionViewCell.swift */, EAF7F0B8289C139800B287F5 /* ColorConfiguration.swift */, EAF7F09D289AAEC000B287F5 /* Constants.swift */, EA3361B5288B2A410071C351 /* Control.swift */, @@ -436,7 +373,6 @@ isa = PBXGroup; children = ( EA33623F2892EF6B0071C351 /* Label.swift */, - EA3362422892EFF20071C351 /* LabelModel.swift */, EAF7F0A9289B13EF00B287F5 /* Attributes */, ); path = Label; @@ -446,7 +382,6 @@ isa = PBXGroup; children = ( EA4DB30128DCBCA500103EE3 /* Badge.swift */, - EA4DB2FF28DCBC9900103EE3 /* BadgeModel.swift */, ); path = Badge; sourceTree = ""; @@ -455,51 +390,11 @@ isa = PBXGroup; children = ( EA89201228B568D8006B9984 /* RadioBox.swift */, - EA89200C28B530FD006B9984 /* RadioBoxModel.swift */, EA89201428B56CF4006B9984 /* RadioBoxGroup.swift */, - EA89201628B56CFF006B9984 /* RadioBoxGroupModel.swift */, ); path = RadioBox; sourceTree = ""; }; - EAA5EEA728EB3EBC003B3210 /* TextFields */ = { - isa = PBXGroup; - children = ( - EAA5EEAF28EB6A49003B3210 /* TextEntryField */, - EAA5EEAE28EB6A3D003B3210 /* EntryField */, - ); - path = TextFields; - sourceTree = ""; - }; - EAA5EEAE28EB6A3D003B3210 /* EntryField */ = { - isa = PBXGroup; - children = ( - EAA5EEA828EB3ED0003B3210 /* EntryField.swift */, - EAA5EEAA28EB3ED9003B3210 /* EntryFieldModel.swift */, - ); - path = EntryField; - sourceTree = ""; - }; - EAA5EEAF28EB6A49003B3210 /* TextEntryField */ = { - isa = PBXGroup; - children = ( - EAA5EEB228EB6A66003B3210 /* TextEntryField.swift */, - EAA5EEB028EB6A5A003B3210 /* TextEntryFieldModel.swift */, - ); - path = TextEntryField; - sourceTree = ""; - }; - EAB1D29F28A598D000DAE764 /* PropertyWrappers */ = { - isa = PBXGroup; - children = ( - EA3C3B4B2894823E000CA526 /* AnyProxy.swift */, - EA84F6B028B94A2500D67ABC /* CodableColor.swift */, - EAB1D2A228A5994800DAE764 /* Debuggable.swift */, - EAB1D2A028A598FE00DAE764 /* UsesAutoLayout.swift */, - ); - path = PropertyWrappers; - sourceTree = ""; - }; EAB1D2D028ABEF3100DAE764 /* Typography */ = { isa = PBXGroup; children = ( @@ -520,13 +415,38 @@ path = Publishers; sourceTree = ""; }; + EAC925852911C9DE00091998 /* TextFields */ = { + isa = PBXGroup; + children = ( + EAC925862911C9DE00091998 /* TextEntryField */, + EAC925892911C9DE00091998 /* EntryField */, + ); + path = TextFields; + sourceTree = ""; + }; + EAC925862911C9DE00091998 /* TextEntryField */ = { + isa = PBXGroup; + children = ( + EAC925872911C9DE00091998 /* TextEntryField.swift */, + EAC925882911C9DE00091998 /* TextEntryFieldModel.swift */, + ); + path = TextEntryField; + sourceTree = ""; + }; + EAC925892911C9DE00091998 /* EntryField */ = { + isa = PBXGroup; + children = ( + EAC9258A2911C9DE00091998 /* EntryFieldModel.swift */, + EAC9258B2911C9DE00091998 /* EntryField.swift */, + ); + path = EntryField; + sourceTree = ""; + }; EAF7F092289985E200B287F5 /* Checkbox */ = { isa = PBXGroup; children = ( EAF7F0932899861000B287F5 /* Checkbox.swift */, - EAF7F0942899861000B287F5 /* CheckboxModel.swift */, EA89200528B526D6006B9984 /* CheckboxGroup.swift */, - EA89200728B526E0006B9984 /* CheckboxGroupModel.swift */, ); path = Checkbox; sourceTree = ""; @@ -539,10 +459,10 @@ EAF7F13228A2A16500B287F5 /* AttachmentLabelAttributeModel.swift */, EAF7F0B0289B177F00B287F5 /* ColorLabelAttribute.swift */, EAF7F0AA289B13FD00B287F5 /* FontLabelAttribute.swift */, - EAF7F0AC289B142900B287F5 /* StrikeThroughLabelAttribute.swift */, EAA5EEB428ECBFB4003B3210 /* ImageLabelAttribute.swift */, - EAF7F0AE289B144C00B287F5 /* UnderlineLabelAttribute.swift */, + EAF7F0AC289B142900B287F5 /* StrikeThroughLabelAttribute.swift */, EAA5EEB628ECC03A003B3210 /* ToolTipLabelAttribute.swift */, + EAF7F0AE289B144C00B287F5 /* UnderlineLabelAttribute.swift */, ); path = Attributes; sourceTree = ""; @@ -551,9 +471,7 @@ isa = PBXGroup; children = ( EAF7F11528A1475A00B287F5 /* RadioButton.swift */, - EAF7F11628A1475A00B287F5 /* RadioButtonModel.swift */, EAB1D29B28A5618900DAE764 /* RadioButtonGroup.swift */, - EAB1D29D28A5619500DAE764 /* RadioButtonGroupModel.swift */, ); path = RadioButton; sourceTree = ""; @@ -678,36 +596,27 @@ EA89200428AECF4B006B9984 /* UITextField+Publisher.swift in Sources */, EA3361C328902D960071C351 /* Toggle.swift in Sources */, EAF7F0A0289AB7EC00B287F5 /* View.swift in Sources */, - EAF7F11828A1475A00B287F5 /* RadioButtonModel.swift in Sources */, - EA89200D28B530FD006B9984 /* RadioBoxModel.swift in Sources */, - EA1F266728B945070033E859 /* RadioSwatchModel.swift in Sources */, - EAA5EEB328EB6A66003B3210 /* TextEntryField.swift in Sources */, EA89201328B568D8006B9984 /* RadioBox.swift in Sources */, - EA84F6B128B94A2500D67ABC /* CodableColor.swift in Sources */, - EAA5EEE028F49DB3003B3210 /* Colorable.swift in Sources */, + EAC9258C2911C9DE00091998 /* TextEntryField.swift in Sources */, EA3362402892EF6C0071C351 /* Label.swift in Sources */, + EAC9258D2911C9DE00091998 /* TextEntryFieldModel.swift in Sources */, EAF7F0B3289B1ADC00B287F5 /* ActionLabelAttribute.swift in Sources */, EA33622E2891EA3C0071C351 /* DispatchQueue+Once.swift in Sources */, EA4DB2FD28D3D0CA00103EE3 /* AnyEquatable.swift in Sources */, EAA5EEB728ECC03A003B3210 /* ToolTipLabelAttribute.swift in Sources */, EAF7F0AF289B144C00B287F5 /* UnderlineLabelAttribute.swift in Sources */, + EAC925842911C63100091998 /* Colorable.swift in Sources */, EA3361C5289030FC0071C351 /* Accessable.swift in Sources */, EA33622C2891E73B0071C351 /* FontProtocol.swift in Sources */, - EAA5EEAB28EB3ED9003B3210 /* EntryFieldModel.swift in Sources */, - EAA5EEA928EB3ED0003B3210 /* EntryField.swift in Sources */, EAF7F11728A1475A00B287F5 /* RadioButton.swift in Sources */, - EAB1D2A128A598FE00DAE764 /* UsesAutoLayout.swift in Sources */, EAB1D2CD28ABE76100DAE764 /* Withable.swift in Sources */, EAF7F0952899861000B287F5 /* Checkbox.swift in Sources */, - EA89201728B56CFF006B9984 /* RadioBoxGroupModel.swift in Sources */, EA3361C9289054C50071C351 /* Surfaceable.swift in Sources */, - EA3361A2288B1E840071C351 /* ToggleModel.swift in Sources */, - EA1F265D28B944F00033E859 /* CollectionView.swift in Sources */, EA4DB30228DCBCA500103EE3 /* Badge.swift in Sources */, - EA3362432892EFF20071C351 /* LabelModel.swift in Sources */, EA33624728931B050071C351 /* Initable.swift in Sources */, EAF7F0A4289B017C00B287F5 /* LabelAttributeModel.swift in Sources */, EAF7F0B1289B177F00B287F5 /* ColorLabelAttribute.swift in Sources */, + EAC9258F2911C9DE00091998 /* EntryField.swift in Sources */, EAB1D2EA28AE84AA00DAE764 /* UIControlPublisher.swift in Sources */, EAF7F13328A2A16500B287F5 /* AttachmentLabelAttributeModel.swift in Sources */, EA89200628B526D6006B9984 /* CheckboxGroup.swift in Sources */, @@ -716,44 +625,30 @@ EA3361BD288B2C760071C351 /* TypeAlias.swift in Sources */, EAB1D2CF28ABEF2B00DAE764 /* Typography.swift in Sources */, EAF7F09A2899B17200B287F5 /* CATransaction.swift in Sources */, - EA89200F28B53921006B9984 /* Selectable.swift in Sources */, - EAB1D29E28A5619500DAE764 /* RadioButtonGroupModel.swift in Sources */, EAF7F0A2289AFB3900B287F5 /* Errorable.swift in Sources */, - EA3C3B4C2894823E000CA526 /* AnyProxy.swift in Sources */, - 5FC35BE528D51414004EBEAC /* ButtonModel.swift in Sources */, EA3361AF288B26310071C351 /* FormFieldable.swift in Sources */, - EAB1D29A28A5611D00DAE764 /* SelectorGroupModelable.swift in Sources */, - EAF7F0BB289D80ED00B287F5 /* Modelable.swift in Sources */, - EAA5EEB128EB6A5A003B3210 /* TextEntryFieldModel.swift in Sources */, EA89201528B56CF4006B9984 /* RadioBoxGroup.swift in Sources */, - EA4DB30028DCBC9900103EE3 /* BadgeModel.swift in Sources */, EAF7F09E289AAEC000B287F5 /* Constants.swift in Sources */, EA1F266528B945070033E859 /* RadioSwatch.swift in Sources */, EA4DB18528CA967F00103EE3 /* SelectorGroupHandlerBase.swift in Sources */, + EAC9258E2911C9DE00091998 /* EntryFieldModel.swift in Sources */, EA89200228AECF2A006B9984 /* UIButton+Publisher.swift in Sources */, EAF7F0AB289B13FD00B287F5 /* FontLabelAttribute.swift in Sources */, EAB1D29C28A5618900DAE764 /* RadioButtonGroup.swift in Sources */, EA336171288B19200071C351 /* VDS.docc in Sources */, EAA5EEB528ECBFB4003B3210 /* ImageLabelAttribute.swift in Sources */, - EAF7F0962899861000B287F5 /* CheckboxModel.swift in Sources */, EAB1D2E628AE842000DAE764 /* Publisher+Bind.swift in Sources */, EA3361AA288B25E40071C351 /* Disabling.swift in Sources */, - EA89200828B526E0006B9984 /* CheckboxGroupModel.swift in Sources */, EA3361B6288B2A410071C351 /* Control.swift in Sources */, - EAB1D2A328A5994800DAE764 /* Debuggable.swift in Sources */, 5F21D7BF28DCEB3D003E7CD6 /* Useable.swift in Sources */, EAF7F0B7289C12A600B287F5 /* UITapGestureRecognizer.swift in Sources */, - EA3362452892F9130071C351 /* Labelable.swift in Sources */, EA3361AD288B26190071C351 /* DataTrackable.swift in Sources */, - EAA5EEE228F4C62C003B3210 /* Colorable.swift in Sources */, EA33623E2892EE950071C351 /* UIDevice.swift in Sources */, EA3362302891EB4A0071C351 /* Fonts.swift in Sources */, - EA1F265E28B944F00033E859 /* CollectionViewCell.swift in Sources */, EAF7F0AD289B142900B287F5 /* StrikeThroughLabelAttribute.swift in Sources */, EA3361B8288B2AAA0071C351 /* ViewProtocol.swift in Sources */, - EA3361BF288B2EA60071C351 /* ModelHandlerable.swift in Sources */, + EA3361BF288B2EA60071C351 /* Handlerable.swift in Sources */, EA3361A8288B23300071C351 /* UIColor.swift in Sources */, - EA1F266428B945070033E859 /* RadioSwatchGroupModel.swift in Sources */, EA1F266628B945070033E859 /* RadioSwatchGroup.swift in Sources */, 5FC35BE328D51405004EBEAC /* Button.swift in Sources */, ); @@ -901,6 +796,7 @@ EA336181288B19210071C351 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; @@ -908,6 +804,7 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/../../SharedFrameworks"; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_NSHumanReadableCopyright = ""; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -923,7 +820,7 @@ SKIP_INSTALL = YES; SUPPORTS_MACCATALYST = YES; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_INSTALL_OBJC_HEADER = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; @@ -932,6 +829,7 @@ EA336182288B19210071C351 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEFINES_MODULE = YES; @@ -939,6 +837,7 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; + FRAMEWORK_SEARCH_PATHS = "$(PROJECT_DIR)/../../SharedFrameworks"; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_KEY_NSHumanReadableCopyright = ""; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; @@ -954,7 +853,7 @@ SKIP_INSTALL = YES; SUPPORTS_MACCATALYST = YES; SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_INSTALL_OBJC_HEADER = YES; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; }; diff --git a/VDS/Classes/CollectionView.swift b/VDS/Classes/CollectionView.swift deleted file mode 100644 index e0b1ab57..00000000 --- a/VDS/Classes/CollectionView.swift +++ /dev/null @@ -1,110 +0,0 @@ -// -// CollectionView.swift -// VDS -// -// Created by Matt Bruce on 8/25/22. -// - -import Foundation -import UIKit -import Combine - -open class CollectionView: UICollectionView, ModelHandlerable, ViewProtocol, Resettable { - - //-------------------------------------------------- - // MARK: - Combine Properties - //-------------------------------------------------- - @Published public var model: ModelType = ModelType() - public var modelPublisher: Published.Publisher { $model } - public var subscribers = Set() - - //-------------------------------------------------- - // MARK: - Properties - //-------------------------------------------------- - private var initialSetupPerformed = false - - @Proxy(\.model.surface) - open var surface: Surface - - @Proxy(\.model.disabled) - open var disabled: Bool { - didSet { - self.isEnabled = !disabled - } - } - - open var isEnabled: Bool { - get { !model.disabled } - set { - //create local vars for clear coding - let disabled = !newValue - if model.disabled != disabled { - model.disabled = disabled - } - } - } - - //-------------------------------------------------- - // MARK: - Initializers - //-------------------------------------------------- - required public init() { - super.init(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout()) - initialSetup() - } - - public required init(with model: ModelType) { - super.init(frame: .zero, collectionViewLayout: UICollectionViewFlowLayout()) - initialSetup() - set(with: model) - } - - public required init(with model: ModelType, collectionViewLayout layout: UICollectionViewLayout) { - super.init(frame: .zero, collectionViewLayout: layout) - initialSetup() - set(with: model) - } - - public override init(frame: CGRect, collectionViewLayout layout: UICollectionViewLayout) { - super.init(frame: frame, collectionViewLayout: layout) - initialSetup() - set(with: model) - } - - public required init?(coder: NSCoder) { - super.init(coder: coder) - initialSetup() - } - - //-------------------------------------------------- - // MARK: - Setup - //-------------------------------------------------- - - public func initialSetup() { - if !initialSetupPerformed { - initialSetupPerformed = true - setupUpdateView() - setup() - } - } - - //-------------------------------------------------- - // MARK: - Overrides - //-------------------------------------------------- - open func updateView(viewModel: ModelType) { - fatalError("Implement updateView") - } - - open func reset() { - backgroundColor = .clear - if let model = model as? Resettable { - model.reset() - } - } - - // MARK: - ViewProtocol - /// Will be called only once. - open func setup() { - translatesAutoresizingMaskIntoConstraints = false - insetsLayoutMarginsFromSafeArea = false - } -} diff --git a/VDS/Classes/CollectionViewCell.swift b/VDS/Classes/CollectionViewCell.swift deleted file mode 100644 index 536053b1..00000000 --- a/VDS/Classes/CollectionViewCell.swift +++ /dev/null @@ -1,97 +0,0 @@ -// -// CollectionViewCell.swift -// VDS -// -// Created by Matt Bruce on 8/25/22. -// - -import Foundation -import UIKit -import Combine - -open class CollectionViewCell: UICollectionViewCell, ViewProtocol { - public typealias ModelType = ModelHandlerType.ModelType - //-------------------------------------------------- - // MARK: - Properties - //-------------------------------------------------- - private var initialSetupPerformed = false - - public var modelHandler: ModelHandlerType = ModelHandlerType() - - @Proxy(\.modelHandler.model.surface) - open var surface: Surface - - @Proxy(\.modelHandler.model.disabled) - open var disabled: Bool - - //-------------------------------------------------- - // MARK: - Initializers - //-------------------------------------------------- - required public init() { - super.init(frame: .zero) - initialSetup() - } - - public required init(with model: ModelType) { - super.init(frame: .zero) - initialSetup() - set(with: model) - } - - public override init(frame: CGRect) { - super.init(frame: frame) - initialSetup() - } - - public required init?(coder: NSCoder) { - super.init(coder: coder) - initialSetup() - } - - //-------------------------------------------------- - // MARK: - Setup - //-------------------------------------------------- - - public func initialSetup() { - if !initialSetupPerformed { - initialSetupPerformed = true - setup() - } - } - - //-------------------------------------------------- - // MARK: - Overrides - //-------------------------------------------------- - open func shouldUpdateView(viewModel: ModelType) -> Bool { - return modelHandler.shouldUpdateView(viewModel: viewModel) - } - - open func updateView(viewModel: ModelType) { - modelHandler.updateView(viewModel: viewModel) - } - - public func set(with model: ModelType) { - modelHandler.set(with: model) - } - - // MARK: - ViewProtocol - /// Will be called only once. - open func setup() { - translatesAutoresizingMaskIntoConstraints = false - insetsLayoutMarginsFromSafeArea = false - - addSubview(modelHandler) - - modelHandler.topAnchor.constraint(equalTo: topAnchor).isActive = true - modelHandler.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true - modelHandler.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true - modelHandler.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true - } - - open override func prepareForReuse() { - super.prepareForReuse() - if let handler = modelHandler as? Resettable { - handler.reset() - } - } -} diff --git a/VDS/Classes/ColorConfiguration.swift b/VDS/Classes/ColorConfiguration.swift index da63fc3a..89cfe2c4 100644 --- a/VDS/Classes/ColorConfiguration.swift +++ b/VDS/Classes/ColorConfiguration.swift @@ -23,13 +23,13 @@ public typealias ObjectColorable = Colorable & Initable & ObjectWithable /// /// let textColor = config.getColor(model) //returns .black final public class SurfaceColorConfiguration: ObjectColorable { - public typealias ModelType = Surfaceable + public typealias ObjectType = Surfaceable public var lightColor: UIColor = .clear public var darkColor: UIColor = .clear required public init(){} - public func getColor(_ viewModel: any ModelType) -> UIColor { - return viewModel.surface == .light ? lightColor : darkColor + public func getColor(_ object: any ObjectType) -> UIColor { + return object.surface == .light ? lightColor : darkColor } } @@ -42,8 +42,8 @@ public protocol DisabledSurfaceColorable: ObjectColorable { } extension DisabledSurfaceColorable { - public func getDisabledColor (_ viewModel: M) -> UIColor { - viewModel.disabled ? disabled.getColor(viewModel) : enabled.getColor(viewModel) + public func getDisabledColor (_ object: M) -> UIColor { + object.disabled ? disabled.getColor(object) : enabled.getColor(object) } } @@ -69,14 +69,14 @@ extension DisabledSurfaceColorable { /// /// final public class DisabledSurfaceColorConfiguration: DisabledSurfaceColorable { - public typealias ModelType = Surfaceable & Disabling + public typealias ObjectType = Surfaceable & Disabling public var disabled = SurfaceColorConfiguration() public var enabled = SurfaceColorConfiguration() required public init(){} - public func getColor(_ viewModel: any ModelType) -> UIColor { - getDisabledColor(viewModel) + public func getColor(_ object: any ObjectType) -> UIColor { + getDisabledColor(object) } } @@ -84,11 +84,11 @@ final public class DisabledSurfaceColorConfiguration: DisabledSurfaceColorable { ///MARK -- BinaryColorable ///------------------------------------------------------------------- public protocol BinaryColorable{ - var userTrueColor: Bool { get } + var useTrueColor: Bool { get } } -extension BinaryColorable where Self: Selectable { - public var userTrueColor: Bool { return selected } +extension BinaryColorable where Self: Control { + public var useTrueColor: Bool { return isSelected } } ///------------------------------------------------------------------- @@ -100,8 +100,8 @@ public protocol BinarySurfaceColorable: ObjectColorable { } extension BinarySurfaceColorable { - public func getBinaryColor(_ viewModel: M) -> UIColor { - viewModel.userTrueColor ? forTrue.getColor(viewModel) : forFalse.getColor(viewModel) + public func getBinaryColor(_ object: M) -> UIColor { + object.useTrueColor ? forTrue.getColor(object) : forFalse.getColor(object) } } @@ -126,14 +126,14 @@ extension BinarySurfaceColorable { /// /// final public class BinarySurfaceColorConfiguration: BinarySurfaceColorable { - public typealias ModelType = Surfaceable & BinaryColorable + public typealias ObjectType = Surfaceable & BinaryColorable public var forTrue = SurfaceColorConfiguration() public var forFalse = SurfaceColorConfiguration() required public init(){} - public func getColor(_ viewModel: any ModelType) -> UIColor { - getBinaryColor(viewModel) + public func getColor(_ object: any ObjectType) -> UIColor { + getBinaryColor(object) } } @@ -147,8 +147,8 @@ public protocol BinaryDisabledSurfaceColorable: ObjectColorable { } extension BinaryDisabledSurfaceColorable { - public func getBinaryColor(_ viewModel: M) -> UIColor { - viewModel.userTrueColor ? forTrue.getColor(viewModel) : forFalse.getColor(viewModel) + public func getBinaryColor(_ object: M) -> UIColor { + object.useTrueColor ? forTrue.getColor(object) : forFalse.getColor(object) } } @@ -179,13 +179,13 @@ extension BinaryDisabledSurfaceColorable { /// /// final public class BinaryDisabledSurfaceColorConfiguration: BinaryDisabledSurfaceColorable { - public typealias ModelType = Disabling & Surfaceable & BinaryColorable + public typealias ObjectType = Disabling & Surfaceable & BinaryColorable public var forTrue = DisabledSurfaceColorConfiguration() public var forFalse = DisabledSurfaceColorConfiguration() required public init(){} - public func getColor(_ viewModel: any ModelType) -> UIColor { - getBinaryColor(viewModel) + public func getColor(_ object: any ObjectType) -> UIColor { + getBinaryColor(object) } } diff --git a/VDS/Classes/Constants.swift b/VDS/Classes/Constants.swift index fe3bd6f0..4928c3f1 100644 --- a/VDS/Classes/Constants.swift +++ b/VDS/Classes/Constants.swift @@ -8,6 +8,6 @@ import Foundation public struct Constants { - public static let ModelStateDebounce = 0.02 + public static let StateDebounce = 0.0 //.001 public static let PaddingOne = 10.0 } diff --git a/VDS/Classes/Control.swift b/VDS/Classes/Control.swift index 50283184..79a26591 100644 --- a/VDS/Classes/Control.swift +++ b/VDS/Classes/Control.swift @@ -9,44 +9,34 @@ import Foundation import UIKit import Combine - -open class Control: UIControl, ModelHandlerable, ViewProtocol, Resettable { +@objc(VDSControl) +open class Control: UIControl, Handlerable, ViewProtocol, Resettable { //-------------------------------------------------- // MARK: - Combine Properties //-------------------------------------------------- - @Published public var model: ModelType = ModelType() - public var modelPublisher: Published.Publisher { $model } + public var subject = PassthroughSubject() public var subscribers = Set() - + //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- private var initialSetupPerformed = false + + open var surface: Surface = .light { didSet { didChange() } } - //if set to true this will call the - //defaultAction() in the class - public var executeDefaultAction = true - - @Proxy(\.model.surface) - open var surface: Surface - - @Proxy(\.model.disabled) - open var disabled: Bool { - didSet { - self.isEnabled = !disabled - } - } + open var disabled: Bool = false { didSet { isEnabled = !disabled } } + + open override var isSelected: Bool { didSet { didChange() } } open override var isEnabled: Bool { - get { !model.disabled } + get { !disabled } set { - //create local vars for clear coding - let disabled = !newValue - if model.disabled != disabled { - model.disabled = disabled + if disabled != !newValue { + disabled = !newValue } isUserInteractionEnabled = isEnabled + didChange() } } @@ -58,12 +48,6 @@ open class Control: UIControl, ModelHandlerable, ViewProto initialSetup() } - public required init(with model: ModelType) { - super.init(frame: .zero) - initialSetup() - set(with: model) - } - public override init(frame: CGRect) { super.init(frame: .zero) initialSetup() @@ -78,10 +62,9 @@ open class Control: UIControl, ModelHandlerable, ViewProto // MARK: - Setup //-------------------------------------------------- - public func initialSetup() { + open func initialSetup() { if !initialSetupPerformed { initialSetupPerformed = true - setupUpdateView() setup() } } @@ -96,15 +79,17 @@ open class Control: UIControl, ModelHandlerable, ViewProto //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- - open func updateView(viewModel: ModelType) { - fatalError("Implement updateView") + open override func layoutSubviews() { + super.layoutSubviews() + updateView() } + open func updateView() {} + open func reset() { backgroundColor = .clear - if let model = model as? Resettable { - model.reset() - } + surface = .light + disabled = false } // MARK: - ViewProtocol diff --git a/VDS/Classes/SelectorGroupHandlerBase.swift b/VDS/Classes/SelectorGroupHandlerBase.swift index 5f8b2b8a..27c56353 100644 --- a/VDS/Classes/SelectorGroupHandlerBase.swift +++ b/VDS/Classes/SelectorGroupHandlerBase.swift @@ -8,12 +8,12 @@ import Foundation import UIKit -public class SelectorGroupHandlerBase>: Control { +public class SelectorGroupHandlerBase: Control { //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- - public var selectorViews: [ModelHandlerType] = [] + public var selectorViews: [HandlerType] = [] //-------------------------------------------------- // MARK: - Overrides @@ -34,68 +34,19 @@ public class SelectorGroupHandlerBase ModelHandlerType? { - return selectorViews.first(where: { existingSelectorView in - return existingSelectorView.model.id == id - }) - } - - public func getCachedSelector(viewModel: ModelHandlerType.ModelType) -> ModelHandlerType.ModelType? { - if let index = model.selectors.firstIndex(where: { element in - return element.id == viewModel.id - }) { - return model.selectors[index] - } else { - return nil - } - } - - public func replace(viewModel: ModelHandlerType.ModelType){ - if let index = model.selectors.firstIndex(where: { element in - return element.id == viewModel.id - }) { - model.selectors[index] = viewModel - } - } - - public func createModelHandler(selector: ModelHandlerType.ModelType) -> ModelHandlerType { - //create view - let newSelectorView = ModelHandlerType(with: selector) - - //add model update to the subscribers - newSelectorView - .modelPublisher - .sink { [weak self] model in - self?.replace(viewModel: model) - } - .store(in: &subscribers) - - //add the selectedPublisher for the change - newSelectorView - .publisher(for: .touchUpInside) - .sink { [weak self] control in - self?.didSelect(control) - } - .store(in: &subscribers) - - return newSelectorView - - } - - open func didSelect(_ selectedControl: ModelHandlerType) { + open func didSelect(_ selectedControl: HandlerType) { fatalError("Must override didSelect") } public func valueChanged() { - DispatchQueue.main.asyncAfter(deadline: .now() + Constants.ModelStateDebounce) { [weak self] in + DispatchQueue.main.asyncAfter(deadline: .now() + Constants.StateDebounce) { [weak self] in self?.sendActions(for: .valueChanged) } } } -public class SelectorGroupSelectedHandlerBase>: SelectorGroupHandlerBase where GroupModelType.SelectorModelType == ModelHandlerType.ModelType { - - public var selectedModel: ModelHandlerType.ModelType? { - return model.selectedModel +public class SelectorGroupSelectedHandlerBase: SelectorGroupHandlerBase{ + public var selectedHandler: HandlerType? { + return selectorViews.filter { $0.isSelected == true }.first } } diff --git a/VDS/Classes/View.swift b/VDS/Classes/View.swift index 5883930f..1a08301a 100644 --- a/VDS/Classes/View.swift +++ b/VDS/Classes/View.swift @@ -10,39 +10,32 @@ import UIKit import Combine -open class View: UIView, ModelHandlerable, ViewProtocol, Resettable { +@objc(VDSView) +open class View: UIView, Handlerable, ViewProtocol, Resettable { //-------------------------------------------------- // MARK: - Combine Properties //-------------------------------------------------- - @Published public var model: ModelType = ModelType() - public var modelPublisher: Published.Publisher { $model } + public var subject = PassthroughSubject() public var subscribers = Set() - + //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- private var initialSetupPerformed = false - - @Proxy(\.model.surface) - open var surface: Surface - @Proxy(\.model.disabled) - open var disabled: Bool { - didSet { - self.isEnabled = !disabled - } - } + open var surface: Surface = .light { didSet { didChange() }} + open var disabled: Bool = false { didSet { isEnabled = !disabled } } + open var isEnabled: Bool { - get { !model.disabled } + get { !disabled } set { - //create local vars for clear coding - let disabled = !newValue - if model.disabled != disabled { - model.disabled = disabled + if disabled != !newValue { + disabled = !newValue } isUserInteractionEnabled = isEnabled + didChange() } } @@ -53,12 +46,6 @@ open class View: UIView, ModelHandlerable, ViewProtocol, R super.init(frame: .zero) initialSetup() } - - public required init(with model: ModelType) { - super.init(frame: .zero) - initialSetup() - set(with: model) - } public override init(frame: CGRect) { super.init(frame: .zero) @@ -74,26 +61,27 @@ open class View: UIView, ModelHandlerable, ViewProtocol, R // MARK: - Setup //-------------------------------------------------- - public func initialSetup() { + open func initialSetup() { if !initialSetupPerformed { initialSetupPerformed = true - setupUpdateView() setup() } } + open override func layoutSubviews() { + super.layoutSubviews() + updateView() + } + //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- - open func updateView(viewModel: ModelType) { - fatalError("Implement updateView") - } + open func updateView() {} open func reset() { backgroundColor = .clear - if let model = model as? Resettable { - model.reset() - } + surface = .light + disabled = false } // MARK: - ViewProtocol diff --git a/VDS/Components/Badge/Badge.swift b/VDS/Components/Badge/Badge.swift index 746a445b..d0550acf 100644 --- a/VDS/Components/Badge/Badge.swift +++ b/VDS/Components/Badge/Badge.swift @@ -11,44 +11,37 @@ import VDSColorTokens import VDSFormControlsTokens import Combine -public class Badge: BadgeBase{} +public enum BadgeFillColor: String, Codable, CaseIterable { + case red, yellow, green, orange, blue, black, white +} -open class BadgeBase: View { +@objc(VDSBadge) +public class Badge: View, Accessable { private var label = Label() //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- - @Proxy(\.model.fillColor) - open var fillColor: BadgeFillColor + open var fillColor: BadgeFillColor = .red { didSet { didChange() }} - @Proxy(\.model.text) - open var text: String + open var text: String = "" { didSet { didChange() }} - @Proxy(\.model.maxWidth) - open var maxWidth: CGFloat? + open var maxWidth: CGFloat? { didSet { didChange() }} - @Proxy(\.model.numberOfLines) - open var numberOfLines: Int + open var numberOfLines: Int = 1 { didSet { didChange() }} - @Proxy(\.model.accessibilityHintEnabled) - open var accessibilityHintEnabled: String? + open var accessibilityHintEnabled: String? { didSet { didChange() }} - @Proxy(\.model.accessibilityHintDisabled) - open var accessibilityHintDisabled: String? + open var accessibilityHintDisabled: String? { didSet { didChange() }} - @Proxy(\.model.accessibilityValueEnabled) - open var accessibilityValueEnabled: String? + open var accessibilityValueEnabled: String? { didSet { didChange() }} - @Proxy(\.model.accessibilityValueDisabled) - open var accessibilityValueDisabled: String? + open var accessibilityValueDisabled: String? { didSet { didChange() }} - @Proxy(\.model.accessibilityLabelEnabled) - open var accessibilityLabelEnabled: String? + open var accessibilityLabelEnabled: String? { didSet { didChange() }} - @Proxy(\.model.accessibilityLabelDisabled) - open var accessibilityLabelDisabled: String? + open var accessibilityLabelDisabled: String? { didSet { didChange() }} //-------------------------------------------------- // MARK: - Constraints @@ -90,9 +83,9 @@ open class BadgeBase: View { //-------------------------------------------------- // MARK: - Configuration //-------------------------------------------------- - public func backgroundColor(for fillColor: BadgeFillColor) -> UIColor { + public func backgroundColor() -> UIColor { var config: SurfaceColorConfiguration - switch model.fillColor { + switch fillColor { case .red: config = SurfaceColorConfiguration().with { $0.lightColor = VDSColor.backgroundBrandhighlight @@ -130,10 +123,10 @@ open class BadgeBase: View { } } - return config.getColor(model) + return config.getColor(self) } - public func textColorConfiguration(for fillColor: BadgeFillColor) -> AnyColorable { + public func textColorConfiguration() -> AnyColorable { switch fillColor { @@ -165,19 +158,23 @@ open class BadgeBase: View { //-------------------------------------------------- // MARK: - State //-------------------------------------------------- - open override func updateView(viewModel: ModelType) { - backgroundColor = backgroundColor(for: viewModel.fillColor) + open override func updateView() { + backgroundColor = backgroundColor() + label.textColorConfiguration = textColorConfiguration() + label.numberOfLines = numberOfLines + label.textPosition = .left + label.typograpicalStyle = .BoldBodySmall + label.text = text + label.surface = surface + label.disabled = disabled - label.textColorConfiguration = textColorConfiguration(for: viewModel.fillColor) - label.numberOfLines = viewModel.numberOfLines - - if let maxWidth = viewModel.maxWidth, let minWidth = minWidthConstraint?.constant, maxWidth > minWidth { + if let maxWidth = maxWidth, let minWidth = minWidthConstraint?.constant, maxWidth > minWidth { maxWidthConstraint?.constant = maxWidth maxWidthConstraint?.isActive = true } else { maxWidthConstraint?.isActive = false } - label.set(with: viewModel.label) + setAccessibilityLabel() } diff --git a/VDS/Components/Badge/BadgeModel.swift b/VDS/Components/Badge/BadgeModel.swift deleted file mode 100644 index 93876157..00000000 --- a/VDS/Components/Badge/BadgeModel.swift +++ /dev/null @@ -1,53 +0,0 @@ -// -// BadgeModel.swift -// VDS -// -// Created by Matt Bruce on 9/22/22. -// - -import Foundation -import UIKit -import VDSColorTokens - -public enum BadgeFillColor: String, Codable, CaseIterable { - case red, yellow, green, orange, blue, black, white -} - -public protocol BadgeModel: Modelable, Accessable { - var fillColor: BadgeFillColor { get set } - var text: String { get set } - var maxWidth: CGFloat? { get set } - var numberOfLines: Int { get set } -} - -extension BadgeModel { - public var label: DefaultLabelModel { - var model = DefaultLabelModel() - model.textPosition = .left - model.typograpicalStyle = .BoldBodySmall - model.text = text - model.surface = surface - model.disabled = disabled - return model - } -} - -public struct DefaultBadgeModel: BadgeModel { - public var id = UUID() - public var fillColor: BadgeFillColor = .red - public var text: String = "" - public var maxWidth: CGFloat? - public var numberOfLines: Int = 1 - - public var surface: Surface = .light - public var disabled: Bool = false - - public var accessibilityHintEnabled: String? - public var accessibilityHintDisabled: String? - public var accessibilityValueEnabled: String? - public var accessibilityValueDisabled: String? - public var accessibilityLabelEnabled: String? - public var accessibilityLabelDisabled: String? - - public init() { } -} diff --git a/VDS/Components/Button/Button.swift b/VDS/Components/Button/Button.swift index d31d4807..0d5ed7d5 100644 --- a/VDS/Components/Button/Button.swift +++ b/VDS/Components/Button/Button.swift @@ -11,15 +11,18 @@ import VDSColorTokens import VDSFormControlsTokens import Combine -public class Button:ButtonBase{} +public enum ButtonSize: String, Codable, CaseIterable { + case large + case small +} -open class ButtonBase: UIButton, ModelHandlerable, ViewProtocol, Resettable { +@objc(VDSButton) +open class Button: UIButton, Handlerable, ViewProtocol, Resettable, Useable { //-------------------------------------------------- // MARK: - Combine Properties //-------------------------------------------------- - @Published public var model: ModelType = ModelType() - public var modelPublisher: Published.Publisher { $model } + public var subject = PassthroughSubject() public var subscribers = Set() //-------------------------------------------------- @@ -32,39 +35,29 @@ open class ButtonBase: UIButton, ModelHandlerable, ViewP //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- - @Proxy(\.model.surface) - open var surface: Surface + open var text: String? { didSet { didChange() } } - @Proxy(\.model.disabled) - open var disabled: Bool { - didSet { - isEnabled = !disabled - } - } + open var use: Use = .primary { didSet { didChange() }} + + open var size: ButtonSize = .large { didSet { didChange() }} - @Proxy(\.model.text) - open var text: String? + open var width: CGFloat? { didSet { didChange() }} - @Proxy(\.model.use) - open var use: Use - - @Proxy(\.model.size) - open var size: ButtonSize - - @Proxy(\.model.width) - open var width: CGFloat? + open var surface: Surface = .light { didSet { didChange() }} + open var disabled: Bool = false { didSet { isEnabled = !disabled } } + open override var isEnabled: Bool { - get { !model.disabled } + get { !disabled } set { - //create local vars for clear coding - let disabled = !newValue - if model.disabled != disabled { - model.disabled = disabled + if disabled != !newValue { + disabled = !newValue } isUserInteractionEnabled = isEnabled + didChange() } } + //-------------------------------------------------- // MARK: - Configuration Properties @@ -113,16 +106,9 @@ open class ButtonBase: UIButton, ModelHandlerable, ViewP initialSetup() } - public required init(with model: ModelType) { - super.init(frame: .zero) - initialSetup() - set(with: model) - } - public override init(frame: CGRect) { super.init(frame: .zero) initialSetup() - set(with: model) } public required init?(coder: NSCoder) { @@ -138,7 +124,6 @@ open class ButtonBase: UIButton, ModelHandlerable, ViewP translatesAutoresizingMaskIntoConstraints = false accessibilityCustomActions = [] accessibilityTraits = .staticText - setupUpdateView() setup() } @@ -149,39 +134,44 @@ open class ButtonBase: UIButton, ModelHandlerable, ViewP //only 1 of the 2 widths can be on at the same time widthConstraint = widthAnchor.constraint(equalToConstant: 0) - minWidthConstraint = widthAnchor.constraint(greaterThanOrEqualToConstant: model.size.minimumWidth) + minWidthConstraint = widthAnchor.constraint(greaterThanOrEqualToConstant: size.minimumWidth) //height - heightConstraint = heightAnchor.constraint(equalToConstant: model.size.height) + heightConstraint = heightAnchor.constraint(equalToConstant: size.height) heightConstraint?.isActive = true } open func reset() { - model = ModelType() + surface = .light + disabled = false + use = .primary + width = nil + size = .large accessibilityCustomActions = [] accessibilityTraits = .staticText } + + open override func layoutSubviews() { + super.layoutSubviews() + updateView() + } //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- - open func updateView(viewModel: ModelType) { + open func updateView() { - let bgColor = buttonBackgroundColorConfiguration.getColor(viewModel) - let borderColor = buttonBorderColorConfiguration.getColor(viewModel) - let titleColor = buttonTitleColorConfiguration.getColor(viewModel) - let borderWidth = viewModel.use == .secondary ? 1.0 : 0.0 - let buttonHeight = viewModel.size.height + let bgColor = buttonBackgroundColorConfiguration.getColor(self) + let borderColor = buttonBorderColorConfiguration.getColor(self) + let titleColor = buttonTitleColorConfiguration.getColor(self) + let borderWidth = use == .secondary ? 1.0 : 0.0 + let buttonHeight = size.height let cornerRadius = buttonHeight / 2 - let minWidth = viewModel.size.minimumWidth - let font = viewModel.size == .large ? TypographicalStyle.BoldBodyLarge.font : TypographicalStyle.BoldBodySmall.font - let edgeInsets = viewModel.size.edgeInsets + let minWidth = size.minimumWidth + let font = size == .large ? TypographicalStyle.BoldBodyLarge.font : TypographicalStyle.BoldBodySmall.font + let edgeInsets = size.edgeInsets - if let text = viewModel.text { - setTitle(text, for: .normal) - } else { - setTitle("No ViewModel Text", for: .normal) - } + setTitle(text ?? "No Text", for: .normal) titleLabel?.font = font backgroundColor = bgColor setTitleColor(titleColor, for: .normal) @@ -193,7 +183,7 @@ open class ButtonBase: UIButton, ModelHandlerable, ViewP minWidthConstraint?.constant = minWidth heightConstraint?.constant = buttonHeight - if let width = viewModel.width, width > minWidth { + if let width, width > minWidth { widthConstraint?.constant = width widthConstraint?.isActive = true minWidthConstraint?.isActive = false @@ -202,20 +192,20 @@ open class ButtonBase: UIButton, ModelHandlerable, ViewP minWidthConstraint?.isActive = true } } - + //-------------------------------------------------- // MARK: - PRIVATE //-------------------------------------------------- private class UseableColorConfiguration: ObjectColorable { - typealias ModelType = Disabling & Surfaceable & Useable + typealias ObjectType = Disabling & Surfaceable & Useable public var primary = DisabledSurfaceColorConfiguration() public var secondary = DisabledSurfaceColorConfiguration() required public init(){} - public func getColor(_ viewModel: ModelType) -> UIColor { - return viewModel.use == .primary ? primary.getColor(viewModel) : secondary.getColor(viewModel) + public func getColor(_ object: ObjectType) -> UIColor { + return object.use == .primary ? primary.getColor(object) : secondary.getColor(object) } } diff --git a/VDS/Components/Button/ButtonModel.swift b/VDS/Components/Button/ButtonModel.swift deleted file mode 100644 index d5b7f0d9..00000000 --- a/VDS/Components/Button/ButtonModel.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// ButtonModel.swift -// VDS -// -// Created by Jarrod Courtney on 9/16/22. -// - -import Foundation -import UIKit - -public enum ButtonSize: String, Codable, CaseIterable { - case large - case small -} - -public protocol ButtonModel: Modelable, Useable { - var text: String? { get set } - var width: CGFloat? { get set } - var size: ButtonSize { get set } - var use: Use { get set } -} - -public struct DefaultButtonModel: ButtonModel { - - public var id = UUID() - public var text: String? - public var typograpicalStyle: TypographicalStyle = .BoldBodyLarge - public var surface: Surface = .light - public var use: Use = .primary - public var disabled: Bool = false - public var width: CGFloat? - public var size: ButtonSize = .large - public init(){} -} diff --git a/VDS/Components/Checkbox/Checkbox.swift b/VDS/Components/Checkbox/Checkbox.swift index e14e5b2b..303d7f81 100644 --- a/VDS/Components/Checkbox/Checkbox.swift +++ b/VDS/Components/Checkbox/Checkbox.swift @@ -11,9 +11,11 @@ import VDSColorTokens import VDSFormControlsTokens import Combine -public class Checkbox: CheckboxBase{} +@objc(VDSCheckbox) +public class Checkbox: CheckboxBase{} -public class SoloCheckbox: CheckboxBase{ +@objc(VDSSoloCheckbox) +public class SoloCheckbox: CheckboxBase{ public override func initialSetup() { super.initialSetup() publisher(for: .touchUpInside) @@ -23,11 +25,36 @@ public class SoloCheckbox: CheckboxBase{ } } -open class CheckboxBase: Control { +@objc(VDSCheckboxBase) +open class CheckboxBase: Control, Accessable, DataTrackable, BinaryColorable, Errorable { + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + required public init() { + super.init(frame: .zero) + } + public override init(frame: CGRect) { + super.init(frame: .zero) + } + + public required init?(coder: NSCoder) { + super.init(coder: coder) + } + //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- + private var shouldShowError: Bool { + guard showError && !disabled && errorText?.isEmpty == false else { return false } + return true + } + + private var shouldShowLabels: Bool { + guard labelText?.isEmpty == false || childText?.isEmpty == false else { return false } + return true + } + private var mainStackView: UIStackView = { return UIStackView().with { $0.translatesAutoresizingMaskIntoConstraints = false @@ -67,60 +94,42 @@ open class CheckboxBase: Control { }() //can't bind to @Proxy - open override var isSelected: Bool { - get { model.selected } - set { - if model.selected != newValue { - model.selected = newValue - } - } - } + open override var isSelected: Bool { didSet { didChange() }} - @Proxy(\.model.labelText) - open var labelText: String? + open var labelText: String? { didSet { didChange() }} - @Proxy(\.model.childText) - open var childText: String? + open var labelTextAttributes: [any LabelAttributeModel]? { didSet { didChange() }} - @Proxy(\.model.showError) - open var showError: Bool - - @Proxy(\.model.errorText) - open var errorText: String? + open var childText: String? { didSet { didChange() }} - @Proxy(\.model.inputId) - open var inputId: String? + open var childTextAttributes: [any LabelAttributeModel]? { didSet { didChange() }} - @Proxy(\.model.value) - open var value: AnyHashable? + open var showError: Bool = false { didSet { didChange() }} - @Proxy(\.model.dataAnalyticsTrack) - open var dataAnalyticsTrack: String? + open var errorText: String? { didSet { didChange() }} - @Proxy(\.model.dataClickStream) - open var dataClickStream: String? + open var inputId: String? { didSet { didChange() }} - @Proxy(\.model.dataTrack) - open var dataTrack: String? + open var value: AnyHashable? { didSet { didChange() }} + + open var dataAnalyticsTrack: String? { didSet { didChange() }} - @Proxy(\.model.accessibilityHintEnabled) - open var accessibilityHintEnabled: String? + open var dataClickStream: String? { didSet { didChange() }} + + open var dataTrack: String? { didSet { didChange() }} + + open var accessibilityHintEnabled: String? { didSet { didChange() }} - @Proxy(\.model.accessibilityHintDisabled) - open var accessibilityHintDisabled: String? + open var accessibilityHintDisabled: String? { didSet { didChange() }} - @Proxy(\.model.accessibilityValueEnabled) - open var accessibilityValueEnabled: String? + open var accessibilityValueEnabled: String? { didSet { didChange() }} - @Proxy(\.model.accessibilityValueDisabled) - open var accessibilityValueDisabled: String? + open var accessibilityValueDisabled: String? { didSet { didChange() }} - @Proxy(\.model.accessibilityLabelEnabled) - open var accessibilityLabelEnabled: String? + open var accessibilityLabelEnabled: String? { didSet { didChange() }} - @Proxy(\.model.accessibilityLabelDisabled) - open var accessibilityLabelDisabled: String? - + open var accessibilityLabelDisabled: String? { didSet { didChange() }} + //-------------------------------------------------- // MARK: - Constraints //-------------------------------------------------- @@ -159,7 +168,7 @@ open class CheckboxBase: Control { selectorWidthConstraint = selectorView.widthAnchor.constraint(equalToConstant: selectorSize.width) selectorWidthConstraint?.isActive = true - updateSelector(model) + updateSelector() mainStackView.topAnchor.constraint(equalTo: topAnchor).isActive = true mainStackView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true @@ -168,22 +177,32 @@ open class CheckboxBase: Control { } - func updateLabels(_ viewModel: ModelType) { + func updateLabels() { //deal with labels - if viewModel.shouldShowLabels { + if shouldShowLabels { //add the stackview to hold the 2 labels //top label - if let labelModel = viewModel.labelModel { - primaryLabel.set(with: labelModel) + if let labelText { + primaryLabel.textPosition = .left + primaryLabel.typograpicalStyle = .BoldBodyLarge + primaryLabel.text = labelText + primaryLabel.surface = surface + primaryLabel.disabled = disabled + primaryLabel.attributes = labelTextAttributes primaryLabel.isHidden = false } else { primaryLabel.isHidden = true } //bottom label - if let childModel = viewModel.childModel { - secondaryLabel.set(with: childModel) + if let childText { + secondaryLabel.textPosition = .left + secondaryLabel.typograpicalStyle = .BodyLarge + secondaryLabel.text = childText + secondaryLabel.surface = surface + secondaryLabel.disabled = disabled + secondaryLabel.attributes = childTextAttributes secondaryLabel.isHidden = false } else { secondaryLabel.isHidden = true @@ -199,20 +218,23 @@ open class CheckboxBase: Control { } //either add/remove the error from the main stack - if let errorModel = model.errorModel, model.shouldShowError { - errorLabel.set(with: errorModel) + if let errorText, shouldShowError { + errorLabel.textPosition = .left + errorLabel.typograpicalStyle = .BodyMedium + errorLabel.text = errorText + errorLabel.surface = surface + errorLabel.disabled = disabled mainStackView.spacing = 8 errorLabel.isHidden = false } else { mainStackView.spacing = 0 errorLabel.isHidden = true } - } public override func reset() { super.reset() - updateSelector(model) + updateSelector() setAccessibilityLabel() } @@ -229,14 +251,12 @@ open class CheckboxBase: Control { //-------------------------------------------------- // MARK: - State //-------------------------------------------------- - open override func updateView(viewModel: ModelType) { - updateLabels(viewModel) - updateSelector(viewModel) + open override func updateView() { + updateLabels() + updateSelector() setAccessibilityHint() - setAccessibilityValue(viewModel.selected) - setAccessibilityLabel(viewModel.selected) - setNeedsLayout() - layoutIfNeeded() + setAccessibilityValue(isSelected) + setAccessibilityLabel(isSelected) } @@ -294,11 +314,11 @@ open class CheckboxBase: Control { return checkboxSize } - open func updateSelector(_ viewModel: ModelType) { + open func updateSelector() { //get the colors - let backgroundColor = checkboxBackgroundColorConfiguration.getColor(viewModel) - let borderColor = checkboxBorderColorConfiguration.getColor(viewModel) - let checkColor = checkboxCheckColorConfiguration.getColor(viewModel) + let backgroundColor = checkboxBackgroundColorConfiguration.getColor(self) + let borderColor = checkboxBorderColorConfiguration.getColor(self) + let checkColor = checkboxCheckColorConfiguration.getColor(self) if let shapeLayer = shapeLayer, let sublayers = layer.sublayers, sublayers.contains(shapeLayer) { shapeLayer.removeFromSuperlayer() @@ -340,7 +360,7 @@ open class CheckboxBase: Control { shapeLayer.lineJoin = .miter shapeLayer.lineWidth = 2 CATransaction.withDisabledAnimations { - shapeLayer.strokeEnd = model.selected ? 1 : 0 + shapeLayer.strokeEnd = isSelected ? 1 : 0 } } } @@ -350,21 +370,21 @@ open class CheckboxBase: Control { // MARK: - Color Class Configurations //-------------------------------------------------- internal class ErrorBinaryDisabledSurfaceColorConfiguration: BinaryDisabledSurfaceColorable { - typealias ModelType = Errorable & Disabling & Surfaceable & BinaryColorable + typealias ObjectType = Errorable & Disabling & Surfaceable & BinaryColorable var error = BinarySurfaceColorConfiguration() var forTrue = DisabledSurfaceColorConfiguration() var forFalse = DisabledSurfaceColorConfiguration() required init() {} - func getColor(_ viewModel: ModelType) -> UIColor { + func getColor(_ object: ObjectType) -> UIColor { //only show error is enabled and showError == true - let showErrorColor = !viewModel.disabled && viewModel.showError + let showErrorColor = !object.disabled && object.showError if showErrorColor { - return error.getColor(viewModel) + return error.getColor(object) } else { - return getBinaryColor(viewModel) + return getBinaryColor(object) } } } diff --git a/VDS/Components/Checkbox/CheckboxGroup.swift b/VDS/Components/Checkbox/CheckboxGroup.swift index 67d87993..7501d11f 100644 --- a/VDS/Components/Checkbox/CheckboxGroup.swift +++ b/VDS/Components/Checkbox/CheckboxGroup.swift @@ -8,7 +8,8 @@ import Foundation import UIKit -public class CheckboxGroup: CheckboxGroupBase { +@objc(VDSCheckboxGroup) +public class CheckboxGroup: CheckboxGroupBase { public override func didSelect(_ selectedControl: Checkbox) { selectedControl.toggle() if selectedControl.isSelected, showError{ @@ -18,13 +19,30 @@ public class CheckboxGroup: CheckboxGroupBase>: SelectorGroupHandlerBase { +public class CheckboxGroupBase: SelectorGroupHandlerBase { //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- + + public override var selectorViews: [HandlerType] { + didSet { + for selector in selectorViews { + if !mainStackView.arrangedSubviews.contains(selector) { + selector + .publisher(for: .touchUpInside) + .sink { [weak self] handler in + self?.didSelect(handler) + }.store(in: &subscribers) + mainStackView.addArrangedSubview(selector) + } + } + } + } + + private var _showError: Bool = false public var showError: Bool { - get { model.showError } + get { _showError } set { var newShowError = newValue let anySelected = selectorViews.filter { $0.isSelected == true }.count > 0 @@ -34,8 +52,7 @@ public class CheckboxGroupBase 0 else { return nil } return selected diff --git a/VDS/Components/Checkbox/CheckboxGroupModel.swift b/VDS/Components/Checkbox/CheckboxGroupModel.swift deleted file mode 100644 index 93e253ab..00000000 --- a/VDS/Components/Checkbox/CheckboxGroupModel.swift +++ /dev/null @@ -1,29 +0,0 @@ -// -// CheckboxGroupModel.swift -// VDS -// -// Created by Matt Bruce on 8/23/22. -// - -import Foundation - -public protocol CheckboxGroupModel: SelectorGroupModelable, Errorable where SelectorModelType: CheckboxModel { - -} - -public struct DefaultCheckboxGroupModel: CheckboxGroupModel { - public typealias SelectorModelType = DefaultCheckboxModel - public var id = UUID() - public var inputId: String? - public var value: AnyHashable? - public var surface: Surface = .light - public var disabled: Bool = false - public var selectors: [SelectorModelType] - public var showError: Bool = false - public var errorText: String? - public init() { selectors = [] } - public init(selectors: [SelectorModelType]){ - self.selectors = selectors - } - -} diff --git a/VDS/Components/Checkbox/CheckboxModel.swift b/VDS/Components/Checkbox/CheckboxModel.swift deleted file mode 100644 index fad6c1e3..00000000 --- a/VDS/Components/Checkbox/CheckboxModel.swift +++ /dev/null @@ -1,123 +0,0 @@ -// -// ToggleModel.swift -// VDS -// -// Created by Matt Bruce on 7/22/22. -// - -import Foundation -import UIKit - -public protocol CheckboxModel: Modelable, FormFieldable, Errorable, DataTrackable, Accessable, Selectable, BinaryColorable { - var labelText: String? { get set } - var labelTextAttributes: [any LabelAttributeModel]? { get set } - var childText: String? { get set } - var childTextAttributes: [any LabelAttributeModel]? { get set } -} - -extension CheckboxModel { - - public var shouldShowError: Bool { - guard showError && !disabled && errorText?.isEmpty == false else { return false } - return true - } - - public var shouldShowLabels: Bool { - guard labelText?.isEmpty == false || childText?.isEmpty == false else { return false } - return true - } - - public var labelModel: DefaultLabelModel? { - guard let labelText = labelText else { return nil } - var model = DefaultLabelModel() - model.textPosition = .left - model.typograpicalStyle = .BoldBodyLarge - model.text = labelText - model.surface = surface - model.disabled = disabled - model.attributes = labelTextAttributes - return model - } - - public var childModel: DefaultLabelModel? { - guard let childText = childText else { return nil } - var model = DefaultLabelModel() - model.textPosition = .left - model.typograpicalStyle = .BodyLarge - model.text = childText - model.surface = surface - model.disabled = disabled - model.attributes = childTextAttributes - return model - } - - public var errorModel: DefaultLabelModel? { - guard let errorText = errorText, showError else { return nil } - var model = DefaultLabelModel() - model.textPosition = .left - model.typograpicalStyle = .BodyMedium - model.text = errorText - model.surface = surface - model.disabled = disabled - return model - } -} - -public struct DefaultCheckboxModel: CheckboxModel { - public var id = UUID() - public var selected: Bool = false - public var labelText: String? - public var labelTextAttributes: [any LabelAttributeModel]? - public var childText: String? - public var childTextAttributes: [any LabelAttributeModel]? - - public var showError: Bool = false - public var errorText: String? - - public var inputId: String? - public var value: AnyHashable? - - public var surface: Surface = .light - public var disabled: Bool = false - - public var dataAnalyticsTrack: String? - public var dataClickStream: String? - public var dataTrack: String? - public var accessibilityHintEnabled: String? - public var accessibilityHintDisabled: String? - public var accessibilityValueEnabled: String? - public var accessibilityValueDisabled: String? - public var accessibilityLabelEnabled: String? - public var accessibilityLabelDisabled: String? - - public init() {} - - public static func == (lhs: DefaultCheckboxModel, rhs: DefaultCheckboxModel) -> Bool { - lhs.isEqual(rhs) - } - - public func isEqual(_ equatable: DefaultCheckboxModel) -> Bool { - return id == equatable.id - && selected == equatable.selected - && labelText == equatable.labelText - && labelTextAttributes == equatable.labelTextAttributes - && childText == equatable.childText - && childTextAttributes == equatable.childTextAttributes - && showError == equatable.showError - && errorText == equatable.errorText - && inputId == equatable.inputId - && value == equatable.value - && surface == equatable.surface - && disabled == equatable.disabled - && dataAnalyticsTrack == equatable.dataAnalyticsTrack - && dataClickStream == equatable.dataClickStream - && dataTrack == equatable.dataTrack - && accessibilityHintEnabled == equatable.accessibilityHintEnabled - && accessibilityHintDisabled == equatable.accessibilityHintDisabled - && accessibilityValueEnabled == equatable.accessibilityValueEnabled - && accessibilityValueDisabled == equatable.accessibilityValueDisabled - && accessibilityLabelEnabled == equatable.accessibilityLabelEnabled - && accessibilityLabelDisabled == equatable.accessibilityLabelDisabled - } - -} diff --git a/VDS/Components/Label/Attributes/UnderlineLabelAttribute.swift b/VDS/Components/Label/Attributes/UnderlineLabelAttribute.swift index 98ed45a2..d1d9399b 100644 --- a/VDS/Components/Label/Attributes/UnderlineLabelAttribute.swift +++ b/VDS/Components/Label/Attributes/UnderlineLabelAttribute.swift @@ -33,7 +33,7 @@ public struct UnderlineLabelAttribute: LabelAttributeModel { } } - public init(location: Int, length: Int, style: UnderlineStyle = .single, color: UIColor = .black, pattern: UnderlineStyle.Pattern? = nil) { + public init(location: Int, length: Int, style: UnderlineStyle = .single, color: UIColor? = nil, pattern: UnderlineStyle.Pattern? = nil) { self.location = location self.length = length self.color = color diff --git a/VDS/Components/Label/Label.swift b/VDS/Components/Label/Label.swift index 5934458e..06f610f8 100644 --- a/VDS/Components/Label/Label.swift +++ b/VDS/Components/Label/Label.swift @@ -10,57 +10,46 @@ import UIKit import VDSColorTokens import Combine -public class Label:LabelBase{} +@objc(VDSLabel) +public class Label: LabelBase {} -open class LabelBase: UILabel, ModelHandlerable, ViewProtocol, Resettable { +@objc(VDSLabelBase) +open class LabelBase: UILabel, Handlerable, ViewProtocol, Resettable { //-------------------------------------------------- // MARK: - Combine Properties //-------------------------------------------------- - @Published public var model: ModelType = ModelType() - public var modelPublisher: Published.Publisher { $model } + public var subject = PassthroughSubject() public var subscribers = Set() - + public var hasChanged: Bool = false + //-------------------------------------------------- // MARK: - Properties //-------------------------------------------------- - @Proxy(\.model.surface) - open var surface: Surface + open var surface: Surface = .light { didSet { didChange() }} - @Proxy(\.model.disabled) - open var disabled: Bool { - didSet { - self.isEnabled = !disabled - } - } + open var disabled: Bool = false { didSet { isEnabled = !disabled } } + + open var attributes: [any LabelAttributeModel]? { didSet { didChange() }} + open var typograpicalStyle: TypographicalStyle = .defaultStyle { didSet { didChange() }} + + open var textPosition: TextPosition = .left { didSet { didChange() }} + open override var isEnabled: Bool { - get { !model.disabled } + get { !disabled } set { - //create local vars for clear coding - let disabled = !newValue - if model.disabled != disabled { - model.disabled = disabled + if disabled != !newValue { + disabled = !newValue } isUserInteractionEnabled = isEnabled + didChange() } } - @Proxy(\.model.attributes) - open var attributes: [any LabelAttributeModel]? - - @Proxy(\.model.typograpicalStyle) - open var typograpicalStyle: TypographicalStyle - - @Proxy(\.model.textPosition) - open var textPosition: TextPosition - - //can't use @Proxy here override open var text: String? { didSet { - if model.text != oldValue { - model.text = text - } + didChange() } } @@ -81,17 +70,10 @@ open class LabelBase: UILabel, ModelHandlerable, ViewProt super.init(frame: .zero) initialSetup() } - - public required init(with model: ModelType) { - super.init(frame: .zero) - initialSetup() - set(with: model) - } public override init(frame: CGRect) { super.init(frame: .zero) initialSetup() - set(with: model) } public required init?(coder: NSCoder) { @@ -109,37 +91,38 @@ open class LabelBase: UILabel, ModelHandlerable, ViewProt translatesAutoresizingMaskIntoConstraints = false accessibilityCustomActions = [] accessibilityTraits = .staticText - setupUpdateView() setup() } open func setup() {} open func reset() { + surface = .light + disabled = false + attributes = nil + typograpicalStyle = .defaultStyle + textPosition = .left text = nil attributedText = nil - textColor = .black - font = TypographicalStyle.BodyLarge.font - textAlignment = .left accessibilityCustomActions = [] accessibilityTraits = .staticText numberOfLines = 0 } - + //-------------------------------------------------- // MARK: - Overrides //-------------------------------------------------- - open func updateView(viewModel: ModelType) { - textAlignment = viewModel.textPosition.textAlignment - textColor = textColorConfiguration.getColor(viewModel) + open override func layoutSubviews() { + super.layoutSubviews() + updateView() + } + + open func updateView() { + textAlignment = textPosition.textAlignment + textColor = textColorConfiguration.getColor(self) + font = typograpicalStyle.font - if let vdsFont = viewModel.font { - font = vdsFont - } else { - font = TypographicalStyle.defaultStyle.font - } - - if let text = viewModel.text, let font = font, let textColor = textColor { + if let text = text, let font = font, let textColor = textColor { //clear the arrays holding actions accessibilityCustomActions = [] actions = [] @@ -149,9 +132,9 @@ open class LabelBase: UILabel, ModelHandlerable, ViewProt let mutableText = NSMutableAttributedString(string: text, attributes: startingAttributes) //set the local lineHeight/lineSpacing attributes - setStyleAttributes(viewModel: viewModel, attributedString: mutableText) + setStyleAttributes(attributedString: mutableText) - if let attributes = viewModel.attributes { + if let attributes = attributes { //loop through the models attributes for attribute in attributes { @@ -170,42 +153,40 @@ open class LabelBase: UILabel, ModelHandlerable, ViewProt } //only enabled if enabled and has actions - isUserInteractionEnabled = !viewModel.disabled && !actions.isEmpty + isUserInteractionEnabled = !disabled && !actions.isEmpty //set the attributed text attributedText = mutableText - } else { - text = viewModel.text } } // MARK: - Private Attributes - private func setStyleAttributes(viewModel: ModelType, attributedString: NSMutableAttributedString) { + private func setStyleAttributes(attributedString: NSMutableAttributedString) { //get the range let entireRange = NSRange(location: 0, length: attributedString.length) //set letterSpacing - if viewModel.typograpicalStyle.letterSpacing > 0.0 { - attributedString.addAttribute(.kern, value: viewModel.typograpicalStyle.letterSpacing, range: entireRange) + if typograpicalStyle.letterSpacing > 0.0 { + attributedString.addAttribute(.kern, value: typograpicalStyle.letterSpacing, range: entireRange) } //set lineHeight - if viewModel.typograpicalStyle.lineHeight > 0.0 { - let lineHeight = viewModel.typograpicalStyle.lineHeight + if typograpicalStyle.lineHeight > 0.0 { + let lineHeight = typograpicalStyle.lineHeight let adjustment = lineHeight > font.lineHeight ? 2.0 : 1.0 let baselineOffset = (lineHeight - font.lineHeight) / 2.0 / adjustment let paragraph = NSMutableParagraphStyle().with { $0.maximumLineHeight = lineHeight $0.minimumLineHeight = lineHeight - $0.alignment = viewModel.textPosition.textAlignment + $0.alignment = textPosition.textAlignment $0.lineBreakMode = lineBreakMode } attributedString.addAttribute(.baselineOffset, value: baselineOffset, range: entireRange) attributedString.addAttribute( .paragraphStyle, value: paragraph, range: entireRange) - } else if viewModel.textPosition != .left { + } else if textPosition != .left { let paragraph = NSMutableParagraphStyle().with { - $0.alignment = viewModel.textPosition.textAlignment + $0.alignment = textPosition.textAlignment $0.lineBreakMode = lineBreakMode } attributedString.addAttribute( .paragraphStyle, value: paragraph, range: entireRange) diff --git a/VDS/Components/Label/LabelModel.swift b/VDS/Components/Label/LabelModel.swift deleted file mode 100644 index a8e8b659..00000000 --- a/VDS/Components/Label/LabelModel.swift +++ /dev/null @@ -1,40 +0,0 @@ -// -// VDSLabelModel.swift -// VDS -// -// Created by Matt Bruce on 7/28/22. -// - -import Foundation -import UIKit - -public protocol LabelModel: Modelable, Labelable, Equatable, AnyEquatable { - var text: String? { get set } - var attributes: [any LabelAttributeModel]? { get set } -} - -public struct DefaultLabelModel: LabelModel { - public static func == (lhs: DefaultLabelModel, rhs: DefaultLabelModel) -> Bool { - lhs.isEqual(rhs) - } - - public func isEqual(_ equatable: DefaultLabelModel) -> Bool { - return id == equatable.id - && attributes == equatable.attributes - && text == equatable.text - && surface == equatable.surface - && typograpicalStyle == equatable.typograpicalStyle - && textPosition == equatable.textPosition - && surface == equatable.surface - && disabled == equatable.disabled - } - - public var id = UUID() - public var text: String? - public var attributes: [any LabelAttributeModel]? - public var typograpicalStyle: TypographicalStyle = .BodySmall - public var textPosition: TextPosition = .left - public var surface: Surface = .light - public var disabled: Bool = false - public init(){} -} diff --git a/VDS/Components/RadioBox/RadioBox.swift b/VDS/Components/RadioBox/RadioBox.swift index a4e47b40..b5a3c252 100644 --- a/VDS/Components/RadioBox/RadioBox.swift +++ b/VDS/Components/RadioBox/RadioBox.swift @@ -11,9 +11,11 @@ import VDSColorTokens import VDSFormControlsTokens import Combine -public class RadioBox: RadioBoxBase{} +@objc(VDSRadioBox) +public class RadioBox: RadioBoxBase{} -public class SolorRadioBox: RadioBoxBase{ +@objc(VDSSoloRadioBox) +public class SoloRadioBox: RadioBoxBase{ public override func initialSetup() { super.initialSetup() @@ -24,8 +26,23 @@ public class SolorRadioBox: RadioBoxBase{ } } -open class RadioBoxBase: Control { +@objc(VDSRadioBoxBase) +open class RadioBoxBase: Control, BinaryColorable, Accessable, DataTrackable{ + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + required public init() { + super.init(frame: .zero) + } + public override init(frame: CGRect) { + super.init(frame: .zero) + } + + public required init?(coder: NSCoder) { + super.init(coder: coder) + } + //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- @@ -67,61 +84,42 @@ open class RadioBoxBase: Control { $0.translatesAutoresizingMaskIntoConstraints = false } }() - - //can't bind to @Proxy - open override var isSelected: Bool { - get { model.selected } - set { - if model.selected != newValue { - model.selected = newValue - } - } - } - @Proxy(\.model.text) - open var text: String + open var text: String = "Default Text" { didSet { didChange() }} - @Proxy(\.model.subText) - open var subText: String? + open var textAttributes: [any LabelAttributeModel]? { didSet { didChange() }} + + open var subText: String? { didSet { didChange() }} - @Proxy(\.model.subTextRight) - open var subTextRight: String? + open var subTextAttributes: [any LabelAttributeModel]? { didSet { didChange() }} - @Proxy(\.model.strikethrough) - open var strikethrough: Bool + open var subTextRight: String? { didSet { didChange() }} - @Proxy(\.model.inputId) - open var inputId: String? + open var subTextRightAttributes: [any LabelAttributeModel]? { didSet { didChange() }} - @Proxy(\.model.value) - open var value: AnyHashable? + open var strikethrough: Bool = false { didSet { didChange() }} + + open var inputId: String? { didSet { didChange() }} + + open var value: AnyHashable? { didSet { didChange() }} - @Proxy(\.model.dataAnalyticsTrack) - open var dataAnalyticsTrack: String? + open var dataAnalyticsTrack: String? { didSet { didChange() }} - @Proxy(\.model.dataClickStream) - open var dataClickStream: String? + open var dataClickStream: String? { didSet { didChange() }} - @Proxy(\.model.dataTrack) - open var dataTrack: String? + open var dataTrack: String? { didSet { didChange() }} - @Proxy(\.model.accessibilityHintEnabled) - open var accessibilityHintEnabled: String? + open var accessibilityHintEnabled: String? { didSet { didChange() }} - @Proxy(\.model.accessibilityHintDisabled) - open var accessibilityHintDisabled: String? + open var accessibilityHintDisabled: String? { didSet { didChange() }} - @Proxy(\.model.accessibilityValueEnabled) - open var accessibilityValueEnabled: String? + open var accessibilityValueEnabled: String? { didSet { didChange() }} - @Proxy(\.model.accessibilityValueDisabled) - open var accessibilityValueDisabled: String? + open var accessibilityValueDisabled: String? { didSet { didChange() }} - @Proxy(\.model.accessibilityLabelEnabled) - open var accessibilityLabelEnabled: String? + open var accessibilityLabelEnabled: String? { didSet { didChange() }} - @Proxy(\.model.accessibilityLabelDisabled) - open var accessibilityLabelDisabled: String? + open var accessibilityLabelDisabled: String? { didSet { didChange() }} //functions //-------------------------------------------------- @@ -153,7 +151,7 @@ open class RadioBoxBase: Control { selectorLeftLabelStackView.spacing = 4 selectorLeftLabelStackView.isHidden = false - updateSelector(model) + updateSelector() selectorView.topAnchor.constraint(equalTo: topAnchor).isActive = true selectorView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true @@ -167,23 +165,38 @@ open class RadioBoxBase: Control { } - func updateLabels(_ viewModel: ModelType) { + func updateLabels() { //add the stackview to hold the 2 labels //text label - textLabel.set(with: viewModel.textModel) - + textLabel.textPosition = .left + textLabel.typograpicalStyle = .BoldBodyLarge + textLabel.text = text + textLabel.surface = surface + textLabel.disabled = disabled + textLabel.attributes = textAttributes + //subText label - if let subTextModel = viewModel.subTextModel { - subTextLabel.set(with: subTextModel) + if let subText { + subTextLabel.textPosition = .left + subTextLabel.typograpicalStyle = .BodyLarge + subTextLabel.text = subText + subTextLabel.surface = surface + subTextLabel.disabled = disabled + subTextLabel.attributes = subTextAttributes subTextLabel.isHidden = false } else { subTextLabel.isHidden = true } //subTextRight label - if let subTextRightModel = viewModel.subTextRightModel { - subTextRightLabel.set(with: subTextRightModel) + if let subTextRight { + subTextRightLabel.textPosition = .right + subTextRightLabel.typograpicalStyle = .BodyLarge + subTextRightLabel.text = subTextRight + subTextRightLabel.surface = surface + subTextRightLabel.disabled = disabled + subTextRightLabel.attributes = subTextRightAttributes subTextRightLabel.isHidden = false } else { subTextRightLabel.isHidden = true @@ -192,7 +205,7 @@ open class RadioBoxBase: Control { public override func reset() { super.reset() - updateSelector(model) + updateSelector() setAccessibilityLabel() } @@ -206,14 +219,12 @@ open class RadioBoxBase: Control { //-------------------------------------------------- // MARK: - State //-------------------------------------------------- - open override func updateView(viewModel: ModelType) { - updateLabels(viewModel) - updateSelector(viewModel) + open override func updateView() { + updateLabels() + updateSelector() setAccessibilityHint() - setAccessibilityValue(viewModel.selected) - setAccessibilityLabel(viewModel.selected) - setNeedsLayout() - layoutIfNeeded() + setAccessibilityValue(isSelected) + setAccessibilityLabel(isSelected) } @@ -256,11 +267,11 @@ open class RadioBoxBase: Control { private var shapeLayer: CAShapeLayer? - open func updateSelector(_ viewModel: ModelType) { + open func updateSelector() { //get the colors - let backgroundColor = radioBoxBackgroundColorConfiguration.getColor(viewModel) - let borderColor = radioBoxBorderColorConfiguration.getColor(viewModel) - let borderWidth = viewModel.selected ? selectorBorderWidthSelected : selectorBorderWidth + let backgroundColor = radioBoxBackgroundColorConfiguration.getColor(self) + let borderColor = radioBoxBorderColorConfiguration.getColor(self) + let borderWidth = isSelected ? selectorBorderWidthSelected : selectorBorderWidth selectorView.backgroundColor = backgroundColor selectorView.layer.borderColor = borderColor.cgColor @@ -278,12 +289,12 @@ open class RadioBoxBase: Control { open override func draw(_ layer: CALayer, in ctx: CGContext) { - let borderColor = radioBoxBorderColorConfiguration.getColor(model) + let borderColor = radioBoxBorderColorConfiguration.getColor(self) shapeLayer?.removeFromSuperlayer() shapeLayer = nil - if model.strikethrough { + if strikethrough { let bounds = selectorView.bounds let length = max(bounds.size.height, bounds.size.width) guard length > 0.0, shapeLayer == nil else { return } diff --git a/VDS/Components/RadioBox/RadioBoxGroup.swift b/VDS/Components/RadioBox/RadioBoxGroup.swift index 74059739..f2c4022f 100644 --- a/VDS/Components/RadioBox/RadioBoxGroup.swift +++ b/VDS/Components/RadioBox/RadioBoxGroup.swift @@ -8,7 +8,8 @@ import Foundation import UIKit -public class RadioBoxGroup: RadioBoxGroupBase { +@objc(VDSRadioBoxGroup) +public class RadioBoxGroup: RadioBoxGroupBase { public override func didSelect(_ selectedControl: RadioBox) { let oldSelectedControl = selectorViews.filter { $0.isSelected == true }.first @@ -18,8 +19,26 @@ public class RadioBoxGroup: RadioBoxGroupBase>: SelectorGroupSelectedHandlerBase { - +public class RadioBoxGroupBase: SelectorGroupSelectedHandlerBase { + + //-------------------------------------------------- + // MARK: - Public Properties + //-------------------------------------------------- + public override var selectorViews: [HandlerType] { + didSet { + for selector in selectorViews { + if !mainStackView.arrangedSubviews.contains(selector) { + selector + .publisher(for: .touchUpInside) + .sink { [weak self] handler in + self?.didSelect(handler) + }.store(in: &subscribers) + mainStackView.addArrangedSubview(selector) + } + } + } + } + //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- @@ -68,31 +87,4 @@ public class RadioBoxGroupBase Bool { - lhs.isEqual(rhs) - } - - public func isEqual(_ equatable: DefaultRadioBoxModel) -> Bool { - return id == equatable.id - && selected == equatable.selected - && text == equatable.text - && textAttributes == equatable.textAttributes - && subText == equatable.subText - && subTextAttributes == equatable.subTextAttributes - && subTextRight == equatable.subTextRight - && subTextRightAttributes == equatable.subTextRightAttributes - && selectedAccentColor == equatable.selectedAccentColor - && strikethrough == equatable.strikethrough - - && inputId == equatable.inputId - && value == equatable.value - - && surface == equatable.surface - && disabled == equatable.disabled - - && dataAnalyticsTrack == equatable.dataAnalyticsTrack - && dataClickStream == equatable.dataClickStream - && dataTrack == equatable.dataTrack - && accessibilityHintEnabled == equatable.accessibilityHintEnabled - && accessibilityHintDisabled == equatable.accessibilityHintDisabled - && accessibilityValueEnabled == equatable.accessibilityValueEnabled - && accessibilityValueDisabled == equatable.accessibilityValueDisabled - && accessibilityLabelEnabled == equatable.accessibilityLabelEnabled - && accessibilityLabelDisabled == equatable.accessibilityLabelDisabled - } -} diff --git a/VDS/Components/RadioButton/RadioButton.swift b/VDS/Components/RadioButton/RadioButton.swift index 4a77cb48..e46a4e8c 100644 --- a/VDS/Components/RadioButton/RadioButton.swift +++ b/VDS/Components/RadioButton/RadioButton.swift @@ -10,7 +10,8 @@ import UIKit import VDSColorTokens import VDSFormControlsTokens -public class RadioButton: RadioButtonBase{ +@objc(VDSRadioButton) +public class RadioButton: RadioButtonBase { //for groups allows "toggle" open override func toggle() { //removed error @@ -21,7 +22,8 @@ public class RadioButton: RadioButtonBase{ } } -public class SoloRadioButton: RadioButtonBase{ +@objc(VDSSoloRadioButton) +public class SoloRadioButton: RadioButtonBase { public override func initialSetup() { super.initialSetup() publisher(for: .touchUpInside) @@ -31,11 +33,36 @@ public class SoloRadioButton: RadioButtonBase{ } } -open class RadioButtonBase: Control { +@objc(VDSRadioButtonBase) +open class RadioButtonBase: Control, Accessable, DataTrackable, BinaryColorable, Errorable { + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + required public init() { + super.init(frame: .zero) + } + public override init(frame: CGRect) { + super.init(frame: .zero) + } + + public required init?(coder: NSCoder) { + super.init(coder: coder) + } + //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- + private var shouldShowError: Bool { + guard showError && !disabled && errorText?.isEmpty == false else { return false } + return true + } + + private var shouldShowLabels: Bool { + guard labelText?.isEmpty == false || childText?.isEmpty == false else { return false } + return true + } + private var mainStackView: UIStackView = { return UIStackView().with { $0.translatesAutoresizingMaskIntoConstraints = false @@ -73,61 +100,40 @@ open class RadioButtonBase: Control { $0.translatesAutoresizingMaskIntoConstraints = false } }() - - //can't bind to @Proxy - open override var isSelected: Bool { - get { model.selected } - set { - if model.selected != newValue { - model.selected = newValue - } - } - } - @Proxy(\.model.labelText) - open var labelText: String? + open var labelText: String? { didSet { didChange() }} - @Proxy(\.model.childText) - open var childText: String? + open var labelTextAttributes: [any LabelAttributeModel]? { didSet { didChange() }} + + open var childText: String? { didSet { didChange() }} - @Proxy(\.model.showError) - open var showError: Bool - - @Proxy(\.model.errorText) - open var errorText: String? + open var childTextAttributes: [any LabelAttributeModel]? { didSet { didChange() }} - @Proxy(\.model.inputId) - open var inputId: String? + open var showError: Bool = false { didSet { didChange() }} + + open var errorText: String? { didSet { didChange() }} - @Proxy(\.model.value) - open var value: AnyHashable? - - @Proxy(\.model.dataAnalyticsTrack) - open var dataAnalyticsTrack: String? + open var inputId: String? { didSet { didChange() }} - @Proxy(\.model.dataClickStream) - open var dataClickStream: String? + open var value: AnyHashable? { didSet { didChange() }} + + open var dataAnalyticsTrack: String? { didSet { didChange() }} - @Proxy(\.model.dataTrack) - open var dataTrack: String? + open var dataClickStream: String? { didSet { didChange() }} - @Proxy(\.model.accessibilityHintEnabled) - open var accessibilityHintEnabled: String? + open var dataTrack: String? { didSet { didChange() }} + + open var accessibilityHintEnabled: String? { didSet { didChange() }} - @Proxy(\.model.accessibilityHintDisabled) - open var accessibilityHintDisabled: String? + open var accessibilityHintDisabled: String? { didSet { didChange() }} - @Proxy(\.model.accessibilityValueEnabled) - open var accessibilityValueEnabled: String? + open var accessibilityValueEnabled: String? { didSet { didChange() }} - @Proxy(\.model.accessibilityValueDisabled) - open var accessibilityValueDisabled: String? + open var accessibilityValueDisabled: String? { didSet { didChange() }} - @Proxy(\.model.accessibilityLabelEnabled) - open var accessibilityLabelEnabled: String? + open var accessibilityLabelEnabled: String? { didSet { didChange() }} - @Proxy(\.model.accessibilityLabelDisabled) - open var accessibilityLabelDisabled: String? + open var accessibilityLabelDisabled: String? { didSet { didChange() }} //-------------------------------------------------- // MARK: - Constraints @@ -167,7 +173,7 @@ open class RadioButtonBase: Control { selectorWidthConstraint = selectorView.widthAnchor.constraint(equalToConstant: selectorSize.width) selectorWidthConstraint?.isActive = true - updateSelector(model) + updateSelector() mainStackView.topAnchor.constraint(equalTo: topAnchor).isActive = true mainStackView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true @@ -176,22 +182,32 @@ open class RadioButtonBase: Control { } - func updateLabels(_ viewModel: ModelType) { + func updateLabels() { //deal with labels - if viewModel.shouldShowLabels { + if shouldShowLabels { //add the stackview to hold the 2 labels //top label - if let labelModel = viewModel.labelModel { - primaryLabel.set(with: labelModel) + if let labelText { + primaryLabel.textPosition = .left + primaryLabel.typograpicalStyle = .BoldBodyLarge + primaryLabel.text = labelText + primaryLabel.surface = surface + primaryLabel.disabled = disabled + primaryLabel.attributes = labelTextAttributes primaryLabel.isHidden = false } else { primaryLabel.isHidden = true } //bottom label - if let childModel = viewModel.childModel { - secondaryLabel.set(with: childModel) + if let childText { + secondaryLabel.textPosition = .left + secondaryLabel.typograpicalStyle = .BodyLarge + secondaryLabel.text = childText + secondaryLabel.surface = surface + secondaryLabel.disabled = disabled + secondaryLabel.attributes = childTextAttributes secondaryLabel.isHidden = false } else { secondaryLabel.isHidden = true @@ -207,8 +223,12 @@ open class RadioButtonBase: Control { } //either add/remove the error from the main stack - if let errorModel = model.errorModel, model.shouldShowError { - errorLabel.set(with: errorModel) + if let errorText, shouldShowError { + errorLabel.textPosition = .left + errorLabel.typograpicalStyle = .BodyMedium + errorLabel.text = errorText + errorLabel.surface = surface + errorLabel.disabled = disabled mainStackView.spacing = 8 errorLabel.isHidden = false } else { @@ -220,7 +240,7 @@ open class RadioButtonBase: Control { public override func reset() { super.reset() - updateSelector(model) + updateSelector() setAccessibilityLabel() } @@ -239,14 +259,12 @@ open class RadioButtonBase: Control { //-------------------------------------------------- // MARK: - State //-------------------------------------------------- - open override func updateView(viewModel: ModelType) { - updateLabels(viewModel) - updateSelector(viewModel) + open override func updateView() { + updateLabels() + updateSelector() setAccessibilityHint() - setAccessibilityValue(viewModel.selected) - setAccessibilityLabel(viewModel.selected) - setNeedsLayout() - layoutIfNeeded() + setAccessibilityValue(isSelected) + setAccessibilityLabel(isSelected) } //-------------------------------------------------- @@ -295,7 +313,7 @@ open class RadioButtonBase: Control { radioButtonSize } - open func updateSelector(_ viewModel: ModelType) { + open func updateSelector() { if let shapeLayer = shapeLayer, let sublayers = layer.sublayers, sublayers.contains(shapeLayer) { shapeLayer.removeFromSuperlayer() @@ -307,9 +325,9 @@ open class RadioButtonBase: Control { guard length > 0.0, shapeLayer == nil else { return } //get the colors - let backgroundColor = radioButtonBackgroundColorConfiguration.getColor(viewModel) - let borderColor = radioButtonBorderColorConfiguration.getColor(viewModel) - let radioSelectedColor = radioButtonCheckColorConfiguration.getColor(viewModel) + let backgroundColor = radioButtonBackgroundColorConfiguration.getColor(self) + let borderColor = radioButtonBorderColorConfiguration.getColor(self) + let radioSelectedColor = radioButtonCheckColorConfiguration.getColor(self) selectorView.backgroundColor = backgroundColor selectorView.layer.borderColor = borderColor.cgColor diff --git a/VDS/Components/RadioButton/RadioButtonGroup.swift b/VDS/Components/RadioButton/RadioButtonGroup.swift index 7d48e404..1b357102 100644 --- a/VDS/Components/RadioButton/RadioButtonGroup.swift +++ b/VDS/Components/RadioButton/RadioButtonGroup.swift @@ -8,11 +8,11 @@ import Foundation import UIKit -public class RadioButtonGroup: RadioButtonGroupBase { +@objc(VDSRadioButtonGroup) +public class RadioButtonGroup: RadioButtonGroupBase { public override func didSelect(_ selectedControl: RadioButton) { - let oldSelectedControl = selectorViews.filter { $0.isSelected == true }.first - oldSelectedControl?.toggle() + selectedHandler?.toggle() selectedControl.toggle() if showError { showError = false @@ -21,22 +21,38 @@ public class RadioButtonGroup: RadioButtonGroupBase>: SelectorGroupSelectedHandlerBase { +public class RadioButtonGroupBase: SelectorGroupSelectedHandlerBase { //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- + public override var selectorViews: [HandlerType] { + didSet { + for selector in selectorViews { + if !mainStackView.arrangedSubviews.contains(selector) { + selector + .publisher(for: .touchUpInside) + .sink { [weak self] handler in + self?.didSelect(handler) + }.store(in: &subscribers) + mainStackView.addArrangedSubview(selector) + } + } + } + } + + private var _showError: Bool = false public var showError: Bool { - get { model.showError } + get { _showError } set { var newShowError = newValue - if selectedModel != nil, newShowError { + if selectedHandler != nil, newShowError { newShowError = false } selectorViews.forEach { handler in handler.showError = newShowError } - model.showError = newShowError + _showError = newShowError } } @@ -68,30 +84,4 @@ public class RadioButtonGroupBase Bool { - lhs.isEqual(rhs) - } - - public func isEqual(_ equatable: DefaultRadioButtonModel) -> Bool { - return id == equatable.id - && selected == equatable.selected - - && labelText == equatable.labelText - && labelTextAttributes == equatable.labelTextAttributes - && childText == equatable.childText - && childTextAttributes == equatable.childTextAttributes - - && showError == equatable.showError - && errorText == equatable.errorText - - && inputId == equatable.inputId - && value == equatable.value - - && surface == equatable.surface - && disabled == equatable.disabled - - && dataAnalyticsTrack == equatable.dataAnalyticsTrack - && dataClickStream == equatable.dataClickStream - && dataTrack == equatable.dataTrack - && accessibilityHintEnabled == equatable.accessibilityHintEnabled - && accessibilityHintDisabled == equatable.accessibilityHintDisabled - && accessibilityValueEnabled == equatable.accessibilityValueEnabled - && accessibilityValueDisabled == equatable.accessibilityValueDisabled - && accessibilityLabelEnabled == equatable.accessibilityLabelEnabled - && accessibilityLabelDisabled == equatable.accessibilityLabelDisabled - } -} diff --git a/VDS/Components/RadioSwatch/RadioSwatch.swift b/VDS/Components/RadioSwatch/RadioSwatch.swift index c53e7079..f548f18d 100644 --- a/VDS/Components/RadioSwatch/RadioSwatch.swift +++ b/VDS/Components/RadioSwatch/RadioSwatch.swift @@ -11,7 +11,8 @@ import VDSColorTokens import VDSFormControlsTokens import Combine -public class RadioSwatch: RadioSwatchBase{ +@objc(VDSRadioSwatch) +public class RadioSwatch: RadioSwatchBase{ public override func initialSetup() { super.initialSetup() publisher(for: .touchUpInside) @@ -21,8 +22,24 @@ public class RadioSwatch: RadioSwatchBase{ } } -open class RadioSwatchBase: Control { - +@objc(VDSRadioSwatchBase) +open class RadioSwatchBase: Control, Accessable, DataTrackable, BinaryColorable { + + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + required public init() { + super.init(frame: .zero) + } + + public override init(frame: CGRect) { + super.init(frame: .zero) + } + + public required init?(coder: NSCoder) { + super.init(coder: coder) + } + //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- @@ -38,55 +55,38 @@ open class RadioSwatchBase: Control { $0.contentMode = .scaleAspectFit } }() + + open var fillImage: UIImage? { didSet { didChange() }} + + open var text: String = "" { didSet { didChange() }} + + open var primaryColor: UIColor? { didSet { didChange() }} + + open var secondaryColor: UIColor? { didSet { didChange() }} + + open var strikethrough: Bool = false { didSet { didChange() }} - //can't bind to @Proxy - open override var isSelected: Bool { - get { model.selected } - set { - if model.selected != newValue { - model.selected = newValue - } - } - } - - @Proxy(\.model.text) - open var text: String - - @Proxy(\.model.strikethrough) - open var strikethrough: Bool + open var inputId: String? { didSet { didChange() }} - @Proxy(\.model.inputId) - open var inputId: String? + open var value: AnyHashable? { didSet { didChange() }} + + open var dataAnalyticsTrack: String? { didSet { didChange() }} - @Proxy(\.model.value) - open var value: AnyHashable? - - @Proxy(\.model.dataAnalyticsTrack) - open var dataAnalyticsTrack: String? + open var dataClickStream: String? { didSet { didChange() }} - @Proxy(\.model.dataClickStream) - open var dataClickStream: String? + open var dataTrack: String? { didSet { didChange() }} - @Proxy(\.model.dataTrack) - open var dataTrack: String? - - @Proxy(\.model.accessibilityHintEnabled) - open var accessibilityHintEnabled: String? + open var accessibilityHintEnabled: String? { didSet { didChange() }} - @Proxy(\.model.accessibilityHintDisabled) - open var accessibilityHintDisabled: String? + open var accessibilityHintDisabled: String? { didSet { didChange() }} - @Proxy(\.model.accessibilityValueEnabled) - open var accessibilityValueEnabled: String? + open var accessibilityValueEnabled: String? { didSet { didChange() }} - @Proxy(\.model.accessibilityValueDisabled) - open var accessibilityValueDisabled: String? + open var accessibilityValueDisabled: String? { didSet { didChange() }} - @Proxy(\.model.accessibilityLabelEnabled) - open var accessibilityLabelEnabled: String? + open var accessibilityLabelEnabled: String? { didSet { didChange() }} - @Proxy(\.model.accessibilityLabelDisabled) - open var accessibilityLabelDisabled: String? + open var accessibilityLabelDisabled: String? { didSet { didChange() }} //functions //-------------------------------------------------- @@ -106,9 +106,7 @@ open class RadioSwatchBase: Control { addSubview(selectorView) selectorView.addSubview(fillView) - - updateSelector(model) - + selectorView.topAnchor.constraint(equalTo: topAnchor).isActive = true selectorView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true selectorView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true @@ -128,7 +126,7 @@ open class RadioSwatchBase: Control { public override func reset() { super.reset() - updateSelector(model) + setNeedsDisplay() setAccessibilityLabel() } @@ -140,15 +138,11 @@ open class RadioSwatchBase: Control { //-------------------------------------------------- // MARK: - State //-------------------------------------------------- - open override func updateView(viewModel: ModelType) { - let enabled = !viewModel.disabled - - updateSelector(viewModel) + open override func updateView() { setAccessibilityHint() - setAccessibilityValue(viewModel.selected) - setAccessibilityLabel(viewModel.selected) - setNeedsLayout() - layoutIfNeeded() + setAccessibilityValue(isSelected) + setAccessibilityLabel(isSelected) + setNeedsDisplay() } //-------------------------------------------------- @@ -201,49 +195,6 @@ open class RadioSwatchBase: Control { return swatchSize } - open func updateSelector(_ viewModel: ModelType) { - //get the colors - let backgroundColor = radioSwatchBackgroundColorConfiguration.getColor(viewModel) - let borderColor = viewModel.selected ? radioSwatchBorderColorConfiguration.getColor(viewModel) : .clear - let fillBorderColor = radioSwatchFillBorderColorConfiguration.getColor(viewModel) - selectorView.backgroundColor = backgroundColor - selectorView.layer.borderColor = borderColor.cgColor - selectorView.layer.cornerRadius = selectorView.bounds.width * 0.5 - selectorView.layer.borderWidth = viewModel.selected ? selectorBorderWidth : 0 - selectorView.layer.masksToBounds = true - - gradientLayer?.removeFromSuperlayer() - gradientLayer = nil - - var fillColorBackground: UIColor = .clear - - if let fillImage = viewModel.fillImage { - fillView.image = viewModel.disabled ? fillImage.image(alpha: disabledAlpha) : fillImage - - } else { - fillView.image = nil - if let primary = viewModel.primaryColor, let secondary = viewModel.secondaryColor { - let firstColor = viewModel.disabled ? primary.withAlphaComponent(disabledAlpha) : primary - let secondColor = viewModel.disabled ? secondary.withAlphaComponent(disabledAlpha) : secondary - let gradient = CAGradientLayer() - gradientLayer = gradient - gradient.frame = fillView.bounds - gradient.colors = [secondColor.cgColor, secondColor.cgColor, firstColor.cgColor, firstColor.cgColor] - gradient.locations = [NSNumber(value: 0.0), NSNumber(value: 0.5), NSNumber(value: 0.5), NSNumber(value: 1.0)] - gradient.transform = CATransform3DMakeRotation(135.0 / 180.0 * .pi, 0.0, 0.0, 1.0) - fillView.layer.addSublayer(gradient) - } else { - fillColorBackground = viewModel.primaryColor ?? .white - } - } - fillView.backgroundColor = viewModel.disabled ? fillColorBackground.withAlphaComponent(disabledAlpha) : fillColorBackground - fillView.layer.borderColor = fillBorderColor.cgColor - fillView.layer.cornerRadius = fillView.bounds.width * 0.5 - fillView.layer.borderWidth = selectorBorderWidth - fillView.layer.masksToBounds = true - setNeedsDisplay() - } - open override func layoutSubviews() { super.layoutSubviews() // Accounts for any size changes @@ -252,12 +203,51 @@ open class RadioSwatchBase: Control { open override func draw(_ layer: CALayer, in ctx: CGContext) { - let borderColor = radioSwatchBorderColorConfiguration.getColor(model) + let backgroundColor = radioSwatchBackgroundColorConfiguration.getColor(self) + let borderColor = isSelected ? radioSwatchBorderColorConfiguration.getColor(self) : .clear + let fillBorderColor = radioSwatchFillBorderColorConfiguration.getColor(self) + selectorView.backgroundColor = backgroundColor + selectorView.layer.borderColor = borderColor.cgColor + selectorView.layer.cornerRadius = selectorView.bounds.width * 0.5 + selectorView.layer.borderWidth = isSelected ? selectorBorderWidth : 0 + selectorView.layer.masksToBounds = true + gradientLayer?.removeFromSuperlayer() + gradientLayer = nil + + var fillColorBackground: UIColor = .clear + + if let fillImage { + fillView.image = disabled ? fillImage.image(alpha: disabledAlpha) : fillImage + + } else { + fillView.image = nil + if let primary = primaryColor, let secondary = secondaryColor { + let firstColor = disabled ? primary.withAlphaComponent(disabledAlpha) : primary + let secondColor = disabled ? secondary.withAlphaComponent(disabledAlpha) : secondary + let gradient = CAGradientLayer() + gradientLayer = gradient + gradient.frame = fillView.bounds + gradient.colors = [secondColor.cgColor, secondColor.cgColor, firstColor.cgColor, firstColor.cgColor] + gradient.locations = [NSNumber(value: 0.0), NSNumber(value: 0.5), NSNumber(value: 0.5), NSNumber(value: 1.0)] + gradient.transform = CATransform3DMakeRotation(135.0 / 180.0 * .pi, 0.0, 0.0, 1.0) + fillView.layer.addSublayer(gradient) + } else { + fillColorBackground = primaryColor ?? .white + } + } + + fillView.backgroundColor = disabled ? fillColorBackground.withAlphaComponent(disabledAlpha) : fillColorBackground + fillView.layer.borderColor = fillBorderColor.cgColor + fillView.layer.cornerRadius = fillView.bounds.width * 0.5 + fillView.layer.borderWidth = selectorBorderWidth + fillView.layer.masksToBounds = true + shapeLayer?.removeFromSuperlayer() shapeLayer = nil - if model.strikethrough { + if strikethrough { + let strikeThroughBorderColor = radioSwatchBorderColorConfiguration.getColor(self) let bounds = selectorView.bounds let length = max(bounds.size.height, bounds.size.width) guard length > 0.0, shapeLayer == nil else { return } @@ -267,7 +257,7 @@ open class RadioSwatchBase: Control { strikeThrough.fillColor = nil strikeThrough.opacity = 1.0 strikeThrough.lineWidth = strikeThroughLineThickness - strikeThrough.strokeColor = borderColor.cgColor + strikeThrough.strokeColor = strikeThroughBorderColor.cgColor let linePath = UIBezierPath() linePath.move(to: CGPoint(x: 0, y: bounds.height)) diff --git a/VDS/Components/RadioSwatch/RadioSwatchGroup.swift b/VDS/Components/RadioSwatch/RadioSwatchGroup.swift index 1769a89a..5132ba7a 100644 --- a/VDS/Components/RadioSwatch/RadioSwatchGroup.swift +++ b/VDS/Components/RadioSwatch/RadioSwatchGroup.swift @@ -9,29 +9,29 @@ import Foundation import UIKit import Combine -public class RadioSwatchGroup: RadioSwatchGroupBase { +@objc(VDSRadioSwatchGroup) +public class RadioSwatchGroup: RadioSwatchGroupBase { public override func didSelect(selector: RadioSwatch) { - if let index = model.selectors.firstIndex(where: {$0.selected == true }), - let cell = collectionView.cellForItem(at: IndexPath(item: index, section: 0)) as? CollectionViewCell { - cell.modelHandler.toggle() - } + selectedHandler?.toggle() selector.toggle() - label.text = selector.model.text + label.text = selector.text valueChanged() } } -public class RadioSwatchGroupBase>: Control, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, UICollectionViewDelegate { +public class RadioSwatchGroupBase: SelectorGroupSelectedHandlerBase, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, UICollectionViewDelegate { //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- - public var selectedModel: ModelHandlerType.ModelType? { - return model.selectedModel + public override var selectorViews: [HandlerType] { + didSet { + collectionView.reloadData() + } } - + //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- @@ -55,7 +55,7 @@ public class RadioSwatchGroupBase.self, forCellWithReuseIdentifier: "collectionViewCell") + $0.register(UICollectionViewCell.self, forCellWithReuseIdentifier: "collectionViewCell") } }() @@ -64,13 +64,19 @@ public class RadioSwatchGroupBase 0 else { + guard selectorViews.count > 0 else { collectionViewHeight?.constant = 0 return } // Calculate the height let swatchesInRow = floor(CGFloat(collectionView.bounds.width/(cellSize + itemSpacing))) - let numberOfRows = ceil(CGFloat(swatches.count)/swatchesInRow) + let numberOfRows = ceil(CGFloat(selectorViews.count)/swatchesInRow) let height = (numberOfRows * cellSize) + (itemSpacing * (numberOfRows-1)) collectionViewHeight?.constant = CGFloat(height) @@ -130,29 +134,13 @@ public class RadioSwatchGroupBase Bool { - return !model.selectors[indexPath.row].disabled + return !selectorViews[indexPath.row].disabled } open func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { - guard let cell = collectionView.cellForItem(at: indexPath) as? CollectionViewCell else { return } - didSelect(selector: cell.modelHandler) + didSelect(selector: selectorViews[indexPath.row]) } //-------------------------------------------------- @@ -182,53 +169,25 @@ public class RadioSwatchGroupBase Int { - return model.selectors.count + return selectorViews.count } var cellsubs: [Int: AnyCancellable] = [:] - + public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { - guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionViewCell", for: indexPath) as? CollectionViewCell else { return UICollectionViewCell() } - - let model = model.selectors[indexPath.row] - cell.modelHandler.isUserInteractionEnabled = false - - //cancel if sub exists - if let sub = cellsubs[indexPath.row] { - sub.cancel() - cellsubs[indexPath.row] = nil - } - - let sub = cell.modelHandler - .handlerPublisher() - .sink { [weak self] changed in - if cell.modelHandler.shouldUpdateView(viewModel: model) { - print("Model Change: \(changed)") - self?.replace(viewModel: changed) - } - } - cellsubs[indexPath.row] = sub - - cell.set(with: model) + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "collectionViewCell", for: indexPath) + let handler = selectorViews[indexPath.row] + handler.isUserInteractionEnabled = false + cell.subviews.forEach { $0.removeFromSuperview() } + cell.addSubview(handler) + handler.topAnchor.constraint(equalTo: cell.topAnchor).isActive = true + handler.leadingAnchor.constraint(equalTo: cell.leadingAnchor).isActive = true + handler.trailingAnchor.constraint(equalTo: cell.trailingAnchor).isActive = true + handler.bottomAnchor.constraint(equalTo: cell.bottomAnchor).isActive = true return cell - } - open func didSelect(selector: ModelHandlerType) { + open func didSelect(selector: HandlerType) { fatalError("Must override didSelect") } - - public func valueChanged() { - DispatchQueue.main.asyncAfter(deadline: .now() + Constants.ModelStateDebounce) { [weak self] in - self?.sendActions(for: .valueChanged) - } - } - - public var selectedModelHandler: ModelHandlerType? { - guard let index = model.selectors.firstIndex(where: {$0.selected == true }), - let cell = collectionView.cellForItem(at: IndexPath(item: index, section: 0)) as? CollectionViewCell else { - return nil - } - return cell.modelHandler - } } diff --git a/VDS/Components/RadioSwatch/RadioSwatchGroupModel.swift b/VDS/Components/RadioSwatch/RadioSwatchGroupModel.swift deleted file mode 100644 index 4aeb9628..00000000 --- a/VDS/Components/RadioSwatch/RadioSwatchGroupModel.swift +++ /dev/null @@ -1,36 +0,0 @@ -// -// RadioSwatchGroupModel.swift -// VDS -// -// Created by Matt Bruce on 8/25/22. -// - -import Foundation - -public protocol RadioSwatchGroupModel: SelectorGroupSelectedModelable, Equatable where SelectorModelType: RadioSwatchModel { } - -extension RadioSwatchGroupModel { - public var labelModel: DefaultLabelModel { - var model = DefaultLabelModel() - model.textPosition = .left - model.typograpicalStyle = .BodySmall - model.text = selectedModel?.text ?? " " - model.surface = surface - model.disabled = disabled - return model - } -} - -public struct DefaultRadioSwatchGroupModel: RadioSwatchGroupModel { - public typealias SelectorModelType = DefaultRadioSwatchModel - public var id = UUID() - public var inputId: String? - public var value: AnyHashable? - public var surface: Surface = .light - public var disabled: Bool = false - public var selectors: [SelectorModelType] - public init() { selectors = [] } - public init(selectors: [SelectorModelType]){ - self.selectors = selectors - } -} diff --git a/VDS/Components/RadioSwatch/RadioSwatchModel.swift b/VDS/Components/RadioSwatch/RadioSwatchModel.swift deleted file mode 100644 index c70e6e43..00000000 --- a/VDS/Components/RadioSwatch/RadioSwatchModel.swift +++ /dev/null @@ -1,47 +0,0 @@ -// -// RadioSwatchModel.swift -// VDS -// -// Created by Matt Bruce on 8/25/22. -// - -import Foundation -import UIKit - -public protocol RadioSwatchModel: Modelable, FormFieldable, DataTrackable, Accessable, Selectable, BinaryColorable, Equatable { - var fillImage: UIImage? { get set } - var primaryColor: UIColor? { get set } - var secondaryColor: UIColor? { get set } - var text: String { get set } - var strikethrough: Bool { get set } -} - -public struct DefaultRadioSwatchModel: RadioSwatchModel { - public var id = UUID() - public var selected: Bool = false - - public var fillImage: UIImage? - public var primaryColor: UIColor? - public var secondaryColor: UIColor? - public var text: String = "" - public var strikethrough: Bool = false - - public var inputId: String? - public var value: AnyHashable? - - public var surface: Surface = .light - public var disabled: Bool = false - - public var dataAnalyticsTrack: String? - public var dataClickStream: String? - public var dataTrack: String? - public var accessibilityHintEnabled: String? - public var accessibilityHintDisabled: String? - public var accessibilityValueEnabled: String? - public var accessibilityValueDisabled: String? - public var accessibilityLabelEnabled: String? - public var accessibilityLabelDisabled: String? - - public init() {} - -} diff --git a/VDS/Components/TextFields/EntryField/EntryField.swift b/VDS/Components/TextFields/EntryField/EntryField.swift index 936138ef..e9945c13 100644 --- a/VDS/Components/TextFields/EntryField/EntryField.swift +++ b/VDS/Components/TextFields/EntryField/EntryField.swift @@ -11,7 +11,25 @@ import VDSColorTokens import VDSFormControlsTokens import Combine -open class EntryField: Control { +public enum HelperTextPlacement: String, CaseIterable { + case bottom, right +} + +open class EntryField: Control { + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + required public init() { + super.init(frame: .zero) + } + + public override init(frame: CGRect) { + super.init(frame: .zero) + } + + public required init?(coder: NSCoder) { + super.init(coder: coder) + } //-------------------------------------------------- // MARK: - Private Properties @@ -75,46 +93,32 @@ open class EntryField: Control { //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- - @Proxy(\.model.labelText) open var labelText: String? - @Proxy(\.model.helperText) open var helperText: String? - @Proxy(\.model.showError) open var showError: Bool - @Proxy(\.model.errorText) open var errorText: String? - @Proxy(\.model.tooltipTitle) open var tooltipTitle: String? - @Proxy(\.model.tooltipContent) open var tooltipContent: String? - @Proxy(\.model.transparentBackground) open var transparentBackground: Bool - @Proxy(\.model.width) open var width: CGFloat? - @Proxy(\.model.maxLength) open var maxLength: Int? - @Proxy(\.model.inputId) open var inputId: String? - @Proxy(\.model.value) open var value: AnyHashable? - @Proxy(\.model.defaultVaue) open var defaultValue: AnyHashable? - @Proxy(\.model.required) open var required: Bool - @Proxy(\.model.readOnly) open var readOnly: Bool diff --git a/VDS/Components/TextFields/EntryField/EntryFieldModel.swift b/VDS/Components/TextFields/EntryField/EntryFieldModel.swift index 746ffda4..f1ce6e22 100644 --- a/VDS/Components/TextFields/EntryField/EntryFieldModel.swift +++ b/VDS/Components/TextFields/EntryField/EntryFieldModel.swift @@ -1,84 +1,84 @@ +//// +//// EntryFieldModel.swift +//// VDS +//// +//// Created by Matt Bruce on 10/3/22. +//// // -// EntryFieldModel.swift -// VDS +//import Foundation // -// Created by Matt Bruce on 10/3/22. +//public enum HelperTextPlacement: String, CaseIterable { +// case bottom, right +//} // - -import Foundation - -public enum HelperTextPlacement: String, CaseIterable { - case bottom, right -} - -public protocol EntryFieldModel: Modelable, FormFieldable, Errorable { - var defaultVaue: AnyHashable? { get set } - var required: Bool { get set } - var readOnly: Bool { get set } - var labelText: String? { get set } - var helperText: String? { get set } - var helperTextPlacement: HelperTextPlacement { get set } - var transparentBackground: Bool { get set } - var width: CGFloat? { get set } - var maxLength: Int? { get set } - var tooltipTitle: String? { get set } - var tooltipContent: String? { get set } -} - -extension EntryFieldModel { - - public var labelModel: DefaultLabelModel { - var model = DefaultLabelModel() - model.textPosition = .left - model.typograpicalStyle = .BodySmall - model.text = labelText - model.surface = surface - model.disabled = disabled - return model - } - - public var helperLabelModel: DefaultLabelModel? { - guard let helperText else { return nil } - var model = DefaultLabelModel() - model.textPosition = .left - model.typograpicalStyle = .BodySmall - model.text = helperText - model.surface = surface - model.disabled = disabled - return model - } - - public var errorLabelModel: DefaultLabelModel? { - guard let errorText else { return nil } - var model = DefaultLabelModel() - model.textPosition = .left - model.typograpicalStyle = .BodySmall - model.text = errorText - model.surface = surface - model.disabled = disabled - return model - } - - public var tooltipTitleModel: DefaultLabelModel? { - guard let tooltipTitle else { return nil } - var model = DefaultLabelModel() - model.textPosition = .left - model.typograpicalStyle = .BodySmall - model.text = tooltipTitle - model.surface = surface - model.disabled = disabled - return model - } - - public var tooltipContentModel: DefaultLabelModel? { - guard let tooltipContent else { return nil } - var model = DefaultLabelModel() - model.textPosition = .left - model.typograpicalStyle = .BodySmall - model.text = tooltipContent - model.surface = surface - model.disabled = disabled - return model - } - -} +//public protocol EntryFieldModel: Modelable, FormFieldable, Errorable { +// var defaultVaue: AnyHashable? { get set } +// var required: Bool { get set } +// var readOnly: Bool { get set } +// var labelText: String? { get set } +// var helperText: String? { get set } +// var helperTextPlacement: HelperTextPlacement { get set } +// var transparentBackground: Bool { get set } +// var width: CGFloat? { get set } +// var maxLength: Int? { get set } +// var tooltipTitle: String? { get set } +// var tooltipContent: String? { get set } +//} +// +//extension EntryFieldModel { +// +// public var labelModel: DefaultLabelModel { +// var model = DefaultLabelModel() +// model.textPosition = .left +// model.typograpicalStyle = .BodySmall +// model.text = labelText +// model.surface = surface +// model.disabled = disabled +// return model +// } +// +// public var helperLabelModel: DefaultLabelModel? { +// guard let helperText else { return nil } +// var model = DefaultLabelModel() +// model.textPosition = .left +// model.typograpicalStyle = .BodySmall +// model.text = helperText +// model.surface = surface +// model.disabled = disabled +// return model +// } +// +// public var errorLabelModel: DefaultLabelModel? { +// guard let errorText else { return nil } +// var model = DefaultLabelModel() +// model.textPosition = .left +// model.typograpicalStyle = .BodySmall +// model.text = errorText +// model.surface = surface +// model.disabled = disabled +// return model +// } +// +// public var tooltipTitleModel: DefaultLabelModel? { +// guard let tooltipTitle else { return nil } +// var model = DefaultLabelModel() +// model.textPosition = .left +// model.typograpicalStyle = .BodySmall +// model.text = tooltipTitle +// model.surface = surface +// model.disabled = disabled +// return model +// } +// +// public var tooltipContentModel: DefaultLabelModel? { +// guard let tooltipContent else { return nil } +// var model = DefaultLabelModel() +// model.textPosition = .left +// model.typograpicalStyle = .BodySmall +// model.text = tooltipContent +// model.surface = surface +// model.disabled = disabled +// return model +// } +// +//} diff --git a/VDS/Components/TextFields/TextEntryField/TextEntryField.swift b/VDS/Components/TextFields/TextEntryField/TextEntryField.swift index d2f49038..2c19fc33 100644 --- a/VDS/Components/TextFields/TextEntryField/TextEntryField.swift +++ b/VDS/Components/TextFields/TextEntryField/TextEntryField.swift @@ -11,10 +11,28 @@ import VDSColorTokens import VDSFormControlsTokens import Combine -public class TextEntryField: TextEntryFieldBase{} +public enum TextEntryFieldType: String, CaseIterable { + case text, number, calendar, inlineAction, password, creditCard, tel, date, securityCode +} -open class TextEntryFieldBase: EntryField { +public class TextEntryField: TextEntryFieldBase{} +open class TextEntryFieldBase: EntryField { + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + required public init() { + super.init(frame: .zero) + } + + public override init(frame: CGRect) { + super.init(frame: .zero) + } + + public required init?(coder: NSCoder) { + super.init(coder: coder) + } + //-------------------------------------------------- // MARK: - Private Properties //-------------------------------------------------- @@ -31,16 +49,12 @@ open class TextEntryFieldBase: EntryField{ +@objc(VDSToggle) +public class Toggle: ToggleBase{ public override func initialSetup() { super.initialSetup() publisher(for: .touchUpInside) @@ -28,7 +41,23 @@ public class Toggle: ToggleBase{ } } -open class ToggleBase: Control { +@objc(VDSToggleBase) +open class ToggleBase: Control, Accessable, DataTrackable, BinaryColorable { + + //-------------------------------------------------- + // MARK: - Initializers + //-------------------------------------------------- + required public init() { + super.init(frame: .zero) + } + + public override init(frame: CGRect) { + super.init(frame: .zero) + } + + public required init?(coder: NSCoder) { + super.init(coder: coder) + } //-------------------------------------------------- // MARK: - Private Properties @@ -86,136 +115,93 @@ open class ToggleBase: Control { $0.forFalse.disabled.darkColor = VDSColor.paletteGray44 } + private var typograpicalStyle: TypographicalStyle { + if textSize == .small { + if textWeight == .bold { + return .BoldBodySmall + } else { + return .BodySmall + } + } else { + if textWeight == .bold { + return .BoldBodyLarge + } else { + return .BodyLarge + } + } + } + //-------------------------------------------------- // MARK: - Public Properties //-------------------------------------------------- - @Proxy(\.model.on) - open var isOn: Bool + open var isOn: Bool = false { didSet { didChange() }} + + open var isAnimated: Bool = true { didSet { didChange() }} - @Proxy(\.model.showText) - public var showText: Bool { - didSet { - if oldValue != showText { - updateLabel(model) - } - } - } + open var showText: Bool = false { didSet { didChange() }} - @Proxy(\.model.onText) - public var onText: String + open var onText: String = "On" { didSet { didChange() }} - @Proxy(\.model.offText) - public var offText: String + open var offText: String = "Off" { didSet { didChange() }} - public var fontSize: TypographicalStyle.FontSize = .small{ - didSet { - updateTypography() - } - } + open var textSize: ToggleTextSize = .small { didSet { didChange() }} - public var isBold: Bool = false { - didSet { - updateTypography() - } - } + open var textWeight: ToggleTextWeight = .regular { didSet { didChange() }} - @Proxy(\.model.textPosition) - public var textPosition: TextPosition { - didSet { - if oldValue != textPosition { - updateLabel(model) - } - } - } + open var textPosition: ToggleTextPosition = .left { didSet { didChange() }} - @Proxy(\.model.inputId) - open var inputId: String? + open var inputId: String? { didSet { didChange() }} - @Proxy(\.model.value) - open var value: AnyHashable? + open var value: AnyHashable? { didSet { didChange() }} - @Proxy(\.model.dataAnalyticsTrack) - open var dataAnalyticsTrack: String? + open var dataAnalyticsTrack: String? { didSet { didChange() }} - @Proxy(\.model.dataClickStream) - open var dataClickStream: String? + open var dataClickStream: String? { didSet { didChange() }} - @Proxy(\.model.dataTrack) - open var dataTrack: String? + open var dataTrack: String? { didSet { didChange() }} - @Proxy(\.model.accessibilityHintEnabled) - open var accessibilityHintEnabled: String? + open var accessibilityHintEnabled: String? { didSet { didChange() }} - @Proxy(\.model.accessibilityHintDisabled) - open var accessibilityHintDisabled: String? + open var accessibilityHintDisabled: String? { didSet { didChange() }} - @Proxy(\.model.accessibilityValueEnabled) - open var accessibilityValueEnabled: String? + open var accessibilityValueEnabled: String? { didSet { didChange() }} - @Proxy(\.model.accessibilityValueDisabled) - open var accessibilityValueDisabled: String? + open var accessibilityValueDisabled: String? { didSet { didChange() }} - @Proxy(\.model.accessibilityLabelEnabled) - open var accessibilityLabelEnabled: String? + open var accessibilityLabelEnabled: String? { didSet { didChange() }} - @Proxy(\.model.accessibilityLabelDisabled) - open var accessibilityLabelDisabled: String? + open var accessibilityLabelDisabled: String? { didSet { didChange() }} - //only allows - //fontSize: small, large - //fontWeight: regular, bold - private func updateTypography() { - if fontSize == .small { - if isBold { - model.typograpicalStyle = .BoldBodySmall - } else { - model.typograpicalStyle = .BodySmall - } - } else { - if isBold { - model.typograpicalStyle = .BoldBodyLarge - } else { - model.typograpicalStyle = .BodyLarge - } - } - } - //-------------------------------------------------- // MARK: - Constraints //-------------------------------------------------- private var knobLeadingConstraint: NSLayoutConstraint? private var knobTrailingConstraint: NSLayoutConstraint? - private var knobHeightConstraint: NSLayoutConstraint? - private var knobWidthConstraint: NSLayoutConstraint? - private var toggleHeightConstraint: NSLayoutConstraint? - private var toggleWidthConstraint: NSLayoutConstraint? //functions //-------------------------------------------------- // MARK: - Toggle //-------------------------------------------------- - private func updateToggle(_ viewModel: ModelType) { - //private func - func constrainKnob(){ - self.knobLeadingConstraint?.isActive = false - self.knobTrailingConstraint?.isActive = false - if viewModel.on { - self.knobTrailingConstraint = self.toggleView.trailingAnchor.constraint(equalTo: self.knobView.trailingAnchor, constant: 2) - self.knobLeadingConstraint = self.knobView.leadingAnchor.constraint(greaterThanOrEqualTo: self.toggleView.leadingAnchor) - } else { - self.knobTrailingConstraint = self.toggleView.trailingAnchor.constraint(greaterThanOrEqualTo: self.knobView.trailingAnchor) - self.knobLeadingConstraint = self.knobView.leadingAnchor.constraint(equalTo: self.toggleView.leadingAnchor, constant: 2) - } - self.knobTrailingConstraint?.isActive = true - self.knobLeadingConstraint?.isActive = true - self.knobWidthConstraint?.constant = self.knobSize.width - self.layoutIfNeeded() + private func constrainKnob(){ + self.knobLeadingConstraint?.isActive = false + self.knobTrailingConstraint?.isActive = false + if isOn { + knobTrailingConstraint = toggleView.trailingAnchor.constraint(equalTo: knobView.trailingAnchor, constant: 2) + knobLeadingConstraint = knobView.leadingAnchor.constraint(greaterThanOrEqualTo: toggleView.leadingAnchor) + } else { + knobTrailingConstraint = toggleView.trailingAnchor.constraint(greaterThanOrEqualTo: knobView.trailingAnchor) + knobLeadingConstraint = knobView.leadingAnchor.constraint(equalTo: toggleView.leadingAnchor, constant: 2) } + knobTrailingConstraint?.isActive = true + knobLeadingConstraint?.isActive = true + self.layoutIfNeeded() + } + + private func updateToggle() { + let toggleColor = toggleColorConfiguration.getColor(self) + let knobColor = knobColorConfiguration.getColor(self) - let toggleColor = toggleColorConfiguration.getColor(viewModel) - let knobColor = knobColorConfiguration.getColor(viewModel) - - if viewModel.disabled { + if disabled || !isAnimated { toggleView.backgroundColor = toggleColor knobView.backgroundColor = knobColor constrainKnob() @@ -225,8 +211,8 @@ open class ToggleBase: Control { self.knobView.backgroundColor = knobColor }, completion: nil) - UIView.animate(withDuration: 0.33, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.5, options: [], animations: { - constrainKnob() + UIView.animate(withDuration: 0.33, delay: 0, usingSpringWithDamping: 0.7, initialSpringVelocity: 0.5, options: [], animations: { [weak self] in + self?.constrainKnob() }, completion: nil) } } @@ -234,8 +220,8 @@ open class ToggleBase: Control { //-------------------------------------------------- // MARK: - Labels //-------------------------------------------------- - private func updateLabel(_ viewModel: ModelType) { - let showText = viewModel.showText + private func updateLabel() { + stackView.spacing = showText ? 12 : 0 if stackView.subviews.contains(label) { @@ -243,6 +229,12 @@ open class ToggleBase: Control { } if showText { + label.textPosition = textPosition == .left ? .left : .right + label.typograpicalStyle = typograpicalStyle + label.text = isOn ? onText : offText + label.surface = surface + label.disabled = disabled + if textPosition == .left { stackView.insertArrangedSubview(label, at: 0) } else { @@ -265,75 +257,68 @@ open class ToggleBase: Control { isAccessibilityElement = true accessibilityTraits = .button addSubview(stackView) - - //create the wrapping view + + //set the h/w to container size, since the width "can" grow if text is there + //allow this to be greaterThanEqualTo + heightAnchor.constraint(equalToConstant: toggleContainerSize.height).isActive = true + widthAnchor.constraint(greaterThanOrEqualToConstant: toggleContainerSize.width).isActive = true + + //create the container for the toggle/knob let toggleContainerView = UIView() toggleContainerView.translatesAutoresizingMaskIntoConstraints = false toggleContainerView.backgroundColor = .clear toggleContainerView.widthAnchor.constraint(equalToConstant: toggleContainerSize.width).isActive = true toggleContainerView.heightAnchor.constraint(equalToConstant: toggleContainerSize.height).isActive = true - toggleHeightConstraint = toggleView.heightAnchor.constraint(equalToConstant: toggleSize.height) - toggleHeightConstraint?.isActive = true - - toggleWidthConstraint = toggleView.widthAnchor.constraint(equalToConstant: toggleSize.width) - toggleWidthConstraint?.isActive = true - - toggleView.layer.cornerRadius = toggleSize.height / 2.0 - knobView.layer.cornerRadius = knobSize.height / 2.0 - - toggleView.backgroundColor = toggleColorConfiguration.getColor(model) - + //adding views toggleContainerView.addSubview(toggleView) toggleView.addSubview(knobView) - - - knobHeightConstraint = knobView.heightAnchor.constraint(equalToConstant: knobSize.height) - knobHeightConstraint?.isActive = true - knobWidthConstraint = knobView.widthAnchor.constraint(equalToConstant: knobSize.width) - knobWidthConstraint?.isActive = true + stackView.addArrangedSubview(toggleContainerView) + + //adding constraints + toggleView.heightAnchor.constraint(equalToConstant: toggleSize.height).isActive = true + toggleView.widthAnchor.constraint(equalToConstant: toggleSize.width).isActive = true + toggleView.layer.cornerRadius = toggleSize.height / 2.0 + toggleView.bottomAnchor.constraint(greaterThanOrEqualTo: knobView.bottomAnchor).isActive = true + toggleView.centerXAnchor.constraint(equalTo: toggleContainerView.centerXAnchor).isActive = true + toggleView.centerYAnchor.constraint(equalTo: toggleContainerView.centerYAnchor).isActive = true + + knobView.layer.cornerRadius = knobSize.height / 2.0 + knobView.heightAnchor.constraint(equalToConstant: knobSize.height).isActive = true + knobView.widthAnchor.constraint(equalToConstant: knobSize.width).isActive = true knobView.centerYAnchor.constraint(equalTo: toggleView.centerYAnchor).isActive = true knobView.topAnchor.constraint(greaterThanOrEqualTo: toggleView.topAnchor).isActive = true - - toggleView.bottomAnchor.constraint(greaterThanOrEqualTo: knobView.bottomAnchor).isActive = true - updateLabel(model) - stackView.addArrangedSubview(toggleContainerView) + //pin stackview to edges stackView.topAnchor.constraint(equalTo: topAnchor).isActive = true stackView.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true stackView.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true stackView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true - - toggleView.centerXAnchor.constraint(equalTo: toggleContainerView.centerXAnchor).isActive = true - toggleView.centerYAnchor.constraint(equalTo: toggleContainerView.centerYAnchor).isActive = true - } public override func reset() { super.reset() - toggleView.backgroundColor = toggleColorConfiguration.getColor(model) - knobView.backgroundColor = knobColorConfiguration.getColor(model) + toggleView.backgroundColor = toggleColorConfiguration.getColor(self) + knobView.backgroundColor = knobColorConfiguration.getColor(self) setAccessibilityLabel() } /// This will toggle the state of the Toggle and execute the actionBlock if provided. open func toggle() { isOn.toggle() + isSelected = isOn sendActions(for: .valueChanged) } //-------------------------------------------------- // MARK: - State //-------------------------------------------------- - open override func updateView(viewModel: ModelType) { - label.set(with: viewModel.label) - updateLabel(viewModel) - updateToggle(viewModel) + open override func updateView() { + updateLabel() + updateToggle() setAccessibilityHint() - setAccessibilityValue(viewModel.on) - setAccessibilityLabel(viewModel.on) - backgroundColor = viewModel.surface.color - setNeedsLayout() - layoutIfNeeded() + setAccessibilityValue(isOn) + setAccessibilityLabel(isOn) + backgroundColor = surface.color } } diff --git a/VDS/Components/Toggle/ToggleModel.swift b/VDS/Components/Toggle/ToggleModel.swift deleted file mode 100644 index 100b2e56..00000000 --- a/VDS/Components/Toggle/ToggleModel.swift +++ /dev/null @@ -1,59 +0,0 @@ -// -// ToggleModel.swift -// VDS -// -// Created by Matt Bruce on 7/22/22. -// - -import Foundation -import UIKit - -public protocol ToggleModel: Modelable, FormFieldable, DataTrackable, Accessable, Labelable, BinaryColorable { - var showText: Bool { get set } - var on: Bool { get set } - var offText: String { get set } - var onText: String { get set } -} - -extension ToggleModel { - public var userTrueColor: Bool { return on } - - public var label: DefaultLabelModel { - var model = DefaultLabelModel() - model.textPosition = textPosition - model.typograpicalStyle = typograpicalStyle - model.text = on ? onText : offText - model.surface = surface - model.disabled = disabled - return model - } -} - -public struct DefaultToggleModel: ToggleModel { - public var id = UUID() - public var on: Bool = false - public var showText: Bool = false - public var offText: String = "Off" - public var onText: String = "On" - - public var typograpicalStyle: TypographicalStyle = .BodySmall - public var textPosition: TextPosition = .left - - public var inputId: String? - public var value: AnyHashable? = true - - public var surface: Surface = .light - public var disabled: Bool = false - - public var dataAnalyticsTrack: String? - public var dataClickStream: String? - public var dataTrack: String? - public var accessibilityHintEnabled: String? - public var accessibilityHintDisabled: String? - public var accessibilityValueEnabled: String? - public var accessibilityValueDisabled: String? - public var accessibilityLabelEnabled: String? - public var accessibilityLabelDisabled: String? - - public init() { } -} diff --git a/VDS/PropertyWrappers/AnyProxy.swift b/VDS/PropertyWrappers/AnyProxy.swift deleted file mode 100644 index b504c36d..00000000 --- a/VDS/PropertyWrappers/AnyProxy.swift +++ /dev/null @@ -1,45 +0,0 @@ -// -// ProxPropertyWrapper.swift -// VDS -// -// Created by Matt Bruce on 7/29/22. -// - -import Foundation - -//https://www.swiftbysundell.com/articles/accessing-a-swift-property-wrappers-enclosing-instance/ -@propertyWrapper -public struct AnyProxy { - private let keyPath: ReferenceWritableKeyPath - - public init(_ keyPath: ReferenceWritableKeyPath) { - self.keyPath = keyPath - } - - @available(*, unavailable, message: "The wrapped value must be accessed from the enclosing instance property.") - public var wrappedValue: Value { - get { fatalError() } - set { fatalError() } - } - - public static subscript( - _enclosingInstance observed: EnclosingSelf, - wrapped wrappedKeyPath: ReferenceWritableKeyPath, - storage storageKeyPath: ReferenceWritableKeyPath - ) -> Value { - get { - let storageValue = observed[keyPath: storageKeyPath] - let value = observed[keyPath: storageValue.keyPath] - return value - } - set { - let storageValue = observed[keyPath: storageKeyPath] - observed[keyPath: storageValue.keyPath] = newValue - } - } -} - -extension NSObject: ProxyContainer {} -public protocol ProxyContainer { - typealias Proxy = AnyProxy -} diff --git a/VDS/PropertyWrappers/CodableColor.swift b/VDS/PropertyWrappers/CodableColor.swift deleted file mode 100644 index 3b046acc..00000000 --- a/VDS/PropertyWrappers/CodableColor.swift +++ /dev/null @@ -1,57 +0,0 @@ -// -// CodableColor.swift -// VDS -// -// Created by Matt Bruce on 8/26/22. -// - -import Foundation -import UIKit - -@propertyWrapper -public struct CodableColor { - public var wrappedValue: UIColor - - public init(wrappedValue: UIColor) { - self.wrappedValue = wrappedValue - } -} - -extension CodableColor: Codable { - public init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - let colorString = try container.decode(String.self) - wrappedValue = UIColor(hexString: colorString) - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - try container.encode(wrappedValue.hexString) - } -} - -@propertyWrapper -public struct OptionalCodableColor { - public var wrappedValue: UIColor? - - public init(wrappedValue: UIColor?) { - self.wrappedValue = wrappedValue - } -} - -extension OptionalCodableColor: Codable { - public init(from decoder: Decoder) throws { - let container = try decoder.singleValueContainer() - if container.decodeNil() { - wrappedValue = nil - } else { - let colorString = try container.decode(String.self) - wrappedValue = UIColor(hexString: colorString) - } - } - - public func encode(to encoder: Encoder) throws { - var container = encoder.singleValueContainer() - try container.encode(wrappedValue?.hexString) - } -} diff --git a/VDS/PropertyWrappers/Debuggable.swift b/VDS/PropertyWrappers/Debuggable.swift deleted file mode 100644 index 822f6182..00000000 --- a/VDS/PropertyWrappers/Debuggable.swift +++ /dev/null @@ -1,31 +0,0 @@ -// -// File.swift -// VDS -// -// Created by Matt Bruce on 8/11/22. -// - -import Foundation - -@propertyWrapper -public struct Debuggable { - private var value: Value - private let description: String - - public init(wrappedValue: Value, description: String = "") { - print("Initialized '\(description)' with value \(wrappedValue)") - self.value = wrappedValue - self.description = description - } - - public var wrappedValue: Value { - get { - print("Accessing '\(description)', returning: \(value)") - return value - } - set { - print("Updating '\(description)', newValue: \(newValue)") - value = newValue - } - } -} diff --git a/VDS/PropertyWrappers/UsesAutoLayout.swift b/VDS/PropertyWrappers/UsesAutoLayout.swift deleted file mode 100644 index 4556a291..00000000 --- a/VDS/PropertyWrappers/UsesAutoLayout.swift +++ /dev/null @@ -1,23 +0,0 @@ -// -// UseAutoLayout.swift -// VDS -// -// Created by Matt Bruce on 8/11/22. -// - -import Foundation -import UIKit - -@propertyWrapper -public struct UsesAutoLayout { - public var wrappedValue: T { - didSet { - wrappedValue.translatesAutoresizingMaskIntoConstraints = false - } - } - - public init(wrappedValue: T) { - self.wrappedValue = wrappedValue - wrappedValue.translatesAutoresizingMaskIntoConstraints = false - } -} diff --git a/VDS/Protocols/Accessable.swift b/VDS/Protocols/Accessable.swift index fd9ad370..75d44fca 100644 --- a/VDS/Protocols/Accessable.swift +++ b/VDS/Protocols/Accessable.swift @@ -18,37 +18,31 @@ public protocol Accessable { } //Configurations to set within the UIControl -extension ModelHandlerable where Self: UIView { - private var accessableModel: Accessable? { - guard let model = self.model as? Accessable else { - return nil - } - return model - } - +extension Handlerable where Self: UIView, Self: Accessable { + public func setAccessibilityHint(_ override: Bool? = nil) { - let check = override ?? !model.disabled - if let value = accessableModel?.accessibilityHintEnabled, check { + let check = override ?? !disabled + if let value = accessibilityHintEnabled, check { accessibilityHint = value - } else if let value = accessableModel?.accessibilityHintDisabled, !check { + } else if let value = accessibilityHintDisabled, !check { accessibilityHint = value } } public func setAccessibilityValue(_ override: Bool? = nil) { - let check = override ?? !model.disabled - if let value = accessableModel?.accessibilityValueEnabled, check { + let check = override ?? !disabled + if let value = accessibilityValueEnabled, check { accessibilityValue = value - } else if let value = accessableModel?.accessibilityValueDisabled, !check { + } else if let value = accessibilityValueDisabled, !check { accessibilityValue = value } } public func setAccessibilityLabel(_ override: Bool? = nil) { - let check = override ?? !model.disabled - if let value = accessableModel?.accessibilityLabelEnabled, check { + let check = override ?? !disabled + if let value = accessibilityLabelEnabled, check { accessibilityLabel = value - } else if let value = accessableModel?.accessibilityLabelDisabled, !check { + } else if let value = accessibilityLabelDisabled, !check { accessibilityLabel = value } } diff --git a/VDS/Protocols/Colorable.swift b/VDS/Protocols/Colorable.swift index 92a7f461..3c43bf1a 100644 --- a/VDS/Protocols/Colorable.swift +++ b/VDS/Protocols/Colorable.swift @@ -8,15 +8,15 @@ import Foundation import UIKit -public protocol Colorable { - associatedtype ModelType - func getColor(_ viewModel: ModelType) -> UIColor +public protocol Colorable { + associatedtype ObjectType + func getColor(_ object: ObjectType) -> UIColor } extension Colorable { - fileprivate func getColor(_ viewModel: Any) -> UIColor { - guard let model = viewModel as? ModelType else { - assertionFailure("Invalid ModelType, Expecting type \(ModelType.self), received \(viewModel) ") + fileprivate func getColor(_ object: Any) -> UIColor { + guard let model = object as? ObjectType else { + assertionFailure("Invalid ObjectType, Expecting type \(ObjectType.self), received \(object) ") return .black } return getColor(model) @@ -25,15 +25,15 @@ extension Colorable { public func eraseToAnyColorable() -> AnyColorable { AnyColorable(self) } } -public struct GenericColorable: Colorable, Withable { - private var wrapped: any Colorable +public struct GenericColorable: Colorable, Withable { + private var wrapped: any Colorable - public init(colorable: C) where C.ModelType == ModelType { + public init(colorable: C) where C.ObjectType == ObjectType { wrapped = colorable } - public func getColor(_ viewModel: ModelType) -> UIColor { - wrapped.getColor(viewModel) + public func getColor(_ object: ObjectType) -> UIColor { + wrapped.getColor(object) } } @@ -44,7 +44,7 @@ public struct AnyColorable: Colorable, Withable { wrapped = colorable } - public func getColor(_ viewModel: Any) -> UIColor { - wrapped.getColor(viewModel) + public func getColor(_ object: Any) -> UIColor { + wrapped.getColor(object) } } diff --git a/VDS/Protocols/Handlerable.swift b/VDS/Protocols/Handlerable.swift new file mode 100644 index 00000000..70c2508f --- /dev/null +++ b/VDS/Protocols/Handlerable.swift @@ -0,0 +1,33 @@ +// +// Handlerable.swift +// VDS +// +// Created by Matt Bruce on 7/22/22. +// + +import Foundation +import Combine +import UIKit + +public protocol Handlerable: AnyObject, Initable, Disabling, Surfaceable { + var subject: PassthroughSubject { get set } + var subscribers: Set { get set } + func updateView() +} + +extension Handlerable { + + public func handlerPublisher() -> AnyPublisher { + subject + .eraseToAnyPublisher() + .debounce(for: .seconds(Constants.StateDebounce), scheduler: RunLoop.main) + .eraseToAnyPublisher() + } +} + +extension Handlerable where Self: UIView { + public func didChange() { + subject.send() + setNeedsLayout() + } +} diff --git a/VDS/Protocols/Labelable.swift b/VDS/Protocols/Labelable.swift deleted file mode 100644 index f5ff76cc..00000000 --- a/VDS/Protocols/Labelable.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// LabelModelable.swift -// VDS -// -// Created by Matt Bruce on 7/28/22. -// - -import Foundation -import UIKit - -public protocol Labelable { - var typograpicalStyle: TypographicalStyle { get set } - var textPosition: TextPosition { get set } -} - -extension Labelable { - public var font: UIFont? { - return typograpicalStyle.font - } -} diff --git a/VDS/Protocols/ModelHandlerable.swift b/VDS/Protocols/ModelHandlerable.swift deleted file mode 100644 index 0b3c684e..00000000 --- a/VDS/Protocols/ModelHandlerable.swift +++ /dev/null @@ -1,53 +0,0 @@ -// -// Modelable.swift -// VDS -// -// Created by Matt Bruce on 7/22/22. -// - -import Foundation -import Combine -import UIKit - -public protocol ModelHandlerable: AnyObject, Initable { - associatedtype ModelType: Modelable - var model: ModelType { get set } - var modelPublisher: Published.Publisher { get } - var subscribers: Set { get set } - init(with model: ModelType) - func set(with model: ModelType) - func shouldUpdateView(viewModel: ModelType) -> Bool - func updateView(viewModel: ModelType) -} - -extension ModelHandlerable { - public init() { - self.init(with: ModelType()) - } - - public func set(with model: ModelType) { - if shouldUpdateView(viewModel: model){ - updateView(viewModel: model) - self.model = model - } - } - - public func shouldUpdateView(viewModel: ModelType) -> Bool { - model != viewModel - } - - public func setupUpdateView() { - handlerPublisher() - .subscribe(on: RunLoop.main) - .sink { [weak self] viewModel in - self?.updateView(viewModel: viewModel) - } - .store(in: &subscribers) - } - - public func handlerPublisher() -> AnyPublisher { - modelPublisher - .debounce(for: .seconds(Constants.ModelStateDebounce), scheduler: RunLoop.main) - .eraseToAnyPublisher() - } -} diff --git a/VDS/Protocols/Modelable.swift b/VDS/Protocols/Modelable.swift deleted file mode 100644 index 78c5ab6c..00000000 --- a/VDS/Protocols/Modelable.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// Modelable.swift -// VDS -// -// Created by Matt Bruce on 8/5/22. -// - -import Foundation - -public protocol Modelable: Surfaceable, Disabling, Initable, Withable, Identifiable, Equatable, CustomDebugStringConvertible where ID == UUID { - -} - -extension Modelable { - public var debugDescription: String { - return "\(id.uuidString)" - } -} diff --git a/VDS/Protocols/Selectable.swift b/VDS/Protocols/Selectable.swift deleted file mode 100644 index fa7777cd..00000000 --- a/VDS/Protocols/Selectable.swift +++ /dev/null @@ -1,12 +0,0 @@ -// -// Selectable.swift -// VDS -// -// Created by Matt Bruce on 8/23/22. -// - -import Foundation - -public protocol Selectable { - var selected: Bool { get set } -} diff --git a/VDS/Protocols/SelectorGroupModelable.swift b/VDS/Protocols/SelectorGroupModelable.swift deleted file mode 100644 index 4657b7c6..00000000 --- a/VDS/Protocols/SelectorGroupModelable.swift +++ /dev/null @@ -1,37 +0,0 @@ -// -// SelectorGroupModel.swift -// VDS -// -// Created by Matt Bruce on 8/11/22. -// - -import Foundation - -///MARK: Groups that allow anything selected -public protocol SelectorGroupModelable: Modelable, FormFieldable { - associatedtype SelectorModelType: Modelable where SelectorModelType: FormFieldable & Selectable - var selectors: [SelectorModelType] { get set } -} - -extension SelectorGroupModelable { - public var debugDescription: String { - "group id: \(id.uuidString)\r\(selectors.compactMap{"id: \($0.debugDescription) \($0.selected ? "*" : "")"}.joined(separator: "\r"))" - } -} - -///MARK: Groups that allow single selections -public protocol SelectorGroupSelectedModelable: SelectorGroupModelable { - var selectedModel: SelectorModelType? { get } -} - -extension SelectorGroupSelectedModelable { - public var selectedModel: SelectorModelType? { - if let index = selectors.firstIndex(where: { element in - return element.selected == true - }) { - return selectors[index] - } else { - return nil - } - } -}