├── .gitignore
├── LICENSE
├── Others
├── day.css
├── iPad.css
├── iPadPro.css
├── iPhone4.css
├── iPhone5.css
├── iPhone6.css
├── iPhone6P.css
└── night.css
├── README.md
├── SwiftCssParser.podspec
├── SwiftCssParser.xcodeproj
├── project.pbxproj
└── project.xcworkspace
│ └── contents.xcworkspacedata
├── SwiftCssParser
├── AppDelegate.swift
├── Assets.xcassets
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
├── CssLexer.swift
├── CssParser.swift
├── Info.plist
├── SwiftCSS.swift
├── SwiftCssTheme.swift
├── SwiftDeviceCss.swift
├── UIColorExtension.swift
└── ViewController.swift
├── SwiftCssParserTests
├── Info.plist
├── SwiftCssParserTests.swift
└── test.css
├── icon.jpeg
└── theme.gif
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.gitignore.io/api/swift
3 |
4 | ### Swift ###
5 | # Xcode
6 | #
7 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
8 |
9 | ## Build generated
10 | build/
11 | DerivedData/
12 |
13 | ## Various settings
14 | *.pbxuser
15 | !default.pbxuser
16 | *.mode1v3
17 | !default.mode1v3
18 | *.mode2v3
19 | !default.mode2v3
20 | *.perspectivev3
21 | !default.perspectivev3
22 | xcuserdata/
23 |
24 | ## Other
25 | *.moved-aside
26 | *.xccheckout
27 | *.xcscmblueprint
28 |
29 | ## Obj-C/Swift specific
30 | *.hmap
31 | *.ipa
32 | *.dSYM.zip
33 | *.dSYM
34 |
35 | ## Playgrounds
36 | timeline.xctimeline
37 | playground.xcworkspace
38 |
39 | # Swift Package Manager
40 | #
41 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
42 | # Packages/
43 | # Package.pins
44 | .build/
45 |
46 | # CocoaPods - Refactored to standalone file
47 |
48 | # Carthage - Refactored to standalone file
49 |
50 | # fastlane
51 | #
52 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
53 | # screenshots whenever they are needed.
54 | # For more information about the recommended setup visit:
55 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
56 |
57 | fastlane/report.xml
58 | fastlane/Preview.html
59 | fastlane/screenshots
60 | fastlane/test_output
61 |
62 | ### Swift.Carthage Stack ###
63 | # Carthage
64 | #
65 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
66 | # Carthage/Checkouts
67 |
68 | Carthage/Build
69 |
70 | ### Swift.CocoaPods Stack ###
71 | ## CocoaPods GitIgnore Template
72 |
73 | # CocoaPods - Only use to conserve bandwidth / Save time on Pushing
74 | # - Also handy if you have a lage number of dependant pods
75 | # - AS PER https://guides.cocoapods.org/using/using-cocoapods.html NEVER IGONRE THE LOCK FILE
76 | Pods/
77 |
78 | # End of https://www.gitignore.io/api/swift
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License
2 |
3 | Copyright (c) 2010-2017 100mango
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Others/day.css:
--------------------------------------------------------------------------------
1 | #View {
2 |
3 | "color" : RGB(255,255,255);
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/Others/iPad.css:
--------------------------------------------------------------------------------
1 | #View {
2 |
3 | "size" : 130 130;
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/Others/iPadPro.css:
--------------------------------------------------------------------------------
1 | #View {
2 |
3 | "size" : 150 150;
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/Others/iPhone4.css:
--------------------------------------------------------------------------------
1 | #View {
2 |
3 | "size" : 50 50;
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/Others/iPhone5.css:
--------------------------------------------------------------------------------
1 | #View {
2 |
3 | "size" : 70 70;
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/Others/iPhone6.css:
--------------------------------------------------------------------------------
1 | #View {
2 |
3 | "size" : 90 90;
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/Others/iPhone6P.css:
--------------------------------------------------------------------------------
1 | #View {
2 |
3 | "size" : 110 110;
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/Others/night.css:
--------------------------------------------------------------------------------
1 | #View {
2 |
3 | "color" : RGB(0,0,0);
4 |
5 | }
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 
5 |
6 |
7 |
8 | A Powerful , Extensible CSS Parser written in pure Swift.
9 |
10 |
11 |
12 | ## Basic Usage
13 |
14 | From CSS:
15 |
16 | ~~~css
17 | #View {
18 | "width" : 118;
19 | "height" : 120.5;
20 | "color1" : "#888888";
21 | "color2" : RGB(200,200,200);
22 | "color3" : RGB(200,200,200,0.5);
23 | "font1" : "Helvetica-Bold" 18;
24 | "font2" : "Cochin";
25 | "size" : 10 10;
26 | }
27 | ~~~
28 |
29 | To Cocoa:
30 |
31 | ~~~swift
32 | let width = css.int(selector: "#View", key: "width") // Int
33 | let height = css.double(selector: "#View", key: "height") //Double
34 | let color1 = css.color(selector: "#View", key: "color1") //UIColor
35 | let font1 = css.font(selector: "#View", key: "font1") //UIFont
36 | let font2 = css.font(selector: "#View", key: "font2", fontSize: 14) //UIFont
37 | let size = testSwiftCSS.size(selector: "#View", key: "size") //CGsize
38 | ~~~
39 |
40 |
41 |
42 | It's very easy to setup and parse CSS with `SwiftCssParser`:
43 |
44 | ~~~~swift
45 | //1.Get CSS file path
46 | let path = Bundle.main.url(forResource: "cssFileNmae", withExtension: "css")
47 | //2.Get parsed CSS
48 | let css = SwiftCSS(CssFileURL: path)
49 | //3.Use it
50 | let width = css.int(selector: "#View", key: "width")
51 | ~~~~
52 |
53 |
54 |
55 | ## Extension
56 |
57 | It's very easy to build your own Powerful, Flexiable CSS based solutions base on `SwiftCssParser`.
58 |
59 | #### Example1: `SwiftDeviceCss`
60 |
61 | In most cases, `Auto Layout` can help us calculates the size and location of our views. But in some cases, we need to set specifc size and location for our views based on device type (device's screen size) to accomplish the `Pixel Perfect` design.
62 |
63 | So, we can use `SwiftCssParser` to get layout value from CSS file. Different Device has different configuration file.
64 |
65 | ~~~swift
66 | public let SwiftDeviceCss = SwiftCssStyleSheet.deviceCss()
67 |
68 | class SwiftCssStyleSheet {
69 |
70 | private enum ScreenSize {
71 | case _320_480 //iPhone4 etc.
72 | case _320_568 //iPhone5 etc.
73 | //iPhone6....
74 | }
75 |
76 | static private let screenSize: ScreenSize = {
77 | let screen = UIScreen.main
78 | let size = UIScreen.main.fixedCoordinateSpace.bounds.size
79 | switch (size.width,size.height) {
80 | case (320,640):
81 | return ._320_480
82 | //......
83 | }
84 | }()
85 |
86 | static func deviceCss() -> SwiftCSS {
87 | switch self.screenSize {
88 | case ._320_480:
89 | return SwiftCSS(CssFileURL: URL.CssURL(name: "iPhone4"))
90 | case ._320_568:
91 | return SwiftCSS(CssFileURL: URL.CssURL(name: "iPhone5"))
92 | //......
93 | }
94 | }
95 |
96 | }
97 | ~~~
98 |
99 | Then just layout:
100 |
101 | ~~~swift
102 | view.frame.size = SwiftDeviceCss.size(selector: "#View", key: "size")
103 | ~~~
104 |
105 |
106 |
107 | #### Exeample2: `SwiftCssTheme`
108 |
109 | We can also create a powerful theme manager base on `SwiftCssParser`.
110 |
111 | For example, we want to create a night & day theme.
112 |
113 | ~~~swift
114 | public class SwiftCssTheme {
115 |
116 | public static let updateThemeNotification = Notification.Name("SwiftCSSThemeUpdate")
117 |
118 | public enum Theme {
119 | case day
120 | case night
121 | }
122 |
123 | public static var theme: Theme = .day {
124 | didSet {
125 | switch theme {
126 | case .day:
127 | self.themeCSS = SwiftCSS(CssFileURL: URL.CssURL(name: "day"))
128 | case .night:
129 | self.themeCSS = SwiftCSS(CssFileURL: URL.CssURL(name: "night"))
130 | }
131 | NotificationCenter.default.post(name: updateThemeNotification, object: nil)
132 | }
133 | }
134 |
135 | public static var themeCSS = SwiftCSS(CssFileURL: URL.CssURL(name: "day"))
136 | }
137 | ~~~
138 |
139 | If we want to be able to dynamically modify the background color of UIView:
140 |
141 | ~~~swift
142 | extension UIView {
143 |
144 | private struct AssociatedKeys {
145 | static var selector = "themeColorSelector"
146 | static var key = "themeColorKey"
147 | }
148 |
149 | var backgroundColorCSS: (selector: String,key: String) {
150 | get {
151 | let selector = //Use objc_getAssociatedObject to get value.....
152 | let key = //.....
153 | return (selector,key)
154 | }
155 |
156 | set {
157 | let selector = newValue.selector
158 | let key = newValue.key
159 |
160 | //Use objc_setAssociatedObject to set value......
161 |
162 | NotificationCenter.default.addObserver(self, selector: #selector(_cssUpdateBackgroundColor), name: SwiftCssTheme.updateThemeNotification, object: nil)
163 |
164 | _cssUpdateBackgroundColor()
165 | }
166 | }
167 |
168 | private dynamic func _cssUpdateBackgroundColor() {
169 | self.backgroundColor = SwiftCssTheme.themeCSS.color(selector: self.backgroundColorCSS.selector, key: self.backgroundColorCSS.key)
170 | }
171 | }
172 | ~~~
173 |
174 | Then, we just need to specify the background color's CSS selector and key:
175 |
176 | ~~~swift
177 | self.view.backgroundColorCSS = ("#View","color")
178 | ~~~
179 |
180 | Changing theme is even easier:
181 |
182 | ~~~swift
183 | @IBAction func changeColor(_ sender: UIButton) {
184 | if SwiftCssTheme.theme == .day {
185 | SwiftCssTheme.theme = .night
186 | } else {
187 | SwiftCssTheme.theme = .day
188 | }
189 | }
190 | ~~~
191 |
192 |
193 |
194 | 
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 | All the code and demo can be found in the project. Feel free to download and experiment. Advice and pull requests are welcome.
203 |
204 |
205 | ## Installation
206 |
207 | CocoaPods:
208 |
209 | ~~~
210 | pod 'SwiftCssParser'
211 | ~~~
212 |
213 |
214 | ## License
215 |
216 | `SwiftCssParser` is under the MIT license.
217 |
--------------------------------------------------------------------------------
/SwiftCssParser.podspec:
--------------------------------------------------------------------------------
1 | #
2 | # Be sure to run `pod lib lint SwiftCssParser.podspec' to ensure this is a
3 | # valid spec before submitting.
4 | #
5 | # Any lines starting with a # are optional, but their use is encouraged
6 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html
7 | #
8 |
9 | Pod::Spec.new do |s|
10 | s.name = 'SwiftCssParser'
11 | s.version = '0.1.0'
12 | s.summary = 'A Powerful, Extensible CSS Parser written in pure Swift.'
13 |
14 | # This description is used to generate tags and improve search results.
15 | # * Think: What does it do? Why did you write it? What is the focus?
16 | # * Try to keep it short, snappy and to the point.
17 | # * Write the description between the DESC delimiters below.
18 | # * Finally, don't worry about the indent, CocoaPods strips it!
19 |
20 | s.description = <<-DESC
21 | A Powerful, Extensible CSS Parser written in pure Swift.
22 | DESC
23 |
24 | s.homepage = 'https://github.com/100mango/SwiftCssParser'
25 | # s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2'
26 | s.license = { :type => 'MIT', :file => 'LICENSE' }
27 | s.author = { '100mango' => '' }
28 | s.source = { :git => 'https://github.com/100mango/SwiftCssParser.git', :tag => s.version.to_s }
29 | s.social_media_url = 'https://twitter.com/100_mango'
30 |
31 | s.source_files = 'SwiftCssParser/*.swift'
32 | s.exclude_files = [
33 | 'SwiftCssParser/AppDelegate.swift',
34 | 'SwiftCssParser/ViewController.swift'
35 | ]
36 |
37 | s.platform = :ios
38 |
39 | #s.frameworks = 'UIKit'
40 | s.ios.framework = 'UIKit'
41 | s.ios.deployment_target = '9.0'
42 |
43 | # Swift v4 fix
44 | s.pod_target_xcconfig = { 'SWIFT_VERSION' => '4.0' }
45 | end
46 |
--------------------------------------------------------------------------------
/SwiftCssParser.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | DB01C1421EE3D78F007D7308 /* SwiftDeviceCss.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB01C1411EE3D78F007D7308 /* SwiftDeviceCss.swift */; };
11 | DB01C1551EE419E3007D7308 /* iPad.css in Resources */ = {isa = PBXBuildFile; fileRef = DB01C14F1EE419E3007D7308 /* iPad.css */; };
12 | DB01C1561EE419E3007D7308 /* iPadPro.css in Resources */ = {isa = PBXBuildFile; fileRef = DB01C1501EE419E3007D7308 /* iPadPro.css */; };
13 | DB01C1571EE419E3007D7308 /* iPhone4.css in Resources */ = {isa = PBXBuildFile; fileRef = DB01C1511EE419E3007D7308 /* iPhone4.css */; };
14 | DB01C1581EE419E3007D7308 /* iPhone5.css in Resources */ = {isa = PBXBuildFile; fileRef = DB01C1521EE419E3007D7308 /* iPhone5.css */; };
15 | DB01C1591EE419E3007D7308 /* iPhone6.css in Resources */ = {isa = PBXBuildFile; fileRef = DB01C1531EE419E3007D7308 /* iPhone6.css */; };
16 | DB01C15A1EE419E3007D7308 /* iPhone6P.css in Resources */ = {isa = PBXBuildFile; fileRef = DB01C1541EE419E3007D7308 /* iPhone6P.css */; };
17 | DB01C15D1EE41A26007D7308 /* day.css in Resources */ = {isa = PBXBuildFile; fileRef = DB01C15B1EE41A26007D7308 /* day.css */; };
18 | DB01C15E1EE41A26007D7308 /* night.css in Resources */ = {isa = PBXBuildFile; fileRef = DB01C15C1EE41A26007D7308 /* night.css */; };
19 | DBC3DF8F1EDD4BF000E527FC /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC3DF8E1EDD4BF000E527FC /* AppDelegate.swift */; };
20 | DBC3DF911EDD4BF000E527FC /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC3DF901EDD4BF000E527FC /* ViewController.swift */; };
21 | DBC3DF941EDD4BF000E527FC /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DBC3DF921EDD4BF000E527FC /* Main.storyboard */; };
22 | DBC3DF961EDD4BF000E527FC /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = DBC3DF951EDD4BF000E527FC /* Assets.xcassets */; };
23 | DBC3DF991EDD4BF000E527FC /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = DBC3DF971EDD4BF000E527FC /* LaunchScreen.storyboard */; };
24 | DBC3DFA31EDD4C4900E527FC /* CssLexer.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC3DFA21EDD4C4900E527FC /* CssLexer.swift */; };
25 | DBC3DFA61EE2D53000E527FC /* CssParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC3DFA51EE2D53000E527FC /* CssParser.swift */; };
26 | DBC3DFA81EE2D8B100E527FC /* SwiftCSS.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC3DFA71EE2D8B100E527FC /* SwiftCSS.swift */; };
27 | DBC3DFAB1EE2D94800E527FC /* UIColorExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC3DFAA1EE2D94800E527FC /* UIColorExtension.swift */; };
28 | DBC3DFB31EE2D9D000E527FC /* SwiftCssParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC3DFB21EE2D9D000E527FC /* SwiftCssParserTests.swift */; };
29 | DBC3DFBB1EE2DA4500E527FC /* test.css in Resources */ = {isa = PBXBuildFile; fileRef = DBC3DFBA1EE2DA4500E527FC /* test.css */; };
30 | DBC3DFBE1EE2E1FC00E527FC /* SwiftCssTheme.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBC3DFBD1EE2E1FC00E527FC /* SwiftCssTheme.swift */; };
31 | /* End PBXBuildFile section */
32 |
33 | /* Begin PBXContainerItemProxy section */
34 | DBC3DFB51EE2D9D000E527FC /* PBXContainerItemProxy */ = {
35 | isa = PBXContainerItemProxy;
36 | containerPortal = DBC3DF831EDD4BF000E527FC /* Project object */;
37 | proxyType = 1;
38 | remoteGlobalIDString = DBC3DF8A1EDD4BF000E527FC;
39 | remoteInfo = SwiftCssParser;
40 | };
41 | /* End PBXContainerItemProxy section */
42 |
43 | /* Begin PBXFileReference section */
44 | DB01C1411EE3D78F007D7308 /* SwiftDeviceCss.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftDeviceCss.swift; sourceTree = ""; };
45 | DB01C14F1EE419E3007D7308 /* iPad.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; name = iPad.css; path = Others/iPad.css; sourceTree = ""; };
46 | DB01C1501EE419E3007D7308 /* iPadPro.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; name = iPadPro.css; path = Others/iPadPro.css; sourceTree = ""; };
47 | DB01C1511EE419E3007D7308 /* iPhone4.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; name = iPhone4.css; path = Others/iPhone4.css; sourceTree = ""; };
48 | DB01C1521EE419E3007D7308 /* iPhone5.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; name = iPhone5.css; path = Others/iPhone5.css; sourceTree = ""; };
49 | DB01C1531EE419E3007D7308 /* iPhone6.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; name = iPhone6.css; path = Others/iPhone6.css; sourceTree = ""; };
50 | DB01C1541EE419E3007D7308 /* iPhone6P.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; name = iPhone6P.css; path = Others/iPhone6P.css; sourceTree = ""; };
51 | DB01C15B1EE41A26007D7308 /* day.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; name = day.css; path = Others/day.css; sourceTree = ""; };
52 | DB01C15C1EE41A26007D7308 /* night.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; name = night.css; path = Others/night.css; sourceTree = ""; };
53 | DBC3DF8B1EDD4BF000E527FC /* SwiftCssParser.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwiftCssParser.app; sourceTree = BUILT_PRODUCTS_DIR; };
54 | DBC3DF8E1EDD4BF000E527FC /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AppDelegate.swift; path = SwiftCssParser/AppDelegate.swift; sourceTree = ""; };
55 | DBC3DF901EDD4BF000E527FC /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ViewController.swift; path = SwiftCssParser/ViewController.swift; sourceTree = ""; };
56 | DBC3DF931EDD4BF000E527FC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
57 | DBC3DF951EDD4BF000E527FC /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = SwiftCssParser/Assets.xcassets; sourceTree = ""; };
58 | DBC3DF981EDD4BF000E527FC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
59 | DBC3DF9A1EDD4BF000E527FC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = SwiftCssParser/Info.plist; sourceTree = ""; };
60 | DBC3DFA21EDD4C4900E527FC /* CssLexer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CssLexer.swift; sourceTree = ""; };
61 | DBC3DFA51EE2D53000E527FC /* CssParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CssParser.swift; sourceTree = ""; };
62 | DBC3DFA71EE2D8B100E527FC /* SwiftCSS.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftCSS.swift; sourceTree = ""; };
63 | DBC3DFAA1EE2D94800E527FC /* UIColorExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIColorExtension.swift; sourceTree = ""; };
64 | DBC3DFB01EE2D9D000E527FC /* SwiftCssParserTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SwiftCssParserTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
65 | DBC3DFB21EE2D9D000E527FC /* SwiftCssParserTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftCssParserTests.swift; sourceTree = ""; };
66 | DBC3DFB41EE2D9D000E527FC /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
67 | DBC3DFBA1EE2DA4500E527FC /* test.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; path = test.css; sourceTree = ""; };
68 | DBC3DFBD1EE2E1FC00E527FC /* SwiftCssTheme.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftCssTheme.swift; sourceTree = ""; };
69 | /* End PBXFileReference section */
70 |
71 | /* Begin PBXFrameworksBuildPhase section */
72 | DBC3DF881EDD4BF000E527FC /* Frameworks */ = {
73 | isa = PBXFrameworksBuildPhase;
74 | buildActionMask = 2147483647;
75 | files = (
76 | );
77 | runOnlyForDeploymentPostprocessing = 0;
78 | };
79 | DBC3DFAD1EE2D9D000E527FC /* Frameworks */ = {
80 | isa = PBXFrameworksBuildPhase;
81 | buildActionMask = 2147483647;
82 | files = (
83 | );
84 | runOnlyForDeploymentPostprocessing = 0;
85 | };
86 | /* End PBXFrameworksBuildPhase section */
87 |
88 | /* Begin PBXGroup section */
89 | DB01C1401EE3D744007D7308 /* DeviceCss */ = {
90 | isa = PBXGroup;
91 | children = (
92 | DB01C1411EE3D78F007D7308 /* SwiftDeviceCss.swift */,
93 | );
94 | name = DeviceCss;
95 | sourceTree = "";
96 | };
97 | DBC3DF821EDD4BF000E527FC = {
98 | isa = PBXGroup;
99 | children = (
100 | DBC3DFA01EDD4C0D00E527FC /* Others */,
101 | DBC3DF8D1EDD4BF000E527FC /* SwiftCssParser */,
102 | DBC3DFB11EE2D9D000E527FC /* SwiftCssParserTests */,
103 | DBC3DF8C1EDD4BF000E527FC /* Products */,
104 | );
105 | sourceTree = "";
106 | };
107 | DBC3DF8C1EDD4BF000E527FC /* Products */ = {
108 | isa = PBXGroup;
109 | children = (
110 | DBC3DF8B1EDD4BF000E527FC /* SwiftCssParser.app */,
111 | DBC3DFB01EE2D9D000E527FC /* SwiftCssParserTests.xctest */,
112 | );
113 | name = Products;
114 | sourceTree = "";
115 | };
116 | DBC3DF8D1EDD4BF000E527FC /* SwiftCssParser */ = {
117 | isa = PBXGroup;
118 | children = (
119 | DBC3DFA41EE2D50800E527FC /* Parser */,
120 | DBC3DFA11EDD4C3300E527FC /* Lexer */,
121 | DBC3DFA91EE2D92100E527FC /* Extension */,
122 | DBC3DFBC1EE2E1DB00E527FC /* CssTheme */,
123 | DB01C1401EE3D744007D7308 /* DeviceCss */,
124 | DBC3DFA71EE2D8B100E527FC /* SwiftCSS.swift */,
125 | );
126 | path = SwiftCssParser;
127 | sourceTree = "";
128 | };
129 | DBC3DFA01EDD4C0D00E527FC /* Others */ = {
130 | isa = PBXGroup;
131 | children = (
132 | DBC3DFC11EE2E2E200E527FC /* CssFile */,
133 | DBC3DF8E1EDD4BF000E527FC /* AppDelegate.swift */,
134 | DBC3DF901EDD4BF000E527FC /* ViewController.swift */,
135 | DBC3DF921EDD4BF000E527FC /* Main.storyboard */,
136 | DBC3DF951EDD4BF000E527FC /* Assets.xcassets */,
137 | DBC3DF971EDD4BF000E527FC /* LaunchScreen.storyboard */,
138 | DBC3DF9A1EDD4BF000E527FC /* Info.plist */,
139 | );
140 | name = Others;
141 | sourceTree = "";
142 | };
143 | DBC3DFA11EDD4C3300E527FC /* Lexer */ = {
144 | isa = PBXGroup;
145 | children = (
146 | DBC3DFA21EDD4C4900E527FC /* CssLexer.swift */,
147 | );
148 | name = Lexer;
149 | sourceTree = "";
150 | };
151 | DBC3DFA41EE2D50800E527FC /* Parser */ = {
152 | isa = PBXGroup;
153 | children = (
154 | DBC3DFA51EE2D53000E527FC /* CssParser.swift */,
155 | );
156 | name = Parser;
157 | sourceTree = "";
158 | };
159 | DBC3DFA91EE2D92100E527FC /* Extension */ = {
160 | isa = PBXGroup;
161 | children = (
162 | DBC3DFAA1EE2D94800E527FC /* UIColorExtension.swift */,
163 | );
164 | name = Extension;
165 | sourceTree = "";
166 | };
167 | DBC3DFB11EE2D9D000E527FC /* SwiftCssParserTests */ = {
168 | isa = PBXGroup;
169 | children = (
170 | DBC3DFB21EE2D9D000E527FC /* SwiftCssParserTests.swift */,
171 | DBC3DFB41EE2D9D000E527FC /* Info.plist */,
172 | DBC3DFBA1EE2DA4500E527FC /* test.css */,
173 | );
174 | path = SwiftCssParserTests;
175 | sourceTree = "";
176 | };
177 | DBC3DFBC1EE2E1DB00E527FC /* CssTheme */ = {
178 | isa = PBXGroup;
179 | children = (
180 | DBC3DFBD1EE2E1FC00E527FC /* SwiftCssTheme.swift */,
181 | );
182 | name = CssTheme;
183 | sourceTree = "";
184 | };
185 | DBC3DFC11EE2E2E200E527FC /* CssFile */ = {
186 | isa = PBXGroup;
187 | children = (
188 | DB01C15B1EE41A26007D7308 /* day.css */,
189 | DB01C15C1EE41A26007D7308 /* night.css */,
190 | DB01C14F1EE419E3007D7308 /* iPad.css */,
191 | DB01C1501EE419E3007D7308 /* iPadPro.css */,
192 | DB01C1511EE419E3007D7308 /* iPhone4.css */,
193 | DB01C1521EE419E3007D7308 /* iPhone5.css */,
194 | DB01C1531EE419E3007D7308 /* iPhone6.css */,
195 | DB01C1541EE419E3007D7308 /* iPhone6P.css */,
196 | );
197 | name = CssFile;
198 | sourceTree = "";
199 | };
200 | /* End PBXGroup section */
201 |
202 | /* Begin PBXNativeTarget section */
203 | DBC3DF8A1EDD4BF000E527FC /* SwiftCssParser */ = {
204 | isa = PBXNativeTarget;
205 | buildConfigurationList = DBC3DF9D1EDD4BF000E527FC /* Build configuration list for PBXNativeTarget "SwiftCssParser" */;
206 | buildPhases = (
207 | DBC3DF871EDD4BF000E527FC /* Sources */,
208 | DBC3DF881EDD4BF000E527FC /* Frameworks */,
209 | DBC3DF891EDD4BF000E527FC /* Resources */,
210 | );
211 | buildRules = (
212 | );
213 | dependencies = (
214 | );
215 | name = SwiftCssParser;
216 | productName = SwiftCssParser;
217 | productReference = DBC3DF8B1EDD4BF000E527FC /* SwiftCssParser.app */;
218 | productType = "com.apple.product-type.application";
219 | };
220 | DBC3DFAF1EE2D9D000E527FC /* SwiftCssParserTests */ = {
221 | isa = PBXNativeTarget;
222 | buildConfigurationList = DBC3DFB71EE2D9D000E527FC /* Build configuration list for PBXNativeTarget "SwiftCssParserTests" */;
223 | buildPhases = (
224 | DBC3DFAC1EE2D9D000E527FC /* Sources */,
225 | DBC3DFAD1EE2D9D000E527FC /* Frameworks */,
226 | DBC3DFAE1EE2D9D000E527FC /* Resources */,
227 | );
228 | buildRules = (
229 | );
230 | dependencies = (
231 | DBC3DFB61EE2D9D000E527FC /* PBXTargetDependency */,
232 | );
233 | name = SwiftCssParserTests;
234 | productName = SwiftCssParserTests;
235 | productReference = DBC3DFB01EE2D9D000E527FC /* SwiftCssParserTests.xctest */;
236 | productType = "com.apple.product-type.bundle.unit-test";
237 | };
238 | /* End PBXNativeTarget section */
239 |
240 | /* Begin PBXProject section */
241 | DBC3DF831EDD4BF000E527FC /* Project object */ = {
242 | isa = PBXProject;
243 | attributes = {
244 | LastSwiftUpdateCheck = 0830;
245 | LastUpgradeCheck = 1020;
246 | ORGANIZATIONNAME = Mango;
247 | TargetAttributes = {
248 | DBC3DF8A1EDD4BF000E527FC = {
249 | CreatedOnToolsVersion = 8.3.2;
250 | LastSwiftMigration = 1020;
251 | ProvisioningStyle = Automatic;
252 | };
253 | DBC3DFAF1EE2D9D000E527FC = {
254 | CreatedOnToolsVersion = 8.3.2;
255 | LastSwiftMigration = 1020;
256 | ProvisioningStyle = Automatic;
257 | TestTargetID = DBC3DF8A1EDD4BF000E527FC;
258 | };
259 | };
260 | };
261 | buildConfigurationList = DBC3DF861EDD4BF000E527FC /* Build configuration list for PBXProject "SwiftCssParser" */;
262 | compatibilityVersion = "Xcode 3.2";
263 | developmentRegion = en;
264 | hasScannedForEncodings = 0;
265 | knownRegions = (
266 | en,
267 | Base,
268 | );
269 | mainGroup = DBC3DF821EDD4BF000E527FC;
270 | productRefGroup = DBC3DF8C1EDD4BF000E527FC /* Products */;
271 | projectDirPath = "";
272 | projectRoot = "";
273 | targets = (
274 | DBC3DF8A1EDD4BF000E527FC /* SwiftCssParser */,
275 | DBC3DFAF1EE2D9D000E527FC /* SwiftCssParserTests */,
276 | );
277 | };
278 | /* End PBXProject section */
279 |
280 | /* Begin PBXResourcesBuildPhase section */
281 | DBC3DF891EDD4BF000E527FC /* Resources */ = {
282 | isa = PBXResourcesBuildPhase;
283 | buildActionMask = 2147483647;
284 | files = (
285 | DBC3DF991EDD4BF000E527FC /* LaunchScreen.storyboard in Resources */,
286 | DB01C1551EE419E3007D7308 /* iPad.css in Resources */,
287 | DB01C1561EE419E3007D7308 /* iPadPro.css in Resources */,
288 | DB01C1581EE419E3007D7308 /* iPhone5.css in Resources */,
289 | DBC3DF961EDD4BF000E527FC /* Assets.xcassets in Resources */,
290 | DB01C1591EE419E3007D7308 /* iPhone6.css in Resources */,
291 | DB01C15D1EE41A26007D7308 /* day.css in Resources */,
292 | DB01C15A1EE419E3007D7308 /* iPhone6P.css in Resources */,
293 | DBC3DF941EDD4BF000E527FC /* Main.storyboard in Resources */,
294 | DB01C15E1EE41A26007D7308 /* night.css in Resources */,
295 | DB01C1571EE419E3007D7308 /* iPhone4.css in Resources */,
296 | );
297 | runOnlyForDeploymentPostprocessing = 0;
298 | };
299 | DBC3DFAE1EE2D9D000E527FC /* Resources */ = {
300 | isa = PBXResourcesBuildPhase;
301 | buildActionMask = 2147483647;
302 | files = (
303 | DBC3DFBB1EE2DA4500E527FC /* test.css in Resources */,
304 | );
305 | runOnlyForDeploymentPostprocessing = 0;
306 | };
307 | /* End PBXResourcesBuildPhase section */
308 |
309 | /* Begin PBXSourcesBuildPhase section */
310 | DBC3DF871EDD4BF000E527FC /* Sources */ = {
311 | isa = PBXSourcesBuildPhase;
312 | buildActionMask = 2147483647;
313 | files = (
314 | DBC3DFA61EE2D53000E527FC /* CssParser.swift in Sources */,
315 | DBC3DF911EDD4BF000E527FC /* ViewController.swift in Sources */,
316 | DBC3DF8F1EDD4BF000E527FC /* AppDelegate.swift in Sources */,
317 | DBC3DFA81EE2D8B100E527FC /* SwiftCSS.swift in Sources */,
318 | DBC3DFA31EDD4C4900E527FC /* CssLexer.swift in Sources */,
319 | DB01C1421EE3D78F007D7308 /* SwiftDeviceCss.swift in Sources */,
320 | DBC3DFAB1EE2D94800E527FC /* UIColorExtension.swift in Sources */,
321 | DBC3DFBE1EE2E1FC00E527FC /* SwiftCssTheme.swift in Sources */,
322 | );
323 | runOnlyForDeploymentPostprocessing = 0;
324 | };
325 | DBC3DFAC1EE2D9D000E527FC /* Sources */ = {
326 | isa = PBXSourcesBuildPhase;
327 | buildActionMask = 2147483647;
328 | files = (
329 | DBC3DFB31EE2D9D000E527FC /* SwiftCssParserTests.swift in Sources */,
330 | );
331 | runOnlyForDeploymentPostprocessing = 0;
332 | };
333 | /* End PBXSourcesBuildPhase section */
334 |
335 | /* Begin PBXTargetDependency section */
336 | DBC3DFB61EE2D9D000E527FC /* PBXTargetDependency */ = {
337 | isa = PBXTargetDependency;
338 | target = DBC3DF8A1EDD4BF000E527FC /* SwiftCssParser */;
339 | targetProxy = DBC3DFB51EE2D9D000E527FC /* PBXContainerItemProxy */;
340 | };
341 | /* End PBXTargetDependency section */
342 |
343 | /* Begin PBXVariantGroup section */
344 | DBC3DF921EDD4BF000E527FC /* Main.storyboard */ = {
345 | isa = PBXVariantGroup;
346 | children = (
347 | DBC3DF931EDD4BF000E527FC /* Base */,
348 | );
349 | name = Main.storyboard;
350 | path = SwiftCssParser;
351 | sourceTree = "";
352 | };
353 | DBC3DF971EDD4BF000E527FC /* LaunchScreen.storyboard */ = {
354 | isa = PBXVariantGroup;
355 | children = (
356 | DBC3DF981EDD4BF000E527FC /* Base */,
357 | );
358 | name = LaunchScreen.storyboard;
359 | path = SwiftCssParser;
360 | sourceTree = "";
361 | };
362 | /* End PBXVariantGroup section */
363 |
364 | /* Begin XCBuildConfiguration section */
365 | DBC3DF9B1EDD4BF000E527FC /* Debug */ = {
366 | isa = XCBuildConfiguration;
367 | buildSettings = {
368 | ALWAYS_SEARCH_USER_PATHS = NO;
369 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
370 | CLANG_ANALYZER_NONNULL = YES;
371 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
372 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
373 | CLANG_CXX_LIBRARY = "libc++";
374 | CLANG_ENABLE_MODULES = YES;
375 | CLANG_ENABLE_OBJC_ARC = YES;
376 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
377 | CLANG_WARN_BOOL_CONVERSION = YES;
378 | CLANG_WARN_COMMA = YES;
379 | CLANG_WARN_CONSTANT_CONVERSION = YES;
380 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
381 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
382 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
383 | CLANG_WARN_EMPTY_BODY = YES;
384 | CLANG_WARN_ENUM_CONVERSION = YES;
385 | CLANG_WARN_INFINITE_RECURSION = YES;
386 | CLANG_WARN_INT_CONVERSION = YES;
387 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
388 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
389 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
390 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
391 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
392 | CLANG_WARN_STRICT_PROTOTYPES = YES;
393 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
394 | CLANG_WARN_UNREACHABLE_CODE = YES;
395 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
396 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
397 | COPY_PHASE_STRIP = NO;
398 | DEBUG_INFORMATION_FORMAT = dwarf;
399 | ENABLE_STRICT_OBJC_MSGSEND = YES;
400 | ENABLE_TESTABILITY = YES;
401 | GCC_C_LANGUAGE_STANDARD = gnu99;
402 | GCC_DYNAMIC_NO_PIC = NO;
403 | GCC_NO_COMMON_BLOCKS = YES;
404 | GCC_OPTIMIZATION_LEVEL = 0;
405 | GCC_PREPROCESSOR_DEFINITIONS = (
406 | "DEBUG=1",
407 | "$(inherited)",
408 | );
409 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
410 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
411 | GCC_WARN_UNDECLARED_SELECTOR = YES;
412 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
413 | GCC_WARN_UNUSED_FUNCTION = YES;
414 | GCC_WARN_UNUSED_VARIABLE = YES;
415 | IPHONEOS_DEPLOYMENT_TARGET = 10.3;
416 | MTL_ENABLE_DEBUG_INFO = YES;
417 | ONLY_ACTIVE_ARCH = YES;
418 | SDKROOT = iphoneos;
419 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
420 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
421 | SWIFT_SWIFT3_OBJC_INFERENCE = Default;
422 | TARGETED_DEVICE_FAMILY = "1,2";
423 | };
424 | name = Debug;
425 | };
426 | DBC3DF9C1EDD4BF000E527FC /* Release */ = {
427 | isa = XCBuildConfiguration;
428 | buildSettings = {
429 | ALWAYS_SEARCH_USER_PATHS = NO;
430 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
431 | CLANG_ANALYZER_NONNULL = YES;
432 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
433 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
434 | CLANG_CXX_LIBRARY = "libc++";
435 | CLANG_ENABLE_MODULES = YES;
436 | CLANG_ENABLE_OBJC_ARC = YES;
437 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
438 | CLANG_WARN_BOOL_CONVERSION = YES;
439 | CLANG_WARN_COMMA = YES;
440 | CLANG_WARN_CONSTANT_CONVERSION = YES;
441 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
442 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
443 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
444 | CLANG_WARN_EMPTY_BODY = YES;
445 | CLANG_WARN_ENUM_CONVERSION = YES;
446 | CLANG_WARN_INFINITE_RECURSION = YES;
447 | CLANG_WARN_INT_CONVERSION = YES;
448 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
449 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
450 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
451 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
452 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
453 | CLANG_WARN_STRICT_PROTOTYPES = YES;
454 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
455 | CLANG_WARN_UNREACHABLE_CODE = YES;
456 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
457 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
458 | COPY_PHASE_STRIP = NO;
459 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
460 | ENABLE_NS_ASSERTIONS = NO;
461 | ENABLE_STRICT_OBJC_MSGSEND = YES;
462 | GCC_C_LANGUAGE_STANDARD = gnu99;
463 | GCC_NO_COMMON_BLOCKS = YES;
464 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
465 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
466 | GCC_WARN_UNDECLARED_SELECTOR = YES;
467 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
468 | GCC_WARN_UNUSED_FUNCTION = YES;
469 | GCC_WARN_UNUSED_VARIABLE = YES;
470 | IPHONEOS_DEPLOYMENT_TARGET = 10.3;
471 | MTL_ENABLE_DEBUG_INFO = NO;
472 | SDKROOT = iphoneos;
473 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
474 | SWIFT_SWIFT3_OBJC_INFERENCE = Default;
475 | TARGETED_DEVICE_FAMILY = "1,2";
476 | VALIDATE_PRODUCT = YES;
477 | };
478 | name = Release;
479 | };
480 | DBC3DF9E1EDD4BF000E527FC /* Debug */ = {
481 | isa = XCBuildConfiguration;
482 | buildSettings = {
483 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
484 | INFOPLIST_FILE = SwiftCssParser/Info.plist;
485 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
486 | PRODUCT_BUNDLE_IDENTIFIER = Mango.SwiftCssParser;
487 | PRODUCT_NAME = "$(TARGET_NAME)";
488 | SWIFT_VERSION = 5.0;
489 | };
490 | name = Debug;
491 | };
492 | DBC3DF9F1EDD4BF000E527FC /* Release */ = {
493 | isa = XCBuildConfiguration;
494 | buildSettings = {
495 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
496 | INFOPLIST_FILE = SwiftCssParser/Info.plist;
497 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
498 | PRODUCT_BUNDLE_IDENTIFIER = Mango.SwiftCssParser;
499 | PRODUCT_NAME = "$(TARGET_NAME)";
500 | SWIFT_VERSION = 5.0;
501 | };
502 | name = Release;
503 | };
504 | DBC3DFB81EE2D9D000E527FC /* Debug */ = {
505 | isa = XCBuildConfiguration;
506 | buildSettings = {
507 | BUNDLE_LOADER = "$(TEST_HOST)";
508 | INFOPLIST_FILE = SwiftCssParserTests/Info.plist;
509 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
510 | PRODUCT_BUNDLE_IDENTIFIER = Mango.SwiftCssParserTests;
511 | PRODUCT_NAME = "$(TARGET_NAME)";
512 | SWIFT_VERSION = 5.0;
513 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SwiftCssParser.app/SwiftCssParser";
514 | };
515 | name = Debug;
516 | };
517 | DBC3DFB91EE2D9D000E527FC /* Release */ = {
518 | isa = XCBuildConfiguration;
519 | buildSettings = {
520 | BUNDLE_LOADER = "$(TEST_HOST)";
521 | INFOPLIST_FILE = SwiftCssParserTests/Info.plist;
522 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
523 | PRODUCT_BUNDLE_IDENTIFIER = Mango.SwiftCssParserTests;
524 | PRODUCT_NAME = "$(TARGET_NAME)";
525 | SWIFT_VERSION = 5.0;
526 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/SwiftCssParser.app/SwiftCssParser";
527 | };
528 | name = Release;
529 | };
530 | /* End XCBuildConfiguration section */
531 |
532 | /* Begin XCConfigurationList section */
533 | DBC3DF861EDD4BF000E527FC /* Build configuration list for PBXProject "SwiftCssParser" */ = {
534 | isa = XCConfigurationList;
535 | buildConfigurations = (
536 | DBC3DF9B1EDD4BF000E527FC /* Debug */,
537 | DBC3DF9C1EDD4BF000E527FC /* Release */,
538 | );
539 | defaultConfigurationIsVisible = 0;
540 | defaultConfigurationName = Release;
541 | };
542 | DBC3DF9D1EDD4BF000E527FC /* Build configuration list for PBXNativeTarget "SwiftCssParser" */ = {
543 | isa = XCConfigurationList;
544 | buildConfigurations = (
545 | DBC3DF9E1EDD4BF000E527FC /* Debug */,
546 | DBC3DF9F1EDD4BF000E527FC /* Release */,
547 | );
548 | defaultConfigurationIsVisible = 0;
549 | defaultConfigurationName = Release;
550 | };
551 | DBC3DFB71EE2D9D000E527FC /* Build configuration list for PBXNativeTarget "SwiftCssParserTests" */ = {
552 | isa = XCConfigurationList;
553 | buildConfigurations = (
554 | DBC3DFB81EE2D9D000E527FC /* Debug */,
555 | DBC3DFB91EE2D9D000E527FC /* Release */,
556 | );
557 | defaultConfigurationIsVisible = 0;
558 | defaultConfigurationName = Release;
559 | };
560 | /* End XCConfigurationList section */
561 | };
562 | rootObject = DBC3DF831EDD4BF000E527FC /* Project object */;
563 | }
564 |
--------------------------------------------------------------------------------
/SwiftCssParser.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/SwiftCssParser/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // SwiftCssParser
4 | //
5 | // Created by Mango on 2017/5/30.
6 | // Copyright © 2017年 Mango. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 | var window: UIWindow?
15 |
16 |
17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
18 | // Override point for customization after application launch.
19 | return true
20 | }
21 |
22 | func applicationWillResignActive(_ application: UIApplication) {
23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
25 | }
26 |
27 | func applicationDidEnterBackground(_ application: UIApplication) {
28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
30 | }
31 |
32 | func applicationWillEnterForeground(_ application: UIApplication) {
33 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
34 | }
35 |
36 | func applicationDidBecomeActive(_ application: UIApplication) {
37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
38 | }
39 |
40 | func applicationWillTerminate(_ application: UIApplication) {
41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
42 | }
43 |
44 |
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/SwiftCssParser/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "29x29",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "29x29",
11 | "scale" : "3x"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "size" : "40x40",
16 | "scale" : "2x"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "size" : "40x40",
21 | "scale" : "3x"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "size" : "60x60",
26 | "scale" : "2x"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "size" : "60x60",
31 | "scale" : "3x"
32 | },
33 | {
34 | "idiom" : "ipad",
35 | "size" : "29x29",
36 | "scale" : "1x"
37 | },
38 | {
39 | "idiom" : "ipad",
40 | "size" : "29x29",
41 | "scale" : "2x"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "size" : "40x40",
46 | "scale" : "1x"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "size" : "40x40",
51 | "scale" : "2x"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "size" : "76x76",
56 | "scale" : "1x"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "size" : "76x76",
61 | "scale" : "2x"
62 | }
63 | ],
64 | "info" : {
65 | "version" : 1,
66 | "author" : "xcode"
67 | }
68 | }
--------------------------------------------------------------------------------
/SwiftCssParser/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/SwiftCssParser/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
31 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/SwiftCssParser/CssLexer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CssLexer.swift
3 | // SwiftCssParser
4 | //
5 | // Created by Mango on 2017/5/30.
6 | // Copyright © 2017年 Mango. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public class Lexer {
12 | let input: String
13 | var currentIndex: String.Index
14 | var currentCharcter : Character?
15 |
16 | public init(input: String) {
17 | self.input = input
18 | currentIndex = input.startIndex
19 | currentCharcter = input.first
20 | }
21 |
22 | public func consume() {
23 |
24 | guard currentIndex != input.endIndex else {
25 | return
26 | }
27 |
28 | currentIndex = input.index(after: currentIndex)
29 | if currentIndex == self.input.endIndex {
30 | currentCharcter = nil
31 | }else{
32 | currentCharcter = self.input[currentIndex]
33 | }
34 | }
35 | }
36 |
37 |
38 | public class CssLexer: Lexer {
39 |
40 | public enum Token: CustomStringConvertible {
41 | case string(String)
42 | case selector(String)
43 | case double(Double)
44 | case rgb(Double,Double,Double,Double)
45 | case font(String,Double)
46 | case leftBrace //{
47 | case rightBrace //}
48 | case colon //:
49 | case semicolon //;
50 |
51 | public var description: String {
52 | switch self {
53 | case let .string(string):
54 | return string
55 | case let .selector(selector):
56 | return selector
57 | case let .double(double):
58 | return String(double)
59 | case let .rgb(r,g,b,a):
60 | return "RGB(\(r),\(g),\(b),\(a))"
61 | case let .font(name,size):
62 | return "Font:\(name) size:\(size)"
63 | case .leftBrace:
64 | return "{"
65 | case .rightBrace:
66 | return "}"
67 | case .colon:
68 | return ":"
69 | case .semicolon:
70 | return ";"
71 | }
72 | }
73 |
74 | var type: String {
75 | switch self {
76 | case .string(_):
77 | return "StringType"
78 | case .selector(_):
79 | return "SelectorType"
80 | case .double(_):
81 | return "DoubleType"
82 | case .rgb(_, _, _, _):
83 | return "RGBType"
84 | case .font(_, _):
85 | return "FontType"
86 | default:
87 | return description
88 | }
89 | }
90 | }
91 |
92 | enum inputWrong: Error, CustomStringConvertible {
93 | case InvaidCharacter(Character)
94 |
95 | var description: String {
96 | switch self {
97 | case let .InvaidCharacter(character):
98 | return "Invalid character " + String(character)
99 | }
100 | }
101 | }
102 | }
103 |
104 | func ~=(pattern: (Character) -> Bool , value: Character) -> Bool {
105 | return pattern(value)
106 | }
107 |
108 | //MARK: Generate Tokens
109 | extension CssLexer {
110 |
111 | func nextToken() -> Token? {
112 |
113 |
114 | var consumedChars = [Character]()
115 | while let currentCharcter = self.currentCharcter {
116 |
117 | switch currentCharcter {
118 | case isSpace:
119 | self.consume()
120 | case isSelector:
121 | return combineSelector()
122 | case isLetter:
123 | return combineLetter()
124 | case isNumber:
125 | return combineNumber()
126 | case isRGB:
127 | return combineRGB()
128 | case ":":
129 | consume()
130 | return .colon
131 | case "{":
132 | consume()
133 | return .leftBrace
134 | case "}":
135 | consume()
136 | return .rightBrace
137 | case ";":
138 | consume()
139 | return .semicolon
140 | default:
141 | fatalError("\(inputWrong.InvaidCharacter(currentCharcter)) Consumued:\(consumedChars)")
142 | }
143 | consumedChars.append(currentCharcter)
144 | }
145 |
146 | return nil
147 | }
148 |
149 |
150 |
151 |
152 | func isSpace(char: Character) -> Bool {
153 | let string = String(char)
154 | let result = string.rangeOfCharacter(from: .whitespacesAndNewlines)
155 | return result != nil
156 | }
157 |
158 | func isLetter(char: Character) -> Bool {
159 | if char == "\"" {
160 | return true
161 | } else {
162 | return false
163 | }
164 | }
165 |
166 | func isAToZ(char: Character) -> Bool {
167 | if char >= "a" && char <= "z" || char >= "A" && char <= "Z" {
168 | return true
169 | }else{
170 | return false
171 | }
172 | }
173 |
174 | func combineLetter() -> Token {
175 | self.consume() //吃掉"
176 | var string = ""
177 | while let next = self.currentCharcter , next != "\"" {
178 | string += String(next)
179 | self.consume()
180 | }
181 | self.consume() //吃掉"
182 | return .string(string)
183 | }
184 |
185 | func isSelector(char: Character) -> Bool {
186 | return char == "#"
187 | }
188 |
189 | func combineSelector() -> Token {
190 | var string = String(self.currentCharcter!)
191 | self.consume()
192 | while let next = self.currentCharcter , isAToZ(char: next) {
193 | string += String(next)
194 | self.consume()
195 | }
196 | return .selector(string)
197 | }
198 |
199 | func isNumber(char: Character) -> Bool {
200 | if (char >= "0" && char <= "9") || char == "." || char == "-" {
201 | return true
202 | } else {
203 | return false
204 | }
205 | }
206 |
207 | func combineNumber() -> Token {
208 | var string = String(self.currentCharcter!)
209 | self.consume()
210 | while let next = self.currentCharcter , isNumber(char: next) {
211 | string += String(next)
212 | self.consume()
213 | }
214 |
215 | if let double = Double(string) {
216 | return .double(double)
217 | } else {
218 | fatalError("Generate Number wrong with \(string)")
219 | }
220 |
221 | }
222 |
223 | func isRGB(char: Character) -> Bool {
224 | if char == "R" {
225 | return true
226 | } else {
227 | return false
228 | }
229 | }
230 |
231 | func combineRGB() -> Token {
232 | var string = ""
233 | while let next = self.currentCharcter {
234 | string += String(next)
235 | self.consume()
236 |
237 | if next == ")" {
238 | break
239 | }
240 | }
241 |
242 | string = string.removeCharacters(from: "RGB()")
243 | let values = string.components(separatedBy: ",")
244 | if values.count == 3 {
245 | return Token.rgb(Double(values[0])!, Double(values[1])!, Double(values[2])!, 1)
246 | } else if values.count == 4 {
247 | return Token.rgb(Double(values[0])!, Double(values[1])!, Double(values[2])!, Double(values[3])!)
248 | } else {
249 | fatalError("Invalid RGB value with \(string)")
250 | }
251 |
252 | }
253 |
254 | }
255 |
256 |
257 | extension String {
258 |
259 | public func removeCharacters(from forbiddenChars: CharacterSet) -> String {
260 | let passed = self.unicodeScalars.filter { !forbiddenChars.contains($0) }
261 | return String(String.UnicodeScalarView(passed))
262 | }
263 |
264 | public func removeCharacters(from: String) -> String {
265 | return removeCharacters(from: CharacterSet(charactersIn: from))
266 | }
267 | }
268 |
--------------------------------------------------------------------------------
/SwiftCssParser/CssParser.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CssParser.swift
3 | // SwiftCssParser
4 | //
5 | // Created by Mango on 2017/6/3.
6 | // Copyright © 2017年 Mango. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public class CssParser {
12 | let lexer: CssLexer
13 | lazy var lookaheads: [Token?] = Array(repeating: nil, count: self.k)
14 | let k = 6 //LL(6)
15 | var index = 0 //circular index of next token position to fill
16 | typealias Token = CssLexer.Token
17 |
18 | public var outputDic = [String:[String:Any]]()
19 |
20 | public init(lexer: CssLexer) {
21 | self.lexer = lexer
22 |
23 | for _ in 1...k {
24 | consume()
25 | }
26 | }
27 |
28 | private var consumedToken = [Token]()
29 | func consume() {
30 |
31 | lookaheads[index] = lexer.nextToken()
32 | index = (index + 1) % k
33 |
34 | //for debug
35 | if let token = lookaheads[index] {
36 | consumedToken.append(token)
37 | }
38 | }
39 |
40 | // form 1 to k
41 | func lookaheadToken(_ index: Int) -> Token? {
42 | let circularIndex = (self.index + index - 1) % k
43 | return lookaheads[circularIndex]
44 | }
45 |
46 | @discardableResult func match(token: Token) -> Token {
47 |
48 | guard let lookaheadToken = lookaheadToken(1) else {
49 | fatalError("lookahead token is nil")
50 | }
51 | guard lookaheadToken.type == token.type else {
52 | fatalError("expecting (\(token.type)) but found (\(lookaheadToken) consumedTokens: \(consumedToken))")
53 | }
54 | consume()
55 | return lookaheadToken
56 | }
57 |
58 | }
59 |
60 | //MARK: Rules
61 | extension CssParser {
62 |
63 | func element(selector: String ) {
64 |
65 | guard var selectorDic = outputDic[selector] else {
66 | fatalError("\(selector) dic not found")
67 | }
68 |
69 | let key = match(token: .string(""))
70 | match(token: .colon)
71 |
72 | guard let currentToken = lookaheadToken(1) else {
73 | fatalError("lookahead token is nil")
74 | }
75 |
76 | switch currentToken {
77 | case let .double(value):
78 |
79 | guard let token2 = lookaheadToken(2) else {
80 | fatalError("token2 is nil")
81 | }
82 | switch token2 {
83 | case let .double(double):
84 | // key : double double;
85 | match(token: currentToken)
86 | match(token: token2)
87 | selectorDic[key.description] = ["double1":value,"double2":double]
88 | default:
89 | // normal double
90 | match(token: currentToken)
91 | selectorDic[key.description] = value
92 | }
93 |
94 | case let .string(value):
95 | //LL(2)
96 | guard let token2 = lookaheadToken(2) else {
97 | fatalError("token2 is nil")
98 | }
99 |
100 | switch token2 {
101 | case let .double(double):
102 | //key : name double
103 | match(token: currentToken)
104 | match(token: token2)
105 | selectorDic[key.description] = ["name":value,"size":double]
106 | default:
107 | //normal string
108 | match(token: currentToken)
109 | selectorDic[key.description] = value
110 | }
111 | case let .rgb(r,g,b,a):
112 | match(token: currentToken)
113 | selectorDic[key.description] = (r,g,b,a)
114 | default:
115 | break
116 | }
117 |
118 | outputDic[selector] = selectorDic
119 | }
120 |
121 | func elements(selector: String) {
122 | element(selector: selector)
123 | while let lookaheadToken = lookaheadToken(1), lookaheadToken.type ==
124 | Token.semicolon.type {
125 | match(token: .semicolon)
126 |
127 | //if current token is "}", it means elements rule is parsed.
128 | if let currentToken = self.lookaheadToken(1), currentToken.type == Token.rightBrace.type {
129 | return
130 | }
131 | element(selector: selector)
132 | }
133 | }
134 |
135 | func selector() {
136 | let selector = match(token: .selector(""))
137 | let dic = [String:Int]()
138 | outputDic[selector.description] = dic
139 |
140 | match(token: .leftBrace)
141 | elements(selector: selector.description)
142 | match(token: .rightBrace)
143 | }
144 |
145 | func css() {
146 |
147 | while lookaheadToken(1) != nil {
148 | selector()
149 | }
150 | }
151 |
152 | public func parse() {
153 | css()
154 | }
155 |
156 | }
157 |
--------------------------------------------------------------------------------
/SwiftCssParser/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UILaunchStoryboardName
24 | LaunchScreen
25 | UIMainStoryboardFile
26 | Main
27 | UIRequiredDeviceCapabilities
28 |
29 | armv7
30 |
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/SwiftCssParser/SwiftCSS.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftCSS.swift
3 | // SwiftCssParser
4 | //
5 | // Created by Mango on 2017/6/3.
6 | // Copyright © 2017年 Mango. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | public class SwiftCSS {
12 |
13 | private let parsedCss: [String:[String:Any]]
14 |
15 | public init(CssFileURL: URL) {
16 | let content = try! String(contentsOf: CssFileURL, encoding: .utf8)
17 | let lexer = CssLexer(input: content)
18 | let parser = CssParser(lexer: lexer)
19 | parser.parse()
20 | parsedCss = parser.outputDic
21 | }
22 |
23 |
24 | public func int(selector: String, key: String) -> Int {
25 |
26 | return Int(double(selector: selector, key: key))
27 | }
28 |
29 | public func double(selector: String, key: String) -> Double {
30 | return value(selector: selector, key: key) ?? 0
31 | }
32 |
33 | public func string(selector: String, key: String) -> String {
34 | return value(selector: selector, key: key) ?? ""
35 | }
36 |
37 | public func size(selector: String, key: String) -> CGSize {
38 |
39 | guard let dic: [String:Double] = value(selector: selector, key: key),
40 | let double1 = dic["double1"], let double2 = dic["double2"] else {
41 | return CGSize(width: 0, height: 0)
42 | }
43 |
44 | return CGSize(width: double1, height: double2)
45 |
46 | }
47 |
48 |
49 | public func color(selector: String, key: String) -> UIColor {
50 |
51 | if let rgb:(Double,Double,Double,Double) = value(selector: selector, key: key) {
52 | return UIColor(red: CGFloat(rgb.0/255), green: CGFloat(rgb.1/255), blue: CGFloat(rgb.2/255), alpha: CGFloat(rgb.3))
53 | } else {
54 | return UIColor(string(selector: selector, key: key))
55 | }
56 |
57 | }
58 |
59 | public func font(selector: String, key: String, fontSize: CGFloat = 14) -> UIFont {
60 |
61 | if let name: String = value(selector: selector, key: key) {
62 |
63 | return UIFont(name: name, size: fontSize) ?? UIFont.systemFont(ofSize: fontSize)
64 |
65 | } else if let dic: [String:Any] = value(selector: selector, key: key) {
66 |
67 | guard let name = dic["name"] as? String ,let size = dic["size"] as? Double else {
68 | return UIFont.systemFont(ofSize: fontSize)
69 | }
70 | return UIFont(name: name, size: CGFloat(size)) ?? UIFont.systemFont(ofSize: fontSize)
71 |
72 | } else {
73 | return UIFont.systemFont(ofSize: fontSize)
74 | }
75 | }
76 |
77 | private func value(selector: String, key: String) -> T? {
78 | guard let dic = parsedCss[selector] else {
79 | return nil
80 | }
81 | guard let value = dic[key] as? T else {
82 | return nil
83 | }
84 |
85 | return value
86 | }
87 |
88 | }
89 |
--------------------------------------------------------------------------------
/SwiftCssParser/SwiftCssTheme.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftCssTheme.swift
3 | // SwiftCssParser
4 | //
5 | // Created by Mango on 2017/6/3.
6 | // Copyright © 2017年 Mango. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | public class SwiftCssTheme {
12 |
13 | public static let updateThemeNotification = Notification.Name("SwiftCSSThemeUpdate")
14 |
15 | public enum Theme {
16 | case day
17 | case night
18 | }
19 |
20 | public static var theme: Theme = .day {
21 | didSet {
22 | switch theme {
23 | case .day:
24 | self.themeCSS = SwiftCSS(CssFileURL: URL.CssURL(name: "day"))
25 | case .night:
26 | self.themeCSS = SwiftCSS(CssFileURL: URL.CssURL(name: "night"))
27 | }
28 | NotificationCenter.default.post(name: updateThemeNotification, object: nil)
29 | }
30 | }
31 |
32 | public static var themeCSS = SwiftCSS(CssFileURL: URL.CssURL(name: "day"))
33 |
34 |
35 | }
36 |
37 | private extension URL {
38 | static func CssURL(name:String) -> URL {
39 | return Bundle.main.url(forResource: name, withExtension: "css")!
40 | }
41 | }
42 |
43 |
44 | extension UIView {
45 |
46 | private struct AssociatedKeys {
47 | static var selector = "themeColorSelector"
48 | static var key = "themeColorKey"
49 | }
50 |
51 | var backgroundColorCSS: (selector: String,key: String) {
52 |
53 | get {
54 | guard let selector = objc_getAssociatedObject(self, &AssociatedKeys.selector) as? String else {
55 | return ("","")
56 | }
57 | guard let key = objc_getAssociatedObject(self, &AssociatedKeys.key) as? String else {
58 | return ("","")
59 | }
60 | return (selector,key)
61 | }
62 |
63 | set {
64 |
65 | let selector = newValue.selector
66 | let key = newValue.key
67 |
68 | objc_setAssociatedObject(self, &AssociatedKeys.selector, selector, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
69 | objc_setAssociatedObject(self, &AssociatedKeys.key, key, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
70 |
71 | NotificationCenter.default.addObserver(self, selector: #selector(_cssUpdateBackgroundColor), name: SwiftCssTheme.updateThemeNotification, object: nil)
72 |
73 | //set css and update backgroundColor
74 | _cssUpdateBackgroundColor()
75 | }
76 | }
77 |
78 | @objc private dynamic func _cssUpdateBackgroundColor() {
79 |
80 | self.backgroundColor = SwiftCssTheme.themeCSS.color(selector: self.backgroundColorCSS.selector, key: self.backgroundColorCSS.key)
81 |
82 | }
83 |
84 | }
85 |
--------------------------------------------------------------------------------
/SwiftCssParser/SwiftDeviceCss.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftDeviceCss.swift
3 | // SwiftCssParser
4 | //
5 | // Created by Mango on 2017/6/4.
6 | // Copyright © 2017年 Mango. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 |
12 | public let SwiftDeviceCss = SwiftCssStyleSheet.deviceCss()
13 |
14 |
15 | class SwiftCssStyleSheet {
16 |
17 | private enum ScreenSize {
18 |
19 |
20 | case _320_480 //iPhone4 etc.
21 | case _320_568 //iPhone5 etc.
22 | case _375_667 //iPhone6 etc.
23 | case _414_736 //iPhone6 plus etc.
24 | case _768_1024 //iPad etc.
25 | case _1024_1366 //iPad Pro
26 |
27 | }
28 |
29 | static private let screenSize: ScreenSize = {
30 |
31 | let screen = UIScreen.main
32 | let size = UIScreen.main.fixedCoordinateSpace.bounds.size
33 |
34 | switch (size.width,size.height) {
35 |
36 | case (320,640):
37 | return ._320_480
38 | case (320,568):
39 | return ._320_568
40 | case (375,667):
41 | return ._375_667
42 | case (414,736):
43 | return ._414_736
44 | case (768,1024):
45 | return ._768_1024
46 | case (1024,1366):
47 | return ._1024_1366
48 |
49 | default:
50 | print("Warning: Can NOT detect screenModel! bounds: \(screen.bounds) nativeScale: \(screen.nativeScale)")
51 | return ._375_667 // Default
52 | }
53 | }()
54 |
55 | static func deviceCss() -> SwiftCSS {
56 |
57 | switch self.screenSize {
58 | case ._320_480:
59 | return SwiftCSS(CssFileURL: URL.CssURL(name: "iPhone4"))
60 | case ._320_568:
61 | return SwiftCSS(CssFileURL: URL.CssURL(name: "iPhone5"))
62 | case ._375_667:
63 | return SwiftCSS(CssFileURL: URL.CssURL(name: "iPhone6"))
64 | case ._414_736:
65 | return SwiftCSS(CssFileURL: URL.CssURL(name: "iPhone6P"))
66 | case ._768_1024:
67 | return SwiftCSS(CssFileURL: URL.CssURL(name: "iPad"))
68 | case ._1024_1366:
69 | return SwiftCSS(CssFileURL: URL.CssURL(name: "iPadPro"))
70 | }
71 | }
72 |
73 | }
74 |
75 | private extension URL {
76 | static func CssURL(name:String) -> URL {
77 | return Bundle.main.url(forResource: name, withExtension: "css")!
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/SwiftCssParser/UIColorExtension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIColorExtension.swift
3 | // HEXColor
4 | //
5 | // Created by R0CKSTAR on 6/13/14.
6 | // Copyright (c) 2014 P.D.Q. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | /**
12 | MissingHashMarkAsPrefix: "Invalid RGB string, missing '#' as prefix"
13 | UnableToScanHexValue: "Scan hex error"
14 | MismatchedHexStringLength: "Invalid RGB string, number of characters after '#' should be either 3, 4, 6 or 8"
15 | */
16 | public enum UIColorInputError : Error {
17 | case missingHashMarkAsPrefix,
18 | unableToScanHexValue,
19 | mismatchedHexStringLength,
20 | unableToOutputHexStringForWideDisplayColor
21 | }
22 |
23 | extension UIColor {
24 | /**
25 | The shorthand three-digit hexadecimal representation of color.
26 | #RGB defines to the color #RRGGBB.
27 |
28 | - parameter hex3: Three-digit hexadecimal value.
29 | - parameter alpha: 0.0 - 1.0. The default is 1.0.
30 | */
31 | public convenience init(hex3: UInt16, alpha: CGFloat = 1) {
32 | let divisor = CGFloat(15)
33 | let red = CGFloat((hex3 & 0xF00) >> 8) / divisor
34 | let green = CGFloat((hex3 & 0x0F0) >> 4) / divisor
35 | let blue = CGFloat( hex3 & 0x00F ) / divisor
36 | self.init(red: red, green: green, blue: blue, alpha: alpha)
37 | }
38 |
39 | /**
40 | The shorthand four-digit hexadecimal representation of color with alpha.
41 | #RGBA defines to the color #RRGGBBAA.
42 |
43 | - parameter hex4: Four-digit hexadecimal value.
44 | */
45 | public convenience init(hex4: UInt16) {
46 | let divisor = CGFloat(15)
47 | let red = CGFloat((hex4 & 0xF000) >> 12) / divisor
48 | let green = CGFloat((hex4 & 0x0F00) >> 8) / divisor
49 | let blue = CGFloat((hex4 & 0x00F0) >> 4) / divisor
50 | let alpha = CGFloat( hex4 & 0x000F ) / divisor
51 | self.init(red: red, green: green, blue: blue, alpha: alpha)
52 | }
53 |
54 | /**
55 | The six-digit hexadecimal representation of color of the form #RRGGBB.
56 |
57 | - parameter hex6: Six-digit hexadecimal value.
58 | */
59 | public convenience init(hex6: UInt32, alpha: CGFloat = 1) {
60 | let divisor = CGFloat(255)
61 | let red = CGFloat((hex6 & 0xFF0000) >> 16) / divisor
62 | let green = CGFloat((hex6 & 0x00FF00) >> 8) / divisor
63 | let blue = CGFloat( hex6 & 0x0000FF ) / divisor
64 | self.init(red: red, green: green, blue: blue, alpha: alpha)
65 | }
66 |
67 | /**
68 | The six-digit hexadecimal representation of color with alpha of the form #RRGGBBAA.
69 |
70 | - parameter hex8: Eight-digit hexadecimal value.
71 | */
72 | public convenience init(hex8: UInt32) {
73 | let divisor = CGFloat(255)
74 | let red = CGFloat((hex8 & 0xFF000000) >> 24) / divisor
75 | let green = CGFloat((hex8 & 0x00FF0000) >> 16) / divisor
76 | let blue = CGFloat((hex8 & 0x0000FF00) >> 8) / divisor
77 | let alpha = CGFloat( hex8 & 0x000000FF ) / divisor
78 | self.init(red: red, green: green, blue: blue, alpha: alpha)
79 | }
80 |
81 | /**
82 | The rgba string representation of color with alpha of the form #RRGGBBAA/#RRGGBB, throws error.
83 |
84 | - parameter rgba: String value.
85 | */
86 | public convenience init(rgba_throws rgba: String) throws {
87 | guard rgba.hasPrefix("#") else {
88 | throw UIColorInputError.missingHashMarkAsPrefix
89 | }
90 |
91 |
92 | let hexString: String = String(rgba.dropFirst());
93 | var hexValue: UInt32 = 0
94 |
95 | guard Scanner(string: hexString).scanHexInt32(&hexValue) else {
96 | throw UIColorInputError.unableToScanHexValue
97 | }
98 |
99 | switch (hexString.count) {
100 | case 3:
101 | self.init(hex3: UInt16(hexValue))
102 | case 4:
103 | self.init(hex4: UInt16(hexValue))
104 | case 6:
105 | self.init(hex6: hexValue)
106 | case 8:
107 | self.init(hex8: hexValue)
108 | default:
109 | throw UIColorInputError.mismatchedHexStringLength
110 | }
111 | }
112 |
113 | /**
114 | The rgba string representation of color with alpha of the form #RRGGBBAA/#RRGGBB, fails to default color.
115 |
116 | - parameter rgba: String value.
117 | */
118 | public convenience init(_ rgba: String, defaultColor: UIColor = UIColor.clear) {
119 | guard let color = try? UIColor(rgba_throws: rgba) else {
120 | self.init(cgColor: defaultColor.cgColor)
121 | return
122 | }
123 | self.init(cgColor: color.cgColor)
124 | }
125 |
126 | /**
127 | Hex string of a UIColor instance, throws error.
128 |
129 | - parameter includeAlpha: Whether the alpha should be included.
130 | */
131 | public func hexStringThrows(_ includeAlpha: Bool = true) throws -> String {
132 | var r: CGFloat = 0
133 | var g: CGFloat = 0
134 | var b: CGFloat = 0
135 | var a: CGFloat = 0
136 | self.getRed(&r, green: &g, blue: &b, alpha: &a)
137 |
138 | guard r >= 0 && r <= 1 && g >= 0 && g <= 1 && b >= 0 && b <= 1 else {
139 | throw UIColorInputError.unableToOutputHexStringForWideDisplayColor
140 | }
141 |
142 | if (includeAlpha) {
143 | return String(format: "#%02X%02X%02X%02X", Int(r * 255), Int(g * 255), Int(b * 255), Int(a * 255))
144 | } else {
145 | return String(format: "#%02X%02X%02X", Int(r * 255), Int(g * 255), Int(b * 255))
146 | }
147 | }
148 |
149 | /**
150 | Hex string of a UIColor instance, fails to empty string.
151 |
152 | - parameter includeAlpha: Whether the alpha should be included.
153 | */
154 | public func hexString(_ includeAlpha: Bool = true) -> String {
155 | guard let hexString = try? hexStringThrows(includeAlpha) else {
156 | return ""
157 | }
158 | return hexString
159 | }
160 | }
161 |
162 | extension String {
163 | /**
164 | Convert argb string to rgba string.
165 | */
166 | public func argb2rgba() -> String? {
167 | guard self.hasPrefix("#") else {
168 | return nil
169 | }
170 |
171 | let hexString: String = String(self[self.index(self.startIndex, offsetBy: 1)...])
172 | switch (hexString.count) {
173 | case 4:
174 | return "#"
175 | + String(hexString[self.index(self.startIndex, offsetBy: 1)...])
176 | + String(hexString[..
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | BNDL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 |
22 |
23 |
--------------------------------------------------------------------------------
/SwiftCssParserTests/SwiftCssParserTests.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SwiftCssParserTests.swift
3 | // SwiftCssParserTests
4 | //
5 | // Created by Mango on 2017/6/3.
6 | // Copyright © 2017年 Mango. All rights reserved.
7 | //
8 |
9 | import XCTest
10 | @testable import SwiftCssParser
11 |
12 | class SwiftCssParserTests: XCTestCase {
13 |
14 | lazy var testSwiftCSS: SwiftCSS = {
15 | let testBundle = Bundle(for: SwiftCssParserTests.self)
16 | let cssPath = testBundle.url(forResource: "test", withExtension: "css")
17 | return SwiftCSS(CssFileURL: cssPath!)
18 | }()
19 |
20 |
21 | func testParseInt() {
22 | let width = testSwiftCSS.int(selector: "#View", key: "width")
23 | XCTAssertTrue(width == 118, "get width")
24 | }
25 |
26 | func testParseDouble() {
27 | let height = testSwiftCSS.double(selector: "#View", key: "height")
28 | XCTAssertTrue(height == 120.5, "get height")
29 | }
30 |
31 | func testParseColor() {
32 | let color1 = testSwiftCSS.color(selector: "#View", key: "color1")
33 | XCTAssertTrue(color1 == UIColor("#888888"), "get color")
34 |
35 | let color2 = testSwiftCSS.color(selector: "#View", key: "color2")
36 | XCTAssertTrue(color2 == UIColor(red: 200/255, green: 200/255, blue: 200/255, alpha: 1), "get color")
37 |
38 | let color3 = testSwiftCSS.color(selector: "#View", key: "color3")
39 | XCTAssertTrue(color3 == UIColor(red: 200/255, green: 200/255, blue: 200/255, alpha: 0.5), "get color")
40 | }
41 |
42 | func testParseFont() {
43 |
44 | let font1 = testSwiftCSS.font(selector: "#View", key: "font1")
45 | let font1test = UIFont(name: "Helvetica-Bold", size: 18)
46 | XCTAssertTrue(font1 == font1test , "get font")
47 |
48 | let font2 = testSwiftCSS.font(selector: "#View", key: "font2", fontSize: 14)
49 | let font2test = UIFont(name: "Cochin", size: 14)
50 | XCTAssertTrue(font2 == font2test, "get font")
51 |
52 | }
53 |
54 | func testParseSize() {
55 | let size = testSwiftCSS.size(selector: "#View", key: "size")
56 | XCTAssertTrue(size == CGSize(width: 10, height: 10) )
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/SwiftCssParserTests/test.css:
--------------------------------------------------------------------------------
1 | #View {
2 |
3 | "width" : 118;
4 | "height" : 120.5;
5 | "color1" : "#888888";
6 | "color2" : RGB(200,200,200);
7 | "color3" : RGB(200,200,200,0.5);
8 | "font1" : "Helvetica-Bold" 18;
9 | "font2" : "Cochin";
10 | "size" : 10 10;
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/icon.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/100mango/SwiftCssParser/4684f76564d611036516e9c0517ca3d66400492f/icon.jpeg
--------------------------------------------------------------------------------
/theme.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/100mango/SwiftCssParser/4684f76564d611036516e9c0517ca3d66400492f/theme.gif
--------------------------------------------------------------------------------