Compare commits
68 Commits
Author | SHA1 | Date | |
---|---|---|---|
166026a766 | |||
ccda606af5 | |||
756de4f1de | |||
8442c89944 | |||
57cedd37bb | |||
09556ba6e0 | |||
dea57dc1e2 | |||
07575bd2bf | |||
8686ae974c | |||
be4c561ea8 | |||
2357a40fff | |||
d4afa9c9e9 | |||
76ef0a2d59 | |||
129eb135f1 | |||
4ad15fcded | |||
fb2ddb2227 | |||
27f86f5c4d | |||
209ba49e3f | |||
ba07005b13 | |||
6c3f3a8982 | |||
0d651b810f | |||
1d7fc76340 | |||
5d4e461933 | |||
55264d61ad | |||
d21ad9d1ea | |||
0bd6c3c2d4 | |||
eed20367b9 | |||
43b5111d79 | |||
2983093a9c | |||
b4bbaa3bfd | |||
498c8fa4ae | |||
2957da6233 | |||
d79af06c38 | |||
d8937f2de6 | |||
9b27f24197 | |||
1d58fd5510 | |||
f6c49bf626 | |||
f1b62d83c4 | |||
ee5055efa5 | |||
6f8e3b6664 | |||
1f2933950b | |||
3b90387e10 | |||
1ee4998ec6 | |||
ca763cd5d0 | |||
3fc2fd9bac | |||
09c153ba65 | |||
2a144fc00e | |||
6aef8bc2de | |||
3e133773a9 | |||
5fd680110c | |||
ce274219fc | |||
fa5bf192e8 | |||
1a45ec7b0d | |||
7d6bb4fcb9 | |||
844a8aec45 | |||
beb28e652d | |||
78be15d57d | |||
d6c4702390 | |||
1e073af5df | |||
188178fe6a | |||
c31d0b1618 | |||
b662fc64f3 | |||
9ab7e74991 | |||
fc427733ee | |||
5a3d273acc | |||
a7a850799d | |||
7d3652f1f9 | |||
41733d2680 |
317
.swiftlint.yml
@ -1,43 +1,276 @@
|
|||||||
disabled_rules: # rule identifiers to exclude from running
|
# All rules here : https://realm.github.io/SwiftLint/rule-directory.html
|
||||||
- leading_whitespace
|
|
||||||
- trailing_whitespace
|
|
||||||
- identifier_name
|
|
||||||
- large_tuple
|
|
||||||
- file_length
|
|
||||||
- line_length
|
|
||||||
- force_try
|
|
||||||
- shorthand_operator
|
|
||||||
- type_body_length
|
|
||||||
- function_body_length
|
|
||||||
- function_parameter_count
|
|
||||||
- redundant_string_enum_value
|
|
||||||
- unused_closure_parameter
|
|
||||||
- cyclomatic_complexity
|
|
||||||
- syntactic_sugar
|
|
||||||
- empty_enum_arguments
|
|
||||||
- force_cast
|
|
||||||
- multiple_closures_with_trailing_closure
|
|
||||||
- private_over_fileprivate
|
|
||||||
- trailing_comma
|
|
||||||
- comment_spacing
|
|
||||||
excluded: # paths to ignore during linting. Takes precedence over `included`.
|
|
||||||
- DerivedData
|
|
||||||
- Carthage
|
|
||||||
- Pods
|
|
||||||
- vendor
|
|
||||||
- Vendor
|
|
||||||
- "*/R2Tag+tags.swift"
|
|
||||||
type_name:
|
|
||||||
min_length: 1 # only warning
|
|
||||||
max_length: # warning and error
|
|
||||||
warning: 50
|
|
||||||
error: 60
|
|
||||||
allowed_symbols: ["_"]
|
|
||||||
nesting:
|
|
||||||
type_level:
|
|
||||||
warning: 3
|
|
||||||
error: 6
|
|
||||||
statement_level:
|
|
||||||
warning: 5
|
|
||||||
error: 10
|
|
||||||
|
|
||||||
|
analyzer_rules:
|
||||||
|
- capture_variable
|
||||||
|
- typesafe_array_init
|
||||||
|
- unused_declaration
|
||||||
|
- unused_import
|
||||||
|
|
||||||
|
included:
|
||||||
|
- Sources
|
||||||
|
|
||||||
|
## Rules configuration
|
||||||
|
|
||||||
|
attributes:
|
||||||
|
always_on_line_above: ["@InjectedValue", "@ViewBuilder", "@IBOutlet"]
|
||||||
|
always_on_same_line: ["@Environment", "@EnvironmentObject", "@StateObject", "@State"]
|
||||||
|
|
||||||
|
identifier_name:
|
||||||
|
min_length:
|
||||||
|
- 2
|
||||||
|
max_length:
|
||||||
|
- 60
|
||||||
|
excluded:
|
||||||
|
- x
|
||||||
|
- y
|
||||||
|
|
||||||
|
type_name:
|
||||||
|
min_length: 3
|
||||||
|
max_length: 60
|
||||||
|
excluded:
|
||||||
|
- T
|
||||||
|
allowed_symbols:
|
||||||
|
- _
|
||||||
|
|
||||||
|
# line_length:
|
||||||
|
# warning: 150
|
||||||
|
|
||||||
|
disabled_rules:
|
||||||
|
- blanket_disable_command # do not warn when rule is not re-enable later in the file
|
||||||
|
- type_contents_order
|
||||||
|
- legacy_objc_type
|
||||||
|
- indentation_width
|
||||||
|
- function_parameter_count
|
||||||
|
- line_length
|
||||||
|
- function_body_length
|
||||||
|
- cyclomatic_complexity
|
||||||
|
- optional_data_string_conversion
|
||||||
|
|
||||||
|
opt_in_rules:
|
||||||
|
# Default rules :
|
||||||
|
- block_based_kvo
|
||||||
|
- class_delegate_protocol
|
||||||
|
- closing_brace
|
||||||
|
- closure_parameter_position
|
||||||
|
- colon
|
||||||
|
- comma
|
||||||
|
- comment_spacing
|
||||||
|
- compiler_protocol_init
|
||||||
|
- computed_accessors_order
|
||||||
|
- control_statement
|
||||||
|
- custom_rules
|
||||||
|
# - cyclomatic_complexity
|
||||||
|
- deployment_target
|
||||||
|
- discouraged_direct_init
|
||||||
|
- duplicate_enum_cases
|
||||||
|
- duplicate_imports
|
||||||
|
- duplicated_key_in_dictionary_literal
|
||||||
|
- dynamic_inline
|
||||||
|
- empty_enum_arguments
|
||||||
|
- empty_parameters
|
||||||
|
- empty_parentheses_with_trailing_closure
|
||||||
|
- file_length
|
||||||
|
- for_where
|
||||||
|
- force_unwrapping
|
||||||
|
- force_cast
|
||||||
|
- force_try
|
||||||
|
- trailing_whitespace
|
||||||
|
# - function_body_length
|
||||||
|
# - function_parameter_count
|
||||||
|
- generic_type_name
|
||||||
|
- identifier_name
|
||||||
|
- implicit_getter
|
||||||
|
- inclusive_language
|
||||||
|
- is_disjoint
|
||||||
|
- large_tuple
|
||||||
|
- leading_whitespace
|
||||||
|
- legacy_cggeometry_functions
|
||||||
|
- legacy_constant
|
||||||
|
- legacy_constructor
|
||||||
|
- legacy_hashing
|
||||||
|
- legacy_nsgeometry_functions
|
||||||
|
- legacy_random
|
||||||
|
# - line_length
|
||||||
|
- mark
|
||||||
|
- multiple_closures_with_trailing_closure
|
||||||
|
- nesting
|
||||||
|
- no_fallthrough_only
|
||||||
|
- no_space_in_method_call
|
||||||
|
- notification_center_detachment
|
||||||
|
- ns_number_init_as_function_reference
|
||||||
|
- nsobject_prefer_isequal
|
||||||
|
- opening_brace
|
||||||
|
- operator_whitespace
|
||||||
|
- orphaned_doc_comment
|
||||||
|
- private_over_fileprivate
|
||||||
|
- private_unit_test
|
||||||
|
- protocol_property_accessors_order
|
||||||
|
- reduce_boolean
|
||||||
|
- redundant_discardable_let
|
||||||
|
- redundant_objc_attribute
|
||||||
|
- redundant_optional_initialization
|
||||||
|
- redundant_set_access_control
|
||||||
|
- redundant_string_enum_value
|
||||||
|
- redundant_void_return
|
||||||
|
- return_arrow_whitespace
|
||||||
|
- self_in_property_initialization
|
||||||
|
- shorthand_operator
|
||||||
|
- statement_position
|
||||||
|
- superfluous_disable_command
|
||||||
|
- switch_case_alignment
|
||||||
|
- syntactic_sugar
|
||||||
|
- todo
|
||||||
|
- trailing_comma
|
||||||
|
- trailing_newline
|
||||||
|
- trailing_semicolon
|
||||||
|
- type_body_length
|
||||||
|
- type_name
|
||||||
|
- unavailable_condition
|
||||||
|
- unneeded_break_in_switch
|
||||||
|
- unused_closure_parameter
|
||||||
|
- unused_control_flow_label
|
||||||
|
- unused_enumerated
|
||||||
|
- unused_optional_binding
|
||||||
|
- unused_setter_value
|
||||||
|
- valid_ibinspectable
|
||||||
|
- vertical_parameter_alignment
|
||||||
|
- vertical_whitespace
|
||||||
|
- void_function_in_ternary
|
||||||
|
- void_return
|
||||||
|
- xctfail_message
|
||||||
|
- accessibility_trait_for_button
|
||||||
|
- array_init
|
||||||
|
- attributes
|
||||||
|
- closure_body_length
|
||||||
|
- closure_end_indentation
|
||||||
|
- closure_spacing
|
||||||
|
- collection_alignment
|
||||||
|
- comma_inheritance
|
||||||
|
- contains_over_filter_count
|
||||||
|
- contains_over_filter_is_empty
|
||||||
|
- contains_over_first_not_nil
|
||||||
|
- contains_over_range_nil_comparison
|
||||||
|
- convenience_type
|
||||||
|
- discarded_notification_center_observer
|
||||||
|
- discouraged_assert
|
||||||
|
- empty_count
|
||||||
|
- empty_string
|
||||||
|
- empty_xctest_method
|
||||||
|
- enum_case_associated_values_count
|
||||||
|
- explicit_init
|
||||||
|
- fallthrough
|
||||||
|
- fatal_error_message
|
||||||
|
- file_header
|
||||||
|
- first_where
|
||||||
|
- flatmap_over_map_reduce
|
||||||
|
- ibinspectable_in_extension
|
||||||
|
- implicit_return
|
||||||
|
- implicitly_unwrapped_optional
|
||||||
|
- joined_default_parameter
|
||||||
|
- last_where
|
||||||
|
- legacy_multiple
|
||||||
|
- let_var_whitespace
|
||||||
|
- literal_expression_end_indentation
|
||||||
|
- lower_acl_than_parent
|
||||||
|
# - missing_docs
|
||||||
|
- modifier_order
|
||||||
|
- multiline_arguments
|
||||||
|
- multiline_arguments_brackets
|
||||||
|
- multiline_function_chains
|
||||||
|
- multiline_literal_brackets
|
||||||
|
- multiline_parameters
|
||||||
|
- multiline_parameters_brackets
|
||||||
|
- nimble_operator
|
||||||
|
- no_extension_access_modifier
|
||||||
|
- no_grouping_extension
|
||||||
|
- nslocalizedstring_key
|
||||||
|
- nslocalizedstring_require_bundle
|
||||||
|
- number_separator
|
||||||
|
- operator_usage_whitespace
|
||||||
|
- optional_enum_case_matching
|
||||||
|
- overridden_super_call
|
||||||
|
- override_in_extension
|
||||||
|
- pattern_matching_keywords
|
||||||
|
- prefer_self_in_static_references
|
||||||
|
- prefer_self_type_over_type_of_self
|
||||||
|
- prefer_zero_over_explicit_init
|
||||||
|
- prefixed_toplevel_constant
|
||||||
|
- private_action
|
||||||
|
- private_outlet
|
||||||
|
- prohibited_interface_builder
|
||||||
|
- prohibited_super_call
|
||||||
|
- quick_discouraged_call
|
||||||
|
- quick_discouraged_focused_test
|
||||||
|
- quick_discouraged_pending_test
|
||||||
|
- redundant_nil_coalescing
|
||||||
|
- redundant_type_annotation
|
||||||
|
- required_enum_case
|
||||||
|
- return_value_from_void_function
|
||||||
|
- self_binding
|
||||||
|
- shorthand_optional_binding
|
||||||
|
- single_test_class
|
||||||
|
- sorted_first_last
|
||||||
|
- sorted_imports
|
||||||
|
- strong_iboutlet
|
||||||
|
- test_case_accessibility
|
||||||
|
- toggle_bool
|
||||||
|
- trailing_closure
|
||||||
|
- unavailable_function
|
||||||
|
- unneeded_parentheses_in_closure_argument
|
||||||
|
- unowned_variable_capture
|
||||||
|
- untyped_error_in_catch
|
||||||
|
- vertical_parameter_alignment_on_call
|
||||||
|
- vertical_whitespace_between_cases
|
||||||
|
- vertical_whitespace_closing_braces
|
||||||
|
- weak_delegate
|
||||||
|
- xct_specific_matcher
|
||||||
|
|
||||||
|
custom_rules:
|
||||||
|
|
||||||
|
# Empty line before and after MARK -------------------------------------------
|
||||||
|
# mark_spacing:
|
||||||
|
# name: "Surround MARK by empty lines"
|
||||||
|
# regex: '\n[^\n]([^\n]*\/\/ MARK[^\n]*)\n[^\n]'
|
||||||
|
# message: "Surround MARK by empty lines"
|
||||||
|
# severity: warning
|
||||||
|
|
||||||
|
# Empty line -----------------------------------------------------------------
|
||||||
|
# no_empty_line_after_func:
|
||||||
|
# name: "No empty line after init or func"
|
||||||
|
# regex: '(func|init|let\s|var\s)[^\n]*\{[^\n\{\}]*\n\n'
|
||||||
|
# message: "No empty line after init or func"
|
||||||
|
# severity: warning
|
||||||
|
|
||||||
|
# Empty line after canImport -----------------------------------------------------------------
|
||||||
|
no_empty_line_after_can_import:
|
||||||
|
name: "Add empty line after #if canImport"
|
||||||
|
regex: '#if canImport\(.*\)\n[^\n]*(import|class|struct|enum|extension|protocol)'
|
||||||
|
message: "Add empty line after #if canImport"
|
||||||
|
severity: warning
|
||||||
|
|
||||||
|
# Spacings -------------------------------------------------------------------
|
||||||
|
empty_line_required:
|
||||||
|
name: "Add empty line after class, struct, enum, extension or protocol"
|
||||||
|
regex: '(class |struct |enum |extension |protocol )[^\n]*\{\n[^\n]*(class|struct|enum|extension|protocol|func|let|var|weak|private|internal|public|open|static|final|\/\/|init|case|@)'
|
||||||
|
message: "Add empty line after class, struct, enum, extension or protocol"
|
||||||
|
severity: warning
|
||||||
|
match_kinds:
|
||||||
|
- argument
|
||||||
|
- attribute.builtin
|
||||||
|
- attribute.id
|
||||||
|
- buildconfig.id
|
||||||
|
- buildconfig.keyword
|
||||||
|
- comment
|
||||||
|
- comment.mark
|
||||||
|
- comment.url
|
||||||
|
- identifier
|
||||||
|
- keyword
|
||||||
|
- number
|
||||||
|
- objectliteral
|
||||||
|
- parameter
|
||||||
|
- placeholder
|
||||||
|
- string
|
||||||
|
- string_interpolation_anchor
|
||||||
|
- typeidentifier
|
||||||
|
|
||||||
|
|
||||||
|
@ -102,6 +102,20 @@
|
|||||||
ReferencedContainer = "container:">
|
ReferencedContainer = "container:">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</BuildableProductRunnable>
|
</BuildableProductRunnable>
|
||||||
|
<CommandLineArguments>
|
||||||
|
<CommandLineArgument
|
||||||
|
argument = "generate"
|
||||||
|
isEnabled = "YES">
|
||||||
|
</CommandLineArgument>
|
||||||
|
<CommandLineArgument
|
||||||
|
argument = ""$(PROJECT_DIR)/../SampleFiles/resgenConfiguration.yml""
|
||||||
|
isEnabled = "YES">
|
||||||
|
</CommandLineArgument>
|
||||||
|
<CommandLineArgument
|
||||||
|
argument = "--project-directory "$(PROJECT_DIR)""
|
||||||
|
isEnabled = "YES">
|
||||||
|
</CommandLineArgument>
|
||||||
|
</CommandLineArguments>
|
||||||
</LaunchAction>
|
</LaunchAction>
|
||||||
<ProfileAction
|
<ProfileAction
|
||||||
buildConfiguration = "Release"
|
buildConfiguration = "Release"
|
||||||
|
34
CHANGELOG.md
@ -0,0 +1,34 @@
|
|||||||
|
# v1.2 - Architecture generation
|
||||||
|
|
||||||
|
## New
|
||||||
|
- New section in configuration file: `architecture`. Define your ressources accessors achitecture and let ResgenSwift generate it for you. Check out Readme to know more.
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
- Errors and Warnings are now shown in Xcode Issue Navigator
|
||||||
|
|
||||||
|
---
|
||||||
|
# v1.1 - SwiftUI compatibility
|
||||||
|
|
||||||
|
## New
|
||||||
|
- Update plist `UIAppFonts` when generated fonts (use plistBuddy)
|
||||||
|
- New parameter: `infoPlistPaths`
|
||||||
|
- Generate SwiftUI extensions for colors, fonts and images
|
||||||
|
- New parameter: `extensionNameUIKit`
|
||||||
|
- Adding Makefile to install, unsintall and create man page.
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
Fix SwiftLint rule `trailing_newline`
|
||||||
|
|
||||||
|
---
|
||||||
|
# v1.0 - Configuration file
|
||||||
|
|
||||||
|
## Major
|
||||||
|
- A new command has been added: `generate`. Instead of runnning every single command, it will run all necessary command based on a `yaml` configuration file. Check out Readme to know more.
|
||||||
|
|
||||||
|
## Minors
|
||||||
|
- Code refactoring
|
||||||
|
- Huge performance improvements
|
||||||
|
- Readme.md update
|
||||||
|
- Add option to generate static properties/methods (`staticMembers`)
|
||||||
|
- Add option to specify the project directory (`--project-directory`). It allows to run ResgenSwift from anywhere
|
||||||
|
- Add `install.sh` script to install ResgenSwift in `/usr/local/bin`
|
||||||
|
6
Jenkinsfile
vendored
@ -1,8 +1,10 @@
|
|||||||
library "openiumpipeline"
|
library "openiumpipeline"
|
||||||
|
|
||||||
env.DEVELOPER_DIR="/Applications/Xcode_13.3.0.app/Contents/Developer"
|
env.DEVELOPER_DIR="/Applications/Xcode-16.3.0.app/Contents/Developer"
|
||||||
//env.SIMULATOR_DEVICE_TYPES="iPad--7th-generation-"
|
// env.SIMULATOR_DEVICE_TYPES="iPhone-14-Pro"
|
||||||
|
// env.SLACK_CHANNEL = "prj-skdevkit"
|
||||||
env.IS_PACKAGE_SWIFT=1
|
env.IS_PACKAGE_SWIFT=1
|
||||||
env.TARGETS_MACOS=1
|
env.TARGETS_MACOS=1
|
||||||
|
env.PACKAGE_NAME="ResgenSwift" // xcodebuild -list => Only 1 scheme
|
||||||
|
|
||||||
iOSpipeline()
|
iOSpipeline()
|
||||||
|
63
Makefile
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
SHELL = /bin/zsh
|
||||||
|
|
||||||
|
#INSTALL_DIR ?= /usr/local/bin
|
||||||
|
INSTALL_DIR = /tmp/ResgenYolo
|
||||||
|
|
||||||
|
MAN_DIR := /usr/local/share/man
|
||||||
|
MAN_PAGE_NAME = resgen-swift.1
|
||||||
|
REPO_DIR = $(shell pwd)
|
||||||
|
BUILD_DIR = $(REPO_DIR)/.build
|
||||||
|
|
||||||
|
#
|
||||||
|
# Man pages
|
||||||
|
#
|
||||||
|
|
||||||
|
# create-man-files:
|
||||||
|
# swift package plugin generate-manual
|
||||||
|
# cp $(BUILDDIR)/plugins/GenerateManualPlugin/outputs/ResgenSwift/resgen-swift.1 $(REPODIR)/man/resgen-swift.1
|
||||||
|
|
||||||
|
# install-man-files:
|
||||||
|
# mkdir -p ${DESTDIR}${mandir}/man1
|
||||||
|
# cp $(REPODIR)/man/resgen-swift.1 ${DESTDIR}${mandir}/man1/resgen-swift.1
|
||||||
|
|
||||||
|
create-and-install-man-files:
|
||||||
|
swift package plugin generate-manual
|
||||||
|
mkdir -p ${MAN_DIR}/man1
|
||||||
|
cp $(BUILD_DIR)/plugins/GenerateManualPlugin/outputs/ResgenSwift/${MAN_PAGE_NAME} ${MAN_DIR}/man1/${MAN_PAGE_NAME}
|
||||||
|
|
||||||
|
#
|
||||||
|
# Build and install
|
||||||
|
#
|
||||||
|
|
||||||
|
build-debug:
|
||||||
|
@swift build \
|
||||||
|
-c debug \
|
||||||
|
--build-path "$(BUILD_DIR)"
|
||||||
|
|
||||||
|
build-release:
|
||||||
|
@swift build \
|
||||||
|
-c release \
|
||||||
|
--build-path "$(BUILD_DIR)"
|
||||||
|
|
||||||
|
.PHONY: install
|
||||||
|
install: build-release
|
||||||
|
@install -d "$(INSTALL_DIR)"
|
||||||
|
@install "$(wildcard $(BUILD_DIR)/**/release/ResgenSwift)" "$(INSTALL_DIR)/resgen-swift"
|
||||||
|
@make create-and-install-man-files
|
||||||
|
|
||||||
|
#
|
||||||
|
# Uninstall and cleaning
|
||||||
|
#
|
||||||
|
|
||||||
|
.PHONY: uninstall
|
||||||
|
uninstall:
|
||||||
|
@rm -rf "$(INSTALL_DIR)/resgen-swift"
|
||||||
|
@rm -rf ${MAN_DIR}/man1/${MAN_PAGE_NAME}
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
distclean:
|
||||||
|
@rm -f $(BUILD_DIR)/release
|
||||||
|
|
||||||
|
.PHONY: clean
|
||||||
|
clean: distclean
|
||||||
|
@rm -rf $(BUILD_DIR)
|
@ -1,25 +1,32 @@
|
|||||||
{
|
{
|
||||||
"object": {
|
"pins" : [
|
||||||
"pins": [
|
{
|
||||||
{
|
"identity" : "swift-argument-parser",
|
||||||
"package": "swift-argument-parser",
|
"kind" : "remoteSourceControl",
|
||||||
"repositoryURL": "https://github.com/apple/swift-argument-parser",
|
"location" : "https://github.com/apple/swift-argument-parser",
|
||||||
"state": {
|
"state" : {
|
||||||
"branch": null,
|
"revision" : "41982a3656a71c768319979febd796c6fd111d5c",
|
||||||
"revision": "e1465042f195f374b94f915ba8ca49de24300a0d",
|
"version" : "1.5.0"
|
||||||
"version": "1.0.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"package": "Yams",
|
|
||||||
"repositoryURL": "https://github.com/jpsim/Yams.git",
|
|
||||||
"state": {
|
|
||||||
"branch": null,
|
|
||||||
"revision": "01835dc202670b5bb90d07f3eae41867e9ed29f6",
|
|
||||||
"version": "5.0.1"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
},
|
||||||
},
|
{
|
||||||
"version": 1
|
"identity" : "swiftlintplugin",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/lukepistrol/SwiftLintPlugin",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "87454f5c9ff4d644086aec2a0df1ffba678e7f3c",
|
||||||
|
"version" : "0.57.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"identity" : "yams",
|
||||||
|
"kind" : "remoteSourceControl",
|
||||||
|
"location" : "https://github.com/jpsim/Yams.git",
|
||||||
|
"state" : {
|
||||||
|
"revision" : "0d9ee7ea8c4ebd4a489ad7a73d5c6cad55d6fed3",
|
||||||
|
"version" : "5.0.6"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version" : 2
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,25 @@
|
|||||||
// swift-tools-version:5.6
|
// swift-tools-version:5.9
|
||||||
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
// The swift-tools-version declares the minimum version of Swift required to build this package.
|
||||||
|
|
||||||
import PackageDescription
|
import PackageDescription
|
||||||
|
|
||||||
let package = Package(
|
let package = Package(
|
||||||
name: "ResgenSwift",
|
name: "ResgenSwift",
|
||||||
platforms: [.macOS(.v12)],
|
platforms: [.macOS(.v14), .iOS(.v15)],
|
||||||
dependencies: [
|
dependencies: [
|
||||||
// Dependencies declare other packages that this package depends on.
|
// Dependencies declare other packages that this package depends on.
|
||||||
.package(url: "https://github.com/apple/swift-argument-parser", from: "1.0.0"),
|
.package(
|
||||||
.package(url: "https://github.com/jpsim/Yams.git", from: "5.0.1")
|
url: "https://github.com/apple/swift-argument-parser",
|
||||||
|
from: "1.5.0"
|
||||||
|
),
|
||||||
|
.package(
|
||||||
|
url: "https://github.com/jpsim/Yams.git",
|
||||||
|
from: "5.0.1"
|
||||||
|
),
|
||||||
|
.package(
|
||||||
|
url: "https://github.com/lukepistrol/SwiftLintPlugin",
|
||||||
|
exact: "0.57.1"
|
||||||
|
),
|
||||||
],
|
],
|
||||||
targets: [
|
targets: [
|
||||||
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
|
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
|
||||||
@ -18,8 +28,14 @@ let package = Package(
|
|||||||
name: "ResgenSwift",
|
name: "ResgenSwift",
|
||||||
dependencies: [
|
dependencies: [
|
||||||
"ToolCore",
|
"ToolCore",
|
||||||
.product(name: "ArgumentParser", package: "swift-argument-parser"),
|
"Yams",
|
||||||
"Yams"
|
.product(
|
||||||
|
name: "ArgumentParser",
|
||||||
|
package: "swift-argument-parser"
|
||||||
|
)
|
||||||
|
],
|
||||||
|
plugins: [
|
||||||
|
// .plugin(name: "SwiftLint", package: "SwiftLintPlugin")
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
|
|
||||||
|
190
README.md
@ -6,8 +6,9 @@ ResgenSwift is a package, fully written in Swift, to help you automatize ressour
|
|||||||
|
|
||||||
## Fonts
|
## Fonts
|
||||||
|
|
||||||
Font generator generates an extension of `UIFont` (or a custom class). It also prints `UIAppFonts` to put in your project `.plist`.
|
Font generator generates an extension of `UIFont` and `Font` (or custom classes). It also prints content of `UIAppFonts` from your project `.plist`. If project `.plist` is specified, it will update `UIAppFonts` content of all `.plist`.
|
||||||
iOS required to use the **real name** of the font, this name can be different from its filename. To get the **real name**, it uses `fc-scan`.
|
|
||||||
|
iOS required to use the **real name** of the font, this name can be different from its filename. To get the **real name**, it uses `fc-scan`. So, be sure that the `$PATH` contains path of `fc-scan`.
|
||||||
|
|
||||||
**Example**
|
**Example**
|
||||||
|
|
||||||
@ -15,8 +16,10 @@ iOS required to use the **real name** of the font, this name can be different fr
|
|||||||
swift run -c release ResgenSwift fonts $FORCE_FLAG "./Fonts/fonts.txt" \
|
swift run -c release ResgenSwift fonts $FORCE_FLAG "./Fonts/fonts.txt" \
|
||||||
--extension-output-path "./Fonts/Generated" \
|
--extension-output-path "./Fonts/Generated" \
|
||||||
--extension-name "AppFont" \
|
--extension-name "AppFont" \
|
||||||
|
--extension-name-ui-kit "UIAppFont" \
|
||||||
--extension-suffix "GreatApp" \
|
--extension-suffix "GreatApp" \
|
||||||
--static-members true
|
--static-members true \
|
||||||
|
--info-plist-paths "./path/one/to/Info.plist ./path/two/to/Info.plist"
|
||||||
```
|
```
|
||||||
|
|
||||||
**Parameters**
|
**Parameters**
|
||||||
@ -24,9 +27,11 @@ swift run -c release ResgenSwift fonts $FORCE_FLAG "./Fonts/fonts.txt" \
|
|||||||
1. `-f`: force generation
|
1. `-f`: force generation
|
||||||
2. Font input folder, it will search for every `.ttf` and `.otf` files specified in `fonts.txt`
|
2. Font input folder, it will search for every `.ttf` and `.otf` files specified in `fonts.txt`
|
||||||
3. `--extension-output-path`: path where to generate generated extension
|
3. `--extension-output-path`: path where to generate generated extension
|
||||||
4. `--extension-name` *(optional)* : name of thee class to add the extension
|
4. `--extension-name` *(optional)* : name of the class to add SwiftUI getters
|
||||||
5. `--extension-suffix` *(optional)* : additional text which is added to the filename (ex: `AppFont+GreatApp.swift`)
|
5. `--extension-name-ui-kit` *(optional)* : name of the class to add UIKit getters
|
||||||
6. `--static-members` *(optional)*: generate static properties or not
|
6. `--extension-suffix` *(optional)* : additional text which is added to the filename (ex: `AppFont+GreatApp.swift`)
|
||||||
|
7. `--static-members` *(optional)*: generate static properties or not
|
||||||
|
8. `--info-plist-paths` *(optional)*: array of `.plist`, you can specify multiple `Info.plist` for multiple targets
|
||||||
|
|
||||||
|
|
||||||
## Colors
|
## Colors
|
||||||
@ -39,6 +44,7 @@ swift run -c release ResgenSwift colors $FORCE_FLAG "./Colors/colors.txt" \
|
|||||||
--xcassets-path "./Colors/colors.xcassets" \
|
--xcassets-path "./Colors/colors.xcassets" \
|
||||||
--extension-output-path "./Colors/Generated/" \
|
--extension-output-path "./Colors/Generated/" \
|
||||||
--extension-name "AppColor" \
|
--extension-name "AppColor" \
|
||||||
|
--extension-name-ui-kit "UIAppColor" \
|
||||||
--extension-suffix "GreatApp" \
|
--extension-suffix "GreatApp" \
|
||||||
--static-members true
|
--static-members true
|
||||||
```
|
```
|
||||||
@ -49,9 +55,10 @@ swift run -c release ResgenSwift colors $FORCE_FLAG "./Colors/colors.txt" \
|
|||||||
2. Input colors file
|
2. Input colors file
|
||||||
3. `--style` can be `all` or `light`
|
3. `--style` can be `all` or `light`
|
||||||
4. `--extension-output-path`: path where to generate generated extension
|
4. `--extension-output-path`: path where to generate generated extension
|
||||||
5. `--extension-name` *(optional)* : name of class to add the extension
|
5. `--extension-name` *(optional)* : name of the class to add SwiftUI getters
|
||||||
6. `--extension-suffix` *(optional)* : additional text which is added to filename (ex: `AppColor+GreatApp.swift`)
|
6. `--extension-name-ui-kit` *(optional)* : name of the class to add UIKit getters
|
||||||
7. `--static-members` *(optional)*: generate static properties or not
|
7. `--extension-suffix` *(optional)* : additional text which is added to filename (ex: `AppColor+GreatApp.swift`)
|
||||||
|
8. `--static-members` *(optional)*: generate static properties or not
|
||||||
|
|
||||||
|
|
||||||
## Strings
|
## Strings
|
||||||
@ -74,7 +81,7 @@ swift run -c release ResgenSwift strings twine $FORCE_FLAG "./Twine/strings.txt"
|
|||||||
2. Input translations file (must be Twine formatted)
|
2. Input translations file (must be Twine formatted)
|
||||||
3. `--langs`: langs to generate (string with space between each lang)
|
3. `--langs`: langs to generate (string with space between each lang)
|
||||||
4. `--default-lang`: default lang that will be in `Base.lproj`. It must be in `langs` as well
|
4. `--default-lang`: default lang that will be in `Base.lproj`. It must be in `langs` as well
|
||||||
4. `--extension-output-path`: path where to generate generated extension
|
5. `--extension-output-path`: path where to generate generated extension
|
||||||
|
|
||||||
### Stringium (recommended)
|
### Stringium (recommended)
|
||||||
|
|
||||||
@ -86,6 +93,7 @@ swift run -c release ResgenSwift strings stringium $FORCE_FLAG "./Strings/string
|
|||||||
--extension-output-path "./Strings/Generated" \
|
--extension-output-path "./Strings/Generated" \
|
||||||
--extension-name "AppString" \
|
--extension-name "AppString" \
|
||||||
--extension-suffix "GreatApp" \
|
--extension-suffix "GreatApp" \
|
||||||
|
--xcStrings true
|
||||||
--static-members true
|
--static-members true
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -98,6 +106,7 @@ swift run -c release ResgenSwift strings stringium $FORCE_FLAG "./Strings/string
|
|||||||
4. `--extension-output-path`: path where to generate generated extension
|
4. `--extension-output-path`: path where to generate generated extension
|
||||||
5. `--extension-name` *(optional)* : name of class to add the extension
|
5. `--extension-name` *(optional)* : name of class to add the extension
|
||||||
6. `--extension-suffix` *(optional)* : additional text which is added to filename (ex: `AppString+GreatApp.swift`)
|
6. `--extension-suffix` *(optional)* : additional text which is added to filename (ex: `AppString+GreatApp.swift`)
|
||||||
|
6. `--xcStrings`*(optional)* : generate string catalog
|
||||||
7. `--static-members` *(optional)*: generate static properties or not
|
7. `--static-members` *(optional)*: generate static properties or not
|
||||||
|
|
||||||
|
|
||||||
@ -126,15 +135,81 @@ swift run -c release ResgenSwift strings tags $FORCE_FLAG "./Tags/tags.txt" \
|
|||||||
|
|
||||||
> ⚠️ If extension name is not set or is `Tags`, it will generate the following typaloas `typealias Tags = String`.
|
> ⚠️ If extension name is not set or is `Tags`, it will generate the following typaloas `typealias Tags = String`.
|
||||||
|
|
||||||
|
|
||||||
|
## Analytics
|
||||||
|
|
||||||
|
Analytics will generate all you need to analyze UX with Matomo or Firebase Analytics. Input files are formatted in YAML. This command will generate a manager for each target and an AnalyticsManager. This is this one you will need to use. And it will generate a method for all tags you have declared in the YAML file. Next, you will need to use the `configure()` method of AnalyticsManager and if you want to use matomo to set up the `siteId` and the `url` of the site.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
swift run -c release ResgenSwift strings tags $FORCE_FLAG "./Tags/tags.txt" \
|
||||||
|
--target "matomo firebase" \
|
||||||
|
--extension-output-path "./Analytics/Generated" \
|
||||||
|
--extension-name "AppAnalytics" \
|
||||||
|
--extension-suffix "GreatApp" \
|
||||||
|
--static-members true
|
||||||
|
```
|
||||||
|
|
||||||
|
**Parameters**
|
||||||
|
|
||||||
|
1. `-f`: force generation
|
||||||
|
2. Input tags file (must be YAML formatted)
|
||||||
|
3. `--target`: target with you will log UX
|
||||||
|
4. `--extension-output-path`: path where to generate generated extension
|
||||||
|
5. `--extension-name` *(optional)* : name of class to add the extension
|
||||||
|
6. `--extension-suffix` *(optional)* : additional text which is added to filename (ex: `AppAnalytics+GreatApp.swift`)
|
||||||
|
7. `--static-members` *(optional)*: generate static properties or not
|
||||||
|
|
||||||
|
> ⚠️ If extension name is not set or is `Analytics`, it will generate the following typaloas `typealias Analytics = String`.
|
||||||
|
|
||||||
|
### YAML
|
||||||
|
|
||||||
|
```
|
||||||
|
- id: s1_def_one
|
||||||
|
name: s1 def one _TITLE_
|
||||||
|
path: s1_def_one/_TITLE_
|
||||||
|
action: Tap
|
||||||
|
category: User
|
||||||
|
tags: ios,droid
|
||||||
|
comments:
|
||||||
|
parameters:
|
||||||
|
- name: title
|
||||||
|
type: String
|
||||||
|
replaceIn: name,path
|
||||||
|
```
|
||||||
|
|
||||||
|
1. `id`: name of the method (method name will be composed of `log` + `Event|Screen` + `id`)
|
||||||
|
2. `name`: name of the tag
|
||||||
|
3. `path` *(optional with firebase)* : needed for matomo but not with firebase (log screen)
|
||||||
|
4. `action` *(optional with firebase)* : needed for matomo but not with firebase (log event)
|
||||||
|
5. `category` *(optional with firebase)* : needed for matomo but not with firebase (log event)
|
||||||
|
6. `tags`: which platform target
|
||||||
|
7. `comments` *(optional)*
|
||||||
|
8. `parameters` *(optional)*
|
||||||
|
|
||||||
|
**Parameters**
|
||||||
|
|
||||||
|
You can use parameters in generate methods.
|
||||||
|
|
||||||
|
1. `name`: name of the parameter
|
||||||
|
2. `type`: type of the parameter (Int, String, Bool, Double)
|
||||||
|
3. `replaceIn` *(optional)*
|
||||||
|
|
||||||
|
**Replace in**
|
||||||
|
|
||||||
|
This is section is equivalent of `%s | %d | %f | %@`. You can put the content of the parameter in *name*, *path*, *action*, *category*.
|
||||||
|
You need to put `_` + `NAME OF THE PARAMETER` + `_` in the target and which target you want in the value of `replaceIn`. (name need to be in uppercase)
|
||||||
|
|
||||||
|
|
||||||
## Images
|
## Images
|
||||||
|
|
||||||
Images generator will generate images assets along with extensions to access those images easily.
|
Images generator will generate images assets along with extensions to access those images easily.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
swift run -c release ResgenSwift images $FORCE_FLAG "./Images/images.txt" \
|
swift run -c release ResgenSwift images $FORCE_FLAG "./Images/images.txt" \
|
||||||
--xcassets-path "./Images/app.xcassets" \
|
--xcassets-path "./Images/app.xcassets" \
|
||||||
--extension-output-path "./Images/Generated" \
|
--extension-output-path "./Images/Generated" \
|
||||||
--extension-name "AppImage" \
|
--extension-name "AppImage" \
|
||||||
|
--extension-name-ui-kit "UIAppImage" \
|
||||||
--extension-suffix "GreatApp" \
|
--extension-suffix "GreatApp" \
|
||||||
--static-members true
|
--static-members true
|
||||||
```
|
```
|
||||||
@ -145,10 +220,12 @@ swift run -c release ResgenSwift images $FORCE_FLAG "./Images/images.txt" \
|
|||||||
2. Input images definitions file
|
2. Input images definitions file
|
||||||
3. `--xcassets-path`: xcasset path where to generate imagesets
|
3. `--xcassets-path`: xcasset path where to generate imagesets
|
||||||
4. `--extension-output-path`: path where to generate generated extension
|
4. `--extension-output-path`: path where to generate generated extension
|
||||||
5. `--extension-name` *(optional)* : name of class to add the extension
|
5. `--extension-name` *(optional)* : name of the class to add SwiftUI getters
|
||||||
|
6. `--extension-name-ui-kit` *(optional)* : name of the class to add UIKit getters
|
||||||
6. `--extension-suffix` *(optional)* : additional text which is added to filename (ex: `AppImage+GreatApp.swift`)
|
6. `--extension-suffix` *(optional)* : additional text which is added to filename (ex: `AppImage+GreatApp.swift`)
|
||||||
7. `--static-members` *(optional)*: generate static properties or not
|
7. `--static-members` *(optional)*: generate static properties or not
|
||||||
|
|
||||||
|
> ⚠️ Svg images will be copied in the assets and rendered as "Original", however if those images are not rendered correctly you can force the png generation by adding the key word "png" like this: id arrow_back 15 ? png
|
||||||
|
|
||||||
## All at once
|
## All at once
|
||||||
|
|
||||||
@ -167,6 +244,7 @@ colors:
|
|||||||
xcassetsPath: String
|
xcassetsPath: String
|
||||||
extensionOutputPath: String
|
extensionOutputPath: String
|
||||||
extensionName: String?
|
extensionName: String?
|
||||||
|
extensionNameUIKit: String?
|
||||||
extensionSuffix: String?
|
extensionSuffix: String?
|
||||||
staticMembers: Bool?
|
staticMembers: Bool?
|
||||||
|
|
||||||
@ -175,15 +253,18 @@ fonts:
|
|||||||
inputFile: String
|
inputFile: String
|
||||||
extensionOutputPath: String
|
extensionOutputPath: String
|
||||||
extensionName: String?
|
extensionName: String?
|
||||||
|
extensionNameUIKit: String?
|
||||||
extensionSuffix: String?
|
extensionSuffix: String?
|
||||||
staticMembers: Bool?
|
staticMembers: Bool?
|
||||||
|
infoPlistPaths: [String]
|
||||||
|
|
||||||
images:
|
images:
|
||||||
-
|
-
|
||||||
inputFile: String
|
inputFile: String
|
||||||
xcassetsPath: String
|
xcassetsPath: String
|
||||||
extensionOutputPath: String
|
extensionOutputPath: String
|
||||||
extensionName: String?
|
extensionName: String?
|
||||||
|
extensionNameUIKit: String?
|
||||||
extensionSuffix: String?
|
extensionSuffix: String?
|
||||||
staticMembers: Bool?
|
staticMembers: Bool?
|
||||||
|
|
||||||
@ -223,6 +304,7 @@ colors:
|
|||||||
xcassetsPath: String
|
xcassetsPath: String
|
||||||
extensionOutputPath: String
|
extensionOutputPath: String
|
||||||
extensionName: String?
|
extensionName: String?
|
||||||
|
extensionNameUIKit: String?
|
||||||
extensionSuffix: String?
|
extensionSuffix: String?
|
||||||
staticMembers: Bool?
|
staticMembers: Bool?
|
||||||
-
|
-
|
||||||
@ -231,6 +313,7 @@ colors:
|
|||||||
xcassetsPath: String
|
xcassetsPath: String
|
||||||
extensionOutputPath: String
|
extensionOutputPath: String
|
||||||
extensionName: String?
|
extensionName: String?
|
||||||
|
extensionNameUIKit: String?
|
||||||
extensionSuffix: String?
|
extensionSuffix: String?
|
||||||
staticMembers: Bool?
|
staticMembers: Bool?
|
||||||
...
|
...
|
||||||
@ -246,6 +329,67 @@ tags: []
|
|||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### File architecture
|
||||||
|
|
||||||
|
ResgenSwift generate extension of classes. Those classes must exists in your project. You can create them yourself OR you can let ResgenSwift create them by specifying what you want. Do as follow:
|
||||||
|
|
||||||
|
```
|
||||||
|
architecture:
|
||||||
|
property: R *(required but not used)*
|
||||||
|
classname: R
|
||||||
|
path: ./path/to/generate
|
||||||
|
children:
|
||||||
|
- property: images
|
||||||
|
classname: R2Image
|
||||||
|
- property: strings
|
||||||
|
classname: R2String
|
||||||
|
- property: fonts
|
||||||
|
classname: R2Font
|
||||||
|
- property: images
|
||||||
|
classname: R2Image
|
||||||
|
- property: uikit
|
||||||
|
classname: R2UI
|
||||||
|
children:
|
||||||
|
- property: images
|
||||||
|
classname: R2UIImage
|
||||||
|
- property: fonts
|
||||||
|
classname: R2UIFont
|
||||||
|
- property: images
|
||||||
|
classname: R2UIImage
|
||||||
|
```
|
||||||
|
|
||||||
|
This will generate a file named as the architecture classname: `R.swift`. Based on the previous architecture, it will generate:
|
||||||
|
```
|
||||||
|
class R {
|
||||||
|
static let images = R2Image()
|
||||||
|
static let strings = R2String()
|
||||||
|
static let fonts = R2Font()
|
||||||
|
static let images = R2Image()
|
||||||
|
static let uikit = R2UI()
|
||||||
|
}
|
||||||
|
|
||||||
|
class R2Image {}
|
||||||
|
|
||||||
|
class R2String {}
|
||||||
|
|
||||||
|
class R2Font {}
|
||||||
|
|
||||||
|
class R2Image {}
|
||||||
|
|
||||||
|
class R2UI {
|
||||||
|
let images = R2UIImage()
|
||||||
|
let fonts = R2UIFont()
|
||||||
|
let images = R2UIImage()
|
||||||
|
}
|
||||||
|
|
||||||
|
class R2UIImage {}
|
||||||
|
|
||||||
|
class R2UIFont {}
|
||||||
|
|
||||||
|
class R2UIImage {}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
### Usage
|
### Usage
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
@ -255,12 +399,22 @@ swift run -c release ResgenSwift generate path/to/configuration.yml --project-di
|
|||||||
> ⚠️ Every path in `configuration.yml` will be prepended by content of `--project-directory` if they are relative path (not starting with `/`)
|
> ⚠️ Every path in `configuration.yml` will be prepended by content of `--project-directory` if they are relative path (not starting with `/`)
|
||||||
|
|
||||||
|
|
||||||
## Installation
|
## Binary usage
|
||||||
|
|
||||||
Simple run `./install.sh`. Binary will be install in `/usr/local/bin`. Then, use ResgenSwift like any other command:
|
### Installation
|
||||||
|
|
||||||
Example:
|
Run `make install`. Binary will be install in `/usr/local/bin`.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
ResgenSwift generate path/to/configuration.yml --project-directory ${PROJECT_DIR}
|
resgen-swift generate path/to/configuration.yml --project-directory ${PROJECT_DIR}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Man page
|
||||||
|
|
||||||
|
Commands parameters and details can be find by running `resgen-swift --help`. If you prefer, a man page is also available. Run `man resgen-swift`. Man page is installed on `make install` but you can install manually by running `make create-and-install-man-file`.
|
||||||
|
|
||||||
|
### Uninstallation
|
||||||
|
|
||||||
|
To uninstall ResgenSwift: `make uninstall`.
|
||||||
|
21
SampleFiles/Colors/Generated/ColorYolo+GenAllScript.swift
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// Generated by ResgenSwift.Color 2.1.0
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
extension ColorYolo {
|
||||||
|
|
||||||
|
/// Color red is #FF0000 (light) or #FF0000 (dark)"
|
||||||
|
var red: Color {
|
||||||
|
Color("red")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Color green_alpha_50 is #A000FF00 (light) or #A000FF00 (dark)"
|
||||||
|
var green_alpha_50: Color {
|
||||||
|
Color("green_alpha_50")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Color blue_light_dark is #0000FF (light) or #0000AA (dark)"
|
||||||
|
var blue_light_dark: Color {
|
||||||
|
Color("blue_light_dark")
|
||||||
|
}
|
||||||
|
}
|
@ -1,21 +0,0 @@
|
|||||||
// Generated from ColorToolCore at 2021-12-20 15:16:18 +0000
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
extension UIColor {
|
|
||||||
|
|
||||||
/// Color red is #FF0000 (light) or #FF0000 (dark)"
|
|
||||||
static var red: UIColor {
|
|
||||||
UIColor(named: "red")!
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Color green_alpha_50 is #A000FF00 (light) or #A000FF00 (dark)"
|
|
||||||
static var green_alpha_50: UIColor {
|
|
||||||
UIColor(named: "green_alpha_50")!
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Color blue_light_dark is #0000FF (light) or #0000AA (dark)"
|
|
||||||
static var blue_light_dark: UIColor {
|
|
||||||
UIColor(named: "blue_light_dark")!
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
// Generated from ColorToolCore at 2022-02-14 09:30:19 +0000
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
extension UIColor {
|
|
||||||
|
|
||||||
/// Color red is #FF0000 (light) or #FF0000 (dark)"
|
|
||||||
static var red: UIColor {
|
|
||||||
UIColor(named: "red")!
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Color green_alpha_50 is #A000FF00 (light) or #A000FF00 (dark)"
|
|
||||||
static var green_alpha_50: UIColor {
|
|
||||||
UIColor(named: "green_alpha_50")!
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Color blue_light_dark is #0000FF (light) or #0000AA (dark)"
|
|
||||||
static var blue_light_dark: UIColor {
|
|
||||||
UIColor(named: "blue_light_dark")!
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
// Generated from ColorToolCore at 2021-12-20 15:17:10 +0000
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
extension UIColor {
|
|
||||||
|
|
||||||
/// Color red is #FF0000 (light) or #FF0000 (dark)"
|
|
||||||
static var red: UIColor {
|
|
||||||
UIColor(named: "red")!
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Color green_alpha_50 is #A000FF00 (light) or #A000FF00 (dark)"
|
|
||||||
static var green_alpha_50: UIColor {
|
|
||||||
UIColor(named: "green_alpha_50")!
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Color blue_light_dark is #0000FF (light) or #0000AA (dark)"
|
|
||||||
static var blue_light_dark: UIColor {
|
|
||||||
UIColor(named: "blue_light_dark")!
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,8 +1,8 @@
|
|||||||
// Generated from ColorToolCore at 2021-12-20 15:34:55 +0000
|
// Generated by ResgenSwift.Color 1.2
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
extension R2Color {
|
extension UIColorYolo {
|
||||||
|
|
||||||
/// Color red is #FF0000 (light) or #FF0000 (dark)"
|
/// Color red is #FF0000 (light) or #FF0000 (dark)"
|
||||||
@objc var red: UIColor {
|
@objc var red: UIColor {
|
||||||
@ -14,8 +14,8 @@ extension R2Color {
|
|||||||
UIColor(named: "green_alpha_50")!
|
UIColor(named: "green_alpha_50")!
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Color blue_light_dark is #0000FF (light) or #0000FF (dark)"
|
/// Color blue_light_dark is #0000FF (light) or #0000AA (dark)"
|
||||||
@objc var blue_light_dark: UIColor {
|
@objc var blue_light_dark: UIColor {
|
||||||
UIColor(named: "blue_light_dark")!
|
UIColor(named: "blue_light_dark")!
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,21 +1,21 @@
|
|||||||
// Generated by ResgenSwift.ColorTool 1.0.0
|
// Generated by ResgenSwift.Color 2.1.0
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
extension UIColor {
|
extension UIhkjhkColorYolo {
|
||||||
|
|
||||||
/// Color red is #FF0000 (light) or #FF0000 (dark)"
|
/// Color red is #FF0000 (light) or #FF0000 (dark)"
|
||||||
static var red: UIColor {
|
@objc var red: UIColor {
|
||||||
UIColor(named: "red")!
|
UIColor(named: "red")!
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Color green_alpha_50 is #A000FF00 (light) or #A000FF00 (dark)"
|
/// Color green_alpha_50 is #A000FF00 (light) or #A000FF00 (dark)"
|
||||||
static var green_alpha_50: UIColor {
|
@objc var green_alpha_50: UIColor {
|
||||||
UIColor(named: "green_alpha_50")!
|
UIColor(named: "green_alpha_50")!
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Color blue_light_dark is #0000FF (light) or #0000AA (dark)"
|
/// Color blue_light_dark is #0000FF (light) or #0000AA (dark)"
|
||||||
static var blue_light_dark: UIColor {
|
@objc var blue_light_dark: UIColor {
|
||||||
UIColor(named: "blue_light_dark")!
|
UIColor(named: "blue_light_dark")!
|
||||||
}
|
}
|
||||||
}
|
}
|
61
SampleFiles/Fonts/Generated/FontYolo+GenAllScript.swift
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
// Generated by ResgenSwift.Fonts 2.1.0
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
extension FontYolo {
|
||||||
|
|
||||||
|
enum FontName: String {
|
||||||
|
case LatoItalic = "Lato-Italic"
|
||||||
|
case LatoLightItalic = "Lato-LightItalic"
|
||||||
|
case LatoHairline = "Lato-Hairline"
|
||||||
|
case LatoBold = "Lato-Bold"
|
||||||
|
case LatoBlack = "Lato-Black"
|
||||||
|
case LatoRegular = "Lato-Regular"
|
||||||
|
case LatoBlackItalic = "Lato-BlackItalic"
|
||||||
|
case LatoBoldItalic = "Lato-BoldItalic"
|
||||||
|
case LatoLight = "Lato-Light"
|
||||||
|
case LatoHairlineItalic = "Lato-HairlineItalic"
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Getter
|
||||||
|
|
||||||
|
func LatoItalic(withSize size: CGFloat) -> Font {
|
||||||
|
Font.custom(FontName.LatoItalic.rawValue, size: size)
|
||||||
|
}
|
||||||
|
|
||||||
|
func LatoLightItalic(withSize size: CGFloat) -> Font {
|
||||||
|
Font.custom(FontName.LatoLightItalic.rawValue, size: size)
|
||||||
|
}
|
||||||
|
|
||||||
|
func LatoHairline(withSize size: CGFloat) -> Font {
|
||||||
|
Font.custom(FontName.LatoHairline.rawValue, size: size)
|
||||||
|
}
|
||||||
|
|
||||||
|
func LatoBold(withSize size: CGFloat) -> Font {
|
||||||
|
Font.custom(FontName.LatoBold.rawValue, size: size)
|
||||||
|
}
|
||||||
|
|
||||||
|
func LatoBlack(withSize size: CGFloat) -> Font {
|
||||||
|
Font.custom(FontName.LatoBlack.rawValue, size: size)
|
||||||
|
}
|
||||||
|
|
||||||
|
func LatoRegular(withSize size: CGFloat) -> Font {
|
||||||
|
Font.custom(FontName.LatoRegular.rawValue, size: size)
|
||||||
|
}
|
||||||
|
|
||||||
|
func LatoBlackItalic(withSize size: CGFloat) -> Font {
|
||||||
|
Font.custom(FontName.LatoBlackItalic.rawValue, size: size)
|
||||||
|
}
|
||||||
|
|
||||||
|
func LatoBoldItalic(withSize size: CGFloat) -> Font {
|
||||||
|
Font.custom(FontName.LatoBoldItalic.rawValue, size: size)
|
||||||
|
}
|
||||||
|
|
||||||
|
func LatoLight(withSize size: CGFloat) -> Font {
|
||||||
|
Font.custom(FontName.LatoLight.rawValue, size: size)
|
||||||
|
}
|
||||||
|
|
||||||
|
func LatoHairlineItalic(withSize size: CGFloat) -> Font {
|
||||||
|
Font.custom(FontName.LatoHairlineItalic.rawValue, size: size)
|
||||||
|
}
|
||||||
|
}
|
@ -1,62 +0,0 @@
|
|||||||
// Generated from FontToolCore
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
extension R2Font {
|
|
||||||
|
|
||||||
enum FontName: String {
|
|
||||||
case LatoItalic = "Lato-Italic"
|
|
||||||
case LatoLightItalic = "Lato-LightItalic"
|
|
||||||
case LatoHairline = "Lato-Hairline"
|
|
||||||
case LatoBold = "Lato-Bold"
|
|
||||||
case LatoBlack = "Lato-Black"
|
|
||||||
case LatoRegular = "Lato-Regular"
|
|
||||||
case LatoBlackItalic = "Lato-BlackItalic"
|
|
||||||
case LatoBoldItalic = "Lato-BoldItalic"
|
|
||||||
case LatoLight = "Lato-Light"
|
|
||||||
case LatoHairlineItalic = "Lato-HairlineItalic"
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Getter
|
|
||||||
|
|
||||||
func LatoItalic(withSize size: CGFloat) -> UIFont {
|
|
||||||
UIFont(name: FontName.LatoItalic.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
func LatoLightItalic(withSize size: CGFloat) -> UIFont {
|
|
||||||
UIFont(name: FontName.LatoLightItalic.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
func LatoHairline(withSize size: CGFloat) -> UIFont {
|
|
||||||
UIFont(name: FontName.LatoHairline.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
func LatoBold(withSize size: CGFloat) -> UIFont {
|
|
||||||
UIFont(name: FontName.LatoBold.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
func LatoBlack(withSize size: CGFloat) -> UIFont {
|
|
||||||
UIFont(name: FontName.LatoBlack.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
func LatoRegular(withSize size: CGFloat) -> UIFont {
|
|
||||||
UIFont(name: FontName.LatoRegular.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
func LatoBlackItalic(withSize size: CGFloat) -> UIFont {
|
|
||||||
UIFont(name: FontName.LatoBlackItalic.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
func LatoBoldItalic(withSize size: CGFloat) -> UIFont {
|
|
||||||
UIFont(name: FontName.LatoBoldItalic.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
func LatoLight(withSize size: CGFloat) -> UIFont {
|
|
||||||
UIFont(name: FontName.LatoLight.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
func LatoHairlineItalic(withSize size: CGFloat) -> UIFont {
|
|
||||||
UIFont(name: FontName.LatoHairlineItalic.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
// Generated from FontToolCore
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
extension R2Font {
|
|
||||||
|
|
||||||
enum FontName: String {
|
|
||||||
case LatoItalic = "Lato-Italic"
|
|
||||||
case LatoLightItalic = "Lato-LightItalic"
|
|
||||||
case LatoHairline = "Lato-Hairline"
|
|
||||||
case LatoBold = "Lato-Bold"
|
|
||||||
case LatoBlack = "Lato-Black"
|
|
||||||
case LatoRegular = "Lato-Regular"
|
|
||||||
case LatoBlackItalic = "Lato-BlackItalic"
|
|
||||||
case LatoBoldItalic = "Lato-BoldItalic"
|
|
||||||
case LatoLight = "Lato-Light"
|
|
||||||
case LatoHairlineItalic = "Lato-HairlineItalic"
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Getter
|
|
||||||
|
|
||||||
func LatoItalic(withSize size: CGFloat) -> UIFont {
|
|
||||||
UIFont(name: FontName.LatoItalic.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
func LatoLightItalic(withSize size: CGFloat) -> UIFont {
|
|
||||||
UIFont(name: FontName.LatoLightItalic.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
func LatoHairline(withSize size: CGFloat) -> UIFont {
|
|
||||||
UIFont(name: FontName.LatoHairline.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
func LatoBold(withSize size: CGFloat) -> UIFont {
|
|
||||||
UIFont(name: FontName.LatoBold.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
func LatoBlack(withSize size: CGFloat) -> UIFont {
|
|
||||||
UIFont(name: FontName.LatoBlack.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
func LatoRegular(withSize size: CGFloat) -> UIFont {
|
|
||||||
UIFont(name: FontName.LatoRegular.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
func LatoBlackItalic(withSize size: CGFloat) -> UIFont {
|
|
||||||
UIFont(name: FontName.LatoBlackItalic.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
func LatoBoldItalic(withSize size: CGFloat) -> UIFont {
|
|
||||||
UIFont(name: FontName.LatoBoldItalic.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
func LatoLight(withSize size: CGFloat) -> UIFont {
|
|
||||||
UIFont(name: FontName.LatoLight.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
func LatoHairlineItalic(withSize size: CGFloat) -> UIFont {
|
|
||||||
UIFont(name: FontName.LatoHairlineItalic.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,63 +0,0 @@
|
|||||||
// Lato/Lato-Italic.ttf Lato/Lato-LightItalic.ttf Lato/Lato-Thin.ttf Lato/Lato-Bold.ttf Lato/Lato-Black.ttf Lato/Lato-Regular.ttf Lato/Lato-BlackItalic.ttf Lato/Lato-BoldItalic.ttf Lato/Lato-Light.ttf Lato/Lato-ThinItalic.ttf
|
|
||||||
// Generated from FontToolCore
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
extension UIFont {
|
|
||||||
|
|
||||||
enum FontName: String {
|
|
||||||
case LatoItalic = "Lato-Italic"
|
|
||||||
case LatoLightItalic = "Lato-LightItalic"
|
|
||||||
case LatoHairline = "Lato-Hairline"
|
|
||||||
case LatoBold = "Lato-Bold"
|
|
||||||
case LatoBlack = "Lato-Black"
|
|
||||||
case LatoRegular = "Lato-Regular"
|
|
||||||
case LatoBlackItalic = "Lato-BlackItalic"
|
|
||||||
case LatoBoldItalic = "Lato-BoldItalic"
|
|
||||||
case LatoLight = "Lato-Light"
|
|
||||||
case LatoHairlineItalic = "Lato-HairlineItalic"
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Getter
|
|
||||||
|
|
||||||
static let LatoItalic: ((_ size: CGFloat) -> UIFont) = { size in
|
|
||||||
UIFont(name: FontName.LatoItalic.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
static let LatoLightItalic: ((_ size: CGFloat) -> UIFont) = { size in
|
|
||||||
UIFont(name: FontName.LatoLightItalic.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
static let LatoHairline: ((_ size: CGFloat) -> UIFont) = { size in
|
|
||||||
UIFont(name: FontName.LatoHairline.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
static let LatoBold: ((_ size: CGFloat) -> UIFont) = { size in
|
|
||||||
UIFont(name: FontName.LatoBold.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
static let LatoBlack: ((_ size: CGFloat) -> UIFont) = { size in
|
|
||||||
UIFont(name: FontName.LatoBlack.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
static let LatoRegular: ((_ size: CGFloat) -> UIFont) = { size in
|
|
||||||
UIFont(name: FontName.LatoRegular.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
static let LatoBlackItalic: ((_ size: CGFloat) -> UIFont) = { size in
|
|
||||||
UIFont(name: FontName.LatoBlackItalic.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
static let LatoBoldItalic: ((_ size: CGFloat) -> UIFont) = { size in
|
|
||||||
UIFont(name: FontName.LatoBoldItalic.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
static let LatoLight: ((_ size: CGFloat) -> UIFont) = { size in
|
|
||||||
UIFont(name: FontName.LatoLight.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
static let LatoHairlineItalic: ((_ size: CGFloat) -> UIFont) = { size in
|
|
||||||
UIFont(name: FontName.LatoHairlineItalic.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
// Generated from FontToolCore
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
extension UIFont {
|
|
||||||
|
|
||||||
enum FontName: String {
|
|
||||||
case LatoItalic = "Lato-Italic"
|
|
||||||
case LatoLightItalic = "Lato-LightItalic"
|
|
||||||
case LatoHairline = "Lato-Hairline"
|
|
||||||
case LatoBold = "Lato-Bold"
|
|
||||||
case LatoBlack = "Lato-Black"
|
|
||||||
case LatoRegular = "Lato-Regular"
|
|
||||||
case LatoBlackItalic = "Lato-BlackItalic"
|
|
||||||
case LatoBoldItalic = "Lato-BoldItalic"
|
|
||||||
case LatoLight = "Lato-Light"
|
|
||||||
case LatoHairlineItalic = "Lato-HairlineItalic"
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Getter
|
|
||||||
|
|
||||||
static let LatoItalic: ((_ size: CGFloat) -> UIFont) = { size in
|
|
||||||
UIFont(name: FontName.LatoItalic.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
static let LatoLightItalic: ((_ size: CGFloat) -> UIFont) = { size in
|
|
||||||
UIFont(name: FontName.LatoLightItalic.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
static let LatoHairline: ((_ size: CGFloat) -> UIFont) = { size in
|
|
||||||
UIFont(name: FontName.LatoHairline.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
static let LatoBold: ((_ size: CGFloat) -> UIFont) = { size in
|
|
||||||
UIFont(name: FontName.LatoBold.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
static let LatoBlack: ((_ size: CGFloat) -> UIFont) = { size in
|
|
||||||
UIFont(name: FontName.LatoBlack.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
static let LatoRegular: ((_ size: CGFloat) -> UIFont) = { size in
|
|
||||||
UIFont(name: FontName.LatoRegular.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
static let LatoBlackItalic: ((_ size: CGFloat) -> UIFont) = { size in
|
|
||||||
UIFont(name: FontName.LatoBlackItalic.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
static let LatoBoldItalic: ((_ size: CGFloat) -> UIFont) = { size in
|
|
||||||
UIFont(name: FontName.LatoBoldItalic.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
static let LatoLight: ((_ size: CGFloat) -> UIFont) = { size in
|
|
||||||
UIFont(name: FontName.LatoLight.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
static let LatoHairlineItalic: ((_ size: CGFloat) -> UIFont) = { size in
|
|
||||||
UIFont(name: FontName.LatoHairlineItalic.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,61 +0,0 @@
|
|||||||
// Generated by ResgenSwift.FontTool 1.0.0
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
extension UIFont {
|
|
||||||
|
|
||||||
enum FontName: String {
|
|
||||||
case LatoItalic = "Lato-Italic"
|
|
||||||
case LatoLightItalic = "Lato-LightItalic"
|
|
||||||
case LatoHairline = "Lato-Hairline"
|
|
||||||
case LatoBold = "Lato-Bold"
|
|
||||||
case LatoBlack = "Lato-Black"
|
|
||||||
case LatoRegular = "Lato-Regular"
|
|
||||||
case LatoBlackItalic = "Lato-BlackItalic"
|
|
||||||
case LatoBoldItalic = "Lato-BoldItalic"
|
|
||||||
case LatoLight = "Lato-Light"
|
|
||||||
case LatoHairlineItalic = "Lato-HairlineItalic"
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Getter
|
|
||||||
|
|
||||||
static let LatoItalic: ((_ size: CGFloat) -> UIFont) = { size in
|
|
||||||
UIFont(name: FontName.LatoItalic.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
static let LatoLightItalic: ((_ size: CGFloat) -> UIFont) = { size in
|
|
||||||
UIFont(name: FontName.LatoLightItalic.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
static let LatoHairline: ((_ size: CGFloat) -> UIFont) = { size in
|
|
||||||
UIFont(name: FontName.LatoHairline.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
static let LatoBold: ((_ size: CGFloat) -> UIFont) = { size in
|
|
||||||
UIFont(name: FontName.LatoBold.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
static let LatoBlack: ((_ size: CGFloat) -> UIFont) = { size in
|
|
||||||
UIFont(name: FontName.LatoBlack.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
static let LatoRegular: ((_ size: CGFloat) -> UIFont) = { size in
|
|
||||||
UIFont(name: FontName.LatoRegular.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
static let LatoBlackItalic: ((_ size: CGFloat) -> UIFont) = { size in
|
|
||||||
UIFont(name: FontName.LatoBlackItalic.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
static let LatoBoldItalic: ((_ size: CGFloat) -> UIFont) = { size in
|
|
||||||
UIFont(name: FontName.LatoBoldItalic.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
static let LatoLight: ((_ size: CGFloat) -> UIFont) = { size in
|
|
||||||
UIFont(name: FontName.LatoLight.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
static let LatoHairlineItalic: ((_ size: CGFloat) -> UIFont) = { size in
|
|
||||||
UIFont(name: FontName.LatoHairlineItalic.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
}
|
|
61
SampleFiles/Fonts/Generated/UIFontYolo+GenAllScript.swift
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
// Generated by ResgenSwift.Fonts 2.1.0
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
extension UIFontYolo {
|
||||||
|
|
||||||
|
enum FontName: String {
|
||||||
|
case LatoItalic = "Lato-Italic"
|
||||||
|
case LatoLightItalic = "Lato-LightItalic"
|
||||||
|
case LatoHairline = "Lato-Hairline"
|
||||||
|
case LatoBold = "Lato-Bold"
|
||||||
|
case LatoBlack = "Lato-Black"
|
||||||
|
case LatoRegular = "Lato-Regular"
|
||||||
|
case LatoBlackItalic = "Lato-BlackItalic"
|
||||||
|
case LatoBoldItalic = "Lato-BoldItalic"
|
||||||
|
case LatoLight = "Lato-Light"
|
||||||
|
case LatoHairlineItalic = "Lato-HairlineItalic"
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Getter
|
||||||
|
|
||||||
|
func LatoItalic(withSize size: CGFloat) -> UIFont {
|
||||||
|
UIFont(name: FontName.LatoItalic.rawValue, size: size)!
|
||||||
|
}
|
||||||
|
|
||||||
|
func LatoLightItalic(withSize size: CGFloat) -> UIFont {
|
||||||
|
UIFont(name: FontName.LatoLightItalic.rawValue, size: size)!
|
||||||
|
}
|
||||||
|
|
||||||
|
func LatoHairline(withSize size: CGFloat) -> UIFont {
|
||||||
|
UIFont(name: FontName.LatoHairline.rawValue, size: size)!
|
||||||
|
}
|
||||||
|
|
||||||
|
func LatoBold(withSize size: CGFloat) -> UIFont {
|
||||||
|
UIFont(name: FontName.LatoBold.rawValue, size: size)!
|
||||||
|
}
|
||||||
|
|
||||||
|
func LatoBlack(withSize size: CGFloat) -> UIFont {
|
||||||
|
UIFont(name: FontName.LatoBlack.rawValue, size: size)!
|
||||||
|
}
|
||||||
|
|
||||||
|
func LatoRegular(withSize size: CGFloat) -> UIFont {
|
||||||
|
UIFont(name: FontName.LatoRegular.rawValue, size: size)!
|
||||||
|
}
|
||||||
|
|
||||||
|
func LatoBlackItalic(withSize size: CGFloat) -> UIFont {
|
||||||
|
UIFont(name: FontName.LatoBlackItalic.rawValue, size: size)!
|
||||||
|
}
|
||||||
|
|
||||||
|
func LatoBoldItalic(withSize size: CGFloat) -> UIFont {
|
||||||
|
UIFont(name: FontName.LatoBoldItalic.rawValue, size: size)!
|
||||||
|
}
|
||||||
|
|
||||||
|
func LatoLight(withSize size: CGFloat) -> UIFont {
|
||||||
|
UIFont(name: FontName.LatoLight.rawValue, size: size)!
|
||||||
|
}
|
||||||
|
|
||||||
|
func LatoHairlineItalic(withSize size: CGFloat) -> UIFont {
|
||||||
|
UIFont(name: FontName.LatoHairlineItalic.rawValue, size: size)!
|
||||||
|
}
|
||||||
|
}
|
@ -1,62 +0,0 @@
|
|||||||
// Generated from FontToolCore
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
extension UIFont {
|
|
||||||
|
|
||||||
enum FontName: String {
|
|
||||||
case LatoItalic = "Lato-Italic"
|
|
||||||
case LatoLightItalic = "Lato-LightItalic"
|
|
||||||
case LatoHairline = "Lato-Hairline"
|
|
||||||
case LatoBold = "Lato-Bold"
|
|
||||||
case LatoBlack = "Lato-Black"
|
|
||||||
case LatoRegular = "Lato-Regular"
|
|
||||||
case LatoBlackItalic = "Lato-BlackItalic"
|
|
||||||
case LatoBoldItalic = "Lato-BoldItalic"
|
|
||||||
case LatoLight = "Lato-Light"
|
|
||||||
case LatoHairlineItalic = "Lato-HairlineItalic"
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Getter
|
|
||||||
|
|
||||||
static let LatoItalic: ((_ size: CGFloat) -> UIFont) = { size in
|
|
||||||
UIFont(name: FontName.LatoItalic.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
static let LatoLightItalic: ((_ size: CGFloat) -> UIFont) = { size in
|
|
||||||
UIFont(name: FontName.LatoLightItalic.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
static let LatoHairline: ((_ size: CGFloat) -> UIFont) = { size in
|
|
||||||
UIFont(name: FontName.LatoHairline.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
static let LatoBold: ((_ size: CGFloat) -> UIFont) = { size in
|
|
||||||
UIFont(name: FontName.LatoBold.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
static let LatoBlack: ((_ size: CGFloat) -> UIFont) = { size in
|
|
||||||
UIFont(name: FontName.LatoBlack.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
static let LatoRegular: ((_ size: CGFloat) -> UIFont) = { size in
|
|
||||||
UIFont(name: FontName.LatoRegular.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
static let LatoBlackItalic: ((_ size: CGFloat) -> UIFont) = { size in
|
|
||||||
UIFont(name: FontName.LatoBlackItalic.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
static let LatoBoldItalic: ((_ size: CGFloat) -> UIFont) = { size in
|
|
||||||
UIFont(name: FontName.LatoBoldItalic.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
static let LatoLight: ((_ size: CGFloat) -> UIFont) = { size in
|
|
||||||
UIFont(name: FontName.LatoLight.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
static let LatoHairlineItalic: ((_ size: CGFloat) -> UIFont) = { size in
|
|
||||||
UIFont(name: FontName.LatoHairlineItalic.rawValue, size: size)!
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
21
SampleFiles/Fonts/Generated/test.plist
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>NewArr</key>
|
||||||
|
<array/>
|
||||||
|
<key>UIAppFonts</key>
|
||||||
|
<array>
|
||||||
|
<string>Lato-Italic.ttf</string>
|
||||||
|
<string>Lato-LightItalic.ttf</string>
|
||||||
|
<string>Lato-Thin.ttf</string>
|
||||||
|
<string>Lato-Bold.ttf</string>
|
||||||
|
<string>Lato-Black.ttf</string>
|
||||||
|
<string>Lato-Regular.ttf</string>
|
||||||
|
<string>Lato-BlackItalic.ttf</string>
|
||||||
|
<string>Lato-BoldItalic.ttf</string>
|
||||||
|
<string>Lato-Light.ttf</string>
|
||||||
|
<string>Lato-ThinItalic.ttf</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
21
SampleFiles/Fonts/Generated/test2.plist
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>NewArr</key>
|
||||||
|
<array/>
|
||||||
|
<key>UIAppFonts</key>
|
||||||
|
<array>
|
||||||
|
<string>Lato-Italic.ttf</string>
|
||||||
|
<string>Lato-LightItalic.ttf</string>
|
||||||
|
<string>Lato-Thin.ttf</string>
|
||||||
|
<string>Lato-Bold.ttf</string>
|
||||||
|
<string>Lato-Black.ttf</string>
|
||||||
|
<string>Lato-Regular.ttf</string>
|
||||||
|
<string>Lato-BlackItalic.ttf</string>
|
||||||
|
<string>Lato-BoldItalic.ttf</string>
|
||||||
|
<string>Lato-Light.ttf</string>
|
||||||
|
<string>Lato-ThinItalic.ttf</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
31
SampleFiles/Images/Generated/ImageYolo+GenAllScript.swift
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// Generated by ResgenSwift.Images 2.1.0
|
||||||
|
// Images from sampleImages
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
extension ImageYolo {
|
||||||
|
|
||||||
|
var article_notification_pull_detail: Image {
|
||||||
|
Image("article_notification_pull_detail")
|
||||||
|
}
|
||||||
|
|
||||||
|
var article_notification_pull: Image {
|
||||||
|
Image("article_notification_pull")
|
||||||
|
}
|
||||||
|
|
||||||
|
var new_article: Image {
|
||||||
|
Image("new_article")
|
||||||
|
}
|
||||||
|
|
||||||
|
var welcome_background: Image {
|
||||||
|
Image("welcome_background")
|
||||||
|
}
|
||||||
|
|
||||||
|
var article_trash: Image {
|
||||||
|
Image("article_trash")
|
||||||
|
}
|
||||||
|
|
||||||
|
var ic_close_article: Image {
|
||||||
|
Image("ic_close_article")
|
||||||
|
}
|
||||||
|
}
|
@ -1,32 +1,31 @@
|
|||||||
// Generated by ResgenSwift.Imagium 1.0.0
|
// Generated by ResgenSwift.Images 1.2
|
||||||
// Images from sampleImages
|
// Images from sampleImages
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
extension UIImage {
|
extension UIImage {
|
||||||
|
|
||||||
static var article_notification_pull_detail: UIImage {
|
var article_notification_pull_detail: UIImage {
|
||||||
UIImage(named: "article_notification_pull_detail")!
|
UIImage(named: "article_notification_pull_detail")!
|
||||||
}
|
}
|
||||||
|
|
||||||
static var article_notification_pull: UIImage {
|
var article_notification_pull: UIImage {
|
||||||
UIImage(named: "article_notification_pull")!
|
UIImage(named: "article_notification_pull")!
|
||||||
}
|
}
|
||||||
|
|
||||||
static var new_article: UIImage {
|
var new_article: UIImage {
|
||||||
UIImage(named: "new_article")!
|
UIImage(named: "new_article")!
|
||||||
}
|
}
|
||||||
|
|
||||||
static var welcome_background: UIImage {
|
var welcome_background: UIImage {
|
||||||
UIImage(named: "welcome_background")!
|
UIImage(named: "welcome_background")!
|
||||||
}
|
}
|
||||||
|
|
||||||
static var article_trash: UIImage {
|
var article_trash: UIImage {
|
||||||
UIImage(named: "article_trash")!
|
UIImage(named: "article_trash")!
|
||||||
}
|
}
|
||||||
|
|
||||||
static var ic_close_article: UIImage {
|
var ic_close_article: UIImage {
|
||||||
UIImage(named: "ic_close_article")!
|
UIImage(named: "ic_close_article")!
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
// Generated from Imagium at 2022-02-14 09:30:23 +0000
|
|
||||||
// Images from sampleImages
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
extension UIImage {
|
|
||||||
|
|
||||||
static var article_notification_pull_detail: UIImage {
|
|
||||||
UIImage(named: "article_notification_pull_detail")!
|
|
||||||
}
|
|
||||||
|
|
||||||
static var article_notification_pull: UIImage {
|
|
||||||
UIImage(named: "article_notification_pull")!
|
|
||||||
}
|
|
||||||
|
|
||||||
static var new_article: UIImage {
|
|
||||||
UIImage(named: "new_article")!
|
|
||||||
}
|
|
||||||
|
|
||||||
static var welcome_background: UIImage {
|
|
||||||
UIImage(named: "welcome_background")!
|
|
||||||
}
|
|
||||||
|
|
||||||
static var article_trash: UIImage {
|
|
||||||
UIImage(named: "article_trash")!
|
|
||||||
}
|
|
||||||
|
|
||||||
static var ic_close_article: UIImage {
|
|
||||||
UIImage(named: "ic_close_article")!
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
31
SampleFiles/Images/Generated/UIImageYolo+GenAllScript.swift
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// Generated by ResgenSwift.Images 2.1.0
|
||||||
|
// Images from sampleImages
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
extension UIImageYolo {
|
||||||
|
|
||||||
|
var article_notification_pull_detail: UIImage {
|
||||||
|
UIImage(named: "article_notification_pull_detail")!
|
||||||
|
}
|
||||||
|
|
||||||
|
var article_notification_pull: UIImage {
|
||||||
|
UIImage(named: "article_notification_pull")!
|
||||||
|
}
|
||||||
|
|
||||||
|
var new_article: UIImage {
|
||||||
|
UIImage(named: "new_article")!
|
||||||
|
}
|
||||||
|
|
||||||
|
var welcome_background: UIImage {
|
||||||
|
UIImage(named: "welcome_background")!
|
||||||
|
}
|
||||||
|
|
||||||
|
var article_trash: UIImage {
|
||||||
|
UIImage(named: "article_trash")!
|
||||||
|
}
|
||||||
|
|
||||||
|
var ic_close_article: UIImage {
|
||||||
|
UIImage(named: "ic_close_article")!
|
||||||
|
}
|
||||||
|
}
|
@ -1,23 +1,16 @@
|
|||||||
{
|
{
|
||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
"idiom" : "universal",
|
"filename" : "article_notification_pull.svg",
|
||||||
"scale" : "1x",
|
"idiom" : "universal"
|
||||||
"filename" : "article_notification_pull.png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x",
|
|
||||||
"filename" : "article_notification_pull@2x.png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x",
|
|
||||||
"filename" : "article_notification_pull@3x.png"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
"version" : 1,
|
"author" : "ResgenSwift-Imagium",
|
||||||
"author" : "ResgenSwift-Imagium"
|
"version" : 1
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"preserves-vector-representation" : true,
|
||||||
|
"template-rendering-intent" : "original"
|
||||||
}
|
}
|
||||||
}
|
}
|
Before Width: | Height: | Size: 5.4 KiB |
@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="111px" height="69px" viewBox="0 0 111 69" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<title>article_notification_pull</title>
|
||||||
|
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<g id="article_notification_pull">
|
||||||
|
<path d="M81.130223,39.5772 C81.130223,39.5772 99.360223,32.4231 102.116223,33.2806 C104.871223,34.1381 111.204223,51.339 110.013223,51.8526 C108.758223,52.4481 102.001223,47.0974 102.001223,47.0974 C102.001223,47.0974 100.834223,50.2797 98.387023,49.5337 C95.939623,48.7877 92.897623,44.9219 92.897623,44.9219 C92.897623,44.9219 92.488923,47.0487 89.986323,46.4577 C87.483723,45.8667 83.726623,41.567 83.726623,41.567 L81.130223,39.5772 Z" id="Path" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||||
|
<path d="M99.259223,10.5629 C97.894023,10.5629 96.788623,9.4412 96.788623,8.0557 C96.788623,7.4619 96.333423,7 95.748223,7 C95.163023,7 94.707823,7.4619 94.707823,8.0557 C94.707823,9.4412 93.602423,10.5629 92.237023,10.5629 C91.651723,10.5629 91.196623,11.0247 91.196623,11.6185 C91.196623,12.2124 91.651723,12.6742 92.237023,12.6742 C93.602423,12.6742 94.707823,13.7959 94.707823,15.1814 C94.707823,15.7752 95.163023,16.2371 95.748223,16.2371 C96.333423,16.2371 96.788623,15.7752 96.788623,15.1814 C96.788623,13.7959 97.894023,12.6742 99.259223,12.6742 C99.844223,12.6742 100.300223,12.2124 100.300223,11.6185 C100.365223,11.0907 99.844223,10.5629 99.259223,10.5629 Z" id="Path" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||||
|
<path d="M81.193523,11.2243 C80.403523,10.8878 79.962423,9.9515 80.333423,9.0805 C80.502023,8.6847 80.287223,8.3125 79.971223,8.1779 C79.576223,8.0096 79.204323,8.2254 79.069423,8.5421 C78.732123,9.3338 77.796523,9.7772 76.927523,9.407 C76.532523,9.2387 76.160623,9.4544 76.025723,9.7711 C75.857023,10.167 76.071823,10.5391 76.387823,10.6738 C77.177923,11.0103 77.619023,11.9466 77.248023,12.8175 C77.079423,13.2134 77.294223,13.5855 77.610223,13.7201 C78.005223,13.8884 78.377123,13.6727 78.512023,13.356 C78.849323,12.5642 79.784923,12.1209 80.653923,12.4911 C81.048923,12.6594 81.420823,12.4436 81.555723,12.1269 C81.769623,11.8439 81.588623,11.3926 81.193523,11.2243 Z" id="Path" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||||
|
<line x1="12.845223" y1="48.192" x2="16.110123" y2="42" id="Path" stroke-opacity="0.8" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round"></line>
|
||||||
|
<ellipse id="Oval" fill-opacity="0.1" fill="#000000" fill-rule="nonzero" cx="50.345223" cy="67.5" rx="17.5" ry="1.5"></ellipse>
|
||||||
|
<line x1="14.517923" y1="53.5959" x2="17.782723" y2="47.404" id="Path" stroke-opacity="0.8" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round"></line>
|
||||||
|
<line x1="20.806323" y1="52.3897" x2="23.604823" y2="47.0823" id="Path" stroke-opacity="0.8" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round"></line>
|
||||||
|
<path d="M79.876623,1.78144 C79.226423,1.78144 78.641223,1.2536 78.641223,0.52783 C78.641223,0.19794 78.381123,0 78.121023,0 C77.795923,0 77.600823,0.26392 77.600823,0.52783 C77.600823,1.18763 77.080623,1.78144 76.365423,1.78144 C76.040323,1.78144 75.845223,2.04535 75.845223,2.30927 C75.845223,2.63917 76.105323,2.8371 76.365423,2.8371 C77.015623,2.8371 77.600823,3.3649 77.600823,4.0907 C77.600823,4.4206 77.860923,4.6185 78.121023,4.6185 C78.446123,4.6185 78.641223,4.3546 78.641223,4.0907 C78.641223,3.4309 79.161423,2.8371 79.876623,2.8371 C80.201823,2.8371 80.396823,2.57319 80.396823,2.30927 C80.461923,2.04535 80.201823,1.78144 79.876623,1.78144 Z" id="Path" fill="#FFFFFF" fill-rule="nonzero"></path>
|
||||||
|
<path d="M31.021323,19.8983 C31.021323,19.8983 19.126423,4.2537 16.379623,3.4694 C13.568923,2.76686 -0.694914027,14.1215 0.0264229726,15.2225 C0.756502973,16.3967 9.32942297,15.3706 9.32942297,15.3706 C9.32942297,15.3706 8.62499297,18.7201 11.136323,19.3843 C13.647723,20.0484 18.238723,18.3857 18.238723,18.3857 C18.238723,18.3857 17.521923,20.401 19.905523,21.2288 C22.289123,22.0567 27.760723,20.3628 27.760723,20.3628 L31.021323,19.8983 Z" id="Path" fill="#E0EDF3" fill-rule="nonzero"></path>
|
||||||
|
<path d="M89.710223,24.4503 L77.707723,58.391 C77.385623,59.3943 76.267023,59.8992 75.270023,59.5733 L21.900823,40.3589 C20.903823,40.033 20.400423,38.9059 20.722523,37.9026 L32.788923,3.88 C32.835323,3.6518 32.954323,3.4149 33.154823,3.2425 C33.674723,2.66083 34.456323,2.41884 35.299323,2.689 L88.604623,21.9853 C89.374923,22.2641 89.834523,23.0254 89.849423,23.7658 C89.875623,23.9852 89.829323,24.2134 89.710223,24.4503 Z" id="Path" fill="#FFD100" fill-rule="nonzero"></path>
|
||||||
|
<path d="M23.390523,38.0285 L52.772523,31.6916 C54.280623,31.3626 55.821223,31.9203 56.778023,33.1416 L75.925523,57.0459 L57.901923,31.4487 C56.910123,29.9348 55.061323,29.2655 53.361523,29.84 L23.390523,38.0285 Z" id="Path" fill="#DA9135" fill-rule="nonzero"></path>
|
||||||
|
<path d="M89.803023,23.9939 L54.122423,34.35 L33.108423,3.4707 C33.610823,2.74268 34.529023,2.41014 35.299323,2.68899 L88.604623,21.9853 C89.374923,22.2641 89.770623,23.1073 89.803023,23.9939 Z" id="Path" fill="#FFDF4F" fill-rule="nonzero"></path>
|
||||||
|
<path d="M34.370723,5.3975 L52.679723,32.148 C53.572623,33.4511 55.113223,34.0088 56.685323,33.598 L87.402923,24.8749 L56.375623,35.9356 C54.611823,36.592 52.771823,35.9959 51.835123,34.327 L34.370723,5.3975 Z" id="Path" fill="#DA9135" fill-rule="nonzero"></path>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 5.4 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 18 KiB |
@ -1,23 +1,16 @@
|
|||||||
{
|
{
|
||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
"idiom" : "universal",
|
"filename" : "article_notification_pull_detail.svg",
|
||||||
"scale" : "1x",
|
"idiom" : "universal"
|
||||||
"filename" : "article_notification_pull_detail.png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x",
|
|
||||||
"filename" : "article_notification_pull_detail@2x.png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x",
|
|
||||||
"filename" : "article_notification_pull_detail@3x.png"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
"version" : 1,
|
"author" : "ResgenSwift-Imagium",
|
||||||
"author" : "ResgenSwift-Imagium"
|
"version" : 1
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"preserves-vector-representation" : true,
|
||||||
|
"template-rendering-intent" : "original"
|
||||||
}
|
}
|
||||||
}
|
}
|
Before Width: | Height: | Size: 5.9 KiB |
@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="111px" height="69px" viewBox="0 0 111 69" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<title>article_notification_pull_detail</title>
|
||||||
|
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<g id="article_notification_pull_detail" transform="translate(0.000000, 0.000000)">
|
||||||
|
<path d="M81.1301131,39.5772 C81.1301131,39.5772 99.3602131,32.4231 102.116213,33.2806 C104.871213,34.1381 111.204213,51.339 110.013213,51.8526 C108.758213,52.4481 102.001213,47.0974 102.001213,47.0974 C102.001213,47.0974 100.834213,50.2797 98.3869131,49.5337 C95.9395131,48.7877 92.8975131,44.9219 92.8975131,44.9219 C92.8975131,44.9219 92.4888131,47.0487 89.9862131,46.4577 C87.4836131,45.8667 83.7265131,41.567 83.7265131,41.567 L81.1301131,39.5772 Z" id="Path" fill="#E0EDF3" fill-rule="nonzero"></path>
|
||||||
|
<path d="M99.2592131,10.5629 C97.8940131,10.5629 96.7886131,9.4412 96.7886131,8.0557 C96.7886131,7.4619 96.3334131,7 95.7482131,7 C95.1630131,7 94.7078131,7.4619 94.7078131,8.0557 C94.7078131,9.4412 93.6024131,10.5629 92.2370131,10.5629 C91.6517131,10.5629 91.1966131,11.0247 91.1966131,11.6185 C91.1966131,12.2124 91.6517131,12.6742 92.2370131,12.6742 C93.6024131,12.6742 94.7078131,13.7959 94.7078131,15.1814 C94.7078131,15.7752 95.1630131,16.2371 95.7482131,16.2371 C96.3334131,16.2371 96.7886131,15.7752 96.7886131,15.1814 C96.7886131,13.7959 97.8940131,12.6742 99.2592131,12.6742 C99.8442131,12.6742 100.300213,12.2124 100.300213,11.6185 C100.365213,11.0907 99.8442131,10.5629 99.2592131,10.5629 Z" id="Path" fill="#E0EDF3" fill-rule="nonzero"></path>
|
||||||
|
<path d="M81.1934131,11.2243 C80.4034131,10.8878 79.9623131,9.9515 80.3333131,9.0805 C80.5019131,8.6847 80.2871131,8.3125 79.9711131,8.1779 C79.5761131,8.0096 79.2042131,8.2254 79.0693131,8.5421 C78.7320131,9.3338 77.7964131,9.7772 76.9274131,9.407 C76.5324131,9.2387 76.1605131,9.4544 76.0256131,9.7711 C75.8569131,10.167 76.0717131,10.5391 76.3877131,10.6738 C77.1778131,11.0103 77.6189131,11.9466 77.2479131,12.8175 C77.0793131,13.2134 77.2941131,13.5855 77.6101131,13.7201 C78.0051131,13.8884 78.3770131,13.6727 78.5119131,13.356 C78.8492131,12.5642 79.7848131,12.1209 80.6538131,12.4911 C81.0488131,12.6594 81.4207131,12.4436 81.5556131,12.1269 C81.7695131,11.8439 81.5885131,11.3926 81.1934131,11.2243 Z" id="Path" fill="#E0EDF3" fill-rule="nonzero"></path>
|
||||||
|
<line x1="12.8452131" y1="48.192" x2="16.1101131" y2="42" id="Path" stroke-opacity="0.8" stroke="#E0EDF3" stroke-width="2" stroke-linecap="round"></line>
|
||||||
|
<path d="M50.3452131,69 C60.0102131,69 67.8452131,68.3284 67.8452131,67.5 C67.8452131,66.6716 60.0102131,66 50.3452131,66 C40.6802131,66 32.8452131,66.6716 32.8452131,67.5 C32.8452131,68.3284 40.6802131,69 50.3452131,69 Z" id="Path" fill-opacity="0.1" fill="#000000" fill-rule="nonzero"></path>
|
||||||
|
<line x1="14.5178131" y1="53.5959" x2="17.7826131" y2="47.404" id="Path" stroke-opacity="0.8" stroke="#E0EDF3" stroke-width="2" stroke-linecap="round"></line>
|
||||||
|
<line x1="20.8064131" y1="52.3897" x2="23.6049131" y2="47.0823" id="Path" stroke-opacity="0.8" stroke="#E0EDF3" stroke-width="2" stroke-linecap="round"></line>
|
||||||
|
<path d="M79.8766131,1.78144 C79.2264131,1.78144 78.6412131,1.2536 78.6412131,0.52783 C78.6412131,0.19794 78.3811131,0 78.1210131,0 C77.7959131,0 77.6008131,0.26392 77.6008131,0.52783 C77.6008131,1.18763 77.0806131,1.78144 76.3654131,1.78144 C76.0403131,1.78144 75.8452131,2.04535 75.8452131,2.30927 C75.8452131,2.63917 76.1053131,2.8371 76.3654131,2.8371 C77.0156131,2.8371 77.6008131,3.3649 77.6008131,4.0907 C77.6008131,4.4206 77.8609131,4.6185 78.1210131,4.6185 C78.4461131,4.6185 78.6412131,4.3546 78.6412131,4.0907 C78.6412131,3.4309 79.1614131,2.8371 79.8766131,2.8371 C80.2018131,2.8371 80.3968131,2.57319 80.3968131,2.30927 C80.4619131,2.04535 80.2018131,1.78144 79.8766131,1.78144 Z" id="Path" fill="#E0EDF3" fill-rule="nonzero"></path>
|
||||||
|
<path d="M31.0213131,19.8983 C31.0213131,19.8983 19.1264131,4.2537 16.3796131,3.4694 C13.5689131,2.76686 -0.694915869,14.1215 0.0264231309,15.2225 C0.756503131,16.3967 9.32941313,15.3706 9.32941313,15.3706 C9.32941313,15.3706 8.62499313,18.7201 11.1363131,19.3843 C13.6477131,20.0484 18.2387131,18.3857 18.2387131,18.3857 C18.2387131,18.3857 17.5219131,20.401 19.9055131,21.2288 C22.2891131,22.0567 27.7607131,20.3628 27.7607131,20.3628 L31.0213131,19.8983 Z" id="Path" fill="#E0EDF3" fill-rule="nonzero"></path>
|
||||||
|
<path d="M89.7103131,24.4503 L77.7078131,58.391 C77.3857131,59.3943 76.2671131,59.8992 75.2701131,59.5733 L21.9009131,40.3589 C20.9039131,40.033 20.4005131,38.9059 20.7226131,37.9026 L32.7890131,3.88 C32.8354131,3.6518 32.9544131,3.4149 33.1549131,3.2425 C33.6748131,2.66083 34.4564131,2.41884 35.2994131,2.689 L88.6047131,21.9853 C89.3750131,22.2641 89.8346131,23.0254 89.8495131,23.7658 C89.8757131,23.9852 89.8294131,24.2134 89.7103131,24.4503 Z" id="Path" fill="#FFD100" fill-rule="nonzero"></path>
|
||||||
|
<path d="M23.3906131,38.0285 L52.7726131,31.6916 C54.2807131,31.3626 55.8213131,31.9203 56.7781131,33.1416 L75.9256131,57.0459 L57.9020131,31.4487 C56.9102131,29.9348 55.0614131,29.2655 53.3616131,29.84 L23.3906131,38.0285 Z" id="Path" fill="#DA9135" fill-rule="nonzero"></path>
|
||||||
|
<path d="M89.8030131,23.9939 L54.1224131,34.35 L33.1084131,3.4707 C33.6108131,2.74268 34.5290131,2.41014 35.2993131,2.68899 L88.6046131,21.9853 C89.3749131,22.2641 89.7706131,23.1073 89.8030131,23.9939 Z" id="Path" fill="#FFDF4F" fill-rule="nonzero"></path>
|
||||||
|
<path d="M34.3706131,5.3975 L52.6796131,32.148 C53.5725131,33.4511 55.1131131,34.0088 56.6852131,33.598 L87.4028131,24.8749 L56.3755131,35.9356 C54.6117131,36.592 52.7717131,35.9959 51.8350131,34.327 L34.3706131,5.3975 Z" id="Path" fill="#DA9135" fill-rule="nonzero"></path>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 19 KiB |
@ -1,23 +1,16 @@
|
|||||||
{
|
{
|
||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
"idiom" : "universal",
|
"filename" : "article_trash.svg",
|
||||||
"scale" : "1x",
|
"idiom" : "universal"
|
||||||
"filename" : "article_trash.png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x",
|
|
||||||
"filename" : "article_trash@2x.png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x",
|
|
||||||
"filename" : "article_trash@3x.png"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
"version" : 1,
|
"author" : "ResgenSwift-Imagium",
|
||||||
"author" : "ResgenSwift-Imagium"
|
"version" : 1
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"preserves-vector-representation" : true,
|
||||||
|
"template-rendering-intent" : "original"
|
||||||
}
|
}
|
||||||
}
|
}
|
Before Width: | Height: | Size: 977 B |
4
SampleFiles/Images/imagium.xcassets/article_trash.imageset/article_trash.svg
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<svg width="37" height="41" viewBox="0 0 37 41" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M1 4.87183L5.45856 32.1227C5.68952 33.5346 6.30709 34.8549 7.24275 35.9371C8.17841 37.0194 9.3956 37.8213 10.7593 38.2539L11.3943 38.4553C15.9682 39.9073 20.8795 39.9073 25.4534 38.4553L26.0884 38.2539C27.4518 37.8215 28.6688 37.0199 29.6044 35.938C30.5401 34.8561 31.1578 33.5362 31.3891 32.1246L35.8476 4.87183" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
<path d="M18.4238 8.74392C28.0467 8.74392 35.8476 7.01039 35.8476 4.87196C35.8476 2.73354 28.0467 1 18.4238 1C8.80091 1 1 2.73354 1 4.87196C1 7.01039 8.80091 8.74392 18.4238 8.74392Z" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 772 B |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 2.9 KiB |
@ -1,23 +1,16 @@
|
|||||||
{
|
{
|
||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
"idiom" : "universal",
|
"filename" : "ic_close_article.svg",
|
||||||
"scale" : "1x",
|
"idiom" : "universal"
|
||||||
"filename" : "ic_close_article.png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x",
|
|
||||||
"filename" : "ic_close_article@2x.png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x",
|
|
||||||
"filename" : "ic_close_article@3x.png"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
"version" : 1,
|
"author" : "ResgenSwift-Imagium",
|
||||||
"author" : "ResgenSwift-Imagium"
|
"version" : 1
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"preserves-vector-representation" : true,
|
||||||
|
"template-rendering-intent" : "original"
|
||||||
}
|
}
|
||||||
}
|
}
|
Before Width: | Height: | Size: 330 B |
4
SampleFiles/Images/imagium.xcassets/ic_close_article.imageset/ic_close_article.svg
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M11.41 1.41L10 0L5.705 4.295L1.41 0L0 1.41L4.295 5.705L0 10L1.41 11.41L5.705 7.115L10 11.41L11.41 10L7.115 5.705L11.41 1.41Z"
|
||||||
|
fill="#EFF2F4" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 268 B |
Before Width: | Height: | Size: 513 B |
Before Width: | Height: | Size: 647 B |
@ -1,23 +1,16 @@
|
|||||||
{
|
{
|
||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
"idiom" : "universal",
|
"filename" : "new_article.svg",
|
||||||
"scale" : "1x",
|
"idiom" : "universal"
|
||||||
"filename" : "new_article.png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "2x",
|
|
||||||
"filename" : "new_article@2x.png"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x",
|
|
||||||
"filename" : "new_article@3x.png"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
"version" : 1,
|
"author" : "ResgenSwift-Imagium",
|
||||||
"author" : "ResgenSwift-Imagium"
|
"version" : 1
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"preserves-vector-representation" : true,
|
||||||
|
"template-rendering-intent" : "original"
|
||||||
}
|
}
|
||||||
}
|
}
|
Before Width: | Height: | Size: 6.0 KiB |
54
SampleFiles/Images/imagium.xcassets/new_article.imageset/new_article.svg
vendored
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
<svg width="102" height="69" viewBox="0 0 102 69" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M100.089 35.765H72.7522V66.626H100.089V35.765Z" fill="#DEF1F8"/>
|
||||||
|
<path d="M89.1215 46.2884H56.9285V66.6806H89.1215V46.2884Z" fill="#FAFAFA"/>
|
||||||
|
<path d="M100.089 46.2883H89.1216V66.6805H100.089V46.2883Z" fill="#DEF1F8"/>
|
||||||
|
<path d="M84.9199 49.5053H82.8464V53.1039H84.9199V49.5053Z" fill="#DEF1F8"/>
|
||||||
|
<path d="M80.8276 49.5053H78.7542V53.1039H80.8276V49.5053Z" fill="#DEF1F8"/>
|
||||||
|
<path d="M62.8759 60.3557H60.8025V63.9543H62.8759V60.3557Z" fill="#DEF1F8"/>
|
||||||
|
<path d="M97.7973 37.7279H95.7239V41.3266H97.7973V37.7279Z" fill="#FAFAFA"/>
|
||||||
|
<path d="M76.9538 37.7279H74.8804V41.3266H76.9538V37.7279Z" fill="#FAFAFA"/>
|
||||||
|
<path d="M36.4457 27.1985H101.509C101.728 25.2457 100.03 23.1303 98.1679 22.4794C96.251 21.8285 94.1699 21.72 92.4721 20.6351C90.3362 19.2791 89.1861 16.5127 86.7216 15.8076C84.3666 15.1566 82.0117 16.7839 79.5472 16.8924C74.5086 17.1094 70.6202 10.8715 65.6364 11.7394C62.2956 12.336 60.3788 15.8076 57.3666 17.3263C54.8473 18.6282 51.8352 18.3569 48.9873 18.7366C44.1678 19.4418 39.2388 23.3472 36.4457 27.1985Z" fill="#DEF1F8"/>
|
||||||
|
<path d="M0.455092 25.2296L32.2995 0.974118C33.9893 -0.324706 36.3288 -0.324706 38.0185 0.974118L69.8305 25.2296C70.1229 25.4569 70.2854 25.8141 70.2854 26.1713V67.7661C70.2854 68.4155 69.7655 68.9351 69.1156 68.9351H1.20246C0.552574 68.9351 0.0326646 68.4155 0.0326646 67.7661V26.1713C0.0001703 25.8141 0.162643 25.4569 0.455092 25.2296Z" fill="url(#paint0_linear)"/>
|
||||||
|
<path d="M53.973 13.183H16.3121L10.5281 17.5666V68.935H59.7245V17.5666L53.973 13.183Z" fill="#FDB918"/>
|
||||||
|
<path d="M55.9878 6.55914H14.2651C12.8353 6.55914 11.6655 7.72808 11.6655 9.15679V64.7789C11.6655 66.2076 12.8353 67.3765 14.2651 67.3765H55.9878C57.4176 67.3765 58.5874 66.2076 58.5874 64.7789V9.15679C58.5874 7.72808 57.4176 6.55914 55.9878 6.55914Z" fill="#FAFAFA"/>
|
||||||
|
<path d="M35.1265 47.2773L68.4657 67.8312C69.2455 68.3182 70.2528 67.7662 70.2528 66.8246V27.6975C70.2528 26.7883 69.2455 26.2039 68.4657 26.6909L35.1265 47.2773Z" fill="url(#paint1_linear)"/>
|
||||||
|
<path d="M0 27.6974V66.8245C0 67.7336 1.00732 68.3181 1.78719 67.831L35.1264 47.2772L1.78719 26.6908C1.00732 26.2038 0 26.7882 0 27.6974Z" fill="url(#paint2_linear)"/>
|
||||||
|
<path d="M40.3255 44.0627L38.1808 42.4392C36.3611 41.0429 33.8591 41.0429 32.0394 42.4392L29.8948 44.0627L35.0939 47.2773L40.3255 44.0627Z" fill="#E6E6E6"/>
|
||||||
|
<path d="M0 67.1491L35.1264 47.2771L29.9273 44.0626L0 67.1491Z" fill="#FDB918"/>
|
||||||
|
<path d="M70.2528 67.1491L35.1265 47.2771L40.3256 44.0626L70.2528 67.1491Z" fill="#FDB918"/>
|
||||||
|
<path d="M29.1147 17.3064C29.1147 17.3064 28.8872 19.969 35.1262 21.4627L33.7939 21.3977C33.7939 21.3977 29.6996 20.9756 28.7573 19.2547C27.8149 17.5337 29.1147 17.3064 29.1147 17.3064Z" fill="#FDB918"/>
|
||||||
|
<path d="M30.1545 15.9104C33.079 13.053 34.9312 21.4304 34.9637 21.4629C35.0287 21.4629 35.0937 21.4629 35.1587 21.4629C35.1587 21.4629 33.014 9.90333 29.4396 14.709C25.6053 19.7744 30.0245 21.3654 35.1262 21.4629C30.7719 21.333 26.8076 19.1574 30.1545 15.9104Z" fill="#FFE368"/>
|
||||||
|
<path d="M40.9753 17.3064C40.9753 17.3064 41.2028 19.969 34.9639 21.4627L36.2961 21.3977C36.2961 21.3977 40.3904 20.9756 41.3328 19.2547C42.2426 17.5337 40.9753 17.3064 40.9753 17.3064Z" fill="#FDB918"/>
|
||||||
|
<path d="M39.9353 15.9104C37.0108 13.053 35.1586 21.4304 35.1261 21.4629C35.0611 21.4629 34.9961 21.4629 34.9312 21.4629C34.9312 21.4629 37.0758 9.90333 40.6502 14.709C44.452 19.7744 40.0328 21.3654 34.9312 21.4629C39.3179 21.333 43.2497 19.1574 39.9353 15.9104Z" fill="#FFE368"/>
|
||||||
|
<path d="M27.3926 36.237V22.0798C27.3926 21.7226 27.685 21.4304 28.0425 21.4304H42.21C42.5674 21.4304 42.8599 21.7226 42.8599 22.0798V36.237C42.8599 36.5941 42.5674 36.8864 42.21 36.8864H28.0425C27.685 36.8864 27.3926 36.5941 27.3926 36.237Z" fill="#063881"/>
|
||||||
|
<path d="M26.3528 24.645V22.0798C26.3528 21.7226 26.6452 21.4304 27.0027 21.4304H43.2499C43.6073 21.4304 43.8997 21.7226 43.8997 22.0798V24.645C43.8997 25.0021 43.6073 25.2944 43.2499 25.2944H27.0027C26.6452 25.2944 26.3528 25.0021 26.3528 24.645Z" fill="#0B559E"/>
|
||||||
|
<path d="M37.5959 21.4304H32.6567V25.2944H37.5959V21.4304Z" fill="#FFE368"/>
|
||||||
|
<path d="M37.3035 25.2943H32.9492V36.8863H37.3035V25.2943Z" fill="#FDB918"/>
|
||||||
|
<path d="M1.5437 69H68.8777C70.1371 69 70.6539 67.4136 69.6204 66.6839L38.0042 44.6961C36.3248 43.427 33.9673 43.427 32.2557 44.6961L0.736338 66.6839C-0.232496 67.4136 0.284216 69 1.5437 69Z" fill="url(#paint3_linear)"/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="paint0_linear" x1="-0.00678347" y1="34.4878" x2="70.2659" y2="34.4878" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#FFE368"/>
|
||||||
|
<stop offset="0.3174" stop-color="#FFE265"/>
|
||||||
|
<stop offset="0.5172" stop-color="#FFDD5D"/>
|
||||||
|
<stop offset="0.6845" stop-color="#FED54E"/>
|
||||||
|
<stop offset="0.8339" stop-color="#FECB39"/>
|
||||||
|
<stop offset="0.9701" stop-color="#FDBD1F"/>
|
||||||
|
<stop offset="1" stop-color="#FDB918"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint1_linear" x1="9.77743" y1="11.1302" x2="102.999" y2="68.0143" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#FFE368"/>
|
||||||
|
<stop offset="0.3387" stop-color="#FED750"/>
|
||||||
|
<stop offset="1" stop-color="#FDB918"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint2_linear" x1="-8.63037" y1="41.2967" x2="84.5914" y2="98.1808" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#FFE368"/>
|
||||||
|
<stop offset="0.3387" stop-color="#FED750"/>
|
||||||
|
<stop offset="1" stop-color="#FDB918"/>
|
||||||
|
</linearGradient>
|
||||||
|
<linearGradient id="paint3_linear" x1="-10.5945" y1="38.7503" x2="87.7585" y2="99.7918" gradientUnits="userSpaceOnUse">
|
||||||
|
<stop stop-color="#FFE368"/>
|
||||||
|
<stop offset="0.3387" stop-color="#FED750"/>
|
||||||
|
<stop offset="1" stop-color="#FDB918"/>
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 20 KiB |
@ -1,23 +1,23 @@
|
|||||||
{
|
{
|
||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
|
"filename" : "welcome_background.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "1x",
|
"scale" : "1x"
|
||||||
"filename" : "welcome_background.png"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"filename" : "welcome_background@2x.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "2x",
|
"scale" : "2x"
|
||||||
"filename" : "welcome_background@2x.png"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"filename" : "welcome_background@3x.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"scale" : "3x",
|
"scale" : "3x"
|
||||||
"filename" : "welcome_background@3x.png"
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
"version" : 1,
|
"author" : "ResgenSwift-Imagium",
|
||||||
"author" : "ResgenSwift-Imagium"
|
"version" : 1
|
||||||
}
|
}
|
||||||
}
|
}
|
Before Width: | Height: | Size: 281 KiB After Width: | Height: | Size: 281 KiB |
Before Width: | Height: | Size: 813 KiB After Width: | Height: | Size: 813 KiB |
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 1.4 MiB |
@ -1,37 +0,0 @@
|
|||||||
// Generated from StringToolCore at 2022-01-10 08:27:11 +0000
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
fileprivate let kStringsFileName = "sampleStrings"
|
|
||||||
|
|
||||||
extension MyString {
|
|
||||||
|
|
||||||
// MARK: - Webservice
|
|
||||||
|
|
||||||
/// Translation in en :
|
|
||||||
/// en
|
|
||||||
var param_lang: String {
|
|
||||||
NSLocalizedString("param_lang", tableName: kStringsFileName, bundle: Bundle.main, value: "en", comment: "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Generic
|
|
||||||
|
|
||||||
/// Translation in en :
|
|
||||||
/// Back
|
|
||||||
var generic_back: String {
|
|
||||||
NSLocalizedString("generic_back", tableName: kStringsFileName, bundle: Bundle.main, value: "Back", comment: "")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Translation in en :
|
|
||||||
/// Loading data...
|
|
||||||
var generic_loading_data: String {
|
|
||||||
NSLocalizedString("generic_loading_data", tableName: kStringsFileName, bundle: Bundle.main, value: "Loading data...", comment: "")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Translation in en :
|
|
||||||
/// Welcome %@ !
|
|
||||||
var generic_welcome_firstname_format: String {
|
|
||||||
NSLocalizedString("generic_welcome_firstname_format", tableName: kStringsFileName, bundle: Bundle.main, value: "Welcome %@ !", comment: "")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,4 +1,4 @@
|
|||||||
// Generated by ResgenSwift.Strings.Stringium 1.0.0
|
// Generated by ResgenSwift.Strings.Stringium 2.1.0
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
@ -6,60 +6,101 @@ fileprivate let kStringsFileName = "sampleStrings"
|
|||||||
|
|
||||||
extension String {
|
extension String {
|
||||||
|
|
||||||
// MARK: - Webservice
|
enum KeyGenAllScript: String {
|
||||||
|
case param_lang = "param_lang"
|
||||||
|
case generic_back = "generic_back"
|
||||||
|
case generic_loading_data = "generic_loading_data"
|
||||||
|
case generic_welcome_firstname_format = "generic_welcome_firstname_format"
|
||||||
|
case test_equal_symbol = "test_equal_symbol"
|
||||||
|
case placeholders_test_one = "placeholders_test_one"
|
||||||
|
|
||||||
|
var keyPath: KeyPath<String, String> {
|
||||||
|
switch self {
|
||||||
|
case .param_lang: return \String.param_lang
|
||||||
|
case .generic_back: return \String.generic_back
|
||||||
|
case .generic_loading_data: return \String.generic_loading_data
|
||||||
|
case .generic_welcome_firstname_format: return \String.generic_welcome_firstname_format
|
||||||
|
case .test_equal_symbol: return \String.test_equal_symbol
|
||||||
|
case .placeholders_test_one: return \String.placeholders_test_one
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Webservice
|
||||||
|
|
||||||
/// Translation in en :
|
/// Translation in en :
|
||||||
/// en
|
/// en
|
||||||
static var param_lang: String {
|
///
|
||||||
|
/// Comment :
|
||||||
|
/// No comment
|
||||||
|
var param_lang: String {
|
||||||
NSLocalizedString("param_lang", tableName: kStringsFileName, bundle: Bundle.main, value: "en", comment: "")
|
NSLocalizedString("param_lang", tableName: kStringsFileName, bundle: Bundle.main, value: "en", comment: "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Generic
|
// MARK: - Generic
|
||||||
|
|
||||||
/// Translation in en :
|
/// Translation in en :
|
||||||
/// Back
|
/// Back
|
||||||
static var generic_back: String {
|
///
|
||||||
|
/// Comment :
|
||||||
|
/// No comment
|
||||||
|
var generic_back: String {
|
||||||
NSLocalizedString("generic_back", tableName: kStringsFileName, bundle: Bundle.main, value: "Back", comment: "")
|
NSLocalizedString("generic_back", tableName: kStringsFileName, bundle: Bundle.main, value: "Back", comment: "")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Translation in en :
|
/// Translation in en :
|
||||||
/// Loading data...
|
/// Loading data...
|
||||||
static var generic_loading_data: String {
|
///
|
||||||
|
/// Comment :
|
||||||
|
/// No comment
|
||||||
|
var generic_loading_data: String {
|
||||||
NSLocalizedString("generic_loading_data", tableName: kStringsFileName, bundle: Bundle.main, value: "Loading data...", comment: "")
|
NSLocalizedString("generic_loading_data", tableName: kStringsFileName, bundle: Bundle.main, value: "Loading data...", comment: "")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Translation in en :
|
/// Translation in en :
|
||||||
/// Welcome \"%@\" !
|
/// Welcome \"%@\" !
|
||||||
static var generic_welcome_firstname_format: String {
|
///
|
||||||
|
/// Comment :
|
||||||
|
/// No comment
|
||||||
|
var generic_welcome_firstname_format: String {
|
||||||
NSLocalizedString("generic_welcome_firstname_format", tableName: kStringsFileName, bundle: Bundle.main, value: "Welcome \"%@\" !", comment: "")
|
NSLocalizedString("generic_welcome_firstname_format", tableName: kStringsFileName, bundle: Bundle.main, value: "Welcome \"%@\" !", comment: "")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Translation in en :
|
/// Translation in en :
|
||||||
/// Welcome \"%@\" !
|
/// Welcome \"%@\" !
|
||||||
static func generic_welcome_firstname_format(arg0: String) -> String {
|
///
|
||||||
String(format: Self.generic_welcome_firstname_format, arg0)
|
/// Comment :
|
||||||
|
/// No comment
|
||||||
|
func generic_welcome_firstname_format(arg0: String) -> String {
|
||||||
|
String(format: self.generic_welcome_firstname_format, arg0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - EqualSymbol
|
// MARK: - EqualSymbol
|
||||||
|
|
||||||
/// Translation in en :
|
/// Translation in en :
|
||||||
/// 1€ = 1 point !
|
/// 1€ = 1 point !
|
||||||
static var test_equal_symbol: String {
|
///
|
||||||
|
/// Comment :
|
||||||
|
/// No comment
|
||||||
|
var test_equal_symbol: String {
|
||||||
NSLocalizedString("test_equal_symbol", tableName: kStringsFileName, bundle: Bundle.main, value: "1€ = 1 point !", comment: "")
|
NSLocalizedString("test_equal_symbol", tableName: kStringsFileName, bundle: Bundle.main, value: "1€ = 1 point !", comment: "")
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Placeholders
|
// MARK: - Placeholders
|
||||||
|
|
||||||
/// Translation in en :
|
/// Translation in en :
|
||||||
/// You %%: %2$@ %1$@ Age: %3$d
|
/// You %%: %2$@ %1$@ Age: %3$d
|
||||||
static var placeholders_test_one: String {
|
///
|
||||||
|
/// Comment :
|
||||||
|
/// No comment
|
||||||
|
var placeholders_test_one: String {
|
||||||
NSLocalizedString("placeholders_test_one", tableName: kStringsFileName, bundle: Bundle.main, value: "You %%: %2$@ %1$@ Age: %3$d", comment: "")
|
NSLocalizedString("placeholders_test_one", tableName: kStringsFileName, bundle: Bundle.main, value: "You %%: %2$@ %1$@ Age: %3$d", comment: "")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Translation in en :
|
/// Translation in en :
|
||||||
/// You %%: %2$@ %1$@ Age: %3$d
|
/// You %%: %2$@ %1$@ Age: %3$d
|
||||||
static func placeholders_test_one(arg0: String, arg1: String, arg2: Int) -> String {
|
///
|
||||||
String(format: Self.placeholders_test_one, arg0, arg1, arg2)
|
/// Comment :
|
||||||
|
/// No comment
|
||||||
|
func placeholders_test_one(arg0: String, arg1: String, arg2: Int) -> String {
|
||||||
|
String(format: self.placeholders_test_one, arg0, arg1, arg2)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
@ -1,57 +0,0 @@
|
|||||||
// Generated from Strings-Stringium at 2022-03-07 11:02:15 +0000
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
fileprivate let kStringsFileName = "sampleStrings"
|
|
||||||
|
|
||||||
extension String {
|
|
||||||
|
|
||||||
// MARK: - Webservice
|
|
||||||
|
|
||||||
/// Translation in en :
|
|
||||||
/// en
|
|
||||||
static var param_lang: String {
|
|
||||||
NSLocalizedString("param_lang", tableName: kStringsFileName, bundle: Bundle.main, value: "en", comment: "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Generic
|
|
||||||
|
|
||||||
/// Translation in en :
|
|
||||||
/// Back
|
|
||||||
static var generic_back: String {
|
|
||||||
NSLocalizedString("generic_back", tableName: kStringsFileName, bundle: Bundle.main, value: "Back", comment: "")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Translation in en :
|
|
||||||
/// Loading data...
|
|
||||||
static var generic_loading_data: String {
|
|
||||||
NSLocalizedString("generic_loading_data", tableName: kStringsFileName, bundle: Bundle.main, value: "Loading data...", comment: "")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Translation in en :
|
|
||||||
/// Welcome \"%@\" !
|
|
||||||
static var generic_welcome_firstname_format: String {
|
|
||||||
NSLocalizedString("generic_welcome_firstname_format", tableName: kStringsFileName, bundle: Bundle.main, value: "Welcome \"%@\" !", comment: "")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Translation in en :
|
|
||||||
/// Welcome \"%@\" !
|
|
||||||
static func generic_welcome_firstname_format(arg0: String) -> String {
|
|
||||||
String(format: Self.generic_welcome_firstname_format, arg0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Placeholders
|
|
||||||
|
|
||||||
/// Translation in en :
|
|
||||||
/// You %%: %2$@ %1$@ Age: %3$d
|
|
||||||
static var placeholders_test_one: String {
|
|
||||||
NSLocalizedString("placeholders_test_one", tableName: kStringsFileName, bundle: Bundle.main, value: "You %%: %2$@ %1$@ Age: %3$d", comment: "")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Translation in en :
|
|
||||||
/// You %%: %2$@ %1$@ Age: %3$d
|
|
||||||
static func placeholders_test_one(arg0: String, arg1: String, arg2: Int) -> String {
|
|
||||||
String(format: Self.placeholders_test_one, arg0, arg1, arg2)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
// Generated from StringToolCore at 2022-01-10 08:39:52 +0000
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
fileprivate let kStringsFileName = "sampleStrings"
|
|
||||||
|
|
||||||
extension String {
|
|
||||||
|
|
||||||
// MARK: - Webservice
|
|
||||||
|
|
||||||
/// Translation in en :
|
|
||||||
/// en
|
|
||||||
static var param_lang: String {
|
|
||||||
NSLocalizedString("param_lang", tableName: kStringsFileName, bundle: Bundle.main, value: "en", comment: "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Generic
|
|
||||||
|
|
||||||
/// Translation in en :
|
|
||||||
/// Back
|
|
||||||
static var generic_back: String {
|
|
||||||
NSLocalizedString("generic_back", tableName: kStringsFileName, bundle: Bundle.main, value: "Back", comment: "")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Translation in en :
|
|
||||||
/// Loading data...
|
|
||||||
static var generic_loading_data: String {
|
|
||||||
NSLocalizedString("generic_loading_data", tableName: kStringsFileName, bundle: Bundle.main, value: "Loading data...", comment: "")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Translation in en :
|
|
||||||
/// Welcome \"%@\" !
|
|
||||||
static var generic_welcome_firstname_format: String {
|
|
||||||
NSLocalizedString("generic_welcome_firstname_format", tableName: kStringsFileName, bundle: Bundle.main, value: "Welcome \"%@\" !", comment: "")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
// Generated from Strings-Stringium at 2022-03-07 11:00:52 +0000
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
fileprivate let kStringsFileName = "sampleStrings"
|
|
||||||
|
|
||||||
extension StringTest {
|
|
||||||
|
|
||||||
// MARK: - Webservice
|
|
||||||
|
|
||||||
/// Translation in en :
|
|
||||||
/// en
|
|
||||||
var param_lang: String {
|
|
||||||
NSLocalizedString("param_lang", tableName: kStringsFileName, bundle: Bundle.main, value: "en", comment: "")
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Generic
|
|
||||||
|
|
||||||
/// Translation in en :
|
|
||||||
/// Back
|
|
||||||
var generic_back: String {
|
|
||||||
NSLocalizedString("generic_back", tableName: kStringsFileName, bundle: Bundle.main, value: "Back", comment: "")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Translation in en :
|
|
||||||
/// Loading data...
|
|
||||||
var generic_loading_data: String {
|
|
||||||
NSLocalizedString("generic_loading_data", tableName: kStringsFileName, bundle: Bundle.main, value: "Loading data...", comment: "")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Translation in en :
|
|
||||||
/// Welcome \"%@\" !
|
|
||||||
var generic_welcome_firstname_format: String {
|
|
||||||
NSLocalizedString("generic_welcome_firstname_format", tableName: kStringsFileName, bundle: Bundle.main, value: "Welcome \"%@\" !", comment: "")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Translation in en :
|
|
||||||
/// Welcome \"%@\" !
|
|
||||||
func generic_welcome_firstname_format(arg0: String) -> String {
|
|
||||||
String(format: self.generic_welcome_firstname_format, arg0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// MARK: - Placeholders
|
|
||||||
|
|
||||||
/// Translation in en :
|
|
||||||
/// You %%: %2$@ %1$@ Age: %3$d
|
|
||||||
var placeholders_test_one: String {
|
|
||||||
NSLocalizedString("placeholders_test_one", tableName: kStringsFileName, bundle: Bundle.main, value: "You %%: %2$@ %1$@ Age: %3$d", comment: "")
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Translation in en :
|
|
||||||
/// You %%: %2$@ %1$@ Age: %3$d
|
|
||||||
func placeholders_test_one(arg0: String, arg1: String, arg2: Int) -> String {
|
|
||||||
String(format: self.placeholders_test_one, arg0, arg1, arg2)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Apple Strings File
|
* Apple Strings File
|
||||||
* Generated by ResgenSwift 1.0.0
|
* Generated by ResgenSwift 2.1.0
|
||||||
* Language: en-us
|
* Language: en-us
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Apple Strings File
|
* Apple Strings File
|
||||||
* Generated by ResgenSwift 1.0.0
|
* Generated by ResgenSwift 2.1.0
|
||||||
* Language: en
|
* Language: en
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Apple Strings File
|
* Apple Strings File
|
||||||
* Generated by ResgenSwift 1.0.0
|
* Generated by ResgenSwift 2.1.0
|
||||||
* Language: fr
|
* Language: fr
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
207
SampleFiles/Tags/Generated/Analytics+GenAllScript.swift
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
// Generated by ResgenSwift.Analytics 2.1.0
|
||||||
|
|
||||||
|
import MatomoTracker
|
||||||
|
import FirebaseAnalytics
|
||||||
|
|
||||||
|
// MARK: - Protocol
|
||||||
|
|
||||||
|
protocol AnalyticsManagerProtocol {
|
||||||
|
|
||||||
|
func logScreen(name: String, path: String)
|
||||||
|
func logEvent(
|
||||||
|
name: String,
|
||||||
|
action: String,
|
||||||
|
category: String,
|
||||||
|
params: [String: Any]?
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Matomo
|
||||||
|
|
||||||
|
class MatomoAnalyticsManager: AnalyticsManagerProtocol {
|
||||||
|
|
||||||
|
// MARK: - Properties
|
||||||
|
|
||||||
|
private var tracker: MatomoTracker
|
||||||
|
|
||||||
|
// MARK: - Init
|
||||||
|
|
||||||
|
init(siteId: String, url: String) {
|
||||||
|
debugPrint("[Matomo service] Server URL: \(url)")
|
||||||
|
debugPrint("[Matomo service] Site ID: \(siteId)")
|
||||||
|
tracker = MatomoTracker(
|
||||||
|
siteId: siteId,
|
||||||
|
baseURL: URL(string: url)!
|
||||||
|
)
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
tracker.dispatchInterval = 5
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
tracker.logger = DefaultLogger(minLevel: .verbose)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
debugPrint("[Matomo service] Configured with content base: \(tracker.contentBase?.absoluteString ?? "-")")
|
||||||
|
debugPrint("[Matomo service] Opt out: \(tracker.isOptedOut)")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Methods
|
||||||
|
|
||||||
|
func logScreen(name: String, path: String) {
|
||||||
|
guard !tracker.isOptedOut else { return }
|
||||||
|
guard let trackerUrl = tracker.contentBase?.absoluteString else { return }
|
||||||
|
|
||||||
|
let urlString = URL(string: "\(trackerUrl)" + "/" + "\(path)" + "iOS")
|
||||||
|
tracker.track(
|
||||||
|
view: [name],
|
||||||
|
url: urlString
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func logEvent(
|
||||||
|
name: String,
|
||||||
|
action: String,
|
||||||
|
category: String,
|
||||||
|
params: [String: Any]?
|
||||||
|
) {
|
||||||
|
guard !tracker.isOptedOut else { return }
|
||||||
|
|
||||||
|
tracker.track(
|
||||||
|
eventWithCategory: category,
|
||||||
|
action: action,
|
||||||
|
name: name,
|
||||||
|
number: nil,
|
||||||
|
url: nil
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Firebase
|
||||||
|
|
||||||
|
class FirebaseAnalyticsManager: AnalyticsManagerProtocol {
|
||||||
|
func logScreen(name: String, path: String) {
|
||||||
|
var parameters = [
|
||||||
|
AnalyticsParameterScreenName: name as NSObject
|
||||||
|
]
|
||||||
|
|
||||||
|
Analytics.logEvent(
|
||||||
|
AnalyticsEventScreenView,
|
||||||
|
parameters: parameters
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func logEvent(
|
||||||
|
name: String,
|
||||||
|
action: String,
|
||||||
|
category: String,
|
||||||
|
params: [String: Any]?
|
||||||
|
) {
|
||||||
|
var parameters: [String:NSObject] = [
|
||||||
|
"action": action as NSObject,
|
||||||
|
"category": category as NSObject,
|
||||||
|
]
|
||||||
|
|
||||||
|
if let supplementaryParameters = params {
|
||||||
|
for (newKey, newValue) in supplementaryParameters {
|
||||||
|
if parameters.contains(where: { (key: String, value: NSObject) in
|
||||||
|
key == newKey
|
||||||
|
}) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
parameters[newKey] = newValue as? NSObject
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Analytics.logEvent(
|
||||||
|
name.replacingOccurrences(of: [" "], with: "_"),
|
||||||
|
parameters: parameters
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Manager
|
||||||
|
|
||||||
|
class AnalyticsManager {
|
||||||
|
|
||||||
|
static var shared = AnalyticsManager()
|
||||||
|
|
||||||
|
// MARK: - Properties
|
||||||
|
|
||||||
|
var managers: [AnalyticsManagerProtocol] = []
|
||||||
|
|
||||||
|
private var isEnabled: Bool = true
|
||||||
|
|
||||||
|
// MARK: - Methods
|
||||||
|
|
||||||
|
func setAnalyticsEnabled(_ enable: Bool) {
|
||||||
|
isEnabled = enable
|
||||||
|
}
|
||||||
|
|
||||||
|
func configure(siteId: String, url: String) {
|
||||||
|
managers.append(
|
||||||
|
MatomoAnalyticsManager(
|
||||||
|
siteId: siteId,
|
||||||
|
url: url
|
||||||
|
)
|
||||||
|
)
|
||||||
|
managers.append(FirebaseAnalyticsManager())
|
||||||
|
}
|
||||||
|
|
||||||
|
private func logScreen(name: String, path: String) {
|
||||||
|
guard isEnabled else { return }
|
||||||
|
|
||||||
|
managers.forEach { manager in
|
||||||
|
manager.logScreen(name: name, path: path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func logEvent(
|
||||||
|
name: String,
|
||||||
|
action: String,
|
||||||
|
category: String,
|
||||||
|
params: [String: Any]?
|
||||||
|
) {
|
||||||
|
guard isEnabled else { return }
|
||||||
|
|
||||||
|
managers.forEach { manager in
|
||||||
|
manager.logEvent(
|
||||||
|
name: name,
|
||||||
|
action: action,
|
||||||
|
category: category,
|
||||||
|
params: params
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - section_one
|
||||||
|
|
||||||
|
func logScreenS1DefOne(title: String) {
|
||||||
|
logScreen(
|
||||||
|
name: "s1 def one \(title)",
|
||||||
|
path: "s1_def_one/\(title)"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func logEventS1DefTwo(title: String, count: String) {
|
||||||
|
logEvent(
|
||||||
|
name: "s1 def two",
|
||||||
|
action: "test",
|
||||||
|
category: "test",
|
||||||
|
params: [
|
||||||
|
"title": title,
|
||||||
|
"count": count
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - section_two
|
||||||
|
|
||||||
|
func logScreenS2DefOne() {
|
||||||
|
logScreen(
|
||||||
|
name: "s2 def one",
|
||||||
|
path: "s2_def_one/"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -1,23 +1,28 @@
|
|||||||
// Generated by ResgenSwift.Strings.Tags 1.0.0
|
// Generated by ResgenSwift.Strings.Tags 2.1.0
|
||||||
|
|
||||||
// typelias Tags = String
|
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
extension Tags {
|
extension Tags {
|
||||||
|
|
||||||
// MARK: - ScreenTag
|
// MARK: - ScreenTag
|
||||||
|
|
||||||
/// Translation in ium :
|
/// Translation in ium :
|
||||||
/// Ecran un
|
/// Ecran un
|
||||||
static var screen_one: String {
|
///
|
||||||
|
/// Comment :
|
||||||
|
/// No comment
|
||||||
|
|
||||||
|
var screen_one: String {
|
||||||
"Ecran un"
|
"Ecran un"
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Translation in ium :
|
/// Translation in ium :
|
||||||
/// Ecran deux
|
/// Ecran deux
|
||||||
static var screen_two: String {
|
///
|
||||||
|
/// Comment :
|
||||||
|
/// No comment
|
||||||
|
|
||||||
|
var screen_two: String {
|
||||||
"Ecran deux"
|
"Ecran deux"
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
// Generated from Strings-Tags at 2022-02-14 09:30:20 +0000
|
|
||||||
|
|
||||||
// typelias Tags = String
|
|
||||||
|
|
||||||
import UIKit
|
|
||||||
|
|
||||||
extension Tags {
|
|
||||||
|
|
||||||
// MARK: - ScreenTag
|
|
||||||
|
|
||||||
/// Translation in ium :
|
|
||||||
/// Ecran un
|
|
||||||
static var screen_one: String {
|
|
||||||
"Ecran un"
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Translation in ium :
|
|
||||||
/// Ecran deux
|
|
||||||
static var screen_two: String {
|
|
||||||
"Ecran deux"
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
52
SampleFiles/Tags/sampleTags.yml
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
---
|
||||||
|
categories:
|
||||||
|
- id: section_one
|
||||||
|
screens:
|
||||||
|
- id: s1_def_one
|
||||||
|
name: s1 def one _TITLE_
|
||||||
|
path: s1_def_one/_TITLE_
|
||||||
|
tags: ios,droid
|
||||||
|
parameters:
|
||||||
|
- name: title
|
||||||
|
type: String
|
||||||
|
replaceIn: name,path
|
||||||
|
|
||||||
|
events:
|
||||||
|
- id: s1_def_two
|
||||||
|
name: s1 def two
|
||||||
|
action: test
|
||||||
|
category: test
|
||||||
|
tags: ios
|
||||||
|
parameters:
|
||||||
|
- name: title
|
||||||
|
type: String
|
||||||
|
- name: count
|
||||||
|
type: String
|
||||||
|
|
||||||
|
- id: section_two
|
||||||
|
screens:
|
||||||
|
- id: s2_def_one
|
||||||
|
name: s2 def one
|
||||||
|
path: s2_def_one/
|
||||||
|
tags: ios
|
||||||
|
|
||||||
|
events:
|
||||||
|
- id: s2_def_two
|
||||||
|
name: s2 def two
|
||||||
|
action: test
|
||||||
|
category: test
|
||||||
|
tags: droid
|
||||||
|
|
||||||
|
- id: section_three
|
||||||
|
screens:
|
||||||
|
- id: s3_def_one
|
||||||
|
name: s3 def one
|
||||||
|
path: s3_def_one/
|
||||||
|
tags: droid
|
||||||
|
|
||||||
|
events:
|
||||||
|
- id: s3_def_two
|
||||||
|
name: s3 def two
|
||||||
|
action: test
|
||||||
|
category: test
|
||||||
|
tags: droid
|
@ -2,26 +2,29 @@
|
|||||||
|
|
||||||
FORCE_FLAG="$1"
|
FORCE_FLAG="$1"
|
||||||
|
|
||||||
# Font
|
## Font
|
||||||
swift run -c release FontTool $FORCE_FLAG "./Fonts/sampleFontsAll.txt" \
|
swift run -c release ResgenSwift fonts $FORCE_FLAG "./Fonts/sampleFontsAll.txt" \
|
||||||
--extension-output-path "./Fonts/Generated" \
|
--extension-output-path "./Fonts/Generated" \
|
||||||
--extension-name "UIFont" \
|
--extension-name "FontYolo" \
|
||||||
--extension-suffix "GenAllScript"
|
--extension-name-ui-kit "UIFontYolo" \
|
||||||
|
--extension-suffix "GenAllScript" \
|
||||||
|
--info-plist-paths "./Fonts/Generated/test.plist ./Fonts/Generated/test2.plist"
|
||||||
|
|
||||||
echo "\n-------------------------\n"
|
echo "\n-------------------------\n"
|
||||||
|
|
||||||
# Color
|
## Color
|
||||||
swift run -c release ColorTool $FORCE_FLAG "./Colors/sampleColors1.txt" \
|
swift run -c release ResgenSwift colors $FORCE_FLAG "./Colors/sampleColors1.txt" \
|
||||||
--style all \
|
--style all \
|
||||||
--xcassets-path "./Colors/colors.xcassets" \
|
--xcassets-path "./Colors/colors.xcassets" \
|
||||||
--extension-output-path "./Colors/Generated/" \
|
--extension-output-path "./Colors/Generated/" \
|
||||||
--extension-name "UIColor" \
|
--extension-name "ColorYolo" \
|
||||||
|
--extension-name-ui-kit "UIhkjhkColorYolo" \
|
||||||
--extension-suffix "GenAllScript"
|
--extension-suffix "GenAllScript"
|
||||||
|
|
||||||
echo "\n-------------------------\n"
|
echo "\n-------------------------\n"
|
||||||
|
|
||||||
# Twine
|
## Twine
|
||||||
swift run -c release Strings twine $FORCE_FLAG "./Twine/sampleStrings.txt" \
|
swift run -c release ResgenSwift strings twine $FORCE_FLAG "./Twine/sampleStrings.txt" \
|
||||||
--output-path "./Twine/Generated" \
|
--output-path "./Twine/Generated" \
|
||||||
--langs "fr en en-us" \
|
--langs "fr en en-us" \
|
||||||
--default-lang "en" \
|
--default-lang "en" \
|
||||||
@ -29,8 +32,8 @@ swift run -c release Strings twine $FORCE_FLAG "./Twine/sampleStrings.txt" \
|
|||||||
|
|
||||||
echo "\n-------------------------\n"
|
echo "\n-------------------------\n"
|
||||||
|
|
||||||
# Strings
|
## Strings
|
||||||
swift run -c release Strings stringium $FORCE_FLAG "./Strings/sampleStrings.txt" \
|
swift run -c release ResgenSwift strings stringium $FORCE_FLAG "./Strings/sampleStrings.txt" \
|
||||||
--output-path "./Strings/Generated" \
|
--output-path "./Strings/Generated" \
|
||||||
--langs "fr en en-us" \
|
--langs "fr en en-us" \
|
||||||
--default-lang "en" \
|
--default-lang "en" \
|
||||||
@ -40,18 +43,28 @@ swift run -c release Strings stringium $FORCE_FLAG "./Strings/sampleStrings.txt"
|
|||||||
|
|
||||||
echo "\n-------------------------\n"
|
echo "\n-------------------------\n"
|
||||||
|
|
||||||
# Tags
|
## Tags
|
||||||
swift run -c release Strings tags $FORCE_FLAG "./Tags/sampleTags.txt" \
|
swift run -c release ResgenSwift strings tags $FORCE_FLAG "./Tags/sampleTags.txt" \
|
||||||
--lang "ium" \
|
--lang "ium" \
|
||||||
--extension-output-path "./Tags/Generated" \
|
--extension-output-path "./Tags/Generated" \
|
||||||
--extension-name "Tags" \
|
--extension-name "Tags" \
|
||||||
--extension-suffix "GenAllScript"
|
--extension-suffix "GenAllScript"
|
||||||
|
|
||||||
|
#echo "\n-------------------------\n"
|
||||||
|
|
||||||
|
# Analytics
|
||||||
|
swift run -c release ResgenSwift analytics $FORCE_FLAG "./Tags/sampleTags.yml" \
|
||||||
|
--target "matomo firebase" \
|
||||||
|
--extension-output-path "./Tags/Generated" \
|
||||||
|
--extension-name "Analytics" \
|
||||||
|
--extension-suffix "GenAllScript"
|
||||||
|
|
||||||
echo "\n-------------------------\n"
|
echo "\n-------------------------\n"
|
||||||
|
|
||||||
# Images
|
## Images
|
||||||
swift run -c release Imagium $FORCE_FLAG "./Images/sampleImages.txt" \
|
swift run -c release ResgenSwift images $FORCE_FLAG "./Images/sampleImages.txt" \
|
||||||
--xcassets-path "./Images/imagium.xcassets" \
|
--xcassets-path "./Images/imagium.xcassets" \
|
||||||
--extension-output-path "./Images/Generated" \
|
--extension-output-path "./Images/Generated" \
|
||||||
--extension-name "UIImage" \
|
--extension-name "ImageYolo" \
|
||||||
|
--extension-name-ui-kit "UIImageYolo" \
|
||||||
--extension-suffix "GenAllScript"
|
--extension-suffix "GenAllScript"
|
||||||
|
103
SampleFiles/resgenConfiguration.yml
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
---
|
||||||
|
#
|
||||||
|
# Class architecture
|
||||||
|
#
|
||||||
|
architecture:
|
||||||
|
property: R
|
||||||
|
classname: R
|
||||||
|
path: ./Tags
|
||||||
|
children:
|
||||||
|
- property: images
|
||||||
|
classname: R2Image
|
||||||
|
- property: strings
|
||||||
|
classname: R2String
|
||||||
|
- property: fonts
|
||||||
|
classname: R2Font
|
||||||
|
- property: images
|
||||||
|
classname: R2Image
|
||||||
|
- property: ui
|
||||||
|
classname: R2UI
|
||||||
|
children:
|
||||||
|
- property: images
|
||||||
|
classname: R2UIImage
|
||||||
|
- property: fonts
|
||||||
|
classname: R2UIFont
|
||||||
|
- property: images
|
||||||
|
classname: R2UIImage
|
||||||
|
|
||||||
|
#
|
||||||
|
# Strings
|
||||||
|
#
|
||||||
|
strings:
|
||||||
|
-
|
||||||
|
inputFile: ./Strings/sampleStrings.txt
|
||||||
|
outputPath: ./Strings/Generated
|
||||||
|
langs: "fr en en-us"
|
||||||
|
defaultLang: en
|
||||||
|
extensionOutputPath: ./Strings/Generated
|
||||||
|
extensionName: String
|
||||||
|
extensionSuffix: GenAllScript
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Images
|
||||||
|
#
|
||||||
|
images:
|
||||||
|
-
|
||||||
|
inputFile: ./Images/sampleImages.txt
|
||||||
|
xcassetsPath: ./Images/imagium.xcassets
|
||||||
|
extensionOutputPath: ./Images/Generated
|
||||||
|
extensionName: ImageYolo
|
||||||
|
extensionNameUIKit: UIImageYolo
|
||||||
|
extensionSuffix: GenAllScript
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Colors
|
||||||
|
#
|
||||||
|
colors:
|
||||||
|
-
|
||||||
|
inputFile: ./Colors/sampleColors1.txt
|
||||||
|
style: all
|
||||||
|
xcassetsPath: ./Colors/colors.xcassets
|
||||||
|
extensionOutputPath: ./Colors/Generated/
|
||||||
|
extensionName: ColorYolo
|
||||||
|
extensionNameUIKit: UIColorYolo
|
||||||
|
extensionSuffix: GenAllScript
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Tags
|
||||||
|
#
|
||||||
|
tags:
|
||||||
|
-
|
||||||
|
inputFile: ./Tags/sampleTags.txt
|
||||||
|
lang: ium
|
||||||
|
extensionOutputPath: ./Tags/Generated
|
||||||
|
extensionName: Tags
|
||||||
|
extensionSuffix: GenAllScript
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Analytics
|
||||||
|
#
|
||||||
|
analytics:
|
||||||
|
-
|
||||||
|
inputFile: ./Tags/sampleTags.yml
|
||||||
|
target: "matomo firebase"
|
||||||
|
extensionOutputPath: ./Tags/Generated
|
||||||
|
extensionName: Analytics
|
||||||
|
extensionSuffix: GenAllScript
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Fonts
|
||||||
|
#
|
||||||
|
fonts:
|
||||||
|
-
|
||||||
|
inputFile: ./Fonts/sampleFontsAll.txt
|
||||||
|
extensionOutputPath: ./Fonts/Generated
|
||||||
|
extensionName: FontYolo
|
||||||
|
extensionNameUIKit: UIFontYolo
|
||||||
|
extensionSuffix: GenAllScript
|
||||||
|
infoPlistPaths: "./Fonts/Generated/test.plist ./Fonts/Generated/test2.plist"
|
90
Sources/ResgenSwift/Analytics/Analytics.swift
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
//
|
||||||
|
// Analytics.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Loris Perret on 08/12/2023.
|
||||||
|
//
|
||||||
|
|
||||||
|
import ArgumentParser
|
||||||
|
import Foundation
|
||||||
|
import ToolCore
|
||||||
|
|
||||||
|
struct Analytics: ParsableCommand {
|
||||||
|
|
||||||
|
// MARK: - Command Configuration
|
||||||
|
|
||||||
|
static var configuration = CommandConfiguration(
|
||||||
|
abstract: "Generate analytics extension file.",
|
||||||
|
version: ResgenSwiftVersion
|
||||||
|
)
|
||||||
|
|
||||||
|
// MARK: - Static
|
||||||
|
|
||||||
|
static let toolName = "Analytics"
|
||||||
|
static let defaultExtensionName = "Analytics"
|
||||||
|
|
||||||
|
// MARK: - Command Options
|
||||||
|
|
||||||
|
@OptionGroup var options: AnalyticsOptions
|
||||||
|
|
||||||
|
// MARK: - Run
|
||||||
|
|
||||||
|
mutating func run() {
|
||||||
|
print("[\(Self.toolName)] Starting analytics generation")
|
||||||
|
print("[\(Self.toolName)] Will use inputFile \(options.inputFile) to generate analytics for target: \(options.target)")
|
||||||
|
|
||||||
|
// Check requirements
|
||||||
|
guard checkRequirements() else { return }
|
||||||
|
|
||||||
|
print("[\(Self.toolName)] Will generate analytics")
|
||||||
|
|
||||||
|
// Check requirements
|
||||||
|
guard checkRequirements() else { return }
|
||||||
|
|
||||||
|
// Parse input file
|
||||||
|
let sections = AnalyticsFileParser().parse(options.inputFile, target: options.target)
|
||||||
|
|
||||||
|
// Generate extension
|
||||||
|
AnalyticsGenerator.writeExtensionFiles(
|
||||||
|
sections: sections,
|
||||||
|
target: options.target,
|
||||||
|
tags: ["ios", "iosonly"],
|
||||||
|
staticVar: options.staticMembers,
|
||||||
|
extensionName: options.extensionName,
|
||||||
|
extensionFilePath: options.extensionFilePath
|
||||||
|
)
|
||||||
|
|
||||||
|
print("[\(Self.toolName)] Analytics generated")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Requirements
|
||||||
|
|
||||||
|
private func checkRequirements() -> Bool {
|
||||||
|
let fileManager = FileManager()
|
||||||
|
|
||||||
|
// Input file
|
||||||
|
guard fileManager.fileExists(atPath: options.inputFile) else {
|
||||||
|
let error = AnalyticsError.fileNotExists(options.inputFile)
|
||||||
|
print(error.description)
|
||||||
|
Self.exit(withError: error)
|
||||||
|
}
|
||||||
|
|
||||||
|
guard TrackerType.hasValidTarget(in: options.target) else {
|
||||||
|
let error = AnalyticsError.noValidTracker(options.target)
|
||||||
|
print(error.description)
|
||||||
|
Self.exit(withError: error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if needed to regenerate
|
||||||
|
guard GeneratorChecker.shouldGenerate(
|
||||||
|
force: options.forceGeneration,
|
||||||
|
inputFilePath: options.inputFile,
|
||||||
|
extensionFilePath: options.extensionFilePath
|
||||||
|
) else {
|
||||||
|
print("[\(Self.toolName)] Analytics are already up to date :) ")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
40
Sources/ResgenSwift/Analytics/AnalyticsError.swift
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
//
|
||||||
|
// AnalyticsError.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Loris Perret on 11/12/2023.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
enum AnalyticsError: Error {
|
||||||
|
|
||||||
|
case noValidTracker(String)
|
||||||
|
case fileNotExists(String)
|
||||||
|
case missingElement(String)
|
||||||
|
case invalidParameter(String)
|
||||||
|
case parseFailed(String)
|
||||||
|
case writeFile(String, String)
|
||||||
|
|
||||||
|
var description: String {
|
||||||
|
switch self {
|
||||||
|
case .noValidTracker(let inputTargets):
|
||||||
|
return "error: [\(Analytics.toolName)] '\(inputTargets)' ne contient aucun tracker valid"
|
||||||
|
|
||||||
|
case .fileNotExists(let filename):
|
||||||
|
return "error: [\(Analytics.toolName)] File \(filename) does not exists"
|
||||||
|
|
||||||
|
case .missingElement(let element):
|
||||||
|
return "error: [\(Analytics.toolName)] Missing \(element) for Matomo"
|
||||||
|
|
||||||
|
case .invalidParameter(let reason):
|
||||||
|
return "error: [\(Analytics.toolName)] Invalid parameter \(reason)"
|
||||||
|
|
||||||
|
case .parseFailed(let baseError):
|
||||||
|
return "error: [\(Analytics.toolName)] Parse input file failed: \(baseError)"
|
||||||
|
|
||||||
|
case let .writeFile(subErrorDescription, filename):
|
||||||
|
return "error: [\(Analytics.toolName)] An error occured while writing content to \(filename): \(subErrorDescription)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
51
Sources/ResgenSwift/Analytics/AnalyticsOptions.swift
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
//
|
||||||
|
// AnalyticsOptions.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Loris Perret on 08/12/2023.
|
||||||
|
//
|
||||||
|
|
||||||
|
import ArgumentParser
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
// swiftlint:disable no_grouping_extension
|
||||||
|
|
||||||
|
struct AnalyticsOptions: ParsableArguments {
|
||||||
|
|
||||||
|
@Flag(name: [.customShort("f"), .customShort("F")], help: "Should force generation")
|
||||||
|
var forceGeneration = false
|
||||||
|
|
||||||
|
@Argument(help: "Input files where tags ared defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||||
|
var inputFile: String
|
||||||
|
|
||||||
|
@Option(help: "Target(s) analytics to generate. (\"matomo\" | \"firebase\")")
|
||||||
|
var target: String
|
||||||
|
|
||||||
|
@Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||||
|
var extensionOutputPath: String
|
||||||
|
|
||||||
|
@Option(help: "Tell if it will generate static properties or not")
|
||||||
|
var staticMembers: Bool = false
|
||||||
|
|
||||||
|
@Option(help: "Extension name. If not specified, it will generate a Analytics extension.")
|
||||||
|
var extensionName: String = Analytics.defaultExtensionName
|
||||||
|
|
||||||
|
@Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+Analytics{extensionSuffix}.swift")
|
||||||
|
var extensionSuffix: String?
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Computed var
|
||||||
|
|
||||||
|
extension AnalyticsOptions {
|
||||||
|
|
||||||
|
var extensionFileName: String {
|
||||||
|
if let extensionSuffix {
|
||||||
|
return "\(extensionName)+\(extensionSuffix).swift"
|
||||||
|
}
|
||||||
|
return "\(extensionName).swift"
|
||||||
|
}
|
||||||
|
|
||||||
|
var extensionFilePath: String {
|
||||||
|
"\(extensionOutputPath)/\(extensionFileName)"
|
||||||
|
}
|
||||||
|
}
|
267
Sources/ResgenSwift/Analytics/Generator/AnalyticsGenerator.swift
Normal file
@ -0,0 +1,267 @@
|
|||||||
|
//
|
||||||
|
// AnalyticsGenerator.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Loris Perret on 08/12/2023.
|
||||||
|
//
|
||||||
|
|
||||||
|
import CoreVideo
|
||||||
|
import Foundation
|
||||||
|
import ToolCore
|
||||||
|
|
||||||
|
// Disabled cause it's a pain to handle in generated string
|
||||||
|
|
||||||
|
enum AnalyticsGenerator {
|
||||||
|
|
||||||
|
// MARK: - Write content
|
||||||
|
|
||||||
|
static func writeExtensionFiles(
|
||||||
|
sections: [AnalyticsCategory],
|
||||||
|
target: String,
|
||||||
|
tags: [String],
|
||||||
|
staticVar: Bool,
|
||||||
|
extensionName: String,
|
||||||
|
extensionFilePath: String
|
||||||
|
) {
|
||||||
|
// Get target type from enum
|
||||||
|
let targetsString: [String] = target.components(separatedBy: " ")
|
||||||
|
let targets = {
|
||||||
|
var targets = [TrackerType]()
|
||||||
|
TrackerType.allCases.forEach { enumTarget in
|
||||||
|
if targetsString.contains(enumTarget.value) {
|
||||||
|
targets.append(enumTarget)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return targets
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Get extension content
|
||||||
|
let extensionFileContent = getExtensionContent(
|
||||||
|
targets: targets,
|
||||||
|
sections: sections,
|
||||||
|
tags: tags,
|
||||||
|
staticVar: staticVar,
|
||||||
|
extensionName: extensionName
|
||||||
|
)
|
||||||
|
|
||||||
|
// Write content
|
||||||
|
let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath)
|
||||||
|
do {
|
||||||
|
try extensionFileContent.write(to: extensionFilePathURL, atomically: false, encoding: .utf8)
|
||||||
|
} catch {
|
||||||
|
let error = AnalyticsError.writeFile(extensionFilePath, error.localizedDescription)
|
||||||
|
print(error.description)
|
||||||
|
Analytics.exit(withError: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Extension content
|
||||||
|
|
||||||
|
static func getExtensionContent(
|
||||||
|
targets: [TrackerType],
|
||||||
|
sections: [AnalyticsCategory],
|
||||||
|
tags: [String],
|
||||||
|
staticVar: Bool,
|
||||||
|
extensionName: String
|
||||||
|
) -> String {
|
||||||
|
[
|
||||||
|
getHeader(
|
||||||
|
targets: targets,
|
||||||
|
extensionClassname: extensionName,
|
||||||
|
staticVar: staticVar
|
||||||
|
),
|
||||||
|
getProperties(
|
||||||
|
sections: sections,
|
||||||
|
tags: tags,
|
||||||
|
staticVar: staticVar
|
||||||
|
),
|
||||||
|
getFooter()
|
||||||
|
]
|
||||||
|
.joined(separator: "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Extension part
|
||||||
|
|
||||||
|
private static func getHeader(
|
||||||
|
targets: [TrackerType],
|
||||||
|
extensionClassname: String,
|
||||||
|
staticVar: Bool
|
||||||
|
) -> String {
|
||||||
|
"""
|
||||||
|
// Generated by ResgenSwift.\(Analytics.toolName) \(ResgenSwiftVersion)
|
||||||
|
|
||||||
|
\(getImport(targets: targets))
|
||||||
|
|
||||||
|
\(getAnalyticsProtocol(targets: targets))
|
||||||
|
// MARK: - Manager
|
||||||
|
|
||||||
|
class AnalyticsManager {
|
||||||
|
|
||||||
|
static var shared = AnalyticsManager()
|
||||||
|
|
||||||
|
// MARK: - Properties
|
||||||
|
|
||||||
|
var managers: [AnalyticsManagerProtocol] = []
|
||||||
|
|
||||||
|
\(getEnabledContent())
|
||||||
|
|
||||||
|
\(getAnalyticsProperties(targets: targets))
|
||||||
|
|
||||||
|
\(getPrivateLogFunction())
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func getEnabledContent() -> String {
|
||||||
|
"""
|
||||||
|
private var isEnabled: Bool = true
|
||||||
|
|
||||||
|
// MARK: - Methods
|
||||||
|
|
||||||
|
func setAnalyticsEnabled(_ enable: Bool) {
|
||||||
|
isEnabled = enable
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func getImport(targets: [TrackerType]) -> String {
|
||||||
|
var result: [String] = []
|
||||||
|
|
||||||
|
if targets.contains(TrackerType.matomo) {
|
||||||
|
result.append("import MatomoTracker")
|
||||||
|
}
|
||||||
|
if targets.contains(TrackerType.firebase) {
|
||||||
|
result.append("import FirebaseAnalytics")
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.joined(separator: "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func getPrivateLogFunction() -> String {
|
||||||
|
"""
|
||||||
|
private func logScreen(name: String, path: String) {
|
||||||
|
guard isEnabled else { return }
|
||||||
|
|
||||||
|
managers.forEach { manager in
|
||||||
|
manager.logScreen(name: name, path: path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func logEvent(
|
||||||
|
name: String,
|
||||||
|
action: String,
|
||||||
|
category: String,
|
||||||
|
params: [String: Any]?
|
||||||
|
) {
|
||||||
|
guard isEnabled else { return }
|
||||||
|
|
||||||
|
managers.forEach { manager in
|
||||||
|
manager.logEvent(
|
||||||
|
name: name,
|
||||||
|
action: action,
|
||||||
|
category: category,
|
||||||
|
params: params
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func getAnalyticsProperties(targets: [TrackerType]) -> String {
|
||||||
|
var header = ""
|
||||||
|
var content: [String] = []
|
||||||
|
let footer = " }"
|
||||||
|
|
||||||
|
if targets.contains(TrackerType.matomo) {
|
||||||
|
header = "func configure(siteId: String, url: String) {"
|
||||||
|
} else if targets.contains(TrackerType.firebase) {
|
||||||
|
header = "func configure() {"
|
||||||
|
}
|
||||||
|
|
||||||
|
if targets.contains(TrackerType.matomo) {
|
||||||
|
content.append("""
|
||||||
|
managers.append(
|
||||||
|
MatomoAnalyticsManager(
|
||||||
|
siteId: siteId,
|
||||||
|
url: url
|
||||||
|
)
|
||||||
|
)
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
if targets.contains(TrackerType.firebase) {
|
||||||
|
content.append(" managers.append(FirebaseAnalyticsManager())")
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
header,
|
||||||
|
content.joined(separator: "\n"),
|
||||||
|
footer
|
||||||
|
]
|
||||||
|
.joined(separator: "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func getAnalyticsProtocol(targets: [TrackerType]) -> String {
|
||||||
|
let proto = """
|
||||||
|
// MARK: - Protocol
|
||||||
|
|
||||||
|
protocol AnalyticsManagerProtocol {
|
||||||
|
|
||||||
|
func logScreen(name: String, path: String)
|
||||||
|
func logEvent(
|
||||||
|
name: String,
|
||||||
|
action: String,
|
||||||
|
category: String,
|
||||||
|
params: [String: Any]?
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
var result: [String] = [proto]
|
||||||
|
|
||||||
|
if targets.contains(TrackerType.matomo) {
|
||||||
|
result.append(MatomoGenerator.service)
|
||||||
|
}
|
||||||
|
|
||||||
|
if targets.contains(TrackerType.firebase) {
|
||||||
|
result.append(FirebaseGenerator.service)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.joined(separator: "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func getProperties(
|
||||||
|
sections: [AnalyticsCategory],
|
||||||
|
tags: [String],
|
||||||
|
staticVar: Bool
|
||||||
|
) -> String {
|
||||||
|
sections
|
||||||
|
.compactMap { section in
|
||||||
|
// Check that at least one string will be generated
|
||||||
|
guard section.hasOneOrMoreMatchingTags(tags: tags) else {
|
||||||
|
return nil// Go to next section
|
||||||
|
}
|
||||||
|
|
||||||
|
var res = "\n // MARK: - \(section.id)"
|
||||||
|
section.definitions.forEach { definition in
|
||||||
|
guard definition.hasOneOrMoreMatchingTags(inputTags: tags) == true else {
|
||||||
|
return // Go to next definition
|
||||||
|
}
|
||||||
|
|
||||||
|
if staticVar {
|
||||||
|
res += "\n\n\(definition.getStaticProperty())"
|
||||||
|
} else {
|
||||||
|
res += "\n\n\(definition.getProperty())"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
.joined(separator: "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func getFooter() -> String {
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,91 @@
|
|||||||
|
//
|
||||||
|
// FirebaseGenerator.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Loris Perret on 05/12/2023.
|
||||||
|
//
|
||||||
|
|
||||||
|
// CPD-OFF
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
enum FirebaseGenerator {
|
||||||
|
|
||||||
|
static var service: String {
|
||||||
|
[
|
||||||
|
Self.header,
|
||||||
|
Self.logScreen,
|
||||||
|
Self.logEvent,
|
||||||
|
Self.footer
|
||||||
|
]
|
||||||
|
.joined(separator: "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Private vars
|
||||||
|
|
||||||
|
private static var header: String {
|
||||||
|
"""
|
||||||
|
// MARK: - Firebase
|
||||||
|
|
||||||
|
class FirebaseAnalyticsManager: AnalyticsManagerProtocol {
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
|
||||||
|
private static var logScreen: String {
|
||||||
|
"""
|
||||||
|
func logScreen(name: String, path: String) {
|
||||||
|
var parameters = [
|
||||||
|
AnalyticsParameterScreenName: name as NSObject
|
||||||
|
]
|
||||||
|
|
||||||
|
Analytics.logEvent(
|
||||||
|
AnalyticsEventScreenView,
|
||||||
|
parameters: parameters
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
|
||||||
|
private static var logEvent: String {
|
||||||
|
"""
|
||||||
|
func logEvent(
|
||||||
|
name: String,
|
||||||
|
action: String,
|
||||||
|
category: String,
|
||||||
|
params: [String: Any]?
|
||||||
|
) {
|
||||||
|
var parameters: [String:NSObject] = [
|
||||||
|
"action": action as NSObject,
|
||||||
|
"category": category as NSObject,
|
||||||
|
]
|
||||||
|
|
||||||
|
if let supplementaryParameters = params {
|
||||||
|
for (newKey, newValue) in supplementaryParameters {
|
||||||
|
if parameters.contains(where: { (key: String, value: NSObject) in
|
||||||
|
key == newKey
|
||||||
|
}) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
parameters[newKey] = newValue as? NSObject
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Analytics.logEvent(
|
||||||
|
name.replacingOccurrences(of: [" "], with: "_"),
|
||||||
|
parameters: parameters
|
||||||
|
)
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
|
||||||
|
private static var footer: String {
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CPD-ON
|
110
Sources/ResgenSwift/Analytics/Generator/MatomoGenerator.swift
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
//
|
||||||
|
// MatomoGenerator.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Loris Perret on 05/12/2023.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
enum MatomoGenerator {
|
||||||
|
|
||||||
|
static var service: String {
|
||||||
|
[
|
||||||
|
Self.header,
|
||||||
|
Self.setup,
|
||||||
|
Self.logScreen,
|
||||||
|
Self.logEvent,
|
||||||
|
Self.footer
|
||||||
|
]
|
||||||
|
.joined(separator: "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Private vars
|
||||||
|
|
||||||
|
private static var header: String {
|
||||||
|
"""
|
||||||
|
// MARK: - Matomo
|
||||||
|
|
||||||
|
class MatomoAnalyticsManager: AnalyticsManagerProtocol {
|
||||||
|
|
||||||
|
// MARK: - Properties
|
||||||
|
|
||||||
|
private var tracker: MatomoTracker
|
||||||
|
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
|
||||||
|
private static var setup: String {
|
||||||
|
"""
|
||||||
|
// MARK: - Init
|
||||||
|
|
||||||
|
init(siteId: String, url: String) {
|
||||||
|
debugPrint("[Matomo service] Server URL: \\(url)")
|
||||||
|
debugPrint("[Matomo service] Site ID: \\(siteId)")
|
||||||
|
tracker = MatomoTracker(
|
||||||
|
siteId: siteId,
|
||||||
|
baseURL: URL(string: url)!
|
||||||
|
)
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
tracker.dispatchInterval = 5
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
tracker.logger = DefaultLogger(minLevel: .verbose)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
debugPrint("[Matomo service] Configured with content base: \\(tracker.contentBase?.absoluteString ?? "-")")
|
||||||
|
debugPrint("[Matomo service] Opt out: \\(tracker.isOptedOut)")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Methods
|
||||||
|
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
|
||||||
|
private static var logScreen: String {
|
||||||
|
"""
|
||||||
|
func logScreen(name: String, path: String) {
|
||||||
|
guard !tracker.isOptedOut else { return }
|
||||||
|
guard let trackerUrl = tracker.contentBase?.absoluteString else { return }
|
||||||
|
|
||||||
|
let urlString = URL(string: "\\(trackerUrl)" + "/" + "\\(path)" + "iOS")
|
||||||
|
tracker.track(
|
||||||
|
view: [name],
|
||||||
|
url: urlString
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
|
||||||
|
private static var logEvent: String {
|
||||||
|
"""
|
||||||
|
func logEvent(
|
||||||
|
name: String,
|
||||||
|
action: String,
|
||||||
|
category: String,
|
||||||
|
params: [String: Any]?
|
||||||
|
) {
|
||||||
|
guard !tracker.isOptedOut else { return }
|
||||||
|
|
||||||
|
tracker.track(
|
||||||
|
eventWithCategory: category,
|
||||||
|
action: action,
|
||||||
|
name: name,
|
||||||
|
number: nil,
|
||||||
|
url: nil
|
||||||
|
)
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
|
||||||
|
private static var footer: String {
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
35
Sources/ResgenSwift/Analytics/Model/AnalyticsCategory.swift
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
//
|
||||||
|
// AnalyticsCategory.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Loris Perret on 05/12/2023.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class AnalyticsCategory {
|
||||||
|
|
||||||
|
// MARK: - Properties
|
||||||
|
|
||||||
|
let id: String // OnBoarding
|
||||||
|
var definitions = [AnalyticsDefinition]()
|
||||||
|
|
||||||
|
// MARK: - Init
|
||||||
|
|
||||||
|
init(id: String) {
|
||||||
|
self.id = id
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Methods
|
||||||
|
|
||||||
|
func hasOneOrMoreMatchingTags(tags: [String]) -> Bool {
|
||||||
|
let allTags = definitions.flatMap { $0.tags }
|
||||||
|
let allTagsSet = Set(allTags)
|
||||||
|
|
||||||
|
let intersection = Set(tags).intersection(allTagsSet)
|
||||||
|
if intersection.isEmpty {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
160
Sources/ResgenSwift/Analytics/Model/AnalyticsDefinition.swift
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
//
|
||||||
|
// AnalyticsDefinition.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Loris Perret on 05/12/2023.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import ToolCore
|
||||||
|
|
||||||
|
class AnalyticsDefinition {
|
||||||
|
|
||||||
|
// MARK: - Properties
|
||||||
|
|
||||||
|
let id: String
|
||||||
|
var name: String
|
||||||
|
var path: String = ""
|
||||||
|
var category: String = ""
|
||||||
|
var action: String = ""
|
||||||
|
var comments: String = ""
|
||||||
|
var tags: [String] = []
|
||||||
|
var parameters: [AnalyticsParameter] = []
|
||||||
|
var type: TagType
|
||||||
|
|
||||||
|
// MARK: - Init
|
||||||
|
|
||||||
|
init(id: String, name: String, type: TagType) {
|
||||||
|
self.id = id
|
||||||
|
self.name = name
|
||||||
|
self.type = type
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Methods
|
||||||
|
|
||||||
|
func hasOneOrMoreMatchingTags(inputTags: [String]) -> Bool {
|
||||||
|
if Set(inputTags).isDisjoint(with: tags) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Private Methods
|
||||||
|
|
||||||
|
private func getFuncName() -> String {
|
||||||
|
var pascalCaseTitle: String = ""
|
||||||
|
id.components(separatedBy: "_").forEach { word in
|
||||||
|
pascalCaseTitle.append(contentsOf: word.uppercasedFirst())
|
||||||
|
}
|
||||||
|
|
||||||
|
return "log\(type == .screen ? "Screen" : "Event")\(pascalCaseTitle)"
|
||||||
|
}
|
||||||
|
|
||||||
|
private func getParameters() -> String {
|
||||||
|
var params = parameters
|
||||||
|
var result: String
|
||||||
|
|
||||||
|
if type == .screen {
|
||||||
|
params = params.filter { param in
|
||||||
|
!param.replaceIn.isEmpty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let paramsString = params.map { parameter in
|
||||||
|
"\(parameter.name): \(parameter.type)"
|
||||||
|
}
|
||||||
|
|
||||||
|
if paramsString.count > 2 {
|
||||||
|
result = """
|
||||||
|
(
|
||||||
|
\(paramsString.joined(separator: ",\n\t\t"))
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
} else {
|
||||||
|
result = """
|
||||||
|
(\(paramsString.joined(separator: ", ")))
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private func replaceIn() {
|
||||||
|
for parameter in parameters {
|
||||||
|
for rep in parameter.replaceIn {
|
||||||
|
switch rep {
|
||||||
|
case "name": name = name.replacingFirstOccurrence(of: "_\(parameter.name.uppercased())_", with: "\\(\(parameter.name))")
|
||||||
|
case "path": path = path.replacingFirstOccurrence(of: "_\(parameter.name.uppercased())_", with: "\\(\(parameter.name))")
|
||||||
|
case "category": category = category.replacingFirstOccurrence(of: "_\(parameter.name.uppercased())_", with: "\\(\(parameter.name))")
|
||||||
|
case "action": action = action.replacingFirstOccurrence(of: "_\(parameter.name.uppercased())_", with: "\\(\(parameter.name))")
|
||||||
|
default: break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func getlogFunction() -> String {
|
||||||
|
var params: [String] = []
|
||||||
|
var result: String
|
||||||
|
|
||||||
|
let supplementaryParams = parameters.filter { param in
|
||||||
|
param.replaceIn.isEmpty
|
||||||
|
}
|
||||||
|
|
||||||
|
supplementaryParams.forEach { param in
|
||||||
|
params.append("\"\(param.name)\": \(param.name)")
|
||||||
|
}
|
||||||
|
|
||||||
|
if params.count > 1 {
|
||||||
|
result = """
|
||||||
|
[
|
||||||
|
\(params.joined(separator: ",\n\t\t\t\t"))
|
||||||
|
]
|
||||||
|
"""
|
||||||
|
} else if params.count == 1 {
|
||||||
|
result = """
|
||||||
|
[\(params.joined(separator: ", "))]
|
||||||
|
"""
|
||||||
|
} else {
|
||||||
|
result = "[:]"
|
||||||
|
}
|
||||||
|
|
||||||
|
if type == .screen {
|
||||||
|
return """
|
||||||
|
logScreen(
|
||||||
|
name: "\(name)",
|
||||||
|
path: "\(path)"
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
} else {
|
||||||
|
return """
|
||||||
|
logEvent(
|
||||||
|
name: "\(name)",
|
||||||
|
action: "\(action)",
|
||||||
|
category: "\(category)",
|
||||||
|
params: \(result)
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Raw strings
|
||||||
|
|
||||||
|
func getProperty() -> String {
|
||||||
|
replaceIn()
|
||||||
|
return """
|
||||||
|
func \(getFuncName())\(getParameters()) {
|
||||||
|
\(getlogFunction())
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
|
||||||
|
func getStaticProperty() -> String {
|
||||||
|
replaceIn()
|
||||||
|
return """
|
||||||
|
static func \(getFuncName())\(getParameters()) {
|
||||||
|
\(getlogFunction())
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
50
Sources/ResgenSwift/Analytics/Model/AnalyticsFile.swift
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
//
|
||||||
|
// AnalyticsFile.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Loris Perret on 06/12/2023.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct AnalyticsFile: Codable {
|
||||||
|
|
||||||
|
var categories: [AnalyticsCategoryDTO]
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AnalyticsCategoryDTO: Codable {
|
||||||
|
|
||||||
|
var id: String
|
||||||
|
var screens: [AnalyticsDefinitionScreenDTO]?
|
||||||
|
var events: [AnalyticsDefinitionEventDTO]?
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AnalyticsDefinitionScreenDTO: Codable {
|
||||||
|
|
||||||
|
var id: String
|
||||||
|
var name: String
|
||||||
|
var tags: String
|
||||||
|
var comments: String?
|
||||||
|
var parameters: [AnalyticsParameterDTO]?
|
||||||
|
|
||||||
|
var path: String?
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AnalyticsDefinitionEventDTO: Codable {
|
||||||
|
|
||||||
|
var id: String
|
||||||
|
var name: String
|
||||||
|
var tags: String
|
||||||
|
var comments: String?
|
||||||
|
var parameters: [AnalyticsParameterDTO]?
|
||||||
|
|
||||||
|
var category: String?
|
||||||
|
var action: String?
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AnalyticsParameterDTO: Codable {
|
||||||
|
|
||||||
|
var name: String
|
||||||
|
var type: String
|
||||||
|
var replaceIn: String?
|
||||||
|
}
|
24
Sources/ResgenSwift/Analytics/Model/AnalyticsParameter.swift
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
//
|
||||||
|
// AnalyticsParameter.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Loris Perret on 06/12/2023.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class AnalyticsParameter {
|
||||||
|
|
||||||
|
// MARK: - Properties
|
||||||
|
|
||||||
|
var name: String
|
||||||
|
var type: String
|
||||||
|
var replaceIn: [String] = []
|
||||||
|
|
||||||
|
// MARK: - Init
|
||||||
|
|
||||||
|
init(name: String, type: String) {
|
||||||
|
self.name = name
|
||||||
|
self.type = type
|
||||||
|
}
|
||||||
|
}
|
17
Sources/ResgenSwift/Analytics/Model/TagType.swift
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
//
|
||||||
|
// TagType.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 08/12/2023.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension AnalyticsDefinition {
|
||||||
|
|
||||||
|
enum TagType {
|
||||||
|
|
||||||
|
case screen
|
||||||
|
case event
|
||||||
|
}
|
||||||
|
}
|
31
Sources/ResgenSwift/Analytics/Model/TargetType.swift
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
//
|
||||||
|
// TargetType.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Thibaut Schmitt on 08/12/2023.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
enum TrackerType: CaseIterable, Sendable {
|
||||||
|
|
||||||
|
case matomo
|
||||||
|
case firebase
|
||||||
|
|
||||||
|
var value: String {
|
||||||
|
switch self {
|
||||||
|
case .matomo:
|
||||||
|
"matomo"
|
||||||
|
|
||||||
|
case .firebase:
|
||||||
|
"firebase"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static func hasValidTarget(in targets: String) -> Bool {
|
||||||
|
for tracker in Self.allCases where targets.contains(tracker.value) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
184
Sources/ResgenSwift/Analytics/Parser/AnalyticsFileParser.swift
Normal file
@ -0,0 +1,184 @@
|
|||||||
|
//
|
||||||
|
// AnalyticsFileParser.swift
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Created by Loris Perret on 06/12/2023.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Yams
|
||||||
|
|
||||||
|
class AnalyticsFileParser {
|
||||||
|
|
||||||
|
// MARK: - Properties
|
||||||
|
|
||||||
|
private var inputFile: String = ""
|
||||||
|
private var target: String = ""
|
||||||
|
|
||||||
|
// MARK: - Methods
|
||||||
|
|
||||||
|
func parse(_ inputFile: String, target: String) -> [AnalyticsCategory] {
|
||||||
|
self.inputFile = inputFile
|
||||||
|
self.target = target
|
||||||
|
|
||||||
|
let tagFile = parseYaml()
|
||||||
|
|
||||||
|
return tagFile
|
||||||
|
.categories
|
||||||
|
.map { categorie in
|
||||||
|
let section = AnalyticsCategory(id: categorie.id)
|
||||||
|
|
||||||
|
if let screens = categorie.screens {
|
||||||
|
section
|
||||||
|
.definitions
|
||||||
|
.append(
|
||||||
|
contentsOf: getTagDefinitionScreen(from: screens)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if let events = categorie.events {
|
||||||
|
section
|
||||||
|
.definitions
|
||||||
|
.append(
|
||||||
|
contentsOf: getTagDefinitionEvent(from: events)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return section
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Private methods
|
||||||
|
|
||||||
|
private func parseYaml() -> AnalyticsFile {
|
||||||
|
guard let data = FileManager().contents(atPath: inputFile) else {
|
||||||
|
let error = AnalyticsError.fileNotExists(inputFile)
|
||||||
|
print(error.description)
|
||||||
|
Analytics.exit(withError: error)
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
let tagFile = try YAMLDecoder().decode(AnalyticsFile.self, from: data)
|
||||||
|
return tagFile
|
||||||
|
} catch {
|
||||||
|
let error = AnalyticsError.parseFailed(error.localizedDescription)
|
||||||
|
print(error.description)
|
||||||
|
Analytics.exit(withError: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func getParameters(from parameters: [AnalyticsParameterDTO]) -> [AnalyticsParameter] {
|
||||||
|
parameters.map { dtoParameter in
|
||||||
|
// Type
|
||||||
|
let type = dtoParameter.type.uppercasedFirst()
|
||||||
|
|
||||||
|
guard
|
||||||
|
type == "String" ||
|
||||||
|
type == "Int" ||
|
||||||
|
type == "Double" ||
|
||||||
|
type == "Bool"
|
||||||
|
else {
|
||||||
|
let error = AnalyticsError.invalidParameter("type of \(dtoParameter.name)")
|
||||||
|
print(error.description)
|
||||||
|
Analytics.exit(withError: error)
|
||||||
|
}
|
||||||
|
|
||||||
|
let parameter = AnalyticsParameter(
|
||||||
|
name: dtoParameter.name,
|
||||||
|
type: type
|
||||||
|
)
|
||||||
|
|
||||||
|
if let replaceIn = dtoParameter.replaceIn {
|
||||||
|
parameter.replaceIn = replaceIn.components(separatedBy: ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
return parameter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func getTagDefinition(
|
||||||
|
id: String,
|
||||||
|
name: String,
|
||||||
|
type: AnalyticsDefinition.TagType,
|
||||||
|
tags: String,
|
||||||
|
comments: String?,
|
||||||
|
parameters: [AnalyticsParameterDTO]?
|
||||||
|
) -> AnalyticsDefinition {
|
||||||
|
let definition = AnalyticsDefinition(id: id, name: name, type: type)
|
||||||
|
definition.tags = tags
|
||||||
|
.components(separatedBy: ",")
|
||||||
|
.map { $0.removeLeadingTrailingWhitespace() }
|
||||||
|
|
||||||
|
if let comments {
|
||||||
|
definition.comments = comments
|
||||||
|
}
|
||||||
|
|
||||||
|
if let parameters {
|
||||||
|
definition.parameters = getParameters(from: parameters)
|
||||||
|
}
|
||||||
|
|
||||||
|
return definition
|
||||||
|
}
|
||||||
|
|
||||||
|
private func getTagDefinitionScreen(from screens: [AnalyticsDefinitionScreenDTO]) -> [AnalyticsDefinition] {
|
||||||
|
screens.map { screen in
|
||||||
|
let definition: AnalyticsDefinition = getTagDefinition(
|
||||||
|
id: screen.id,
|
||||||
|
name: screen.name,
|
||||||
|
type: .screen,
|
||||||
|
tags: screen.tags,
|
||||||
|
comments: screen.comments,
|
||||||
|
parameters: screen.parameters
|
||||||
|
)
|
||||||
|
|
||||||
|
if target.contains(TrackerType.matomo.value) {
|
||||||
|
// Path
|
||||||
|
|
||||||
|
guard let path = screen.path else {
|
||||||
|
let error = AnalyticsError.missingElement("screen path")
|
||||||
|
print(error.description)
|
||||||
|
Analytics.exit(withError: error)
|
||||||
|
}
|
||||||
|
|
||||||
|
definition.path = path
|
||||||
|
}
|
||||||
|
|
||||||
|
return definition
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func getTagDefinitionEvent(from events: [AnalyticsDefinitionEventDTO]) -> [AnalyticsDefinition] {
|
||||||
|
events.map { event in
|
||||||
|
let definition: AnalyticsDefinition = getTagDefinition(
|
||||||
|
id: event.id,
|
||||||
|
name: event.name,
|
||||||
|
type: .event,
|
||||||
|
tags: event.tags,
|
||||||
|
comments: event.comments,
|
||||||
|
parameters: event.parameters
|
||||||
|
)
|
||||||
|
|
||||||
|
if target.contains(TrackerType.matomo.value) {
|
||||||
|
// Category
|
||||||
|
guard let category = event.category else {
|
||||||
|
let error = AnalyticsError.missingElement("event category")
|
||||||
|
print(error.description)
|
||||||
|
Analytics.exit(withError: error)
|
||||||
|
}
|
||||||
|
|
||||||
|
definition.category = category
|
||||||
|
|
||||||
|
// Action
|
||||||
|
guard let action = event.action else {
|
||||||
|
let error = AnalyticsError.missingElement("event action")
|
||||||
|
print(error.description)
|
||||||
|
Analytics.exit(withError: error)
|
||||||
|
}
|
||||||
|
|
||||||
|
definition.action = action
|
||||||
|
}
|
||||||
|
|
||||||
|
return definition
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,109 +1,134 @@
|
|||||||
//
|
//
|
||||||
// main.swift
|
// main.swift
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// Created by Thibaut Schmitt on 20/12/2021.
|
// Created by Thibaut Schmitt on 20/12/2021.
|
||||||
//
|
//
|
||||||
|
|
||||||
import ToolCore
|
@preconcurrency import ArgumentParser
|
||||||
import Foundation
|
import Foundation
|
||||||
import ArgumentParser
|
import ToolCore
|
||||||
|
|
||||||
struct Colors: ParsableCommand {
|
struct Colors: ParsableCommand {
|
||||||
|
|
||||||
// MARK: - CommandConfiguration
|
// MARK: - CommandConfiguration
|
||||||
|
|
||||||
static var configuration = CommandConfiguration(
|
static let configuration = CommandConfiguration(
|
||||||
abstract: "A utility for generate colors assets and their getters.",
|
abstract: "A utility for generate colors assets and their getters.",
|
||||||
version: ResgenSwiftVersion
|
version: ResgenSwiftVersion
|
||||||
)
|
)
|
||||||
|
|
||||||
// MARK: - Static
|
// MARK: - Static
|
||||||
|
|
||||||
static let toolName = "Color"
|
static let toolName = "Color"
|
||||||
static let defaultExtensionName = "UIColor"
|
static let defaultExtensionName = "Color"
|
||||||
|
static let defaultExtensionNameUIKit = "UIColor"
|
||||||
static let assetsColorsFolderName = "Colors"
|
static let assetsColorsFolderName = "Colors"
|
||||||
|
|
||||||
// MARK: - Command options
|
// MARK: - Command options
|
||||||
|
|
||||||
@OptionGroup var options: ColorsToolOptions
|
@OptionGroup var options: ColorsToolOptions
|
||||||
|
|
||||||
// MARK: - Run
|
// MARK: - Run
|
||||||
|
|
||||||
public func run() throws {
|
func run() throws {
|
||||||
print("[\(Self.toolName)] Starting colors generation")
|
print("[\(Self.toolName)] Starting colors generation")
|
||||||
|
|
||||||
// Check requirements
|
// Check requirements
|
||||||
guard checkRequirements() else { return }
|
guard checkRequirements() else { return }
|
||||||
|
|
||||||
print("[\(Self.toolName)] Will generate colors")
|
print("[\(Self.toolName)] Will generate colors")
|
||||||
|
|
||||||
// Delete current colors
|
// Delete current colors
|
||||||
deleteCurrentColors()
|
deleteCurrentColors()
|
||||||
|
|
||||||
// Get colors to generate
|
// Get colors to generate
|
||||||
let parsedColors = ColorFileParser.parse(options.inputFile,
|
let parsedColors = ColorFileParser.parse(
|
||||||
colorStyle: options.colorStyle)
|
options.inputFile,
|
||||||
|
colorStyle: options.style
|
||||||
|
)
|
||||||
// -> Time: 0.0020350217819213867 seconds
|
// -> Time: 0.0020350217819213867 seconds
|
||||||
|
|
||||||
// Generate all colors in xcassets
|
// Generate all colors in xcassets
|
||||||
ColorXcassetHelper.generateXcassetColors(colors: parsedColors,
|
ColorXcassetHelper.generateXcassetColors(
|
||||||
to: options.xcassetsPath)
|
colors: parsedColors,
|
||||||
|
to: options.xcassetsPath
|
||||||
|
)
|
||||||
// -> Time: 3.4505380392074585 seconds
|
// -> Time: 3.4505380392074585 seconds
|
||||||
|
|
||||||
// Generate extension
|
// Generate extension
|
||||||
ColorExtensionGenerator.writeExtensionFile(colors: parsedColors,
|
ColorExtensionGenerator.writeExtensionFile(
|
||||||
staticVar: options.staticMembers,
|
colors: parsedColors,
|
||||||
extensionName: options.extensionName,
|
staticVar: options.staticMembers,
|
||||||
extensionFilePath: options.extensionFilePath)
|
extensionName: options.extensionName,
|
||||||
// -> Time: 0.0010340213775634766 seconds
|
extensionFilePath: options.extensionFilePath,
|
||||||
|
isSwiftUI: true
|
||||||
|
)
|
||||||
|
|
||||||
|
// Generate extension
|
||||||
|
ColorExtensionGenerator.writeExtensionFile(
|
||||||
|
colors: parsedColors,
|
||||||
|
staticVar: options.staticMembers,
|
||||||
|
extensionName: options.extensionNameUIKit,
|
||||||
|
extensionFilePath: options.extensionFilePathUIKit,
|
||||||
|
isSwiftUI: false
|
||||||
|
)
|
||||||
|
|
||||||
print("[\(Self.toolName)] Colors generated")
|
print("[\(Self.toolName)] Colors generated")
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Requirements
|
// MARK: - Requirements
|
||||||
|
|
||||||
private func checkRequirements() -> Bool {
|
private func checkRequirements() -> Bool {
|
||||||
let fileManager = FileManager()
|
let fileManager = FileManager()
|
||||||
|
|
||||||
// Check if input file exists
|
// Check if input file exists
|
||||||
guard fileManager.fileExists(atPath: options.inputFile) else {
|
guard fileManager.fileExists(atPath: options.inputFile) else {
|
||||||
let error = ColorsToolError.fileNotExists(options.inputFile)
|
let error = ColorsToolError.fileNotExists(options.inputFile)
|
||||||
print(error.localizedDescription)
|
print(error.description)
|
||||||
Colors.exit(withError: error)
|
Self.exit(withError: error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if xcassets file exists
|
// Check if xcassets file exists
|
||||||
guard fileManager.fileExists(atPath: options.xcassetsPath) else {
|
guard fileManager.fileExists(atPath: options.xcassetsPath) else {
|
||||||
let error = ColorsToolError.fileNotExists(options.xcassetsPath)
|
let error = ColorsToolError.fileNotExists(options.xcassetsPath)
|
||||||
print(error.localizedDescription)
|
print(error.description)
|
||||||
Colors.exit(withError: error)
|
Self.exit(withError: error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Extension for UIKit and SwiftUI should have different name
|
||||||
|
guard options.extensionName != options.extensionNameUIKit else {
|
||||||
|
let error = ColorsToolError.extensionNamesCollision(options.extensionName)
|
||||||
|
print(error.description)
|
||||||
|
Self.exit(withError: error)
|
||||||
|
}
|
||||||
|
|
||||||
// Check if needed to regenerate
|
// Check if needed to regenerate
|
||||||
guard GeneratorChecker.shouldGenerate(force: options.forceGeneration,
|
guard GeneratorChecker.shouldGenerate(
|
||||||
inputFilePath: options.inputFile,
|
force: options.forceGeneration,
|
||||||
extensionFilePath: options.extensionFilePath) else {
|
inputFilePath: options.inputFile,
|
||||||
|
extensionFilePath: options.extensionFilePath
|
||||||
|
) else {
|
||||||
print("[\(Self.toolName)] Colors are already up to date :) ")
|
print("[\(Self.toolName)] Colors are already up to date :) ")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Helpers
|
// MARK: - Helpers
|
||||||
|
|
||||||
private func deleteCurrentColors() {
|
private func deleteCurrentColors() {
|
||||||
let fileManager = FileManager()
|
let fileManager = FileManager()
|
||||||
let assetsColorPath = "\(options.xcassetsPath)/Colors"
|
let assetsColorPath = "\(options.xcassetsPath)/Colors"
|
||||||
|
|
||||||
if fileManager.fileExists(atPath: assetsColorPath) {
|
if fileManager.fileExists(atPath: assetsColorPath) {
|
||||||
do {
|
do {
|
||||||
try fileManager.removeItem(atPath: assetsColorPath)
|
try fileManager.removeItem(atPath: assetsColorPath)
|
||||||
} catch {
|
} catch {
|
||||||
let error = ColorsToolError.deleteExistingColors("\(options.xcassetsPath)/Colors")
|
let error = ColorsToolError.deleteExistingColors("\(options.xcassetsPath)/Colors")
|
||||||
print(error.localizedDescription)
|
print(error.description)
|
||||||
Colors.exit(withError: error)
|
Self.exit(withError: error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,8 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
enum ColorsToolError: Error {
|
enum ColorsToolError: Error {
|
||||||
|
|
||||||
|
case extensionNamesCollision(String)
|
||||||
case badFormat(String)
|
case badFormat(String)
|
||||||
case writeAsset(String)
|
case writeAsset(String)
|
||||||
case createAssetFolder(String)
|
case createAssetFolder(String)
|
||||||
@ -15,29 +17,32 @@ enum ColorsToolError: Error {
|
|||||||
case fileNotExists(String)
|
case fileNotExists(String)
|
||||||
case badColorDefinition(String, String)
|
case badColorDefinition(String, String)
|
||||||
case deleteExistingColors(String)
|
case deleteExistingColors(String)
|
||||||
|
|
||||||
var description: String {
|
var description: String {
|
||||||
switch self {
|
switch self {
|
||||||
|
case .extensionNamesCollision(let extensionName):
|
||||||
|
return "error: [\(Fonts.toolName)] Error on extension names, extension name and SwiftUI extension name should be different (\(extensionName) is used on both)"
|
||||||
|
|
||||||
case .badFormat(let info):
|
case .badFormat(let info):
|
||||||
return "error:[\(Colors.toolName)] Bad line format: \(info). Accepted format are: colorName=\"#RGB/#ARGB\"; colorName \"#RGB/#ARGB\"; colorName \"#RGB/#ARGB\" \"#RGB/#ARGB\""
|
return "error: [\(Colors.toolName)] Bad line format: \(info). Accepted format are: colorName=\"#RGB/#ARGB\"; colorName \"#RGB/#ARGB\"; colorName \"#RGB/#ARGB\" \"#RGB/#ARGB\""
|
||||||
|
|
||||||
case .writeAsset(let info):
|
case .writeAsset(let info):
|
||||||
return "error:[\(Colors.toolName)] An error occured while writing color in Xcasset: \(info)"
|
return "error: [\(Colors.toolName)] An error occured while writing color in Xcasset: \(info)"
|
||||||
|
|
||||||
case .createAssetFolder(let assetsFolder):
|
case .createAssetFolder(let assetsFolder):
|
||||||
return "error:[\(Colors.toolName)] An error occured while creating colors folder `\(assetsFolder)`"
|
return "error: [\(Colors.toolName)] An error occured while creating colors folder `\(assetsFolder)`"
|
||||||
|
|
||||||
case .writeExtension(let filename, let info):
|
case let .writeExtension(filename, info):
|
||||||
return "error:[\(Colors.toolName)] An error occured while writing extension in \(filename): \(info)"
|
return "error: [\(Colors.toolName)] An error occured while writing extension in \(filename): \(info)"
|
||||||
|
|
||||||
case .fileNotExists(let filename):
|
case .fileNotExists(let filename):
|
||||||
return "error:[\(Colors.toolName)] File \(filename) does not exists"
|
return "error: [\(Colors.toolName)] File \(filename) does not exists"
|
||||||
|
|
||||||
case .badColorDefinition(let lightColor, let darkColor):
|
case let .badColorDefinition(lightColor, darkColor):
|
||||||
return "error:[\(Colors.toolName)] One of these two colors has invalid synthax: -\(lightColor)- or -\(darkColor)-"
|
return "error: [\(Colors.toolName)] One of these two colors has invalid synthax: -\(lightColor)- or -\(darkColor)-"
|
||||||
|
|
||||||
case .deleteExistingColors(let assetsFolder):
|
case .deleteExistingColors(let assetsFolder):
|
||||||
return "error:[\(Colors.toolName)] An error occured while deleting colors folder `\(assetsFolder)`"
|
return "error: [\(Colors.toolName)] An error occured while deleting colors folder `\(assetsFolder)`"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,35 +1,41 @@
|
|||||||
//
|
//
|
||||||
// ColorsToolOptions.swift
|
// ColorsToolOptions.swift
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// Created by Thibaut Schmitt on 17/01/2022.
|
// Created by Thibaut Schmitt on 17/01/2022.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import ArgumentParser
|
import ArgumentParser
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
// swiftlint:disable no_grouping_extension
|
||||||
|
|
||||||
struct ColorsToolOptions: ParsableArguments {
|
struct ColorsToolOptions: ParsableArguments {
|
||||||
|
|
||||||
@Flag(name: [.customShort("f"), .customShort("F")], help: "Should force generation")
|
@Flag(name: [.customShort("f"), .customShort("F")], help: "Should force generation")
|
||||||
var forceGeneration = false
|
var forceGeneration = false
|
||||||
|
|
||||||
@Argument(help: "Input files where colors ared defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
@Argument(help: "Input files where colors ared defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||||
var inputFile: String
|
var inputFile: String
|
||||||
|
|
||||||
@Option(help: "Color style to generate: light for light colors only, or all for dark and light colors")
|
@Option(help: "Color style to generate: light for light colors only, or all for dark and light colors")
|
||||||
fileprivate var style: String
|
var style: ColorStyle
|
||||||
|
|
||||||
@Option(help: "Path of xcassets where to generate colors", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
@Option(help: "Path of xcassets where to generate colors", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||||
var xcassetsPath: String
|
var xcassetsPath: String
|
||||||
|
|
||||||
@Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
@Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||||
var extensionOutputPath: String
|
var extensionOutputPath: String
|
||||||
|
|
||||||
@Option(help: "Tell if it will generate static properties or not")
|
@Option(help: "Tell if it will generate static properties or not")
|
||||||
var staticMembers: Bool = false
|
var staticMembers: Bool = false
|
||||||
|
|
||||||
@Option(help: "Extension name. If not specified, it will generate an UIColor extension. Using default extension name will generate static property.")
|
@Option(help: "Extension name. If not specified, it will generate an Color extension.")
|
||||||
var extensionName: String = Colors.defaultExtensionName
|
var extensionName: String = Colors.defaultExtensionName
|
||||||
|
|
||||||
|
@Option(help: "SwiftUI Extension name. If not specified, it will generate an UIColor extension.")
|
||||||
|
var extensionNameUIKit: String = Colors.defaultExtensionNameUIKit
|
||||||
|
|
||||||
@Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+ColorsMyApp.swift")
|
@Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+ColorsMyApp.swift")
|
||||||
var extensionSuffix: String?
|
var extensionSuffix: String?
|
||||||
}
|
}
|
||||||
@ -37,18 +43,30 @@ struct ColorsToolOptions: ParsableArguments {
|
|||||||
// MARK: - Computed var
|
// MARK: - Computed var
|
||||||
|
|
||||||
extension ColorsToolOptions {
|
extension ColorsToolOptions {
|
||||||
var colorStyle: ColorStyle {
|
|
||||||
ColorStyle(rawValue: style) ?? .all
|
// MARK: - SwiftUI
|
||||||
}
|
|
||||||
|
|
||||||
var extensionFileName: String {
|
var extensionFileName: String {
|
||||||
if let extensionSuffix = extensionSuffix {
|
if let extensionSuffix {
|
||||||
return "\(extensionName)+\(extensionSuffix).swift"
|
return "\(extensionName)+\(extensionSuffix).swift"
|
||||||
}
|
}
|
||||||
return "\(extensionName).swift"
|
return "\(extensionName).swift"
|
||||||
}
|
}
|
||||||
|
|
||||||
var extensionFilePath: String {
|
var extensionFilePath: String {
|
||||||
"\(extensionOutputPath)/\(extensionFileName)"
|
"\(extensionOutputPath)/\(extensionFileName)"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - UIKit
|
||||||
|
|
||||||
|
var extensionFileNameUIKit: String {
|
||||||
|
if let extensionSuffix {
|
||||||
|
return "\(extensionNameUIKit)+\(extensionSuffix).swift"
|
||||||
|
}
|
||||||
|
return "\(extensionNameUIKit).swift"
|
||||||
|
}
|
||||||
|
|
||||||
|
var extensionFilePathUIKit: String {
|
||||||
|
"\(extensionOutputPath)/\(extensionFileNameUIKit)"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//
|
//
|
||||||
// ColorExtensionGenerator.swift
|
// ColorExtensionGenerator.swift
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// Created by Thibaut Schmitt on 20/12/2021.
|
// Created by Thibaut Schmitt on 20/12/2021.
|
||||||
//
|
//
|
||||||
@ -9,58 +9,76 @@ import Foundation
|
|||||||
import ToolCore
|
import ToolCore
|
||||||
|
|
||||||
struct ColorExtensionGenerator {
|
struct ColorExtensionGenerator {
|
||||||
|
|
||||||
let colors: [ParsedColor]
|
let colors: [ParsedColor]
|
||||||
let extensionClassname: String
|
let extensionClassname: String
|
||||||
|
|
||||||
static func writeExtensionFile(colors: [ParsedColor], staticVar: Bool, extensionName: String, extensionFilePath: String) {
|
// MARK: - UIKit
|
||||||
|
|
||||||
|
static func writeExtensionFile(
|
||||||
|
colors: [ParsedColor],
|
||||||
|
staticVar: Bool,
|
||||||
|
extensionName: String,
|
||||||
|
extensionFilePath: String,
|
||||||
|
isSwiftUI: Bool
|
||||||
|
) {
|
||||||
// Create extension content
|
// Create extension content
|
||||||
let extensionContent = Self.getExtensionContent(colors: colors,
|
let extensionContent = Self.getExtensionContent(
|
||||||
staticVar: staticVar,
|
colors: colors,
|
||||||
extensionName: extensionName)
|
staticVar: staticVar,
|
||||||
|
extensionName: extensionName,
|
||||||
|
isSwiftUI: isSwiftUI
|
||||||
|
)
|
||||||
|
|
||||||
// Write content
|
// Write content
|
||||||
let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath)
|
let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath)
|
||||||
do {
|
do {
|
||||||
try extensionContent.write(to: extensionFilePathURL, atomically: false, encoding: .utf8)
|
try extensionContent.write(to: extensionFilePathURL, atomically: false, encoding: .utf8)
|
||||||
} catch (let error) {
|
} catch {
|
||||||
let error = ColorsToolError.writeExtension(extensionFilePath, error.localizedDescription)
|
let error = ColorsToolError.writeExtension(extensionFilePath, error.localizedDescription)
|
||||||
print(error.localizedDescription)
|
print(error.description)
|
||||||
Colors.exit(withError: error)
|
Colors.exit(withError: error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static func getExtensionContent(colors: [ParsedColor], staticVar: Bool, extensionName: String) -> String {
|
static func getExtensionContent(
|
||||||
|
colors: [ParsedColor],
|
||||||
|
staticVar: Bool,
|
||||||
|
extensionName: String,
|
||||||
|
isSwiftUI: Bool
|
||||||
|
) -> String {
|
||||||
[
|
[
|
||||||
Self.getHeader(extensionClassname: extensionName),
|
Self.getHeader(extensionClassname: extensionName, isSwiftUI: isSwiftUI),
|
||||||
Self.getProperties(for: colors, withStaticVar: staticVar),
|
Self.getProperties(for: colors, withStaticVar: staticVar, isSwiftUI: isSwiftUI),
|
||||||
Self.getFooter()
|
Self.getFooter()
|
||||||
]
|
]
|
||||||
.joined(separator: "\n")
|
.joined(separator: "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func getHeader(extensionClassname: String) -> String {
|
private static func getHeader(extensionClassname: String, isSwiftUI: Bool) -> String {
|
||||||
"""
|
"""
|
||||||
// Generated by ResgenSwift.\(Colors.toolName) \(ResgenSwiftVersion)
|
// Generated by ResgenSwift.\(Colors.toolName) \(ResgenSwiftVersion)
|
||||||
|
|
||||||
import UIKit
|
import \(isSwiftUI ? "SwiftUI" : "UIKit")
|
||||||
|
|
||||||
extension \(extensionClassname) {\n
|
extension \(extensionClassname) {\n
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func getFooter() -> String {
|
private static func getFooter() -> String {
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func getProperties(for colors: [ParsedColor], withStaticVar staticVar: Bool) -> String {
|
private static func getProperties(
|
||||||
|
for colors: [ParsedColor],
|
||||||
|
withStaticVar staticVar: Bool,
|
||||||
|
isSwiftUI: Bool
|
||||||
|
) -> String {
|
||||||
colors.map {
|
colors.map {
|
||||||
if staticVar {
|
$0.getColorProperty(isStatic: staticVar, isSwiftUI: isSwiftUI)
|
||||||
return $0.getColorStaticProperty()
|
|
||||||
}
|
|
||||||
return $0.getColorProperty()
|
|
||||||
}
|
}
|
||||||
.joined(separator: "\n\n")
|
.joined(separator: "\n\n")
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//
|
//
|
||||||
// ColorXcassetHelper.swift
|
// ColorXcassetHelper.swift
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// Created by Thibaut Schmitt on 20/12/2021.
|
// Created by Thibaut Schmitt on 20/12/2021.
|
||||||
//
|
//
|
||||||
@ -8,39 +8,41 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import ToolCore
|
import ToolCore
|
||||||
|
|
||||||
struct ColorXcassetHelper {
|
enum ColorXcassetHelper {
|
||||||
|
|
||||||
static func generateXcassetColors(colors: [ParsedColor], to xcassetsPath: String) {
|
static func generateXcassetColors(colors: [ParsedColor], to xcassetsPath: String) {
|
||||||
colors.forEach {
|
colors.forEach {
|
||||||
Self.generateColorSetAssets(from: $0, to: xcassetsPath)
|
Self.generateColorSetAssets(from: $0, to: xcassetsPath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate ColorSet in XCAssets file
|
// Generate ColorSet in XCAssets file
|
||||||
private static func generateColorSetAssets(from color: ParsedColor, to xcassetsPath: String) {
|
private static func generateColorSetAssets(from color: ParsedColor, to xcassetsPath: String) {
|
||||||
// Create ColorSet
|
// Create ColorSet
|
||||||
let colorSetPath = "\(xcassetsPath)/Colors/\(color.name).colorset"
|
let colorSetPath = "\(xcassetsPath)/Colors/\(color.name).colorset"
|
||||||
let contentsJsonPath = "\(colorSetPath)/Contents.json"
|
let contentsJsonPath = "\(colorSetPath)/Contents.json"
|
||||||
|
|
||||||
let fileManager = FileManager()
|
let fileManager = FileManager()
|
||||||
if fileManager.fileExists(atPath: colorSetPath) == false {
|
if fileManager.fileExists(atPath: colorSetPath) == false {
|
||||||
do {
|
do {
|
||||||
try fileManager.createDirectory(atPath: colorSetPath,
|
try fileManager.createDirectory(
|
||||||
withIntermediateDirectories: true)
|
atPath: colorSetPath,
|
||||||
|
withIntermediateDirectories: true
|
||||||
|
)
|
||||||
} catch {
|
} catch {
|
||||||
let error = ColorsToolError.createAssetFolder(colorSetPath)
|
let error = ColorsToolError.createAssetFolder(colorSetPath)
|
||||||
print(error.localizedDescription)
|
print(error.description)
|
||||||
Colors.exit(withError: error)
|
Colors.exit(withError: error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write content in Contents.json
|
// Write content in Contents.json
|
||||||
let contentsJsonPathURL = URL(fileURLWithPath: contentsJsonPath)
|
let contentsJsonPathURL = URL(fileURLWithPath: contentsJsonPath)
|
||||||
do {
|
do {
|
||||||
try color.contentsJSON().write(to: contentsJsonPathURL, atomically: false, encoding: .utf8)
|
try color.contentsJSON().write(to: contentsJsonPathURL, atomically: false, encoding: .utf8)
|
||||||
} catch (let error) {
|
} catch {
|
||||||
let error = ColorsToolError.writeAsset(error.localizedDescription)
|
let error = ColorsToolError.writeAsset(error.localizedDescription)
|
||||||
print(error.localizedDescription)
|
print(error.description)
|
||||||
Colors.exit(withError: error)
|
Colors.exit(withError: error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,9 +5,18 @@
|
|||||||
// Created by Thibaut Schmitt on 29/08/2022.
|
// Created by Thibaut Schmitt on 29/08/2022.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import ArgumentParser
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
enum ColorStyle: String, Decodable {
|
enum ColorStyle: String, Decodable, ExpressibleByArgument {
|
||||||
|
|
||||||
case light
|
case light
|
||||||
case all
|
case all
|
||||||
|
|
||||||
|
static var allValueStrings: [String] {
|
||||||
|
[
|
||||||
|
Self.light.rawValue,
|
||||||
|
Self.all.rawValue
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,28 +8,29 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct ParsedColor {
|
struct ParsedColor {
|
||||||
|
|
||||||
let name: String
|
let name: String
|
||||||
let light: String
|
let light: String
|
||||||
let dark: String
|
let dark: String
|
||||||
|
|
||||||
// Generate Contents.json content
|
// Generate Contents.json content
|
||||||
func contentsJSON() -> String {
|
func contentsJSON() -> String {
|
||||||
let lightARGB = light.colorComponent()
|
let lightARGB = light.colorComponent()
|
||||||
let darkARGB = dark.colorComponent()
|
let darkARGB = dark.colorComponent()
|
||||||
|
|
||||||
let allComponents = [
|
let allComponents = [
|
||||||
lightARGB.alpha, lightARGB.red, lightARGB.green, lightARGB.blue,
|
lightARGB.alpha, lightARGB.red, lightARGB.green, lightARGB.blue,
|
||||||
darkARGB.alpha, darkARGB.red, darkARGB.green, darkARGB.blue
|
darkARGB.alpha, darkARGB.red, darkARGB.green, darkARGB.blue
|
||||||
].map {
|
].map {
|
||||||
$0.isEmpty
|
$0.isEmpty
|
||||||
}
|
}
|
||||||
|
|
||||||
guard allComponents.contains(true) == false else {
|
guard allComponents.contains(true) == false else {
|
||||||
let error = ColorsToolError.badColorDefinition(light, dark)
|
let error = ColorsToolError.badColorDefinition(light, dark)
|
||||||
print(error.localizedDescription)
|
print(error.description)
|
||||||
Colors.exit(withError: error)
|
Colors.exit(withError: error)
|
||||||
}
|
}
|
||||||
|
|
||||||
return """
|
return """
|
||||||
{
|
{
|
||||||
"colors": [
|
"colors": [
|
||||||
@ -71,20 +72,21 @@ struct ParsedColor {
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
|
|
||||||
func getColorProperty() -> String {
|
|
||||||
"""
|
|
||||||
/// Color \(name) is \(light) (light) or \(dark) (dark)"
|
|
||||||
@objc var \(name): UIColor {
|
|
||||||
UIColor(named: "\(name)")!
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
|
|
||||||
func getColorStaticProperty() -> String {
|
// MARK: - UIKit
|
||||||
"""
|
|
||||||
|
func getColorProperty(isStatic: Bool, isSwiftUI: Bool) -> String {
|
||||||
|
if isSwiftUI {
|
||||||
|
return """
|
||||||
|
/// Color \(name) is \(light) (light) or \(dark) (dark)"
|
||||||
|
\(isStatic ? "static " : "")var \(name): Color {
|
||||||
|
Color("\(name)")
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
return """
|
||||||
/// Color \(name) is \(light) (light) or \(dark) (dark)"
|
/// Color \(name) is \(light) (light) or \(dark) (dark)"
|
||||||
static var \(name): UIColor {
|
\(isStatic ? "static " : "@objc ")var \(name): UIColor {
|
||||||
UIColor(named: "\(name)")!
|
UIColor(named: "\(name)")!
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
|
@ -7,44 +7,45 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
class ColorFileParser {
|
enum ColorFileParser {
|
||||||
|
|
||||||
static func parse(_ inputFile: String, colorStyle: ColorStyle) -> [ParsedColor] {
|
static func parse(_ inputFile: String, colorStyle: ColorStyle) -> [ParsedColor] {
|
||||||
// Get content of input file
|
// Get content of input file
|
||||||
let inputFileContent = try! String(contentsOfFile: inputFile, encoding: .utf8)
|
let inputFileContent = try! String(contentsOfFile: inputFile, encoding: .utf8) // swiftlint:disable:this force_try
|
||||||
let colorsByLines = inputFileContent.components(separatedBy: CharacterSet.newlines)
|
let colorsByLines = inputFileContent.components(separatedBy: CharacterSet.newlines)
|
||||||
|
|
||||||
// Iterate on each line of input file
|
// Iterate on each line of input file
|
||||||
return parseLines(lines: colorsByLines, colorStyle: colorStyle)
|
return parseLines(lines: colorsByLines, colorStyle: colorStyle)
|
||||||
}
|
}
|
||||||
|
|
||||||
static func parseLines(lines: [String], colorStyle: ColorStyle) -> [ParsedColor] {
|
static func parseLines(lines: [String], colorStyle: ColorStyle) -> [ParsedColor] {
|
||||||
lines
|
lines
|
||||||
.enumerated()
|
.enumerated()
|
||||||
.compactMap { lineNumber, colorLine in
|
.compactMap { _, colorLine in // swiftlint:disable:this unused_enumerated
|
||||||
// Required format:
|
// Required format:
|
||||||
// colorName = "#RGB/#ARGB", colorName "#RGB/#ARGB", colorName "#RGB/#ARGB" "#RGB/#ARGB"
|
// colorName = "#RGB/#ARGB", colorName "#RGB/#ARGB", colorName "#RGB/#ARGB" "#RGB/#ARGB"
|
||||||
let colorLineCleanedUp = colorLine
|
let colorLineCleanedUp = colorLine
|
||||||
.removeLeadingWhitespace()
|
.removeLeadingWhitespace()
|
||||||
.removeTrailingWhitespace()
|
.removeTrailingWhitespace()
|
||||||
.replacingOccurrences(of: "=", with: "") // Keep compat with current file format
|
.replacingOccurrences(of: "=", with: "") // Keep compat with current file format
|
||||||
|
|
||||||
guard colorLineCleanedUp.hasPrefix("#") == false, colorLineCleanedUp.isEmpty == false else {
|
guard colorLineCleanedUp.hasPrefix("#") == false, colorLineCleanedUp.isEmpty == false else {
|
||||||
// debugPrint("[\(Colors.toolName)] ⚠️ BadFormat or empty line (line number: \(lineNumber + 1)). Skip this line")
|
// debugPrint("[\(Colors.toolName)] ⚠️ BadFormat or empty line (line number: \(lineNumber + 1)). Skip this line")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
let colorContent = colorLineCleanedUp.split(separator: " ")
|
let colorContent = colorLineCleanedUp.split(separator: " ")
|
||||||
|
|
||||||
guard colorContent.count >= 2 else {
|
guard colorContent.count >= 2 else {
|
||||||
let error = ColorsToolError.badFormat(colorLine)
|
let error = ColorsToolError.badFormat(colorLine)
|
||||||
print(error.localizedDescription)
|
print(error.description)
|
||||||
Colors.exit(withError: error)
|
Colors.exit(withError: error)
|
||||||
}
|
}
|
||||||
|
|
||||||
switch colorStyle {
|
switch colorStyle {
|
||||||
case .light:
|
case .light:
|
||||||
return ParsedColor(name: String(colorContent[0]), light: String(colorContent[1]), dark: String(colorContent[1]))
|
return ParsedColor(name: String(colorContent[0]), light: String(colorContent[1]), dark: String(colorContent[1]))
|
||||||
|
|
||||||
case .all:
|
case .all:
|
||||||
if colorContent.count == 3 {
|
if colorContent.count == 3 {
|
||||||
return ParsedColor(name: String(colorContent[0]), light: String(colorContent[1]), dark: String(colorContent[2]))
|
return ParsedColor(name: String(colorContent[0]), light: String(colorContent[1]), dark: String(colorContent[2]))
|
||||||
|
@ -5,40 +5,73 @@
|
|||||||
// Created by Thibaut Schmitt on 17/01/2022.
|
// Created by Thibaut Schmitt on 17/01/2022.
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import ArgumentParser
|
import ArgumentParser
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
// swiftlint:disable no_grouping_extension
|
||||||
|
|
||||||
struct FontsOptions: ParsableArguments {
|
struct FontsOptions: ParsableArguments {
|
||||||
|
|
||||||
@Flag(name: [.customShort("f"), .customShort("F")], help: "Should force generation")
|
@Flag(name: [.customShort("f"), .customShort("F")], help: "Should force generation")
|
||||||
var forceGeneration = false
|
var forceGeneration = false
|
||||||
|
|
||||||
@Argument(help: "Input files where fonts ared defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
@Argument(help: "Input files where fonts ared defined.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||||
var inputFile: String
|
var inputFile: String
|
||||||
|
|
||||||
@Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
@Option(help: "Path where to generate the extension.", transform: { $0.replaceTiltWithHomeDirectoryPath() })
|
||||||
var extensionOutputPath: String
|
var extensionOutputPath: String
|
||||||
|
|
||||||
@Option(help: "Tell if it will generate static properties or methods")
|
@Option(help: "Tell if it will generate static properties or methods")
|
||||||
var staticMembers: Bool = false
|
var staticMembers: Bool = false
|
||||||
|
|
||||||
@Option(help: "Extension name. If not specified, it will generate an UIFont extension. Using default extension name will generate static property.")
|
@Option(help: "Extension name. If not specified, it will generate an Font extension.")
|
||||||
var extensionName: String = Fonts.defaultExtensionName
|
var extensionName: String = Fonts.defaultExtensionName
|
||||||
|
|
||||||
|
@Option(help: "Extension name. If not specified, it will generate an UIFont extension.")
|
||||||
|
var extensionNameUIKit: String = Fonts.defaultExtensionNameUIKit
|
||||||
|
|
||||||
@Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+FontsMyApp.swift")
|
@Option(help: "Extension suffix. Ex: MyApp, it will generate {extensionName}+FontsMyApp.swift")
|
||||||
var extensionSuffix: String = ""
|
var extensionSuffix: String = ""
|
||||||
|
|
||||||
|
@Option(name: .customLong("info-plist-paths"), help: "Info.plist paths (array). Will be used to update UIAppFonts content")
|
||||||
|
fileprivate var infoPlistPathsRaw: String = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Computed var
|
// MARK: - Computed var
|
||||||
|
|
||||||
extension FontsOptions {
|
extension FontsOptions {
|
||||||
|
|
||||||
|
// MARK: - SwiftUI
|
||||||
|
|
||||||
var extensionFileName: String {
|
var extensionFileName: String {
|
||||||
if extensionSuffix.isEmpty == false {
|
if extensionSuffix.isEmpty == false {
|
||||||
return "\(extensionName)+\(extensionSuffix).swift"
|
return "\(extensionName)+\(extensionSuffix).swift"
|
||||||
}
|
}
|
||||||
return "\(extensionName).swift"
|
return "\(extensionName).swift"
|
||||||
}
|
}
|
||||||
|
|
||||||
var extensionFilePath: String {
|
var extensionFilePath: String {
|
||||||
"\(extensionOutputPath)/\(extensionFileName)"
|
"\(extensionOutputPath)/\(extensionFileName)"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MARK: - UIKit
|
||||||
|
|
||||||
|
var extensionFileNameUIKit: String {
|
||||||
|
if extensionSuffix.isEmpty == false {
|
||||||
|
return "\(extensionNameUIKit)+\(extensionSuffix).swift"
|
||||||
|
}
|
||||||
|
return "\(extensionNameUIKit).swift"
|
||||||
|
}
|
||||||
|
|
||||||
|
var extensionFilePathUIKit: String {
|
||||||
|
"\(extensionOutputPath)/\(extensionFileNameUIKit)"
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: -
|
||||||
|
|
||||||
|
var infoPlistPaths: [String] {
|
||||||
|
infoPlistPathsRaw
|
||||||
|
.split(separator: " ")
|
||||||
|
.map { String($0) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,83 +1,109 @@
|
|||||||
//
|
//
|
||||||
// Fonts.swift
|
// Fonts.swift
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// Created by Thibaut Schmitt on 13/12/2021.
|
// Created by Thibaut Schmitt on 13/12/2021.
|
||||||
//
|
//
|
||||||
|
|
||||||
import ToolCore
|
|
||||||
import Foundation
|
|
||||||
import ArgumentParser
|
import ArgumentParser
|
||||||
|
import Foundation
|
||||||
|
import ToolCore
|
||||||
|
|
||||||
struct Fonts: ParsableCommand {
|
struct Fonts: ParsableCommand {
|
||||||
|
|
||||||
// MARK: - CommandConfiguration
|
// MARK: - CommandConfiguration
|
||||||
|
|
||||||
static var configuration = CommandConfiguration(
|
static var configuration = CommandConfiguration(
|
||||||
abstract: "A utility to generate an helpful etension to access your custom font from code and also Info.plist UIAppsFont content.",
|
abstract: "A utility to generate an helpful etension to access your custom font from code and also Info.plist UIAppsFont content.",
|
||||||
version: ResgenSwiftVersion
|
version: ResgenSwiftVersion
|
||||||
)
|
)
|
||||||
|
|
||||||
// MARK: - Static
|
// MARK: - Static
|
||||||
|
|
||||||
static let toolName = "Fonts"
|
static let toolName = "Fonts"
|
||||||
static let defaultExtensionName = "UIFont"
|
static let defaultExtensionName = "Font"
|
||||||
|
static let defaultExtensionNameUIKit = "UIFont"
|
||||||
|
|
||||||
// MARK: - Command Options
|
// MARK: - Command Options
|
||||||
|
|
||||||
@OptionGroup var options: FontsOptions
|
@OptionGroup var options: FontsOptions
|
||||||
|
|
||||||
// MARK: - Run
|
// MARK: - Run
|
||||||
|
|
||||||
public func run() throws {
|
func run() throws {
|
||||||
print("[\(Self.toolName)] Starting fonts generation")
|
print("[\(Self.toolName)] Starting fonts generation")
|
||||||
print("[\(Self.toolName)] Will use inputFile \(options.inputFile) to generate fonts")
|
print("[\(Self.toolName)] Will use inputFile \(options.inputFile) to generate fonts")
|
||||||
|
|
||||||
// Check requirements
|
// Check requirements
|
||||||
guard checkRequirements() else { return }
|
guard checkRequirements() else { return }
|
||||||
|
|
||||||
print("[\(Self.toolName)] Will generate fonts")
|
print("[\(Self.toolName)] Will generate fonts")
|
||||||
|
|
||||||
// Get fonts to generate
|
// Get fonts to generate
|
||||||
let fontsToGenerate = FontFileParser.parse(options.inputFile)
|
let fontsToGenerate = FontFileParser.parse(options.inputFile)
|
||||||
|
|
||||||
// Get real font names
|
// Get real font names
|
||||||
let inputFolder = URL(fileURLWithPath: options.inputFile).deletingLastPathComponent().relativePath
|
let inputFolder = URL(fileURLWithPath: options.inputFile)
|
||||||
let fontsNames = FontsToolHelper.getFontPostScriptName(for: fontsToGenerate,
|
.deletingLastPathComponent()
|
||||||
inputFolder: inputFolder)
|
.relativePath
|
||||||
|
|
||||||
|
let fontsNames = FontsToolHelper.getFontPostScriptName(
|
||||||
|
for: fontsToGenerate,
|
||||||
|
inputFolder: inputFolder
|
||||||
|
)
|
||||||
|
|
||||||
// Generate extension
|
// Generate extension
|
||||||
FontExtensionGenerator.writeExtensionFile(fontsNames: fontsNames,
|
FontExtensionGenerator.writeExtensionFile(
|
||||||
staticVar: options.staticMembers,
|
fontsNames: fontsNames,
|
||||||
extensionName: options.extensionName,
|
staticVar: options.staticMembers,
|
||||||
extensionFilePath: options.extensionFilePath)
|
extensionName: options.extensionName,
|
||||||
|
extensionFilePath: options.extensionFilePath,
|
||||||
print("Info.plist information:")
|
isSwiftUI: true
|
||||||
print("\(FontPlistGenerator.generatePlistUIAppsFontContent(for: fontsNames))")
|
)
|
||||||
|
|
||||||
|
FontExtensionGenerator.writeExtensionFile(
|
||||||
|
fontsNames: fontsNames,
|
||||||
|
staticVar: options.staticMembers,
|
||||||
|
extensionName: options.extensionNameUIKit,
|
||||||
|
extensionFilePath: options.extensionFilePathUIKit,
|
||||||
|
isSwiftUI: false
|
||||||
|
)
|
||||||
|
|
||||||
|
print("Info.plist has been updated with:")
|
||||||
|
print("\(FontPlistGenerator.generatePlistUIAppsFontContent(for: fontsNames, infoPlistPaths: options.infoPlistPaths))")
|
||||||
|
|
||||||
print("[\(Self.toolName)] Fonts generated")
|
print("[\(Self.toolName)] Fonts generated")
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Requirements
|
// MARK: - Requirements
|
||||||
|
|
||||||
private func checkRequirements() -> Bool {
|
private func checkRequirements() -> Bool {
|
||||||
let fileManager = FileManager()
|
let fileManager = FileManager()
|
||||||
|
|
||||||
// Check input file exists
|
// Check input file exists
|
||||||
guard fileManager.fileExists(atPath: options.inputFile) else {
|
guard fileManager.fileExists(atPath: options.inputFile) else {
|
||||||
let error = FontsToolError.fileNotExists(options.inputFile)
|
let error = FontsToolError.fileNotExists(options.inputFile)
|
||||||
print(error.localizedDescription)
|
print(error.description)
|
||||||
Fonts.exit(withError: error)
|
Self.exit(withError: error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Extension for UIKit and SwiftUI should have different name
|
||||||
|
guard options.extensionName != options.extensionNameUIKit else {
|
||||||
|
let error = FontsToolError.extensionNamesCollision(options.extensionName)
|
||||||
|
print(error.description)
|
||||||
|
Self.exit(withError: error)
|
||||||
|
}
|
||||||
|
|
||||||
// Check if needed to regenerate
|
// Check if needed to regenerate
|
||||||
guard GeneratorChecker.shouldGenerate(force: options.forceGeneration,
|
guard GeneratorChecker.shouldGenerate(
|
||||||
inputFilePath: options.inputFile,
|
force: options.forceGeneration,
|
||||||
extensionFilePath: options.extensionFilePath) else {
|
inputFilePath: options.inputFile,
|
||||||
|
extensionFilePath: options.extensionFilePath
|
||||||
|
) else {
|
||||||
print("[\(Self.toolName)] Fonts are already up to date :) ")
|
print("[\(Self.toolName)] Fonts are already up to date :) ")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,24 +8,29 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
enum FontsToolError: Error {
|
enum FontsToolError: Error {
|
||||||
|
|
||||||
|
case extensionNamesCollision(String)
|
||||||
case fcScan(String, Int32, String?)
|
case fcScan(String, Int32, String?)
|
||||||
case inputFolderNotFound(String)
|
case inputFolderNotFound(String)
|
||||||
case fileNotExists(String)
|
case fileNotExists(String)
|
||||||
case writeExtension(String, String)
|
case writeExtension(String, String)
|
||||||
|
|
||||||
var localizedDescription: String {
|
var description: String {
|
||||||
switch self {
|
switch self {
|
||||||
case .fcScan(let path, let code, let output):
|
case .extensionNamesCollision(let extensionName):
|
||||||
return "error:[\(Fonts.toolName)] Error while getting fontName (fc-scan --format %{postscriptname} \(path). fc-scan exit with \(code) and output is: \(output ?? "no output")"
|
return "error: [\(Fonts.toolName)] Error on extension names, extension name and SwiftUI extension name should be different (\(extensionName) is used on both)"
|
||||||
|
|
||||||
|
case let .fcScan(path, code, output):
|
||||||
|
return "error: [\(Fonts.toolName)] Error while getting fontName (fc-scan --format %{postscriptname} \(path). fc-scan exit with \(code) and output is: \(output ?? "no output")"
|
||||||
|
|
||||||
case .inputFolderNotFound(let inputFolder):
|
case .inputFolderNotFound(let inputFolder):
|
||||||
return " error:[\(Fonts.toolName)] Input folder not found: \(inputFolder)"
|
return "error: [\(Fonts.toolName)] Input folder not found: \(inputFolder)"
|
||||||
|
|
||||||
case .fileNotExists(let filename):
|
case .fileNotExists(let filename):
|
||||||
return " error:[\(Fonts.toolName)] File \(filename) does not exists"
|
return "error: [\(Fonts.toolName)] File \(filename) does not exists"
|
||||||
|
|
||||||
case .writeExtension(let filename, let info):
|
case let .writeExtension(filename, info):
|
||||||
return "error:[\(Fonts.toolName)] An error occured while writing extension in \(filename): \(info)"
|
return "error: [\(Fonts.toolName)] An error occured while writing extension in \(filename): \(info)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//
|
//
|
||||||
// FontsToolHelper.swift
|
// FontsToolHelper.swift
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
// Created by Thibaut Schmitt on 13/12/2021.
|
// Created by Thibaut Schmitt on 13/12/2021.
|
||||||
//
|
//
|
||||||
@ -8,66 +8,75 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import ToolCore
|
import ToolCore
|
||||||
|
|
||||||
class FontsToolHelper {
|
enum FontsToolHelper {
|
||||||
|
|
||||||
static func getFontPostScriptName(for fonts: [String], inputFolder: String) -> [FontName] {
|
static func getFontPostScriptName(for fonts: [String], inputFolder: String) -> [FontName] {
|
||||||
let fontsFilenames = Self.getFontsFilenames(fromInputFolder: inputFolder)
|
let fontsFilenames = Self.getFontsFilenames(fromInputFolder: inputFolder)
|
||||||
.filter { fontNameWithPath in
|
.filter { fontNameWithPath in
|
||||||
let fontName = URL(fileURLWithPath: fontNameWithPath)
|
let fontName = URL(fileURLWithPath: fontNameWithPath)
|
||||||
.deletingPathExtension()
|
.deletingPathExtension()
|
||||||
.lastPathComponent
|
.lastPathComponent
|
||||||
|
|
||||||
if fonts.contains(fontName) {
|
if fonts.contains(fontName) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
let fontsFilesnamesWithPath = fontsFilenames.map {
|
let fontsFilesnamesWithPath = fontsFilenames.map {
|
||||||
"\(inputFolder)/\($0)"
|
"\(inputFolder)/\($0)"
|
||||||
}
|
}
|
||||||
|
|
||||||
return fontsFilesnamesWithPath.compactMap {
|
return fontsFilesnamesWithPath.compactMap {
|
||||||
Self.getFontName(atPath: $0)
|
Self.getFontName(atPath: $0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: - Private
|
// MARK: - Private
|
||||||
|
|
||||||
private static func getFontsFilenames(fromInputFolder inputFolder: String) -> [String] {
|
private static func getFontsFilenames(fromInputFolder inputFolder: String) -> [String] {
|
||||||
// Get a enumerator for all files
|
// Get a enumerator for all files
|
||||||
let fileManager = FileManager()
|
let fileManager = FileManager()
|
||||||
guard fileManager.fileExists(atPath: inputFolder) else {
|
guard fileManager.fileExists(atPath: inputFolder) else {
|
||||||
let error = FontsToolError.inputFolderNotFound(inputFolder)
|
let error = FontsToolError.inputFolderNotFound(inputFolder)
|
||||||
print(error.localizedDescription)
|
print(error.description)
|
||||||
Fonts.exit(withError: error)
|
Fonts.exit(withError: error)
|
||||||
}
|
}
|
||||||
|
|
||||||
let enumerator: FileManager.DirectoryEnumerator = fileManager.enumerator(atPath: inputFolder)!
|
let enumerator: FileManager.DirectoryEnumerator = fileManager.enumerator(atPath: inputFolder)! // swiftlint:disable:this force_unwrapping
|
||||||
|
|
||||||
// Filters font files
|
// Filters font files
|
||||||
let fontsFileNames: [String] = (enumerator.allObjects as! [String])
|
let fontsFileNames: [String] = (enumerator.allObjects as! [String]) // swiftlint:disable:this force_cast
|
||||||
.filter {
|
.filter {
|
||||||
if $0.hasSuffix(".ttf") || $0.hasSuffix(".otf") {
|
if $0.hasSuffix(".ttf") || $0.hasSuffix(".otf") {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return fontsFileNames
|
return fontsFileNames
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func getFontName(atPath path: String) -> String {
|
private static func getFontName(atPath path: String) -> FontName {
|
||||||
//print("fc-scan --format %{postscriptname} \(path)")
|
// print("fc-scan --format %{postscriptname} \(path)")
|
||||||
// Get real font name
|
// Get real font name
|
||||||
let task = Shell.shell(["fc-scan", "--format", "%{postscriptname}", path])
|
let task = Shell.shell(["fc-scan", "--format", "%{postscriptname}", path])
|
||||||
|
|
||||||
guard let fontName = task.output, task.terminationStatus == 0 else {
|
guard let postscriptName = task.output, task.terminationStatus == 0 else {
|
||||||
let error = FontsToolError.fcScan(path, task.terminationStatus, task.output)
|
let error = FontsToolError.fcScan(path, task.terminationStatus, task.output)
|
||||||
print(error.localizedDescription)
|
print(error.description)
|
||||||
Fonts.exit(withError: error)
|
Fonts.exit(withError: error)
|
||||||
}
|
}
|
||||||
|
|
||||||
return fontName
|
let pathURL = URL(fileURLWithPath: path)
|
||||||
|
let filename = pathURL
|
||||||
|
.deletingPathExtension()
|
||||||
|
.lastPathComponent
|
||||||
|
|
||||||
|
return FontName(
|
||||||
|
postscriptName: postscriptName,
|
||||||
|
filename: filename,
|
||||||
|
fileExtension: pathURL.pathExtension
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,17 +6,46 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
import ToolCore
|
||||||
|
|
||||||
class FontPlistGenerator {
|
enum FontPlistGenerator {
|
||||||
static func generatePlistUIAppsFontContent(for fonts: [FontName]) -> String {
|
|
||||||
var plistData = "<key>UIAppFonts</key>\n\t<array>\n"
|
static func generatePlistUIAppsFontContent(for fonts: [FontName], infoPlistPaths: [String]) -> String {
|
||||||
fonts
|
let fontsToAddToPlist = fonts
|
||||||
.compactMap { $0 }
|
.compactMap { $0 }
|
||||||
.forEach {
|
|
||||||
plistData += "\t\t<string>\($0)</string>\n"
|
// Update each plist
|
||||||
|
infoPlistPaths.forEach { infoPlist in
|
||||||
|
// Remove UIAppFonts value
|
||||||
|
Shell.shell(
|
||||||
|
launchPath: "/usr/libexec/PlistBuddy",
|
||||||
|
["-c", "delete :UIAppFonts", infoPlist]
|
||||||
|
)
|
||||||
|
|
||||||
|
// Add UIAppFonts empty array
|
||||||
|
debugPrint("Will PlistBuddy -c add :UIAppFonts array \(infoPlist)")
|
||||||
|
Shell.shell(
|
||||||
|
launchPath: "/usr/libexec/PlistBuddy",
|
||||||
|
["-c", "add :UIAppFonts array", infoPlist]
|
||||||
|
)
|
||||||
|
|
||||||
|
// Fill array with fonts
|
||||||
|
fontsToAddToPlist
|
||||||
|
.forEach { fontName in
|
||||||
|
Shell.shell(
|
||||||
|
launchPath: "/usr/libexec/PlistBuddy",
|
||||||
|
["-c", "add :UIAppFonts: string \(fontName.filename).\(fontName.fileExtension)", infoPlist]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var plistData = "<key>UIAppFonts</key>\n\t<array>\n"
|
||||||
|
fontsToAddToPlist
|
||||||
|
.forEach { fontName in
|
||||||
|
plistData += "\t\t<string>\(fontName.filename).\(fontName.fileExtension)</string>\n"
|
||||||
}
|
}
|
||||||
plistData += "\t</array>"
|
plistData += "\t</array>"
|
||||||
|
|
||||||
return plistData
|
return plistData
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,76 +8,87 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
import ToolCore
|
import ToolCore
|
||||||
|
|
||||||
class FontExtensionGenerator {
|
enum FontExtensionGenerator {
|
||||||
|
|
||||||
static func writeExtensionFile(fontsNames: [String], staticVar: Bool, extensionName: String, extensionFilePath: String) {
|
private static func getFontNameEnum(fontsNames: [FontName]) -> String {
|
||||||
|
var enumDefinition = " enum FontName: String {\n"
|
||||||
|
|
||||||
|
fontsNames.forEach {
|
||||||
|
enumDefinition += " case \($0.fontNameSanitize) = \"\($0.postscriptName)\"\n"
|
||||||
|
}
|
||||||
|
enumDefinition += " }\n"
|
||||||
|
|
||||||
|
return enumDefinition
|
||||||
|
}
|
||||||
|
|
||||||
|
static func writeExtensionFile(
|
||||||
|
fontsNames: [FontName],
|
||||||
|
staticVar: Bool,
|
||||||
|
extensionName: String,
|
||||||
|
extensionFilePath: String,
|
||||||
|
isSwiftUI: Bool
|
||||||
|
) {
|
||||||
// Create extension content
|
// Create extension content
|
||||||
let extensionContent = Self.getExtensionContent(fontsNames: fontsNames,
|
let extensionContent = Self.getExtensionContent(
|
||||||
staticVar: staticVar,
|
fontsNames: fontsNames,
|
||||||
extensionName: extensionName)
|
staticVar: staticVar,
|
||||||
|
extensionName: extensionName,
|
||||||
|
isSwiftUI: isSwiftUI
|
||||||
|
)
|
||||||
|
|
||||||
// Write content
|
// Write content
|
||||||
let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath)
|
let extensionFilePathURL = URL(fileURLWithPath: extensionFilePath)
|
||||||
do {
|
do {
|
||||||
try extensionContent.write(to: extensionFilePathURL, atomically: false, encoding: .utf8)
|
try extensionContent.write(to: extensionFilePathURL, atomically: false, encoding: .utf8)
|
||||||
} catch (let error) {
|
} catch {
|
||||||
let error = FontsToolError.writeExtension(extensionFilePath, error.localizedDescription)
|
let error = FontsToolError.writeExtension(extensionFilePath, error.localizedDescription)
|
||||||
print(error.localizedDescription)
|
print(error.description)
|
||||||
Fonts.exit(withError: error)
|
Fonts.exit(withError: error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static func getExtensionContent(fontsNames: [String], staticVar: Bool, extensionName: String) -> String {
|
static func getExtensionContent(
|
||||||
|
fontsNames: [FontName],
|
||||||
|
staticVar: Bool,
|
||||||
|
extensionName: String,
|
||||||
|
isSwiftUI: Bool
|
||||||
|
) -> String {
|
||||||
[
|
[
|
||||||
Self.getHeader(extensionClassname: extensionName),
|
Self.getHeader(extensionClassname: extensionName, isSwiftUI: isSwiftUI),
|
||||||
Self.getFontNameEnum(fontsNames: fontsNames),
|
Self.getFontNameEnum(fontsNames: fontsNames),
|
||||||
Self.getFontMethods(fontsNames: fontsNames, staticVar: staticVar),
|
Self.getFontMethods(fontsNames: fontsNames, staticVar: staticVar, isSwiftUI: isSwiftUI),
|
||||||
Self.getFooter()
|
Self.getFooter()
|
||||||
]
|
]
|
||||||
.joined(separator: "\n")
|
.joined(separator: "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func getHeader(extensionClassname: String) -> String {
|
private static func getHeader(extensionClassname: String, isSwiftUI: Bool) -> String {
|
||||||
"""
|
"""
|
||||||
// Generated by ResgenSwift.\(Fonts.toolName) \(ResgenSwiftVersion)
|
// Generated by ResgenSwift.\(Fonts.toolName) \(ResgenSwiftVersion)
|
||||||
|
|
||||||
import UIKit
|
import \(isSwiftUI ? "SwiftUI" : "UIKit")
|
||||||
|
|
||||||
extension \(extensionClassname) {\n
|
extension \(extensionClassname) {\n
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func getFontNameEnum(fontsNames: [String]) -> String {
|
private static func getFontMethods(fontsNames: [FontName], staticVar: Bool, isSwiftUI: Bool) -> String {
|
||||||
var enumDefinition = " enum FontName: String {\n"
|
|
||||||
|
|
||||||
fontsNames.forEach {
|
|
||||||
enumDefinition += " case \($0.fontNameSanitize) = \"\($0)\"\n"
|
|
||||||
}
|
|
||||||
enumDefinition += " }\n"
|
|
||||||
|
|
||||||
return enumDefinition
|
|
||||||
}
|
|
||||||
|
|
||||||
private static func getFontMethods(fontsNames: [FontName], staticVar: Bool) -> String {
|
|
||||||
let pragma = " // MARK: - Getter"
|
let pragma = " // MARK: - Getter"
|
||||||
|
|
||||||
var propertiesOrMethods: [String] = fontsNames
|
var propertiesOrMethods: [String] = fontsNames
|
||||||
.unique()
|
.unique()
|
||||||
.map {
|
.map {
|
||||||
if staticVar {
|
$0.getProperty(isStatic: staticVar, isSwiftUI: isSwiftUI)
|
||||||
return $0.staticProperty
|
|
||||||
} else {
|
|
||||||
return $0.method
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
propertiesOrMethods.insert(pragma, at: 0)
|
propertiesOrMethods.insert(pragma, at: 0)
|
||||||
return propertiesOrMethods.joined(separator: "\n\n")
|
return propertiesOrMethods.joined(separator: "\n\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func getFooter() -> String {
|
private static func getFooter() -> String {
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,26 +7,48 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
typealias FontName = String
|
// swiftlint:disable no_grouping_extension
|
||||||
|
|
||||||
|
struct FontName: Hashable {
|
||||||
|
|
||||||
|
let postscriptName: String
|
||||||
|
let filename: String
|
||||||
|
let fileExtension: String
|
||||||
|
}
|
||||||
|
|
||||||
extension FontName {
|
extension FontName {
|
||||||
|
|
||||||
var fontNameSanitize: String {
|
var fontNameSanitize: String {
|
||||||
self.removeCharacters(from: "[]+-_")
|
postscriptName.removeCharacters(from: "[]+-_")
|
||||||
}
|
}
|
||||||
|
|
||||||
var method: String {
|
func getProperty(isStatic: Bool, isSwiftUI: Bool) -> String {
|
||||||
"""
|
if isSwiftUI {
|
||||||
|
if isStatic {
|
||||||
|
return """
|
||||||
|
static let \(fontNameSanitize): ((_ size: CGFloat) -> Font) = { size in
|
||||||
|
Font.custom(FontName.\(fontNameSanitize).rawValue, size: size)
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
return """
|
||||||
|
func \(fontNameSanitize)(withSize size: CGFloat) -> Font {
|
||||||
|
Font.custom(FontName.\(fontNameSanitize).rawValue, size: size)
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
// UIKit
|
||||||
|
if isStatic {
|
||||||
|
return """
|
||||||
|
static let \(fontNameSanitize): ((_ size: CGFloat) -> UIFont) = { size in
|
||||||
|
UIFont(name: FontName.\(fontNameSanitize).rawValue, size: size)!
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
return """
|
||||||
func \(fontNameSanitize)(withSize size: CGFloat) -> UIFont {
|
func \(fontNameSanitize)(withSize size: CGFloat) -> UIFont {
|
||||||
UIFont(name: FontName.\(fontNameSanitize).rawValue, size: size)!
|
UIFont(name: FontName.\(fontNameSanitize).rawValue, size: size)!
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
}
|
}
|
||||||
|
|
||||||
var staticProperty: String {
|
|
||||||
"""
|
|
||||||
static let \(fontNameSanitize): ((_ size: CGFloat) -> UIFont) = { size in
|
|
||||||
UIFont(name: FontName.\(fontNameSanitize).rawValue, size: size)!
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -7,10 +7,13 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
class FontFileParser {
|
enum FontFileParser {
|
||||||
|
|
||||||
static func parse(_ inputFile: String) -> [String] {
|
static func parse(_ inputFile: String) -> [String] {
|
||||||
let inputFileContent = try! String(contentsOfFile: inputFile,
|
let inputFileContent = try! String( // swiftlint:disable:this force_try
|
||||||
encoding: .utf8)
|
contentsOfFile: inputFile,
|
||||||
|
encoding: .utf8
|
||||||
|
)
|
||||||
return inputFileContent.components(separatedBy: CharacterSet.newlines)
|
return inputFileContent.components(separatedBy: CharacterSet.newlines)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|