Help
RSS
API
Feed
Maltego
Contact
Domain > matthewman.net
×
More information on this domain is in
AlienVault OTX
Is this malicious?
Yes
No
DNS Resolutions
Date
IP Address
2014-10-06
104.28.16.99
(
ClassC
)
2024-08-05
50.18.142.31
(
ClassC
)
Port 443
HTTP/1.1 200 OKAccept-Ranges: bytesAge: 0Cache-Control: public,max-age0,must-revalidateCache-Status: Netlify Edge; fwdmissContent-Length: 16013Content-Type: text/html; charsetUTF-8Date: Mon, 05 Aug 2024 09:39:52 GMTEtag: 58b25a6b340365e66806847a61793794-sslNetlify-Vary: cookie__next_preview_data:presence|__prerender_bypass:presenceServer: NetlifyStrict-Transport-Security: max-age31536000X-Nf-Request-Id: 01J4GYHMJ7HXZ0RKV8MP9A1AE4 !DOCTYPE html>html langen classtheme-compiled>head>meta charSetutf-8/>meta nameviewport contentwidthdevice-width/>title>Scott Matthewman/title>meta namedescription contentCoding, theatre and other stuff/>meta propertyog:title contentScott Matthewman/>meta namenext-head-count content5/>link relpreload href/_next/static/css/47712a5c5e339b1c.css asstyle/>link relstylesheet href/_next/static/css/47712a5c5e339b1c.css data-n-g/>link relpreload href/_next/static/css/93ce1fd8c3c80774.css asstyle/>link relstylesheet href/_next/static/css/93ce1fd8c3c80774.css data-n-p/>noscript data-n-css>/noscript>script defer nomodule src/_next/static/chunks/polyfills-c67a75d1b6f99dc8.js>/script>script src/_next/static/chunks/webpack-69bfa6990bb9e155.js defer>/script>script src/_next/static/chunks/framework-4556c45dd113b893.js defer>/script>script src/_next/static/chunks/main-7ca0c04a95757ac7.js defer>/script>script src/_next/static/chunks/pages/_app-2e454f504ce39999.js defer>/script>script src/_next/static/chunks/664-775502a0263923aa.js defer>/script>script src/_next/static/chunks/pages/index-64ab60baca539e16.js defer>/script>script src/_next/static/e0V5hsI3W_yMVjss4avM1/_buildManifest.js defer>/script>script src/_next/static/e0V5hsI3W_yMVjss4avM1/_ssgManifest.js defer>/script>/head>body classantialiased text-lg bg-white dark:bg-gray-900 dark:text-white leading-base>div id__next>span classtheme-bejamas>/span>div classrelative pb-24 overflow-hidden>div classflex flex-col items-center max-w-2xl w-full mx-auto>header classpt-20 pb-12>div classw-12 h-12 rounded-full block mx-auto mb-4 bg-gradient-conic from-gradient-3 to-gradient-4>/div>p classtext-2xl dark:text-white text-center>a href/>Scott Matthewman/a>/p>/header>main classw-full>h1 classtext-3xl lg:text-5xl text-center mb-12>Coding, theatre and other stuff/h1>ul classw-full>li classmd:first:rounded-t-lg md:last:rounded-b-lg backdrop-blur-lg bg-white dark:bg-black dark:bg-opacity-30 bg-opacity-10 hover:bg-opacity-20 dark:hover:bg-opacity-50 transition border border-gray-800 dark:border-white border-opacity-10 dark:border-opacity-10 border-b-0 last:border-b hover:border-b hovered-sibling:border-t-0>a classpy-6 lg:py-10 px-6 lg:px-16 block focus:outline-none focus:ring-4 href/posts/creating-csv-library-for-swift>p classuppercase mb-3 font-bold opacity-60>January 2, 2024/p>h2 classtext-2xl md:text-3xl>Creating a CSV Export library for Swift (Part 1)/h2>p classmt-3 text-lg opacity-60>For My SwiftUI app I needed to export data to CSV. Public open source packages didn't quite fit my needs – so why not write my own?/p>svg xmlnshttp://www.w3.org/2000/svg width24 height24 fillnone viewBox0 0 24 24 classmt-4>path classstroke-current text-primary stroke-linecapround stroke-linejoinround stroke-width2 dM5 12h14M12 19l7-7-7-7>/path>/svg>/a>/li>/ul>/main>footer classpy-16 flex flex-col items-center>p classdark:text-white uppercase mb-3 font-bold opacity-60>© 2024 Scott Matthewman. All rights reserved./p>div classflex mt-6 bg-white justify-center dark:bg-gray-900 rounded-3xl p-1>button typebutton aria-labelUse Dark Mode classflex items-center h-full pr-2 dark:bg-primary rounded-3xl flex justify-center align-center p-2 w-24 h-10 transition>svg xmlnshttp://www.w3.org/2000/svg width21 height20 fillnone viewBox0 0 21 20>path stroke#fff stroke-linecapround stroke-linejoinround stroke-width2 classstroke-current text-gray-400 dark:text-white dM19.5 10.79A9 9 0 119.71 1a7 7 0 009.79 9.79v0z>/path>/svg>/button>button typebutton aria-labelUse Light Mode classflex items-center h-full pr-2 bg-primary dark:bg-transparent rounded-3xl flex justify-center align-center p-2 w-24 h-10 transition>svg xmlnshttp://www.w3.org/2000/svg width25 height24 fillnone viewBox0 0 25 24 classdark:opacity-50>g stroke#fff stroke-linecapround stroke-linejoinround stroke-width2 clip-pathurl(#clip0_192_823)>path dM12.5 17a5 5 0 100-10 5 5 0 000 10zM12.5 1v2M12.5 21v2M4.72 4.22l1.42 1.42M18.86 18.36l1.42 1.42M1.5 12h2M21.5 12h2M4.72 19.78l1.42-1.42M18.86 5.64l1.42-1.42>/path>/g>defs>clipPath idclip0_192_823>path classfill-current text-white dM0 0H24V24H0z transformtranslate(.5)>/path>/clipPath>/defs>/svg>/button>/div>/footer>div classLayout_colorBackground__Adyt4 fixed top-20 opacity-40 dark:opacity-60>/div>div classLayout_colorBackgroundBottom__9Yf_q absolute bottom-0 opacity-20 dark:opacity-10>/div>/div>/div>/div>script id__NEXT_DATA__ typeapplication/json>{props:{pageProps:{posts:{content:\nIm currently building a large and rather complicated app in SwiftUI that involves quite a lot of data. While the app\nhandles most tasks internally, there are some features of spreadsheets like Excel or Numbers that could do a better\njob of analysing the data, and there are some tabular views that Id want to share with people who dont have a copy\nof the app.\n\nOne of the best supported file formats for sharing tabular data is **CSV** (comma separated value). This represents\ntabular data as one line of text per row, with each item separated by commas. For example, the table:\n\n| ID | Doctor | Notes | First episode |\n|----|--------|-------|---------------|\n| 1 | William Hartnell | Later played by Richard Hurndall and David Bradley | 23 November 1963 | \n| 2 | Patrick Troughton | | 5 November 1966 |\n| 3 | Jon Pertwee | | 3 January 1970 |\n| 4 | Tom Baker | No relation to the Sixth Doctor, Colin Baker | 28 December 1974 |\n\nwould be expressed as\n\n```\nID,Full Name,Description,First appearance\n1,William Hartnell,Later played by Richard Hurndall and David Bradley,1963-11-23\n2,Patrick Troughton,,1966-11-05\n3,Jon Pertwee,,1970-01-03\n4,Tom Baker,\No relation to the Sixth Doctor, Colin Baker\,1974-12-28\n```\n\nThe Swift Package Index(https://swiftpackageindex.com/search?querycsv) has a fair few CSV parsers available, and there\nare several that would do the job. But none of them quite works in the way I want, or has the exact feature set that I require.\nSo, why not write my own?\n\nEnter SwiftCSVEncodergithub.\n\n# Basic principles\n\nFrom the start, I wanted my encoder system to support a number of features:\n\n1. **Strongly typed.** A CSV file is a collection of string data, but the information we use to build it may not be. I want to\n be able to rely on the compiler ensuring that I dont accidentally pass something to the encoder that it doesnt know how to\n handle.\n \n2. **Easy to understand syntax.** Code that is understood by computers is all very well – for it to be maintainable, it absolutely\n needs to be readble by humans.\n \n3. **Allows for multiple CSV formats from each object.** `Codable` is the gold standard for encoding objects in Swift – but its\n defined at the object level: there is one and only one way in which an object marked `Codable` is going to be structured in\n output files.\n \n In contrast, I may need multiple CSV files with different combinations of columns for different needs. So while each CSV output\n definition needs to know about its source data, it shouldnt be coupled to it as closely as `Codable` is.\n \n4. **Handles primitive types automatically.** CSV has very common ways of encoding numbers and text strings, so we shouldnt need to\n to do anything extra to work with those data types.\n \n5. **Handles other common data types sensibly.** Some data types used commonly in Swift applications, including `UUID` and `Date`,\n might need a little bit of massaging to get them into a format that CSV encoding can understand. This should be easy to do, even\n when needs might change from file to file. For instance, the CSV specification doesnt specify how dates should be formatted, so\n we may have to tweak those values depending on which application is going to import them.\n \n6. **Doesnt worry about importing CSV files.** Parsing CSV and knowing how to convert those into data objects isnt something I\n need, so I dont want to overcomplicate things by worrying about that. \n \n# Devising a syntax\n\nCSV files represent a **table** of **rows** and **columns**, so lets use those terms in our syntax. Lets start at the end, with\nan idea about the sort of syntax we want to use:\n\n```swift\nlet table CSVTable(\n columns: \n CSVColumn(\ID\, /* how to get the ID */),\n CSVColumn(\Name\, /* how to get the name */),\n // etc.\n \n )\ntable.export(rows: myData)\n```\n\nWhen it comes to \how to get the attribute\ code, this needs to be something that can be applied consistently for every record in the\ncollection were exporting. So lets provide a block that takes the `row` object, and returns a value:\n\n\n```swift\nlet table CSVTable(\n columns: \n CSVColumn(\ID\, attribute: { row in row.id }), // { $0.id } would also work\n // trailing block syntax also works\n CSVColumn(\Name\) { row in row.name } \n // etc.\n \n )\ntable.export(rows: myData)\n```\n\nWe can instantly see this is flexible: if we want to add a persons full name but only have their separate first and last names, we\ncould calculate this in the block:^1\n\n```swift\nCSVColumn(\Full Name\) { $0.firstName, $1.lastName.joined(separator: \ \) }\n```\n\nWe might also want a shorthand approach - if all the block is doing is retrieving an attribute, we could have the option of using a\nkey path. For example:\n\n```swift\nCSVColumn(\ID\, \\.id)\n```\n\nFor all this to work, though, `CSVTable` and `CSVColumn` will need to know the data type of the row were using as a source. Thankfully\nSwifts generics will be able to help us here:\n\n```swift\nlet myData: Person /* ... */\n\nlet table CSVTable\u003cPerson\u003e(\n columns: \n CSVColumn(\ID\, \\.id),\n CSVColumn(\Name\, \\.name) \n // etc.\n \n )\ntable.export(rows: myData)\n```\n\n`CSVColumn` will also need to know the type of record its dealing with, but we should be able to construct our definitions in such a way\nthat the compiler will automatically infer that for us. If were defining a `CSVTable` for `Person`, then it stands to reason that the\ntables `CSVColumn` records also relate to `Person` objects.\n\nSo thats the basic top level syntax done. Time to get into the code.\n\n### Building up the code\n\nBefore we define the structure for `CSVTable` and `CSVColumn` we need to think about what those definition blocks will actually return.\nWe could define the block as having to return a `String`, which would work just fine for properties that were strings themselves:\n\n```swift\nCSVColumn\u003cPerson\u003e(\Name\) { $0.name }\n```\n\nBut for a property that was any other type thatd be a bit of a faff:\n\n```swift\nCSVColumn\u003cPerson\u003e(\Age\) { String($0.age) }\n```\n\nWhat we can do instead is a define a protocol, which Im going to call `CSVEncodable`. Data types that conform to `CSVEncodable` will need\nto define how to turn themselves into a string. For example:\n\n```swift\nprotocol CSVEncodable {\n func encode() -\u003e String\n}\n\nextension String: CSVEncodable {\n func encode() -\u003e String { self }\n}\nextension Int: CSVEncodable {\n func encode() -\u003e String { String(self) }\n}\nextension UUID: CSVEncodable {\n func encode() -\u003e String { uuidString }\n}\n// etc.\n```\n\nThis means that I can then define my columns to accept a block that takes a record, and returns any data type that conforms to `CSVEncodable`:\n\n```swift\nstruct CSVColumn\u003cRecord\u003e {\n var header: String\n var attribute: (Record) -\u003e CSVEncodable\n \n init(_ header: String, attribute: @escaping (Record) -\u003e CSVEncodable) {\n self.header header\n self.attribute attribute\n }\n}\n```\n\nAnd we can add an alternative initializer for the keypath-based approach, telling the compiler that the keypath must point to an attribute of\nour `Record` generic type, and the attribute must conform to `CSVEncodable`:\n\n```swift\nextension CSVColumn {\n init\u003cT: CSVEncodable\u003e(_ header: String, _ keyPath: KeyPath\u003cRecord, T\u003e) {\n self.init(header, attribute: { $0keyPath: keyPath })\n }\n}\n```\n\nThis makes `CSVTable`s initial definition relatively easy:\n\n```swift\nstruct CSVTable\u003cRecord\u003e {\n var columns: CSVColumn\u003cRecord\u003e\n}\n```\n\n\u003cbr/\u003e\n\n# Putting it together\n\nSo now we have our CSV table defined, its time to generate the output!\n\nThat basically boils down to:\n\n1. Loop through a collection of items\n2. For each item, loop through all the `CSVColumn` entries and collect the output of the `attribute` blocks\n3. Join those items up with commas to form a single line per item\n4. Join those lines with line breaks\n\nSo that may look like:\n\n```swift\nextension CSVTable {\n func export(rows: Record) -\u003e String {\n // loop through all rows, and collect the results\n let csvRows rows.map { record in\n // loop through all columns, encode them to Strings and collect the results \n let values columns.map { column in\n column.attribute(record).encode()\n }\n return values.joined(separator: \,\)\n }\n return csvRows.joined(separator: \\\r\\n\)\n }\n}\n```\n\nAnd that, at its core, is our CSV encoder pretty much done. We create a definition of how the table of CSV elements is created, we give it\na collection of data objects, and get a list of comma-separated results back.\n\nTheres slightly more to account for before this encoder is fully working though. Some of the things not covered here:\n\n- Adding a header row to the start of the CSV file\n- Dealing with differing date format needs and other variations\n- How to ensure that strings including special characters (including commas and quote marks) dont break the CSV format\n\nThose things are already handled by SwiftCSVEncodergithub so if you want to see how Ive handled them, feel free to browse the source code.\nOtherwise, Ill be back to cover these and other tune-ups in a second blog post. \n\n^1: Its worth pointing out that treating names in this way is poor for internationalisation purposes, as it presumes a lot about\n how peoples names are structured across the globe. A better approach would be to think of \given name\ and \family name\, and\n use Apples `PersonNameComponentsFormatter`(https://developer.apple.com/documentation/foundation/personnamecomponentsformatter#)\n to build up a culturally appropriate construction. Or if you dont really have a reason why first name and last name need to be\n treated separately, keep everything in a `name` field.\n \ngithub: https://github.com/scottmatthewman/swiftcsvencoder,data:{title:Creating a CSV Export library for Swift (Part 1),description:For My SwiftUI app I needed to export data to CSV. Public open source packages didnt quite fit my needs – so why not write my own?,date:January 2, 2024},filePath:creating-csv-library-for-swift.mdx},globalData:{name:Scott Matthewman,blogTitle:Coding, theatre and other stuff,footerText:© 2024 Scott Matthewman. All rights reserved.}},__N_SSG:true},page:/,query:{},buildId:e0V5hsI3W_yMVjss4avM1,isFallback:false,gsp:true,scriptLoader:}/script>/body>/html>
View on OTX
|
View on ThreatMiner
Please enable JavaScript to view the
comments powered by Disqus.
Data with thanks to
AlienVault OTX
,
VirusTotal
,
Malwr
and
others
. [
Sitemap
]