9 | //
10 | // Permission is hereby granted, free of charge, to any person obtaining a copy
11 | // of this software and associated documentation files (the "Software"), to deal
12 | // in the Software without restriction, including without limitation the rights
13 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 | // copies of the Software, and to permit persons to whom the Software is
15 | // furnished to do so, subject to the following conditions:
16 | //
17 | // The above copyright notice and this permission notice shall be included in
18 | // all copies or substantial portions of the Software.
19 | //
20 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26 | // THE SOFTWARE.
27 |
28 | import PackageDescription
29 |
30 | let package = Package(
31 | name: "EFSafeArray",
32 | platforms: [.iOS(.v12), .macOS(.v10_13), .tvOS(.v12), .watchOS(.v6), .visionOS(.v1)],
33 | products: [
34 | .library(name: "EFSafeArray", targets: ["EFSafeArray"])
35 | ],
36 | targets: [
37 | .target(name: "EFSafeArray", path: "EFSafeArray", exclude: ["Info.plist", "Info-tvOS.plist"])
38 | ],
39 | swiftLanguageVersions: [.v6]
40 | )
41 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing Guidelines
2 |
3 | This document contains information and guidelines about contributing to this project.
4 | Please read it before you start participating.
5 |
6 | **Topics**
7 |
8 | * [Asking Questions](#asking-questions)
9 | * [Reporting Issues](#reporting-issues)
10 | * [Developers Certificate of Origin](#developers-certificate-of-origin)
11 |
12 | ## Asking Questions
13 |
14 | We don't use GitHub as a support forum.
15 | For any usage questions that are not specific to the project itself,
16 | please ask on [Stack Overflow](https://stackoverflow.com) instead.
17 | By doing so, you'll be more likely to quickly solve your problem,
18 | and you'll allow anyone else with the same question to find the answer.
19 | This also allows maintainers to focus on improving the project for others.
20 |
21 | ## Reporting Issues
22 |
23 | A great way to contribute to the project
24 | is to send a detailed issue when you encounter an problem.
25 | We always appreciate a well-written, thorough bug report.
26 |
27 | Check that the project issues database
28 | doesn't already include that problem or suggestion before submitting an issue.
29 | If you find a match, add a quick "+1" or "I have this problem too."
30 | Doing this helps prioritize the most common problems and requests.
31 |
32 | When reporting issues, please include the following:
33 |
34 | * The version of Xcode you're using
35 | * The version of iOS or macOS you're targeting
36 | * The full output of any stack trace or compiler error
37 | * A code snippet that reproduces the described behavior, if applicable
38 | * Any other details that would be useful in understanding the problem
39 |
40 | This information will help us review and fix your issue faster.
41 |
42 | ## Developer's Certificate of Origin
43 |
44 | By making a contribution to this project, I certify that:
45 |
46 | - (a) The contribution was created in whole or in part by me and I
47 | have the right to submit it under the open source license
48 | indicated in the file; or
49 |
50 | - (b) The contribution is based upon previous work that, to the best
51 | of my knowledge, is covered under an appropriate open source
52 | license and I have the right under that license to submit that
53 | work with modifications, whether created in whole or in part
54 | by me, under the same open source license (unless I am
55 | permitted to submit under a different license), as indicated
56 | in the file; or
57 |
58 | - (c) The contribution was provided directly to me by some other
59 | person who certified (a), (b) or (c) and I have not modified
60 | it.
61 |
62 | - (d) I understand and agree that this project and the contribution
63 | are public and that a record of the contribution (including all
64 | personal information I submit with it, including my sign-off) is
65 | maintained indefinitely and may be redistributed consistent with
66 | this project or the open source license(s) involved.
67 |
68 | ---
69 |
70 | *Some of the ideas and wording for the statements above were based on work by the [Alamofire](https://github.com/Alamofire/Alamofire/blob/master/CONTRIBUTING.md) communities. We commend them for their efforts to facilitate collaboration in their projects.*
71 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
6 |
7 | ## Our Standards
8 |
9 | Examples of behavior that contributes to creating a positive environment include:
10 |
11 | * Using welcoming and inclusive language
12 | * Being respectful of differing viewpoints and experiences
13 | * Gracefully accepting constructive criticism
14 | * Focusing on what is best for the community
15 | * Showing empathy towards other community members
16 |
17 | Examples of unacceptable behavior by participants include:
18 |
19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances
20 | * Trolling, insulting/derogatory comments, and personal or political attacks
21 | * Public or private harassment
22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission
23 | * Other conduct which could reasonably be considered inappropriate in a professional setting
24 |
25 | ## Our Responsibilities
26 |
27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
28 |
29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
30 |
31 | ## Scope
32 |
33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
34 |
35 | ## Enforcement
36 |
37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at eyrefree@eyrefree.org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
38 |
39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
40 |
41 | ## Attribution
42 |
43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
44 |
45 | [homepage]: http://contributor-covenant.org
46 | [version]: http://contributor-covenant.org/version/1/4/
47 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | EFSafeArray is an extension to make array subscript safe, nil will be return instead of crash if index is out of range, it works on `iOS`, `macOS`, `watchOS` and `tvOS`.
25 |
26 | ## Example
27 |
28 | To run the example project, clone the repo, and run `pod install` from the Example directory first.
29 |
30 | ## Requirements
31 |
32 | - Xcode 16+
33 | - Swift 6.0+
34 |
35 | ## Installation
36 |
37 | ### CocoaPods
38 |
39 | EFSafeArray is available through [CocoaPods](http://cocoapods.org). To install it, simply add the following line to your Podfile:
40 |
41 | ```ruby
42 | pod 'EFSafeArray'
43 | ```
44 |
45 | ### Swift Package Manager
46 |
47 | The [Swift Package Manager](https://swift.org/package-manager/) is a tool for automating the distribution of Swift code and is integrated into the Swift compiler.
48 |
49 | Once you have your Swift package set up, adding EFSafeArray as a dependency is as easy as adding it to the `dependencies` value of your `Package.swift`.
50 |
51 | ```swift
52 | dependencies: [
53 | .package(url: "https://github.com/EFPrefix/EFSafeArray.git", .upToNextMinor(from: "6.0.0.0"))
54 | ]
55 | ```
56 |
57 | ## Use
58 |
59 | ```swift
60 | var list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
61 | // Get Index
62 | let xxx = list[0] // xxx: Int = 1
63 | let zzz = list[0~] // zzz: Int? = 1
64 | let yyy = list[10~] // yyy: Int? = nil
65 |
66 | // Set Index
67 | list[0] = 0 // list = [0, 2, 3, 4, 5, 6, 7, 8, 9, 0]
68 | list[0~] = 1 // list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
69 | list[10~] = 10 // list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
70 |
71 | // Get Bounds
72 | let iiii = list[(0...5)~] // iiii: ArraySlice? = [1, 2, 3, 4, 5, 6]
73 | let oooo = list[(-1...12)~] // oooo: ArraySlice? = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]
74 |
75 | // Set Bounds
76 | list[(0...5)~] = [1] // list = [1, 7, 8, 9, 0]
77 | list[(-1...12)~] = [2, 3, 4, 5] // list = [2, 3, 4, 5]
78 | ```
79 |
80 | ## Author
81 |
82 | EyreFree, eyrefree@eyrefree.org
83 |
84 | ## License
85 |
86 | 
87 |
88 | EFSafeArray is available under the MIT license. See the LICENSE file for more info.
89 |
--------------------------------------------------------------------------------
/EFSafeArray/EFSafeArray.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EFSafeArray.swift
3 | // EFSafeArray
4 | //
5 | // Created by EyreFree on 2017/7/28.
6 | //
7 | // Copyright (c) 2017 EyreFree
8 | //
9 | // Permission is hereby granted, free of charge, to any person obtaining a copy
10 | // of this software and associated documentation files (the "Software"), to deal
11 | // in the Software without restriction, including without limitation the rights
12 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 | // copies of the Software, and to permit persons to whom the Software is
14 | // furnished to do so, subject to the following conditions:
15 | //
16 | // The above copyright notice and this permission notice shall be included in
17 | // all copies or substantial portions of the Software.
18 | //
19 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 | // THE SOFTWARE.
26 |
27 | import Foundation
28 |
29 | // ~
30 | postfix operator ~
31 | public postfix func ~ (value: Int?) -> EFSafeInt? {
32 | return EFSafeInt(value: value)
33 | }
34 | public postfix func ~ (value: Range?) -> EFSafeRange? {
35 | return EFSafeRange(value: value)
36 | }
37 | public postfix func ~ (value: CountableClosedRange?) -> EFSafeRange? {
38 | guard let value = value else {
39 | return nil
40 | }
41 | return EFSafeRange(value: Range(value))
42 | }
43 |
44 | // Struct
45 | public struct EFSafeInt {
46 | var index: Int
47 | init?(value: Int?) {
48 | guard let value = value else {
49 | return nil
50 | }
51 | self.index = value
52 | }
53 | }
54 |
55 | public struct EFSafeRange {
56 | var range: Range
57 | init?(value: Range?) {
58 | guard let value = value else {
59 | return nil
60 | }
61 | self.range = value
62 | }
63 | }
64 |
65 | // Core
66 | public extension Array {
67 | subscript(index: EFSafeInt?) -> Element? {
68 | get {
69 | if let index = index?.index {
70 | return (self.startIndex.. ArraySlice? {
84 | get {
85 | if let range = bounds?.range {
86 | return self[range.clamped(to: self.startIndex ..< self.endIndex)]
87 | }
88 | return nil
89 | }
90 | set {
91 | if let range = bounds?.range, let newValue = newValue {
92 | self[range.clamped(to: self.startIndex ..< self.endIndex)] = newValue
93 | }
94 | }
95 | }
96 | }
97 |
--------------------------------------------------------------------------------