Skip to content

Commit 6a60f06

Browse files
committed
feat: Initial
0 parents  commit 6a60f06

File tree

10 files changed

+263
-0
lines changed

10 files changed

+263
-0
lines changed

.gitignore

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
.DS_Store
2+
/.build
3+
/Packages
4+
xcuserdata/
5+
DerivedData/
6+
.swiftpm/configuration/registries.json
7+
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
8+
.netrc

.spi.yml

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
version: 1
2+
builder:
3+
configs:
4+
- documentation_targets: [FoundationPlus]

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2024, Felix Ruppert
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

Package.swift

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// swift-tools-version: 6.0
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: "FoundationPlus",
8+
products: [
9+
// Products define the executables and libraries a package produces, making them visible to other packages.
10+
.library(
11+
name: "FoundationPlus",
12+
targets: ["FoundationPlus"]),
13+
],
14+
targets: [
15+
// Targets are the basic building blocks of a package, defining a module or a test suite.
16+
// Targets can depend on other targets in this package and products from dependencies.
17+
.target(
18+
name: "FoundationPlus"),
19+
.testTarget(
20+
name: "FoundationPlusTests",
21+
dependencies: ["FoundationPlus"]
22+
),
23+
]
24+
)

README.md

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# FoundationPlus
2+
3+
[![Swift](https://img.shields.io/badge/Swift-6.0-orange.svg)](https://swift.org)
4+
[![License](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
5+
6+
## Overview
7+
8+
This package extends the Swift Foundation framework with extensions to existing types and (in future) entirely new types.
9+
10+
## Requirements
11+
- Swift 6.0
12+
13+
## Installation
14+
15+
### Swift Package Manager
16+
17+
To integrate `FoundationPlus` into your Xcode project using Swift Package Manager, follow these steps:
18+
19+
1. Open your project in Xcode.
20+
2. Select `File` > `Swift Packages` > `Add Package Dependency...`.
21+
3. Enter the package repository URL: `https://github.com/flexlixrup/FoundationPlus`.
22+
4. Choose the latest release or specify a version range.
23+
5. Add the package to your target.
24+
25+
Alternatively, you can add the following dependency to your `Package.swift` file:
26+
27+
```swift
28+
dependencies: [
29+
.package(url: "https://github.com/flexlixrup/FoundationPlus", from: "1.0.0")
30+
]
31+
```
32+
33+
Then, include `FoundationPlus` as a dependency in your target:
34+
35+
```swift
36+
.target(
37+
name: "YourTargetName",
38+
dependencies: [
39+
"FoundationPlus"
40+
]),
41+
```
42+
43+
## Usage
44+
45+
The full documentation is provided via Docc on [Swift Package Manager](https://swiftpackageindex.com)
46+
47+
## Contributing
48+
49+
If you would like to contribute, please follow these steps:
50+
51+
1. Fork the repository.
52+
2. Create a new branch (`git checkout -b feature-branch`).
53+
3. Commit your changes (`git commit -am 'Add new feature'`).
54+
4. Push to the branch (`git push origin feature-branch`).
55+
5. Create a new Pull Request.
56+
57+
## License
58+
59+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
60+
61+
## Contact
62+
63+
If you have any questions, feel free to open an issue.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# ``FoundationPlus``
2+
3+
This package extends the Swift Foundation framework with extensions to existing types and (in future) entirely new types.
4+
5+
## Overview
6+
7+
``FoundationPlus`` acts as a pool of extensions and new types to the Swift Foundation Framework. It doesn't follow any specific order or plans as it just a collection of things I found useful when working with Swift and Foundation.
8+
Neverheless it is tested and should work as the documentation suggests.
9+
10+
## Extensions
11+
12+
### String
13+
14+
- ``Swift/String/dateFormat``
15+
16+
### CaseIterable
17+
18+
- ``Swift/CaseIterable/index``
19+
20+
### Array
21+
22+
- ``Swift/Array/secondToLast``
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// The Swift Programming Language
2+
// https://docs.swift.org/swift-book
3+
4+
public extension Array {
5+
/// The second to last element of the collection.
6+
@inlinable var secondToLast: Element? {
7+
guard count >= 2 else {
8+
return nil
9+
}
10+
return self[count - 2]
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//
2+
// CaseIterable+Extensions.swift
3+
// FoundationPlus
4+
//
5+
// Created by Felix Ruppert on 13.10.24.
6+
//
7+
8+
public extension CaseIterable where Self: Equatable {
9+
/// Finds the index of an equatale case in a CaseIterable enum.
10+
@inlinable var index: Self.AllCases.Index {
11+
Self.allCases.firstIndex { self == $0 }!
12+
}
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
//
2+
// String+Extensions.swift
3+
// FoundationPlus
4+
//
5+
// Created by Felix Ruppert on 13.10.24.
6+
//
7+
import Foundation
8+
9+
public extension String {
10+
/// Finds the date format in a given String and returns it as String.
11+
///
12+
/// This method supports following date formats:
13+
/// - MM/dd/yyyy
14+
/// - yyyy-MM-dd
15+
/// - dd-MM-yyyy
16+
/// - dd.MM.yy
17+
/// - yyyy/MM/dd
18+
/// - yyyy-MM-dd'T'HH:mm:ssZ
19+
/// - yyyy-MM-dd HH:mm:ss
20+
/// - yyyy-MM-dd'T'HH:mm:ssXXXXX
21+
@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
22+
@inlinable var dateFormat: String {
23+
let regexes: [(Regex<(Substring, Substring)>, String)] = [
24+
// MM/dd/yyyy e.g., 01/31/2020
25+
(/(\d{2}\/\d{2}\/\d{4}$)/, "MM/dd/yyyy"),
26+
// yyyy-MM-dd e.g., 2020-01-31 (ISO 8601 Date)
27+
(/(\d{4}-\d{2}-\d{2}$)/, "yyyy-MM-dd"),
28+
// dd-MM-yyyy e.g., 31-01-2020
29+
(/(\d{2}-\d{2}-\d{4}$)/, "dd-MM-yyyy"),
30+
// dd.MM.yyyy e.g., 31.01.2020
31+
(/(\d{2}\.\d{2}\.\d{4}$)/, "dd.MM.yyyy"),
32+
// dd.MM.yy e.g., 31.01.20
33+
(/(\d{2}\.\d{2}\.\d{2}$)/, "dd.MM.yy"),
34+
// yyyy/MM/dd e.g., 2020/01/31
35+
(/(\d{4}\/\d{2}\/\d{2}$)/, "yyyy/MM/dd"),
36+
// ISO 8601 Date and Time e.g., 2020-01-31T15:20:30Z
37+
(/(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}\:\d{2}Z?$)/, "yyyy-MM-dd'T'HH:mm:ssZ"),
38+
(/(\d{4}-\d{2}-\d{2} \d{2}:\d{2}\:\d{2}?$)/, "yyyy-MM-dd HH:mm:ss"),
39+
// ISO 8601 Date and Time with Timezone e.g., 2020-01-31T15:20:30+00:00
40+
(/(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}[\+\-]\\d{2}:\d{2}$)/, "yyyy-MM-dd'T'HH:mm:ssXXXXX")
41+
] // Date contains 2 dots
42+
for regex in regexes {
43+
if contains(regex.0) {
44+
return regex.1
45+
}
46+
}
47+
return "dd.MM.yyyy"
48+
}
49+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
@testable import FoundationPlus
2+
import Testing
3+
4+
@Suite("Array Test Suite")
5+
struct ArrayTests {
6+
7+
@Test("Test to get the second to last element")
8+
func getSecondToLastElementNotNil() {
9+
let array: [Int] = [1, 2, 3, 4, 5]
10+
#expect(array.secondToLast == 4)
11+
}
12+
13+
@Test("Test to get the second to last element when the array has < 2 elements")
14+
func getSecondToLastElementNil() {
15+
let array: [Int] = [1]
16+
#expect(array.secondToLast == nil)
17+
}
18+
}
19+
20+
@Suite("CaseIterable Test Suite")
21+
struct CaseIterableTests {
22+
23+
@Test("Test to get the index of an element")
24+
func getIndexOfElement() {
25+
enum Direction: CaseIterable, Equatable {
26+
case north, south, east, west
27+
}
28+
let direction: Direction = .east
29+
#expect(direction.index == 2)
30+
}
31+
}
32+
33+
@Suite("String test suite")
34+
struct StringTests {
35+
36+
@Test("Test the detection of Date Formats", arguments: [
37+
("12.12.2023", "dd.MM.yyyy"),
38+
("12.12.23", "dd.MM.yy"),
39+
("12/12/2023", "MM/dd/yyyy"),
40+
("2023-02-25 09:09:25", "yyyy-MM-dd HH:mm:ss"),
41+
("2023/12/12", "yyyy/MM/dd")
42+
])
43+
func testDateFormatDetector(values: (String, String)) {
44+
let result = values.0.dateFormat
45+
#expect(result == values.1)
46+
}
47+
}

0 commit comments

Comments
 (0)