├── .gitignore ├── .travis.yml ├── Analysis.podspec ├── Analysis ├── Assets │ └── .gitkeep └── Classes │ ├── Analysis.swift │ ├── Character+Casing.swift │ ├── Dictionary+Sorting.swift │ ├── String+Analysis.swift │ └── SyllableCounter.swift ├── Changelog.md ├── Example ├── Analysis.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ ├── Analysis-Example.xcscheme │ │ └── Analysis_Tests.xcscheme ├── Analysis.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings ├── Analysis │ ├── Analysis.storyboard │ ├── AnalysisTableViewController.swift │ ├── AppDelegate.swift │ ├── Base.lproj │ │ ├── LaunchScreen.xib │ │ └── Main.storyboard │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Info.plist │ ├── UIViewController+Safari.swift │ └── ViewController.swift ├── Podfile ├── Podfile.lock └── Tests │ ├── AnalysisTests.swift │ ├── CharacterCasingTests.swift │ └── Info.plist ├── Gemfile ├── Gemfile.lock ├── License └── Readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xcuserstate 23 | 24 | ## Obj-C/Swift specific 25 | *.hmap 26 | *.ipa 27 | *.dSYM.zip 28 | *.dSYM 29 | 30 | ## Playgrounds 31 | timeline.xctimeline 32 | playground.xcworkspace 33 | 34 | # Swift Package Manager 35 | # 36 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 37 | # Packages/ 38 | .build/ 39 | 40 | # CocoaPods 41 | # 42 | # We recommend against adding the Pods directory to your .gitignore. However 43 | # you should judge for yourself, the pros and cons are mentioned at: 44 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 45 | # 46 | # Pods/ 47 | 48 | # Carthage 49 | # 50 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 51 | # Carthage/Checkouts 52 | 53 | Carthage/Build 54 | 55 | # fastlane 56 | # 57 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 58 | # screenshots whenever they are needed. 59 | # For more information about the recommended setup visit: 60 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 61 | 62 | fastlane/report.xml 63 | fastlane/Preview.html 64 | fastlane/screenshots 65 | fastlane/test_output 66 | 67 | Example/Pods 68 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: swift 2 | osx_image: xcode11.1 3 | 4 | script: 5 | - cd Example/ 6 | - pod install 7 | - cd .. 8 | - xcodebuild clean test -scheme Analysis_Tests -workspace Example/Analysis.xcworkspace -destination "platform=iOS Simulator,name=iPhone 11 Pro Max,OS=13.1" 9 | -------------------------------------------------------------------------------- /Analysis.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'Analysis' 3 | s.version = '0.5.0' 4 | s.summary = 'Analyse your strings.' 5 | 6 | s.description = <<-DESC 7 | Analysis analyses strings, checking word count, sentence count, frequency of words and more. 8 | DESC 9 | 10 | s.homepage = 'https://github.com/BasThomas/Analysis' 11 | s.license = { :type => 'MIT', :file => 'LICENSE' } 12 | s.author = { 'Bas Broek' => 'bas@basbroek.nl' } 13 | s.source = { :git => 'https://github.com/BasThomas/Analysis.git', :tag => s.version } 14 | s.social_media_url = 'https://twitter.com/basthomas' 15 | 16 | s.ios.deployment_target = '8.0' 17 | s.swift_version = '5.1' 18 | 19 | s.source_files = 'Analysis/Classes/**/*' 20 | 21 | s.frameworks = 'Foundation' 22 | end 23 | -------------------------------------------------------------------------------- /Analysis/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BasThomas/Analysis/196a4d52d23e9a2922b1e050e4d431610c0b3e0b/Analysis/Assets/.gitkeep -------------------------------------------------------------------------------- /Analysis/Classes/Analysis.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | /// An analysis of a `String`. 4 | public struct Analysis: Hashable { 5 | 6 | /// The option to use when calculating average length. 7 | /// This is either `.word` or `.sentence`. 8 | public enum LengthOption { 9 | case word 10 | case sentence 11 | } 12 | 13 | /// A typealias of `Double`. 14 | public typealias Percentage = Double 15 | 16 | /// A typealias of `Double`. 17 | public typealias Grade = Double 18 | 19 | /// The string used to construct the `Analysis`. 20 | public let input: String 21 | 22 | /// The sentences of the `input`. 23 | public let sentences: [String] 24 | 25 | /// The words of the `input`. 26 | public let words: [String] 27 | 28 | /// The characters of the `input`. 29 | public let characters: [Character] 30 | 31 | /// Initializes an `Analysis` object with the given `String`. 32 | public init(of string: String) { 33 | input = string 34 | sentences = input 35 | .replacingOccurrences(of: "! ", with: "!\n") 36 | .replacingOccurrences(of: ". ", with: ".\n") 37 | .replacingOccurrences(of: "? ", with: "?\n") 38 | .lines 39 | .filter { !$0.trimmingCharacters(in: .whitespaces).isEmpty } 40 | words = input 41 | .split(separator: " ") 42 | .map { $0.trimmingCharacters(in: CharacterSet.letters.inverted) } 43 | characters = Array(input) 44 | } 45 | 46 | /// Returns the sentence count of the `input`. 47 | public func sentenceCount() -> Int { 48 | sentences.count 49 | } 50 | 51 | /// Returns the word count of the `input`. 52 | /// 53 | /// - Parameter unique: Indicating if words should be 54 | /// counted regardless of how many times they occur. 55 | /// Defaults to `false`. 56 | public func wordCount(unique: Bool = false) -> Int { 57 | if unique { 58 | return _wordOccurrences().keys.count 59 | } else { 60 | return words.count 61 | } 62 | } 63 | 64 | /// Returns the total amount of syllables of the `input`. 65 | public func syllableCount() -> Int { 66 | words 67 | .map { $0.syllables } 68 | .reduce(0, +) 69 | } 70 | 71 | /// Returns the character count of the `input`. 72 | /// 73 | /// - Parameter includingSpaces: Indicating if characters 74 | /// should be counted including spaces or not. 75 | /// Defaults to `true`. 76 | public func characterCount(includingSpaces: Bool = true) -> Int { 77 | if includingSpaces { 78 | return characters.count 79 | } else { 80 | return characters.filter { $0 != " " }.count 81 | } 82 | } 83 | 84 | private func _wordOccurrences(caseSensitive: Bool = false) -> [String: Int] { 85 | var occurrences: [String: Int] = [:] 86 | words 87 | .map { (caseSensitive) ? $0 : $0.lowercased() } 88 | .forEach { occurrences[$0] = (occurrences[$0] ?? 0) + 1 } 89 | return occurrences 90 | } 91 | 92 | private func _characterOccurences(caseSensitive: Bool = false) -> [Character: Int] { 93 | var occurrences: [Character: Int] = [:] 94 | characters 95 | .map { (caseSensitive) ? $0 : $0.lowercased() } 96 | .forEach { occurrences[$0] = (occurrences[$0] ?? 0) + 1 } 97 | return occurrences 98 | } 99 | 100 | /// Returns the word occurrences of the `input`. 101 | /// 102 | /// - Parameter caseSensitive: Indicating if words 103 | /// should be counted regardless of their case sensitivity. 104 | /// Defaults to `false`. 105 | /// 106 | /// - Returns: A `Dictionary` containing the words and their 107 | /// occurence. 108 | public func wordOccurrences(caseSensitive: Bool = false) -> [String: Int] { 109 | _wordOccurrences(caseSensitive: caseSensitive) 110 | } 111 | 112 | /// Returns the character occurrences of the `input`. 113 | /// 114 | /// - Parameter caseSensitive: Indicating if characters 115 | /// should be counted regardless of their case sensitivity. 116 | /// Defaults to `false`. 117 | /// 118 | /// - Returns: A `Dictionary` containing the characters and their 119 | /// occurence. 120 | public func characterOccurences(caseSensitive: Bool = false) -> [Character: Int] { 121 | _characterOccurences(caseSensitive: caseSensitive) 122 | } 123 | 124 | /// Returns the amount of occurrences of the specified word. 125 | /// 126 | /// - Parameter caseSensitive: Indicating if words 127 | /// should be counted regardless of their case sensitivity. 128 | /// Defaults to `false`. 129 | public func occurrences( 130 | of word: String, 131 | caseSensitive: Bool = false 132 | ) -> Int { 133 | let word = (caseSensitive) ? word : word.lowercased() 134 | return _wordOccurrences(caseSensitive: caseSensitive)[word] ?? 0 135 | } 136 | 137 | /// Returns the amount of occurrences of the specified `Character`. 138 | /// 139 | /// - Parameter caseSensitive: Indicating if words 140 | /// should be counted regardless of their case sensitivity. 141 | /// Defaults to `false`. 142 | public func occurrences( 143 | of character: Character, 144 | caseSensitive: Bool = false 145 | ) -> Int { 146 | let character = (caseSensitive) ? character : character.lowercased() 147 | return characters 148 | .map { (caseSensitive) ? $0 : $0.lowercased() } 149 | .filter { $0 == character }.count 150 | } 151 | 152 | /// Returns the syllables of every unique word. 153 | public func wordSyllables() -> [String: Int] { 154 | var syllables: [String: Int] = [:] 155 | let uniqueWords = Array(_wordOccurrences(caseSensitive: false).keys) 156 | 157 | uniqueWords.forEach { syllables[$0] = $0.syllables } 158 | 159 | return syllables 160 | } 161 | 162 | /// Returns the frequency of the specified word. 163 | /// 164 | /// - Parameter caseSensitive: Indicating if words 165 | /// should be counted regardless of their case sensitivity. 166 | /// Defaults to `false`. 167 | /// 168 | /// - Returns: A percentage based on the `wordCount()`. 169 | public func frequency( 170 | of word: String, 171 | caseSensitive: Bool = false 172 | ) -> Percentage { 173 | Double(occurrences( 174 | of: word, 175 | caseSensitive: caseSensitive 176 | )) / Double(wordCount()) * 100.0 177 | } 178 | 179 | /// Returns the frequency of the specified `Character`. 180 | /// 181 | /// - Parameter caseSensitive: Indicating if words 182 | /// should be counted regardless of their case sensitivity. 183 | /// Defaults to `false`. 184 | /// - Parameter includesSpaces: Indicating if characters 185 | /// should be counted including spaces or not. 186 | /// Defaults to `true`. 187 | /// 188 | /// - Returns: A percentage based on the `characterCount()`. 189 | public func frequency( 190 | of character: Character, 191 | caseSensitive: Bool = false, 192 | includingSpaces: Bool = true 193 | ) -> Percentage { 194 | Double(occurrences( 195 | of: character, 196 | caseSensitive: caseSensitive 197 | )) / Double(characterCount( 198 | includingSpaces: includingSpaces 199 | )) * 100.0 200 | } 201 | 202 | /// Returns the average characters of the specified `LengthOption`. 203 | /// 204 | /// - Parameter option: The option to calculate the average of. 205 | /// This is either by `.word` or by `.sentence`. 206 | public func averageCharacters(per option: LengthOption) -> Double { 207 | switch option { 208 | case .word: 209 | return Double(words.reduce("", +).count) / Double(wordCount()) 210 | case .sentence: 211 | if sentences.count > 1 { 212 | return Double(sentences.reduce("", +).count) / Double(sentenceCount()) 213 | } else { 214 | return Double(characterCount(includingSpaces: true)) / Double(sentenceCount()) 215 | } 216 | } 217 | } 218 | 219 | /// Returns the average words per sentence. 220 | public var averageWordsPerSentence: Double { 221 | Double(wordCount()) / Double(sentenceCount()) 222 | } 223 | 224 | private var _wordsPerSentences: Double { 225 | Double(wordCount()) / Double(sentenceCount()) 226 | } 227 | 228 | private var _syllablesPerWords: Double { 229 | Double(syllableCount()) / Double(wordCount()) 230 | } 231 | 232 | /// Returns the Flesch reading ease score. 233 | /// 234 | /// - Note: https://en.wikipedia.org/wiki/Flesch–Kincaid_readability_tests#Flesch_reading_ease 235 | public func fleschReadingEase() -> Percentage { 236 | 206.835 - 1.015 * _wordsPerSentences - 84.6 * _syllablesPerWords 237 | } 238 | 239 | /// Returns the Flesch-Kincaid grade level. 240 | /// 241 | /// - Note: https://en.wikipedia.org/wiki/Flesch–Kincaid_readability_tests#Flesch.E2.80.93Kincaid_grade_level 242 | public func fleschKincaidGradeLevel() -> Grade { 243 | 0.39 * _wordsPerSentences + 11.8 * _syllablesPerWords - 15.59 244 | } 245 | } 246 | 247 | extension Analysis: Comparable { 248 | 249 | public static func <(lhs: Analysis, rhs: Analysis) -> Bool { 250 | lhs.input < rhs.input 251 | } 252 | } 253 | 254 | extension Analysis: CustomStringConvertible, CustomDebugStringConvertible { 255 | 256 | /// A textual representation of this instance. 257 | public var description: String { 258 | "Analysis(\"\(input)\")" 259 | } 260 | 261 | /// A representation of the string that is suitable for debugging. 262 | public var debugDescription: String { 263 | dump(description) 264 | } 265 | } 266 | 267 | extension Analysis: ExpressibleByStringLiteral { 268 | public typealias UnicodeScalarLiteralType = StringLiteralType 269 | public typealias ExtendedGraphemeClusterLiteralType = StringLiteralType 270 | 271 | public init(stringLiteral value: StringLiteralType) { 272 | self.init(of: value) 273 | } 274 | 275 | public init(extendedGraphemeClusterLiteral value: ExtendedGraphemeClusterLiteralType) { 276 | self.init(of: value) 277 | } 278 | 279 | public init(unicodeScalarLiteral value: UnicodeScalarLiteralType) { 280 | self.init(of: value) 281 | } 282 | } 283 | -------------------------------------------------------------------------------- /Analysis/Classes/Character+Casing.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | internal extension Character { 4 | 5 | func lowercased() -> Character { 6 | return Character(String(describing: self).lowercased()) 7 | } 8 | 9 | func uppercased() -> Character { 10 | return Character(String(describing: self).uppercased()) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Analysis/Classes/Dictionary+Sorting.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | /// The sort option of the dictionary. This is either `.key` or `.value`. 4 | public enum SortOption { 5 | case key 6 | case value 7 | } 8 | 9 | /// The sort direction used when sorting. This is either `.ascending` or `.descending`. 10 | public enum SortDirection { 11 | case ascending 12 | case descending 13 | } 14 | 15 | extension Dictionary where Key: Comparable, Value: Comparable { 16 | 17 | /// Sorts the dictionary with the specified `SortOption` and `SortDirection`. 18 | /// 19 | /// - Parameter option: The sort option to use. This is either by `.key` or by `.value`. 20 | /// - Parameter direction: The sort direction to use. Defaults to `.ascending`. 21 | /// 22 | /// - Returns: The sorted `Dictionary` as an array with `(key: Key, value: Value)` pairs. 23 | public func sorted(by option: SortOption, direction: SortDirection = .ascending) -> [(key: Key, value: Value)] { 24 | switch option { 25 | case .key: 26 | switch direction { 27 | case .ascending: 28 | return sorted { $0.key < $1.key } 29 | case .descending: 30 | return sorted { $0.key > $1.key } 31 | } 32 | case .value: 33 | switch direction { 34 | case .ascending: 35 | return sorted { $0.value < $1.value } 36 | case .descending: 37 | return sorted { $0.value > $1.value } 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Analysis/Classes/String+Analysis.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension String { 4 | 5 | /// Returns an analysis of `self`. 6 | public func analysed() -> Analysis { 7 | return Analysis(of: self) 8 | } 9 | 10 | internal var lines: [String] { 11 | var lines: [String] = [] 12 | self.enumerateLines { line, _ in 13 | lines.append(line) 14 | } 15 | return lines 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Analysis/Classes/SyllableCounter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SyllableCounter.swift 3 | // 4 | // Created by Warren Freitag on 2/14/16. 5 | // Copyright © 2016 Warren Freitag. All rights reserved. 6 | // Licensed under the Apache 2.0 License. 7 | // 8 | // Adapted from a Java implementation created by Hugo "m09" Mougard. 9 | // https://github.com/m09/syllable-counter 10 | // 11 | 12 | import Foundation 13 | 14 | public class SyllableCounter { 15 | 16 | // MARK: - Shared instance 17 | 18 | public static let shared = SyllableCounter() 19 | 20 | // MARK: - Private properties 21 | 22 | private var exceptions: [String: Int] = [ 23 | "brutes": 1, 24 | "chummed": 1, 25 | "flapped": 1, 26 | "foamed": 1, 27 | "gaped": 1, 28 | "h'm": 1, 29 | "lb": 1, 30 | "mimes": 1, 31 | "ms": 1, 32 | "peeped": 1, 33 | "sheered": 1, 34 | "st": 1, 35 | "queue": 1, 36 | "none": 1, 37 | "leaves": 1, 38 | "awesome": 2, 39 | "60": 2, 40 | "capered": 2, 41 | "caressed": 2, 42 | "clattered": 2, 43 | "deafened": 2, 44 | "dr": 2, 45 | "effaced": 2, 46 | "effaces": 2, 47 | "fringed": 2, 48 | "greyish": 2, 49 | "jr": 2, 50 | "mangroves": 2, 51 | "messieurs": 2, 52 | "motioned": 2, 53 | "moustaches": 2, 54 | "mr": 2, 55 | "mrs": 2, 56 | "pencilled": 2, 57 | "poleman": 2, 58 | "quivered": 2, 59 | "reclined": 2, 60 | "shivered": 2, 61 | "sidespring": 2, 62 | "slandered": 2, 63 | "sombre": 2, 64 | "sr": 2, 65 | "stammered": 2, 66 | "suavely": 2, 67 | "tottered": 2, 68 | "trespassed": 2, 69 | "truckle": 2, 70 | "unstained": 2, 71 | "therefore": 2, 72 | "businesses": 3, 73 | "bottleful": 3, 74 | "discoloured": 3, 75 | "disinterred": 3, 76 | "hemispheres": 3, 77 | "manoeuvred": 3, 78 | "sepulchre": 3, 79 | "shamefully": 3, 80 | "unexpressed": 3, 81 | "veriest": 3, 82 | "wyoming": 3, 83 | "etc": 4, 84 | "sailmaker": 4, 85 | "satiated": 4, 86 | "sententiously": 4, 87 | "particularized": 5, 88 | "unostentatious": 5, 89 | "propitiatory": 6, 90 | ] 91 | 92 | private var addSyllables: [NSRegularExpression]! 93 | private var subSyllables: [NSRegularExpression]! 94 | 95 | private let vowels: Set = ["a", "e", "i", "o", "u", "y"] 96 | 97 | // MARK: - Error enum 98 | 99 | private enum SyllableCounterError: Error { 100 | case badRegex(String) 101 | case badExceptionsData(String) 102 | } 103 | 104 | // MARK: - Constructors 105 | 106 | public init() { 107 | do { 108 | try populateAddSyllables() 109 | try populateSubSyllables() 110 | } 111 | catch SyllableCounterError.badRegex(let pattern) { 112 | print("Bad Regex pattern: \(pattern)") 113 | } 114 | catch SyllableCounterError.badExceptionsData(let info) { 115 | print("Problem parsing exceptions dataset: \(info)") 116 | } 117 | catch { 118 | print("An unexpected error occured while initializing the syllable counter.") 119 | } 120 | } 121 | 122 | // MARK: - Setup 123 | 124 | private func populateAddSyllables() throws { 125 | try addSyllables = buildRegexes(forPatterns: [ 126 | "ia", "riet", "dien", "iu", "io", "ii", 127 | "[aeiouy]bl$", "mbl$", "tl$", "sl$", "[aeiou]{3}", 128 | "^mc", "ism$", "(.)(?!\\1)([aeiouy])\\2l$", "[^l]llien", "^coad.", 129 | "^coag.", "^coal.", "^coax.", "(.)(?!\\1)[gq]ua(.)(?!\\2)[aeiou]", "dnt$", 130 | "thm$", "ier$", "iest$", "[^aeiou][aeiouy]ing$"]) 131 | } 132 | 133 | private func populateSubSyllables() throws { 134 | try subSyllables = buildRegexes(forPatterns: [ 135 | "cial", "cian", "tia", "cius", "cious", 136 | "gui", "ion", "iou", "sia$", ".ely$", 137 | "ves$", "geous$", "gious$", "[^aeiou]eful$", ".red$"]) 138 | } 139 | 140 | private func buildRegexes(forPatterns patterns: [String]) throws -> [NSRegularExpression] { 141 | return try patterns.map { pattern -> NSRegularExpression in 142 | do { 143 | let regex = try NSRegularExpression(pattern: pattern, options: [.caseInsensitive, .anchorsMatchLines]) 144 | return regex 145 | } 146 | catch { 147 | throw SyllableCounterError.badRegex(pattern) 148 | } 149 | } 150 | } 151 | 152 | // MARK: - Public methods 153 | 154 | internal func count(word: String) -> Int { 155 | if word.count <= 1 { 156 | return word.count 157 | } 158 | 159 | var mutatedWord = word.lowercased(with: Locale(identifier: "en_US")).trimmingCharacters(in: .punctuationCharacters) 160 | 161 | if let exceptionValue = exceptions[mutatedWord] { 162 | return exceptionValue 163 | } 164 | 165 | if mutatedWord.last == "e" { 166 | mutatedWord = String(mutatedWord.dropLast()) 167 | } 168 | 169 | var count = 0 170 | var previousIsVowel = false 171 | 172 | for character in mutatedWord { 173 | let isVowel = vowels.contains(character) 174 | if isVowel && !previousIsVowel { 175 | count += 1 176 | } 177 | previousIsVowel = isVowel 178 | } 179 | 180 | for pattern in addSyllables { 181 | let matches = pattern.matches(in: mutatedWord, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: NSRange(location: 0, length: mutatedWord.count)) 182 | if !matches.isEmpty { 183 | count += 1 184 | } 185 | } 186 | 187 | for pattern in subSyllables { 188 | let matches = pattern.matches(in: mutatedWord, options: NSRegularExpression.MatchingOptions(rawValue: 0), range: NSRange(location: 0, length: mutatedWord.count)) 189 | if !matches.isEmpty { 190 | count -= 1 191 | } 192 | } 193 | 194 | return (count > 0) ? count : 1 195 | } 196 | } 197 | 198 | extension String { 199 | 200 | internal var syllables: Int { 201 | return SyllableCounter.shared.count(word: self) 202 | } 203 | } 204 | -------------------------------------------------------------------------------- /Changelog.md: -------------------------------------------------------------------------------- 1 | # next 2 | 3 | # [0.5.0](https://github.com/BasThomas/Analysis/releases/tag/0.4.0) 4 | 5 | - Updated to Swift 5.1. 6 | 7 | # [0.4.0](https://github.com/BasThomas/Analysis/releases/tag/0.4.0) 8 | 9 | - **Breaking**: Nested `LengthOption` under the `Analysis` namespace. If it is accessed without type inference, 10 | such as like `LengthOption.word`, you'll need to use `Analysis.LengthOption.word` going forward. Type inference will continue 11 | to work as expected, allowing you to use `.word` or `.sentence`. 12 | - **Breaking**: Updated to Swift 5. 13 | 14 | # [0.3.0](https://github.com/BasThomas/Analysis/releases/tag/0.3.0) 15 | 16 | - Updated to Swift 4.2. 17 | 18 | # [0.2.0](https://github.com/BasThomas/Analysis/releases/tag/0.2.0) 19 | 20 | - Added `syllableCount()`, which counts the total amount of syllables of the `input`. 21 | - Added `wordSyllables()`, which returns the syllables of every unique word. 22 | - Added `fleschReadingEase()`, which calculates the [Flesch reading ease score](https://en.wikipedia.org/wiki/Flesch–Kincaid_readability_tests#Flesch_reading_ease). 23 | - Added `fleschKincaidGradeLevel()`, which calculates the [Flesch-Kincaid grade level](https://en.wikipedia.org/wiki/Flesch–Kincaid_readability_tests#Flesch.E2.80.93Kincaid_grade_level). 24 | - Dropped the deployment target from iOS `8.3` to `8.0`. 25 | 26 | # [0.1.0](https://github.com/BasThomas/Analysis/releases/tag/0.1.0) 27 | Initial release. 28 | -------------------------------------------------------------------------------- /Example/Analysis.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 4926B8801DFAB4E30021C0B1 /* CharacterCasingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4926B87F1DFAB4E30021C0B1 /* CharacterCasingTests.swift */; }; 11 | 4978479C1DD87E4A003CFFBB /* Analysis.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4978479B1DD87E4A003CFFBB /* Analysis.storyboard */; }; 12 | 4978479E1DD88148003CFFBB /* AnalysisTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4978479D1DD88148003CFFBB /* AnalysisTableViewController.swift */; }; 13 | 4985BEC11DF1959500B36F51 /* UIViewController+Safari.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4985BEC01DF1959500B36F51 /* UIViewController+Safari.swift */; }; 14 | 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD51AFB9204008FA782 /* AppDelegate.swift */; }; 15 | 607FACD81AFB9204008FA782 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD71AFB9204008FA782 /* ViewController.swift */; }; 16 | 607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 607FACD91AFB9204008FA782 /* Main.storyboard */; }; 17 | 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDC1AFB9204008FA782 /* Images.xcassets */; }; 18 | 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */; }; 19 | 607FACEC1AFB9204008FA782 /* AnalysisTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACEB1AFB9204008FA782 /* AnalysisTests.swift */; }; 20 | A71F471EAB2E518BCB5BA4FA /* Pods_Analysis_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 079C311AB87F9BCEED7E8427 /* Pods_Analysis_Tests.framework */; }; 21 | A90BA7EF5C025664F550BA49 /* Pods_Analysis_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2E00E999478F1BFC6D31CAD6 /* Pods_Analysis_Example.framework */; }; 22 | /* End PBXBuildFile section */ 23 | 24 | /* Begin PBXContainerItemProxy section */ 25 | 607FACE61AFB9204008FA782 /* PBXContainerItemProxy */ = { 26 | isa = PBXContainerItemProxy; 27 | containerPortal = 607FACC81AFB9204008FA782 /* Project object */; 28 | proxyType = 1; 29 | remoteGlobalIDString = 607FACCF1AFB9204008FA782; 30 | remoteInfo = Analysis; 31 | }; 32 | /* End PBXContainerItemProxy section */ 33 | 34 | /* Begin PBXFileReference section */ 35 | 057B64DE3E9E6A24F7FFE95C /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; 36 | 079C311AB87F9BCEED7E8427 /* Pods_Analysis_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Analysis_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 37 | 2E00E999478F1BFC6D31CAD6 /* Pods_Analysis_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Analysis_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 38 | 45E49C4F9720F93380BF3A10 /* Pods-Analysis_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Analysis_Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Analysis_Tests/Pods-Analysis_Tests.debug.xcconfig"; sourceTree = ""; }; 39 | 4926B87F1DFAB4E30021C0B1 /* CharacterCasingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CharacterCasingTests.swift; sourceTree = ""; }; 40 | 4978479B1DD87E4A003CFFBB /* Analysis.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Analysis.storyboard; sourceTree = ""; }; 41 | 4978479D1DD88148003CFFBB /* AnalysisTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AnalysisTableViewController.swift; sourceTree = ""; }; 42 | 4985BEC01DF1959500B36F51 /* UIViewController+Safari.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewController+Safari.swift"; sourceTree = ""; }; 43 | 607FACD01AFB9204008FA782 /* Analysis_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Analysis_Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 44 | 607FACD41AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 45 | 607FACD51AFB9204008FA782 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 46 | 607FACD71AFB9204008FA782 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 47 | 607FACDA1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 48 | 607FACDC1AFB9204008FA782 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 49 | 607FACDF1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 50 | 607FACE51AFB9204008FA782 /* Analysis_Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Analysis_Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 51 | 607FACEA1AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 52 | 607FACEB1AFB9204008FA782 /* AnalysisTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalysisTests.swift; sourceTree = ""; }; 53 | 7C9C2D06E98A8917E118492B /* Pods-Analysis_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Analysis_Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-Analysis_Tests/Pods-Analysis_Tests.release.xcconfig"; sourceTree = ""; }; 54 | B0B3599D76907561E0BB232E /* Analysis.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = Analysis.podspec; path = ../Analysis.podspec; sourceTree = ""; }; 55 | DE84EF1912913AEE64540651 /* Pods-Analysis_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Analysis_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-Analysis_Example/Pods-Analysis_Example.release.xcconfig"; sourceTree = ""; }; 56 | E2191E54DA43E7984AA43E68 /* Pods-Analysis_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Analysis_Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Analysis_Example/Pods-Analysis_Example.debug.xcconfig"; sourceTree = ""; }; 57 | F8692656D56C24435D89A72E /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; 58 | /* End PBXFileReference section */ 59 | 60 | /* Begin PBXFrameworksBuildPhase section */ 61 | 607FACCD1AFB9204008FA782 /* Frameworks */ = { 62 | isa = PBXFrameworksBuildPhase; 63 | buildActionMask = 2147483647; 64 | files = ( 65 | A90BA7EF5C025664F550BA49 /* Pods_Analysis_Example.framework in Frameworks */, 66 | ); 67 | runOnlyForDeploymentPostprocessing = 0; 68 | }; 69 | 607FACE21AFB9204008FA782 /* Frameworks */ = { 70 | isa = PBXFrameworksBuildPhase; 71 | buildActionMask = 2147483647; 72 | files = ( 73 | A71F471EAB2E518BCB5BA4FA /* Pods_Analysis_Tests.framework in Frameworks */, 74 | ); 75 | runOnlyForDeploymentPostprocessing = 0; 76 | }; 77 | /* End PBXFrameworksBuildPhase section */ 78 | 79 | /* Begin PBXGroup section */ 80 | 15E3E7AC787378199B796EB1 /* Pods */ = { 81 | isa = PBXGroup; 82 | children = ( 83 | E2191E54DA43E7984AA43E68 /* Pods-Analysis_Example.debug.xcconfig */, 84 | DE84EF1912913AEE64540651 /* Pods-Analysis_Example.release.xcconfig */, 85 | 45E49C4F9720F93380BF3A10 /* Pods-Analysis_Tests.debug.xcconfig */, 86 | 7C9C2D06E98A8917E118492B /* Pods-Analysis_Tests.release.xcconfig */, 87 | ); 88 | name = Pods; 89 | sourceTree = ""; 90 | }; 91 | 607FACC71AFB9204008FA782 = { 92 | isa = PBXGroup; 93 | children = ( 94 | 607FACF51AFB993E008FA782 /* Podspec Metadata */, 95 | 607FACD21AFB9204008FA782 /* Example for Analysis */, 96 | 607FACE81AFB9204008FA782 /* Tests */, 97 | 607FACD11AFB9204008FA782 /* Products */, 98 | 15E3E7AC787378199B796EB1 /* Pods */, 99 | B304DC813B6481930221C272 /* Frameworks */, 100 | ); 101 | sourceTree = ""; 102 | }; 103 | 607FACD11AFB9204008FA782 /* Products */ = { 104 | isa = PBXGroup; 105 | children = ( 106 | 607FACD01AFB9204008FA782 /* Analysis_Example.app */, 107 | 607FACE51AFB9204008FA782 /* Analysis_Tests.xctest */, 108 | ); 109 | name = Products; 110 | sourceTree = ""; 111 | }; 112 | 607FACD21AFB9204008FA782 /* Example for Analysis */ = { 113 | isa = PBXGroup; 114 | children = ( 115 | 607FACD51AFB9204008FA782 /* AppDelegate.swift */, 116 | 607FACD71AFB9204008FA782 /* ViewController.swift */, 117 | 4978479D1DD88148003CFFBB /* AnalysisTableViewController.swift */, 118 | 4985BEC01DF1959500B36F51 /* UIViewController+Safari.swift */, 119 | 607FACD91AFB9204008FA782 /* Main.storyboard */, 120 | 4978479B1DD87E4A003CFFBB /* Analysis.storyboard */, 121 | 607FACDC1AFB9204008FA782 /* Images.xcassets */, 122 | 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */, 123 | 607FACD31AFB9204008FA782 /* Supporting Files */, 124 | ); 125 | name = "Example for Analysis"; 126 | path = Analysis; 127 | sourceTree = ""; 128 | }; 129 | 607FACD31AFB9204008FA782 /* Supporting Files */ = { 130 | isa = PBXGroup; 131 | children = ( 132 | 607FACD41AFB9204008FA782 /* Info.plist */, 133 | ); 134 | name = "Supporting Files"; 135 | sourceTree = ""; 136 | }; 137 | 607FACE81AFB9204008FA782 /* Tests */ = { 138 | isa = PBXGroup; 139 | children = ( 140 | 607FACEB1AFB9204008FA782 /* AnalysisTests.swift */, 141 | 4926B87F1DFAB4E30021C0B1 /* CharacterCasingTests.swift */, 142 | 607FACE91AFB9204008FA782 /* Supporting Files */, 143 | ); 144 | path = Tests; 145 | sourceTree = ""; 146 | }; 147 | 607FACE91AFB9204008FA782 /* Supporting Files */ = { 148 | isa = PBXGroup; 149 | children = ( 150 | 607FACEA1AFB9204008FA782 /* Info.plist */, 151 | ); 152 | name = "Supporting Files"; 153 | sourceTree = ""; 154 | }; 155 | 607FACF51AFB993E008FA782 /* Podspec Metadata */ = { 156 | isa = PBXGroup; 157 | children = ( 158 | B0B3599D76907561E0BB232E /* Analysis.podspec */, 159 | 057B64DE3E9E6A24F7FFE95C /* README.md */, 160 | F8692656D56C24435D89A72E /* LICENSE */, 161 | ); 162 | name = "Podspec Metadata"; 163 | sourceTree = ""; 164 | }; 165 | B304DC813B6481930221C272 /* Frameworks */ = { 166 | isa = PBXGroup; 167 | children = ( 168 | 2E00E999478F1BFC6D31CAD6 /* Pods_Analysis_Example.framework */, 169 | 079C311AB87F9BCEED7E8427 /* Pods_Analysis_Tests.framework */, 170 | ); 171 | name = Frameworks; 172 | sourceTree = ""; 173 | }; 174 | /* End PBXGroup section */ 175 | 176 | /* Begin PBXNativeTarget section */ 177 | 607FACCF1AFB9204008FA782 /* Analysis_Example */ = { 178 | isa = PBXNativeTarget; 179 | buildConfigurationList = 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "Analysis_Example" */; 180 | buildPhases = ( 181 | 8E99E9A2B4D2FE8196C886FC /* [CP] Check Pods Manifest.lock */, 182 | 607FACCC1AFB9204008FA782 /* Sources */, 183 | 607FACCD1AFB9204008FA782 /* Frameworks */, 184 | 607FACCE1AFB9204008FA782 /* Resources */, 185 | B726F347928269BC02F4A2CC /* [CP] Embed Pods Frameworks */, 186 | ); 187 | buildRules = ( 188 | ); 189 | dependencies = ( 190 | ); 191 | name = Analysis_Example; 192 | productName = Analysis; 193 | productReference = 607FACD01AFB9204008FA782 /* Analysis_Example.app */; 194 | productType = "com.apple.product-type.application"; 195 | }; 196 | 607FACE41AFB9204008FA782 /* Analysis_Tests */ = { 197 | isa = PBXNativeTarget; 198 | buildConfigurationList = 607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "Analysis_Tests" */; 199 | buildPhases = ( 200 | 9720F7DDF7A6499D6392BC2D /* [CP] Check Pods Manifest.lock */, 201 | 607FACE11AFB9204008FA782 /* Sources */, 202 | 607FACE21AFB9204008FA782 /* Frameworks */, 203 | 607FACE31AFB9204008FA782 /* Resources */, 204 | ); 205 | buildRules = ( 206 | ); 207 | dependencies = ( 208 | 607FACE71AFB9204008FA782 /* PBXTargetDependency */, 209 | ); 210 | name = Analysis_Tests; 211 | productName = Tests; 212 | productReference = 607FACE51AFB9204008FA782 /* Analysis_Tests.xctest */; 213 | productType = "com.apple.product-type.bundle.unit-test"; 214 | }; 215 | /* End PBXNativeTarget section */ 216 | 217 | /* Begin PBXProject section */ 218 | 607FACC81AFB9204008FA782 /* Project object */ = { 219 | isa = PBXProject; 220 | attributes = { 221 | LastSwiftUpdateCheck = 0720; 222 | LastUpgradeCheck = 1000; 223 | ORGANIZATIONNAME = "Bas Broek"; 224 | TargetAttributes = { 225 | 607FACCF1AFB9204008FA782 = { 226 | CreatedOnToolsVersion = 6.3.1; 227 | LastSwiftMigration = 1020; 228 | }; 229 | 607FACE41AFB9204008FA782 = { 230 | CreatedOnToolsVersion = 6.3.1; 231 | LastSwiftMigration = 1020; 232 | TestTargetID = 607FACCF1AFB9204008FA782; 233 | }; 234 | }; 235 | }; 236 | buildConfigurationList = 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "Analysis" */; 237 | compatibilityVersion = "Xcode 3.2"; 238 | developmentRegion = en; 239 | hasScannedForEncodings = 0; 240 | knownRegions = ( 241 | en, 242 | Base, 243 | ); 244 | mainGroup = 607FACC71AFB9204008FA782; 245 | productRefGroup = 607FACD11AFB9204008FA782 /* Products */; 246 | projectDirPath = ""; 247 | projectRoot = ""; 248 | targets = ( 249 | 607FACCF1AFB9204008FA782 /* Analysis_Example */, 250 | 607FACE41AFB9204008FA782 /* Analysis_Tests */, 251 | ); 252 | }; 253 | /* End PBXProject section */ 254 | 255 | /* Begin PBXResourcesBuildPhase section */ 256 | 607FACCE1AFB9204008FA782 /* Resources */ = { 257 | isa = PBXResourcesBuildPhase; 258 | buildActionMask = 2147483647; 259 | files = ( 260 | 607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */, 261 | 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */, 262 | 4978479C1DD87E4A003CFFBB /* Analysis.storyboard in Resources */, 263 | 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */, 264 | ); 265 | runOnlyForDeploymentPostprocessing = 0; 266 | }; 267 | 607FACE31AFB9204008FA782 /* Resources */ = { 268 | isa = PBXResourcesBuildPhase; 269 | buildActionMask = 2147483647; 270 | files = ( 271 | ); 272 | runOnlyForDeploymentPostprocessing = 0; 273 | }; 274 | /* End PBXResourcesBuildPhase section */ 275 | 276 | /* Begin PBXShellScriptBuildPhase section */ 277 | 8E99E9A2B4D2FE8196C886FC /* [CP] Check Pods Manifest.lock */ = { 278 | isa = PBXShellScriptBuildPhase; 279 | buildActionMask = 2147483647; 280 | files = ( 281 | ); 282 | inputPaths = ( 283 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 284 | "${PODS_ROOT}/Manifest.lock", 285 | ); 286 | name = "[CP] Check Pods Manifest.lock"; 287 | outputPaths = ( 288 | "$(DERIVED_FILE_DIR)/Pods-Analysis_Example-checkManifestLockResult.txt", 289 | ); 290 | runOnlyForDeploymentPostprocessing = 0; 291 | shellPath = /bin/sh; 292 | 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"; 293 | showEnvVarsInLog = 0; 294 | }; 295 | 9720F7DDF7A6499D6392BC2D /* [CP] Check Pods Manifest.lock */ = { 296 | isa = PBXShellScriptBuildPhase; 297 | buildActionMask = 2147483647; 298 | files = ( 299 | ); 300 | inputPaths = ( 301 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 302 | "${PODS_ROOT}/Manifest.lock", 303 | ); 304 | name = "[CP] Check Pods Manifest.lock"; 305 | outputPaths = ( 306 | "$(DERIVED_FILE_DIR)/Pods-Analysis_Tests-checkManifestLockResult.txt", 307 | ); 308 | runOnlyForDeploymentPostprocessing = 0; 309 | shellPath = /bin/sh; 310 | 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"; 311 | showEnvVarsInLog = 0; 312 | }; 313 | B726F347928269BC02F4A2CC /* [CP] Embed Pods Frameworks */ = { 314 | isa = PBXShellScriptBuildPhase; 315 | buildActionMask = 2147483647; 316 | files = ( 317 | ); 318 | inputPaths = ( 319 | "${PODS_ROOT}/Target Support Files/Pods-Analysis_Example/Pods-Analysis_Example-frameworks.sh", 320 | "${BUILT_PRODUCTS_DIR}/Analysis/Analysis.framework", 321 | ); 322 | name = "[CP] Embed Pods Frameworks"; 323 | outputPaths = ( 324 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Analysis.framework", 325 | ); 326 | runOnlyForDeploymentPostprocessing = 0; 327 | shellPath = /bin/sh; 328 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Analysis_Example/Pods-Analysis_Example-frameworks.sh\"\n"; 329 | showEnvVarsInLog = 0; 330 | }; 331 | /* End PBXShellScriptBuildPhase section */ 332 | 333 | /* Begin PBXSourcesBuildPhase section */ 334 | 607FACCC1AFB9204008FA782 /* Sources */ = { 335 | isa = PBXSourcesBuildPhase; 336 | buildActionMask = 2147483647; 337 | files = ( 338 | 607FACD81AFB9204008FA782 /* ViewController.swift in Sources */, 339 | 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */, 340 | 4985BEC11DF1959500B36F51 /* UIViewController+Safari.swift in Sources */, 341 | 4978479E1DD88148003CFFBB /* AnalysisTableViewController.swift in Sources */, 342 | ); 343 | runOnlyForDeploymentPostprocessing = 0; 344 | }; 345 | 607FACE11AFB9204008FA782 /* Sources */ = { 346 | isa = PBXSourcesBuildPhase; 347 | buildActionMask = 2147483647; 348 | files = ( 349 | 4926B8801DFAB4E30021C0B1 /* CharacterCasingTests.swift in Sources */, 350 | 607FACEC1AFB9204008FA782 /* AnalysisTests.swift in Sources */, 351 | ); 352 | runOnlyForDeploymentPostprocessing = 0; 353 | }; 354 | /* End PBXSourcesBuildPhase section */ 355 | 356 | /* Begin PBXTargetDependency section */ 357 | 607FACE71AFB9204008FA782 /* PBXTargetDependency */ = { 358 | isa = PBXTargetDependency; 359 | target = 607FACCF1AFB9204008FA782 /* Analysis_Example */; 360 | targetProxy = 607FACE61AFB9204008FA782 /* PBXContainerItemProxy */; 361 | }; 362 | /* End PBXTargetDependency section */ 363 | 364 | /* Begin PBXVariantGroup section */ 365 | 607FACD91AFB9204008FA782 /* Main.storyboard */ = { 366 | isa = PBXVariantGroup; 367 | children = ( 368 | 607FACDA1AFB9204008FA782 /* Base */, 369 | ); 370 | name = Main.storyboard; 371 | sourceTree = ""; 372 | }; 373 | 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */ = { 374 | isa = PBXVariantGroup; 375 | children = ( 376 | 607FACDF1AFB9204008FA782 /* Base */, 377 | ); 378 | name = LaunchScreen.xib; 379 | sourceTree = ""; 380 | }; 381 | /* End PBXVariantGroup section */ 382 | 383 | /* Begin XCBuildConfiguration section */ 384 | 607FACED1AFB9204008FA782 /* Debug */ = { 385 | isa = XCBuildConfiguration; 386 | buildSettings = { 387 | ALWAYS_SEARCH_USER_PATHS = NO; 388 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 389 | CLANG_CXX_LIBRARY = "libc++"; 390 | CLANG_ENABLE_MODULES = YES; 391 | CLANG_ENABLE_OBJC_ARC = YES; 392 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 393 | CLANG_WARN_BOOL_CONVERSION = YES; 394 | CLANG_WARN_COMMA = YES; 395 | CLANG_WARN_CONSTANT_CONVERSION = YES; 396 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 397 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 398 | CLANG_WARN_EMPTY_BODY = YES; 399 | CLANG_WARN_ENUM_CONVERSION = YES; 400 | CLANG_WARN_INFINITE_RECURSION = YES; 401 | CLANG_WARN_INT_CONVERSION = YES; 402 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 403 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 404 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 405 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 406 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 407 | CLANG_WARN_STRICT_PROTOTYPES = YES; 408 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 409 | CLANG_WARN_UNREACHABLE_CODE = YES; 410 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 411 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 412 | COPY_PHASE_STRIP = NO; 413 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 414 | ENABLE_STRICT_OBJC_MSGSEND = YES; 415 | ENABLE_TESTABILITY = YES; 416 | GCC_C_LANGUAGE_STANDARD = gnu99; 417 | GCC_DYNAMIC_NO_PIC = NO; 418 | GCC_NO_COMMON_BLOCKS = YES; 419 | GCC_OPTIMIZATION_LEVEL = 0; 420 | GCC_PREPROCESSOR_DEFINITIONS = ( 421 | "DEBUG=1", 422 | "$(inherited)", 423 | ); 424 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 425 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 426 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 427 | GCC_WARN_UNDECLARED_SELECTOR = YES; 428 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 429 | GCC_WARN_UNUSED_FUNCTION = YES; 430 | GCC_WARN_UNUSED_VARIABLE = YES; 431 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 432 | MTL_ENABLE_DEBUG_INFO = YES; 433 | ONLY_ACTIVE_ARCH = YES; 434 | SDKROOT = iphoneos; 435 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 436 | }; 437 | name = Debug; 438 | }; 439 | 607FACEE1AFB9204008FA782 /* Release */ = { 440 | isa = XCBuildConfiguration; 441 | buildSettings = { 442 | ALWAYS_SEARCH_USER_PATHS = NO; 443 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 444 | CLANG_CXX_LIBRARY = "libc++"; 445 | CLANG_ENABLE_MODULES = YES; 446 | CLANG_ENABLE_OBJC_ARC = YES; 447 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 448 | CLANG_WARN_BOOL_CONVERSION = YES; 449 | CLANG_WARN_COMMA = YES; 450 | CLANG_WARN_CONSTANT_CONVERSION = YES; 451 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 452 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 453 | CLANG_WARN_EMPTY_BODY = YES; 454 | CLANG_WARN_ENUM_CONVERSION = YES; 455 | CLANG_WARN_INFINITE_RECURSION = YES; 456 | CLANG_WARN_INT_CONVERSION = YES; 457 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 458 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 459 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 460 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 461 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 462 | CLANG_WARN_STRICT_PROTOTYPES = YES; 463 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 464 | CLANG_WARN_UNREACHABLE_CODE = YES; 465 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 466 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 467 | COPY_PHASE_STRIP = NO; 468 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 469 | ENABLE_NS_ASSERTIONS = NO; 470 | ENABLE_STRICT_OBJC_MSGSEND = YES; 471 | GCC_C_LANGUAGE_STANDARD = gnu99; 472 | GCC_NO_COMMON_BLOCKS = YES; 473 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 474 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 475 | GCC_WARN_UNDECLARED_SELECTOR = YES; 476 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 477 | GCC_WARN_UNUSED_FUNCTION = YES; 478 | GCC_WARN_UNUSED_VARIABLE = YES; 479 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 480 | MTL_ENABLE_DEBUG_INFO = NO; 481 | SDKROOT = iphoneos; 482 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 483 | VALIDATE_PRODUCT = YES; 484 | }; 485 | name = Release; 486 | }; 487 | 607FACF01AFB9204008FA782 /* Debug */ = { 488 | isa = XCBuildConfiguration; 489 | baseConfigurationReference = E2191E54DA43E7984AA43E68 /* Pods-Analysis_Example.debug.xcconfig */; 490 | buildSettings = { 491 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 492 | INFOPLIST_FILE = Analysis/Info.plist; 493 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 494 | MODULE_NAME = ExampleApp; 495 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; 496 | PRODUCT_NAME = "$(TARGET_NAME)"; 497 | SWIFT_VERSION = 5.0; 498 | }; 499 | name = Debug; 500 | }; 501 | 607FACF11AFB9204008FA782 /* Release */ = { 502 | isa = XCBuildConfiguration; 503 | baseConfigurationReference = DE84EF1912913AEE64540651 /* Pods-Analysis_Example.release.xcconfig */; 504 | buildSettings = { 505 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 506 | INFOPLIST_FILE = Analysis/Info.plist; 507 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 508 | MODULE_NAME = ExampleApp; 509 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)"; 510 | PRODUCT_NAME = "$(TARGET_NAME)"; 511 | SWIFT_VERSION = 5.0; 512 | }; 513 | name = Release; 514 | }; 515 | 607FACF31AFB9204008FA782 /* Debug */ = { 516 | isa = XCBuildConfiguration; 517 | baseConfigurationReference = 45E49C4F9720F93380BF3A10 /* Pods-Analysis_Tests.debug.xcconfig */; 518 | buildSettings = { 519 | FRAMEWORK_SEARCH_PATHS = ( 520 | "$(SDKROOT)/Developer/Library/Frameworks", 521 | "$(inherited)", 522 | ); 523 | GCC_PREPROCESSOR_DEFINITIONS = ( 524 | "DEBUG=1", 525 | "$(inherited)", 526 | ); 527 | INFOPLIST_FILE = Tests/Info.plist; 528 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 529 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)"; 530 | PRODUCT_NAME = "$(TARGET_NAME)"; 531 | SWIFT_VERSION = 5.0; 532 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Analysis_Example.app/Analysis_Example"; 533 | }; 534 | name = Debug; 535 | }; 536 | 607FACF41AFB9204008FA782 /* Release */ = { 537 | isa = XCBuildConfiguration; 538 | baseConfigurationReference = 7C9C2D06E98A8917E118492B /* Pods-Analysis_Tests.release.xcconfig */; 539 | buildSettings = { 540 | FRAMEWORK_SEARCH_PATHS = ( 541 | "$(SDKROOT)/Developer/Library/Frameworks", 542 | "$(inherited)", 543 | ); 544 | INFOPLIST_FILE = Tests/Info.plist; 545 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 546 | PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.$(PRODUCT_NAME:rfc1034identifier)"; 547 | PRODUCT_NAME = "$(TARGET_NAME)"; 548 | SWIFT_VERSION = 5.0; 549 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Analysis_Example.app/Analysis_Example"; 550 | }; 551 | name = Release; 552 | }; 553 | /* End XCBuildConfiguration section */ 554 | 555 | /* Begin XCConfigurationList section */ 556 | 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "Analysis" */ = { 557 | isa = XCConfigurationList; 558 | buildConfigurations = ( 559 | 607FACED1AFB9204008FA782 /* Debug */, 560 | 607FACEE1AFB9204008FA782 /* Release */, 561 | ); 562 | defaultConfigurationIsVisible = 0; 563 | defaultConfigurationName = Release; 564 | }; 565 | 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "Analysis_Example" */ = { 566 | isa = XCConfigurationList; 567 | buildConfigurations = ( 568 | 607FACF01AFB9204008FA782 /* Debug */, 569 | 607FACF11AFB9204008FA782 /* Release */, 570 | ); 571 | defaultConfigurationIsVisible = 0; 572 | defaultConfigurationName = Release; 573 | }; 574 | 607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "Analysis_Tests" */ = { 575 | isa = XCConfigurationList; 576 | buildConfigurations = ( 577 | 607FACF31AFB9204008FA782 /* Debug */, 578 | 607FACF41AFB9204008FA782 /* Release */, 579 | ); 580 | defaultConfigurationIsVisible = 0; 581 | defaultConfigurationName = Release; 582 | }; 583 | /* End XCConfigurationList section */ 584 | }; 585 | rootObject = 607FACC81AFB9204008FA782 /* Project object */; 586 | } 587 | -------------------------------------------------------------------------------- /Example/Analysis.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/Analysis.xcodeproj/xcshareddata/xcschemes/Analysis-Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 45 | 46 | 48 | 54 | 55 | 56 | 57 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 79 | 81 | 87 | 88 | 89 | 90 | 91 | 92 | 98 | 100 | 106 | 107 | 108 | 109 | 111 | 112 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /Example/Analysis.xcodeproj/xcshareddata/xcschemes/Analysis_Tests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 45 | 46 | 48 | 54 | 55 | 56 | 57 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 79 | 80 | 86 | 87 | 88 | 89 | 90 | 91 | 97 | 98 | 104 | 105 | 106 | 107 | 109 | 110 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /Example/Analysis.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/Analysis.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/Analysis.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Example/Analysis/Analysis.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 | 29 | 30 | 37 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 61 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 85 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 109 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 133 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 157 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 185 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 209 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 233 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 261 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 285 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | -------------------------------------------------------------------------------- /Example/Analysis/AnalysisTableViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AnalysisTableViewController.swift 3 | // Analysis 4 | // 5 | // Created by Bas Broek on 13/11/2016. 6 | // Copyright (c) 2016 Bas Broek. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import Analysis 11 | 12 | class AnalysisTableViewController: UITableViewController { 13 | 14 | fileprivate let readingEaseIndexPath = IndexPath(row: 0, section: 2) 15 | fileprivate let gradeLevelIndexPath = IndexPath(row: 1, section: 2) 16 | 17 | // Count 18 | @IBOutlet var characterCount: UILabel! 19 | @IBOutlet var excludingSpacesCharactersCount: UILabel! 20 | @IBOutlet var wordCount: UILabel! 21 | @IBOutlet var uniqueWordCount: UILabel! 22 | @IBOutlet var sentenceCount: UILabel! 23 | @IBOutlet var syllableCount: UILabel! 24 | 25 | // Average 26 | @IBOutlet var averageCharactersPerWord: UILabel! 27 | @IBOutlet var averageCharactersPerSentence: UILabel! 28 | @IBOutlet var averageWordsPerSentence: UILabel! 29 | 30 | // Flesch-Kincaid 31 | @IBOutlet var fleschReadingEase: UILabel! 32 | @IBOutlet var fleschKincaidGradeLevel: UILabel! 33 | 34 | var input: String? 35 | 36 | override func viewDidLoad() { 37 | super.viewDidLoad() 38 | populate() 39 | } 40 | } 41 | 42 | // MARK: - UITableViewDelegate 43 | extension AnalysisTableViewController { 44 | 45 | override func tableView(_ tableView: UITableView, accessoryButtonTappedForRowWith indexPath: IndexPath) { 46 | switch indexPath { 47 | case readingEaseIndexPath: 48 | guard let url = URL(string: "https://en.wikipedia.org/wiki/Flesch-Kincaid_readability_tests#Flesch_reading_ease") else { return } 49 | open(url: url, reader: true) 50 | case gradeLevelIndexPath: 51 | guard let url = URL(string: "https://en.wikipedia.org/wiki/Flesch-Kincaid_readability_tests#Flesch.E2.80.93Kincaid_grade_level") else { return } 52 | open(url: url, reader: true) 53 | default: 54 | break 55 | } 56 | } 57 | 58 | override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { 59 | tableView.deselectRow(at: indexPath, animated: true) 60 | } 61 | } 62 | 63 | // MARK: - Private 64 | private extension AnalysisTableViewController { 65 | 66 | func populate() { 67 | guard let input = input else { fatalError("No input from previous view.") } 68 | let analysis = Analysis(of: input) 69 | 70 | // Count 71 | characterCount.text = String(describing: analysis.characterCount()) 72 | excludingSpacesCharactersCount.text = String(describing: analysis.characterCount(includingSpaces: false)) 73 | wordCount.text = String(describing: analysis.wordCount()) 74 | uniqueWordCount.text = String(describing: analysis.wordCount(unique: true)) 75 | sentenceCount.text = String(describing: analysis.sentenceCount()) 76 | syllableCount.text = String(describing: analysis.syllableCount()) 77 | 78 | // Average 79 | averageCharactersPerWord.text = String(format: "%.2f", analysis.averageCharacters(per: .word)) 80 | averageCharactersPerSentence.text = String(format: "%.2f", analysis.averageCharacters(per: .sentence)) 81 | averageWordsPerSentence.text = String(format: "%.2f", analysis.averageWordsPerSentence) 82 | 83 | // Flesch-Kincaid 84 | fleschReadingEase.text = String(format: "%.2f", analysis.fleschReadingEase()) 85 | fleschKincaidGradeLevel.text = String(format: "%.2f", analysis.fleschKincaidGradeLevel()) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /Example/Analysis/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Analysis 4 | // 5 | // Created by Bas Broek on 11/11/2016. 6 | // Copyright (c) 2016 Bas Broek. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | func application( 17 | _ application: UIApplication, 18 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 19 | ) -> Bool { 20 | true 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Example/Analysis/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /Example/Analysis/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 | 25 | 26 | 27 | Lorem ipsum dolor sit er elit lamet, consectetaur cillium adipisicing pecu, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /Example/Analysis/Images.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 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Example/Analysis/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 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Example/Analysis/UIViewController+Safari.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+Safari.swift 3 | // Analysis 4 | // 5 | // Created by Bas Broek on 02/12/2016. 6 | // Copyright (c) 2016 Bas Broek. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SafariServices 11 | 12 | extension UIViewController: SFSafariViewControllerDelegate { 13 | 14 | func open( 15 | url: URL, 16 | inSafariViewController safariViewController: Bool = true, 17 | reader: Bool = false, 18 | application: UIApplication = .shared 19 | ) { 20 | func openSafari(with url: URL) { 21 | if #available(iOS 10.0, *) { 22 | application.open(url) 23 | } else { 24 | application.openURL(url) 25 | } 26 | } 27 | if safariViewController == false { 28 | openSafari(with: url) 29 | } else if #available(iOS 9.0, *) { 30 | let safariViewController = SFSafariViewController( 31 | url: url, 32 | entersReaderIfAvailable: reader 33 | ) 34 | safariViewController.delegate = self 35 | present(safariViewController, animated: true) 36 | } else { 37 | openSafari(with: url) 38 | } 39 | } 40 | 41 | @available(iOS 9.0, *) 42 | public func safariViewControllerDidFinish(controller: SFSafariViewController) { 43 | dismiss(animated: true) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Example/Analysis/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // Analysis 4 | // 5 | // Created by Bas Broek on 11/11/2016. 6 | // Copyright (c) 2016 Bas Broek. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | private let analyseIdentifier = "analyse" 12 | 13 | class ViewController: UIViewController { 14 | 15 | @IBOutlet var analyseBarButtonItem: UIBarButtonItem! 16 | @IBOutlet var textView: UITextView! { 17 | didSet { 18 | textView.becomeFirstResponder() 19 | analyseBarButtonItem.isEnabled = !textView.text.isEmpty 20 | } 21 | } 22 | } 23 | 24 | // MARK: - Text view delegate 25 | extension ViewController: UITextViewDelegate { 26 | 27 | func textViewDidChange(_ textView: UITextView) { 28 | analyseBarButtonItem.isEnabled = !textView.text.isEmpty 29 | } 30 | } 31 | 32 | // MARK: - Actions 33 | extension ViewController { 34 | 35 | @IBAction func analyse(_ sender: UIBarButtonItem) { 36 | performSegue(withIdentifier: analyseIdentifier, sender: self) 37 | } 38 | } 39 | 40 | // MARK: - Navigation 41 | extension ViewController { 42 | 43 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 44 | guard let destinationViewController = segue.destination as? AnalysisTableViewController else { return } 45 | destinationViewController.input = textView.text 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '8.0' 2 | 3 | use_frameworks! 4 | 5 | target 'Analysis_Example' do 6 | pod 'Analysis', :path => '../' 7 | 8 | target 'Analysis_Tests' do 9 | inherit! :search_paths 10 | 11 | 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Analysis (0.5.0) 3 | 4 | DEPENDENCIES: 5 | - Analysis (from `../`) 6 | 7 | EXTERNAL SOURCES: 8 | Analysis: 9 | :path: "../" 10 | 11 | SPEC CHECKSUMS: 12 | Analysis: d00f01ab60b1602882add658b90a4c47a14caad7 13 | 14 | PODFILE CHECKSUM: d91e06da3cd073207837737c6c65ef37d1dfd49f 15 | 16 | COCOAPODS: 1.8.4 17 | -------------------------------------------------------------------------------- /Example/Tests/AnalysisTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AnalysisTests.swift 3 | // Analysis 4 | // 5 | // Created by Bas Broek on 11/11/2016. 6 | // Copyright (c) 2016 Bas Broek. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import XCTest 11 | @testable import Analysis 12 | 13 | class AnalysisTests: XCTestCase { 14 | 15 | let helloWorld1 = Analysis(of: "Hello, world!") 16 | let helloWorld2 = "Hello, world!".analysed() 17 | let three = "Hello. Hallo? Hoi!".analysed() 18 | let a = "a".analysed() 19 | let z = "z".analysed() 20 | let repeating = "repeat, repeat, repeat".analysed() 21 | let differentSentenceLengths = "Hi. How are you? I am good".analysed() 22 | let spaces = "Hi. How are you doing? ".analysed() 23 | let face = "Can't feel my face".analysed() 24 | 25 | let literal: Analysis = "How are you doing?" 26 | let nonLiteral = Analysis(of: "How are you doing?") 27 | 28 | override func setUp() { 29 | super.setUp() 30 | } 31 | 32 | override func tearDown() { 33 | super.tearDown() 34 | } 35 | 36 | func testEquality() { 37 | XCTAssertEqual(helloWorld1, helloWorld2) 38 | XCTAssertEqual(helloWorld2, helloWorld1) 39 | 40 | XCTAssertNotEqual(helloWorld1, three) 41 | XCTAssertNotEqual(three, helloWorld2) 42 | } 43 | 44 | func testComparability() { 45 | XCTAssertLessThan(a, z) 46 | XCTAssertLessThanOrEqual(a, a) 47 | XCTAssertLessThanOrEqual(z, z) 48 | 49 | XCTAssertGreaterThan(z, a) 50 | XCTAssertGreaterThanOrEqual(z, z) 51 | XCTAssertGreaterThanOrEqual(a, a) 52 | } 53 | 54 | func testSentenceCount() { 55 | XCTAssertEqual(three.sentenceCount(), 3) 56 | XCTAssertEqual(three.sentences.count, 3) 57 | XCTAssertEqual(spaces.sentenceCount(), 2) 58 | XCTAssertEqual(spaces.sentences.count, 2) 59 | XCTAssertEqual(face.sentenceCount(), 1) 60 | XCTAssertEqual(face.sentences.count, 1) 61 | } 62 | 63 | func testWordCount() { 64 | XCTAssertEqual(helloWorld1.wordCount(), 2) 65 | XCTAssertEqual(three.wordCount(), 3) 66 | XCTAssertEqual(repeating.wordCount(), 3) 67 | XCTAssertEqual(face.wordCount(), 4) 68 | 69 | XCTAssertEqual(helloWorld1.wordCount(unique: true), 2) 70 | XCTAssertEqual(three.wordCount(unique: true), 3) 71 | XCTAssertEqual(repeating.wordCount(unique: true), 1) 72 | XCTAssertEqual(face.wordCount(unique: true), 4) 73 | } 74 | 75 | func testCharacterCount() { 76 | XCTAssertEqual(helloWorld1.characterCount(), helloWorld1.characters.count) 77 | XCTAssertEqual(three.characterCount(), three.characters.count) 78 | XCTAssertEqual(repeating.characterCount(), repeating.characters.count) 79 | XCTAssertEqual(face.characterCount(), face.characters.count) 80 | 81 | XCTAssertEqual(helloWorld1.characterCount(includingSpaces: false), helloWorld1.characters.count - 1) 82 | XCTAssertEqual(three.characterCount(includingSpaces: false), three.characters.count - 2) 83 | XCTAssertEqual(repeating.characterCount(includingSpaces: false), repeating.characters.count - 2) 84 | XCTAssertEqual(face.characterCount(includingSpaces: false), face.characters.count - 3) 85 | } 86 | 87 | func testWordOccurences() { 88 | XCTAssertEqual(helloWorld1.wordOccurrences(), ["hello": 1, "world": 1]) 89 | XCTAssertEqual(repeating.wordOccurrences(), ["repeat": 3]) 90 | XCTAssertEqual(face.wordOccurrences(), ["can't": 1, "feel": 1, "my": 1, "face": 1]) 91 | 92 | XCTAssertEqual(helloWorld1.wordOccurrences(caseSensitive: true), ["Hello": 1, "world": 1]) 93 | XCTAssertEqual(repeating.wordOccurrences(caseSensitive: true), ["repeat": 3]) 94 | XCTAssertEqual(face.wordOccurrences(caseSensitive: true), ["Can't": 1, "feel": 1, "my": 1, "face": 1]) 95 | 96 | XCTAssertEqual(helloWorld1.occurrences(of: "hello"), 1) 97 | XCTAssertEqual(helloWorld1.occurrences(of: "HELLO"), 1) 98 | XCTAssertEqual(helloWorld1.occurrences(of: "invalid"), 0) 99 | XCTAssertEqual(repeating.occurrences(of: "repeat"), 3) 100 | XCTAssertEqual(repeating.occurrences(of: "re"), 0) 101 | 102 | XCTAssertEqual(helloWorld1.occurrences(of: "Hello", caseSensitive: true), 1) 103 | XCTAssertEqual(helloWorld1.occurrences(of: "HELLO", caseSensitive: true), 0) 104 | XCTAssertEqual(helloWorld1.occurrences(of: "invalid", caseSensitive: true), 0) 105 | XCTAssertEqual(repeating.occurrences(of: "repeat", caseSensitive: true), 3) 106 | XCTAssertEqual(repeating.occurrences(of: "re", caseSensitive: true), 0) 107 | } 108 | 109 | func testCharacterOccurrences() { 110 | let helloWorldCharacterOccurences: [Character: Int] = ["h": 1, "e": 1, "l": 3, "o": 2, ",": 1, " ": 1, "w": 1, "r": 1, "d": 1, "!": 1] 111 | let repeatingCharacterOccurences: [Character: Int] = ["r": 3, "e": 6, "p": 3, "a": 3, "t": 3, ",": 2, " ": 2] 112 | XCTAssertEqual(helloWorld1.characterOccurences(), helloWorldCharacterOccurences) 113 | XCTAssertEqual(repeating.characterOccurences(), repeatingCharacterOccurences) 114 | 115 | let helloWorldCaseSensitiveCharacterOccurences: [Character: Int] = ["H": 1, "e": 1, "l": 3, "o": 2, ",": 1, " ": 1, "w": 1, "r": 1, "d": 1, "!": 1] 116 | let repeatingCaseSensitiveCharacterOccurences = repeatingCharacterOccurences 117 | 118 | XCTAssertEqual(helloWorld1.characterOccurences(caseSensitive: true), helloWorldCaseSensitiveCharacterOccurences) 119 | XCTAssertEqual(repeating.characterOccurences(caseSensitive: true), repeatingCaseSensitiveCharacterOccurences) 120 | 121 | XCTAssertEqual(helloWorld1.occurrences(of: Character("h")), 1) 122 | XCTAssertEqual(helloWorld1.occurrences(of: Character("H")), 1) 123 | XCTAssertEqual(repeating.occurrences(of: Character("a")), 3) 124 | XCTAssertEqual(repeating.occurrences(of: Character("R")), 3) 125 | 126 | XCTAssertEqual(helloWorld1.occurrences(of: Character("h"), caseSensitive: true), 0) 127 | XCTAssertEqual(helloWorld1.occurrences(of: Character("H"), caseSensitive: true), 1) 128 | XCTAssertEqual(repeating.occurrences(of: Character("I"), caseSensitive: true), 0) 129 | XCTAssertEqual(repeating.occurrences(of: Character("r"), caseSensitive: true), 3) 130 | } 131 | 132 | func testWordFrequency() { 133 | XCTAssertEqual(three.frequency(of: "hello"), 33.3, accuracy: 0.1) 134 | XCTAssertEqual(three.frequency(of: "HELLO"), 33.3, accuracy: 0.1) 135 | 136 | XCTAssertEqual(three.frequency(of: "he"), 0.0) 137 | XCTAssertEqual(helloWorld1.frequency(of: "hello"), 50.0) 138 | XCTAssertEqual(repeating.frequency(of: "repeat"), 100.0) 139 | XCTAssertEqual(repeating.frequency(of: "Repeat"), 100.0) 140 | 141 | XCTAssertEqual(three.frequency(of: "Hello", caseSensitive: true), 33.3, accuracy: 0.1) 142 | 143 | XCTAssertEqual(three.frequency(of: "hello", caseSensitive: true), 0.0) 144 | XCTAssertEqual(three.frequency(of: "he", caseSensitive: true), 0.0) 145 | XCTAssertEqual(three.frequency(of: "HELLO", caseSensitive: true), 0.0) 146 | XCTAssertEqual(helloWorld1.frequency(of: "hello", caseSensitive: true), 0.0) 147 | XCTAssertEqual(repeating.frequency(of: "repeat", caseSensitive: true), 100.0) 148 | XCTAssertEqual(repeating.frequency(of: "Repeat", caseSensitive: true), 0.0) 149 | } 150 | 151 | func testCharacterFrequency() { 152 | XCTAssertEqual(three.frequency(of: Character("h")), 16.6, accuracy: 0.1) 153 | XCTAssertEqual(three.frequency(of: Character("H")), 16.6, accuracy: 0.1) 154 | XCTAssertEqual(three.frequency(of: Character("i")), 5.5, accuracy: 0.1) 155 | XCTAssertEqual(helloWorld1.frequency(of: Character("h")), 7.7, accuracy: 0.1) 156 | XCTAssertEqual(repeating.frequency(of: Character("e")), 27.3, accuracy: 0.1) 157 | XCTAssertEqual(repeating.frequency(of: Character("E")), 27.3, accuracy: 0.1) 158 | 159 | XCTAssertEqual(three.frequency(of: Character("H"), caseSensitive: true), 16.6, accuracy: 0.1) 160 | XCTAssertEqual(three.frequency(of: Character("i"), caseSensitive: true), 5.5, accuracy: 0.1) 161 | XCTAssertEqual(helloWorld1.frequency(of: Character("e"), caseSensitive: true), 7.7, accuracy: 0.1) 162 | XCTAssertEqual(repeating.frequency(of: Character("e"), caseSensitive: true), 27.3, accuracy: 0.1) 163 | 164 | XCTAssertEqual(three.frequency(of: Character("h"), caseSensitive: true), 0.0) 165 | XCTAssertEqual(three.frequency(of: Character("h"), caseSensitive: true), 0.0) 166 | XCTAssertEqual(repeating.frequency(of: Character("T"), caseSensitive: true), 0.0) 167 | } 168 | 169 | func testAverageCharactersPerWord() { 170 | XCTAssertEqual(three.averageCharacters(per: .word), 4.3, accuracy: 0.1) 171 | 172 | XCTAssertEqual(helloWorld1.averageCharacters(per: .word), 5.0) 173 | XCTAssertEqual(repeating.averageCharacters(per: .word), 6.0) 174 | } 175 | 176 | func testAverageCharactersPerSentence() { 177 | XCTAssertEqual(three.averageCharacters(per: .sentence), 5.3, accuracy: 0.1) 178 | 179 | XCTAssertEqual(helloWorld1.averageCharacters(per: .sentence), 13.0) 180 | XCTAssertEqual(repeating.averageCharacters(per: .sentence), 22.0) 181 | } 182 | 183 | func testAverageWordsPerSentence() { 184 | XCTAssertEqual(differentSentenceLengths.averageWordsPerSentence, 2.3, accuracy: 0.1) 185 | 186 | XCTAssertEqual(three.averageWordsPerSentence, 1.0) 187 | XCTAssertEqual(helloWorld1.averageWordsPerSentence, 2.0) 188 | XCTAssertEqual(repeating.averageWordsPerSentence, 3.0) 189 | } 190 | 191 | func testSyllableCount() { 192 | XCTAssertEqual(helloWorld1.syllableCount(), 3) 193 | XCTAssertEqual(helloWorld2.syllableCount(), 3) 194 | XCTAssertEqual(a.syllableCount(), 1) 195 | XCTAssertEqual(z.syllableCount(), 1) 196 | XCTAssertEqual(repeating.syllableCount(), 6) 197 | } 198 | 199 | func testWordSyllables() { 200 | XCTAssertEqual(helloWorld1.wordSyllables(), ["hello": 2, "world": 1]) 201 | XCTAssertEqual(helloWorld2.wordSyllables(), ["hello": 2, "world": 1]) 202 | XCTAssertEqual(a.wordSyllables(), ["a": 1]) 203 | XCTAssertEqual(z.wordSyllables(), ["z": 1]) 204 | XCTAssertEqual(repeating.wordSyllables(), ["repeat": 2]) 205 | } 206 | 207 | func testDescription() { 208 | XCTAssertEqual(helloWorld1.description, "Analysis(\"\(helloWorld1.input)\")") 209 | XCTAssertEqual(helloWorld2.description, "Analysis(\"\(helloWorld2.input)\")") 210 | XCTAssertEqual(a.description, "Analysis(\"\(a.input)\")") 211 | XCTAssertEqual(z.description, "Analysis(\"\(z.input)\")") 212 | } 213 | 214 | func testDebugDescription() { 215 | XCTAssertEqual(helloWorld1.debugDescription, dump(helloWorld1.description)) 216 | XCTAssertEqual(helloWorld2.debugDescription, dump(helloWorld2.description)) 217 | XCTAssertEqual(a.debugDescription, dump(a.description)) 218 | XCTAssertEqual(z.debugDescription, dump(z.description)) 219 | } 220 | 221 | func testExpressibleByStringLiteral() { 222 | XCTAssertEqual(literal, nonLiteral) 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /Example/Tests/CharacterCasingTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CharacterCasingTests.swift 3 | // Analysis 4 | // 5 | // Created by Bas Broek on 09/12/2016. 6 | // Copyright (c) 2016 Bas Broek. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import Analysis 11 | 12 | class CharacterCasingTests: XCTestCase { 13 | 14 | let aLowercase: Character = "a" 15 | let aUppercase: Character = "A" 16 | let bLowercase: Character = "b" 17 | let bUppercase: Character = "B" 18 | 19 | override func setUp() { 20 | super.setUp() 21 | } 22 | 23 | override func tearDown() { 24 | super.tearDown() 25 | } 26 | 27 | func testLowercaseCharacters() { 28 | XCTAssertEqual(aUppercase.lowercased(), aLowercase) 29 | XCTAssertEqual(aLowercase.lowercased(), aLowercase) 30 | XCTAssertEqual(bUppercase.lowercased(), bLowercase) 31 | XCTAssertEqual(bLowercase.lowercased(), bLowercase) 32 | 33 | XCTAssertNotEqual(aUppercase.lowercased(), aUppercase) 34 | XCTAssertNotEqual(aLowercase.lowercased(), aUppercase) 35 | XCTAssertNotEqual(bUppercase.lowercased(), bUppercase) 36 | XCTAssertNotEqual(bLowercase.lowercased(), bUppercase) 37 | } 38 | 39 | func testUppercaseCharacters() { 40 | XCTAssertEqual(aUppercase.uppercased(), aUppercase) 41 | XCTAssertEqual(aLowercase.uppercased(), aUppercase) 42 | XCTAssertEqual(bUppercase.uppercased(), bUppercase) 43 | XCTAssertEqual(bLowercase.uppercased(), bUppercase) 44 | 45 | XCTAssertNotEqual(aUppercase.uppercased(), aLowercase) 46 | XCTAssertNotEqual(aLowercase.uppercased(), aLowercase) 47 | XCTAssertNotEqual(bUppercase.uppercased(), bLowercase) 48 | XCTAssertNotEqual(bLowercase.uppercased(), bLowercase) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Example/Tests/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 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem 'cocoapods' 4 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | CFPropertyList (3.0.7) 5 | base64 6 | nkf 7 | rexml 8 | activesupport (6.1.7.8) 9 | concurrent-ruby (~> 1.0, >= 1.0.2) 10 | i18n (>= 1.6, < 2) 11 | minitest (>= 5.1) 12 | tzinfo (~> 2.0) 13 | zeitwerk (~> 2.3) 14 | addressable (2.8.7) 15 | public_suffix (>= 2.0.2, < 7.0) 16 | algoliasearch (1.27.5) 17 | httpclient (~> 2.8, >= 2.8.3) 18 | json (>= 1.5.1) 19 | atomos (0.1.3) 20 | base64 (0.2.0) 21 | claide (1.1.0) 22 | cocoapods (1.15.2) 23 | addressable (~> 2.8) 24 | claide (>= 1.0.2, < 2.0) 25 | cocoapods-core (= 1.15.2) 26 | cocoapods-deintegrate (>= 1.0.3, < 2.0) 27 | cocoapods-downloader (>= 2.1, < 3.0) 28 | cocoapods-plugins (>= 1.0.0, < 2.0) 29 | cocoapods-search (>= 1.0.0, < 2.0) 30 | cocoapods-trunk (>= 1.6.0, < 2.0) 31 | cocoapods-try (>= 1.1.0, < 2.0) 32 | colored2 (~> 3.1) 33 | escape (~> 0.0.4) 34 | fourflusher (>= 2.3.0, < 3.0) 35 | gh_inspector (~> 1.0) 36 | molinillo (~> 0.8.0) 37 | nap (~> 1.0) 38 | ruby-macho (>= 2.3.0, < 3.0) 39 | xcodeproj (>= 1.23.0, < 2.0) 40 | cocoapods-core (1.15.2) 41 | activesupport (>= 5.0, < 8) 42 | addressable (~> 2.8) 43 | algoliasearch (~> 1.0) 44 | concurrent-ruby (~> 1.1) 45 | fuzzy_match (~> 2.0.4) 46 | nap (~> 1.0) 47 | netrc (~> 0.11) 48 | public_suffix (~> 4.0) 49 | typhoeus (~> 1.0) 50 | cocoapods-deintegrate (1.0.5) 51 | cocoapods-downloader (2.1) 52 | cocoapods-plugins (1.0.0) 53 | nap 54 | cocoapods-search (1.0.1) 55 | cocoapods-trunk (1.6.0) 56 | nap (>= 0.8, < 2.0) 57 | netrc (~> 0.11) 58 | cocoapods-try (1.2.0) 59 | colored2 (3.1.2) 60 | concurrent-ruby (1.3.4) 61 | escape (0.0.4) 62 | ethon (0.16.0) 63 | ffi (>= 1.15.0) 64 | ffi (1.17.0) 65 | fourflusher (2.3.1) 66 | fuzzy_match (2.0.4) 67 | gh_inspector (1.1.3) 68 | httpclient (2.8.3) 69 | i18n (1.14.6) 70 | concurrent-ruby (~> 1.0) 71 | json (2.7.2) 72 | minitest (5.25.1) 73 | molinillo (0.8.0) 74 | nanaimo (0.3.0) 75 | nap (1.1.0) 76 | netrc (0.11.0) 77 | nkf (0.2.0) 78 | public_suffix (4.0.7) 79 | rexml (3.3.7) 80 | ruby-macho (2.5.1) 81 | typhoeus (1.4.1) 82 | ethon (>= 0.9.0) 83 | tzinfo (2.0.6) 84 | concurrent-ruby (~> 1.0) 85 | xcodeproj (1.25.0) 86 | CFPropertyList (>= 2.3.3, < 4.0) 87 | atomos (~> 0.1.3) 88 | claide (>= 1.0.2, < 2.0) 89 | colored2 (~> 3.1) 90 | nanaimo (~> 0.3.0) 91 | rexml (>= 3.3.2, < 4.0) 92 | zeitwerk (2.6.18) 93 | 94 | PLATFORMS 95 | ruby 96 | 97 | DEPENDENCIES 98 | cocoapods 99 | 100 | BUNDLED WITH 101 | 2.2.23 102 | -------------------------------------------------------------------------------- /License: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Bas Broek 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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | [![Travis status](https://img.shields.io/travis/BasThomas/Analysis.svg)](https://travis-ci.org/BasThomas/Analysis) 2 | 3 | # Analysis 4 | 5 | Analysis is a tool that helps you extract useful information from strings. It calculates the amount of sentences, words, characters, occurrence of words and characters, and more. 6 | 7 | ## Installation 8 | 9 | Analysis is available via CocoaPods. 10 | 11 | ```ruby 12 | pod 'Analysis' 13 | ``` 14 | 15 | Then run `pod install`. 16 | 17 | ## Sample Project 18 | 19 | There's a sample project in the `Example` directory. To use it, run `pod install` to download the required libraries. 20 | 21 | ## Usage 22 | 23 | Analysing a string is very straightforward. You can use any of these two ways to instantiate an `Analysis`: 24 | 25 | ```swift 26 | import Analysis 27 | 28 | let analysis = Analysis(of: "You are awesome, you!") 29 | ``` 30 | 31 | or 32 | 33 | ```swift 34 | import Analysis 35 | 36 | let analysis = "You are awesome, you!".analysed() 37 | ``` 38 | 39 | After that, you can get the information you need. 40 | 41 | ```swift 42 | analysis.sentenceCount() // 1 43 | analysis.wordCount(unique: true) // 4 44 | analysis.characterCount(includingSpaces: false) // 18 45 | analysis.wordOccurrences(caseSensitive: true) // ["You": 1, "are": 1, "awesome", 1, "you": 1] 46 | analysis.wordOccurrences(caseSensitive: false) // ["you": 2, "are": 1, "awesome", 1] 47 | analysis.frequency(of: "you", caseSensitive: false) // 50.0% 48 | analysis.averageCharacters(per: .word) // 5.33 49 | 50 | analysis.syllableCount() // 5 51 | analysis.wordSyllables() // ["you": 1, "are": 1, "awesome": 2] 52 | analysis.fleschReadingEase() // 97.025 53 | analysis.fleschKincaidGradeLevel() // 0.72 54 | ``` 55 | 56 | You can also easily sort your occurences via an enhanced sorting method on `Dictionary`. 57 | 58 | ```swift 59 | analysis 60 | .wordOccurrences(caseSensitive: false) 61 | .sorted(by: .key, order: .ascending) // [("are", 1), ("awesome", 1), ("you", 2)] 62 | ``` 63 | 64 | ## Contributing 65 | 66 | Want to contribute to this project? Great! There's some things that still need work, but definitely also let me know when you encounter any issues, spot a bug, or have a feature request! 67 | 68 | Check out the issues for some tasks to get started with. Or, write tests. We can use as many as you can think of. 69 | 70 | ## License 71 | 72 | Analysis is released under an MIT license. See [License](License) for more information. 73 | --------------------------------------------------------------------------------