Skip to content
This repository was archived by the owner on Jan 2, 2025. It is now read-only.

Commit ec7f7a0

Browse files
authored
Merge pull request #1 from hyperlinkgroup/develop
1.0
2 parents 3766dea + 5111a37 commit ec7f7a0

13 files changed

+548
-0
lines changed

.github/README.md

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
# Swift CSV Codable Package
2+
3+
This repository contains a small Swift Package, that helps writing Codable-Objects into CSV-Files.
4+
5+
It is made by **[SPACE SQUAD](https://www.spacesquad.de)**! We make great software with ♥️ in Berlin.
6+
7+
<img src="assets/README-spacesquad_logo_full.png" width="120">
8+
9+
---
10+
11+
## Content
12+
- [Features](#features)
13+
- [Installation](#installation)
14+
- [How to Use](#how-to-use)
15+
16+
17+
## Features
18+
- [x] Convert Codable-Objects to CSV-Content
19+
- [x] Export and Save CSV Files
20+
- [ ] Import CSV Files and convert to Codable-Objects
21+
22+
---
23+
24+
## Installation
25+
##### Requirements
26+
- iOS 14.0+ / macOS 11.0+
27+
- Xcode 13+
28+
- Swift 5+
29+
30+
##### Swift Package Manager
31+
In Xcode, go to `File > Add Packages` and add `https://github.com/hyperlink/swift-csv-codable`. Add the package to all your targets.
32+
In the files you need any CSV-Capabilities add the import-Statement on top of your file:
33+
```
34+
import CSVCodable
35+
```
36+
37+
---
38+
39+
## How to Use
40+
### Generate CSV Content
41+
42+
```Swift
43+
public struct Person: Codable {
44+
let name: String
45+
let age: Int
46+
}
47+
48+
let people: [Person]
49+
50+
// fill array with content
51+
52+
let content = try? ExportManager.toCSV(people)
53+
54+
/**
55+
"name;age
56+
Jane;24
57+
John;29"
58+
*/
59+
60+
```
61+
62+
### Configuration
63+
During the export process you might need some custom settings. For now, all values are separated by semicolons and can be changed by initializing the `ExportManager` with a custom configuration object.
64+
65+
More customization options will be available in future releases.
66+
67+
```Swift
68+
var config = ExportConfiguration()
69+
config.delimiter = "," // Default: ";""
70+
ExportManager.setup(config)
71+
```
72+
73+
74+
### Export and Save
75+
76+
```Swift
77+
// Store in Document Directory
78+
79+
let content = try? ExportManager.toCSV(people)
80+
ExportManager.saveInDocumentDirectory(data: content) { result in
81+
switch result {
82+
case .success(let url):
83+
print("Saved successfully at \(url)")
84+
case .failure(let error):
85+
print("Error: \(error.localizedDescription)")
86+
}
87+
}
88+
89+
90+
// Short style: Store in Document Directory
91+
ExportManager.toCSVDocument(people) { result in
92+
switch result {
93+
case .success(let url):
94+
print("Saved successfully at \(url)")
95+
case .failure(let error):
96+
print("Error: \(error.localizedDescription)")
97+
}
98+
}
99+
```
100+
101+
102+
#### SwiftUI
103+
104+
To save the generated file from your SwiftUI-App in the file system, you need to use the `fileExporter()`-Modifier and reference the generated `CSVDocument` by calling `ExportManager.toCSVDocument(:_)`
105+
106+
<img src="assets/save_macos.jpeg" width="300">
107+
108+
```Swift
109+
struct ContentView: View {
110+
@State private var document: CSVDocument?
111+
@State private var showExporter = false
112+
113+
var body: some View {
114+
VStack {
115+
Button {
116+
let people: [Person]
117+
118+
// fill array with content
119+
120+
ExportManager.toCSVDocument(people) { result in
121+
switch result {
122+
case .success(let document):
123+
self.document = document
124+
self.showExporter = true
125+
case .failure(let error):
126+
print("Error: \(error.localizedDescription)")
127+
}
128+
}
129+
} label: {
130+
Image(systemName: "square.and.arrow.up.circle.fill")
131+
}
132+
}
133+
.fileExporter(isPresented: $showExporter, document: document, contentType: .commaSeparatedText) { result in
134+
switch result {
135+
case .success(let url):
136+
print("Saved successfully at \(url)")
137+
case .failure(let error):
138+
print("Error: \(error.localizedDescription)")
139+
}
140+
}
141+
}
142+
}
143+
```
144+
145+
146+
#### UIKit
147+
148+
For sharing and saving the file in your UIKit-App, you can use our `.share(:_)`-Function from any `UIViewController`. It creates and automatically opens an `UIActivityViewController`.
149+
150+
<img src="assets/sharesheet_uikit.jpeg" width="300">
151+
152+
```Swift
153+
class ViewController: UIViewController {
154+
155+
func exportCSV() {
156+
let people: [Person]
157+
158+
// fill array with content
159+
160+
ExportManager.saveAsCSV(people, fileName: "MyCSVFile.csv") { result in
161+
switch result {
162+
case .success(let url):
163+
print("Saved successfully at \(url)")
164+
// Open ActivityViewController
165+
ExportManager.share(url: url, viewController: self) {
166+
// ShareSheet was closed
167+
}
168+
case .failure(let error):
169+
print(error.localizedDescription)
170+
}
171+
}
172+
}
173+
}
174+
```
47.8 KB
Loading

.github/assets/save_macos.jpeg

16.6 KB
Loading

.github/assets/sharesheet_uikit.jpeg

439 KB
Loading

.gitignore

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
# Xcode
2+
#
3+
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4+
5+
## User settings
6+
xcuserdata/
7+
8+
## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9)
9+
*.xcscmblueprint
10+
*.xccheckout
11+
12+
## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4)
13+
build/
14+
DerivedData/
15+
*.moved-aside
16+
*.pbxuser
17+
!default.pbxuser
18+
*.mode1v3
19+
!default.mode1v3
20+
*.mode2v3
21+
!default.mode2v3
22+
*.perspectivev3
23+
!default.perspectivev3
24+
25+
## Obj-C/Swift specific
26+
*.hmap
27+
28+
## App packaging
29+
*.ipa
30+
*.dSYM.zip
31+
*.dSYM
32+
33+
## Playgrounds
34+
timeline.xctimeline
35+
playground.xcworkspace
36+
37+
# Swift Package Manager
38+
#
39+
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
40+
Packages/
41+
Package.pins
42+
Package.resolved
43+
*.xcodeproj
44+
#
45+
# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
46+
# hence it is not needed unless you have added a package configuration file to your project
47+
.swiftpm
48+
49+
.build/
50+
51+
# CocoaPods
52+
#
53+
# We recommend against adding the Pods directory to your .gitignore. However
54+
# you should judge for yourself, the pros and cons are mentioned at:
55+
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
56+
#
57+
# Pods/
58+
#
59+
# Add this line if you want to avoid checking in source code from the Xcode workspace
60+
# *.xcworkspace
61+
62+
# Carthage
63+
#
64+
# Add this line if you want to avoid checking in source code from Carthage dependencies.
65+
# Carthage/Checkouts
66+
67+
Carthage/Build/
68+
69+
# Accio dependency management
70+
Dependencies/
71+
.accio/
72+
73+
# fastlane
74+
#
75+
# It is recommended to not store the screenshots in the git repo.
76+
# Instead, use fastlane to re-generate the screenshots whenever they are needed.
77+
# For more information about the recommended setup visit:
78+
# https://docs.fastlane.tools/best-practices/source-control/#source-control
79+
80+
fastlane/report.xml
81+
fastlane/Preview.html
82+
fastlane/screenshots/**/*.png
83+
fastlane/test_output
84+
85+
# Code Injection
86+
#
87+
# After new code Injection tools there's a generated folder /iOSInjectionProject
88+
# https://github.com/johnno1962/injectionforxcode
89+
90+
iOSInjectionProject/

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2023 SPACE SQUAD
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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// swift-tools-version: 5.6
2+
3+
import PackageDescription
4+
5+
let package = Package(
6+
name: "CSVCodable",
7+
platforms: [
8+
.iOS(.v14),
9+
.macOS(.v11)
10+
],
11+
products: [
12+
.library(
13+
name: "CSVCodable",
14+
targets: ["CSVCodable"]),
15+
],
16+
dependencies: [],
17+
targets: [
18+
.target(
19+
name: "CSVCodable",
20+
dependencies: [],
21+
path: "Sources/CSVCodable")
22+
]
23+
)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//
2+
// Encodable.swift
3+
//
4+
//
5+
// Created by Anna Münster on 20.07.22.
6+
//
7+
8+
import Foundation
9+
10+
extension Encodable {
11+
/**
12+
Creates a dictionary of Key-Value-Pairs (property names and property values) for every encodable object.
13+
14+
- Important:
15+
Throws an error if the class implements Firebase-specific properties, like @DocumentID. Maybe specific CodingKeys for exluding these properties would work.
16+
Also keep in mind that inherited classes need specific coding keys to encode child's properties (so if A is a subclass of B, only the fields of B are in the exported dictionary)!
17+
*/
18+
public func dictionary() throws -> [String: Any] {
19+
do {
20+
let data = try JSONEncoder().encode(self)
21+
guard let jsonObject = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any] else {
22+
throw ExportError.encode(error: nil)
23+
}
24+
return jsonObject
25+
} catch {
26+
throw ExportError.encode(error: error)
27+
}
28+
}
29+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//
2+
// ExportConfiguration.swift
3+
//
4+
//
5+
// Created by Anna Münster on 14.02.23.
6+
//
7+
8+
import Foundation
9+
10+
public struct ExportConfiguration {
11+
public var delimiter: String = ";"
12+
13+
public init() { }
14+
}

Sources/CSVCodable/ExportError.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
//
2+
// ExportError.swift
3+
//
4+
//
5+
// Created by Anna Münster on 23.09.22.
6+
//
7+
8+
import Foundation
9+
10+
public enum ExportError: LocalizedError {
11+
case csvWrite(error: Error),
12+
dataIsEmpty,
13+
encode(error: Error?),
14+
fileManager,
15+
unknown(error: Error)
16+
17+
public var errorDescription: String? {
18+
switch self {
19+
case .csvWrite(let error): return "Error writing CSV to file: \(error)"
20+
case .dataIsEmpty: return "Error: Encoded Data is empty"
21+
case .encode(let error): return "Error encoding object: \(error?.localizedDescription ?? "Cast to [String: Any] failed")"
22+
case .fileManager: return "Error finding document directory"
23+
case .unknown(let error): return "Error: \(error.localizedDescription)"
24+
}
25+
}
26+
}

0 commit comments

Comments
 (0)