{"id":16686283,"url":"https://github.com/evermeer/evcloudkitdao","last_synced_at":"2025-04-04T15:08:35.420Z","repository":{"id":18881354,"uuid":"22098838","full_name":"evermeer/EVCloudKitDao","owner":"evermeer","description":"Simplified access to Apple's CloudKit","archived":false,"fork":false,"pushed_at":"2019-08-19T11:25:56.000Z","size":2937,"stargazers_count":644,"open_issues_count":7,"forks_count":66,"subscribers_count":22,"default_branch":"master","last_synced_at":"2024-10-19T19:35:26.013Z","etag":null,"topics":["apple-cloudkit","cloudkit","evreflection","swift"],"latest_commit_sha":null,"homepage":null,"language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/evermeer.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-07-22T09:46:18.000Z","updated_at":"2024-08-27T02:29:50.000Z","dependencies_parsed_at":"2022-07-12T22:10:28.686Z","dependency_job_id":null,"html_url":"https://github.com/evermeer/EVCloudKitDao","commit_stats":null,"previous_names":[],"tags_count":102,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evermeer%2FEVCloudKitDao","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evermeer%2FEVCloudKitDao/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evermeer%2FEVCloudKitDao/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/evermeer%2FEVCloudKitDao/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/evermeer","download_url":"https://codeload.github.com/evermeer/EVCloudKitDao/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247198460,"owners_count":20900080,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["apple-cloudkit","cloudkit","evreflection","swift"],"created_at":"2024-10-12T15:05:17.266Z","updated_at":"2025-04-04T15:08:35.399Z","avatar_url":"https://github.com/evermeer.png","language":"Swift","readme":"EVCloudKitDao\n=============\n\n\u003c!---\n[![Circle CI](https://img.shields.io/circleci/project/evermeer/EVCloudKitDao.svg?style=flat)](https://circleci.com/gh/evermeer/EVCloudKitDao)\n --\u003e\n[![Build Status](https://travis-ci.org/evermeer/EVCloudKitDao.svg?style=flat\u0026branch=master)](https://travis-ci.org/evermeer/EVCloudKitDao)\n[![Issues](https://img.shields.io/github/issues-raw/evermeer/EVCloudKitDao.svg?style=flat)](https://github.com/evermeer/EVCloudKitDao/issues)\n[![Stars](https://img.shields.io/github/stars/evermeer/EVCloudKitDao.svg?style=flat)](https://github.com/evermeer/EVCloudKitDao/stargazers)\n[![Documentation](https://img.shields.io/badge/documented-100%-brightgreen.svg?style=flat)](http://cocoadocs.org/docsets/EVCloudKitDao)\n[![Version](https://img.shields.io/cocoapods/v/EVCloudKitDao.svg?style=flat)](http://cocoadocs.org/docsets/EVCloudKitDao)\n[![Awesome](https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg)](https://github.com/matteocrippa/awesome-swift#other-data)\n\n[![Language](https://img.shields.io/badge/language-swift3-f48041.svg?style=flat)](https://developer.apple.com/swift)\n[![Platform](https://img.shields.io/cocoapods/p/EVCloudKitDao.svg?style=flat)](http://cocoadocs.org/docsets/EVCloudKitDao)\n[![Support](https://img.shields.io/badge/support-iOS%208%2B%20|%20OSX%2010.9+%20-blue.svg?style=flat)](https://www.apple.com/nl/ios/)\n[![License](https://img.shields.io/cocoapods/l/EVCloudKitDao.svg?style=flat)](http://cocoadocs.org/docsets/EVCloudKitDao)\n\n[![Git](https://img.shields.io/badge/GitHub-evermeer-blue.svg?style=flat)](https://github.com/evermeer)\n[![Twitter](https://img.shields.io/badge/twitter-@evermeer-blue.svg?style=flat)](http://twitter.com/evermeer)\n[![LinkedIn](https://img.shields.io/badge/linkedin-Edwin%20Vermeer-blue.svg?style=flat)](http://nl.linkedin.com/in/evermeer/en)\n[![Website](https://img.shields.io/badge/website-evict.nl-blue.svg?style=flat)](http://evict.nl)\n[![eMail](https://img.shields.io/badge/email-edwin@evict.nl-blue.svg?style=flat)](mailto:edwin@evict.nl?SUBJECT=About%20EVCloudKitDao)\n\nDiscuss EVCloudKitDao : [![Join the chat at https://gitter.im/evermeer/EVCloudKitDao](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/evermeer/EVCloudKitDao?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge)\n\n## What is this\nWith Apple CloudKit, you can focus on your client-side app development and let iCloud eliminate the need to write server-side application logic. CloudKit provides you with Authentication, private and public database, structured and asset storage services - all for free with very high [limits](https://developer.apple.com/icloud/documentation/cloudkit-storage/). For more information see [Apple CloudKit documentation](https://developer.apple.com/icloud/index.html)\n\nThis is a library to simplify the access to Apple's CloudKit data and notifications (see a more detailed description below)\n \n- EVCloudKitDao.swift for if you want control over your in app data and notifications. \n- EVCloudData.swift will let you handle CloudKit data as easy as possible. \n- EVglobal.swift for a couple of easy to use global bridging functions (EVLog and EVtry)\n\nThere is a dependency with [EVReflection](https://github.com/evermeer/EVReflection) This will automatically be setup if you are using cocoapods.\n- [EVReflection](https://github.com/evermeer/EVReflection) for if you want easy to use reflection methods. (not only for CloudKit)\n\nSee the Quick Help info for method descriptions or the documentation at [cocoadocs.org](http://cocoadocs.org/docsets/EVCloudKitDao/)\n\nThe AppMessage demo is a complete functional messaging app based on CloudKit:\n\n- News items are fully functional. Just try adding, deleting and updating newsitems from the CloudKit dashboard. \n- The Contacts list based on your phone contacts that also have installed the app. \n- Chat with someone using text messages, pictures and sending your location\n- A search window (async autocomplete) where you can search all chat messages using a tokenized or begiswith query\n- It also has TestViewController.swift for an overview of the functionality\n\nI'm looking for feedback. Please let me know if you want something changed or added to the library or the demo.\n\n\n## A picture says more than 1000 words\nHere are screenshots of the included demo app chat functionality:\n\n![Screenshot0](https://github.com/evermeer/EVCloudKitDao/blob/master/Screenshots/Screenshot.png?raw=true)\n![Screenshot1](https://github.com/evermeer/EVCloudKitDao/blob/master/Screenshots/Screenshot2.png?raw=true)\n\n\n![Screenshot2](https://github.com/evermeer/EVCloudKitDao/blob/master/Screenshots/Screenshot3.PNG?raw=true)\n![Screenshot3](https://github.com/evermeer/EVCloudKitDao/blob/master/Screenshots/Screenshot4.PNG?raw=true)\n\n## Documentation ##\nDocumentation is now available at [cocoadocs.org](http://cocoadocs.org/docsets/EVCloudKitDao/)\n\n\n## Main features of EVCloudKitDao:\n- simple singleton access to your public or private database and containers (default and named)\n- Object mapping: You do not have to parse from and to CKRecord (mapping is based on reflection, including system fields)\n- Generic and simplified query handling\n- Error handling (separate completionHandler and errorHandler code blocks)\n- Storing CKReference objects\n- Storing CKAsset objects\n- Optionally auto continue reading from cursor (batch query)\n- Organising subscription\n- Handling incoming notifications\n- (Re)setting the badge\n\n## Main features of EVCloudData:\n- Use just one predicate for a query, subscription and processing incoming notifications.\n- it's only one method call with a couple of callback events (optional which to use)\n- it will store the fetched data collection in memory.\n- notifications will update the data collections and call the appropriate events.\n- local updates will also update the data collection and call the appropriate events\n- since all data is processed all callback events will be executed on the mainQueue\n- caching of the results to a file for speedy app restart. (You can set the caching strategy) \n- Internal app notifications using NSNotificationCenter\n\n## Main features of EVglobal\n- EVLog as a replacement for NSLog which will also output the file, function and line number.\n\n## Known issues (Swift limitations) ##\n- If you add a property to your object of type CKReference, then also add a property of type String for the RecordID.recordName. You could add a setter for populating both properties. Then if you query this using a NSPredicate, then query the string field and not the CKReference field. You have to do this because a NSPredicate works difrently for NSCloudkit than for an object. The EVCloudData class needs them to function in the same way. For a sample, see the Message class.\n\n- Optional objects properties can now be used. Optional type properties not. Swift is not able to do a .setValue forKey on an optional like Int? or Double? As a workaround for this you could use a NSNumber? This limitation is part of [EVReflection](https://github.com/evermeer/EVReflection)\n\n## External components for the demo\nThe AppMessage demo is using the following components which can be installed using CocoaPods. See instructions below.\nBecause of dependency compatibility the AppMessage demo requires Xcode 6.2 or later.\n\n- [EVReflection](https://github.com/evermeer/EVReflection) - Swift helper library with reflection functions\n- [SSASideMenu](https://github.com/SSA111/SSASideMenu) - A Swift implementation of RESideMenu\n- [JSQMessagesViewController](https://github.com/jessesquires/JSQMessagesViewController) - An elegant messages UI library\n- [JSQSystemSoundPlayer](https://github.com/jessesquires/JSQSystemSoundPlayer) - A fancy Obj-C wrapper for iOS System Sound Services\n- [CRToast](https://github.com/cruffenach/CRToast) - A modern iOS toast view that can fit your notification needs\n- [UIImage-Resize](https://github.com/AliSoftware/UIImage-Resize) - Category to add some resizing methods to the UIImage class, to resize it to a given CGSize — or fit in a CGSize keeping aspect ratio\n- [SwiftLocation](https://github.com/malcommac/SwiftLocation) - iOS CoreLocation Wrapper made in Swift\n- [UzysAssetsPickerController](https://github.com/uzysjung/UzysAssetsPickerController) - Alternative UIImagePickerController , You can take a picture with camera and choose multiple photos and videos\n- [VIPhotoView](https://github.com/vitoziv/VIPhotoView) - View a photo with simple and basic interactive gesture\n- [Async](https://github.com/duemunk/Async) Syntactic sugar in Swift for asynchronous dispatches in Grand Central Dispatch\n- [PermissionScope](https://github.com/nickoneill/PermissionScope) - Intelligent iOS permissions UI and unified API\n\nBesides these the dependency to EVCloudKitDao has been skipped by just using the classes directly\n- [EVCloudKitDao](https://github.com/evermeer/EVCloudKitDao) - Simplified access to Apple's CloudKit\n\n\n## Using EVCloudKitDao or EVCloudData in your own App \n\n'EVCloudKitDao' is now available through the dependency manager [CocoaPods](http://cocoapods.org). \nYou do have to use cocoapods version 0.36. At this moment this can be installed by executing:\n\n```\n[sudo] gem install cocoapods\n```\n\nIf you have installed cocoapods version 0.36 or later, then you can just add EVCloudKitDao to your workspace by adding the folowing 2 lines to your Podfile:\n\n```ruby\nuse_frameworks!\npod \"EVCloudKitDao\"\n```\n\nI have now moved on to Swift 2. If you want to use EVCloudKitDao with Swift 1.2, then get that version by using the podfile command:\n```ruby\nuse_frameworks!\npod 'EVReflection', :git =\u003e 'https://github.com/evermeer/EVReflection.git', :branch =\u003e 'Swift1.2'\npod 'SwiftTryCatch'\npod 'EVCloudKitDao', '~\u003e 2.6'\n```\n\n\nVersion 0.36 of cocoapods will make a dynamic framework of all the pods that you use. Because of that it's only supported in iOS 8.0 or later. When using a framework, you also have to add an import at the top of your swift file like this:\n\n```swift\nimport EVCloudKitDao\n```\n\nIf you want support for older versions than iOS 8.0, then you can also just copy the Cloudkit folder containing the 5 classes EVCloudKitDao, EVCloudData, EVReflection, EVCloudData and EVglobal to your app.\n\nWhen you have added EVCloudKitDao to your project, then have a look at the AppMessage code for how to implement push notifications and how to connect to CloudKit data (see AppDelegate.swift and LeftMenuViewController.swift) For contacts see the RightMenuViewController.swift and for other usage see the TestsViewController.swift\n\n\n## Building the AppMessage demo\n\n1) Clone the repo to a working directory\n\n2) [CocoaPods](http://cocoapods.org) is used to manage dependencies. Pods are setup easily and are distributed via a ruby gem. Follow the simple instructions on the website to setup. After setup, run the following command from the toplevel directory of AppMessage to download the dependencies for AppMessage:\n\n```sh\npod install\n```\n\n3) Open the `AppMessage.xcworkspace` in Xcode.\n\n4) Go to AppMessage target settings and update the:\n\n- bundle name (usually your own reversed domain)\n- Change the team settings (your own certificate that is enabled for push notifications)\n- fix the iCloud capabilities. (check key-value store and CloudKit with a default container)\n- fix the capabilities for Background Modes 'Background fetch' and 'Remote notifications'\n\n5) Build and Run the app. In the AppDelegate there is a call to initiate all objects (createRecordTypes). All required CloudKit objects will be created.\n\n6) Open the CloudKit dashboard, select all recordtypes and enable all 'Metadata Indexes'\n\n7) Disable the call to .createRecordTypes in AppDelegate and run the app again.\n\n8) Make sure you run the app on 2 devices, each using a diverent iCloud account and each device having the other account in it's contact list.\n\n\nand you are ready to go!\n\n## How to use the EVCloudData\nBelow is all the code you need to setup a news feed including push notification handling for any changes.\n\n\n```swift\n// Just enherit from CKDataObject so that you have access to the CloudKit metadata\nclass News : CKDataObject {\n    var Subject : String = \"\"\n    var Text : String = \"\"\n}\n\nclass AppDelegate: UIResponder, UIApplicationDelegate {\n    var window: UIWindow?\n\n    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: NSDictionary?) -\u003e Bool {\n        // Make sure we receive subscription notifications\n        application.registerUserNotificationSettings(UIUserNotificationSettings(forTypes: .Alert | .Badge | .Sound, categories: nil))\n        application.registerForRemoteNotifications()\n        return true\n    }\n\n    func application(application: UIApplication!, didReceiveRemoteNotification userInfo: [NSObject : NSObject]!) {\n        EVLog(\"Push received\")\n        EVCloudData.publicDB.didReceiveRemoteNotification(userInfo, {\n            EVLog(\"Not a CloudKit Query notification.\")            \n        })\n    }\n\n    func applicationDidEnterBackground(application: UIApplication) {\n        // If you do a backup then this backup will be reloaded after app restart.\n        EVCloudData.publicDB.backupData()        \n    }\n}\n\n\nclass LeftMenuViewController: UIViewController {\n    var newsController: NewsViewController!\n\n    override func viewDidLoad() {\n        super.viewDidLoad()\n        connectToNews()\n        // Only already setup CloudKit connect's will receive these notifications (like the News above)\n        EVCloudData.publicDB.fetchChangeNotifications()        \n    }\n\n    deinit {\n        EVCloudData.publicDB.disconnect(\"News_All\")\n    }\n\n    func connectToNews() {\n        EVCloudData.publicDB.connect(News()\n        , predicate: NSPredicate(value: true)\n        , filterId: \"News_All\"\n        , configureNotificationInfo: { notificationInfo in\n            notificationInfo.alertBody = \"New news item\"\n            notificationInfo.shouldSendContentAvailable = true }\n        , completionHandler: { results in\n            EVLog(\"There are \\(results.count) existing news items\")\n            self.newsController.tableView.reloadData()\n            return results.count \u003c 200 // Continue reading if we have less than 200 records and if there are more.\n        }, insertedHandler: {item in\n            Helper.showStatus(\"New News item: '\\(item.Subject)'\")\n            self.newsController.tableView.reloadData()\n        }, updatedHandler: {item in\n            Helper.showStatus(\"Updated News item:'\\(item.Subject)'\")\n            self.newsController.tableView.reloadData()\n        }, deletedHandler: {recordId in\n            Helper.showStatus(\"News item was removed\")\n            self.newsController.tableView.reloadData()\n        }, dataChangedHandler : {\n            EVLog(\"Some News data was changed\")\n        }, errorHandler: {error in\n            Helper.showError(\"Could not load news: \\(error.description)\")\n        })\n    }\n}\n\n\nclass NewsViewController : UIViewController, UITableViewDataSource, UITableViewDelegate, RESideMenuDelegate {\n\t...\n    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -\u003e UITableViewCell {\n        var cell:UITableViewCell! = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as? UITableViewCell\n        ...\n        //This line all you need to get the correct data for the cell\n        var news:News = EVCloudData.publicDB.data[\"News_All\"]![indexPath.row] as News\n\n        cell.textLabel?.text = news.Subject\n        cell.detailTextLabel?.text = news.Body\n        return cell;\n    }\n}\n\n```\n\n\n\n## How to use the EVCloudKitDao\n```swift\n// Just enherit from CKDataObject so that you have access to the CloudKit metadata\nclass Message : CKDataObject {\n    var From : String = \"\"\n    var To : String = \"\"\n    var Text : String = \"\"\n}\n\nlet dao: EVCloudKitDao = EVCloudKitDao.publicDB\nlet dao2 = EVCloudKitDao.publicDBForContainer(\"iCloud.nl.evict.myapp\")\n\nvar message = Message()\nmessage.From = \"me@me.com\"\nmessage.To = \"you@me.com\"\nmessage.Text = \"This is the message text\"\n\ndao.saveItem(message, completionHandler: {record in\n        createdId = record.recordID.recordName;\n        EVLog(\"saveItem : \\(createdId)\");\n    }, errorHandler: {error in\n        EVLog(\"\u003c--- ERROR saveItem\");\n    })\n\ndao.query(Message()\n    , completionHandler: { results in\n        EVLog(\"query : result count = \\(results.count)\")\n    }, errorHandler: { error in\n        EVLog(\"\u003c--- ERROR query Message\")\n    })\n```\n\n## All you need for a keyword search (async autocomplete)\n```swift\nvar queryRunning:Int = 0\nvar data:[Message] = []\n\nfunc searchDisplayController(controller: UISearchDisplayController!, shouldReloadTableForSearchString searchString: String!) -\u003e Bool {\n    self.filterContentForSearchText(searchString)\n    return false\n}\n\nfunc searchDisplayController(controller: UISearchDisplayController!, shouldReloadTableForSearchScope searchOption: Int) -\u003e Bool {\n    self.filterContentForSearchText(self.searchDisplayController!.searchBar.text)\n    return false\n}\n\nfunc filterContentForSearchText(searchText: String) {\n    EVLog(\"Filter for \\(searchText)\")\n    networkSpinner(1)\n    EVCloudKitDao.publicDB.query(Message(), tokens: searchText, completionHandler: { results in\n        EVLog(\"query for tokens '\\(searchText)' result count = \\(results.count)\")\n        self.data = results\n        NSOperationQueue.mainQueue().addOperationWithBlock {\n            self.searchDisplayController!.searchResultsTableView.reloadData()\n            self.tableView.reloadData()\n            self.networkSpinner(-1)\n        }\n    }, errorHandler: { error in\n        EVLog(\"ERROR: query Message for words \\(searchText)\")\n        self.networkSpinner(-1)\n    })\n}\n\nfunc networkSpinner(adjust: Int) {\n    self.queryRunning = self.queryRunning + adjust\n    UIApplication.sharedApplication().networkActivityIndicatorVisible = self.queryRunning \u003e 0\n}\n\noverride func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -\u003e Int {\n    return data.count\n}\n\noverride func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -\u003e UITableViewCell {\n    let cellIdentifier = \"Folowin_Search_Cell\";\n    var cell:UITableViewCell! = tableView.dequeueReusableCellWithIdentifier(cellIdentifier) as? UITableViewCell\n    if cell == nil {\n        cell = UITableViewCell(style: .Subtitle, reuseIdentifier: cellIdentifier)\n    }\n\n    var item:Message = data[indexPath.row]\n    cell.textLabel?.text = item.Text\n    return cell;\n}\n\n```\n\n## Error handling\nAll cloudkit function have an errorHandler codeblock. You should handle the error appropriate. There is a helper function for getting a functional error status. In most cases you would get something like the code below. When you are doing data manupilations you should also handle the .RecoverableError\n\n```swift\nfunc initializeCommunication(retryCount: Double = 1) {\n    ...\n    }, errorHandler: { error in\n        switch EVCloudKitDao.handleCloudKitErrorAs(error, retryAttempt: retryCount) {\n            case .Retry(let timeToWait):\n                Async.background(after: timeToWait) {\n                    self.initializeCommunication(retryCount: retryCount + 1)\n                }\n            case .Fail:\n                if error.code == .limitExceeded {\n                    //TODO: try again with a smaller load?\n                }            \n                Helper.showError(\"Could not load messages: \\(error.localizedDescription)\")\n            default: // For here there is no need to handle the .Success, and .RecoverableError\n                break\n        }\n    })\n}\n```\n\n\n## License\n\nEVCloudKitDao is available under the MIT license. See the LICENSE file for more info.\n\n## My other libraries:\nAlso see my other open source iOS libraries:\n\n- [EVReflection](https://github.com/evermeer/EVReflection) - Reflection based (Dictionary, CKRecord, JSON and XML) object mapping with extensions for Alamofire and Moya with RxSwift or ReactiveSwift \n- [EVCloudKitDao](https://github.com/evermeer/EVCloudKitDao) - Simplified access to Apple's CloudKit\n- [EVFaceTracker](https://github.com/evermeer/EVFaceTracker) - Calculate the distance and angle of your device with regards to your face in order to simulate a 3D effect\n- [EVURLCache](https://github.com/evermeer/EVURLCache) - a NSURLCache subclass for handling all web requests that use NSURLReques\n- [AlamofireOauth2](https://github.com/evermeer/AlamofireOauth2) - A swift implementation of OAuth2 using Alamofire\n- [EVWordPressAPI](https://github.com/evermeer/EVWordPressAPI) - Swift Implementation of the WordPress (Jetpack) API using AlamofireOauth2, AlomofireJsonToObjects and EVReflection (work in progress)\n- [PassportScanner](https://github.com/evermeer/PassportScanner) - Scan the MRZ code of a passport and extract the firstname, lastname, passport number, nationality, date of birth, expiration date and personal numer.\n- [AttributedTextView](https://github.com/evermeer/AttributedTextView) - Easiest way to create an attributed UITextView with support for multiple links (url, hashtags, mentions).\n\n\n## Evolution of EVReflection (Gource Visualization)\n[![Evolution of EVReflection (Gource Visualization)](https://img.youtube.com/vi/1MQd1tlE8mM/0.jpg)](https://www.youtube.com/watch?v=1MQd1tlE8mM)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fevermeer%2Fevcloudkitdao","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fevermeer%2Fevcloudkitdao","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fevermeer%2Fevcloudkitdao/lists"}