├── .gitignore ├── .swiftpm └── xcode │ └── package.xcworkspace │ └── contents.xcworkspacedata ├── LICENSE ├── Package.swift ├── Podfile ├── Podfile.lock ├── README.md ├── Source ├── Codable │ └── Codable.swift ├── Enum │ └── Enum.swift ├── Print │ └── Print.swift ├── Stuff.swift └── TODO │ └── TODO.swift ├── Stuff.podspec ├── Stuff.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── Stuff.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ └── IDEWorkspaceChecks.plist └── StuffTests ├── CodableStuffTests.swift ├── EnumStuffTests.swift ├── Info.plist ├── PrintStuffTests.swift └── TODOStuffTests.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | 20 | # CocoaPods 21 | # 22 | # We recommend against adding the Pods directory to your .gitignore. However 23 | # you should judge for yourself, the pros and cons are mentioned at: 24 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 25 | # 26 | Pods/ 27 | 28 | # Carthage 29 | # 30 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 31 | # Carthage/Checkouts 32 | Carthage/Build 33 | -------------------------------------------------------------------------------- /.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT 3 License 2 | 3 | Copyright (c) 2015, EVICT B.V. 4 | All rights reserved. 5 | http://evict.nl, mailto://edwin@evict.nl 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | * Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | * Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | * Neither the name of EVICT B.V. nor the 15 | names of its contributors may be used to endorse or promote products 16 | derived from this software without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 22 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.5 2 | 3 | // 4 | // Package.swift 5 | // 6 | // 7 | // Created by Sander van Tulden on 12/10/2023. 8 | // 9 | 10 | import PackageDescription 11 | 12 | let packageName = "Stuff" 13 | 14 | let package = Package( 15 | name: packageName, 16 | platforms: [ 17 | .iOS(.v8), 18 | .watchOS(.v2), 19 | .macOS(.v10_10), 20 | .tvOS(.v9) 21 | ], 22 | products: [ 23 | .library( 24 | name: packageName, 25 | targets: [packageName] 26 | ) 27 | ], 28 | targets: [ 29 | .target( 30 | name: packageName, 31 | path: "Source" 32 | ), 33 | .testTarget( 34 | name: packageName + "Tests", 35 | dependencies: [.target(name: packageName)], 36 | path: "StuffTests" 37 | ) 38 | ] 39 | ) 40 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | source 'https://github.com/CocoaPods/Specs.git' 2 | use_frameworks! 3 | workspace 'Stuff' 4 | 5 | target 'StuffTests' do 6 | platform :ios, '8.0' 7 | pod 'Stuff', :path => "./" 8 | end 9 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Stuff (0.9.0): 3 | - Stuff/All (= 0.9.0) 4 | - Stuff/All (0.9.0): 5 | - Stuff/Codable 6 | - Stuff/Enum 7 | - Stuff/Print 8 | - Stuff/TODO 9 | - Stuff/Codable (0.9.0) 10 | - Stuff/Enum (0.9.0) 11 | - Stuff/Print (0.9.0) 12 | - Stuff/TODO (0.9.0) 13 | 14 | DEPENDENCIES: 15 | - Stuff (from `./`) 16 | 17 | EXTERNAL SOURCES: 18 | Stuff: 19 | :path: "./" 20 | 21 | SPEC CHECKSUMS: 22 | Stuff: c61c749c1f6464ad1e9ef8fcc561792c265fb10e 23 | 24 | PODFILE CHECKSUM: bf19cca12781c8d2d79fc7cc174325633ebbe909 25 | 26 | COCOAPODS: 1.6.0.rc.2 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Stuff 2 | 3 | [![Issues](https://img.shields.io/github/issues-raw/evermeer/Stuff.svg?style=flat)](https://github.com/evermeer/Stuff/issues) 4 | [![Documentation](https://img.shields.io/badge/documented-100%25-green.svg?style=flat)](http://cocoadocs.org/docsets/Stuff/) 5 | [![Stars](https://img.shields.io/github/stars/evermeer/Stuff.svg?style=flat)](https://github.com/evermeer/Stuff/stargazers) 6 | [![Downloads](https://img.shields.io/cocoapods/dt/Stuff.svg?style=flat)](https://cocoapods.org/pods/Stuff) 7 | 8 | 9 | [![Version](https://img.shields.io/cocoapods/v/Stuff.svg?style=flat)](http://cocoadocs.org/docsets/Stuff) 10 | [![Language](https://img.shields.io/badge/language-swift%203-f48041.svg?style=flat)](https://developer.apple.com/swift) 11 | [![Platform](https://img.shields.io/cocoapods/p/Stuff.svg?style=flat)](http://cocoadocs.org/docsets/Stuff) 12 | [![License](https://img.shields.io/cocoapods/l/Stuff.svg?style=flat)](http://cocoadocs.org/docsets/Stuff) 13 | 14 | [![Git](https://img.shields.io/badge/GitHub-evermeer-blue.svg?style=flat)](https://github.com/evermeer) 15 | [![Twitter](https://img.shields.io/badge/twitter-@evermeer-blue.svg?style=flat)](http://twitter.com/evermeer) 16 | [![LinkedIn](https://img.shields.io/badge/linkedin-Edwin%20Vermeer-blue.svg?style=flat)](http://nl.linkedin.com/in/evermeer/en) 17 | [![Website](https://img.shields.io/badge/website-evict.nl-blue.svg?style=flat)](http://evict.nl) 18 | [![eMail](https://img.shields.io/badge/email-edwin@evict.nl-blue.svg?style=flat)](mailto:edwin@evict.nl?SUBJECT=About%20Stuff) 19 | 20 | # General information 21 | 22 | Stuff is a collection of code 'snippets' that are too small to create a library for and which do not fit in an other library. Run the unit tests to see the code in action. 23 | 24 | - [Print](#print) - For creating a nice output log 25 | - [Enum](#enum) - Adding functionality to an enum 26 | - [TODO](#todo) - Adding a TODO helper function 27 | - [Codable](#codable) - Adding Codable helper functions (Swift 4) 28 | 29 | To install all parts via Swift Package Manager, add version `1.1.4` or higher with URL `https://github.com/evermeer/Stuff` to your project file. 30 | 31 | ## Print 32 | 33 | You can install this by adding the following line to your Podfile: 34 | 35 | ``` 36 | pod "Stuff/Print" 37 | ``` 38 | 39 | The Stuff.print function is meant to be a replacement for the standard .print function. It will give you extra information about when and where the print function was executed. Besides that there is also support for different log levels and you can specify the minimum log level that should be in the output. 40 | 41 | Here are some samples: 42 | 43 | ```swift 44 | Stuff.print("Just as the standard print but now with detailed information") 45 | Stuff.print("Now it's a warning", .warn) 46 | Stuff.print("Or even an error", .error) 47 | 48 | Stuff.minimumLogLevel = .error 49 | Stuff.print("Now you won't see normal log output") 50 | Stuff.print("Only errors are shown", .error) 51 | 52 | Stuff.minimumLogLevel = .none 53 | Stuff.print("Or if it's disabled you won't see any log", .error) 54 | ``` 55 | 56 | The output of the code above is: 57 | 58 | ```console 59 | ✳️ .debug ⏱ 02/13/2017 09:52:51:852 📱 xctest [18960:?] 📂 PrintStuffTests.swift(15) ⚙️ testExample() ➡️ 60 | Just as the standard print but now with detailed information 61 | 62 | ⚠️ .warn ⏱ 02/13/2017 09:52:51:855 📱 xctest [18960:?] 📂 PrintStuffTests.swift(16) ⚙️ testExample() ➡️ 63 | Now it's a warning 64 | 65 | 🚫 .error ⏱ 02/13/2017 09:52:51:855 📱 xctest [18960:?] 📂 PrintStuffTests.swift(17) ⚙️ testExample() ➡️ 66 | Or even an error 67 | 68 | 🚫 .error ⏱ 02/13/2017 09:52:51:855 📱 xctest [18960:?] 📂 PrintStuffTests.swift(21) ⚙️ testExample() ➡️ 69 | Only errors are shown 70 | ``` 71 | 72 | There is also a shortcut print statement for an error object. This will print the localizedDescription and set the warning level to .error. 73 | ```swift 74 | Stuff.print(error) 75 | ``` 76 | 77 | Update: From now on printing .error will also give you a stacktrace. 78 | 79 | Update 04-04-2019 : You can now also set the minimumLogLevel to productionLogAll. This will make sure you can also see the logging in the console (menu 'Window', 'Devices and Simulators', 'Open Console') In most cases you should only use this if you want to see logging for an app that you distributed using Testflight. 80 | 81 | ## Enum 82 | 83 | You can install this by adding the following line to your Podfile: 84 | 85 | ``` 86 | pod "Stuff/Enum" 87 | ``` 88 | 89 | - getting all possible enum values in an arry 90 | - getting the associated value of any enum 91 | - converting an array of enums with associated values to a dictionary or an query string. 92 | - get the raw value of an enum even it it was passed in an any 93 | 94 | 95 | ```swift 96 | // You have to extend the enum with the Enum protocol (and RawEnum if you want the raw value when it's an Any) 97 | enum test1: String, Enum, RawEnum { 98 | case option1 99 | case option2 100 | case option3 101 | } 102 | 103 | enum test2: Enum { 104 | case option4(String) 105 | case option5(Int) 106 | case option6(Int, String) 107 | } 108 | 109 | XCTAssert(test1.allValues.count == 3, "There should be 3 values") 110 | for value in test1.allValues { 111 | print("value = \(value)") 112 | } 113 | 114 | let v1: test2 = .option4("test") 115 | print("v1 = \(v1.associated.label), \(v1.associated.value!)") 116 | let v2: test2 = .option5(3) 117 | print("v2 = \(v2.associated.label), \(v2.associated.value!)") 118 | let v3: test2 = .option6(4, "more") 119 | print("v3 = \(v3.associated.label), \(v3.associated.value!)") 120 | 121 | let array = [v1, v2, v3] 122 | let dict = [String:Any](array) 123 | print("Array of enums as dictionary:\n \(dict)") 124 | 125 | print("query = \(array.queryString))") 126 | 127 | let v = getRawValue(test1.option2) 128 | print("raw value = \(v)") 129 | 130 | func getRawValue(_ value: Any) -> Any { 131 | return (value as? RawEnum)?.anyRawValue ?? "" 132 | } 133 | 134 | ``` 135 | The output is: 136 | 137 | ```swift 138 | value = option1 139 | value = option2 140 | value = option3 141 | v1 = option4, test 142 | v2 = option5, 3 143 | v3 = option6, (4, "more") 144 | Array of enums as dictionary: 145 | ["option5": 3, "option4": "test", "option6": (4, "more")] 146 | query = option4=test,option5=3,option6=(4, "more")) 147 | raw value = option2 148 | ``` 149 | 150 | ## TODO 151 | 152 | You can install this by adding the following line to your Podfile: 153 | 154 | ``` 155 | pod "Stuff/TODO" 156 | ``` 157 | 158 | Whenever you add a function to your project and postpone implementing it, you should add a TODO. 159 | Whenever you temporarilly change functioning code in order to debug something, you should add a TODO 160 | Before commiting/pushing your code, you should evaluate/solve your TODO's 161 | Usually you just add a TODO comment in your code like this: 162 | 163 | ```swift 164 | //TODO: This needs to be fixed 165 | ``` 166 | 167 | With the Stuff/TODO helper, you can get compile time and run time support for helping to find your TODO's. Here is an overview of the variants that you can use: 168 | 169 | ```swift 170 | // We need to fix something, but this code can run (compiler warning) 171 | TODO() 172 | 173 | // Now output extra info when this code is executed. 174 | TODO("An other todo, now giving some detailed info") 175 | 176 | // We need to fix this. Otherwise just fail. The code will crash here. See the stacktrace, 177 | TODO_ 178 | ``` 179 | 180 | The code above will put the following in your output. Besides that, you will also see depricated warnings when you have the code open in Xcode. 181 | 182 | ``` 183 | ⚠️ TODO code is still in use! ⚠️ 184 | 185 | ⚠️ TODO code is still in use! ⚠️ 186 | An other todo, now giving some detailed info 187 | fatal error: TODO left in code: file /Users/evermeer/Desktop/dev/GitHub/Stuff/Source/TODO/TODO.swift, line 15 188 | ``` 189 | 190 | But... It might be nicer to just add the following build phase script which will also give you compile time warnings: 191 | 192 | ``` 193 | KEYWORDS="TODO|FIXME|\?\?\?:|\!\!\!:" 194 | find "${SRCROOT}" \( -name "*.swift" \) -print0 | \ 195 | xargs -0 egrep --with-filename --line-number --only-matching "($KEYWORDS).*\$" | \ 196 | perl -p -e "s/($KEYWORDS)/ warning: \$1/" 197 | ``` 198 | 199 | ## Codable 200 | 201 | You can install this by adding the following line to your Podfile: 202 | 203 | ``` 204 | pod "Stuff/Codable" 205 | ``` 206 | 207 | Swift 4 added the Codable (EnCodable and DeCodable) protocol which you can add to a class or struct. The swift compile will then add coding and decoding functionality to your object. With the JSONEncoder and JSONDecoder classes you can then convert an object from and to json. The Stuff Codable extension will let you do these things in a 1 liner. Here is Apple documentaion about [Codable](https://developer.apple.com/documentation/foundation/archives_and_serialization/encoding_and_decoding_custom_types) The one liners that you can use are: 208 | 209 | ```swift 210 | let json = yourEncodableObjectInstance.toJsonString() 211 | let data = yourEncodableObjectInstance.toJsonData() 212 | let newObject = try? YourCodableObject(json: json) 213 | let newObject2 = try? YourCodableObject(data: data) 214 | let objectArray = try? [YourCodableObject](json: json) 215 | let objectArray2 = try? [YourCodableObject](data: data) 216 | let newJson = objectArray.toJsonString() 217 | let innerObject = try? TestCodable(json: "{\"user\":{\"id\":1,\"naam\":\"Edwin\"}}", keyPath: "user") 218 | try initialObject.saveToDocuments("myFile.dat") 219 | let readObject = try? TestCodable(fileNameInDocuments: "myFile.dat") 220 | try objectArray.saveToDocuments("myFile2.dat") 221 | let objectArray3 = try? [TestCodable](fileNameInDocuments: "myFile2.dat") 222 | ``` 223 | 224 | And here you can see how you can use these Stuff/Codable functions: 225 | 226 | ```swift 227 | func test() { 228 | let initialObject = TestCodable(naam: "Edwin", id: 1, testField: "tst") 229 | 230 | guard let json = initialObject.toJsonString() else { 231 | print("Could not create json from object") 232 | return 233 | } 234 | print("Json string of object = \n\t\(json)") 235 | 236 | guard let newObject = try? TestCodable(json: json) else { 237 | print("Could not create object from json") 238 | return 239 | } 240 | print("Object created with json = \n\t\(newObject)") 241 | 242 | let json2 = "[{\"id\":1,\"naam\":\"Edwin\"},{\"id\":2,\"naam\":\"Vermeer\"}]" 243 | guard let array = try? [TestCodable](jsonArray: json2) else { 244 | print("Could not create object array from json") 245 | return 246 | } 247 | print("Object array created with json = \(array)") 248 | 249 | let newJson = array.toJsonString() 250 | print("Json from object array = \n\t\(newJson)") 251 | 252 | guard let innerObject = try? TestCodable(json: "{\"user\":{\"id\":1,\"naam\":\"Edwin\"}}", keyPath: "user") else { 253 | print("Could not create object from json") 254 | return 255 | } 256 | print("inner object from json \(String(describing: innerObject))") 257 | 258 | guard let custom = try TestCodable(json: "{\"Naam\":\"UpperN\", \"Id\":5, \"Test_field\":\"lowerSnake\"}", keyPath: nil, codingStrategy: customCodingStragegy) else { 259 | print("Could not custom case convert") 260 | return 261 | } 262 | print("read object with custom key coding from json to \(String(describing: 263 | } 264 | 265 | struct TestCodable : Codable { 266 | var naam: String? 267 | var id: Int? 268 | var testField: String? 269 | } 270 | ``` 271 | 272 | besides the keyPath a shown in the sample code above you could also add one or more of the folowing parameters which are part of the standard JSONDecoder. If these parameters are not supplied, then the default value (see what's after the =) will be used. 273 | 274 | ``` 275 | keyDecodingStrategy: JSONDecoder.KeyDecodingStrategy = .convertFromSnakeCase, 276 | dateDecodingStrategy: JSONDecoder.DateDecodingStrategy = .deferredToDate, 277 | dataDecodingStrategy: JSONDecoder.DataDecodingStrategy = .base64, 278 | nonConformingFloatDecodingStrategy: JSONDecoder.NonConformingFloatDecodingStrategy = .throw 279 | ``` 280 | 281 | ## License 282 | 283 | Stuff is available under the MIT 3 license. See the LICENSE file for more info. 284 | 285 | ## My other libraries: 286 | Also see my other public source iOS libraries: 287 | 288 | - [EVReflection](https://github.com/evermeer/EVReflection) - Reflection based (Dictionary, CKRecord, JSON and XML) object mapping with extensions for Alamofire and Moya with RxSwift or ReactiveSwift 289 | - [EVCloudKitDao](https://github.com/evermeer/EVCloudKitDao) - Simplified access to Apple's CloudKit 290 | - [EVFaceTracker](https://github.com/evermeer/EVFaceTracker) - Calculate the distance and angle of your device with regards to your face in order to simulate a 3D effect 291 | - [EVURLCache](https://github.com/evermeer/EVURLCache) - a NSURLCache subclass for handling all web requests that use NSURLReques 292 | - [AlamofireOauth2](https://github.com/evermeer/AlamofireOauth2) - A swift implementation of OAuth2 using Alamofire 293 | - [EVWordPressAPI](https://github.com/evermeer/EVWordPressAPI) - Swift Implementation of the WordPress (Jetpack) API using AlamofireOauth2, EVReflection and Alamofire (work in progress) 294 | - [PassportScanner](https://github.com/evermeer/PassportScanner) - Scan the MRZ code of a passport and extract the firstname, lastname, passport number, nationality, date of birth, expiration date and personal numer. 295 | - [AttributedTextView](https://github.com/evermeer/AttributedTextView) - Easiest way to create an attributed UITextView with support for multiple links (url, hashtags, mentions). 296 | - [UITestHelper](https://github.com/evermeer/UITestHelper) - UI test helper functions. 297 | 298 | -------------------------------------------------------------------------------- /Source/Codable/Codable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Codable.swift 3 | // Stuff 4 | // 5 | // Created by Edwin Vermeer on 28/06/2017. 6 | // Copyright © 2017 EVICT BV. All rights reserved. 7 | // 8 | import Foundation 9 | 10 | enum CodingError : Error { 11 | case RuntimeError(String) 12 | } 13 | 14 | struct AnyKey: CodingKey { 15 | var stringValue: String 16 | var intValue: Int? 17 | 18 | init(stringValue: String) { 19 | self.stringValue = stringValue 20 | self.intValue = nil 21 | } 22 | 23 | init(intValue: Int) { 24 | self.stringValue = String(intValue) 25 | self.intValue = intValue 26 | } 27 | } 28 | 29 | public var customCodingStragegy: JSONDecoder.KeyDecodingStrategy = .custom { keys in 30 | let lastComponent = keys.last!.stringValue 31 | let snakeCased = lastComponent.split(separator: "_").map { $0.prefix(1).uppercased() + $0.dropFirst() }.reduce("") { $0 + $1} 32 | let lowerFirst = snakeCased.prefix(1).lowercased() + snakeCased.dropFirst() 33 | return AnyKey(stringValue: lowerFirst) 34 | } 35 | 36 | public extension Encodable { 37 | /** 38 | Convert this object to json data 39 | 40 | - parameter outputFormatting: The formatting of the output JSON data (compact or pritty printed) 41 | - parameter dateEncodinStrategy: how do you want to format the date 42 | - parameter dataEncodingStrategy: what kind of encoding. base64 is the default 43 | 44 | - returns: The json data 45 | */ 46 | func toJsonData(outputFormatting: JSONEncoder.OutputFormatting = [], dateEncodingStrategy: JSONEncoder.DateEncodingStrategy = .deferredToDate, dataEncodingStrategy: JSONEncoder.DataEncodingStrategy = .base64) -> Data? { 47 | let encoder = JSONEncoder() 48 | encoder.outputFormatting = outputFormatting 49 | encoder.dateEncodingStrategy = dateEncodingStrategy 50 | encoder.dataEncodingStrategy = dataEncodingStrategy 51 | return try? encoder.encode(self) 52 | } 53 | 54 | /** 55 | Convert this object to a json string 56 | 57 | - parameter outputFormatting: The formatting of the output JSON data (compact or pritty printed) 58 | - parameter dateEncodinStrategy: how do you want to format the date 59 | - parameter dataEncodingStrategy: what kind of encoding. base64 is the default 60 | 61 | - returns: The json string 62 | */ 63 | func toJsonString(outputFormatting: JSONEncoder.OutputFormatting = [], dateEncodingStrategy: JSONEncoder.DateEncodingStrategy = .deferredToDate, dataEncodingStrategy: JSONEncoder.DataEncodingStrategy = .base64) -> String? { 64 | let data = self.toJsonData(outputFormatting: outputFormatting, dateEncodingStrategy: dateEncodingStrategy, dataEncodingStrategy: dataEncodingStrategy) 65 | return data == nil ? nil : String(data: data!, encoding: .utf8) 66 | } 67 | 68 | 69 | /** 70 | Save this object to a file in the temp directory 71 | 72 | - parameter fileName: The filename 73 | 74 | - returns: Nothing 75 | */ 76 | func saveTo(_ fileURL: URL) throws { 77 | guard let data = self.toJsonData() else { throw CodingError.RuntimeError("cannot create data from object")} 78 | try data.write(to: fileURL, options: .atomic) 79 | } 80 | 81 | 82 | /** 83 | Save this object to a file in the temp directory 84 | 85 | - parameter fileName: The filename 86 | 87 | - returns: Nothing 88 | */ 89 | func saveToTemp(_ fileName: String) throws { 90 | let fileURL = try FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: false).appendingPathComponent(fileName) 91 | try self.saveTo(fileURL) 92 | } 93 | 94 | 95 | 96 | #if os(tvOS) 97 | // Save to documents folder is not supported on tvOS 98 | #else 99 | /** 100 | Save this object to a file in the documents directory 101 | 102 | - parameter fileName: The filename 103 | 104 | - returns: true if successfull 105 | */ 106 | func saveToDocuments(_ fileName: String) throws { 107 | let fileURL = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false).appendingPathComponent(fileName) 108 | try self.saveTo(fileURL) 109 | } 110 | #endif 111 | } 112 | 113 | public extension Decodable { 114 | /** 115 | Create an instance of this type from a json string 116 | 117 | - parameter json: The json string 118 | - parameter keyPath: for if you want something else than the root object 119 | */ 120 | init(json: String, keyPath: String? = nil, 121 | keyDecodingStrategy: JSONDecoder.KeyDecodingStrategy = .convertFromSnakeCase, 122 | dateDecodingStrategy: JSONDecoder.DateDecodingStrategy = .deferredToDate, 123 | dataDecodingStrategy: JSONDecoder.DataDecodingStrategy = .base64, 124 | nonConformingFloatDecodingStrategy: JSONDecoder.NonConformingFloatDecodingStrategy = .throw) throws { 125 | guard let data = json.data(using: .utf8) else { throw CodingError.RuntimeError("cannot create data from string") } 126 | try self.init(data: data, keyPath: keyPath, keyDecodingStrategy: keyDecodingStrategy, dateDecodingStrategy: dateDecodingStrategy, dataDecodingStrategy: dataDecodingStrategy, nonConformingFloatDecodingStrategy: nonConformingFloatDecodingStrategy) 127 | } 128 | 129 | /** 130 | Create an instance of this type from a json string 131 | 132 | - parameter data: The json data 133 | - parameter keyPath: for if you want something else than the root object 134 | */ 135 | init(data: Data, keyPath: String? = nil, 136 | keyDecodingStrategy: JSONDecoder.KeyDecodingStrategy = .convertFromSnakeCase, 137 | dateDecodingStrategy: JSONDecoder.DateDecodingStrategy = .deferredToDate, 138 | dataDecodingStrategy: JSONDecoder.DataDecodingStrategy = .base64, 139 | nonConformingFloatDecodingStrategy: JSONDecoder.NonConformingFloatDecodingStrategy = .throw) throws { 140 | 141 | let decoder = JSONDecoder() 142 | decoder.keyDecodingStrategy = keyDecodingStrategy 143 | decoder.dateDecodingStrategy = dateDecodingStrategy 144 | decoder.dataDecodingStrategy = dataDecodingStrategy 145 | decoder.nonConformingFloatDecodingStrategy = nonConformingFloatDecodingStrategy 146 | 147 | if let keyPath = keyPath { 148 | let topLevel = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) 149 | guard let nestedJson = (topLevel as AnyObject).value(forKeyPath: keyPath) else { throw CodingError.RuntimeError("Cannot decode data to object") } 150 | let nestedData = try JSONSerialization.data(withJSONObject: nestedJson) 151 | self = try decoder.decode(Self.self, from: nestedData) 152 | return 153 | } 154 | self = try decoder.decode(Self.self, from: data) 155 | } 156 | 157 | /** 158 | Initialize this object from an archived file from an URL 159 | 160 | - parameter fileNameInTemp: The filename 161 | */ 162 | init(fileURL: URL) throws { 163 | let data = try Data(contentsOf: fileURL) 164 | try self.init(data: data) 165 | } 166 | 167 | /** 168 | Initialize this object from an archived file from the temp directory 169 | 170 | - parameter fileNameInTemp: The filename 171 | */ 172 | init(fileNameInTemp: String) throws { 173 | let fileURL = try FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: false).appendingPathComponent(fileNameInTemp) 174 | try self.init(fileURL: fileURL) 175 | } 176 | 177 | /** 178 | Initialize this object from an archived file from the documents directory 179 | 180 | - parameter fileNameInDocuments: The filename 181 | */ 182 | init(fileNameInDocuments: String) throws { 183 | let fileURL = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false).appendingPathComponent(fileNameInDocuments) 184 | try self.init(fileURL: fileURL) 185 | } 186 | } 187 | 188 | 189 | -------------------------------------------------------------------------------- /Source/Enum/Enum.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EnumExtensions.swift 3 | // Stuff 4 | // 5 | // Created by Edwin Vermeer on 10/02/2017. 6 | // Copyright © 2017 EVICT BV. All rights reserved. 7 | // 8 | 9 | 10 | /** 11 | Protocol for the enum extension functions 12 | */ 13 | public protocol Enum { 14 | } 15 | 16 | /** 17 | Protocol for the workaround when using an enum with a rawValue of an undefined type 18 | */ 19 | public protocol RawEnum { 20 | /** 21 | For implementing a function that will return the rawValue for a non sepecific enum 22 | */ 23 | var anyRawValue: Any { get } 24 | } 25 | 26 | 27 | /** 28 | Enum extension for getting the associated value of an enum 29 | */ 30 | public extension Enum { 31 | /** 32 | Get the associated value of the enum 33 | */ 34 | var associated: (label: String, value: Any?, values: [Any]) { 35 | get { 36 | let mirror = Mirror(reflecting: self) 37 | if mirror.displayStyle == .enum { 38 | if let associated = mirror.children.first { 39 | let values = Mirror(reflecting: associated.value).children 40 | var valuesArray = [Any]() 41 | for item in values { 42 | valuesArray.append(item.value) 43 | } 44 | return (associated.label!, associated.value, valuesArray) 45 | } 46 | print("WARNING: Enum option of \(self) does not have an associated value") 47 | return ("\(self)", nil, []) 48 | } 49 | print("WARNING: You can only extend an enum with the EnumExtension") 50 | return ("\(self)", nil, []) 51 | } 52 | } 53 | } 54 | 55 | 56 | /** 57 | Enum extension for getting all enum options in an array 58 | */ 59 | public extension Enum where Self: Hashable { 60 | /** 61 | Return all enum values 62 | */ 63 | static var allValues: [Self] { 64 | return [Self](AnySequence { () -> AnyIterator in 65 | var raw = 0 66 | var first: Self? 67 | return AnyIterator { 68 | let current = withUnsafeBytes(of: &raw) { $0.load(as: Self.self) } 69 | if raw == 0 { 70 | first = current 71 | } else if current == first { 72 | return nil 73 | } 74 | raw += 1 75 | return current 76 | } 77 | }) 78 | } 79 | } 80 | 81 | 82 | /** 83 | Enum extension for getting the rawValue even if the enum is passed in an Enum 84 | */ 85 | public extension RawEnum where Self: RawRepresentable { 86 | /** 87 | Return the raw value of the enum 88 | */ 89 | var anyRawValue: Any { 90 | get { 91 | let mirror = Mirror(reflecting: self) 92 | if mirror.displayStyle != .enum { 93 | print("WARNING: You can only extend an enum with the Enum protocol") 94 | } 95 | return rawValue as Any 96 | 97 | } 98 | } 99 | } 100 | 101 | /** 102 | extension for creating a querystring from an array of Enum values (with associated value) 103 | */ 104 | public extension Array where Element: Enum { 105 | var queryString: String { 106 | get { 107 | return (self.map {"\($0.associated.label)=\($0.associated.value!)"}).joined(separator: ",") 108 | } 109 | } 110 | } 111 | 112 | 113 | /** 114 | Dictionary extension for creating a dictionary from an array of enum values 115 | */ 116 | public extension Dictionary { 117 | /** 118 | Create a dictionairy based on all associated values of an enum array 119 | 120 | - parameter associated: array of dictionairy values which have an associated value 121 | 122 | */ 123 | init(_ associated: [T]?) { 124 | self.init() 125 | if associated != nil { 126 | for myEnum in associated! { 127 | self[(myEnum.associated.label as? Key)!] = myEnum.associated.value as? Value 128 | } 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /Source/Print/Print.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Print.swift 3 | // 4 | // Created by Edwin Vermeer on 29/01/2017. 5 | // Copyright © 2017 EVICT BV. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | import os.log 10 | 11 | /** 12 | Extending the Stuff object with print functionality 13 | */ 14 | public extension Stuff { 15 | 16 | /** 17 | Enumeration of the log levels 18 | */ 19 | enum logLevel: Int { 20 | // Make sure all log events goes to the device log 21 | case productionLogAll = 0 22 | // Informational loging, lowest level 23 | case info = 1 24 | // Debug loging, default level 25 | case debug = 2 26 | // Warning loging, You should take notice 27 | case warn = 3 28 | // Error loging, Something went wrong, take action 29 | case error = 4 30 | // Fatal loging, Something went seriously wrong, can't recover from it. 31 | case fatal = 5 32 | // Set the minimumLogLevel to .none to stop everything from loging 33 | case none = 6 34 | 35 | /** 36 | Get the emoticon for the log level. 37 | */ 38 | public func description() -> String { 39 | switch self { 40 | case .info: 41 | return "❓" 42 | case .debug: 43 | return "✳️" 44 | case .warn: 45 | return "⚠️" 46 | case .error: 47 | return "🚫" 48 | case .fatal: 49 | return "🆘" 50 | case .productionLogAll: 51 | return "🚧" 52 | case .none: 53 | return "" 54 | } 55 | } 56 | } 57 | 58 | /** 59 | Set the minimum log level. By default set to .info which is the minimum. Everything will be loged. 60 | */ 61 | static var minimumLogLevel: logLevel = .info 62 | 63 | /** 64 | The print command for writing to the output window 65 | */ 66 | static func print(_ object: T, _ level: logLevel = (T.self is Error ? .error : .debug), showTrace: Bool = false, filename: String = #file, line: Int = #line, funcname: String = #function, trace: [String] = Thread.callStackSymbols) { 67 | if level.rawValue >= Stuff.minimumLogLevel.rawValue { 68 | let dateFormatter = DateFormatter() 69 | dateFormatter.dateFormat = "MM/dd/yyyy HH:mm:ss:SSS" 70 | let process = ProcessInfo.processInfo 71 | let threadId = Thread.current.name ?? "" 72 | let file = URL(string: filename)?.lastPathComponent ?? "" 73 | let traceOutput: String = !showTrace ? "" : trace.map { "\t\t\($0)" }.reduce("\n") { "\($0)\n\($1)" } 74 | let output: String = object is Error ? "\((object as! Error).localizedDescription)\(traceOutput)" : "\(object)" 75 | let logText = "\n\(level.description()) .\(level) ⏱ \(dateFormatter.string(from: Foundation.Date())) 📱 \(process.processName) [\(process.processIdentifier):\(threadId)] 📂 \(file)(\(line)) ⚙️ \(funcname) ➡️\r\t\(output)" 76 | if Stuff.minimumLogLevel == .productionLogAll || level == .productionLogAll { 77 | if #available(iOS 10.0, macOS 10.12, tvOS 10.0, watchOS 3 , *) { 78 | let log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "Stuff") 79 | os_log("%{public}@", log: log, logText) 80 | } else { 81 | NSLog("#Stuff# /(logText)") 82 | } 83 | } else { 84 | Swift.print(logText) 85 | } 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /Source/Stuff.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Stuff.swift 3 | // 4 | // Created by Edwin Vermeer on 29/01/2017. 5 | // Copyright © 2017 EVICT BV. All rights reserved. 6 | // 7 | 8 | /** 9 | Just the root class for extending with generic functionality (See Print) 10 | */ 11 | open class Stuff { 12 | } 13 | -------------------------------------------------------------------------------- /Source/TODO/TODO.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TODO.swift 3 | // 4 | // Created by Edwin Vermeer on 28/02/2017. 5 | // Copyright © 2017 EVICT BV. All rights reserved. 6 | // 7 | 8 | @available(*, deprecated, message: "TODO left in code") 9 | func TODO(_ log: String = "") { 10 | print("⚠️ TODO code is still in use! ⚠️\n\(log)") 11 | } 12 | 13 | @available(*, deprecated, message: "TODO left in code") 14 | var TODO_: Never { 15 | fatalError("TODO left in code") 16 | } 17 | -------------------------------------------------------------------------------- /Stuff.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = "Stuff" 3 | s.version = "1.1.2" 4 | s.summary = "Too small for a library, too important to just forget" 5 | 6 | s.description = <<-EOS 7 | This is a collection of helpfull code that is difficult to place in a specific library. 8 | EOS 9 | 10 | s.homepage = "https://github.com/evermeer/Stuff" 11 | s.license = { :type => "MIT", :file => "License" } 12 | s.author = { "Edwin Vermeer" => "edwin@evict.nl" } 13 | s.social_media_url = "http://twitter.com/evermeer" 14 | 15 | s.ios.deployment_target = '8.0' 16 | s.osx.deployment_target = '10.10' 17 | s.watchos.deployment_target = '2.0' 18 | s.tvos.deployment_target = '9.0' 19 | 20 | s.source = { :git => "https://github.com/evermeer/Stuff.git", :tag => s.version } 21 | s.default_subspec = "All" 22 | 23 | # This is the complete Stuff library 24 | s.subspec "All" do |ss| 25 | ss.dependency "Stuff/Enum" 26 | ss.dependency "Stuff/Print" 27 | ss.dependency "Stuff/TODO" 28 | ss.dependency "Stuff/Codable" 29 | end 30 | 31 | # This is the core Print library 32 | s.subspec "Print" do |ss| 33 | ss.source_files = "Source/*.swift", "Source/Print/*.swift" 34 | end 35 | 36 | # This is the core Enum library 37 | s.subspec "Enum" do |ss| 38 | ss.source_files = "Source/*.swift", "Source/Enum/*.swift" 39 | end 40 | 41 | # This is the core TODO library 42 | s.subspec "TODO" do |ss| 43 | ss.source_files = "Source/TODO/*.swift" 44 | end 45 | 46 | # This is the core Coding library 47 | s.subspec "Codable" do |ss| 48 | ss.source_files = "Source/Codable/*.swift" 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /Stuff.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 08E428A7B01137966D47BE47 /* Pods_StuffTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E27E8C40F9CACB5D81F585D5 /* Pods_StuffTests.framework */; }; 11 | 13E8825E1F03CF3900F1DA7D /* CodableStuffTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 13E8825D1F03CF3900F1DA7D /* CodableStuffTests.swift */; }; 12 | 7F29F37C1E4E05070048B915 /* EnumStuffTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F29F37B1E4E05070048B915 /* EnumStuffTests.swift */; }; 13 | 7F9A2FC81E51A9C8004450B6 /* PrintStuffTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F9A2FC71E51A9C8004450B6 /* PrintStuffTests.swift */; }; 14 | 7FF3E19E1E65888A0057B575 /* TODOStuffTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FF3E19D1E65888A0057B575 /* TODOStuffTests.swift */; }; 15 | /* End PBXBuildFile section */ 16 | 17 | /* Begin PBXFileReference section */ 18 | 0BD3780882CCECD3D4223F11 /* Pods-StuffTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-StuffTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-StuffTests/Pods-StuffTests.debug.xcconfig"; sourceTree = ""; }; 19 | 13E8825D1F03CF3900F1DA7D /* CodableStuffTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodableStuffTests.swift; sourceTree = ""; }; 20 | 14C53FB6FF7E4E49FE104565 /* Pods-StuffTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-StuffTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-StuffTests/Pods-StuffTests.release.xcconfig"; sourceTree = ""; }; 21 | 7F29F37B1E4E05070048B915 /* EnumStuffTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnumStuffTests.swift; sourceTree = ""; }; 22 | 7F29F37D1E4E05070048B915 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 23 | 7F29F3861E4E092C0048B915 /* StuffTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = StuffTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 24 | 7F9A2FC71E51A9C8004450B6 /* PrintStuffTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrintStuffTests.swift; sourceTree = ""; }; 25 | 7FF3E19D1E65888A0057B575 /* TODOStuffTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TODOStuffTests.swift; sourceTree = ""; }; 26 | E27E8C40F9CACB5D81F585D5 /* Pods_StuffTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_StuffTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 27 | /* End PBXFileReference section */ 28 | 29 | /* Begin PBXFrameworksBuildPhase section */ 30 | 7F29F3741E4E05070048B915 /* Frameworks */ = { 31 | isa = PBXFrameworksBuildPhase; 32 | buildActionMask = 2147483647; 33 | files = ( 34 | 08E428A7B01137966D47BE47 /* Pods_StuffTests.framework in Frameworks */, 35 | ); 36 | runOnlyForDeploymentPostprocessing = 0; 37 | }; 38 | /* End PBXFrameworksBuildPhase section */ 39 | 40 | /* Begin PBXGroup section */ 41 | 50526C767D66A5D96A2B0D34 /* Frameworks */ = { 42 | isa = PBXGroup; 43 | children = ( 44 | E27E8C40F9CACB5D81F585D5 /* Pods_StuffTests.framework */, 45 | ); 46 | name = Frameworks; 47 | sourceTree = ""; 48 | }; 49 | 7F29F35A1E4E05070048B915 = { 50 | isa = PBXGroup; 51 | children = ( 52 | 7F29F37A1E4E05070048B915 /* StuffTests */, 53 | 973835B12DDA3113D12045C3 /* Pods */, 54 | 50526C767D66A5D96A2B0D34 /* Frameworks */, 55 | 7F29F3861E4E092C0048B915 /* StuffTests.xctest */, 56 | ); 57 | sourceTree = ""; 58 | }; 59 | 7F29F37A1E4E05070048B915 /* StuffTests */ = { 60 | isa = PBXGroup; 61 | children = ( 62 | 13E8825D1F03CF3900F1DA7D /* CodableStuffTests.swift */, 63 | 7F29F37B1E4E05070048B915 /* EnumStuffTests.swift */, 64 | 7F9A2FC71E51A9C8004450B6 /* PrintStuffTests.swift */, 65 | 7FF3E19D1E65888A0057B575 /* TODOStuffTests.swift */, 66 | 7F29F37D1E4E05070048B915 /* Info.plist */, 67 | ); 68 | path = StuffTests; 69 | sourceTree = ""; 70 | }; 71 | 973835B12DDA3113D12045C3 /* Pods */ = { 72 | isa = PBXGroup; 73 | children = ( 74 | 0BD3780882CCECD3D4223F11 /* Pods-StuffTests.debug.xcconfig */, 75 | 14C53FB6FF7E4E49FE104565 /* Pods-StuffTests.release.xcconfig */, 76 | ); 77 | name = Pods; 78 | sourceTree = ""; 79 | }; 80 | /* End PBXGroup section */ 81 | 82 | /* Begin PBXNativeTarget section */ 83 | 7F29F3761E4E05070048B915 /* StuffTests */ = { 84 | isa = PBXNativeTarget; 85 | buildConfigurationList = 7F29F3831E4E05070048B915 /* Build configuration list for PBXNativeTarget "StuffTests" */; 86 | buildPhases = ( 87 | F3824567CF8D34B0A4F24E01 /* [CP] Check Pods Manifest.lock */, 88 | 7F29F3731E4E05070048B915 /* Sources */, 89 | 7F29F3741E4E05070048B915 /* Frameworks */, 90 | 7F29F3751E4E05070048B915 /* Resources */, 91 | 25B19118C00DCF76F87052DF /* [CP] Embed Pods Frameworks */, 92 | ); 93 | buildRules = ( 94 | ); 95 | dependencies = ( 96 | ); 97 | name = StuffTests; 98 | productName = StuffTests; 99 | productReference = 7F29F3861E4E092C0048B915 /* StuffTests.xctest */; 100 | productType = "com.apple.product-type.bundle.unit-test"; 101 | }; 102 | /* End PBXNativeTarget section */ 103 | 104 | /* Begin PBXProject section */ 105 | 7F29F35B1E4E05070048B915 /* Project object */ = { 106 | isa = PBXProject; 107 | attributes = { 108 | LastSwiftUpdateCheck = 0820; 109 | LastUpgradeCheck = 1020; 110 | ORGANIZATIONNAME = evict; 111 | TargetAttributes = { 112 | 7F29F3761E4E05070048B915 = { 113 | CreatedOnToolsVersion = 8.2.1; 114 | DevelopmentTeam = X356V8CMHF; 115 | LastSwiftMigration = 0910; 116 | ProvisioningStyle = Automatic; 117 | }; 118 | }; 119 | }; 120 | buildConfigurationList = 7F29F35E1E4E05070048B915 /* Build configuration list for PBXProject "Stuff" */; 121 | compatibilityVersion = "Xcode 3.2"; 122 | developmentRegion = en; 123 | hasScannedForEncodings = 0; 124 | knownRegions = ( 125 | en, 126 | Base, 127 | ); 128 | mainGroup = 7F29F35A1E4E05070048B915; 129 | productRefGroup = 7F29F35A1E4E05070048B915; 130 | projectDirPath = ""; 131 | projectRoot = ""; 132 | targets = ( 133 | 7F29F3761E4E05070048B915 /* StuffTests */, 134 | ); 135 | }; 136 | /* End PBXProject section */ 137 | 138 | /* Begin PBXResourcesBuildPhase section */ 139 | 7F29F3751E4E05070048B915 /* Resources */ = { 140 | isa = PBXResourcesBuildPhase; 141 | buildActionMask = 2147483647; 142 | files = ( 143 | ); 144 | runOnlyForDeploymentPostprocessing = 0; 145 | }; 146 | /* End PBXResourcesBuildPhase section */ 147 | 148 | /* Begin PBXShellScriptBuildPhase section */ 149 | 25B19118C00DCF76F87052DF /* [CP] Embed Pods Frameworks */ = { 150 | isa = PBXShellScriptBuildPhase; 151 | buildActionMask = 2147483647; 152 | files = ( 153 | ); 154 | inputPaths = ( 155 | "${PODS_ROOT}/Target Support Files/Pods-StuffTests/Pods-StuffTests-frameworks.sh", 156 | "${BUILT_PRODUCTS_DIR}/Stuff/Stuff.framework", 157 | ); 158 | name = "[CP] Embed Pods Frameworks"; 159 | outputPaths = ( 160 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Stuff.framework", 161 | ); 162 | runOnlyForDeploymentPostprocessing = 0; 163 | shellPath = /bin/sh; 164 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-StuffTests/Pods-StuffTests-frameworks.sh\"\n"; 165 | showEnvVarsInLog = 0; 166 | }; 167 | F3824567CF8D34B0A4F24E01 /* [CP] Check Pods Manifest.lock */ = { 168 | isa = PBXShellScriptBuildPhase; 169 | buildActionMask = 2147483647; 170 | files = ( 171 | ); 172 | inputPaths = ( 173 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 174 | "${PODS_ROOT}/Manifest.lock", 175 | ); 176 | name = "[CP] Check Pods Manifest.lock"; 177 | outputPaths = ( 178 | "$(DERIVED_FILE_DIR)/Pods-StuffTests-checkManifestLockResult.txt", 179 | ); 180 | runOnlyForDeploymentPostprocessing = 0; 181 | shellPath = /bin/sh; 182 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 183 | showEnvVarsInLog = 0; 184 | }; 185 | /* End PBXShellScriptBuildPhase section */ 186 | 187 | /* Begin PBXSourcesBuildPhase section */ 188 | 7F29F3731E4E05070048B915 /* Sources */ = { 189 | isa = PBXSourcesBuildPhase; 190 | buildActionMask = 2147483647; 191 | files = ( 192 | 7F9A2FC81E51A9C8004450B6 /* PrintStuffTests.swift in Sources */, 193 | 7FF3E19E1E65888A0057B575 /* TODOStuffTests.swift in Sources */, 194 | 7F29F37C1E4E05070048B915 /* EnumStuffTests.swift in Sources */, 195 | 13E8825E1F03CF3900F1DA7D /* CodableStuffTests.swift in Sources */, 196 | ); 197 | runOnlyForDeploymentPostprocessing = 0; 198 | }; 199 | /* End PBXSourcesBuildPhase section */ 200 | 201 | /* Begin XCBuildConfiguration section */ 202 | 7F29F37E1E4E05070048B915 /* Debug */ = { 203 | isa = XCBuildConfiguration; 204 | buildSettings = { 205 | ALWAYS_SEARCH_USER_PATHS = NO; 206 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 207 | CLANG_ANALYZER_NONNULL = YES; 208 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 209 | CLANG_CXX_LIBRARY = "libc++"; 210 | CLANG_ENABLE_MODULES = YES; 211 | CLANG_ENABLE_OBJC_ARC = YES; 212 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 213 | CLANG_WARN_BOOL_CONVERSION = YES; 214 | CLANG_WARN_COMMA = YES; 215 | CLANG_WARN_CONSTANT_CONVERSION = YES; 216 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 217 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 218 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 219 | CLANG_WARN_EMPTY_BODY = YES; 220 | CLANG_WARN_ENUM_CONVERSION = YES; 221 | CLANG_WARN_INFINITE_RECURSION = YES; 222 | CLANG_WARN_INT_CONVERSION = YES; 223 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 224 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 225 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 226 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 227 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 228 | CLANG_WARN_STRICT_PROTOTYPES = YES; 229 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 230 | CLANG_WARN_UNREACHABLE_CODE = YES; 231 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 232 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer: evermeer@beterdichtbij.nl (4SA8U36ZTM)"; 233 | COPY_PHASE_STRIP = NO; 234 | DEBUG_INFORMATION_FORMAT = dwarf; 235 | ENABLE_STRICT_OBJC_MSGSEND = YES; 236 | ENABLE_TESTABILITY = YES; 237 | GCC_C_LANGUAGE_STANDARD = gnu99; 238 | GCC_DYNAMIC_NO_PIC = NO; 239 | GCC_NO_COMMON_BLOCKS = YES; 240 | GCC_OPTIMIZATION_LEVEL = 0; 241 | GCC_PREPROCESSOR_DEFINITIONS = ( 242 | "DEBUG=1", 243 | "$(inherited)", 244 | ); 245 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 246 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 247 | GCC_WARN_UNDECLARED_SELECTOR = YES; 248 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 249 | GCC_WARN_UNUSED_FUNCTION = YES; 250 | GCC_WARN_UNUSED_VARIABLE = YES; 251 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 252 | MTL_ENABLE_DEBUG_INFO = YES; 253 | ONLY_ACTIVE_ARCH = YES; 254 | SDKROOT = iphoneos; 255 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 256 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 257 | SWIFT_VERSION = 5.0; 258 | TARGETED_DEVICE_FAMILY = "1,2"; 259 | }; 260 | name = Debug; 261 | }; 262 | 7F29F37F1E4E05070048B915 /* Release */ = { 263 | isa = XCBuildConfiguration; 264 | buildSettings = { 265 | ALWAYS_SEARCH_USER_PATHS = NO; 266 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 267 | CLANG_ANALYZER_NONNULL = YES; 268 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 269 | CLANG_CXX_LIBRARY = "libc++"; 270 | CLANG_ENABLE_MODULES = YES; 271 | CLANG_ENABLE_OBJC_ARC = YES; 272 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 273 | CLANG_WARN_BOOL_CONVERSION = YES; 274 | CLANG_WARN_COMMA = YES; 275 | CLANG_WARN_CONSTANT_CONVERSION = YES; 276 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 277 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 278 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 279 | CLANG_WARN_EMPTY_BODY = YES; 280 | CLANG_WARN_ENUM_CONVERSION = YES; 281 | CLANG_WARN_INFINITE_RECURSION = YES; 282 | CLANG_WARN_INT_CONVERSION = YES; 283 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 284 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 285 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 286 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 287 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 288 | CLANG_WARN_STRICT_PROTOTYPES = YES; 289 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 290 | CLANG_WARN_UNREACHABLE_CODE = YES; 291 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 292 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer: evermeer@beterdichtbij.nl (4SA8U36ZTM)"; 293 | COPY_PHASE_STRIP = NO; 294 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 295 | ENABLE_NS_ASSERTIONS = NO; 296 | ENABLE_STRICT_OBJC_MSGSEND = YES; 297 | GCC_C_LANGUAGE_STANDARD = gnu99; 298 | GCC_NO_COMMON_BLOCKS = YES; 299 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 300 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 301 | GCC_WARN_UNDECLARED_SELECTOR = YES; 302 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 303 | GCC_WARN_UNUSED_FUNCTION = YES; 304 | GCC_WARN_UNUSED_VARIABLE = YES; 305 | IPHONEOS_DEPLOYMENT_TARGET = 10.2; 306 | MTL_ENABLE_DEBUG_INFO = NO; 307 | SDKROOT = iphoneos; 308 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 309 | SWIFT_VERSION = 5.0; 310 | TARGETED_DEVICE_FAMILY = "1,2"; 311 | VALIDATE_PRODUCT = YES; 312 | }; 313 | name = Release; 314 | }; 315 | 7F29F3841E4E05070048B915 /* Debug */ = { 316 | isa = XCBuildConfiguration; 317 | baseConfigurationReference = 0BD3780882CCECD3D4223F11 /* Pods-StuffTests.debug.xcconfig */; 318 | buildSettings = { 319 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 320 | DEVELOPMENT_TEAM = X356V8CMHF; 321 | INFOPLIST_FILE = StuffTests/Info.plist; 322 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 323 | PRODUCT_BUNDLE_IDENTIFIER = nl.evict.StuffTests; 324 | PRODUCT_NAME = "$(TARGET_NAME)"; 325 | SWIFT_VERSION = 5.0; 326 | }; 327 | name = Debug; 328 | }; 329 | 7F29F3851E4E05070048B915 /* Release */ = { 330 | isa = XCBuildConfiguration; 331 | baseConfigurationReference = 14C53FB6FF7E4E49FE104565 /* Pods-StuffTests.release.xcconfig */; 332 | buildSettings = { 333 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 334 | DEVELOPMENT_TEAM = X356V8CMHF; 335 | INFOPLIST_FILE = StuffTests/Info.plist; 336 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 337 | PRODUCT_BUNDLE_IDENTIFIER = nl.evict.StuffTests; 338 | PRODUCT_NAME = "$(TARGET_NAME)"; 339 | SWIFT_VERSION = 5.0; 340 | }; 341 | name = Release; 342 | }; 343 | /* End XCBuildConfiguration section */ 344 | 345 | /* Begin XCConfigurationList section */ 346 | 7F29F35E1E4E05070048B915 /* Build configuration list for PBXProject "Stuff" */ = { 347 | isa = XCConfigurationList; 348 | buildConfigurations = ( 349 | 7F29F37E1E4E05070048B915 /* Debug */, 350 | 7F29F37F1E4E05070048B915 /* Release */, 351 | ); 352 | defaultConfigurationIsVisible = 0; 353 | defaultConfigurationName = Release; 354 | }; 355 | 7F29F3831E4E05070048B915 /* Build configuration list for PBXNativeTarget "StuffTests" */ = { 356 | isa = XCConfigurationList; 357 | buildConfigurations = ( 358 | 7F29F3841E4E05070048B915 /* Debug */, 359 | 7F29F3851E4E05070048B915 /* Release */, 360 | ); 361 | defaultConfigurationIsVisible = 0; 362 | defaultConfigurationName = Release; 363 | }; 364 | /* End XCConfigurationList section */ 365 | }; 366 | rootObject = 7F29F35B1E4E05070048B915 /* Project object */; 367 | } 368 | -------------------------------------------------------------------------------- /Stuff.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Stuff.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 12 | 13 | 15 | 16 | 18 | 19 | 21 | 22 | 24 | 25 | 27 | 28 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Stuff.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /StuffTests/CodableStuffTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CodableStuffTests.swift 3 | // StuffTests 4 | // 5 | // Created by Vermeer, Edwin on 28/06/2017. 6 | // Copyright © 2017 evict. All rights reserved. 7 | // 8 | 9 | 10 | import XCTest 11 | @testable import Stuff 12 | 13 | class CodingStuffTests: XCTestCase { 14 | 15 | func test() { 16 | let initialObject = TestCodable(naam: "Edwin", id: 1, testField: "tst") 17 | 18 | guard let json = initialObject.toJsonString() else { 19 | print("Could not create json from object") 20 | assertionFailure() 21 | return 22 | } 23 | print("Json string of object = \n\t\(json)") 24 | 25 | guard let newObject = try? TestCodable(json: json) else { 26 | print("Could not create object from json") 27 | assertionFailure() 28 | return 29 | } 30 | print("Object created with json = \n\t\(String(describing: newObject))") 31 | 32 | let json2 = "[{\"id\":1,\"naam\":\"Edwin\"},{\"id\":2,\"naam\":\"Vermeer\"}]" 33 | guard let array = try? [TestCodable](json: json2) else { 34 | print("Could not create object array from json") 35 | assertionFailure() 36 | return 37 | } 38 | print("Object array created with json = \(String(describing: array))") 39 | 40 | let newJson = array.toJsonString() ?? "" 41 | print("Json from object array = \n\t\(newJson)") 42 | 43 | guard let innerObject = try? TestCodable(json: "{\"user\":{\"id\":1,\"naam\":\"Edwin\"}}", keyPath: "user") else { 44 | print("Could not create object from json") 45 | assertionFailure() 46 | return 47 | } 48 | print("inner object from json \(String(describing: innerObject))") 49 | 50 | do { 51 | let custom = try TestCodable(json: "{\"Naam\":\"UN\", \"Id\":5, \"Test_field\":\"tst\"}", keyPath: nil, keyDecodingStrategy: customCodingStragegy) 52 | print("read object with custom key coding from json to \(String(describing: custom))") 53 | } catch { 54 | print("Could not custom case convert \(error)") 55 | assertionFailure() 56 | return 57 | } 58 | 59 | do { 60 | try initialObject.saveToDocuments("myFile.dat") 61 | } catch { 62 | print("Could not write to documents data : \(error)") 63 | // assertionFailure() 64 | return 65 | } 66 | guard let readObject = try? TestCodable(fileNameInDocuments: "myFile.dat") else { 67 | print("Could not read from documents data") 68 | assertionFailure() 69 | return 70 | } 71 | print("read object from documents \(String(describing: readObject))") 72 | 73 | do { 74 | try array.saveToDocuments("myFile2.dat") 75 | } catch { 76 | print("Could not write to documents data") 77 | assertionFailure() 78 | return 79 | } 80 | guard let readObjectArray = try? [TestCodable](fileNameInDocuments: "myFile2.dat") else { 81 | print("Could not read from documents data") 82 | assertionFailure() 83 | return 84 | } 85 | print("read object array from documents \(String(describing: readObjectArray))") 86 | } 87 | } 88 | 89 | struct TestCodable : Codable { 90 | var naam: String? 91 | var id: Int? 92 | var testField: String? 93 | } 94 | -------------------------------------------------------------------------------- /StuffTests/EnumStuffTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StuffTests.swift 3 | // StuffTests 4 | // 5 | // Created by Edwin Vermeer on 10/02/2017. 6 | // Copyright © 2017 evict. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import Stuff 11 | 12 | class EnumStuffTests: XCTestCase { 13 | 14 | // You have to extend the enum with the Enum protocol (and RawEnum if you want the raw value when it's an Any) 15 | enum test1: String, Enum, RawEnum { 16 | case option1 17 | case option2 18 | case option3 19 | } 20 | 21 | // You have to extend the enum with the Enum protocol 22 | enum test2: Enum { 23 | case option4(String) 24 | case option5(Int) 25 | case option6(Int, String) 26 | } 27 | 28 | func testEnum() { 29 | XCTAssert(test1.allValues.count == 3, "There should be 3 values") 30 | for value in test1.allValues { 31 | print("value = \(value)") 32 | } 33 | 34 | let v1: test2 = .option4("test") 35 | print("v1 = \(v1.associated.label), \(v1.associated.value!)") 36 | let v2: test2 = .option5(3) 37 | print("v2 = \(v2.associated.label), \(v2.associated.value!)") 38 | let v3: test2 = .option6(4, "more") 39 | print("v3 = \(v3.associated.label), \(v3.associated.value!)") 40 | print("v3 = \(v3.associated.label), \(v3.associated.values)") 41 | 42 | let array = [v1, v2, v3] 43 | let dict = [String:Any](array) 44 | print("Array of enums as dictionary:\n \(dict)") 45 | 46 | print("query = \(array.queryString))") 47 | 48 | let v = getRawValue(test1.option2) 49 | print("raw value = \(v)") 50 | } 51 | 52 | func getRawValue(_ value: Any) -> Any { 53 | return (value as? RawEnum)?.anyRawValue ?? "" 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /StuffTests/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 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /StuffTests/PrintStuffTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PrintStuffTests.swift 3 | // Stuff 4 | // 5 | // Created by Edwin Vermeer on 13/02/2017. 6 | // Copyright © 2017 evict. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import Stuff 11 | 12 | class PrintStuffTests: XCTestCase { 13 | 14 | func testPrint() { 15 | Stuff.print("Just as the standard print but now with detailed information") 16 | Stuff.print("Now it's a warning", .warn) 17 | Stuff.print("Or even an error", .error) 18 | 19 | Stuff.minimumLogLevel = .error 20 | Stuff.print("Now you won't see normal log output") 21 | Stuff.print("Only errors are shown", .error) 22 | 23 | Stuff.minimumLogLevel = .none 24 | Stuff.print("Or if it's disabled you won't see any log", .error) 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /StuffTests/TODOStuffTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TODOStuffTests.swift 3 | // Stuff 4 | // 5 | // Created by Edwin Vermeer on 28/02/2017. 6 | // Copyright © 2017 evict. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import Stuff 11 | 12 | class TODOStuffTests: XCTestCase { 13 | 14 | func testTODO() { 15 | // We need to fix something, but function can run 16 | TODO() 17 | 18 | TODO("An other todo, now giving some detailed info") 19 | 20 | // We need to fix this. Otherwise just fail. The tet will crash here. See the stacktrace 21 | //Cant test anymore TODO_ 22 | } 23 | 24 | } 25 | --------------------------------------------------------------------------------