├── .gitignore ├── .swiftpm └── xcode │ └── package.xcworkspace │ └── contents.xcworkspacedata ├── CONTRIBUTING.md ├── LICENSE.md ├── Omnia.xcodeproj ├── Omnia_Info.plist ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings └── xcshareddata │ └── xcschemes │ └── Omnia-Package.xcscheme ├── Package.swift ├── README.md ├── Screenshots └── Artboard.png ├── Sources ├── Shared │ ├── Bundle.swift │ ├── CGColor.swift │ ├── Character.swift │ ├── Codable.swift │ ├── Collection.swift │ ├── Comparable.swift │ ├── Data.swift │ ├── Date.swift │ ├── Debouncer.swift │ ├── Double.swift │ ├── Error.swift │ ├── Function.swift │ ├── JSON.swift │ ├── NSObject.swift │ ├── Once.swift │ ├── Optional.swift │ ├── Predicate.swift │ ├── Sequence.swift │ ├── Shared.swift │ ├── String.swift │ ├── Tailwind.swift │ ├── Throttler.swift │ ├── Type.swift │ ├── URL.swift │ ├── UXImage.swift │ └── UserDefault.swift ├── iOS │ ├── Haptics.swift │ ├── NSLayoutConstraint.swift │ ├── UICollectionView.swift │ ├── UIColor.swift │ ├── UIFont.swift │ ├── UIImage.swift │ ├── UIScrollView.swift │ ├── UITableView.swift │ ├── UIView.swift │ └── UIViewController.swift ├── macOS │ ├── ClickedCollectionView.swift │ ├── CollectionViewHandler.swift │ ├── CollectionViewSectionHandler.swift │ ├── MenuHandler.swift │ ├── NSButton.swift │ ├── NSCollectionView.swift │ ├── NSCollectionViewItem.swift │ ├── NSColor.swift │ ├── NSImage.swift │ ├── NSTextView.swift │ ├── NSView.swift │ └── Process.swift ├── tvOS │ └── tvOS.swift └── watchOS │ └── watchOS.swift ├── Tests ├── LinuxMain.swift └── OmniaTests │ ├── OmniaTests.swift │ ├── Shared │ ├── BundleTests.swift │ ├── CodableTests.swift │ ├── ComparableTests.swift │ ├── DataTests.swift │ ├── DateTests.swift │ ├── DebouncerTests.swift │ ├── DoubleTests.swift │ ├── JSONTests.swift │ ├── NSObjectTests.swift │ ├── OnceTests.swift │ ├── OptionalTests.swift │ ├── SequenceTests.swift │ ├── SharedTests.swift │ └── StringTests.swift │ ├── XCTestManifests.swift │ ├── iOS │ ├── NSLayoutConstraintTests.swift │ ├── UIColorTests.swift │ ├── UIImageTests.swift │ ├── UIViewControllerTests.swift │ └── UIViewTests.swift │ ├── macOS │ └── macOSTests.swift │ └── tvOS │ └── tvOSTests.swift └── docs ├── Classes.html ├── Classes ├── Debouncer.html └── Once.html ├── Extensions.html ├── Extensions ├── Bundle.html ├── Comparable.html ├── Data.html ├── Date.html ├── Decoder.html ├── Double.html ├── Encoder.html ├── Error.html ├── NSLayoutConstraint.html ├── Optional.html ├── Sequence.html ├── String.html ├── UICollectionView.html ├── UIColor.html ├── UIFont.html ├── UIImage.html ├── UIScrollView.html ├── UITableView.html ├── UIView.html └── UIViewController.html ├── Functions.html ├── Structs.html ├── Structs └── HSLColor.html ├── Typealiases.html ├── badge.svg ├── css ├── highlight.css └── jazzy.css ├── docsets ├── Omnia.docset │ └── Contents │ │ ├── Info.plist │ │ └── Resources │ │ ├── Documents │ │ ├── Classes.html │ │ ├── Classes │ │ │ ├── Debouncer.html │ │ │ └── Once.html │ │ ├── Extensions.html │ │ ├── Extensions │ │ │ ├── Bundle.html │ │ │ ├── Comparable.html │ │ │ ├── Data.html │ │ │ ├── Date.html │ │ │ ├── Decoder.html │ │ │ ├── Double.html │ │ │ ├── Encoder.html │ │ │ ├── Error.html │ │ │ ├── NSLayoutConstraint.html │ │ │ ├── Optional.html │ │ │ ├── Sequence.html │ │ │ ├── String.html │ │ │ ├── UICollectionView.html │ │ │ ├── UIColor.html │ │ │ ├── UIFont.html │ │ │ ├── UIImage.html │ │ │ ├── UIScrollView.html │ │ │ ├── UITableView.html │ │ │ ├── UIView.html │ │ │ └── UIViewController.html │ │ ├── Functions.html │ │ ├── Structs.html │ │ ├── Structs │ │ │ └── HSLColor.html │ │ ├── Typealiases.html │ │ ├── css │ │ │ ├── highlight.css │ │ │ └── jazzy.css │ │ ├── img │ │ │ ├── carat.png │ │ │ ├── dash.png │ │ │ └── gh.png │ │ ├── index.html │ │ ├── js │ │ │ ├── jazzy.js │ │ │ └── jquery.min.js │ │ └── search.json │ │ └── docSet.dsidx └── Omnia.tgz ├── img ├── carat.png ├── dash.png └── gh.png ├── index.html ├── js ├── jazzy.js └── jquery.min.js ├── search.json └── undocumented.json /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | .AppleDouble 4 | .LSOverride 5 | Icon 6 | ._* 7 | .Spotlight-V100 8 | .Trashes 9 | 10 | # Xcode 11 | # 12 | build/ 13 | *.pbxuser 14 | !default.pbxuser 15 | *.mode1v3 16 | !default.mode1v3 17 | *.mode2v3 18 | !default.mode2v3 19 | *.perspectivev3 20 | !default.perspectivev3 21 | xcuserdata 22 | *.xccheckout 23 | *.moved-aside 24 | DerivedData 25 | *.hmap 26 | *.ipa 27 | *.xcuserstate 28 | 29 | # CocoaPods 30 | Pods 31 | 32 | # Carthage 33 | Carthage 34 | 35 | # SPM 36 | .build/ 37 | -------------------------------------------------------------------------------- /.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | GitHub Issues is for reporting bugs, discussing features and general feedback in **Omnia**. Be sure to check our [documentation](http://cocoadocs.org/docsets/Omnia), [FAQ](https://github.com/onmyway133/Omnia/wiki/FAQ) and [past issues](https://github.com/onmyway133/Omnia/issues?state=closed) before opening any new issues. 2 | 3 | If you are posting about a crash in your application, a stack trace is helpful, but additional context, in the form of code and explanation, is necessary to be of any use. 4 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Licensed under the **MIT** license 2 | 3 | > Copyright (c) 2015 Khoa Pham 4 | > 5 | > Permission is hereby granted, free of charge, to any person obtaining 6 | > a copy of this software and associated documentation files (the 7 | > "Software"), to deal in the Software without restriction, including 8 | > without limitation the rights to use, copy, modify, merge, publish, 9 | > distribute, sublicense, and/or sell copies of the Software, and to 10 | > permit persons to whom the Software is furnished to do so, subject to 11 | > the following conditions: 12 | > 13 | > The above copyright notice and this permission notice shall be 14 | > included in all copies or substantial portions of the Software. 15 | > 16 | > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | > EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | > MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | > IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | > CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | > TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | > SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /Omnia.xcodeproj/Omnia_Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | CFBundleDevelopmentRegion 5 | en 6 | CFBundleExecutable 7 | $(EXECUTABLE_NAME) 8 | CFBundleIdentifier 9 | $(PRODUCT_BUNDLE_IDENTIFIER) 10 | CFBundleInfoDictionaryVersion 11 | 6.0 12 | CFBundleName 13 | $(PRODUCT_NAME) 14 | CFBundlePackageType 15 | FMWK 16 | CFBundleShortVersionString 17 | 1.0 18 | CFBundleSignature 19 | ???? 20 | CFBundleVersion 21 | $(CURRENT_PROJECT_VERSION) 22 | NSPrincipalClass 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Omnia.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /Omnia.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Omnia.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded 6 | 7 | 8 | -------------------------------------------------------------------------------- /Omnia.xcodeproj/xcshareddata/xcschemes/Omnia-Package.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 53 | 54 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.1 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "Omnia", 8 | platforms: [ 9 | .macOS(.v10_15), 10 | .iOS(.v13), 11 | .tvOS(.v13), 12 | .watchOS(.v6) 13 | ], 14 | products: [ 15 | .library( 16 | name: "Omnia", 17 | targets: ["Omnia"]), 18 | ], 19 | targets: [ 20 | .target( 21 | name: "Omnia", 22 | path: "Sources" 23 | ) 24 | ], 25 | swiftLanguageVersions: [.v5] 26 | ) 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Omnia 2 | 3 | ❤️ Support my apps ❤️ 4 | 5 | - [Push Hero - pure Swift native macOS application to test push notifications](https://onmyway133.com/pushhero) 6 | - [PastePal - Pasteboard, note and shortcut manager](https://onmyway133.com/pastepal) 7 | - [Quick Check - smart todo manager](https://onmyway133.com/quickcheck) 8 | - [Alias - App and file shortcut manager](https://onmyway133.com/alias) 9 | - [My other apps](https://onmyway133.com/apps/) 10 | 11 | ❤️❤️😇😍🤘❤️❤️ 12 | 13 | [![Version](https://img.shields.io/cocoapods/v/Omnia.svg?style=flat)](http://cocoadocs.org/docsets/Omnia) 14 | [![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 15 | [![License](https://img.shields.io/cocoapods/l/Omnia.svg?style=flat)](http://cocoadocs.org/docsets/Omnia) 16 | [![Platform](https://img.shields.io/cocoapods/p/Omnia.svg?style=flat)](http://cocoadocs.org/docsets/Omnia) 17 | ![Swift](https://img.shields.io/badge/%20in-swift%205.0-orange.svg) 18 | 19 | ![](Screenshots/Artboard.png) 20 | 21 | ## Description 22 | 23 | **Omnia** contains a huge collection of handy extensions for your everyday project. Inspired by utility and standard libraries from many other languages such as Kotlin, Ruby, Javascript, Haskell, ... **Omnia** implements the most necessary extensions 24 | 25 | For other sweets 26 | 27 | - [Anchors](https://github.com/onmyway133/Anchors) ⚓️ Declarative, extensible, powerful Auto Layout 28 | - [EasyClosure](https://github.com/onmyway133/EasyClosure) 🍩 Unified communication pattern for iOS and macOS 29 | - [Smile](https://github.com/onmyway133/Smile) 😄 Emoji in Swift 30 | - [Wave](https://github.com/onmyway133/Wave) 🌊 Declarative chainable animations in Swift 31 | - [EasyStash](https://github.com/onmyway133/EasyStash) 🗳Easy data persistence in Swift 32 | 33 | ## Usage 34 | 35 | - Documentation can be found at https://onmyway133.github.io/Omnia/ 36 | 37 | 38 | ## Installation 39 | 40 | **Omnia** is available through [CocoaPods](http://cocoapods.org). To install 41 | it, simply add the following line to your Podfile: 42 | 43 | ```ruby 44 | pod 'Omnia' 45 | ``` 46 | 47 | **Omnia** is also available through [Carthage](https://github.com/Carthage/Carthage). 48 | To install just write into your Cartfile: 49 | 50 | ```ruby 51 | github "onmyway133/Omnia" 52 | ``` 53 | 54 | **Omnia** can also be installed manually. Just download and drop `Sources` folders in your project. 55 | 56 | ### Swift Package Manager 57 | 58 | ```swift 59 | .package( 60 | url: "https://github.com/onmyway133/Omnia.git", 61 | .upToNextMajor(from: "3.3.0") 62 | ), 63 | ``` 64 | 65 | ## Author 66 | 67 | Khoa Pham, onmyway133@gmail.com 68 | 69 | ## License 70 | 71 | **Omnia** is available under the MIT license. See the [LICENSE](https://github.com/onmyway133/Omnia/blob/master/LICENSE.md) file for more info. 72 | -------------------------------------------------------------------------------- /Screenshots/Artboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onmyway133/Omnia/56589071d33ad116b5fae544e8017a08605b094f/Screenshots/Artboard.png -------------------------------------------------------------------------------- /Sources/Shared/Bundle.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Bundle { 4 | 5 | /// Return application display name 6 | public var name: String? { 7 | return infoDictionary?["CFBundleDisplayName"] as? String 8 | ?? infoDictionary?["CFBundleName"] as? String 9 | } 10 | 11 | /// Return the public version number 12 | public var versionNumber: String? { 13 | return infoDictionary?["CFBundleShortVersionString"] as? String 14 | } 15 | 16 | /// Return build version number 17 | public var buildNumber: String? { 18 | return infoDictionary?["CFBundleVersion"] as? String 19 | } 20 | } 21 | 22 | -------------------------------------------------------------------------------- /Sources/Shared/CGColor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CGColor.swift 3 | // Omnia 4 | // 5 | // Created by khoa on 08/03/2021. 6 | // 7 | 8 | 9 | import CoreGraphics 10 | 11 | public extension CGColor { 12 | func isLight(threshold: Float = 0.7) -> Bool { 13 | let RGBCGColor = converted( 14 | to: CGColorSpaceCreateDeviceRGB(), 15 | intent: .defaultIntent, 16 | options: nil 17 | ) 18 | 19 | guard 20 | let components = RGBCGColor?.components, 21 | components.count >= 3 22 | else { 23 | return false 24 | } 25 | 26 | let brightness = Float( 27 | ((components[0] * 299) + (components[1] * 587) + (components[2] * 114)) / 1000 28 | ) 29 | 30 | return (brightness > threshold) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Sources/Shared/Character.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Character.swift 3 | // 4 | // 5 | // Created by khoa on 29/12/2020. 6 | // 7 | 8 | import Foundation 9 | 10 | // https://stackoverflow.com/a/39425959/1418457 11 | 12 | public extension Character { 13 | /// A simple emoji is one scalar and presented to the user as an Emoji 14 | var isSimpleEmoji: Bool { 15 | guard let firstScalar = unicodeScalars.first else { return false } 16 | return firstScalar.properties.isEmoji && firstScalar.value > 0x238C 17 | } 18 | 19 | /// Checks if the scalars will be merged into an emoji 20 | var isCombinedIntoEmoji: Bool { unicodeScalars.count > 1 && unicodeScalars.first?.properties.isEmoji ?? false } 21 | 22 | var isEmoji: Bool { isSimpleEmoji || isCombinedIntoEmoji } 23 | } 24 | 25 | public extension String { 26 | var isSingleEmoji: Bool { count == 1 && containsEmoji } 27 | 28 | var containsEmoji: Bool { contains { $0.isEmoji } } 29 | 30 | var containsOnlyEmoji: Bool { !isEmpty && !contains { !$0.isEmoji } } 31 | 32 | var emojiString: String { emojis.map { String($0) }.reduce("", +) } 33 | 34 | var emojis: [Character] { filter { $0.isEmoji } } 35 | 36 | var emojiScalars: [UnicodeScalar] { filter { $0.isEmoji }.flatMap { $0.unicodeScalars } } 37 | } 38 | -------------------------------------------------------------------------------- /Sources/Shared/Codable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Codable.swift 3 | // Omnia-iOS 4 | // 5 | // Created by khoa on 12/04/2019. 6 | // Copyright © 2019 Khoa Pham. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // https://github.com/JohnSundell/Codextended 12 | 13 | public extension Decoder { 14 | func decode(key: String, type: T.Type = T.self) throws -> T { 15 | return try decode(codingKey: SingleCodingKey(key: key), type: type) 16 | } 17 | 18 | func decode(codingKey: K, type: T.Type = T.self) throws -> T { 19 | let container = try self.container(keyedBy: K.self) 20 | return try container.decode(type, forKey: codingKey) 21 | } 22 | } 23 | 24 | public extension Encoder { 25 | func encode(value: T, key: String) throws { 26 | return try encode(value: value, codingKey: SingleCodingKey(key: key)) 27 | } 28 | 29 | func encode(value: T, codingKey: K) throws { 30 | var container = self.container(keyedBy: K.self) 31 | return try container.encode(value, forKey: codingKey) 32 | } 33 | } 34 | 35 | private struct SingleCodingKey: CodingKey { 36 | var stringValue: String 37 | 38 | init?(stringValue: String) { 39 | self.stringValue = stringValue 40 | } 41 | 42 | var intValue: Int? 43 | 44 | init?(intValue: Int) { 45 | self.intValue = intValue 46 | self.stringValue = String(intValue) 47 | } 48 | 49 | init(key: String) { 50 | self.stringValue = key 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Sources/Shared/Collection.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // 4 | // 5 | // Created by khoa on 08/01/2021. 6 | // 7 | 8 | import Foundation 9 | 10 | public extension Collection { 11 | subscript (safe index: Index) -> Element? { 12 | return indices.contains(index) ? self[index] : nil 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Sources/Shared/Comparable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Omnia 3 | // 4 | // Created by khoa on 11/04/2019. 5 | // Copyright © 2019 Khoa Pham. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Comparable { 11 | 12 | /// Check if a number is between 2 numbers 13 | /// 14 | /// - Parameters: 15 | /// - min: The min number 16 | /// - max: The max number 17 | /// - Returns: True if it is between 18 | public func isBetween(min: Self, max: Self) -> Bool { 19 | return min <= self && self <= max 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Shared/Data.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Omnia 3 | // 4 | // Created by khoa on 11/04/2019. 5 | // Copyright © 2019 Khoa Pham. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Data { 11 | 12 | /// Convert to hex string 13 | public var hexString: String { 14 | return map({ String(format: "%02hhx", $0) }).joined() 15 | } 16 | 17 | public func toString() -> String { 18 | return String(decoding: self, as: UTF8.self) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Sources/Shared/Date.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Date.swift 3 | // Omnia 4 | // 5 | // Created by khoa on 11/04/2019. 6 | // Copyright © 2019 Khoa Pham. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension Date { 12 | 13 | func second(calendar: Calendar = .current) -> Int { 14 | return calendar.component(.second, from: self) 15 | } 16 | 17 | func minute(calendar: Calendar = .current) -> Int { 18 | return calendar.component(.minute, from: self) 19 | } 20 | 21 | func hour(calendar: Calendar = .current) -> Int { 22 | return calendar.component(.hour, from: self) 23 | } 24 | 25 | func day(calendar: Calendar = .current) -> Int{ 26 | return calendar.component(.day, from: self) 27 | } 28 | 29 | func month(calendar: Calendar = .current) -> Int { 30 | return calendar.component(.month, from: self) 31 | } 32 | 33 | func year(calendar: Calendar = .current) -> Int { 34 | return calendar.component(.year, from: self) 35 | } 36 | 37 | func weekday(calendar: Calendar = .current) -> Int { 38 | return calendar.component(.weekday, from: self) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Sources/Shared/Debouncer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Debouncer.swift 3 | // Omnia 4 | // 5 | // Created by khoa on 26/04/2019. 6 | // Copyright © 2019 Khoa Pham. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /// Run the action after delay 12 | public class Debouncer { 13 | private let delay: TimeInterval 14 | private var workItem: DispatchWorkItem? 15 | private let queue: DispatchQueue 16 | 17 | public init(delay: TimeInterval, queue: DispatchQueue = .main) { 18 | self.delay = delay 19 | self.queue = queue 20 | } 21 | 22 | public func run(action: @escaping () -> Void) { 23 | workItem?.cancel() 24 | let workItem = DispatchWorkItem(block: action) 25 | queue.asyncAfter(deadline: .now() + delay, execute: workItem) 26 | self.workItem = workItem 27 | } 28 | 29 | public func cancel() { 30 | workItem?.cancel() 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Sources/Shared/Double.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Double.swift 3 | // Omnia 4 | // 5 | // Created by khoa on 11/04/2019. 6 | // Copyright © 2019 Khoa Pham. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension Double { 12 | var second: TimeInterval { 13 | return self 14 | } 15 | 16 | var minute: TimeInterval { 17 | return second * 60 18 | } 19 | 20 | var hour: TimeInterval { 21 | return minute * 60 22 | } 23 | 24 | var day: TimeInterval { 25 | return hour * 24 26 | } 27 | 28 | var week: TimeInterval { 29 | return day * 7 30 | } 31 | 32 | var month: TimeInterval { 33 | return day * 30 34 | } 35 | 36 | var year: TimeInterval { 37 | return day * 365 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Sources/Shared/Error.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Error.swift 3 | // Omnia 4 | // 5 | // Created by khoa on 03/10/2019. 6 | // Copyright © 2019 Khoa Pham. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public extension Error { 12 | func toString() -> String { 13 | return String(describing: self) 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Sources/Shared/Function.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Color.swift 3 | // Omnia 4 | // 5 | // Created by khoa on 14/11/2019. 6 | // Copyright © 2019 Khoa Pham. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // https://www.webnots.com/flat-ui-color-codes/ 12 | public func flatColorsInHex() -> [String] { 13 | /* 14 | TURQUOISE 26,188,156 #1ABC9C 15 | GREEN SEA 22,160,133 #16A085 16 | EMERALD 46,204,113 #2ECC71 17 | NEPHRITIS 39,174,96 #27AE60 18 | PETER RIVER 52,152,219 #3498DB 19 | BELIZE HOLE 41,128,185 #2980B9 20 | AMETHYST 155,89,182 #9B59B6 21 | WISTERIA 142,68,173 #8E44AD 22 | WET ASPHALT 52,73,94 #34495E 23 | MIDNIGHT BLUE 44,62,80 #2C3E50 24 | SUN FLOWER 241,196,15 #F1C40F 25 | ORANGE 243,156,18 #F39C12 26 | CARROT 230,126,34 #E67E22 27 | PUMPKIN 211,84,0 #D35400 28 | ALIZARIN 231,76,60 #E74C3C 29 | POMEGRANATE 192,57,43 #C0392B 30 | CLOUDS 236,240,241 #ECF0F1 31 | SILVER 189,195,199 #BDC3C7 32 | CONCRETE 149,165,166 #95A5A6 33 | ASBESTOS 127,140,141 #7F8C8D 34 | */ 35 | 36 | return [ 37 | "#1ABC9C", 38 | "#16A085", 39 | "#2ECC71", 40 | "#27AE60", 41 | "#3498DB", 42 | "#2980B9", 43 | "#9B59B6", 44 | "#8E44AD", 45 | "#34495E", 46 | "#2C3E50", 47 | "#F1C40F", 48 | "#F39C12", 49 | "#E67E22", 50 | "#D35400", 51 | "#E74C3C", 52 | "#C0392B", 53 | "#BDC3C7", 54 | "#95A5A6", 55 | "#7F8C8D" 56 | ] 57 | } 58 | -------------------------------------------------------------------------------- /Sources/Shared/JSON.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JSON.swift 3 | // Omnia 4 | // 5 | // Created by khoa on 12/09/2019. 6 | // Copyright © 2019 Khoa Pham. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public func resolve(_ jsonDictionary: [String: Any], keyPath: String) -> T? { 12 | var current: Any? = jsonDictionary 13 | 14 | keyPath.split(separator: ".").forEach { component in 15 | if let maybeInt = Int(component), let array = current as? Array { 16 | current = array[maybeInt] 17 | } else if let dictionary = current as? JSONDictionary { 18 | current = dictionary[String(component)] 19 | } 20 | } 21 | 22 | return current as? T 23 | } 24 | -------------------------------------------------------------------------------- /Sources/Shared/NSObject.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Omnia 3 | // 4 | // Created by khoa on 11/04/2019. 5 | // Copyright © 2019 Khoa Pham. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | 10 | /// Add-on utitlies to all types 11 | /// https://github.com/JetBrains/kotlin/blob/master/libraries/stdlib/src/kotlin/util/Standard.kt 12 | protocol TypeAddOnAware {} 13 | 14 | extension TypeAddOnAware { 15 | /// Calls the specified closure with this instance as its receiver 16 | /// and returns this instance 17 | /// 18 | /// - Parameter closure: The closure to apply 19 | func apply(closure: (Self) -> Void) { 20 | closure(self) 21 | } 22 | } 23 | 24 | /// Conform NSObject by default 25 | extension NSObject: TypeAddOnAware {} 26 | -------------------------------------------------------------------------------- /Sources/Shared/Once.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Once.swift 3 | // Omnia 4 | // 5 | // Created by khoa on 11/04/2019. 6 | // Copyright © 2019 Khoa Pham. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public class Once { 12 | 13 | private var already: Bool = false 14 | 15 | public init() {} 16 | 17 | public func run(_ block: () -> Void) { 18 | guard !already else { 19 | return 20 | } 21 | 22 | block() 23 | already = true 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Sources/Shared/Optional.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Omnia 3 | // 4 | // Created by khoa on 11/04/2019. 5 | // Copyright © 2019 Khoa Pham. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Optional { 11 | 12 | /// Unwrap with message 13 | /// 14 | /// - Parameter message: A message to give more info 15 | public func unwrap(message: String) -> Wrapped { 16 | if let value = self { 17 | return value 18 | } else { 19 | fatalError(message) 20 | } 21 | } 22 | 23 | /// Same as Kotlin Nullable.let 24 | /// Run the closure if it is not nil 25 | /// 26 | /// - Parameter closure: The closure to be run 27 | public func runIfNotNil(closure: (Wrapped) -> Void) { 28 | if let value = self { 29 | closure(value) 30 | } 31 | } 32 | } 33 | 34 | extension Optional where Wrapped == String { 35 | 36 | /// Return true if it is not nil and not empty 37 | public var isNotNilOrEmpty: Bool { 38 | if let value = self { 39 | return !value.isEmpty 40 | } else { 41 | return false 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Sources/Shared/Predicate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Predicate.swift 3 | // 4 | // 5 | // Created by khoa on 11/11/2021. 6 | // 7 | 8 | import Foundation 9 | 10 | public extension NSPredicate { 11 | static func and(_ predicates: [NSPredicate]) -> NSPredicate { 12 | NSCompoundPredicate(type: .and, subpredicates: predicates) 13 | } 14 | 15 | static func or(_ predicates: [NSPredicate]) -> NSPredicate { 16 | NSCompoundPredicate(type: .or, subpredicates: predicates) 17 | } 18 | 19 | static func not(_ predicate: NSPredicate) -> NSPredicate { 20 | NSCompoundPredicate(type: .not, subpredicates: [predicate]) 21 | } 22 | 23 | static func compare( 24 | _ kp: KeyPath, 25 | op: NSComparisonPredicate.Operator, 26 | value: Any 27 | ) -> NSPredicate { 28 | let ex1 = \Root.self == kp 29 | ? NSExpression.expressionForEvaluatedObject() 30 | : NSExpression(forKeyPath: kp) 31 | let ex2 = NSExpression(forConstantValue: value) 32 | 33 | return NSComparisonPredicate( 34 | leftExpression: ex1, 35 | rightExpression: ex2, 36 | modifier: .direct, 37 | type: op 38 | ) 39 | } 40 | } 41 | 42 | public extension NSPredicate { 43 | static func isTrue( 44 | _ kp: KeyPath 45 | ) -> NSPredicate { 46 | compare(kp, op: .equalTo, value: true) 47 | } 48 | 49 | static func isFalse( 50 | _ kp: KeyPath 51 | ) -> NSPredicate { 52 | compare(kp, op: .equalTo, value: false) 53 | } 54 | 55 | static func isNil( 56 | _ kp: KeyPath 57 | ) -> NSPredicate { 58 | compare(kp, op: .equalTo, value: NSNull()) 59 | } 60 | 61 | static func isNotNil( 62 | _ kp: KeyPath 63 | ) -> NSPredicate { 64 | compare(kp, op: .notEqualTo, value: NSNull()) 65 | } 66 | 67 | static func isEmpty( 68 | _ kp: KeyPath 69 | ) -> NSPredicate { 70 | compare(kp, op: .equalTo, value: "") 71 | } 72 | 73 | static func isNotEmpty( 74 | _ kp: KeyPath 75 | ) -> NSPredicate { 76 | compare(kp, op: .notEqualTo, value: "") 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Sources/Shared/Sequence.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Omnia 3 | // 4 | // Created by khoa on 11/04/2019. 5 | // Copyright © 2019 Khoa Pham. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Sequence { 11 | 12 | /// Shuffle sequence by creating a new array 13 | /// 14 | /// - Returns: Shuffled array 15 | public func shuffled() -> [Element] { 16 | var array = Array(self) 17 | 18 | guard array.count > 1 else { 19 | return array 20 | } 21 | 22 | let count = array.count 23 | for i in 0..<(array.count - 1) { 24 | let j = Int(arc4random_uniform(UInt32(count - i))) + i 25 | array.swapAt(i, j) 26 | } 27 | 28 | return array 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Sources/Shared/Shared.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public func with(_ op1: A?, _ op2: B?, _ closure: (A, B) -> Void) { 4 | if let value1 = op1, let value2 = op2 { 5 | closure(value1, value2) 6 | } 7 | } 8 | 9 | public func withObject(_ value: T, closure: (T) -> Void) -> T { 10 | closure(value) 11 | return value 12 | } 13 | 14 | public func withValue(_ value: T, closure: (inout T) -> Void) -> T { 15 | var mutableValue = value 16 | closure(&mutableValue) 17 | return mutableValue 18 | } 19 | -------------------------------------------------------------------------------- /Sources/Shared/String.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Omnia 3 | // 4 | // Created by khoa on 11/04/2019. 5 | // Copyright © 2019 Khoa Pham. All rights reserved. 6 | // 7 | 8 | import Foundation 9 | 10 | public extension String { 11 | /// Return a new string with first letter being capitalized 12 | var capitalizedFirstLetter: String { 13 | guard !isEmpty else { 14 | return "" 15 | } 16 | 17 | return "\(prefix(1).uppercased())\(dropFirst())" 18 | } 19 | 20 | /// Return a localized string using NSLocalizedString 21 | var localized: String { 22 | return NSLocalizedString(self, comment: "") 23 | } 24 | 25 | func toData() -> Data { 26 | return Data(self.utf8) 27 | } 28 | 29 | var trimmed: String { 30 | return trimmingCharacters(in: .whitespacesAndNewlines) 31 | } 32 | 33 | func hasPattern(regex: String) -> Bool { 34 | return range(of: regex, options: .regularExpression, range: nil, locale: nil) != nil 35 | } 36 | 37 | func containsIgnoringCase(_ find: String) -> Bool { 38 | return self.range(of: find, options: .caseInsensitive) != nil 39 | } 40 | 41 | func trimmingInsideSmartQuotes() -> String { 42 | return self.replacingOccurrences(of: #"“\S*”"#, with: "", options: .regularExpression).trimmed 43 | } 44 | 45 | func trimmingInsideParenthesis() -> String { 46 | return self.replacingOccurrences(of: #"\(\S*\)"#, with: "", options: .regularExpression).trimmed 47 | } 48 | 49 | func trimmingInsideAsterisks() -> String { 50 | return self.replacingOccurrences(of: #"\*\S*\*"#, with: "", options: .regularExpression).trimmed 51 | } 52 | 53 | func trimmingInsideQuotes() -> String { 54 | return self.replacingOccurrences(of: #"\"\S*\""#, with: "", options: .regularExpression).trimmed 55 | } 56 | 57 | /// Check whether self contains any element in the array 58 | func contains(anyIn array: [String]) -> Bool { 59 | return array.contains(where: self.contains) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Sources/Shared/Tailwind.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Tailwind.swift 3 | // 4 | // 5 | // Created by khoa on 03/06/2020. 6 | // 7 | 8 | // https://tailwindcss.com/docs/customizing-colors#default-color-palette 9 | import Foundation 10 | 11 | public struct Tailwind { 12 | public struct color { 13 | public static let gray1 = 0xF7FAFC 14 | public static let gray2 = 0xEDF2F7 15 | public static let gray3 = 0xE2E8F0 16 | public static let gray4 = 0xCBD5E0 17 | public static let gray5 = 0xA0AEC0 18 | public static let gray6 = 0x718096 19 | public static let gray7 = 0x4A5568 20 | public static let gray8 = 0x2D3748 21 | public static let gray9 = 0x1A202C 22 | 23 | public static let blue1 = 0xEBF8FF 24 | public static let blue2 = 0xBEE3F8 25 | public static let blue3 = 0x90CDF4 26 | public static let blue4 = 0x63B3ED 27 | public static let blue5 = 0x4299E1 28 | public static let blue6 = 0x3182CE 29 | public static let blue7 = 0x2B6CB0 30 | public static let blue8 = 0x2C5282 31 | public static let blue9 = 0x2A4365 32 | 33 | public static let teal1 = 0xE6FFFA 34 | public static let teal2 = 0xB2F5EA 35 | public static let teal3 = 0x81E6D9 36 | public static let teal4 = 0x4FD1C5 37 | public static let teal5 = 0x38B2AC 38 | public static let teal6 = 0x319795 39 | public static let teal7 = 0x2C7A7B 40 | public static let teal8 = 0x285E61 41 | public static let teal9 = 0x234E52 42 | 43 | public static let indigo1 = 0xEBF4FF 44 | public static let indigo2 = 0xC3DAFE 45 | public static let indigo3 = 0xA3BFFA 46 | public static let indigo4 = 0x7F9CF5 47 | public static let indigo5 = 0x667EEA 48 | public static let indigo6 = 0x5A67D8 49 | public static let indigo7 = 0x4C51BF 50 | public static let indigo8 = 0x434190 51 | public static let indigo9 = 0x3C366B 52 | 53 | public static let red1 = 0xFFF5F5 54 | public static let red2 = 0xFED7D7 55 | public static let red3 = 0xFEB2B2 56 | public static let red4 = 0xFC8181 57 | public static let red5 = 0xF56565 58 | public static let red6 = 0xE53E3E 59 | public static let red7 = 0xC53030 60 | public static let red8 = 0x9B2C2C 61 | public static let red9 = 0x742A2A 62 | 63 | public static let green1 = 0xF0FFF4 64 | public static let green2 = 0xC6F6D5 65 | public static let green3 = 0x9AE6B4 66 | public static let green4 = 0x68D391 67 | public static let green5 = 0x48BB78 68 | public static let green6 = 0x38A169 69 | public static let green7 = 0x2F855A 70 | public static let green8 = 0x276749 71 | public static let green9 = 0x22543D 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Sources/Shared/Throttler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Throttler.swift 3 | // Omnia 4 | // 5 | // Created by khoa on 12/02/2021. 6 | // 7 | 8 | import Foundation 9 | 10 | /// Run the action once during timeframe 11 | public class Throttler { 12 | private let timeFrame: TimeInterval 13 | private let queue: DispatchQueue 14 | private var runAt: Date? 15 | 16 | public init(timeFrame: TimeInterval, queue: DispatchQueue = .main) { 17 | self.timeFrame = timeFrame 18 | self.queue = queue 19 | } 20 | 21 | public func run(action: @escaping () -> Void) { 22 | let now = Date() 23 | if let runAt = runAt, now.timeIntervalSince(runAt) < timeFrame { 24 | return 25 | } 26 | 27 | queue.async(execute: action) 28 | runAt = now 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Sources/Shared/Type.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Type.swift 3 | // Omnia 4 | // 5 | // Created by khoa on 12/09/2019. 6 | // Copyright © 2019 Khoa Pham. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public typealias JSONDictionary = [String: Any] 12 | public typealias JSONArray = [JSONDictionary] 13 | 14 | #if canImport(UIKit) 15 | import UIKit 16 | public typealias UXImage = UIImage 17 | #elseif canImport(AppKit) 18 | import AppKit 19 | public typealias UXImage = NSImage 20 | #elseif canImport(WatchKit) 21 | import WatchKit 22 | public typealias UXImage = UIImage 23 | #endif 24 | -------------------------------------------------------------------------------- /Sources/Shared/URL.swift: -------------------------------------------------------------------------------- 1 | // 2 | // URL.swift 3 | // Omnia 4 | // 5 | // Created by khoa on 20/06/2021. 6 | // 7 | 8 | import Foundation 9 | import CryptoKit 10 | 11 | public extension String { 12 | var sha256Hashed: String { 13 | guard let data = data(using: .utf8) else { 14 | return self 15 | } 16 | 17 | let hashed = SHA256.hash(data: data) 18 | return hashed.compactMap { String(format: "%02x", $0) }.joined() 19 | } 20 | } 21 | 22 | public extension URL { 23 | var sha256Hashed: String { 24 | absoluteString.sha256Hashed 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Sources/Shared/UXImage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UXImage.swift 3 | // Omnia 4 | // 5 | // Created by khoa on 15/09/2021. 6 | // 7 | 8 | #if canImport(UIKit) 9 | import UIKit 10 | #else 11 | import AppKit 12 | #endif 13 | 14 | import ImageIO 15 | 16 | public extension UXImage { 17 | /// Using ImageIO 18 | static func downsampled( 19 | at url: URL, 20 | size: CGSize, 21 | shouldCacheImmediately: Bool = true 22 | ) -> UXImage? { 23 | // Create an CGImageSource that represent an image 24 | let imageSourceOptions = [ 25 | kCGImageSourceShouldCache: false 26 | ] as CFDictionary 27 | 28 | guard 29 | let imageSource = CGImageSourceCreateWithURL(url as CFURL, imageSourceOptions) 30 | else { 31 | return nil 32 | } 33 | 34 | let scale: CGFloat 35 | #if canImport(AppKit) 36 | scale = NSScreen.main?.backingScaleFactor ?? 1 37 | #else 38 | scale = UIScreen.main.scale 39 | #endif 40 | 41 | let maxDimensionInPixels = max(size.width, size.height) * scale 42 | 43 | let downsampleOptions = [ 44 | kCGImageSourceCreateThumbnailFromImageAlways: true, 45 | kCGImageSourceShouldCacheImmediately: shouldCacheImmediately, 46 | kCGImageSourceCreateThumbnailWithTransform: true, 47 | kCGImageSourceThumbnailMaxPixelSize: maxDimensionInPixels 48 | ] as CFDictionary 49 | 50 | guard let downsampledImage = CGImageSourceCreateThumbnailAtIndex( 51 | imageSource, 0, downsampleOptions 52 | ) else { 53 | return nil 54 | } 55 | 56 | #if canImport(AppKit) 57 | return NSImage(cgImage: downsampledImage, size: size) 58 | #else 59 | return UIImage(cgImage: downsampledImage) 60 | #endif 61 | } 62 | 63 | func scaledSize(targetSize: CGSize) -> CGSize { 64 | let widthRatio = targetSize.width / size.width 65 | let heightRatio = targetSize.height / size.height 66 | let scaleFactor = min(widthRatio, heightRatio) 67 | return CGSize( 68 | width: size.width * scaleFactor, 69 | height: size.height * scaleFactor 70 | ) 71 | } 72 | } 73 | 74 | -------------------------------------------------------------------------------- /Sources/Shared/UserDefault.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserDefaults.swift 3 | // Omnia 4 | // 5 | // Created by khoa on 20/03/2021. 6 | // 7 | 8 | import Foundation 9 | 10 | @propertyWrapper 11 | public struct UserDefault { 12 | let key: String 13 | let defaultValue: Value 14 | let userDefaults: UserDefaults 15 | 16 | public init( 17 | key: String, 18 | defaultValue: Value, 19 | userDefaults: UserDefaults = .standard 20 | ) { 21 | self.key = key 22 | self.defaultValue = defaultValue 23 | self.userDefaults = userDefaults 24 | } 25 | 26 | public var wrappedValue: Value { 27 | get { 28 | if let value = userDefaults.object(forKey: key) as? Value { 29 | return value 30 | } else { 31 | userDefaults.set(defaultValue, forKey: key) 32 | return defaultValue 33 | } 34 | } 35 | set { 36 | userDefaults.set(newValue, forKey: key) 37 | } 38 | } 39 | } 40 | 41 | -------------------------------------------------------------------------------- /Sources/iOS/Haptics.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Haptics.swift 3 | // Omnia 4 | // 5 | // Created by khoa on 14/05/2021. 6 | // 7 | 8 | #if os(iOS) 9 | import UIKit 10 | 11 | public struct Haptics { 12 | public static func selection() { 13 | let generator = UISelectionFeedbackGenerator() 14 | generator.prepare() 15 | generator.selectionChanged() 16 | } 17 | 18 | public static func impact(_ style: UIImpactFeedbackGenerator.FeedbackStyle) { 19 | let generator = UIImpactFeedbackGenerator(style: style) 20 | generator.prepare() 21 | generator.impactOccurred() 22 | } 23 | 24 | public static func notification(_ style: UINotificationFeedbackGenerator.FeedbackType) { 25 | let generator = UINotificationFeedbackGenerator() 26 | generator.prepare() 27 | generator.notificationOccurred(style) 28 | } 29 | } 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /Sources/iOS/NSLayoutConstraint.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSLayoutConstraint+Extensions.swift 3 | // Omnia-iOS 4 | // 5 | // Created by khoa on 10/04/2019. 6 | // Copyright © 2019 Khoa Pham. All rights reserved. 7 | // 8 | 9 | #if canImport(UIKit) && os(iOS) 10 | 11 | import UIKit 12 | 13 | public extension NSLayoutConstraint { 14 | 15 | /// Disable auto resizing mask and activate constraints 16 | /// 17 | /// - Parameter constraints: constraints to activate 18 | static func on(_ constraints: [NSLayoutConstraint]) { 19 | constraints.forEach { 20 | ($0.firstItem as? UIView)?.translatesAutoresizingMaskIntoConstraints = false 21 | $0.isActive = true 22 | } 23 | } 24 | 25 | static func on(_ constraintsArray: [[NSLayoutConstraint]]) { 26 | let constraints = constraintsArray.flatMap({ $0 }) 27 | NSLayoutConstraint.on(constraints) 28 | } 29 | 30 | func priority(_ value: Float) -> NSLayoutConstraint { 31 | priority = UILayoutPriority(value) 32 | return self 33 | } 34 | } 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /Sources/iOS/UICollectionView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Omnia 3 | // 4 | // Created by khoa on 11/04/2019. 5 | // Copyright © 2019 Khoa Pham. All rights reserved. 6 | // 7 | 8 | #if canImport(UIKit) && os(iOS) 9 | 10 | import UIKit 11 | 12 | public extension UICollectionView { 13 | 14 | /// Register a cell 15 | /// 16 | /// - Parameter cell: The type of the cell 17 | func register(cellType: T.Type) { 18 | register(T.self, forCellWithReuseIdentifier: String(describing: T.self)) 19 | } 20 | 21 | /// Dequeue a cell 22 | /// 23 | /// - Parameter indexPath: The indexPath to dequeue 24 | /// - Returns: The dequeued cell 25 | func dequeue(for indexPath: IndexPath) -> T? { 26 | return dequeueReusableCell( 27 | withReuseIdentifier: String(describing: T.self), 28 | for: indexPath 29 | ) as? T 30 | } 31 | 32 | func centerIndexPath() -> IndexPath? { 33 | guard 34 | let point = superview?.convert(center, to: self), 35 | let indexPath = indexPathForItem(at: point) 36 | else { 37 | return nil 38 | } 39 | 40 | return indexPath 41 | } 42 | 43 | func sideCells() -> (leftCell: UICollectionViewCell?, rightCell: UICollectionViewCell?) { 44 | guard let indexPath = centerIndexPath() else { 45 | return (nil, nil) 46 | } 47 | 48 | let leftCell = cellForItem(at: IndexPath(item: indexPath.item-1, section: indexPath.section)) 49 | let rightCell = cellForItem(at: IndexPath(item: indexPath.item+1, section: indexPath.section)) 50 | 51 | return (leftCell, rightCell) 52 | } 53 | } 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /Sources/iOS/UIColor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Omnia 3 | // 4 | // Created by khoa on 11/04/2019. 5 | // Copyright © 2019 Khoa Pham. All rights reserved. 6 | // 7 | 8 | #if canImport(UIKit) 9 | 10 | import UIKit 11 | 12 | public extension UIColor { 13 | 14 | /// Init color from hex string 15 | /// 16 | /// - Parameter hex: A hex string, with or without # 17 | convenience init(hex: String) { 18 | let hex = hex.replacingOccurrences(of: "#", with: "") 19 | 20 | // Need 6 characters 21 | guard hex.count == 6 else { 22 | self.init(white: 1.0, alpha: 1.0) 23 | return 24 | } 25 | 26 | self.init( 27 | red: CGFloat((Int(hex, radix: 16)! >> 16) & 0xFF) / 255.0, 28 | green: CGFloat((Int(hex, radix: 16)! >> 8) & 0xFF) / 255.0, 29 | blue: CGFloat((Int(hex, radix: 16)!) & 0xFF) / 255.0, 30 | alpha: 1.0 31 | ) 32 | } 33 | 34 | convenience init(hex: Int, alpha: Double = 1) { 35 | let components = ( 36 | R: Double((hex >> 16) & 0xff) / 255, 37 | G: Double((hex >> 08) & 0xff) / 255, 38 | B: Double((hex >> 00) & 0xff) / 255 39 | ) 40 | 41 | self.init( 42 | red: CGFloat(components.R), 43 | green: CGFloat(components.G), 44 | blue: CGFloat(components.B), 45 | alpha: CGFloat(alpha) 46 | ) 47 | } 48 | } 49 | 50 | /// Represent a HSL color 51 | public struct HSLColor { 52 | public var hue: CGFloat 53 | public var saturation: CGFloat 54 | public var lightness: CGFloat 55 | public var alpha: CGFloat 56 | } 57 | 58 | extension UIColor { 59 | 60 | /// Convert to HSL color 61 | public func toHSL() -> HSLColor { 62 | var h: CGFloat = 0 63 | var s: CGFloat = 0 64 | 65 | var r: CGFloat = 0 66 | var g: CGFloat = 0 67 | var b: CGFloat = 0 68 | var a: CGFloat = 0 69 | 70 | getRed(&r, green: &g, blue: &b, alpha: &a) 71 | 72 | let max = Swift.max(r, g, b) 73 | let min = Swift.min(r, g, b) 74 | 75 | let l = (min+max) / 2.0 76 | 77 | if (l <= 0.0) { 78 | return HSLColor(hue: h, saturation: s, lightness: l, alpha: a) 79 | } 80 | 81 | let delta = max - min 82 | s = delta 83 | 84 | if (s > 0.0) { 85 | s = s / ((l <= 0.5) ? (max + min) : (2.0 - max - min)) 86 | } else { 87 | return HSLColor(hue: h, saturation: s, lightness: l, alpha: a) 88 | } 89 | 90 | let r2 = (max - r) / delta 91 | let g2 = (max - g) / delta 92 | let b2 = (max - b) / delta 93 | 94 | if (r == max) { 95 | h = (g == min ? 5.0 + b2 : 1.0 - g2) 96 | } else if (g == max) { 97 | h = (b == min ? 1.0 + r2 : 3.0 - b2) 98 | } else { 99 | h = (r == min ? 3.0 + g2 : 5.0 - r2) 100 | } 101 | 102 | h = h / 6.0 103 | 104 | return HSLColor(hue: h, saturation: s, lightness: l, alpha: a) 105 | } 106 | 107 | /// Init from HSL color 108 | /// 109 | /// - Parameter hslColor: The HSL color 110 | public convenience init(hslColor: HSLColor) { 111 | // https://github.com/thisandagain/color 112 | 113 | // Check for saturation. 114 | // If there isn't any just return the luminance 115 | // value for each, which results in gray. 116 | if (hslColor.saturation == 0.0) { 117 | self.init( 118 | red: hslColor.lightness, 119 | green: hslColor.lightness, 120 | blue: hslColor.lightness, 121 | alpha: hslColor.alpha 122 | ) 123 | return 124 | } 125 | 126 | // Test for luminance and compute temporary 127 | // values based on luminance and saturation 128 | var temp2: CGFloat 129 | if (hslColor.lightness < 0.5) { 130 | temp2 = hslColor.lightness * (1.0 + hslColor.saturation) 131 | } else { 132 | temp2 = hslColor.lightness 133 | + hslColor.saturation 134 | - hslColor.lightness * hslColor.saturation 135 | } 136 | 137 | let temp1 = 2.0 * hslColor.lightness - temp2 138 | 139 | // Compute intermediate values based on hue 140 | var temp: [CGFloat] = Array(repeatElement(0, count: 3)) 141 | temp[0] = hslColor.hue + 1.0 / 3.0 142 | temp[1] = hslColor.hue 143 | temp[2] = hslColor.hue - 1.0 / 3.0 144 | 145 | Array(0..<3).forEach { i in 146 | // Adjust the range 147 | if (temp[i] < 0.0) { 148 | temp[i] += 1.0 149 | } 150 | 151 | if (temp[i] > 1.0) { 152 | temp[i] -= 1.0 153 | } 154 | 155 | if (6.0 * temp[i] < 1.0) { 156 | temp[i] = temp1 + (temp2 - temp1) * 6.0 * temp[i] 157 | } else { 158 | if (2.0 * temp[i] < 1.0) { 159 | temp[i] = temp2 160 | } else { 161 | if (3.0 * temp[i] < 2.0) { 162 | temp[i] = temp1 + (temp2 - temp1) * ((2.0 / 3.0) - temp[i]) * 6.0 163 | } else { 164 | temp[i] = temp1 165 | } 166 | } 167 | } 168 | } 169 | 170 | self.init(red: temp[0], green: temp[1], blue: temp[2], alpha: hslColor.alpha) 171 | } 172 | } 173 | 174 | extension UIColor { 175 | 176 | /// Return a random color 177 | /// 178 | /// - Returns: A random color 179 | public static func random() -> UIColor { 180 | let randomHue = CGFloat(arc4random_uniform(UInt32.max)) / CGFloat(UInt32.max) 181 | 182 | let hslColor = HSLColor( 183 | hue: randomHue, 184 | saturation: 0.5, 185 | lightness: 0.5, 186 | alpha: 1 187 | ) 188 | 189 | return UIColor(hslColor: hslColor) 190 | } 191 | 192 | /// Darken the color 193 | /// 194 | /// - Parameter amount: The amount to alter lightness 195 | /// - Returns: A darkened color 196 | public func darken(amount: CGFloat = 0.2) -> UIColor { 197 | var hslColor = toHSL() 198 | hslColor.lightness = min(1 - amount, hslColor.lightness - amount) 199 | 200 | return UIColor(hslColor: hslColor) 201 | } 202 | 203 | /// Lighten the color 204 | /// 205 | /// - Parameter amount: The amount to alter lightness 206 | /// - Returns: A lightened color 207 | public func lighten(amount: CGFloat = 0.2) -> UIColor { 208 | var hslColor = toHSL() 209 | hslColor.lightness = max(amount, hslColor.lightness + amount) 210 | 211 | return UIColor(hslColor: hslColor) 212 | } 213 | } 214 | 215 | extension UIColor { 216 | static func rgb(_ r: CGFloat, _ g: CGFloat, _ b: CGFloat) -> UIColor { 217 | return UIColor(red: r/255, green: r/255, blue: b/255, alpha: 1.0) 218 | } 219 | } 220 | 221 | #endif 222 | -------------------------------------------------------------------------------- /Sources/iOS/UIFont.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIFont.swift 3 | // Omnia-iOS 4 | // 5 | // Created by khoa on 11/04/2019. 6 | // Copyright © 2019 Khoa Pham. All rights reserved. 7 | // 8 | 9 | #if canImport(UIKit) 10 | 11 | import UIKit 12 | 13 | public extension UIFont { 14 | static func registerCustom(fileName: String, fileExtension: String, bundle: Bundle = .main) { 15 | guard 16 | let url = bundle.url(forResource: fileName, withExtension: fileExtension), 17 | let data = try? Data(contentsOf: url), 18 | let provider = CGDataProvider(data: data as CFData), 19 | let ref = CGFont(provider) 20 | else { 21 | return 22 | } 23 | 24 | var errorRef: Unmanaged? = nil 25 | if !CTFontManagerRegisterGraphicsFont(ref, &errorRef) { 26 | print(errorRef as Any) 27 | } 28 | } 29 | } 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /Sources/iOS/UIImage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Omnia 3 | // 4 | // Created by khoa on 11/04/2019. 5 | // Copyright © 2019 Khoa Pham. All rights reserved. 6 | // 7 | 8 | #if canImport(UIKit) && os(iOS) 9 | 10 | import UIKit 11 | 12 | public extension UIImage { 13 | 14 | /// Init an UIImage from a UIColor 15 | /// 16 | /// - Parameter color: The color to fill 17 | /// - Parameter size: The size of the generated image 18 | /// - Returns: The image with solid color 19 | static func from(color: UIColor, size: CGSize) -> UIImage { 20 | let renderer = UIGraphicsImageRenderer(size: size) 21 | return renderer.image { context in 22 | color.setFill() 23 | context.fill(CGRect(origin: .zero, size: size)) 24 | } 25 | } 26 | 27 | func resize(targetSize: CGSize) -> UIImage { 28 | let scaledSize = self.scaledSize(targetSize: targetSize) 29 | 30 | let renderer = UIGraphicsImageRenderer( 31 | size: scaledSize 32 | ) 33 | 34 | let image = renderer.image { _ in 35 | self.draw(in: CGRect( 36 | origin: .zero, 37 | size: scaledSize 38 | )) 39 | } 40 | 41 | return image 42 | } 43 | } 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /Sources/iOS/UIScrollView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Omnia 3 | // 4 | // Created by khoa on 11/04/2019. 5 | // Copyright © 2019 Khoa Pham. All rights reserved. 6 | // 7 | 8 | #if canImport(UIKit) && os(iOS) 9 | 10 | import UIKit 11 | 12 | extension UIScrollView { 13 | 14 | /// Scroll to bottom 15 | public func scrollToBottom() { 16 | let bottomOffset = CGPoint( 17 | x: 0, 18 | y: contentSize.height - bounds.size.height + contentInset.bottom 19 | ) 20 | 21 | setContentOffset(bottomOffset, animated: true) 22 | } 23 | } 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /Sources/iOS/UITableView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Omnia 3 | // 4 | // Created by khoa on 11/04/2019. 5 | // Copyright © 2019 Khoa Pham. All rights reserved. 6 | // 7 | 8 | #if canImport(UIKit) && os(iOS) 9 | 10 | import UIKit 11 | 12 | extension UITableView { 13 | 14 | /// Register a cell 15 | /// 16 | /// - Parameter cell: The type of the cell 17 | public func register(cellType: T.Type) { 18 | register(T.self, forCellReuseIdentifier: String(describing: T.self)) 19 | } 20 | 21 | /// Dequeue a cell 22 | /// 23 | /// - Parameter indexPath: The indexPath to dequeue 24 | /// - Returns: The dequeued cell 25 | public func dequeue(for indexPath: IndexPath) -> T? { 26 | return dequeueReusableCell( 27 | withIdentifier: String(describing: T.self), 28 | for: indexPath 29 | ) as? T 30 | } 31 | } 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /Sources/iOS/UIView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Omnia 3 | // 4 | // Created by khoa on 11/04/2019. 5 | // Copyright © 2019 Khoa Pham. All rights reserved. 6 | // 7 | 8 | #if canImport(UIKit) && os(iOS) 9 | 10 | import UIKit 11 | 12 | public extension UIView { 13 | 14 | /// Take a snapshot of a view 15 | /// 16 | /// - Returns: The image from the snapshot 17 | func toImage() -> UIImage? { 18 | let renderer = UIGraphicsImageRenderer(size: bounds.size) 19 | return renderer.image { _ in 20 | self.drawHierarchy(in: bounds, afterScreenUpdates: true) 21 | } 22 | } 23 | 24 | /// Add many subviews 25 | /// 26 | /// - Parameter views: The subviews 27 | func addSubviews(_ views: [UIView]) { 28 | views.forEach { 29 | addSubview($0) 30 | } 31 | } 32 | 33 | func findRecursively(type: T.Type, match: (T) -> Bool) -> T? { 34 | for view in subviews { 35 | if let subview = view as? T, match(subview) { 36 | return subview 37 | } else { 38 | return view.findRecursively(type: type, match: match) 39 | } 40 | } 41 | 42 | return nil 43 | } 44 | } 45 | 46 | public extension UIView { 47 | func pinCenter(view: UIView) -> [NSLayoutConstraint] { 48 | return [ 49 | centerXAnchor.constraint(equalTo: view.centerXAnchor), 50 | centerYAnchor.constraint(equalTo: view.centerYAnchor) 51 | ] 52 | } 53 | 54 | func pinEdges(view: UIView, inset: UIEdgeInsets = UIEdgeInsets.zero) -> [NSLayoutConstraint] { 55 | return [ 56 | leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: inset.left), 57 | trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: inset.right), 58 | topAnchor.constraint(equalTo: view.topAnchor, constant: inset.top), 59 | bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: inset.bottom) 60 | ] 61 | } 62 | 63 | func pin(size: CGSize) -> [NSLayoutConstraint] { 64 | return [ 65 | widthAnchor.constraint(equalToConstant: size.width), 66 | heightAnchor.constraint(equalToConstant: size.height) 67 | ] 68 | } 69 | } 70 | 71 | #endif 72 | -------------------------------------------------------------------------------- /Sources/iOS/UIViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Omnia 3 | // 4 | // Created by khoa on 11/04/2019. 5 | // Copyright © 2019 Khoa Pham. All rights reserved. 6 | // 7 | 8 | #if canImport(UIKit) && os(iOS) 9 | 10 | import UIKit 11 | 12 | extension UIViewController { 13 | /// Add a child view controller 14 | /// 15 | /// - Parameter chilController: The child view controller to add 16 | public func install(childController: UIViewController) { 17 | addChild(childController) 18 | view.addSubview(childController.view) 19 | childController.didMove(toParent: self) 20 | } 21 | 22 | /// Remove from the parent view controller 23 | public func uninstall() { 24 | guard parent != nil else { 25 | return 26 | } 27 | 28 | willMove(toParent: nil) 29 | view.removeFromSuperview() 30 | removeFromParent() 31 | } 32 | } 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /Sources/macOS/ClickedCollectionView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ClickedCollectionView.swift 3 | // Omnia-macOS 4 | // 5 | // Created by khoa on 24/09/2019. 6 | // Copyright © 2019 Khoa Pham. All rights reserved. 7 | // 8 | 9 | #if os(OSX) && !targetEnvironment(macCatalyst) 10 | 11 | import AppKit 12 | 13 | public class ClickedCollectionView: NSCollectionView { 14 | public var clickedIndexPath: IndexPath? 15 | 16 | public override func menu(for event: NSEvent) -> NSMenu? { 17 | clickedIndexPath = nil 18 | 19 | let point = convert(event.locationInWindow, from: nil) 20 | for section in 0.. 14 | : NSObject, NSCollectionViewDataSource, NSCollectionViewDelegateFlowLayout { 15 | 16 | public let layout = NSCollectionViewFlowLayout() 17 | public let scrollView = NSScrollView() 18 | public let collectionView = ClickedCollectionView() 19 | 20 | public var items = [Item]() 21 | public var itemSize: () -> CGSize = { .zero } 22 | public var configure: (Item, Cell) -> Void = { _, _ in } 23 | 24 | public var menuItemActions = [MenuItemAction]() 25 | public var menu = NSMenu() 26 | 27 | public struct MenuItemAction { 28 | public let menuItem: NSMenuItem 29 | public let action: (Item, IndexPath) -> Void 30 | } 31 | 32 | public override init() { 33 | super.init() 34 | 35 | layout.minimumLineSpacing = 4 36 | layout.sectionInset = NSEdgeInsets(top: 4, left: 4, bottom: 4, right: 4) 37 | 38 | collectionView.dataSource = self 39 | collectionView.delegate = self 40 | collectionView.collectionViewLayout = layout 41 | collectionView.allowsMultipleSelection = false 42 | collectionView.backgroundColors = [.clear] 43 | collectionView.isSelectable = true 44 | collectionView.menu = self.menu 45 | 46 | collectionView.register(Cell.self, forItemWithIdentifier: Cell.itemId) 47 | 48 | scrollView.documentView = collectionView 49 | } 50 | 51 | open func reload(items: [Item]) { 52 | self.items = items 53 | collectionView.reloadData() 54 | } 55 | 56 | // MARK: - Menu 57 | 58 | open func addMenuItem(title: String, action: @escaping (Item, IndexPath) -> Void) { 59 | let item = NSMenuItem( 60 | title: title, 61 | action: #selector(onMenuItemClicked(_:)), 62 | keyEquivalent: "" 63 | ) 64 | 65 | item.target = self 66 | 67 | let menuItemAction = MenuItemAction(menuItem: item, action: action) 68 | self.menuItemActions.append(menuItemAction) 69 | menu.addItem(item) 70 | } 71 | 72 | @objc open func onMenuItemClicked(_ menuItem: NSMenuItem) { 73 | guard 74 | let indexPath = collectionView.clickedIndexPath, 75 | indexPath.item < items.count, 76 | let menuItemAction = self.menuItemActions.first(where: { $0.menuItem == menuItem }) 77 | else { 78 | return 79 | } 80 | 81 | menuItemAction.action(items[indexPath.item], indexPath) 82 | } 83 | 84 | // MARK: - Items 85 | 86 | open func add(item: Item) { 87 | items.insert(item, at: 0) 88 | let indexPath = IndexPath(item: 0, section: 0) 89 | collectionView.animator().insertItems(at: Set(arrayLiteral: indexPath)) 90 | } 91 | 92 | open func remove(item: Item) { 93 | guard let index = items.firstIndex(where: { $0 == item }) else { 94 | return 95 | } 96 | 97 | remove(index: index) 98 | } 99 | 100 | open func remove(index: Int) { 101 | items.remove(at: index) 102 | let indexPath = IndexPath(item: index, section: 0) 103 | collectionView.animator().deleteItems(at: Set(arrayLiteral: indexPath)) 104 | } 105 | 106 | // MARK: - NSCollectionViewDataSource 107 | 108 | open func numberOfSections(in collectionView: NSCollectionView) -> Int { 109 | return 1 110 | } 111 | 112 | open func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int { 113 | return items.count 114 | } 115 | 116 | open func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem { 117 | 118 | let cell = collectionView.makeItem(withIdentifier: Cell.itemId, for: indexPath) as! Cell 119 | let item = items[indexPath.item] 120 | configure(item, cell) 121 | return cell 122 | } 123 | 124 | // MARK: - NSCollectionViewDelegateFlowLayout 125 | 126 | open func collectionView(_ collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> NSSize { 127 | 128 | return itemSize() 129 | } 130 | 131 | open func collectionView(_ collectionView: NSCollectionView, didSelectItemsAt indexPaths: Set) { 132 | collectionView.deselectItems(at: indexPaths) 133 | } 134 | } 135 | 136 | #endif 137 | -------------------------------------------------------------------------------- /Sources/macOS/CollectionViewSectionHandler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CollectionViewSectionHandler.swift 3 | // Omnia-macOS 4 | // 5 | // Created by khoa on 02/10/2019. 6 | // Copyright © 2019 Khoa Pham. All rights reserved. 7 | // 8 | 9 | #if os(OSX) && !targetEnvironment(macCatalyst) 10 | 11 | import AppKit 12 | 13 | open class CollectionViewSectionHandler 14 | : CollectionViewHandler { 15 | 16 | public struct Section { 17 | public let name: String 18 | public let items: [Item] 19 | 20 | public init(name: String, items: [Item]) { 21 | self.name = name 22 | self.items = items 23 | } 24 | } 25 | 26 | public var sections = [Section]() 27 | public var configureHeader: (Section, HeaderView) -> Void = { _, _ in } 28 | 29 | public override init() { 30 | super.init() 31 | 32 | layout.headerReferenceSize = CGSize(width: 100, height: 20) 33 | 34 | collectionView.register( 35 | HeaderView.self, 36 | forSupplementaryViewOfKind: NSCollectionView.elementKindSectionHeader, 37 | withIdentifier: HeaderView.itemId 38 | ) 39 | } 40 | 41 | open func reload(sections: [Section]) { 42 | self.sections = sections 43 | collectionView.reloadData() 44 | } 45 | 46 | override open func numberOfSections(in collectionView: NSCollectionView) -> Int { 47 | return sections.count 48 | } 49 | 50 | override open func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int { 51 | return sections[section].items.count 52 | } 53 | 54 | override open func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem { 55 | 56 | let item = sections[indexPath.section].items[indexPath.item] 57 | let cell = collectionView.makeItem(withIdentifier: Cell.itemId, for: indexPath) as! Cell 58 | configure(item, cell) 59 | return cell 60 | } 61 | 62 | @objc (collectionView:viewForSupplementaryElementOfKind:atIndexPath:) 63 | open func collectionView(_ collectionView: NSCollectionView, viewForSupplementaryElementOfKind kind: NSCollectionView.SupplementaryElementKind, at indexPath: IndexPath) -> NSView { 64 | 65 | if kind == NSCollectionView.elementKindSectionHeader { 66 | let view = collectionView.makeSupplementaryView( 67 | ofKind: kind, 68 | withIdentifier: HeaderView.itemId, 69 | for: indexPath 70 | ) as! HeaderView 71 | 72 | configureHeader(sections[indexPath.section], view) 73 | return view 74 | } else { 75 | return NSView() 76 | } 77 | } 78 | 79 | @objc open override func onMenuItemClicked(_ menuItem: NSMenuItem) { 80 | guard 81 | let indexPath = collectionView.clickedIndexPath, 82 | indexPath.section < sections.count, 83 | let menuItemAction = self.menuItemActions.first(where: { $0.menuItem == menuItem }) 84 | else { 85 | return 86 | } 87 | 88 | let section = sections[indexPath.section] 89 | 90 | guard 91 | indexPath.item < section.items.count 92 | else { 93 | return 94 | } 95 | 96 | menuItemAction.action(section.items[indexPath.item], indexPath) 97 | } 98 | } 99 | 100 | #endif 101 | -------------------------------------------------------------------------------- /Sources/macOS/MenuHandler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MenuHandler.swift 3 | // Omnia-macOS 4 | // 5 | // Created by khoa on 01/10/2019. 6 | // Copyright © 2019 Khoa Pham. All rights reserved. 7 | // 8 | 9 | #if os(OSX) && !targetEnvironment(macCatalyst) 10 | 11 | import AppKit 12 | 13 | public class MenuHandler { 14 | public let menu = NSMenu() 15 | public var items = [Item]() 16 | 17 | public class Item { 18 | public let menuItem: NSMenuItem 19 | public let action: () -> Void 20 | 21 | public init(menuItem: NSMenuItem, action: @escaping () -> Void) { 22 | self.menuItem = menuItem 23 | self.action = action 24 | } 25 | } 26 | 27 | public init() {} 28 | 29 | public func add(title: String, image: NSImage? = nil, action: @escaping () -> Void) { 30 | let menuItem = NSMenuItem( 31 | title: title, 32 | action: #selector(onMenuItemTouched(_:)), 33 | keyEquivalent: "" 34 | ) 35 | 36 | menuItem.image = image 37 | menuItem.target = self 38 | menu.addItem(menuItem) 39 | 40 | let item = Item(menuItem: menuItem, action: action) 41 | items.append(item) 42 | } 43 | 44 | public func show(from button: NSButton, in view: NSView) { 45 | menu.popUp( 46 | positioning: nil, 47 | at: button.frame.origin, 48 | in: view 49 | ) 50 | } 51 | 52 | @objc public func onMenuItemTouched(_ menuItem: NSMenuItem) { 53 | if let item = items.first(where: { $0.menuItem === menuItem }) { 54 | item.action() 55 | } 56 | } 57 | } 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /Sources/macOS/NSButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSButton.swift 3 | // Omnia-macOS 4 | // 5 | // Created by khoa on 12/09/2019. 6 | // Copyright © 2019 Khoa Pham. All rights reserved. 7 | // 8 | 9 | #if os(OSX) && !targetEnvironment(macCatalyst) 10 | 11 | import AppKit 12 | 13 | public extension NSButton { 14 | func stylePlain(title: String, color: NSColor, font: NSFont) { 15 | attributedTitle = NSAttributedString(string: title, attributes: [ 16 | NSAttributedString.Key.foregroundColor: color, 17 | NSAttributedString.Key.font: font 18 | ]) 19 | 20 | isBordered = false 21 | setButtonType(.momentaryChange) 22 | } 23 | 24 | func stylePlain(imageName: String) { 25 | isBordered = false 26 | image = NSImage(named: NSImage.Name(imageName)) 27 | imageScaling = .scaleProportionallyUpOrDown 28 | } 29 | 30 | var isOn: Bool { 31 | get { 32 | return state == .on ? true : false 33 | } 34 | set { 35 | state = newValue ? .on : .off 36 | } 37 | } 38 | } 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /Sources/macOS/NSCollectionView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSCollectionView.swift 3 | // Omnia-macOS 4 | // 5 | // Created by khoa on 02/10/2019. 6 | // Copyright © 2019 Khoa Pham. All rights reserved. 7 | // 8 | 9 | #if os(OSX) && !targetEnvironment(macCatalyst) 10 | 11 | import AppKit 12 | 13 | public extension NSCollectionView { 14 | func scrollToTop() { 15 | enclosingScrollView?.contentView.scroll(.zero) 16 | } 17 | } 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /Sources/macOS/NSCollectionViewItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSCollectionViewItem.swift 3 | // Omnia-macOS 4 | // 5 | // Created by khoa on 24/09/2019. 6 | // Copyright © 2019 Khoa Pham. All rights reserved. 7 | // 8 | 9 | #if os(OSX) && !targetEnvironment(macCatalyst) 10 | 11 | import AppKit 12 | 13 | public extension NSCollectionViewItem { 14 | static var itemId: NSUserInterfaceItemIdentifier { 15 | return NSUserInterfaceItemIdentifier(rawValue: String(describing: self)) 16 | } 17 | } 18 | 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /Sources/macOS/NSColor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSColor+Extensions.swift 3 | // Omnia-macOS 4 | // 5 | // Created by Khoa Pham on 21.12.2017. 6 | // Copyright © 2017 Khoa Pham. All rights reserved. 7 | // 8 | 9 | #if os(OSX) && !targetEnvironment(macCatalyst) 10 | 11 | import AppKit 12 | 13 | public extension NSColor { 14 | 15 | /// Init color from hex string 16 | /// 17 | /// - Parameter hex: A hex string, with or without # 18 | convenience init(hex: String) { 19 | let hex = hex.replacingOccurrences(of: "#", with: "") 20 | 21 | // Need 6 characters 22 | guard hex.count == 6 else { 23 | self.init(white: 1.0, alpha: 1.0) 24 | return 25 | } 26 | 27 | self.init( 28 | red: CGFloat((Int(hex, radix: 16)! >> 16) & 0xFF) / 255.0, 29 | green: CGFloat((Int(hex, radix: 16)! >> 8) & 0xFF) / 255.0, 30 | blue: CGFloat((Int(hex, radix: 16)!) & 0xFF) / 255.0, alpha: 1.0) 31 | } 32 | 33 | convenience init(hex: Int, alpha: Double = 1) { 34 | let components = ( 35 | R: Double((hex >> 16) & 0xff) / 255, 36 | G: Double((hex >> 08) & 0xff) / 255, 37 | B: Double((hex >> 00) & 0xff) / 255 38 | ) 39 | 40 | self.init( 41 | red: CGFloat(components.R), 42 | green: CGFloat(components.G), 43 | blue: CGFloat(components.B), 44 | alpha: CGFloat(alpha) 45 | ) 46 | } 47 | } 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /Sources/macOS/NSImage.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSImage.swift 3 | // Omnia-macOS 4 | // 5 | // Created by khoa on 12/09/2019. 6 | // Copyright © 2019 Khoa Pham. All rights reserved. 7 | // 8 | 9 | #if os(OSX) && !targetEnvironment(macCatalyst) 10 | 11 | import AppKit 12 | 13 | public extension NSImage { 14 | func tint(color: NSColor) -> NSImage { 15 | let image = self.copy() as! NSImage 16 | image.lockFocus() 17 | 18 | color.set() 19 | let imageRect = NSRect(origin: .zero, size: image.size) 20 | imageRect.fill(using: .sourceAtop) 21 | 22 | image.unlockFocus() 23 | 24 | return image 25 | } 26 | 27 | func resize( 28 | targetSize: CGSize, 29 | imageInterpolation: NSImageInterpolation = .medium 30 | ) -> NSImage { 31 | let scaledSize = self.scaledSize(targetSize: targetSize) 32 | let img = NSImage(size: scaledSize) 33 | img.lockFocus() 34 | NSGraphicsContext.current?.imageInterpolation = imageInterpolation 35 | draw( 36 | in: NSRect(origin: .zero, size: scaledSize), 37 | from: NSRect(origin: .zero, size: size), 38 | operation: .copy, 39 | fraction: 1 40 | ) 41 | img.unlockFocus() 42 | return img 43 | } 44 | } 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /Sources/macOS/NSTextView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSTextView.swift 3 | // Omnia-macOS 4 | // 5 | // Created by khoa on 20/09/2019. 6 | // Copyright © 2019 Khoa Pham. All rights reserved. 7 | // 8 | 9 | #if os(OSX) && !targetEnvironment(macCatalyst) 10 | 11 | import AppKit 12 | 13 | public extension NSTextView { 14 | func styleAsLabel(color: NSColor, font: NSFont) { 15 | textColor = color 16 | self.font = font 17 | isEditable = false 18 | backgroundColor = NSColor.clear 19 | } 20 | } 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /Sources/macOS/NSView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSView+Extensions.swift 3 | // Omnia-macOS 4 | // 5 | // Created by Khoa Pham on 21.12.2017. 6 | // Copyright © 2017 Khoa Pham. All rights reserved. 7 | // 8 | 9 | #if os(OSX) && !targetEnvironment(macCatalyst) 10 | 11 | import AppKit 12 | 13 | public extension NSView { 14 | /// Add many subviews 15 | /// 16 | /// - Parameter views: The subviews 17 | func addSubviews(_ views: [NSView]) { 18 | views.forEach { 19 | addSubview($0) 20 | } 21 | } 22 | 23 | func shake() { 24 | let midX = layer?.position.x ?? 0 25 | let midY = layer?.position.y ?? 0 26 | 27 | let animation = CABasicAnimation(keyPath: "position") 28 | animation.duration = 0.06 29 | animation.repeatCount = 4 30 | animation.autoreverses = true 31 | animation.fromValue = CGPoint(x: midX - 10, y: midY) 32 | animation.toValue = CGPoint(x: midX + 10, y: midY) 33 | layer?.add(animation, forKey: "position") 34 | } 35 | 36 | static var itemId: NSUserInterfaceItemIdentifier { 37 | return NSUserInterfaceItemIdentifier(rawValue: String(describing: self)) 38 | } 39 | 40 | func scaleDown(scale: CGFloat = 0.95) { 41 | wantsLayer = true 42 | let frame = layer!.frame 43 | layer?.anchorPoint = CGPoint(x: 0.5, y: 0.5) 44 | layer?.frame = frame 45 | 46 | let animation = CABasicAnimation(keyPath: "transform") 47 | animation.duration = 0.25 48 | animation.repeatCount = 1 49 | animation.autoreverses = false 50 | animation.fromValue = CATransform3DMakeScale(1.0, 1.0, 1.0) 51 | animation.toValue = CATransform3DMakeScale(scale, scale, 1.0) 52 | layer?.add(animation, forKey: "scale") 53 | } 54 | } 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /Sources/macOS/Process.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Process.swift 3 | // Omnia 4 | // 5 | // Created by khoa on 21/03/2021. 6 | // 7 | 8 | import Foundation 9 | 10 | #if os(OSX) 11 | public extension Process { 12 | @discardableResult 13 | func launchBash( 14 | with command: String, 15 | onOutput: ((Data) -> Void)? = nil, 16 | onError: ((Data) -> Void)? = nil 17 | ) throws -> String { 18 | launchPath = "/bin/bash" 19 | arguments = ["-c", command] 20 | 21 | let outputQueue = DispatchQueue(label: "Process.OutputQueue") 22 | 23 | var outputData = Data() 24 | var errorData = Data() 25 | 26 | let outputPipe = Pipe() 27 | standardOutput = outputPipe 28 | 29 | let errorPipe = Pipe() 30 | standardError = errorPipe 31 | 32 | outputPipe.fileHandleForReading.readabilityHandler = { handler in 33 | outputQueue.async { 34 | let data = handler.availableData 35 | outputData.append(data) 36 | onOutput?(data) 37 | } 38 | } 39 | 40 | errorPipe.fileHandleForReading.readabilityHandler = { handler in 41 | outputQueue.async { 42 | let data = handler.availableData 43 | errorData.append(data) 44 | onError?(data) 45 | } 46 | } 47 | 48 | launch() 49 | waitUntilExit() 50 | 51 | outputPipe.fileHandleForReading.readabilityHandler = nil 52 | errorPipe.fileHandleForReading.readabilityHandler = nil 53 | 54 | return try outputQueue.sync { 55 | if terminationStatus != 0 { 56 | let errorString = String(data: errorData, encoding: .utf8) ?? "" 57 | if !errorString.isEmpty { 58 | throw NSError(domain: "", code: 0, userInfo: [ 59 | NSLocalizedDescriptionKey: errorString 60 | ]) 61 | } 62 | } 63 | 64 | return outputData.shellOutput() 65 | } 66 | } 67 | } 68 | 69 | private extension Data { 70 | func shellOutput() -> String { 71 | guard let output = String(data: self, encoding: .utf8) else { 72 | return "" 73 | } 74 | 75 | guard !output.hasSuffix("\n") else { 76 | let endIndex = output.index(before: output.endIndex) 77 | return String(output[.. [XCTestCaseEntry] { 5 | return [ 6 | testCase(OmniaTests.allTests), 7 | ] 8 | } 9 | #endif 10 | -------------------------------------------------------------------------------- /Tests/OmniaTests/iOS/NSLayoutConstraintTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSLayoutConstraint+ExtensionsTests.swift 3 | // Omnia-iOS-Tests 4 | // 5 | // Created by khoa on 11/04/2019. 6 | // Copyright © 2019 Khoa Pham. All rights reserved. 7 | // 8 | 9 | #if canImport(UIKit) 10 | 11 | import XCTest 12 | 13 | class NSLayoutConstraintTests: XCTestCase { 14 | 15 | override func setUp() { 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | } 22 | 23 | func testExample() { 24 | // This is an example of a functional test case. 25 | // Use XCTAssert and related functions to verify your tests produce the correct results. 26 | } 27 | 28 | func testPerformanceExample() { 29 | // This is an example of a performance test case. 30 | self.measure { 31 | // Put the code you want to measure the time of here. 32 | } 33 | } 34 | 35 | } 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /Tests/OmniaTests/iOS/UIColorTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Omnia 3 | // 4 | // Created by khoa on 11/04/2019. 5 | // Copyright © 2019 Khoa Pham. All rights reserved. 6 | // 7 | 8 | #if canImport(UIKit) 9 | 10 | import XCTest 11 | import Omnia 12 | 13 | class UIColorTests: XCTestCase { 14 | func testDarken() { 15 | let _ = UIColor.red.darken() 16 | } 17 | 18 | func testLighten() { 19 | let _ = UIColor.red.lighten() 20 | } 21 | } 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /Tests/OmniaTests/iOS/UIImageTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Omnia 3 | // 4 | // Created by khoa on 11/04/2019. 5 | // Copyright © 2019 Khoa Pham. All rights reserved. 6 | // 7 | 8 | #if canImport(UIKit) 9 | 10 | import XCTest 11 | import Omnia 12 | 13 | class UIImagExtensionsTests: XCTestCase { 14 | 15 | func testInitFromColor() { 16 | let image = UIImage.from(color: .red, size: CGSize(width: 20, height: 20)) 17 | XCTAssertEqual(image.size, CGSize(width: 20, height: 20)) 18 | } 19 | } 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /Tests/OmniaTests/iOS/UIViewControllerTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Omnia 3 | // 4 | // Created by khoa on 11/04/2019. 5 | // Copyright © 2019 Khoa Pham. All rights reserved. 6 | // 7 | 8 | #if canImport(UIKit) 9 | 10 | import XCTest 11 | import Omnia 12 | 13 | class UIViewControllerTests: XCTestCase { 14 | 15 | func testInstall() { 16 | let parent = UIViewController() 17 | let child = UIViewController() 18 | parent.install(childController: child) 19 | 20 | XCTAssertEqual(parent.children.first, child) 21 | } 22 | 23 | func testUninstall() { 24 | let parent = UIViewController() 25 | let child = UIViewController() 26 | parent.install(childController: child) 27 | child.uninstall() 28 | 29 | XCTAssertTrue(parent.children.isEmpty) 30 | } 31 | } 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /Tests/OmniaTests/iOS/UIViewTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Omnia 3 | // 4 | // Created by khoa on 11/04/2019. 5 | // Copyright © 2019 Khoa Pham. All rights reserved. 6 | // 7 | 8 | #if canImport(UIKit) 9 | 10 | import XCTest 11 | import Omnia 12 | 13 | class UIViewTests: XCTestCase { 14 | 15 | func testToImage() { 16 | let button = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) 17 | button.setTitle("Hello", for: .normal) 18 | button.setTitleColor(.red, for: .normal) 19 | 20 | let image = button.toImage() 21 | 22 | XCTAssertEqual(image?.size, CGSize(width: 100, height: 100)) 23 | } 24 | } 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /Tests/OmniaTests/macOS/macOSTests.swift: -------------------------------------------------------------------------------- 1 | #if canImport(AppKit) && !targetEnvironment(macCatalyst) 2 | 3 | import XCTest 4 | 5 | class macOSTests: XCTestCase { 6 | 7 | override func setUp() { 8 | super.setUp() 9 | // Put setup code here. This method is called before the invocation of each test method in the class. 10 | } 11 | 12 | override func tearDown() { 13 | // Put teardown code here. This method is called after the invocation of each test method in the class. 14 | super.tearDown() 15 | } 16 | 17 | func testExample() { 18 | // This is an example of a functional test case. 19 | // Use XCTAssert and related functions to verify your tests produce the correct results. 20 | } 21 | 22 | func testPerformanceExample() { 23 | // This is an example of a performance test case. 24 | self.measure { 25 | // Put the code you want to measure the time of here. 26 | } 27 | } 28 | } 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /Tests/OmniaTests/tvOS/tvOSTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | 3 | class tvOSTests: XCTestCase { 4 | 5 | override func setUp() { 6 | super.setUp() 7 | // Put setup code here. This method is called before the invocation of each test method in the class. 8 | } 9 | 10 | override func tearDown() { 11 | // Put teardown code here. This method is called after the invocation of each test method in the class. 12 | super.tearDown() 13 | } 14 | 15 | func testExample() { 16 | // This is an example of a functional test case. 17 | // Use XCTAssert and related functions to verify your tests produce the correct results. 18 | } 19 | 20 | func testPerformanceExample() { 21 | // This is an example of a performance test case. 22 | self.measure { 23 | // Put the code you want to measure the time of here. 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /docs/Extensions/Error.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Error Extension Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

Omnia Docs (43% documented)

18 |
19 |
20 |
21 | 26 |
27 |
28 | 150 |
151 |
152 |
153 |

Error

154 |
155 |
156 |
public extension Error
157 | 158 |
159 |
160 | 161 |
162 |
163 |
164 |
    165 |
  • 166 |
    167 | 168 | 169 | 170 | toString() 171 | 172 |
    173 |
    174 |
    175 |
    176 |
    177 |
    178 |

    Undocumented

    179 | 180 |
    181 |
    182 |

    Declaration

    183 |
    184 |

    Swift

    185 |
    func toString() -> String
    186 | 187 |
    188 |
    189 |
    190 |
    191 |
  • 192 |
193 |
194 |
195 |
196 | 200 |
201 |
202 | 203 | 204 | 205 | -------------------------------------------------------------------------------- /docs/Extensions/Sequence.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Sequence Extension Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

Omnia Docs (43% documented)

18 |
19 |
20 |
21 | 26 |
27 |
28 | 150 |
151 |
152 |
153 |

Sequence

154 |
155 |
156 |
extension Sequence
157 | 158 |
159 |
160 | 161 |
162 |
163 |
164 |
    165 |
  • 166 |
    167 | 168 | 169 | 170 | shuffled() 171 | 172 |
    173 |
    174 |
    175 |
    176 |
    177 |
    178 |

    Shuffle sequence by creating a new array

    179 | 180 |
    181 |
    182 |

    Declaration

    183 |
    184 |

    Swift

    185 |
    public func shuffled() -> [Element]
    186 | 187 |
    188 |
    189 |
    190 |

    Return Value

    191 |

    Shuffled array

    192 |
    193 |
    194 |
    195 |
  • 196 |
197 |
198 |
199 |
200 | 204 |
205 |
206 | 207 | 208 | 209 | -------------------------------------------------------------------------------- /docs/Extensions/UIFont.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | UIFont Extension Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

Omnia Docs (43% documented)

18 |
19 |
20 |
21 | 26 |
27 |
28 | 150 |
151 |
152 |
153 |

UIFont

154 |
155 |
156 |
public extension UIFont
157 | 158 |
159 |
160 | 161 |
162 |
163 |
164 |
    165 |
  • 166 |
    167 | 168 | 169 | 170 | registerCustom(fileName:fileExtension:bundle:) 171 | 172 |
    173 |
    174 |
    175 |
    176 |
    177 |
    178 |

    Undocumented

    179 | 180 |
    181 |
    182 |

    Declaration

    183 |
    184 |

    Swift

    185 |
    static func registerCustom(fileName: String, fileExtension: String, bundle: Bundle = .main)
    186 | 187 |
    188 |
    189 |
    190 |
    191 |
  • 192 |
193 |
194 |
195 |
196 | 200 |
201 |
202 | 203 | 204 | 205 | -------------------------------------------------------------------------------- /docs/Extensions/UIScrollView.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | UIScrollView Extension Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

Omnia Docs (43% documented)

18 |
19 |
20 |
21 | 26 |
27 |
28 | 150 |
151 |
152 |
153 |

UIScrollView

154 |
155 |
156 |
extension UIScrollView
157 | 158 |
159 |
160 | 161 |
162 |
163 |
164 |
    165 |
  • 166 |
    167 | 168 | 169 | 170 | scrollToBottom() 171 | 172 |
    173 |
    174 |
    175 |
    176 |
    177 |
    178 |

    Scroll to bottom

    179 | 180 |
    181 |
    182 |

    Declaration

    183 |
    184 |

    Swift

    185 |
    public func scrollToBottom()
    186 | 187 |
    188 |
    189 |
    190 |
    191 |
  • 192 |
193 |
194 |
195 |
196 | 200 |
201 |
202 | 203 | 204 | 205 | -------------------------------------------------------------------------------- /docs/Structs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Structures Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

Omnia Docs (43% documented)

18 |
19 |
20 |
21 | 26 |
27 |
28 | 150 |
151 |
152 |
153 |

Structures

154 |

The following structures are available globally.

155 | 156 |
157 |
158 |
159 |
    160 |
  • 161 |
    162 | 163 | 164 | 165 | HSLColor 166 | 167 |
    168 |
    169 |
    170 |
    171 |
    172 |
    173 |

    Represent a HSL color

    174 | 175 | See more 176 |
    177 |
    178 |

    Declaration

    179 |
    180 |

    Swift

    181 |
    public struct HSLColor
    182 | 183 |
    184 |
    185 |
    186 |
    187 |
  • 188 |
189 |
190 |
191 |
192 | 196 |
197 |
198 | 199 | 200 | 201 | -------------------------------------------------------------------------------- /docs/badge.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | documentation 17 | 18 | 19 | documentation 20 | 21 | 22 | 43% 23 | 24 | 25 | 43% 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /docs/css/highlight.css: -------------------------------------------------------------------------------- 1 | /* Credit to https://gist.github.com/wataru420/2048287 */ 2 | .highlight { 3 | /* Comment */ 4 | /* Error */ 5 | /* Keyword */ 6 | /* Operator */ 7 | /* Comment.Multiline */ 8 | /* Comment.Preproc */ 9 | /* Comment.Single */ 10 | /* Comment.Special */ 11 | /* Generic.Deleted */ 12 | /* Generic.Deleted.Specific */ 13 | /* Generic.Emph */ 14 | /* Generic.Error */ 15 | /* Generic.Heading */ 16 | /* Generic.Inserted */ 17 | /* Generic.Inserted.Specific */ 18 | /* Generic.Output */ 19 | /* Generic.Prompt */ 20 | /* Generic.Strong */ 21 | /* Generic.Subheading */ 22 | /* Generic.Traceback */ 23 | /* Keyword.Constant */ 24 | /* Keyword.Declaration */ 25 | /* Keyword.Pseudo */ 26 | /* Keyword.Reserved */ 27 | /* Keyword.Type */ 28 | /* Literal.Number */ 29 | /* Literal.String */ 30 | /* Name.Attribute */ 31 | /* Name.Builtin */ 32 | /* Name.Class */ 33 | /* Name.Constant */ 34 | /* Name.Entity */ 35 | /* Name.Exception */ 36 | /* Name.Function */ 37 | /* Name.Namespace */ 38 | /* Name.Tag */ 39 | /* Name.Variable */ 40 | /* Operator.Word */ 41 | /* Text.Whitespace */ 42 | /* Literal.Number.Float */ 43 | /* Literal.Number.Hex */ 44 | /* Literal.Number.Integer */ 45 | /* Literal.Number.Oct */ 46 | /* Literal.String.Backtick */ 47 | /* Literal.String.Char */ 48 | /* Literal.String.Doc */ 49 | /* Literal.String.Double */ 50 | /* Literal.String.Escape */ 51 | /* Literal.String.Heredoc */ 52 | /* Literal.String.Interpol */ 53 | /* Literal.String.Other */ 54 | /* Literal.String.Regex */ 55 | /* Literal.String.Single */ 56 | /* Literal.String.Symbol */ 57 | /* Name.Builtin.Pseudo */ 58 | /* Name.Variable.Class */ 59 | /* Name.Variable.Global */ 60 | /* Name.Variable.Instance */ 61 | /* Literal.Number.Integer.Long */ } 62 | .highlight .c { 63 | color: #999988; 64 | font-style: italic; } 65 | .highlight .err { 66 | color: #a61717; 67 | background-color: #e3d2d2; } 68 | .highlight .k { 69 | color: #000000; 70 | font-weight: bold; } 71 | .highlight .o { 72 | color: #000000; 73 | font-weight: bold; } 74 | .highlight .cm { 75 | color: #999988; 76 | font-style: italic; } 77 | .highlight .cp { 78 | color: #999999; 79 | font-weight: bold; } 80 | .highlight .c1 { 81 | color: #999988; 82 | font-style: italic; } 83 | .highlight .cs { 84 | color: #999999; 85 | font-weight: bold; 86 | font-style: italic; } 87 | .highlight .gd { 88 | color: #000000; 89 | background-color: #ffdddd; } 90 | .highlight .gd .x { 91 | color: #000000; 92 | background-color: #ffaaaa; } 93 | .highlight .ge { 94 | color: #000000; 95 | font-style: italic; } 96 | .highlight .gr { 97 | color: #aa0000; } 98 | .highlight .gh { 99 | color: #999999; } 100 | .highlight .gi { 101 | color: #000000; 102 | background-color: #ddffdd; } 103 | .highlight .gi .x { 104 | color: #000000; 105 | background-color: #aaffaa; } 106 | .highlight .go { 107 | color: #888888; } 108 | .highlight .gp { 109 | color: #555555; } 110 | .highlight .gs { 111 | font-weight: bold; } 112 | .highlight .gu { 113 | color: #aaaaaa; } 114 | .highlight .gt { 115 | color: #aa0000; } 116 | .highlight .kc { 117 | color: #000000; 118 | font-weight: bold; } 119 | .highlight .kd { 120 | color: #000000; 121 | font-weight: bold; } 122 | .highlight .kp { 123 | color: #000000; 124 | font-weight: bold; } 125 | .highlight .kr { 126 | color: #000000; 127 | font-weight: bold; } 128 | .highlight .kt { 129 | color: #445588; } 130 | .highlight .m { 131 | color: #009999; } 132 | .highlight .s { 133 | color: #d14; } 134 | .highlight .na { 135 | color: #008080; } 136 | .highlight .nb { 137 | color: #0086B3; } 138 | .highlight .nc { 139 | color: #445588; 140 | font-weight: bold; } 141 | .highlight .no { 142 | color: #008080; } 143 | .highlight .ni { 144 | color: #800080; } 145 | .highlight .ne { 146 | color: #990000; 147 | font-weight: bold; } 148 | .highlight .nf { 149 | color: #990000; } 150 | .highlight .nn { 151 | color: #555555; } 152 | .highlight .nt { 153 | color: #000080; } 154 | .highlight .nv { 155 | color: #008080; } 156 | .highlight .ow { 157 | color: #000000; 158 | font-weight: bold; } 159 | .highlight .w { 160 | color: #bbbbbb; } 161 | .highlight .mf { 162 | color: #009999; } 163 | .highlight .mh { 164 | color: #009999; } 165 | .highlight .mi { 166 | color: #009999; } 167 | .highlight .mo { 168 | color: #009999; } 169 | .highlight .sb { 170 | color: #d14; } 171 | .highlight .sc { 172 | color: #d14; } 173 | .highlight .sd { 174 | color: #d14; } 175 | .highlight .s2 { 176 | color: #d14; } 177 | .highlight .se { 178 | color: #d14; } 179 | .highlight .sh { 180 | color: #d14; } 181 | .highlight .si { 182 | color: #d14; } 183 | .highlight .sx { 184 | color: #d14; } 185 | .highlight .sr { 186 | color: #009926; } 187 | .highlight .s1 { 188 | color: #d14; } 189 | .highlight .ss { 190 | color: #990073; } 191 | .highlight .bp { 192 | color: #999999; } 193 | .highlight .vc { 194 | color: #008080; } 195 | .highlight .vg { 196 | color: #008080; } 197 | .highlight .vi { 198 | color: #008080; } 199 | .highlight .il { 200 | color: #009999; } 201 | -------------------------------------------------------------------------------- /docs/css/jazzy.css: -------------------------------------------------------------------------------- 1 | html, body, div, span, h1, h3, h4, p, a, code, em, img, ul, li, table, tbody, tr, td { 2 | background: transparent; 3 | border: 0; 4 | margin: 0; 5 | outline: 0; 6 | padding: 0; 7 | vertical-align: baseline; } 8 | 9 | body { 10 | background-color: #f2f2f2; 11 | font-family: Helvetica, freesans, Arial, sans-serif; 12 | font-size: 14px; 13 | -webkit-font-smoothing: subpixel-antialiased; 14 | word-wrap: break-word; } 15 | 16 | h1, h2, h3 { 17 | margin-top: 0.8em; 18 | margin-bottom: 0.3em; 19 | font-weight: 100; 20 | color: black; } 21 | 22 | h1 { 23 | font-size: 2.5em; } 24 | 25 | h2 { 26 | font-size: 2em; 27 | border-bottom: 1px solid #e2e2e2; } 28 | 29 | h4 { 30 | font-size: 13px; 31 | line-height: 1.5; 32 | margin-top: 21px; } 33 | 34 | h5 { 35 | font-size: 1.1em; } 36 | 37 | h6 { 38 | font-size: 1.1em; 39 | color: #777; } 40 | 41 | .section-name { 42 | color: gray; 43 | display: block; 44 | font-family: Helvetica; 45 | font-size: 22px; 46 | font-weight: 100; 47 | margin-bottom: 15px; } 48 | 49 | pre, code { 50 | font: 0.95em Menlo, monospace; 51 | color: #777; 52 | word-wrap: normal; } 53 | 54 | p code, li code { 55 | background-color: #eee; 56 | padding: 2px 4px; 57 | border-radius: 4px; } 58 | 59 | a { 60 | color: #0088cc; 61 | text-decoration: none; } 62 | 63 | ul { 64 | padding-left: 15px; } 65 | 66 | li { 67 | line-height: 1.8em; } 68 | 69 | img { 70 | max-width: 100%; } 71 | 72 | blockquote { 73 | margin-left: 0; 74 | padding: 0 10px; 75 | border-left: 4px solid #ccc; } 76 | 77 | .content-wrapper { 78 | margin: 0 auto; 79 | width: 980px; } 80 | 81 | header { 82 | font-size: 0.85em; 83 | line-height: 26px; 84 | background-color: #414141; 85 | position: fixed; 86 | width: 100%; 87 | z-index: 1; } 88 | header img { 89 | padding-right: 6px; 90 | vertical-align: -4px; 91 | height: 16px; } 92 | header a { 93 | color: #fff; } 94 | header p { 95 | float: left; 96 | color: #999; } 97 | header .header-right { 98 | float: right; 99 | margin-left: 16px; } 100 | 101 | #breadcrumbs { 102 | background-color: #f2f2f2; 103 | height: 27px; 104 | padding-top: 17px; 105 | position: fixed; 106 | width: 100%; 107 | z-index: 1; 108 | margin-top: 26px; } 109 | #breadcrumbs #carat { 110 | height: 10px; 111 | margin: 0 5px; } 112 | 113 | .sidebar { 114 | background-color: #f9f9f9; 115 | border: 1px solid #e2e2e2; 116 | overflow-y: auto; 117 | overflow-x: hidden; 118 | position: fixed; 119 | top: 70px; 120 | bottom: 0; 121 | width: 230px; 122 | word-wrap: normal; } 123 | 124 | .nav-groups { 125 | list-style-type: none; 126 | background: #fff; 127 | padding-left: 0; } 128 | 129 | .nav-group-name { 130 | border-bottom: 1px solid #e2e2e2; 131 | font-size: 1.1em; 132 | font-weight: 100; 133 | padding: 15px 0 15px 20px; } 134 | .nav-group-name > a { 135 | color: #333; } 136 | 137 | .nav-group-tasks { 138 | margin-top: 5px; } 139 | 140 | .nav-group-task { 141 | font-size: 0.9em; 142 | list-style-type: none; 143 | white-space: nowrap; } 144 | .nav-group-task a { 145 | color: #888; } 146 | 147 | .main-content { 148 | background-color: #fff; 149 | border: 1px solid #e2e2e2; 150 | margin-left: 246px; 151 | position: absolute; 152 | overflow: hidden; 153 | padding-bottom: 20px; 154 | top: 70px; 155 | width: 734px; } 156 | .main-content p, .main-content a, .main-content code, .main-content em, .main-content ul, .main-content table, .main-content blockquote { 157 | margin-bottom: 1em; } 158 | .main-content p { 159 | line-height: 1.8em; } 160 | .main-content section .section:first-child { 161 | margin-top: 0; 162 | padding-top: 0; } 163 | .main-content section .task-group-section .task-group:first-of-type { 164 | padding-top: 10px; } 165 | .main-content section .task-group-section .task-group:first-of-type .section-name { 166 | padding-top: 15px; } 167 | .main-content section .heading:before { 168 | content: ""; 169 | display: block; 170 | padding-top: 70px; 171 | margin: -70px 0 0; } 172 | 173 | .section { 174 | padding: 0 25px; } 175 | 176 | .highlight { 177 | background-color: #eee; 178 | padding: 10px 12px; 179 | border: 1px solid #e2e2e2; 180 | border-radius: 4px; 181 | overflow-x: auto; } 182 | 183 | .declaration .highlight { 184 | overflow-x: initial; 185 | padding: 0 40px 40px 0; 186 | margin-bottom: -25px; 187 | background-color: transparent; 188 | border: none; } 189 | 190 | .section-name { 191 | margin: 0; 192 | margin-left: 18px; } 193 | 194 | .task-group-section { 195 | padding-left: 6px; 196 | border-top: 1px solid #e2e2e2; } 197 | 198 | .task-group { 199 | padding-top: 0px; } 200 | 201 | .task-name-container a[name]:before { 202 | content: ""; 203 | display: block; 204 | padding-top: 70px; 205 | margin: -70px 0 0; } 206 | 207 | .item { 208 | padding-top: 8px; 209 | width: 100%; 210 | list-style-type: none; } 211 | .item a[name]:before { 212 | content: ""; 213 | display: block; 214 | padding-top: 70px; 215 | margin: -70px 0 0; } 216 | .item code { 217 | background-color: transparent; 218 | padding: 0; } 219 | .item .token, .item .direct-link { 220 | padding-left: 3px; 221 | margin-left: 15px; 222 | font-size: 11.9px; 223 | transition: all 300ms; } 224 | .item .token-open { 225 | margin-left: 0px; } 226 | .item .discouraged { 227 | text-decoration: line-through; } 228 | .item .declaration-note { 229 | font-size: .85em; 230 | color: gray; 231 | font-style: italic; } 232 | 233 | .pointer-container { 234 | border-bottom: 1px solid #e2e2e2; 235 | left: -23px; 236 | padding-bottom: 13px; 237 | position: relative; 238 | width: 110%; } 239 | 240 | .pointer { 241 | background: #f9f9f9; 242 | border-left: 1px solid #e2e2e2; 243 | border-top: 1px solid #e2e2e2; 244 | height: 12px; 245 | left: 21px; 246 | top: -7px; 247 | -webkit-transform: rotate(45deg); 248 | -moz-transform: rotate(45deg); 249 | -o-transform: rotate(45deg); 250 | transform: rotate(45deg); 251 | position: absolute; 252 | width: 12px; } 253 | 254 | .height-container { 255 | display: none; 256 | left: -25px; 257 | padding: 0 25px; 258 | position: relative; 259 | width: 100%; 260 | overflow: hidden; } 261 | .height-container .section { 262 | background: #f9f9f9; 263 | border-bottom: 1px solid #e2e2e2; 264 | left: -25px; 265 | position: relative; 266 | width: 100%; 267 | padding-top: 10px; 268 | padding-bottom: 5px; } 269 | 270 | .aside, .language { 271 | padding: 6px 12px; 272 | margin: 12px 0; 273 | border-left: 5px solid #dddddd; 274 | overflow-y: hidden; } 275 | .aside .aside-title, .language .aside-title { 276 | font-size: 9px; 277 | letter-spacing: 2px; 278 | text-transform: uppercase; 279 | padding-bottom: 0; 280 | margin: 0; 281 | color: #aaa; 282 | -webkit-user-select: none; } 283 | .aside p:last-child, .language p:last-child { 284 | margin-bottom: 0; } 285 | 286 | .language { 287 | border-left: 5px solid #cde9f4; } 288 | .language .aside-title { 289 | color: #4b8afb; } 290 | 291 | .aside-warning, .aside-deprecated, .aside-unavailable { 292 | border-left: 5px solid #ff6666; } 293 | .aside-warning .aside-title, .aside-deprecated .aside-title, .aside-unavailable .aside-title { 294 | color: #ff0000; } 295 | 296 | .graybox { 297 | border-collapse: collapse; 298 | width: 100%; } 299 | .graybox p { 300 | margin: 0; 301 | word-break: break-word; 302 | min-width: 50px; } 303 | .graybox td { 304 | border: 1px solid #e2e2e2; 305 | padding: 5px 25px 5px 10px; 306 | vertical-align: middle; } 307 | .graybox tr td:first-of-type { 308 | text-align: right; 309 | padding: 7px; 310 | vertical-align: top; 311 | word-break: normal; 312 | width: 40px; } 313 | 314 | .slightly-smaller { 315 | font-size: 0.9em; } 316 | 317 | #footer { 318 | position: relative; 319 | top: 10px; 320 | bottom: 0px; 321 | margin-left: 25px; } 322 | #footer p { 323 | margin: 0; 324 | color: #aaa; 325 | font-size: 0.8em; } 326 | 327 | html.dash header, html.dash #breadcrumbs, html.dash .sidebar { 328 | display: none; } 329 | 330 | html.dash .main-content { 331 | width: 980px; 332 | margin-left: 0; 333 | border: none; 334 | width: 100%; 335 | top: 0; 336 | padding-bottom: 0; } 337 | 338 | html.dash .height-container { 339 | display: block; } 340 | 341 | html.dash .item .token { 342 | margin-left: 0; } 343 | 344 | html.dash .content-wrapper { 345 | width: auto; } 346 | 347 | html.dash #footer { 348 | position: static; } 349 | -------------------------------------------------------------------------------- /docs/docsets/Omnia.docset/Contents/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleIdentifier 6 | com.jazzy.omnia 7 | CFBundleName 8 | Omnia 9 | DocSetPlatformFamily 10 | omnia 11 | isDashDocset 12 | 13 | dashIndexFilePath 14 | index.html 15 | isJavaScriptEnabled 16 | 17 | DashDocSetFamily 18 | dashtoc 19 | 20 | 21 | -------------------------------------------------------------------------------- /docs/docsets/Omnia.docset/Contents/Resources/Documents/Extensions/Error.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Error Extension Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

Omnia Docs (43% documented)

18 |
19 |
20 |
21 | 26 |
27 |
28 | 150 |
151 |
152 |
153 |

Error

154 |
155 |
156 |
public extension Error
157 | 158 |
159 |
160 | 161 |
162 |
163 |
164 |
    165 |
  • 166 |
    167 | 168 | 169 | 170 | toString() 171 | 172 |
    173 |
    174 |
    175 |
    176 |
    177 |
    178 |

    Undocumented

    179 | 180 |
    181 |
    182 |

    Declaration

    183 |
    184 |

    Swift

    185 |
    func toString() -> String
    186 | 187 |
    188 |
    189 |
    190 |
    191 |
  • 192 |
193 |
194 |
195 |
196 | 200 |
201 |
202 | 203 | 204 | 205 | -------------------------------------------------------------------------------- /docs/docsets/Omnia.docset/Contents/Resources/Documents/Extensions/Sequence.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Sequence Extension Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

Omnia Docs (43% documented)

18 |
19 |
20 |
21 | 26 |
27 |
28 | 150 |
151 |
152 |
153 |

Sequence

154 |
155 |
156 |
extension Sequence
157 | 158 |
159 |
160 | 161 |
162 |
163 |
164 |
    165 |
  • 166 |
    167 | 168 | 169 | 170 | shuffled() 171 | 172 |
    173 |
    174 |
    175 |
    176 |
    177 |
    178 |

    Shuffle sequence by creating a new array

    179 | 180 |
    181 |
    182 |

    Declaration

    183 |
    184 |

    Swift

    185 |
    public func shuffled() -> [Element]
    186 | 187 |
    188 |
    189 |
    190 |

    Return Value

    191 |

    Shuffled array

    192 |
    193 |
    194 |
    195 |
  • 196 |
197 |
198 |
199 |
200 | 204 |
205 |
206 | 207 | 208 | 209 | -------------------------------------------------------------------------------- /docs/docsets/Omnia.docset/Contents/Resources/Documents/Extensions/UIScrollView.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | UIScrollView Extension Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

Omnia Docs (43% documented)

18 |
19 |
20 |
21 | 26 |
27 |
28 | 150 |
151 |
152 |
153 |

UIScrollView

154 |
155 |
156 |
extension UIScrollView
157 | 158 |
159 |
160 | 161 |
162 |
163 |
164 |
    165 |
  • 166 |
    167 | 168 | 169 | 170 | scrollToBottom() 171 | 172 |
    173 |
    174 |
    175 |
    176 |
    177 |
    178 |

    Scroll to bottom

    179 | 180 |
    181 |
    182 |

    Declaration

    183 |
    184 |

    Swift

    185 |
    public func scrollToBottom()
    186 | 187 |
    188 |
    189 |
    190 |
    191 |
  • 192 |
193 |
194 |
195 |
196 | 200 |
201 |
202 | 203 | 204 | 205 | -------------------------------------------------------------------------------- /docs/docsets/Omnia.docset/Contents/Resources/Documents/Structs.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Structures Reference 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |

Omnia Docs (43% documented)

18 |
19 |
20 |
21 | 26 |
27 |
28 | 150 |
151 |
152 |
153 |

Structures

154 |

The following structures are available globally.

155 | 156 |
157 |
158 |
159 |
    160 |
  • 161 |
    162 | 163 | 164 | 165 | HSLColor 166 | 167 |
    168 |
    169 |
    170 |
    171 |
    172 |
    173 |

    Represent a HSL color

    174 | 175 | See more 176 |
    177 |
    178 |

    Declaration

    179 |
    180 |

    Swift

    181 |
    public struct HSLColor
    182 | 183 |
    184 |
    185 |
    186 |
    187 |
  • 188 |
189 |
190 |
191 |
192 | 196 |
197 |
198 | 199 | 200 | 201 | -------------------------------------------------------------------------------- /docs/docsets/Omnia.docset/Contents/Resources/Documents/css/highlight.css: -------------------------------------------------------------------------------- 1 | /* Credit to https://gist.github.com/wataru420/2048287 */ 2 | .highlight { 3 | /* Comment */ 4 | /* Error */ 5 | /* Keyword */ 6 | /* Operator */ 7 | /* Comment.Multiline */ 8 | /* Comment.Preproc */ 9 | /* Comment.Single */ 10 | /* Comment.Special */ 11 | /* Generic.Deleted */ 12 | /* Generic.Deleted.Specific */ 13 | /* Generic.Emph */ 14 | /* Generic.Error */ 15 | /* Generic.Heading */ 16 | /* Generic.Inserted */ 17 | /* Generic.Inserted.Specific */ 18 | /* Generic.Output */ 19 | /* Generic.Prompt */ 20 | /* Generic.Strong */ 21 | /* Generic.Subheading */ 22 | /* Generic.Traceback */ 23 | /* Keyword.Constant */ 24 | /* Keyword.Declaration */ 25 | /* Keyword.Pseudo */ 26 | /* Keyword.Reserved */ 27 | /* Keyword.Type */ 28 | /* Literal.Number */ 29 | /* Literal.String */ 30 | /* Name.Attribute */ 31 | /* Name.Builtin */ 32 | /* Name.Class */ 33 | /* Name.Constant */ 34 | /* Name.Entity */ 35 | /* Name.Exception */ 36 | /* Name.Function */ 37 | /* Name.Namespace */ 38 | /* Name.Tag */ 39 | /* Name.Variable */ 40 | /* Operator.Word */ 41 | /* Text.Whitespace */ 42 | /* Literal.Number.Float */ 43 | /* Literal.Number.Hex */ 44 | /* Literal.Number.Integer */ 45 | /* Literal.Number.Oct */ 46 | /* Literal.String.Backtick */ 47 | /* Literal.String.Char */ 48 | /* Literal.String.Doc */ 49 | /* Literal.String.Double */ 50 | /* Literal.String.Escape */ 51 | /* Literal.String.Heredoc */ 52 | /* Literal.String.Interpol */ 53 | /* Literal.String.Other */ 54 | /* Literal.String.Regex */ 55 | /* Literal.String.Single */ 56 | /* Literal.String.Symbol */ 57 | /* Name.Builtin.Pseudo */ 58 | /* Name.Variable.Class */ 59 | /* Name.Variable.Global */ 60 | /* Name.Variable.Instance */ 61 | /* Literal.Number.Integer.Long */ } 62 | .highlight .c { 63 | color: #999988; 64 | font-style: italic; } 65 | .highlight .err { 66 | color: #a61717; 67 | background-color: #e3d2d2; } 68 | .highlight .k { 69 | color: #000000; 70 | font-weight: bold; } 71 | .highlight .o { 72 | color: #000000; 73 | font-weight: bold; } 74 | .highlight .cm { 75 | color: #999988; 76 | font-style: italic; } 77 | .highlight .cp { 78 | color: #999999; 79 | font-weight: bold; } 80 | .highlight .c1 { 81 | color: #999988; 82 | font-style: italic; } 83 | .highlight .cs { 84 | color: #999999; 85 | font-weight: bold; 86 | font-style: italic; } 87 | .highlight .gd { 88 | color: #000000; 89 | background-color: #ffdddd; } 90 | .highlight .gd .x { 91 | color: #000000; 92 | background-color: #ffaaaa; } 93 | .highlight .ge { 94 | color: #000000; 95 | font-style: italic; } 96 | .highlight .gr { 97 | color: #aa0000; } 98 | .highlight .gh { 99 | color: #999999; } 100 | .highlight .gi { 101 | color: #000000; 102 | background-color: #ddffdd; } 103 | .highlight .gi .x { 104 | color: #000000; 105 | background-color: #aaffaa; } 106 | .highlight .go { 107 | color: #888888; } 108 | .highlight .gp { 109 | color: #555555; } 110 | .highlight .gs { 111 | font-weight: bold; } 112 | .highlight .gu { 113 | color: #aaaaaa; } 114 | .highlight .gt { 115 | color: #aa0000; } 116 | .highlight .kc { 117 | color: #000000; 118 | font-weight: bold; } 119 | .highlight .kd { 120 | color: #000000; 121 | font-weight: bold; } 122 | .highlight .kp { 123 | color: #000000; 124 | font-weight: bold; } 125 | .highlight .kr { 126 | color: #000000; 127 | font-weight: bold; } 128 | .highlight .kt { 129 | color: #445588; } 130 | .highlight .m { 131 | color: #009999; } 132 | .highlight .s { 133 | color: #d14; } 134 | .highlight .na { 135 | color: #008080; } 136 | .highlight .nb { 137 | color: #0086B3; } 138 | .highlight .nc { 139 | color: #445588; 140 | font-weight: bold; } 141 | .highlight .no { 142 | color: #008080; } 143 | .highlight .ni { 144 | color: #800080; } 145 | .highlight .ne { 146 | color: #990000; 147 | font-weight: bold; } 148 | .highlight .nf { 149 | color: #990000; } 150 | .highlight .nn { 151 | color: #555555; } 152 | .highlight .nt { 153 | color: #000080; } 154 | .highlight .nv { 155 | color: #008080; } 156 | .highlight .ow { 157 | color: #000000; 158 | font-weight: bold; } 159 | .highlight .w { 160 | color: #bbbbbb; } 161 | .highlight .mf { 162 | color: #009999; } 163 | .highlight .mh { 164 | color: #009999; } 165 | .highlight .mi { 166 | color: #009999; } 167 | .highlight .mo { 168 | color: #009999; } 169 | .highlight .sb { 170 | color: #d14; } 171 | .highlight .sc { 172 | color: #d14; } 173 | .highlight .sd { 174 | color: #d14; } 175 | .highlight .s2 { 176 | color: #d14; } 177 | .highlight .se { 178 | color: #d14; } 179 | .highlight .sh { 180 | color: #d14; } 181 | .highlight .si { 182 | color: #d14; } 183 | .highlight .sx { 184 | color: #d14; } 185 | .highlight .sr { 186 | color: #009926; } 187 | .highlight .s1 { 188 | color: #d14; } 189 | .highlight .ss { 190 | color: #990073; } 191 | .highlight .bp { 192 | color: #999999; } 193 | .highlight .vc { 194 | color: #008080; } 195 | .highlight .vg { 196 | color: #008080; } 197 | .highlight .vi { 198 | color: #008080; } 199 | .highlight .il { 200 | color: #009999; } 201 | -------------------------------------------------------------------------------- /docs/docsets/Omnia.docset/Contents/Resources/Documents/css/jazzy.css: -------------------------------------------------------------------------------- 1 | html, body, div, span, h1, h3, h4, p, a, code, em, img, ul, li, table, tbody, tr, td { 2 | background: transparent; 3 | border: 0; 4 | margin: 0; 5 | outline: 0; 6 | padding: 0; 7 | vertical-align: baseline; } 8 | 9 | body { 10 | background-color: #f2f2f2; 11 | font-family: Helvetica, freesans, Arial, sans-serif; 12 | font-size: 14px; 13 | -webkit-font-smoothing: subpixel-antialiased; 14 | word-wrap: break-word; } 15 | 16 | h1, h2, h3 { 17 | margin-top: 0.8em; 18 | margin-bottom: 0.3em; 19 | font-weight: 100; 20 | color: black; } 21 | 22 | h1 { 23 | font-size: 2.5em; } 24 | 25 | h2 { 26 | font-size: 2em; 27 | border-bottom: 1px solid #e2e2e2; } 28 | 29 | h4 { 30 | font-size: 13px; 31 | line-height: 1.5; 32 | margin-top: 21px; } 33 | 34 | h5 { 35 | font-size: 1.1em; } 36 | 37 | h6 { 38 | font-size: 1.1em; 39 | color: #777; } 40 | 41 | .section-name { 42 | color: gray; 43 | display: block; 44 | font-family: Helvetica; 45 | font-size: 22px; 46 | font-weight: 100; 47 | margin-bottom: 15px; } 48 | 49 | pre, code { 50 | font: 0.95em Menlo, monospace; 51 | color: #777; 52 | word-wrap: normal; } 53 | 54 | p code, li code { 55 | background-color: #eee; 56 | padding: 2px 4px; 57 | border-radius: 4px; } 58 | 59 | a { 60 | color: #0088cc; 61 | text-decoration: none; } 62 | 63 | ul { 64 | padding-left: 15px; } 65 | 66 | li { 67 | line-height: 1.8em; } 68 | 69 | img { 70 | max-width: 100%; } 71 | 72 | blockquote { 73 | margin-left: 0; 74 | padding: 0 10px; 75 | border-left: 4px solid #ccc; } 76 | 77 | .content-wrapper { 78 | margin: 0 auto; 79 | width: 980px; } 80 | 81 | header { 82 | font-size: 0.85em; 83 | line-height: 26px; 84 | background-color: #414141; 85 | position: fixed; 86 | width: 100%; 87 | z-index: 1; } 88 | header img { 89 | padding-right: 6px; 90 | vertical-align: -4px; 91 | height: 16px; } 92 | header a { 93 | color: #fff; } 94 | header p { 95 | float: left; 96 | color: #999; } 97 | header .header-right { 98 | float: right; 99 | margin-left: 16px; } 100 | 101 | #breadcrumbs { 102 | background-color: #f2f2f2; 103 | height: 27px; 104 | padding-top: 17px; 105 | position: fixed; 106 | width: 100%; 107 | z-index: 1; 108 | margin-top: 26px; } 109 | #breadcrumbs #carat { 110 | height: 10px; 111 | margin: 0 5px; } 112 | 113 | .sidebar { 114 | background-color: #f9f9f9; 115 | border: 1px solid #e2e2e2; 116 | overflow-y: auto; 117 | overflow-x: hidden; 118 | position: fixed; 119 | top: 70px; 120 | bottom: 0; 121 | width: 230px; 122 | word-wrap: normal; } 123 | 124 | .nav-groups { 125 | list-style-type: none; 126 | background: #fff; 127 | padding-left: 0; } 128 | 129 | .nav-group-name { 130 | border-bottom: 1px solid #e2e2e2; 131 | font-size: 1.1em; 132 | font-weight: 100; 133 | padding: 15px 0 15px 20px; } 134 | .nav-group-name > a { 135 | color: #333; } 136 | 137 | .nav-group-tasks { 138 | margin-top: 5px; } 139 | 140 | .nav-group-task { 141 | font-size: 0.9em; 142 | list-style-type: none; 143 | white-space: nowrap; } 144 | .nav-group-task a { 145 | color: #888; } 146 | 147 | .main-content { 148 | background-color: #fff; 149 | border: 1px solid #e2e2e2; 150 | margin-left: 246px; 151 | position: absolute; 152 | overflow: hidden; 153 | padding-bottom: 20px; 154 | top: 70px; 155 | width: 734px; } 156 | .main-content p, .main-content a, .main-content code, .main-content em, .main-content ul, .main-content table, .main-content blockquote { 157 | margin-bottom: 1em; } 158 | .main-content p { 159 | line-height: 1.8em; } 160 | .main-content section .section:first-child { 161 | margin-top: 0; 162 | padding-top: 0; } 163 | .main-content section .task-group-section .task-group:first-of-type { 164 | padding-top: 10px; } 165 | .main-content section .task-group-section .task-group:first-of-type .section-name { 166 | padding-top: 15px; } 167 | .main-content section .heading:before { 168 | content: ""; 169 | display: block; 170 | padding-top: 70px; 171 | margin: -70px 0 0; } 172 | 173 | .section { 174 | padding: 0 25px; } 175 | 176 | .highlight { 177 | background-color: #eee; 178 | padding: 10px 12px; 179 | border: 1px solid #e2e2e2; 180 | border-radius: 4px; 181 | overflow-x: auto; } 182 | 183 | .declaration .highlight { 184 | overflow-x: initial; 185 | padding: 0 40px 40px 0; 186 | margin-bottom: -25px; 187 | background-color: transparent; 188 | border: none; } 189 | 190 | .section-name { 191 | margin: 0; 192 | margin-left: 18px; } 193 | 194 | .task-group-section { 195 | padding-left: 6px; 196 | border-top: 1px solid #e2e2e2; } 197 | 198 | .task-group { 199 | padding-top: 0px; } 200 | 201 | .task-name-container a[name]:before { 202 | content: ""; 203 | display: block; 204 | padding-top: 70px; 205 | margin: -70px 0 0; } 206 | 207 | .item { 208 | padding-top: 8px; 209 | width: 100%; 210 | list-style-type: none; } 211 | .item a[name]:before { 212 | content: ""; 213 | display: block; 214 | padding-top: 70px; 215 | margin: -70px 0 0; } 216 | .item code { 217 | background-color: transparent; 218 | padding: 0; } 219 | .item .token, .item .direct-link { 220 | padding-left: 3px; 221 | margin-left: 15px; 222 | font-size: 11.9px; 223 | transition: all 300ms; } 224 | .item .token-open { 225 | margin-left: 0px; } 226 | .item .discouraged { 227 | text-decoration: line-through; } 228 | .item .declaration-note { 229 | font-size: .85em; 230 | color: gray; 231 | font-style: italic; } 232 | 233 | .pointer-container { 234 | border-bottom: 1px solid #e2e2e2; 235 | left: -23px; 236 | padding-bottom: 13px; 237 | position: relative; 238 | width: 110%; } 239 | 240 | .pointer { 241 | background: #f9f9f9; 242 | border-left: 1px solid #e2e2e2; 243 | border-top: 1px solid #e2e2e2; 244 | height: 12px; 245 | left: 21px; 246 | top: -7px; 247 | -webkit-transform: rotate(45deg); 248 | -moz-transform: rotate(45deg); 249 | -o-transform: rotate(45deg); 250 | transform: rotate(45deg); 251 | position: absolute; 252 | width: 12px; } 253 | 254 | .height-container { 255 | display: none; 256 | left: -25px; 257 | padding: 0 25px; 258 | position: relative; 259 | width: 100%; 260 | overflow: hidden; } 261 | .height-container .section { 262 | background: #f9f9f9; 263 | border-bottom: 1px solid #e2e2e2; 264 | left: -25px; 265 | position: relative; 266 | width: 100%; 267 | padding-top: 10px; 268 | padding-bottom: 5px; } 269 | 270 | .aside, .language { 271 | padding: 6px 12px; 272 | margin: 12px 0; 273 | border-left: 5px solid #dddddd; 274 | overflow-y: hidden; } 275 | .aside .aside-title, .language .aside-title { 276 | font-size: 9px; 277 | letter-spacing: 2px; 278 | text-transform: uppercase; 279 | padding-bottom: 0; 280 | margin: 0; 281 | color: #aaa; 282 | -webkit-user-select: none; } 283 | .aside p:last-child, .language p:last-child { 284 | margin-bottom: 0; } 285 | 286 | .language { 287 | border-left: 5px solid #cde9f4; } 288 | .language .aside-title { 289 | color: #4b8afb; } 290 | 291 | .aside-warning, .aside-deprecated, .aside-unavailable { 292 | border-left: 5px solid #ff6666; } 293 | .aside-warning .aside-title, .aside-deprecated .aside-title, .aside-unavailable .aside-title { 294 | color: #ff0000; } 295 | 296 | .graybox { 297 | border-collapse: collapse; 298 | width: 100%; } 299 | .graybox p { 300 | margin: 0; 301 | word-break: break-word; 302 | min-width: 50px; } 303 | .graybox td { 304 | border: 1px solid #e2e2e2; 305 | padding: 5px 25px 5px 10px; 306 | vertical-align: middle; } 307 | .graybox tr td:first-of-type { 308 | text-align: right; 309 | padding: 7px; 310 | vertical-align: top; 311 | word-break: normal; 312 | width: 40px; } 313 | 314 | .slightly-smaller { 315 | font-size: 0.9em; } 316 | 317 | #footer { 318 | position: relative; 319 | top: 10px; 320 | bottom: 0px; 321 | margin-left: 25px; } 322 | #footer p { 323 | margin: 0; 324 | color: #aaa; 325 | font-size: 0.8em; } 326 | 327 | html.dash header, html.dash #breadcrumbs, html.dash .sidebar { 328 | display: none; } 329 | 330 | html.dash .main-content { 331 | width: 980px; 332 | margin-left: 0; 333 | border: none; 334 | width: 100%; 335 | top: 0; 336 | padding-bottom: 0; } 337 | 338 | html.dash .height-container { 339 | display: block; } 340 | 341 | html.dash .item .token { 342 | margin-left: 0; } 343 | 344 | html.dash .content-wrapper { 345 | width: auto; } 346 | 347 | html.dash #footer { 348 | position: static; } 349 | -------------------------------------------------------------------------------- /docs/docsets/Omnia.docset/Contents/Resources/Documents/img/carat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onmyway133/Omnia/56589071d33ad116b5fae544e8017a08605b094f/docs/docsets/Omnia.docset/Contents/Resources/Documents/img/carat.png -------------------------------------------------------------------------------- /docs/docsets/Omnia.docset/Contents/Resources/Documents/img/dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onmyway133/Omnia/56589071d33ad116b5fae544e8017a08605b094f/docs/docsets/Omnia.docset/Contents/Resources/Documents/img/dash.png -------------------------------------------------------------------------------- /docs/docsets/Omnia.docset/Contents/Resources/Documents/img/gh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onmyway133/Omnia/56589071d33ad116b5fae544e8017a08605b094f/docs/docsets/Omnia.docset/Contents/Resources/Documents/img/gh.png -------------------------------------------------------------------------------- /docs/docsets/Omnia.docset/Contents/Resources/Documents/js/jazzy.js: -------------------------------------------------------------------------------- 1 | window.jazzy = {'docset': false} 2 | if (typeof window.dash != 'undefined') { 3 | document.documentElement.className += ' dash' 4 | window.jazzy.docset = true 5 | } 6 | if (navigator.userAgent.match(/xcode/i)) { 7 | document.documentElement.className += ' xcode' 8 | window.jazzy.docset = true 9 | } 10 | 11 | function toggleItem($link, $content) { 12 | var animationDuration = 300; 13 | $link.toggleClass('token-open'); 14 | $content.slideToggle(animationDuration); 15 | } 16 | 17 | function itemLinkToContent($link) { 18 | return $link.parent().parent().next(); 19 | } 20 | 21 | // On doc load + hash-change, open any targetted item 22 | function openCurrentItemIfClosed() { 23 | if (window.jazzy.docset) { 24 | return; 25 | } 26 | var $link = $(`.token[href="${location.hash}"]`); 27 | $content = itemLinkToContent($link); 28 | if ($content.is(':hidden')) { 29 | toggleItem($link, $content); 30 | } 31 | } 32 | 33 | $(openCurrentItemIfClosed); 34 | $(window).on('hashchange', openCurrentItemIfClosed); 35 | 36 | // On item link ('token') click, toggle its discussion 37 | $('.token').on('click', function(event) { 38 | if (window.jazzy.docset) { 39 | return; 40 | } 41 | var $link = $(this); 42 | toggleItem($link, itemLinkToContent($link)); 43 | 44 | // Keeps the document from jumping to the hash. 45 | var href = $link.attr('href'); 46 | if (history.pushState) { 47 | history.pushState({}, '', href); 48 | } else { 49 | location.hash = href; 50 | } 51 | event.preventDefault(); 52 | }); 53 | 54 | // Clicks on links to the current, closed, item need to open the item 55 | $("a:not('.token')").on('click', function() { 56 | if (location == this.href) { 57 | openCurrentItemIfClosed(); 58 | } 59 | }); 60 | -------------------------------------------------------------------------------- /docs/docsets/Omnia.docset/Contents/Resources/docSet.dsidx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onmyway133/Omnia/56589071d33ad116b5fae544e8017a08605b094f/docs/docsets/Omnia.docset/Contents/Resources/docSet.dsidx -------------------------------------------------------------------------------- /docs/docsets/Omnia.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onmyway133/Omnia/56589071d33ad116b5fae544e8017a08605b094f/docs/docsets/Omnia.tgz -------------------------------------------------------------------------------- /docs/img/carat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onmyway133/Omnia/56589071d33ad116b5fae544e8017a08605b094f/docs/img/carat.png -------------------------------------------------------------------------------- /docs/img/dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onmyway133/Omnia/56589071d33ad116b5fae544e8017a08605b094f/docs/img/dash.png -------------------------------------------------------------------------------- /docs/img/gh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/onmyway133/Omnia/56589071d33ad116b5fae544e8017a08605b094f/docs/img/gh.png -------------------------------------------------------------------------------- /docs/js/jazzy.js: -------------------------------------------------------------------------------- 1 | window.jazzy = {'docset': false} 2 | if (typeof window.dash != 'undefined') { 3 | document.documentElement.className += ' dash' 4 | window.jazzy.docset = true 5 | } 6 | if (navigator.userAgent.match(/xcode/i)) { 7 | document.documentElement.className += ' xcode' 8 | window.jazzy.docset = true 9 | } 10 | 11 | function toggleItem($link, $content) { 12 | var animationDuration = 300; 13 | $link.toggleClass('token-open'); 14 | $content.slideToggle(animationDuration); 15 | } 16 | 17 | function itemLinkToContent($link) { 18 | return $link.parent().parent().next(); 19 | } 20 | 21 | // On doc load + hash-change, open any targetted item 22 | function openCurrentItemIfClosed() { 23 | if (window.jazzy.docset) { 24 | return; 25 | } 26 | var $link = $(`.token[href="${location.hash}"]`); 27 | $content = itemLinkToContent($link); 28 | if ($content.is(':hidden')) { 29 | toggleItem($link, $content); 30 | } 31 | } 32 | 33 | $(openCurrentItemIfClosed); 34 | $(window).on('hashchange', openCurrentItemIfClosed); 35 | 36 | // On item link ('token') click, toggle its discussion 37 | $('.token').on('click', function(event) { 38 | if (window.jazzy.docset) { 39 | return; 40 | } 41 | var $link = $(this); 42 | toggleItem($link, itemLinkToContent($link)); 43 | 44 | // Keeps the document from jumping to the hash. 45 | var href = $link.attr('href'); 46 | if (history.pushState) { 47 | history.pushState({}, '', href); 48 | } else { 49 | location.hash = href; 50 | } 51 | event.preventDefault(); 52 | }); 53 | 54 | // Clicks on links to the current, closed, item need to open the item 55 | $("a:not('.token')").on('click', function() { 56 | if (location == this.href) { 57 | openCurrentItemIfClosed(); 58 | } 59 | }); 60 | --------------------------------------------------------------------------------