├── .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 | ![](http://img.shields.io/badge/Swift-5.0-orange.svg) 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 | ![](theme.gif) 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 --------------------------------------------------------------------------------