├── .gitignore ├── .github ├── FUNDING.yml └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── SweeterSwift ├── SweeterSwift.xcodeproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── xcshareddata │ │ └── xcschemes │ │ │ └── SweeterSwift.xcscheme │ └── project.pbxproj ├── SweeterSwift.xcworkspace │ ├── xcshareddata │ │ ├── WorkspaceSettings.xcsettings │ │ └── IDEWorkspaceChecks.plist │ └── contents.xcworkspacedata ├── SweeterSwiftTests │ ├── BundleTests.swift │ ├── StringTests.swift │ ├── Info.plist │ ├── NSAttributedStringTests.swift │ ├── CodableTests.swift │ └── SwiftTests.swift ├── SweeterSwift │ ├── SweeterSwift.h │ └── Info.plist ├── Podfile.lock └── Podfile ├── Source ├── PrivacyInfo.xcprivacy ├── TimeInterval+Sweeter.swift ├── DateFormatter+Sweeter.swift ├── UIViewController+Sweeter.swift ├── UIStackView+Sweeter.swift ├── NSObject+Sweeter.swift ├── Bundle+Sweeter.swift ├── Codable+Sweeter.swift ├── UITextView+Sweeter.swift ├── String+Sweeter.swift ├── NSAttributedString+Sweeter.swift ├── UIApplication+Sweeter.swift ├── NSManagedObjectContext+Sweeter.swift ├── Swift+Sweeter.swift └── UIView+Sweeter.swift ├── Package.swift ├── SweeterSwift.podspec ├── LICENSE.txt ├── CHANGELOG.md └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | Pods 2 | 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [yonat] 2 | -------------------------------------------------------------------------------- /SweeterSwift/SweeterSwift.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SweeterSwift/SweeterSwift.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /SweeterSwift/SweeterSwift.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /SweeterSwift/SweeterSwift.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /SweeterSwift/SweeterSwift.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: yonat 7 | 8 | --- 9 | 10 | **Description:** 11 | [description] 12 | 13 | **Problems I encountered when trying to implement this myself:** 14 | [if none, please submit a pull request.] 15 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: yonat 7 | 8 | --- 9 | 10 | **Description of the problem:** 11 | [description] 12 | 13 | **Minimal project that reproduces the problem (so I'll be able to figure out how to fix it):** 14 | [link to a Minimal Reproducible Example as described at https://ootips.org/yonat/repex ] 15 | -------------------------------------------------------------------------------- /Source/PrivacyInfo.xcprivacy: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSPrivacyTracking 6 | 7 | NSPrivacyCollectedDataTypes 8 | 9 | NSPrivacyAccessedAPITypes 10 | 11 | NSPrivacyTrackingDomains 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /SweeterSwift/SweeterSwiftTests/BundleTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BundleTests.swift 3 | // 4 | // Created by Yonat Sharon on 2019-06-29. 5 | // Copyright © 2019 Yonat Sharon. All rights reserved. 6 | // 7 | 8 | import SweeterSwift 9 | import XCTest 10 | 11 | class BundleTests: XCTestCase { 12 | func testBundle() { 13 | XCTAssertEqual(Bundle.main.name, "xctest") 14 | XCTAssertTrue(Bundle.main.infoString.contains(" ")) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /SweeterSwift/SweeterSwiftTests/StringTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StringTests.swift 3 | // 4 | // Created by Yonat Sharon on 2019-06-29. 5 | // Copyright © 2019 Yonat Sharon. All rights reserved. 6 | // 7 | 8 | import SweeterSwift 9 | import XCTest 10 | 11 | class StringTests: XCTestCase { 12 | func testUnCamelCase() { 13 | XCTAssertEqual("winterIsComing".unCamelCased, "Winter Is Coming") 14 | XCTAssertEqual("winterIsComing".camelToSnakeCased, "winter_is_coming") 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Source/TimeInterval+Sweeter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TimeInterval+Sweeter.swift 3 | // 4 | // Created by Yonat Sharon on 2019-02-08. 5 | // 6 | 7 | import Foundation 8 | 9 | // swiftlint:disable no_magic_numbers 10 | public extension TimeInterval { 11 | static let minute: TimeInterval = 60 12 | static let hour: TimeInterval = 60 * minute 13 | static let day: TimeInterval = 24 * hour 14 | static let week: TimeInterval = 7 * day 15 | static let year: TimeInterval = 365.25 * day 16 | static let month: TimeInterval = year / 12 17 | } 18 | -------------------------------------------------------------------------------- /Source/DateFormatter+Sweeter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DateFormatter+Sweeter.swift 3 | // 4 | // Created by Yonat Sharon on 2019-02-08. 5 | // 6 | 7 | import Foundation 8 | 9 | public extension DateFormatter { 10 | /// Sweeter: Create a new formatter with format string. 11 | convenience init(format: String, timeZone: TimeZone = .current, locale: String? = nil) { 12 | self.init() 13 | dateFormat = format 14 | self.timeZone = timeZone 15 | if let locale = locale { 16 | self.locale = Locale(identifier: locale) 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /SweeterSwift/SweeterSwift/SweeterSwift.h: -------------------------------------------------------------------------------- 1 | // 2 | // SweeterSwift.h 3 | // SweeterSwift 4 | // 5 | // Created by Yonat Sharon on 29/06/2019. 6 | // Copyright © 2019 Yonat Sharon. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for SweeterSwift. 12 | FOUNDATION_EXPORT double SweeterSwiftVersionNumber; 13 | 14 | //! Project version string for SweeterSwift. 15 | FOUNDATION_EXPORT const unsigned char SweeterSwiftVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.6 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "SweeterSwift", 7 | platforms: [ 8 | .iOS(.v9), 9 | ], 10 | products: [ 11 | .library(name: "SweeterSwift", targets: ["SweeterSwift"]), 12 | ], 13 | dependencies: [], 14 | targets: [ 15 | .target(name: "SweeterSwift", dependencies: [], path: "Source", resources: [.process("PrivacyInfo.xcprivacy")]), 16 | .testTarget(name: "SweeterSwiftTests", dependencies: ["SweeterSwift"], path: "SweeterSwift/SweeterSwiftTests"), 17 | ], 18 | swiftLanguageVersions: [.v5] 19 | ) 20 | -------------------------------------------------------------------------------- /Source/UIViewController+Sweeter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewController+Sweeter.swift 3 | // 4 | // Created by Yonat Sharon on 2019-02-08. 5 | // 6 | 7 | import UIKit 8 | 9 | public extension UIViewController { 10 | /// Sweeter: Add child view controller pinned to specific places. 11 | /// Example: addConstrainedChild(pages, constrain: .bottomMargin, .top, .left, .right) 12 | func addConstrainedChild(_ viewController: UIViewController, constrain: NSLayoutConstraint.Attribute...) { 13 | addChild(viewController) 14 | view.addConstrainedSubview(viewController.view, constrainedAttributes: constrain) 15 | viewController.didMove(toParent: self) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Source/UIStackView+Sweeter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIStackView+Sweeter.swift 3 | // 4 | // Created by Yonat Sharon on 2019-02-08. 5 | // 6 | 7 | import UIKit 8 | 9 | public extension UIStackView { 10 | /// Sweeter: Remove `subview` from the view hierarchy, not just the stack arrangement. 11 | func removeArrangedSubviewCompletely(_ subview: UIView) { 12 | removeArrangedSubview(subview) 13 | subview.removeFromSuperview() 14 | } 15 | 16 | /// Sweeter: Remove all arranged subviews from the view hierarchy, not just the stack arrangement. 17 | func removeAllArrangedSubviewsCompletely() { 18 | for subview in arrangedSubviews.reversed() { 19 | removeArrangedSubviewCompletely(subview) 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Source/NSObject+Sweeter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+Sweeter.swift 3 | // 4 | // Created by Yonat Sharon on 2019-02-08. 5 | // 6 | 7 | import Foundation 8 | 9 | public extension NSObject { 10 | /// Sweeter: Post a local notification using `NotificationCenter.default`. 11 | func notify(_ notificationName: Notification.Name, userInfo: [AnyHashable: Any]? = nil) { 12 | NotificationCenter.default.post(name: notificationName, object: self, userInfo: userInfo) 13 | } 14 | 15 | /// Sweeter: Respond to a local notification from `NotificationCenter.default`. 16 | func observeNotification(name: Notification.Name, selector: Selector, object: Any? = nil) { 17 | NotificationCenter.default.addObserver(self, selector: selector, name: name, object: object) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /SweeterSwift.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | 3 | s.name = "SweeterSwift" 4 | s.version = "1.2.6" 5 | s.summary = "Extensions and syntactic sugar to enrich the Swift standard library, iOS frameworks, and SwifterSwift." 6 | 7 | s.homepage = "https://github.com/yonat/SweeterSwift" 8 | s.license = { :type => "MIT", :file => "LICENSE.txt" } 9 | s.author = { "Yonat Sharon" => "yonat@ootips.org" } 10 | 11 | s.swift_version = '4.2' 12 | s.swift_versions = ['4.2', '5.0'] 13 | s.platform = :ios, "9.0" 14 | s.requires_arc = true 15 | 16 | s.source = { :git => "https://github.com/yonat/SweeterSwift.git", :tag => s.version } 17 | s.source_files = "Source/*.swift" 18 | s.resource_bundles = {'SweeterSwift' => ['Source/PrivacyInfo.xcprivacy']} 19 | 20 | end 21 | -------------------------------------------------------------------------------- /SweeterSwift/SweeterSwift/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.2.2 19 | CFBundleVersion 20 | 41 21 | 22 | 23 | -------------------------------------------------------------------------------- /SweeterSwift/SweeterSwiftTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 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 | -------------------------------------------------------------------------------- /Source/Bundle+Sweeter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Bundle+Sweeter.swift 3 | // 4 | // Created by Yonat Sharon on 2019-02-08. 5 | // 6 | 7 | import Foundation 8 | 9 | public extension Bundle { 10 | /// Sweeter: app name with reasonable fallback to process name 11 | var name: String { 12 | return infoDictionary?["CFBundleDisplayName"] as? String 13 | ?? infoDictionary?["CFBundleName"] as? String 14 | ?? ProcessInfo.processInfo.processName 15 | } 16 | 17 | /// Sweeter: app name, version, and build number 18 | var infoString: String { 19 | let version = infoDictionary?["CFBundleShortVersionString"] as? String 20 | let build = infoDictionary?["CFBundleVersion"] as? String 21 | 22 | let nameAndVersion = [name, version].compact.joined(separator: " ") 23 | return [nameAndVersion, build].compact.joined(separator: " #") 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Source/Codable+Sweeter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Codable+Sweeter.swift 3 | // 4 | // Created by Yonat Sharon on 2019-02-08. 5 | // 6 | 7 | import Foundation 8 | 9 | public extension Decodable { 10 | /// Sweeter: Create object from a dictionary 11 | init?(dictionary: [String: Any]) { 12 | guard let data = try? JSONSerialization.data(withJSONObject: dictionary, options: .prettyPrinted) else { return nil } 13 | guard let decodedSelf = try? JSONDecoder().decode(Self.self, from: data) else { return nil } 14 | self = decodedSelf 15 | } 16 | } 17 | 18 | public extension Encodable { 19 | /// Sweeter: Export object to a dictionary representation 20 | var dictionary: [String: Any]? { 21 | guard let data = try? JSONEncoder().encode(self) else { return nil } 22 | return (try? JSONSerialization.jsonObject(with: data, options: .allowFragments)).flatMap { $0 as? [String: Any] } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Source/UITextView+Sweeter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UITextView+Sweeter.swift 3 | // 4 | // Created by Yonat Sharon on 2019-02-08. 5 | // 6 | 7 | import UIKit 8 | 9 | public extension UITextView { 10 | /// Sweeter: Create a label with links, by using a `UITextView` to auto-detect links and simulate `UILabel` appearance. 11 | @available(iOS 9, *) 12 | @available(tvOS, unavailable) 13 | @available(macOS, unavailable) 14 | convenience init(simulatedLabelWithLinksInText: String, font: UIFont = UIFont.systemFont(ofSize: UIFont.labelFontSize)) { 15 | self.init() 16 | text = simulatedLabelWithLinksInText 17 | isEditable = false 18 | dataDetectorTypes = .link 19 | textAlignment = .center 20 | textContainerInset = .zero 21 | backgroundColor = .clear 22 | self.font = font 23 | constrain(.height, to: font.pointSize * 1.5) // swiftlint:disable:this no_magic_numbers 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /SweeterSwift/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - SweeterSwift (1.2.1) 3 | - SwiftFormat/CLI (0.48.1) 4 | - SwiftLint (0.41.0) 5 | - SwiftQuality (1.1.1): 6 | - SwiftFormat/CLI (= 0.48.1) 7 | - SwiftLint (= 0.41.0) 8 | 9 | DEPENDENCIES: 10 | - SweeterSwift (from `..`) 11 | - SwiftQuality (from `https://github.com/yonat/SwiftQuality`) 12 | 13 | SPEC REPOS: 14 | trunk: 15 | - SwiftFormat 16 | - SwiftLint 17 | 18 | EXTERNAL SOURCES: 19 | SweeterSwift: 20 | :path: ".." 21 | SwiftQuality: 22 | :git: https://github.com/yonat/SwiftQuality 23 | 24 | CHECKOUT OPTIONS: 25 | SwiftQuality: 26 | :commit: acf4031acdaf3e6f3e035ad6d6de9ab8b83f4e17 27 | :git: https://github.com/yonat/SwiftQuality 28 | 29 | SPEC CHECKSUMS: 30 | SweeterSwift: 266367986dec90ba53bf521eabe0210e22e80445 31 | SwiftFormat: 094c5e2e6c36dafdd128d04cff4a0a7d87492e73 32 | SwiftLint: c585ebd615d9520d7fbdbe151f527977b0534f1e 33 | SwiftQuality: fba6a2775b0e35f9d8c945fd0011c44c8b0e93bd 34 | 35 | PODFILE CHECKSUM: 2a19f03182702703782ea18706704da1e9dc9cc0 36 | 37 | COCOAPODS: 1.12.1 38 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Yonat Sharon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Source/String+Sweeter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String+Sweeter.swift 3 | // 4 | // Created by Yonat Sharon on 2019-02-08. 5 | // 6 | 7 | import Foundation 8 | 9 | extension String { 10 | /// Sweeter: Separate CamelCase into capitalized words. 11 | /// E.g., "winterIsComing" -> "Winter Is Coming" 12 | public var unCamelCased: String { 13 | return replacingCamelCase(with: "$1 $2").capitalized 14 | } 15 | 16 | /// Sweeter: Change CamelCase into snake_case 17 | /// E.g., "winterIsComing" -> "winter_is_coming" 18 | public var camelToSnakeCased: String { 19 | return replacingCamelCase(with: "$1_$2").lowercased() 20 | } 21 | 22 | func replacingCamelCase(with template: String) -> String { 23 | return String.camelCaseWordBoundaryRegex 24 | .stringByReplacingMatches(in: self, range: NSRange(startIndex..., in: self), withTemplate: template) 25 | } 26 | 27 | static let camelCaseWordBoundaryRegex: NSRegularExpression = { 28 | do { 29 | return try NSRegularExpression(pattern: "([a-z])([A-Z])") 30 | } catch { 31 | fatalError(error.localizedDescription) 32 | } 33 | }() 34 | } 35 | -------------------------------------------------------------------------------- /SweeterSwift/SweeterSwiftTests/NSAttributedStringTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSAttributedStringTests.swift 3 | // swiftlint:disable no_magic_numbers 4 | // 5 | // Created by Yonat Sharon on 2019-06-29. 6 | // Copyright © 2019 Yonat Sharon. All rights reserved. 7 | // 8 | 9 | import SweeterSwift 10 | import XCTest 11 | 12 | class NSAttributedStringTests: XCTestCase { 13 | func testInitFromHTML() { 14 | let stringFromHTML = NSAttributedString(htmlString: "Text with bold and italic parts.") 15 | XCTAssertEqual(stringFromHTML?.string, "Text with bold and italic parts.") 16 | XCTAssertTrue(stringFromHTML?.traits(at: 12)?.contains(.traitBold) ?? false) 17 | XCTAssertTrue(stringFromHTML?.traits(at: 24)?.contains(.traitItalic) ?? false) 18 | } 19 | 20 | func testLinkAnchorTextToURL() { 21 | let stringWithAnchor = NSMutableAttributedString(string: "Text with anchor text to link") 22 | stringWithAnchor.link(anchorText: "anchor text", url: "https://ootips.org") 23 | XCTAssertNotNil(stringWithAnchor.attributes(at: 12, effectiveRange: nil)[.link]) 24 | } 25 | } 26 | 27 | extension NSAttributedString { 28 | func traits(at index: Int) -> UIFontDescriptor.SymbolicTraits? { 29 | return (attributes(at: index, effectiveRange: nil)[.font] as? UIFont)?.fontDescriptor.symbolicTraits 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Source/NSAttributedString+Sweeter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSAttributedString+Sweeter.swift 3 | // 4 | // Created by Yonat Sharon on 2019-02-08. 5 | // 6 | 7 | import Foundation 8 | 9 | public extension NSAttributedString { 10 | /// Sweeter: Create attributed string from HTML 11 | convenience init?(htmlString: String, defaultAttributes: [NSAttributedString.Key: Any]? = nil) { 12 | guard let data = htmlString.data(using: .utf8) else { return nil } 13 | let options: [NSAttributedString.DocumentReadingOptionKey: Any] = [ 14 | .documentType: NSAttributedString.DocumentType.html, 15 | .characterEncoding: String.Encoding.utf8.rawValue, 16 | .defaultAttributes: defaultAttributes ?? [:], 17 | ] 18 | 19 | try? self.init(data: data, options: options, documentAttributes: nil) 20 | } 21 | } 22 | 23 | public extension NSMutableAttributedString { 24 | /// Sweeter: Make part of the string into a link. 25 | /// 26 | /// - Parameters: 27 | /// - url: link address. 28 | /// - anchorText: substring to make into a link. 29 | func link(anchorText: String, url: String) { 30 | guard let urlObject = URL(string: url) else { return } 31 | let anchorRange = mutableString.range(of: anchorText, options: [.caseInsensitive, .diacriticInsensitive, .widthInsensitive]) 32 | guard NSNotFound != anchorRange.location else { return } 33 | addAttribute(.link, value: urlObject, range: anchorRange) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Source/UIApplication+Sweeter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIApplication+Sweeter.swift 3 | // 4 | // Created by Yonat Sharon on 2019-02-08. 5 | // 6 | 7 | import UIKit 8 | 9 | public extension UIApplication { 10 | /// Sweeter: `keyWindow` for scene-based apps 11 | var legacyKeyWindow: UIWindow? { 12 | if #available(iOS 13, *) { 13 | return windows.first { $0.isKeyWindow } 14 | } else { 15 | return keyWindow 16 | } 17 | } 18 | 19 | /// Sweeter: Returns the currently top-most view controller. 20 | class func topViewController(base: UIViewController? = UIApplication.shared.legacyKeyWindow?.rootViewController) -> UIViewController? { 21 | if let nav = base as? UINavigationController { 22 | return topViewController(base: nav.visibleViewController) 23 | } 24 | if let tab = base as? UITabBarController { 25 | if let selected = tab.selectedViewController { 26 | return topViewController(base: selected) 27 | } 28 | } 29 | if let presented = base?.presentedViewController { 30 | return topViewController(base: presented) 31 | } 32 | return base 33 | } 34 | 35 | /// Sweeter: Show `viewController` over the top-most view controller. 36 | class func present(_ viewController: UIViewController, animated: Bool = true, completion: (() -> Void)? = nil) { 37 | DispatchQueue.main.async { 38 | topViewController()?.present(viewController, animated: animated, completion: completion) 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /SweeterSwift/Podfile: -------------------------------------------------------------------------------- 1 | platform :ios, '9.3' 2 | use_frameworks! 3 | 4 | pod 'SweeterSwift', :path => '..' 5 | pod 'SwiftQuality', :git => 'https://github.com/yonat/SwiftQuality' 6 | 7 | def quality_scripts 8 | script_phase :name => 'SwiftFormat', 9 | :execution_position => :before_compile, 10 | :script => 'if [ "Debug" == "${CONFIGURATION}" ]; then "${PODS_ROOT}/SwiftFormat/CommandLineTool/swiftformat" --swiftversion ${SWIFT_VERSION} --config "${PODS_ROOT}/SwiftQuality/.swiftformat" "${SRCROOT}/.." ; fi' 11 | 12 | script_phase :name => 'SwiftLintAutocorrect', 13 | :execution_position => :before_compile, 14 | :script => 'if [ "Debug" == "${CONFIGURATION}" ]; then "${PODS_ROOT}/SwiftLint/swiftlint" autocorrect --config "${PODS_ROOT}/SwiftQuality/.swiftlint.yml" --path "${SRCROOT}/.." ; fi' 15 | 16 | script_phase :name => 'SwiftLint', 17 | :execution_position => :after_compile, 18 | :script => 'if [ "Debug" == "${CONFIGURATION}" ]; then "${PODS_ROOT}/SwiftLint/swiftlint" --config "${PODS_ROOT}/SwiftQuality/.swiftlint.yml" --path "${SRCROOT}/.." ; fi' 19 | end 20 | 21 | target 'SweeterSwift' do 22 | quality_scripts 23 | end 24 | 25 | target 'SweeterSwiftTests' do 26 | quality_scripts 27 | end 28 | 29 | # Workaround for Xcode 15 error DT_TOOLCHAIN_DIR cannot be used to evaluate LIBRARY_SEARCH_PATHS 30 | post_install do |installer| 31 | installer.aggregate_targets.each do |target| 32 | target.xcconfigs.each do |variant, xcconfig| 33 | xcconfig_path = target.client_root + target.xcconfig_relative_path(variant) 34 | IO.write(xcconfig_path, IO.read(xcconfig_path).gsub("DT_TOOLCHAIN_DIR", "TOOLCHAIN_DIR")) 35 | end 36 | end 37 | installer.pods_project.targets.each do |target| 38 | target.build_configurations.each do |config| 39 | if config.base_configuration_reference.is_a? Xcodeproj::Project::Object::PBXFileReference 40 | xcconfig_path = config.base_configuration_reference.real_path 41 | IO.write(xcconfig_path, IO.read(xcconfig_path).gsub("DT_TOOLCHAIN_DIR", "TOOLCHAIN_DIR")) 42 | end 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /SweeterSwift/SweeterSwiftTests/CodableTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CodableTests.swift 3 | // swiftlint:disable no_magic_numbers 4 | // 5 | // Created by Yonat Sharon on 2019-06-29. 6 | // Copyright © 2019 Yonat Sharon. All rights reserved. 7 | // 8 | 9 | import SweeterSwift 10 | import XCTest 11 | 12 | struct CodableTestStruct: Codable { 13 | var number: Int 14 | var string: String 15 | var flag: Bool 16 | var optionalFloat: Float? 17 | } 18 | 19 | class CodableTests: XCTestCase { 20 | let testStruct = CodableTestStruct(number: 42, string: "How many roads", flag: true) 21 | let testDictionary: [String: Any] = [ 22 | "number": 42, 23 | "string": "How many roads", 24 | "flag": true, 25 | ] 26 | 27 | func testInitFromDictionary() throws { 28 | let structFromDictionary = try XCTUnwrap(CodableTestStruct(dictionary: testDictionary)) 29 | XCTAssertEqual(structFromDictionary.number, 42) 30 | XCTAssertEqual(structFromDictionary.string, "How many roads") 31 | XCTAssertEqual(structFromDictionary.flag, true) 32 | XCTAssertNil(structFromDictionary.optionalFloat) 33 | } 34 | 35 | func testSaveToDictionary() throws { 36 | let dictionaryFromStruct = try XCTUnwrap(testStruct.dictionary) 37 | XCTAssertEqual(dictionaryFromStruct["number"] as? Int, 42) 38 | XCTAssertEqual(dictionaryFromStruct["string"] as? String, "How many roads") 39 | XCTAssertEqual(dictionaryFromStruct["flag"] as? Bool, true) 40 | XCTAssertNil(dictionaryFromStruct["optionalFloat"]) 41 | } 42 | 43 | func testCodableNameConflict() throws { 44 | let testDecodableStruct = TestDecodableStruct(dictionary: ["number": 1, "string": "Sweeter"]) 45 | XCTAssertEqual(testDecodableStruct?.number, 42) 46 | XCTAssertEqual(testDecodableStruct?.string, "Override Sweeter init") 47 | } 48 | } 49 | 50 | struct TestDecodableStruct { 51 | var number: Int 52 | var string: String 53 | } 54 | 55 | extension TestDecodableStruct: Decodable {} 56 | 57 | extension TestDecodableStruct { 58 | init?(dictionary: [String: Any]) { 59 | self.init(number: 42, string: "Override Sweeter init") 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) 5 | and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). 6 | 7 | ## [1.2.6] - 2023-08-19 8 | 9 | ### Fixed 10 | - fix privacy manifest PrivacyInfo.xcprivacy. 11 | 12 | ## [1.2.2] - 2023-08-19 13 | 14 | ### Added 15 | - add privacy manifest PrivacyInfo.xcprivacy. 16 | 17 | ## [1.2.1] - 2022-04-07 18 | 19 | ### Fixed 20 | - fix crash on iOS 14 when using Xcode 13.3. (thanks tylerjames!) 21 | 22 | ## [1.2.0] - 2022-02-01 23 | 24 | ### Added 25 | - add to `NSManagedObjectContext.printAllObjects()` optional variadic parameter `entityName`. 26 | - add `NSManagedObjectContext.deleteAllObjects()`. 27 | 28 | ## [1.1.5] - 2021-05-15 29 | 30 | ### Fixed 31 | - fix #3 tvOS build error. 32 | 33 | ## [1.1.4] - 2020-09-21 34 | 35 | ### Added 36 | - add `UIApplication.shared.legacyKeyWindow` to get `keyWindow` for scene-based apps. 37 | 38 | ## [1.1.3] - 2020-02-23 39 | 40 | ### Changed 41 | - rename `Decodable.init(from:)` to `Decodable.init(dictionary:)` to avoid clash with R.swift . 42 | 43 | ## [1.1.2] - 2020-02-07 44 | 45 | ### Fixed 46 | - fix build error when importing using spm. 47 | 48 | ## [1.1.1] - 2020-02-05 49 | 50 | ### Fixed 51 | - fix crash on third-party code that doesn't implement default constructor for classes derived from NSObject. 52 | 53 | ## [1.1.0] - 2020-02-03 54 | 55 | ### Added 56 | - add optional `identifier` parameter to `UIView.constrain`. 57 | - add `weak(self, in: MyClass.foo)` to avoid retain cycles when passing a member function as an @escaping closure. 58 | 59 | ## [1.0.5] - 2019-09-21 60 | 61 | ### Fixed 62 | - mark `simulatedLabelWithLinksInText` unavailable in tvOS, so that interface builder will not show an erroneous error. 63 | 64 | ## [1.0.4] - 2019-09-16 65 | 66 | ### Added 67 | - add operator =? to assign iff right side is not nil (useful for wrapping old UIView classes in new SwiftUI wrappers). 68 | 69 | ## [1.0.2] - 2019-08-22 70 | 71 | ### Added 72 | - support Swift Package Manager. 73 | 74 | ## [1.0.1] - 2019-07-23 75 | 76 | ### Added 77 | - optional priority for UIView constraints. 78 | 79 | ## [1.0.0] 80 | 81 | ### Added 82 | - initial release. 83 | -------------------------------------------------------------------------------- /SweeterSwift/SweeterSwiftTests/SwiftTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftTests.swift 3 | // swiftlint:disable no_magic_numbers 4 | // 5 | // Created by Yonat Sharon on 2019-06-29. 6 | // Copyright © 2019 Yonat Sharon. All rights reserved. 7 | // 8 | 9 | import SweeterSwift 10 | import UIKit 11 | import XCTest 12 | 13 | enum SwiftTestEnum: CaseIterable { 14 | case first, second, third, fourth, fifth, sixth 15 | } 16 | 17 | class SwiftTests: XCTestCase { 18 | func testEnumIndex() { 19 | XCTAssertEqual(SwiftTestEnum.first.index, 0) 20 | XCTAssertEqual(SwiftTestEnum.third.index, 2) 21 | XCTAssertEqual(SwiftTestEnum.sixth.index, 5) 22 | } 23 | 24 | func testCompactSequence() { 25 | let arrayOfOptionals: [Int?] = [0, 1, nil, 2, nil, nil, 3] 26 | XCTAssertEqual(arrayOfOptionals.compact, [0, 1, 2, 3]) 27 | } 28 | 29 | func testWeakSelfInMemberFunction() { 30 | class Foo: DefaultConstructible { 31 | static var refCount = 0 32 | 33 | func foo() {} 34 | func fooWithParam(n: Int) {} 35 | func fooWithReturn() -> String { "" } 36 | func fooWithParamAndReturn(array: [Character]) -> Double { 0 } 37 | 38 | lazy var fooRef = weak(self, in: Foo.foo) 39 | lazy var fooWithParamRef = weak(self, in: Foo.fooWithParam) 40 | lazy var fooWithReturnRef = weak(self, in: Foo.fooWithReturn) 41 | lazy var fooWithParamAndReturnRef = weak(self, in: Foo.fooWithParamAndReturn) 42 | 43 | required init() { 44 | fooRef() 45 | fooWithParamRef(7) 46 | _ = fooWithReturn() 47 | _ = fooWithParamAndReturnRef([]) 48 | Foo.refCount += 1 49 | } 50 | 51 | deinit { 52 | Foo.refCount -= 1 53 | } 54 | } 55 | 56 | XCTAssertEqual(Foo.refCount, 0) 57 | 58 | for _ in 0 ..< 3 { 59 | let aFoo = Foo() 60 | aFoo.foo() 61 | XCTAssertEqual(Foo.refCount, 1) 62 | } 63 | 64 | XCTAssertEqual(Foo.refCount, 0) 65 | } 66 | 67 | func testOptionableValue() { 68 | let optionalStepper: UIStepper? = UIStepper() 69 | optionalStepper?.value = 42 70 | XCTAssertEqual(optionalStepper?.value, 42) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Source/NSManagedObjectContext+Sweeter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSManagedObjectContext+Sweeter.swift 3 | // 4 | // Created by Yonat Sharon on 2019-02-08. 5 | // 6 | 7 | import CoreData 8 | 9 | public extension NSManagedObjectContext { 10 | /// Sweeter: Names of all entities in the object model associated with the receiver 11 | var allEntityNames: [String] { 12 | return persistentStoreCoordinator?.managedObjectModel.entities.compactMap(\.name) ?? [] 13 | } 14 | 15 | /// Sweeter: Delete all objects, or all objects of specific entity name(s). 16 | func deleteAllObjects(entityName: String...) throws { 17 | let entityNames = entityName.isEmpty ? allEntityNames : entityName 18 | for entityName in entityNames { 19 | let deleteFetch = NSFetchRequest(entityName: entityName) 20 | let deleteRequest = NSBatchDeleteRequest(fetchRequest: deleteFetch) 21 | try execute(deleteRequest) 22 | } 23 | } 24 | 25 | /// Sweeter: Dump contents to console - for debugging 26 | func printAllObjects(entityName: String...) { 27 | let entityNames = entityName.isEmpty ? allEntityNames : entityName 28 | for entityName in entityNames { 29 | guard let objects = try? fetch(NSFetchRequest(entityName: entityName)) else { continue } 30 | print("== \(entityName) (\(objects.count)) ==") 31 | for object in objects { 32 | print(String(describing: object)) 33 | } 34 | } 35 | } 36 | 37 | /// Sweeter: Create a copy of the store for backup or for using later as initial setup 38 | func backupStore() { 39 | guard let persistentStoreCoordinator = persistentStoreCoordinator else { return } 40 | guard let sourceStore = persistentStoreCoordinator.persistentStores.first else { return } 41 | let backupCoordinator = NSPersistentStoreCoordinator(managedObjectModel: persistentStoreCoordinator.managedObjectModel) 42 | 43 | let intermediateStoreOptions = (sourceStore.options ?? [:]).merging([NSReadOnlyPersistentStoreOption: true], uniquingKeysWith: { $1 }) 44 | guard let intermediateStore = try? backupCoordinator.addPersistentStore( 45 | ofType: sourceStore.type, 46 | configurationName: sourceStore.configurationName, 47 | at: sourceStore.url, 48 | options: intermediateStoreOptions 49 | ) else { return } 50 | 51 | let backupStoreOptions: [AnyHashable: Any] = [ 52 | NSReadOnlyPersistentStoreOption: true, 53 | // Disable write-ahead logging so that the entire store will be 54 | // contained in a single file. No need to handle -wal/-shm files. 55 | // https://developer.apple.com/library/content/qa/qa1809/_index.html 56 | NSSQLitePragmasOption: ["journal_mode": "DELETE"], 57 | // Minimize file size 58 | NSSQLiteManualVacuumOption: true, 59 | ] 60 | 61 | let basename = sourceStore.url?.deletingPathExtension().lastPathComponent ?? "store" 62 | let documentsDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last ?? URL(fileURLWithPath: ".") 63 | let backupURL = documentsDirectory.appendingPathComponent("\(basename)-backup.sqlite") 64 | print("Backed up store to " + backupURL.path) 65 | _ = try? backupCoordinator.migratePersistentStore(intermediateStore, to: backupURL, options: backupStoreOptions, withType: NSSQLiteStoreType) 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Source/Swift+Sweeter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Swift+Sweeter.swift 3 | // 4 | // Created by Yonat Sharon on 2019-02-21. 5 | // 6 | 7 | public extension CaseIterable where Self: Equatable { 8 | /// Sweeter: Index of current case in `allCases`. 9 | var index: Int { 10 | let all = Array(Self.allCases) 11 | return all.firstIndex(of: self)! // swiftlint:disable:this force_unwrapping 12 | } 13 | } 14 | 15 | // from https://gist.github.com/siejkowski/a2b187800f2e28b53c96 16 | 17 | public extension Sequence where Element: Optionable { 18 | /// Sweeter: Shorthand for `compactMap { $0 }`. 19 | var compact: [Element.Wrapped] { 20 | return compactMap { $0.value } 21 | } 22 | } 23 | 24 | public protocol Optionable { 25 | associatedtype Wrapped 26 | var value: Wrapped? { get } 27 | } 28 | 29 | // extension for Optional provides the implementations for Optional enum 30 | extension Optional: Optionable { 31 | public var value: Wrapped? { return self } 32 | } 33 | 34 | infix operator =?: AssignmentPrecedence 35 | 36 | /// Sweeter: Assign iff right side is not nil. 37 | public func =? (lhs: inout T, rhs: T?) { 38 | if nil != rhs, let rhs = rhs { 39 | lhs = rhs 40 | } 41 | } 42 | 43 | public protocol DefaultConstructible { 44 | init() 45 | } 46 | 47 | extension Optional: DefaultConstructible { 48 | public init() { 49 | self = .none 50 | } 51 | } 52 | 53 | extension IntegerLiteralType: DefaultConstructible {} 54 | extension BooleanLiteralType: DefaultConstructible {} 55 | extension FloatLiteralType: DefaultConstructible {} 56 | extension StringLiteralType: DefaultConstructible {} 57 | extension Array: DefaultConstructible {} 58 | extension Dictionary: DefaultConstructible {} 59 | extension Set: DefaultConstructible {} 60 | 61 | // based on https://sveinhal.github.io/2016/03/16/retain-cycles-function-references/ 62 | 63 | /// Sweeter: Pass a member function as an @escaping closure without retaining its object. 64 | /// 65 | /// Example: `var closure = weak(self, in: MyClass.someFunction)` 66 | public func weak(_ instance: T, in classFunction: @escaping (T) -> () -> Void) -> () -> Void { 67 | return { [weak instance] in 68 | guard let instance = instance else { return } 69 | classFunction(instance)() 70 | } 71 | } 72 | 73 | /// Sweeter: Pass a member function as an @escaping closure without retaining its object. 74 | /// 75 | /// Example: `var closure = weak(self, in: MyClass.someFunction)` 76 | public func weak(_ instance: T, in classFunction: @escaping (T) -> (U) -> Void) -> (U) -> Void { 77 | return { [weak instance] arguments in 78 | guard let instance = instance else { return } 79 | classFunction(instance)(arguments) 80 | } 81 | } 82 | 83 | /// Sweeter: Pass a member function as an @escaping closure without retaining its object. 84 | /// 85 | /// Example: `var closure = weak(self, in: MyClass.someFunction)` 86 | public func weak(_ instance: T, in classFunction: @escaping (T) -> () -> V) -> () -> V { 87 | return { [weak instance] in 88 | guard let instance = instance else { return V() } 89 | return classFunction(instance)() 90 | } 91 | } 92 | 93 | /// Sweeter: Pass a member function as an @escaping closure without retaining its object. 94 | /// 95 | /// Example: `var closure = weak(self, in: MyClass.someFunction)` 96 | public func weak(_ instance: T, in classFunction: @escaping (T) -> (U) -> V) -> (U) -> V { 97 | return { [weak instance] arguments in 98 | guard let instance = instance else { return V() } 99 | return classFunction(instance)(arguments) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /SweeterSwift/SweeterSwift.xcodeproj/xcshareddata/xcschemes/SweeterSwift.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 50 | 51 | 52 | 53 | 54 | 55 | 65 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 84 | 90 | 91 | 92 | 93 | 95 | 96 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /Source/UIView+Sweeter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+Sweeter.swift 3 | // 4 | // Created by Yonat Sharon on 2019-02-08. 5 | // 6 | 7 | import UIKit 8 | 9 | public extension UIView { 10 | /// Sweeter: Set constant attribute. Example: `constrain(.width, to: 17)` 11 | @discardableResult func constrain( 12 | _ at: NSLayoutConstraint.Attribute, 13 | to: CGFloat = 0, 14 | ratio: CGFloat = 1, 15 | relation: NSLayoutConstraint.Relation = .equal, 16 | priority: UILayoutPriority = .required, 17 | identifier: String? = nil 18 | ) -> NSLayoutConstraint { 19 | let constraint = NSLayoutConstraint( 20 | item: self, attribute: at, relatedBy: relation, 21 | toItem: nil, attribute: .notAnAttribute, multiplier: ratio, constant: to 22 | ) 23 | constraint.priority = priority 24 | constraint.identifier = identifier 25 | addConstraintWithoutConflict(constraint) 26 | return constraint 27 | } 28 | 29 | /// Sweeter: Pin subview at a specific place. Example: `constrain(label, at: .top)` 30 | @discardableResult func constrain( 31 | _ subview: UIView, 32 | at: NSLayoutConstraint.Attribute, 33 | diff: CGFloat = 0, 34 | ratio: CGFloat = 1, 35 | relation: NSLayoutConstraint.Relation = .equal, 36 | priority: UILayoutPriority = .required, 37 | identifier: String? = nil 38 | ) -> NSLayoutConstraint { 39 | let constraint = NSLayoutConstraint( 40 | item: subview, attribute: at, relatedBy: relation, 41 | toItem: self, attribute: at, multiplier: ratio, constant: diff 42 | ) 43 | constraint.priority = priority 44 | constraint.identifier = identifier 45 | addConstraintWithoutConflict(constraint) 46 | return constraint 47 | } 48 | 49 | /// Sweeter: Pin two subviews to each other. Example: 50 | /// 51 | /// `constrain(label, at: .leading, to: textField)` 52 | /// 53 | /// `constrain(textField, at: .top, to: label, at: .bottom, diff: 8)` 54 | @discardableResult func constrain( 55 | _ subview: UIView, 56 | at: NSLayoutConstraint.Attribute, 57 | to subview2: UIView, 58 | at at2: NSLayoutConstraint.Attribute = .notAnAttribute, 59 | diff: CGFloat = 0, 60 | ratio: CGFloat = 1, 61 | relation: NSLayoutConstraint.Relation = .equal, 62 | priority: UILayoutPriority = .required, 63 | identifier: String? = nil 64 | ) -> NSLayoutConstraint { 65 | let at2real = at2 == .notAnAttribute ? at : at2 66 | let constraint = NSLayoutConstraint( 67 | item: subview, attribute: at, relatedBy: relation, 68 | toItem: subview2, attribute: at2real, multiplier: ratio, constant: diff 69 | ) 70 | constraint.priority = priority 71 | constraint.identifier = identifier 72 | addConstraintWithoutConflict(constraint) 73 | return constraint 74 | } 75 | 76 | /// Sweeter: Add subview pinned to specific places. Example: `addConstrainedSubview(button, constrain: .centerX, .centerY)` 77 | @discardableResult func addConstrainedSubview(_ subview: UIView, constrain: NSLayoutConstraint.Attribute...) -> [NSLayoutConstraint] { 78 | return addConstrainedSubview(subview, constrainedAttributes: constrain) 79 | } 80 | 81 | @discardableResult internal func addConstrainedSubview(_ subview: UIView, constrainedAttributes: [NSLayoutConstraint.Attribute]) -> [NSLayoutConstraint] { 82 | subview.translatesAutoresizingMaskIntoConstraints = false 83 | addSubview(subview) 84 | return constrainedAttributes.map { self.constrain(subview, at: $0) } 85 | } 86 | 87 | internal func addConstraintWithoutConflict(_ constraint: NSLayoutConstraint) { 88 | removeConstraints(constraints.filter { 89 | constraint.firstItem === $0.firstItem 90 | && constraint.secondItem === $0.secondItem 91 | && constraint.firstAttribute == $0.firstAttribute 92 | && constraint.secondAttribute == $0.secondAttribute 93 | }) 94 | addConstraint(constraint) 95 | } 96 | 97 | /// Sweeter: Search the view hierarchy recursively for a subview that conforms to `predicate` 98 | func viewInHierarchy(frontFirst: Bool = true, where predicate: (UIView) -> Bool) -> UIView? { 99 | if predicate(self) { return self } 100 | let views = frontFirst ? subviews.reversed() : subviews 101 | for subview in views { 102 | if let found = subview.viewInHierarchy(frontFirst: frontFirst, where: predicate) { 103 | return found 104 | } 105 | } 106 | return nil 107 | } 108 | 109 | /// Sweeter: Search the view hierarchy recursively for a subview with `aClass` 110 | func viewWithClass(_ aClass: T.Type, frontFirst: Bool = true) -> T? { 111 | return viewInHierarchy(frontFirst: frontFirst, where: { $0 is T }) as? T 112 | } 113 | 114 | /// Sweeter: The color used to tint the view, as inherited from its superviews. 115 | var actualTintColor: UIColor { 116 | var tintedView: UIView? = self 117 | while let currentView = tintedView, nil == currentView.tintColor { 118 | tintedView = currentView.superview 119 | } 120 | return tintedView?.tintColor ?? UIColor(red: 0, green: 0.5, blue: 1, alpha: 1) // swiftlint:disable:this no_magic_numbers 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SweeterSwift 2 | 3 | 4 | [![Swift Version][swift-image]][swift-url] 5 | [![Build Status][travis-image]][travis-url] 6 | [![License][license-image]][license-url] 7 | [![CocoaPods Compatible](https://img.shields.io/cocoapods/v/SweeterSwift.svg)](https://img.shields.io/cocoapods/v/SweeterSwift.svg) 8 | [![Platform](https://img.shields.io/cocoapods/p/SweeterSwift.svg?style=flat)](http://cocoapods.org/pods/SweeterSwift) 9 | [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com) 10 | 11 | Extensions and syntactic sugar to enrich the Swift standard library, iOS frameworks, and [SwifterSwift](https://github.com/SwifterSwift/SwifterSwift). 12 | 13 | ## Installation 14 | 15 | ### CocoaPods: 16 | 17 | ```ruby 18 | pod 'SweeterSwift' 19 | ``` 20 | 21 | ### Swift Package Manager: 22 | 23 | ```swift 24 | dependencies: [ 25 | .package(url: "https://github.com/yonat/SweeterSwift", from: "1.2.6") 26 | ] 27 | ``` 28 | 29 | ## Usage 30 | 31 | * [Auto Layout](#auto-layout) 32 | * [Bundle](#bundle) 33 | * [Codable](#codable) 34 | * [DateFormatter](#dateformatter) 35 | * [NotificationCenter](#notificationcenter) 36 | * [NSAttributedString](#nsattributedstring) 37 | * [NSManagedObjectContext](#nsmanagedobjectcontext) 38 | * [String](#string) 39 | * [Swift Standard Library](#swift-standard-library) 40 | * [TimeInterval](#timeinterval) 41 | * [UIApplication](#uiapplication) 42 | * [UILabel / UITextView](#uilabel-uitextview) 43 | * [UIStackView](#uistackview) 44 | * [UIView](#uiview) 45 | 46 | 47 | ### Auto Layout 48 | 49 | Add button at the center of view: 50 | 51 | ```swift 52 | view.addConstrainedSubview(button, constrain: .centerX, .centerY) 53 | ``` 54 | 55 | Put label over textField: 56 | 57 | ```swift 58 | view.constrain(label, at: .leading, to: textField) 59 | view.constrain(textField, at: .top, to: label, at: .bottom, diff: 8) 60 | ``` 61 | 62 | Add child view controller covering all but the bottom margin: 63 | 64 | ```swift 65 | addConstrainedChild(vc, constrain: .bottomMargin, .top, .left, .right) 66 | ``` 67 | 68 | ### Bundle 69 | 70 | App name with reasonable fallback to process name: 71 | 72 | ```swift 73 | let appName = Bundle.main.name 74 | ``` 75 | 76 | App info string with name, version, and build number: 77 | 78 | ```swift 79 | let appInfo = Bundle.main.infoString // "MyApp 1.0 #42" 80 | ``` 81 | 82 | ### Codable 83 | 84 | Create object from a dictionary: 85 | 86 | ```swift 87 | let decodableObject = MyDecodableClass(dictionary: aDictionary) 88 | ``` 89 | 90 | Export object to a dictionary: 91 | 92 | ```swift 93 | let aDictionary = encodableObject.dictionary 94 | ``` 95 | 96 | ### DateFormatter 97 | 98 | Create with format: 99 | 100 | ```swift 101 | let dateFormatter = DateFormatter(format: "cccc, MMMM dd") 102 | ``` 103 | 104 | ### NotificationCenter 105 | 106 | Post a local notification using `NotificationCenter.default`: 107 | 108 | ```swift 109 | notify(notificationName, userInfo: infoDictionary) 110 | ``` 111 | 112 | Respond to a local notification from `NotificationCenter.default`: 113 | 114 | ```swift 115 | observeNotification(notificationName, selector: #selector(someFunc)) 116 | ``` 117 | 118 | ### NSAttributedString 119 | 120 | Create from HTML: 121 | 122 | ```swift 123 | let attributedString = NSAttributedString(htmlString: "Hello, world!") 124 | ``` 125 | 126 | Turn a substring into a link: 127 | 128 | ```swift 129 | mutableAttributedString.link(anchorText: "Hello", url: "https://ootips.org") 130 | ``` 131 | 132 | ### NSManagedObjectContext 133 | 134 | Dump contents to console for debugging: 135 | 136 | ```swift 137 | managedObjectContext.printAllObjects() 138 | ``` 139 | 140 | Create a copy of the store for backup or for using later as initial setup: 141 | 142 | ```swift 143 | managedObjectContext.backupStore() 144 | ``` 145 | 146 | ### String 147 | 148 | Separate CamelCase into capitalized words: 149 | 150 | ```swift 151 | let words = "winterIsComing".unCamelCased // "Winter Is Coming" 152 | ``` 153 | 154 | Change CamelCase into snake_case: 155 | 156 | ```swift 157 | let key = "winterIsComing".camelToSnakeCased // "winter_is_coming" 158 | ``` 159 | 160 | ### Swift Standard Library 161 | 162 | Index of current enum case in `allCases`: 163 | 164 | ```swift 165 | let index = MyEnum.someCase.index 166 | ``` 167 | 168 | Unwrap collection, shorthand for `compactMap { $0 }`: 169 | 170 | ```swift 171 | let nonOptionals = optionalsCollection.compact 172 | ``` 173 | 174 | Avoid retain cycles when passing a member function as an @escaping closure: 175 | 176 | ```swift 177 | var closure = weak(self, in: MyClass.someFunction) 178 | ``` 179 | 180 | ### TimeInterval 181 | 182 | Standard time intervals: 183 | 184 | ```swift 185 | let myInterval: TimeInterval = .year + .month + .week + .day + .hour + .minute 186 | ``` 187 | 188 | ### UIApplication 189 | 190 | Find the top-most view controller: 191 | 192 | ```swift 193 | let topVC = UIApplication.topViewController() 194 | ``` 195 | 196 | Present modal over the top view controller: 197 | 198 | ```swift 199 | UIApplication.present(modalVC) 200 | ``` 201 | 202 | ### UILabel / UITextView 203 | 204 | Create a label with links by using a `UITextView` to auto-detect links and simulate `UILabel` appearance: 205 | 206 | ```swift 207 | let hyperlinkedLabel = UITextView(simulatedLabelWithLinksInText: "More at https://ootips.org") 208 | ``` 209 | 210 | ### UIStackView 211 | 212 | Remove an arranged subview from the view hierarchy, not just the stack arrangement: 213 | 214 | ```swift 215 | stackView.removeArrangedSubviewCompletely(subview) 216 | ``` 217 | 218 | Remove all arranged subviews from the view hierarchy, not just the stack arrangement: 219 | 220 | ```swift 221 | stackView.removeAllArrangedSubviewsCompletely() 222 | ``` 223 | 224 | ### UIView 225 | 226 | Search the view hierarchy recursively for a subview that meets a condition: 227 | 228 | ```swift 229 | let blueSubview = view.viewInHierarchy(where { $0.backgroundColor == .blue }) 230 | ``` 231 | 232 | Search the view hierarchy recursively for a subview with a specific class: 233 | 234 | ```swift 235 | let button = view.viewWithClass(UIButton.self) 236 | ``` 237 | 238 | 239 | ## Meta 240 | 241 | [@yonatsharon](https://twitter.com/yonatsharon) 242 | 243 | [https://github.com/yonat/SweeterSwift](https://github.com/yonat/SweeterSwift) 244 | 245 | [swift-image]:https://img.shields.io/badge/swift-5.0-orange.svg 246 | [swift-url]: https://swift.org/ 247 | [license-image]: https://img.shields.io/badge/License-MIT-blue.svg 248 | [license-url]: LICENSE.txt 249 | [travis-image]: https://img.shields.io/travis/dbader/node-datadog-metrics/master.svg?style=flat-square 250 | [travis-url]: https://travis-ci.org/dbader/node-datadog-metrics 251 | -------------------------------------------------------------------------------- /SweeterSwift/SweeterSwift.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 51; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 07284F5B590A0ED462322095 /* Pods_SweeterSwiftTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FC2984858EBCDD93C0E95C87 /* Pods_SweeterSwiftTests.framework */; }; 11 | 860DDA0DD3CCFF0B1A997E00 /* Pods_SweeterSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8A1861DE58465E6B65F6095D /* Pods_SweeterSwift.framework */; }; 12 | DC7BD85722C7C4FB0046FFFA /* SweeterSwift.h in Headers */ = {isa = PBXBuildFile; fileRef = DC7BD84922C7C4FB0046FFFA /* SweeterSwift.h */; settings = {ATTRIBUTES = (Public, ); }; }; 13 | DC7BD86722C7C7B00046FFFA /* SwiftTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC7BD86622C7C7B00046FFFA /* SwiftTests.swift */; }; 14 | DC7BD86922C7C7B00046FFFA /* SweeterSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC7BD84622C7C4FB0046FFFA /* SweeterSwift.framework */; }; 15 | DC7BD88222C7CA1E0046FFFA /* NSAttributedString+Sweeter.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC7BD87422C7CA1E0046FFFA /* NSAttributedString+Sweeter.swift */; }; 16 | DC7BD88322C7CA1E0046FFFA /* DateFormatter+Sweeter.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC7BD87522C7CA1E0046FFFA /* DateFormatter+Sweeter.swift */; }; 17 | DC7BD88422C7CA1E0046FFFA /* String+Sweeter.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC7BD87622C7CA1E0046FFFA /* String+Sweeter.swift */; }; 18 | DC7BD88522C7CA1E0046FFFA /* UIApplication+Sweeter.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC7BD87722C7CA1E0046FFFA /* UIApplication+Sweeter.swift */; }; 19 | DC7BD88622C7CA1E0046FFFA /* UITextView+Sweeter.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC7BD87822C7CA1E0046FFFA /* UITextView+Sweeter.swift */; }; 20 | DC7BD88722C7CA1E0046FFFA /* NSManagedObjectContext+Sweeter.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC7BD87922C7CA1E0046FFFA /* NSManagedObjectContext+Sweeter.swift */; }; 21 | DC7BD88822C7CA1E0046FFFA /* Bundle+Sweeter.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC7BD87A22C7CA1E0046FFFA /* Bundle+Sweeter.swift */; }; 22 | DC7BD88922C7CA1E0046FFFA /* Codable+Sweeter.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC7BD87B22C7CA1E0046FFFA /* Codable+Sweeter.swift */; }; 23 | DC7BD88A22C7CA1E0046FFFA /* Swift+Sweeter.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC7BD87C22C7CA1E0046FFFA /* Swift+Sweeter.swift */; }; 24 | DC7BD88B22C7CA1E0046FFFA /* UIViewController+Sweeter.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC7BD87D22C7CA1E0046FFFA /* UIViewController+Sweeter.swift */; }; 25 | DC7BD88C22C7CA1E0046FFFA /* UIStackView+Sweeter.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC7BD87E22C7CA1E0046FFFA /* UIStackView+Sweeter.swift */; }; 26 | DC7BD88D22C7CA1E0046FFFA /* NSObject+Sweeter.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC7BD87F22C7CA1E0046FFFA /* NSObject+Sweeter.swift */; }; 27 | DC7BD88E22C7CA1E0046FFFA /* TimeInterval+Sweeter.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC7BD88022C7CA1E0046FFFA /* TimeInterval+Sweeter.swift */; }; 28 | DC7BD88F22C7CA1E0046FFFA /* UIView+Sweeter.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC7BD88122C7CA1E0046FFFA /* UIView+Sweeter.swift */; }; 29 | DC7BD89122C7DAF70046FFFA /* NSAttributedStringTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC7BD89022C7DAF70046FFFA /* NSAttributedStringTests.swift */; }; 30 | DC7BD89322C7DC620046FFFA /* StringTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC7BD89222C7DC620046FFFA /* StringTests.swift */; }; 31 | DC7BD89522C7EA2B0046FFFA /* BundleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC7BD89422C7EA2B0046FFFA /* BundleTests.swift */; }; 32 | DC7BD89722C7EAC40046FFFA /* CodableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DC7BD89622C7EAC40046FFFA /* CodableTests.swift */; }; 33 | /* End PBXBuildFile section */ 34 | 35 | /* Begin PBXContainerItemProxy section */ 36 | DC7BD86A22C7C7B00046FFFA /* PBXContainerItemProxy */ = { 37 | isa = PBXContainerItemProxy; 38 | containerPortal = DC7BD83D22C7C4FB0046FFFA /* Project object */; 39 | proxyType = 1; 40 | remoteGlobalIDString = DC7BD84522C7C4FB0046FFFA; 41 | remoteInfo = SweeterSwift; 42 | }; 43 | /* End PBXContainerItemProxy section */ 44 | 45 | /* Begin PBXFileReference section */ 46 | 2CCD433E24A0BDB1106A3D61 /* Pods-SweeterSwift.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SweeterSwift.debug.xcconfig"; path = "Target Support Files/Pods-SweeterSwift/Pods-SweeterSwift.debug.xcconfig"; sourceTree = ""; }; 47 | 7E30926EC2BD4FB85BB3108B /* Pods-SweeterSwiftTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SweeterSwiftTests.release.xcconfig"; path = "Target Support Files/Pods-SweeterSwiftTests/Pods-SweeterSwiftTests.release.xcconfig"; sourceTree = ""; }; 48 | 841073221FF5B2AF5BB49742 /* Pods-SweeterSwift.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SweeterSwift.release.xcconfig"; path = "Target Support Files/Pods-SweeterSwift/Pods-SweeterSwift.release.xcconfig"; sourceTree = ""; }; 49 | 8A1861DE58465E6B65F6095D /* Pods_SweeterSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SweeterSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 50 | DC7BD84622C7C4FB0046FFFA /* SweeterSwift.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SweeterSwift.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 51 | DC7BD84922C7C4FB0046FFFA /* SweeterSwift.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SweeterSwift.h; sourceTree = ""; }; 52 | DC7BD84A22C7C4FB0046FFFA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 53 | DC7BD86422C7C7B00046FFFA /* SweeterSwiftTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SweeterSwiftTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 54 | DC7BD86622C7C7B00046FFFA /* SwiftTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftTests.swift; sourceTree = ""; }; 55 | DC7BD86822C7C7B00046FFFA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 56 | DC7BD87422C7CA1E0046FFFA /* NSAttributedString+Sweeter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSAttributedString+Sweeter.swift"; sourceTree = ""; }; 57 | DC7BD87522C7CA1E0046FFFA /* DateFormatter+Sweeter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DateFormatter+Sweeter.swift"; sourceTree = ""; }; 58 | DC7BD87622C7CA1E0046FFFA /* String+Sweeter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+Sweeter.swift"; sourceTree = ""; }; 59 | DC7BD87722C7CA1E0046FFFA /* UIApplication+Sweeter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIApplication+Sweeter.swift"; sourceTree = ""; }; 60 | DC7BD87822C7CA1E0046FFFA /* UITextView+Sweeter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UITextView+Sweeter.swift"; sourceTree = ""; }; 61 | DC7BD87922C7CA1E0046FFFA /* NSManagedObjectContext+Sweeter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSManagedObjectContext+Sweeter.swift"; sourceTree = ""; }; 62 | DC7BD87A22C7CA1E0046FFFA /* Bundle+Sweeter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Bundle+Sweeter.swift"; sourceTree = ""; }; 63 | DC7BD87B22C7CA1E0046FFFA /* Codable+Sweeter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Codable+Sweeter.swift"; sourceTree = ""; }; 64 | DC7BD87C22C7CA1E0046FFFA /* Swift+Sweeter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Swift+Sweeter.swift"; sourceTree = ""; }; 65 | DC7BD87D22C7CA1E0046FFFA /* UIViewController+Sweeter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIViewController+Sweeter.swift"; sourceTree = ""; }; 66 | DC7BD87E22C7CA1E0046FFFA /* UIStackView+Sweeter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIStackView+Sweeter.swift"; sourceTree = ""; }; 67 | DC7BD87F22C7CA1E0046FFFA /* NSObject+Sweeter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+Sweeter.swift"; sourceTree = ""; }; 68 | DC7BD88022C7CA1E0046FFFA /* TimeInterval+Sweeter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TimeInterval+Sweeter.swift"; sourceTree = ""; }; 69 | DC7BD88122C7CA1E0046FFFA /* UIView+Sweeter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIView+Sweeter.swift"; sourceTree = ""; }; 70 | DC7BD89022C7DAF70046FFFA /* NSAttributedStringTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSAttributedStringTests.swift; sourceTree = ""; }; 71 | DC7BD89222C7DC620046FFFA /* StringTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StringTests.swift; sourceTree = ""; }; 72 | DC7BD89422C7EA2B0046FFFA /* BundleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BundleTests.swift; sourceTree = ""; }; 73 | DC7BD89622C7EAC40046FFFA /* CodableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodableTests.swift; sourceTree = ""; }; 74 | DC7BD89822C7F68A0046FFFA /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; 75 | DC7BD89922C7F68A0046FFFA /* CHANGELOG.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = CHANGELOG.md; path = ../CHANGELOG.md; sourceTree = ""; }; 76 | EB5626A87EBAC9FB4EE89466 /* Pods-SweeterSwiftTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SweeterSwiftTests.debug.xcconfig"; path = "Target Support Files/Pods-SweeterSwiftTests/Pods-SweeterSwiftTests.debug.xcconfig"; sourceTree = ""; }; 77 | FC2984858EBCDD93C0E95C87 /* Pods_SweeterSwiftTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SweeterSwiftTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 78 | /* End PBXFileReference section */ 79 | 80 | /* Begin PBXFrameworksBuildPhase section */ 81 | DC7BD84322C7C4FB0046FFFA /* Frameworks */ = { 82 | isa = PBXFrameworksBuildPhase; 83 | buildActionMask = 2147483647; 84 | files = ( 85 | 860DDA0DD3CCFF0B1A997E00 /* Pods_SweeterSwift.framework in Frameworks */, 86 | ); 87 | runOnlyForDeploymentPostprocessing = 0; 88 | }; 89 | DC7BD86122C7C7B00046FFFA /* Frameworks */ = { 90 | isa = PBXFrameworksBuildPhase; 91 | buildActionMask = 2147483647; 92 | files = ( 93 | DC7BD86922C7C7B00046FFFA /* SweeterSwift.framework in Frameworks */, 94 | 07284F5B590A0ED462322095 /* Pods_SweeterSwiftTests.framework in Frameworks */, 95 | ); 96 | runOnlyForDeploymentPostprocessing = 0; 97 | }; 98 | /* End PBXFrameworksBuildPhase section */ 99 | 100 | /* Begin PBXGroup section */ 101 | A973AB6F69C64733BFAD39F4 /* Frameworks */ = { 102 | isa = PBXGroup; 103 | children = ( 104 | 8A1861DE58465E6B65F6095D /* Pods_SweeterSwift.framework */, 105 | FC2984858EBCDD93C0E95C87 /* Pods_SweeterSwiftTests.framework */, 106 | ); 107 | name = Frameworks; 108 | sourceTree = ""; 109 | }; 110 | D50430A535E3B884148CE11D /* Pods */ = { 111 | isa = PBXGroup; 112 | children = ( 113 | 2CCD433E24A0BDB1106A3D61 /* Pods-SweeterSwift.debug.xcconfig */, 114 | 841073221FF5B2AF5BB49742 /* Pods-SweeterSwift.release.xcconfig */, 115 | EB5626A87EBAC9FB4EE89466 /* Pods-SweeterSwiftTests.debug.xcconfig */, 116 | 7E30926EC2BD4FB85BB3108B /* Pods-SweeterSwiftTests.release.xcconfig */, 117 | ); 118 | path = Pods; 119 | sourceTree = ""; 120 | }; 121 | DC7BD83C22C7C4FB0046FFFA = { 122 | isa = PBXGroup; 123 | children = ( 124 | DC7BD89822C7F68A0046FFFA /* README.md */, 125 | DC7BD89922C7F68A0046FFFA /* CHANGELOG.md */, 126 | DC7BD87322C7CA1E0046FFFA /* Source */, 127 | DC7BD84822C7C4FB0046FFFA /* SweeterSwift */, 128 | DC7BD86522C7C7B00046FFFA /* SweeterSwiftTests */, 129 | DC7BD84722C7C4FB0046FFFA /* Products */, 130 | D50430A535E3B884148CE11D /* Pods */, 131 | A973AB6F69C64733BFAD39F4 /* Frameworks */, 132 | ); 133 | sourceTree = ""; 134 | }; 135 | DC7BD84722C7C4FB0046FFFA /* Products */ = { 136 | isa = PBXGroup; 137 | children = ( 138 | DC7BD84622C7C4FB0046FFFA /* SweeterSwift.framework */, 139 | DC7BD86422C7C7B00046FFFA /* SweeterSwiftTests.xctest */, 140 | ); 141 | name = Products; 142 | sourceTree = ""; 143 | }; 144 | DC7BD84822C7C4FB0046FFFA /* SweeterSwift */ = { 145 | isa = PBXGroup; 146 | children = ( 147 | DC7BD84922C7C4FB0046FFFA /* SweeterSwift.h */, 148 | DC7BD84A22C7C4FB0046FFFA /* Info.plist */, 149 | ); 150 | path = SweeterSwift; 151 | sourceTree = ""; 152 | }; 153 | DC7BD86522C7C7B00046FFFA /* SweeterSwiftTests */ = { 154 | isa = PBXGroup; 155 | children = ( 156 | DC7BD86822C7C7B00046FFFA /* Info.plist */, 157 | DC7BD89022C7DAF70046FFFA /* NSAttributedStringTests.swift */, 158 | DC7BD89222C7DC620046FFFA /* StringTests.swift */, 159 | DC7BD89422C7EA2B0046FFFA /* BundleTests.swift */, 160 | DC7BD89622C7EAC40046FFFA /* CodableTests.swift */, 161 | DC7BD86622C7C7B00046FFFA /* SwiftTests.swift */, 162 | ); 163 | path = SweeterSwiftTests; 164 | sourceTree = ""; 165 | }; 166 | DC7BD87322C7CA1E0046FFFA /* Source */ = { 167 | isa = PBXGroup; 168 | children = ( 169 | DC7BD87422C7CA1E0046FFFA /* NSAttributedString+Sweeter.swift */, 170 | DC7BD87522C7CA1E0046FFFA /* DateFormatter+Sweeter.swift */, 171 | DC7BD87622C7CA1E0046FFFA /* String+Sweeter.swift */, 172 | DC7BD87722C7CA1E0046FFFA /* UIApplication+Sweeter.swift */, 173 | DC7BD87822C7CA1E0046FFFA /* UITextView+Sweeter.swift */, 174 | DC7BD87922C7CA1E0046FFFA /* NSManagedObjectContext+Sweeter.swift */, 175 | DC7BD87A22C7CA1E0046FFFA /* Bundle+Sweeter.swift */, 176 | DC7BD87B22C7CA1E0046FFFA /* Codable+Sweeter.swift */, 177 | DC7BD87C22C7CA1E0046FFFA /* Swift+Sweeter.swift */, 178 | DC7BD87D22C7CA1E0046FFFA /* UIViewController+Sweeter.swift */, 179 | DC7BD87E22C7CA1E0046FFFA /* UIStackView+Sweeter.swift */, 180 | DC7BD87F22C7CA1E0046FFFA /* NSObject+Sweeter.swift */, 181 | DC7BD88022C7CA1E0046FFFA /* TimeInterval+Sweeter.swift */, 182 | DC7BD88122C7CA1E0046FFFA /* UIView+Sweeter.swift */, 183 | ); 184 | name = Source; 185 | path = ../Source; 186 | sourceTree = ""; 187 | }; 188 | /* End PBXGroup section */ 189 | 190 | /* Begin PBXHeadersBuildPhase section */ 191 | DC7BD84122C7C4FB0046FFFA /* Headers */ = { 192 | isa = PBXHeadersBuildPhase; 193 | buildActionMask = 2147483647; 194 | files = ( 195 | DC7BD85722C7C4FB0046FFFA /* SweeterSwift.h in Headers */, 196 | ); 197 | runOnlyForDeploymentPostprocessing = 0; 198 | }; 199 | /* End PBXHeadersBuildPhase section */ 200 | 201 | /* Begin PBXNativeTarget section */ 202 | DC7BD84522C7C4FB0046FFFA /* SweeterSwift */ = { 203 | isa = PBXNativeTarget; 204 | buildConfigurationList = DC7BD85A22C7C4FB0046FFFA /* Build configuration list for PBXNativeTarget "SweeterSwift" */; 205 | buildPhases = ( 206 | AC9CF0C9D911503A4E51014E /* [CP] Check Pods Manifest.lock */, 207 | DC7BD84122C7C4FB0046FFFA /* Headers */, 208 | 7BBF9EB03B2533D86CBFA454 /* [CP-User] SwiftFormat */, 209 | 7527C9AF64CED71731CB051B /* [CP-User] SwiftLintAutocorrect */, 210 | DC7BD84222C7C4FB0046FFFA /* Sources */, 211 | DC7BD84322C7C4FB0046FFFA /* Frameworks */, 212 | DC7BD84422C7C4FB0046FFFA /* Resources */, 213 | 6FDE19AE23F9D318D0EA3D82 /* [CP-User] SwiftLint */, 214 | ); 215 | buildRules = ( 216 | ); 217 | dependencies = ( 218 | ); 219 | name = SweeterSwift; 220 | productName = SweeterSwift; 221 | productReference = DC7BD84622C7C4FB0046FFFA /* SweeterSwift.framework */; 222 | productType = "com.apple.product-type.framework"; 223 | }; 224 | DC7BD86322C7C7B00046FFFA /* SweeterSwiftTests */ = { 225 | isa = PBXNativeTarget; 226 | buildConfigurationList = DC7BD86E22C7C7B00046FFFA /* Build configuration list for PBXNativeTarget "SweeterSwiftTests" */; 227 | buildPhases = ( 228 | 47346164AA5A56B45FEABEA5 /* [CP] Check Pods Manifest.lock */, 229 | ADC0E09EF1516A58D8F1F414 /* [CP-User] SwiftFormat */, 230 | 6A6825C739C7214D6DBED9E2 /* [CP-User] SwiftLintAutocorrect */, 231 | DC7BD86022C7C7B00046FFFA /* Sources */, 232 | DC7BD86122C7C7B00046FFFA /* Frameworks */, 233 | DC7BD86222C7C7B00046FFFA /* Resources */, 234 | E8D61AEAE019F1BE39F833AE /* [CP-User] SwiftLint */, 235 | 6BEE5A36F2607305F11A6C5D /* [CP] Embed Pods Frameworks */, 236 | ); 237 | buildRules = ( 238 | ); 239 | dependencies = ( 240 | DC7BD86B22C7C7B00046FFFA /* PBXTargetDependency */, 241 | ); 242 | name = SweeterSwiftTests; 243 | productName = SweeterSwiftTests; 244 | productReference = DC7BD86422C7C7B00046FFFA /* SweeterSwiftTests.xctest */; 245 | productType = "com.apple.product-type.bundle.unit-test"; 246 | }; 247 | /* End PBXNativeTarget section */ 248 | 249 | /* Begin PBXProject section */ 250 | DC7BD83D22C7C4FB0046FFFA /* Project object */ = { 251 | isa = PBXProject; 252 | attributes = { 253 | LastSwiftUpdateCheck = 1020; 254 | LastUpgradeCheck = 1020; 255 | ORGANIZATIONNAME = "Yonat Sharon"; 256 | TargetAttributes = { 257 | DC7BD84522C7C4FB0046FFFA = { 258 | CreatedOnToolsVersion = 10.2.1; 259 | }; 260 | DC7BD86322C7C7B00046FFFA = { 261 | CreatedOnToolsVersion = 10.2.1; 262 | }; 263 | }; 264 | }; 265 | buildConfigurationList = DC7BD84022C7C4FB0046FFFA /* Build configuration list for PBXProject "SweeterSwift" */; 266 | compatibilityVersion = "Xcode 9.3"; 267 | developmentRegion = en; 268 | hasScannedForEncodings = 0; 269 | knownRegions = ( 270 | en, 271 | ); 272 | mainGroup = DC7BD83C22C7C4FB0046FFFA; 273 | productRefGroup = DC7BD84722C7C4FB0046FFFA /* Products */; 274 | projectDirPath = ""; 275 | projectRoot = ""; 276 | targets = ( 277 | DC7BD84522C7C4FB0046FFFA /* SweeterSwift */, 278 | DC7BD86322C7C7B00046FFFA /* SweeterSwiftTests */, 279 | ); 280 | }; 281 | /* End PBXProject section */ 282 | 283 | /* Begin PBXResourcesBuildPhase section */ 284 | DC7BD84422C7C4FB0046FFFA /* Resources */ = { 285 | isa = PBXResourcesBuildPhase; 286 | buildActionMask = 2147483647; 287 | files = ( 288 | ); 289 | runOnlyForDeploymentPostprocessing = 0; 290 | }; 291 | DC7BD86222C7C7B00046FFFA /* Resources */ = { 292 | isa = PBXResourcesBuildPhase; 293 | buildActionMask = 2147483647; 294 | files = ( 295 | ); 296 | runOnlyForDeploymentPostprocessing = 0; 297 | }; 298 | /* End PBXResourcesBuildPhase section */ 299 | 300 | /* Begin PBXShellScriptBuildPhase section */ 301 | 47346164AA5A56B45FEABEA5 /* [CP] Check Pods Manifest.lock */ = { 302 | isa = PBXShellScriptBuildPhase; 303 | buildActionMask = 2147483647; 304 | files = ( 305 | ); 306 | inputFileListPaths = ( 307 | ); 308 | inputPaths = ( 309 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 310 | "${PODS_ROOT}/Manifest.lock", 311 | ); 312 | name = "[CP] Check Pods Manifest.lock"; 313 | outputFileListPaths = ( 314 | ); 315 | outputPaths = ( 316 | "$(DERIVED_FILE_DIR)/Pods-SweeterSwiftTests-checkManifestLockResult.txt", 317 | ); 318 | runOnlyForDeploymentPostprocessing = 0; 319 | shellPath = /bin/sh; 320 | 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"; 321 | showEnvVarsInLog = 0; 322 | }; 323 | 6A6825C739C7214D6DBED9E2 /* [CP-User] SwiftLintAutocorrect */ = { 324 | isa = PBXShellScriptBuildPhase; 325 | buildActionMask = 2147483647; 326 | files = ( 327 | ); 328 | name = "[CP-User] SwiftLintAutocorrect"; 329 | runOnlyForDeploymentPostprocessing = 0; 330 | shellPath = /bin/sh; 331 | shellScript = "if [ \"Debug\" == \"${CONFIGURATION}\" ]; then \"${PODS_ROOT}/SwiftLint/swiftlint\" autocorrect --config \"${PODS_ROOT}/SwiftQuality/.swiftlint.yml\" --path \"${SRCROOT}/..\" ; fi"; 332 | }; 333 | 6BEE5A36F2607305F11A6C5D /* [CP] Embed Pods Frameworks */ = { 334 | isa = PBXShellScriptBuildPhase; 335 | buildActionMask = 2147483647; 336 | files = ( 337 | ); 338 | inputFileListPaths = ( 339 | "${PODS_ROOT}/Target Support Files/Pods-SweeterSwiftTests/Pods-SweeterSwiftTests-frameworks-${CONFIGURATION}-input-files.xcfilelist", 340 | ); 341 | name = "[CP] Embed Pods Frameworks"; 342 | outputFileListPaths = ( 343 | "${PODS_ROOT}/Target Support Files/Pods-SweeterSwiftTests/Pods-SweeterSwiftTests-frameworks-${CONFIGURATION}-output-files.xcfilelist", 344 | ); 345 | runOnlyForDeploymentPostprocessing = 0; 346 | shellPath = /bin/sh; 347 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-SweeterSwiftTests/Pods-SweeterSwiftTests-frameworks.sh\"\n"; 348 | showEnvVarsInLog = 0; 349 | }; 350 | 6FDE19AE23F9D318D0EA3D82 /* [CP-User] SwiftLint */ = { 351 | isa = PBXShellScriptBuildPhase; 352 | buildActionMask = 2147483647; 353 | files = ( 354 | ); 355 | name = "[CP-User] SwiftLint"; 356 | runOnlyForDeploymentPostprocessing = 0; 357 | shellPath = /bin/sh; 358 | shellScript = "if [ \"Debug\" == \"${CONFIGURATION}\" ]; then \"${PODS_ROOT}/SwiftLint/swiftlint\" --config \"${PODS_ROOT}/SwiftQuality/.swiftlint.yml\" --path \"${SRCROOT}/..\" ; fi"; 359 | }; 360 | 7527C9AF64CED71731CB051B /* [CP-User] SwiftLintAutocorrect */ = { 361 | isa = PBXShellScriptBuildPhase; 362 | buildActionMask = 2147483647; 363 | files = ( 364 | ); 365 | name = "[CP-User] SwiftLintAutocorrect"; 366 | runOnlyForDeploymentPostprocessing = 0; 367 | shellPath = /bin/sh; 368 | shellScript = "if [ \"Debug\" == \"${CONFIGURATION}\" ]; then \"${PODS_ROOT}/SwiftLint/swiftlint\" autocorrect --config \"${PODS_ROOT}/SwiftQuality/.swiftlint.yml\" --path \"${SRCROOT}/..\" ; fi"; 369 | }; 370 | 7BBF9EB03B2533D86CBFA454 /* [CP-User] SwiftFormat */ = { 371 | isa = PBXShellScriptBuildPhase; 372 | buildActionMask = 2147483647; 373 | files = ( 374 | ); 375 | name = "[CP-User] SwiftFormat"; 376 | runOnlyForDeploymentPostprocessing = 0; 377 | shellPath = /bin/sh; 378 | shellScript = "if [ \"Debug\" == \"${CONFIGURATION}\" ]; then \"${PODS_ROOT}/SwiftFormat/CommandLineTool/swiftformat\" --swiftversion ${SWIFT_VERSION} --config \"${PODS_ROOT}/SwiftQuality/.swiftformat\" \"${SRCROOT}/..\" ; fi"; 379 | }; 380 | AC9CF0C9D911503A4E51014E /* [CP] Check Pods Manifest.lock */ = { 381 | isa = PBXShellScriptBuildPhase; 382 | buildActionMask = 2147483647; 383 | files = ( 384 | ); 385 | inputFileListPaths = ( 386 | ); 387 | inputPaths = ( 388 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 389 | "${PODS_ROOT}/Manifest.lock", 390 | ); 391 | name = "[CP] Check Pods Manifest.lock"; 392 | outputFileListPaths = ( 393 | ); 394 | outputPaths = ( 395 | "$(DERIVED_FILE_DIR)/Pods-SweeterSwift-checkManifestLockResult.txt", 396 | ); 397 | runOnlyForDeploymentPostprocessing = 0; 398 | shellPath = /bin/sh; 399 | 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"; 400 | showEnvVarsInLog = 0; 401 | }; 402 | ADC0E09EF1516A58D8F1F414 /* [CP-User] SwiftFormat */ = { 403 | isa = PBXShellScriptBuildPhase; 404 | buildActionMask = 2147483647; 405 | files = ( 406 | ); 407 | name = "[CP-User] SwiftFormat"; 408 | runOnlyForDeploymentPostprocessing = 0; 409 | shellPath = /bin/sh; 410 | shellScript = "if [ \"Debug\" == \"${CONFIGURATION}\" ]; then \"${PODS_ROOT}/SwiftFormat/CommandLineTool/swiftformat\" --swiftversion ${SWIFT_VERSION} --config \"${PODS_ROOT}/SwiftQuality/.swiftformat\" \"${SRCROOT}/..\" ; fi"; 411 | }; 412 | E8D61AEAE019F1BE39F833AE /* [CP-User] SwiftLint */ = { 413 | isa = PBXShellScriptBuildPhase; 414 | buildActionMask = 2147483647; 415 | files = ( 416 | ); 417 | name = "[CP-User] SwiftLint"; 418 | runOnlyForDeploymentPostprocessing = 0; 419 | shellPath = /bin/sh; 420 | shellScript = "if [ \"Debug\" == \"${CONFIGURATION}\" ]; then \"${PODS_ROOT}/SwiftLint/swiftlint\" --config \"${PODS_ROOT}/SwiftQuality/.swiftlint.yml\" --path \"${SRCROOT}/..\" ; fi"; 421 | }; 422 | /* End PBXShellScriptBuildPhase section */ 423 | 424 | /* Begin PBXSourcesBuildPhase section */ 425 | DC7BD84222C7C4FB0046FFFA /* Sources */ = { 426 | isa = PBXSourcesBuildPhase; 427 | buildActionMask = 2147483647; 428 | files = ( 429 | DC7BD88422C7CA1E0046FFFA /* String+Sweeter.swift in Sources */, 430 | DC7BD88322C7CA1E0046FFFA /* DateFormatter+Sweeter.swift in Sources */, 431 | DC7BD88C22C7CA1E0046FFFA /* UIStackView+Sweeter.swift in Sources */, 432 | DC7BD88D22C7CA1E0046FFFA /* NSObject+Sweeter.swift in Sources */, 433 | DC7BD88522C7CA1E0046FFFA /* UIApplication+Sweeter.swift in Sources */, 434 | DC7BD88222C7CA1E0046FFFA /* NSAttributedString+Sweeter.swift in Sources */, 435 | DC7BD88922C7CA1E0046FFFA /* Codable+Sweeter.swift in Sources */, 436 | DC7BD88A22C7CA1E0046FFFA /* Swift+Sweeter.swift in Sources */, 437 | DC7BD88F22C7CA1E0046FFFA /* UIView+Sweeter.swift in Sources */, 438 | DC7BD88E22C7CA1E0046FFFA /* TimeInterval+Sweeter.swift in Sources */, 439 | DC7BD88622C7CA1E0046FFFA /* UITextView+Sweeter.swift in Sources */, 440 | DC7BD88822C7CA1E0046FFFA /* Bundle+Sweeter.swift in Sources */, 441 | DC7BD88B22C7CA1E0046FFFA /* UIViewController+Sweeter.swift in Sources */, 442 | DC7BD88722C7CA1E0046FFFA /* NSManagedObjectContext+Sweeter.swift in Sources */, 443 | ); 444 | runOnlyForDeploymentPostprocessing = 0; 445 | }; 446 | DC7BD86022C7C7B00046FFFA /* Sources */ = { 447 | isa = PBXSourcesBuildPhase; 448 | buildActionMask = 2147483647; 449 | files = ( 450 | DC7BD89722C7EAC40046FFFA /* CodableTests.swift in Sources */, 451 | DC7BD89122C7DAF70046FFFA /* NSAttributedStringTests.swift in Sources */, 452 | DC7BD89522C7EA2B0046FFFA /* BundleTests.swift in Sources */, 453 | DC7BD89322C7DC620046FFFA /* StringTests.swift in Sources */, 454 | DC7BD86722C7C7B00046FFFA /* SwiftTests.swift in Sources */, 455 | ); 456 | runOnlyForDeploymentPostprocessing = 0; 457 | }; 458 | /* End PBXSourcesBuildPhase section */ 459 | 460 | /* Begin PBXTargetDependency section */ 461 | DC7BD86B22C7C7B00046FFFA /* PBXTargetDependency */ = { 462 | isa = PBXTargetDependency; 463 | target = DC7BD84522C7C4FB0046FFFA /* SweeterSwift */; 464 | targetProxy = DC7BD86A22C7C7B00046FFFA /* PBXContainerItemProxy */; 465 | }; 466 | /* End PBXTargetDependency section */ 467 | 468 | /* Begin XCBuildConfiguration section */ 469 | DC7BD85822C7C4FB0046FFFA /* Debug */ = { 470 | isa = XCBuildConfiguration; 471 | buildSettings = { 472 | ALWAYS_SEARCH_USER_PATHS = NO; 473 | CLANG_ANALYZER_NONNULL = YES; 474 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 475 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 476 | CLANG_CXX_LIBRARY = "libc++"; 477 | CLANG_ENABLE_MODULES = YES; 478 | CLANG_ENABLE_OBJC_ARC = YES; 479 | CLANG_ENABLE_OBJC_WEAK = YES; 480 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 481 | CLANG_WARN_BOOL_CONVERSION = YES; 482 | CLANG_WARN_COMMA = YES; 483 | CLANG_WARN_CONSTANT_CONVERSION = YES; 484 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 485 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 486 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 487 | CLANG_WARN_EMPTY_BODY = YES; 488 | CLANG_WARN_ENUM_CONVERSION = YES; 489 | CLANG_WARN_INFINITE_RECURSION = YES; 490 | CLANG_WARN_INT_CONVERSION = YES; 491 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 492 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 493 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 494 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 495 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 496 | CLANG_WARN_STRICT_PROTOTYPES = YES; 497 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 498 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 499 | CLANG_WARN_UNREACHABLE_CODE = YES; 500 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 501 | CODE_SIGN_IDENTITY = "iPhone Developer"; 502 | COPY_PHASE_STRIP = NO; 503 | CURRENT_PROJECT_VERSION = 41; 504 | DEBUG_INFORMATION_FORMAT = dwarf; 505 | ENABLE_STRICT_OBJC_MSGSEND = YES; 506 | ENABLE_TESTABILITY = YES; 507 | GCC_C_LANGUAGE_STANDARD = gnu11; 508 | GCC_DYNAMIC_NO_PIC = NO; 509 | GCC_NO_COMMON_BLOCKS = YES; 510 | GCC_OPTIMIZATION_LEVEL = 0; 511 | GCC_PREPROCESSOR_DEFINITIONS = ( 512 | "DEBUG=1", 513 | "$(inherited)", 514 | ); 515 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 516 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 517 | GCC_WARN_UNDECLARED_SELECTOR = YES; 518 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 519 | GCC_WARN_UNUSED_FUNCTION = YES; 520 | GCC_WARN_UNUSED_VARIABLE = YES; 521 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 522 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 523 | MTL_FAST_MATH = YES; 524 | ONLY_ACTIVE_ARCH = YES; 525 | SDKROOT = iphoneos; 526 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 527 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 528 | SWIFT_VERSION = 5.0; 529 | VERSIONING_SYSTEM = "apple-generic"; 530 | VERSION_INFO_PREFIX = ""; 531 | }; 532 | name = Debug; 533 | }; 534 | DC7BD85922C7C4FB0046FFFA /* Release */ = { 535 | isa = XCBuildConfiguration; 536 | buildSettings = { 537 | ALWAYS_SEARCH_USER_PATHS = NO; 538 | CLANG_ANALYZER_NONNULL = YES; 539 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 540 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 541 | CLANG_CXX_LIBRARY = "libc++"; 542 | CLANG_ENABLE_MODULES = YES; 543 | CLANG_ENABLE_OBJC_ARC = YES; 544 | CLANG_ENABLE_OBJC_WEAK = YES; 545 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 546 | CLANG_WARN_BOOL_CONVERSION = YES; 547 | CLANG_WARN_COMMA = YES; 548 | CLANG_WARN_CONSTANT_CONVERSION = YES; 549 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 550 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 551 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 552 | CLANG_WARN_EMPTY_BODY = YES; 553 | CLANG_WARN_ENUM_CONVERSION = YES; 554 | CLANG_WARN_INFINITE_RECURSION = YES; 555 | CLANG_WARN_INT_CONVERSION = YES; 556 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 557 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 558 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 559 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 560 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 561 | CLANG_WARN_STRICT_PROTOTYPES = YES; 562 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 563 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 564 | CLANG_WARN_UNREACHABLE_CODE = YES; 565 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 566 | CODE_SIGN_IDENTITY = "iPhone Developer"; 567 | COPY_PHASE_STRIP = NO; 568 | CURRENT_PROJECT_VERSION = 41; 569 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 570 | ENABLE_NS_ASSERTIONS = NO; 571 | ENABLE_STRICT_OBJC_MSGSEND = YES; 572 | GCC_C_LANGUAGE_STANDARD = gnu11; 573 | GCC_NO_COMMON_BLOCKS = YES; 574 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 575 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 576 | GCC_WARN_UNDECLARED_SELECTOR = YES; 577 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 578 | GCC_WARN_UNUSED_FUNCTION = YES; 579 | GCC_WARN_UNUSED_VARIABLE = YES; 580 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 581 | MTL_ENABLE_DEBUG_INFO = NO; 582 | MTL_FAST_MATH = YES; 583 | SDKROOT = iphoneos; 584 | SWIFT_COMPILATION_MODE = wholemodule; 585 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 586 | SWIFT_VERSION = 5.0; 587 | VALIDATE_PRODUCT = YES; 588 | VERSIONING_SYSTEM = "apple-generic"; 589 | VERSION_INFO_PREFIX = ""; 590 | }; 591 | name = Release; 592 | }; 593 | DC7BD85B22C7C4FB0046FFFA /* Debug */ = { 594 | isa = XCBuildConfiguration; 595 | baseConfigurationReference = 2CCD433E24A0BDB1106A3D61 /* Pods-SweeterSwift.debug.xcconfig */; 596 | buildSettings = { 597 | CODE_SIGN_IDENTITY = ""; 598 | CODE_SIGN_STYLE = Automatic; 599 | DEFINES_MODULE = YES; 600 | DYLIB_COMPATIBILITY_VERSION = 1; 601 | DYLIB_CURRENT_VERSION = 41; 602 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 603 | INFOPLIST_FILE = SweeterSwift/Info.plist; 604 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 605 | LD_RUNPATH_SEARCH_PATHS = ( 606 | "$(inherited)", 607 | "@executable_path/Frameworks", 608 | "@loader_path/Frameworks", 609 | ); 610 | PRODUCT_BUNDLE_IDENTIFIER = com.roysharon.SweeterSwift; 611 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 612 | SKIP_INSTALL = YES; 613 | TARGETED_DEVICE_FAMILY = "1,2"; 614 | }; 615 | name = Debug; 616 | }; 617 | DC7BD85C22C7C4FB0046FFFA /* Release */ = { 618 | isa = XCBuildConfiguration; 619 | baseConfigurationReference = 841073221FF5B2AF5BB49742 /* Pods-SweeterSwift.release.xcconfig */; 620 | buildSettings = { 621 | CODE_SIGN_IDENTITY = ""; 622 | CODE_SIGN_STYLE = Automatic; 623 | DEFINES_MODULE = YES; 624 | DYLIB_COMPATIBILITY_VERSION = 1; 625 | DYLIB_CURRENT_VERSION = 41; 626 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 627 | INFOPLIST_FILE = SweeterSwift/Info.plist; 628 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 629 | LD_RUNPATH_SEARCH_PATHS = ( 630 | "$(inherited)", 631 | "@executable_path/Frameworks", 632 | "@loader_path/Frameworks", 633 | ); 634 | PRODUCT_BUNDLE_IDENTIFIER = com.roysharon.SweeterSwift; 635 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 636 | SKIP_INSTALL = YES; 637 | TARGETED_DEVICE_FAMILY = "1,2"; 638 | }; 639 | name = Release; 640 | }; 641 | DC7BD86C22C7C7B00046FFFA /* Debug */ = { 642 | isa = XCBuildConfiguration; 643 | baseConfigurationReference = EB5626A87EBAC9FB4EE89466 /* Pods-SweeterSwiftTests.debug.xcconfig */; 644 | buildSettings = { 645 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 646 | CODE_SIGN_STYLE = Automatic; 647 | INFOPLIST_FILE = SweeterSwiftTests/Info.plist; 648 | LD_RUNPATH_SEARCH_PATHS = ( 649 | "$(inherited)", 650 | "@executable_path/Frameworks", 651 | "@loader_path/Frameworks", 652 | ); 653 | PRODUCT_BUNDLE_IDENTIFIER = com.roysharon.SweeterSwiftTests; 654 | PRODUCT_NAME = "$(TARGET_NAME)"; 655 | TARGETED_DEVICE_FAMILY = "1,2"; 656 | }; 657 | name = Debug; 658 | }; 659 | DC7BD86D22C7C7B00046FFFA /* Release */ = { 660 | isa = XCBuildConfiguration; 661 | baseConfigurationReference = 7E30926EC2BD4FB85BB3108B /* Pods-SweeterSwiftTests.release.xcconfig */; 662 | buildSettings = { 663 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 664 | CODE_SIGN_STYLE = Automatic; 665 | INFOPLIST_FILE = SweeterSwiftTests/Info.plist; 666 | LD_RUNPATH_SEARCH_PATHS = ( 667 | "$(inherited)", 668 | "@executable_path/Frameworks", 669 | "@loader_path/Frameworks", 670 | ); 671 | PRODUCT_BUNDLE_IDENTIFIER = com.roysharon.SweeterSwiftTests; 672 | PRODUCT_NAME = "$(TARGET_NAME)"; 673 | TARGETED_DEVICE_FAMILY = "1,2"; 674 | }; 675 | name = Release; 676 | }; 677 | /* End XCBuildConfiguration section */ 678 | 679 | /* Begin XCConfigurationList section */ 680 | DC7BD84022C7C4FB0046FFFA /* Build configuration list for PBXProject "SweeterSwift" */ = { 681 | isa = XCConfigurationList; 682 | buildConfigurations = ( 683 | DC7BD85822C7C4FB0046FFFA /* Debug */, 684 | DC7BD85922C7C4FB0046FFFA /* Release */, 685 | ); 686 | defaultConfigurationIsVisible = 0; 687 | defaultConfigurationName = Release; 688 | }; 689 | DC7BD85A22C7C4FB0046FFFA /* Build configuration list for PBXNativeTarget "SweeterSwift" */ = { 690 | isa = XCConfigurationList; 691 | buildConfigurations = ( 692 | DC7BD85B22C7C4FB0046FFFA /* Debug */, 693 | DC7BD85C22C7C4FB0046FFFA /* Release */, 694 | ); 695 | defaultConfigurationIsVisible = 0; 696 | defaultConfigurationName = Release; 697 | }; 698 | DC7BD86E22C7C7B00046FFFA /* Build configuration list for PBXNativeTarget "SweeterSwiftTests" */ = { 699 | isa = XCConfigurationList; 700 | buildConfigurations = ( 701 | DC7BD86C22C7C7B00046FFFA /* Debug */, 702 | DC7BD86D22C7C7B00046FFFA /* Release */, 703 | ); 704 | defaultConfigurationIsVisible = 0; 705 | defaultConfigurationName = Release; 706 | }; 707 | /* End XCConfigurationList section */ 708 | }; 709 | rootObject = DC7BD83D22C7C4FB0046FFFA /* Project object */; 710 | } 711 | --------------------------------------------------------------------------------