Swiftpack.co - Swift Packages by mergesort

Swiftpack.co is a collection of thousands of indexed Swift packages. Search packages.

Packages published by mergesort

mergesort/Boutique 2.4.3
✨ A magical persistence library (and so much more) for state-driven iOS and Mac apps ✨
⭐️ 861
🕓 8 weeks ago
🔖 Release Notes

Releases

The markdown parsing is broken/disabled for release notes. Sorry about that, I'm chasing the source of a crash that's been bringing this website down for the last couple of days.
*Don't* Remove All
8 weeks ago
> [!IMPORTANT] > This release contains a crucial fix, please update your library. This release fixes an bug in Boutique that could lead to data-loss in specific circumstances when chaining `.remove()` and `.insert()` using Boutique. Boutique was exhibiting incorrect behavior when chaining the `remove()` function with an `insert()` after, due to an underlying implementation bug. The code below demonstrates how the bug would manifest. ```swift // We start with self.items = [1, 2, 3, 4, 5, 6, 7, 8] // An API call is made and we receive [1, 2, 3, 8, 9, 10] to be inserted into to self.items. // We pass that `updatedItems` array into an `update` function that removes any items that need to be removed, and then inserts the newly updated items. func update(_ updatedItems: [Int]) async throws { let items = self.items.filter({ updatedItems.contains($0) }) try await self.$items .remove(items) .insert(updatedItems) .run() } // `self.items` now should be [1, 2, 3, 4, 5, 6, 7, 8] // `self.items` is actually [10] ``` There was an assumption built into how chained operations work, based on how Boutique was being used in the early days of the library. Internally Boutique has two `ItemRemovalStrategy` properties, `.removeAll` which removes all the items by deleting the underlying table, and `removeItems(items)` to remove a specific set of items. Unfortunately due to a logic error `.removeAll` would be called whenever the amount of items to remove matched the amount of items that were being inserted in a chain, which is not always the developer's intention. That would delete the underlying data and insert the last item, leaving users with only one item. My sincerest apologies for this bug, and since this pattern is not necessarily common I hope that it has not affected many users.
Your Presence Is Requested
15 weeks ago
`StoredValue` and `AsyncStoredValue` have a new API when the `Item` stored is an `Array`. The new `togglePresence` function is a handy little shortcut to insert or remove an item from a `StoredValue` (or `AsyncStoredValue`) based on whether the currently `StoredValue` already contains that value. It's very simple to use. ```swift self.$redPandas.togglePresence(.pabu) ``` 1. If `pabu` isn't in the array of red pandas then Pabu will be inserted. 2. If `pabu` is in the array of red pandas then Pabu will be removed. Why add this function? I found myself reaching for a function of this shape when interacting with stateful interfaces in SwiftUI, and thought it would make your life easier as it's made mine. 🦊
At Your Service (And Group)
27 weeks ago
Boutique's `SecurelyStoredValue` is meant to be a simple layer to over a whole complex set of keychain APIs to build a simple solution for simple use cases. Occasionally a little additional complexity is valuable though, complexity that allows for more powerful use cases. This release provides two new properties when initializing a `SecurelyStoredValue`, `group` and `service`. These two properties represent a Keychain's group and a Keychain's service, which control how and where data is stored in the system Keychain. The `group` and `service` properties are of types `KeychainGroup` and `KeychainService` respectively. > [!NOTE] > Previously no `group` was ever set, and the `service` always mapped to `Bundle.main.bundleIdentifier`. This made it so values could not be shared between two targets (for example an app and a widget). The same `SecurelyStoredValue` would have a different bundle identifier based on where the value was being accessed, and would return no value for one target's valid keychain entry. The `group` and `service` properties are optional so you can keep your code the same way it was before. ```swift @SecurelyStoredValue<AuthToken>(key: "authToken") ``` Or if you'd like to share a value across targets, you can use the `group` or `service` parameters, or both together. ```swift @SecurelyStoredValue<AuthToken>(key: "authToken", group: keychainGroup) @SecurelyStoredValue<AuthToken>(key: "authToken", service: keychainService) @SecurelyStoredValue<AuthToken>(key: "authToken", service: keychainService, group: keychainGroup) ``` Both `KeychainGroup` and `KeychainService` conform to `ExpressibleByStringLiteral`, so you can also use a string in place of these types. ```swift @SecurelyStoredValue<AuthToken>(key: "authToken", service: "com.boutique.service", group: "com.boutique.group") ``` Now let's go be more secure than ever!
No YOU'RE Insecure
30 weeks ago
This is a big release, adding a new `@SecurelyStoredValue` property wrapper to make Boutique a one stop shop for all your persistence needs. The `@SecurelyStoredValue` property wrapper can do everything a `@StoredValue` does, but instead of persisting values in `UserDefaults` a `@SecurelyStoredValue` will save items in the system's Keychain. This is perfect for storing sensitive values such as passwords or auth tokens, which you would not want to store in `UserDefaults`. --- Using a `SecurelyStoredValue` is drop dead simple. Declare the property: ```swift @SecurelyStoredValue<String>(key: "authToken") private var authToken ``` Set a value: ```swift $authToken.set("super_secret_p@ssw0rd") ``` And now it's ready to use anywhere you need. ```swift self.apiController.authenticatedAPICall(withToken: self.authToken) ``` --- Breaking change: - One small API update in this release, `@StoredValue`'s `set` and `reset` functions are now bound to the `@MainActor`. This is to prevent race conditions that could occur when attempting to modify `StoredValue`'s `publisher` property.
Self-Evident Truths: All Stores Are Created Equal
31 weeks ago
This release makes a few subtle improvements to improve some of Boutique's ergonomics and potential race conditions. - Removing the `Equatable` constraint on a Store's `Item`, now all `Item` has to conform to is `Codable`. - Thank you @connor-ricks so much for #53! - Adding a `do/catch` in `loadStoreTask` to make debugging Store load failures easier. This isn't strictly necessary but I found myself doing this often when I couldn't figure out why a Store was throwing an error, and thought it might be helpful to expose. - `StoredValue` is now bound to `@MainActor`, which is more in line with expectations. - This also addresses any potential race conditions where the `publisher` property could emit at a different time than the underlying change to `UserDefaults` occurred.
Wait Up A Second…
1 year ago
The highlight of this release a new async initializer for a Boutique `Store`, thanks to the contribution of @rl-pavel. This initializer solves two problems. 1. Previously when an app was starting up you would have to wait for a `Store` to finish loading before moving onto your next task, ostensibly acting as a blocking procedure. The `Store` was fast so it was not very noticeable from a performance perspective, but depending on the state-driven interface you were constructing and how big your `Store` was, it could be noticeable. 2. The main problem this caused was not being able to tell whether the items in your `Store` still hadn't loaded, or if they had loaded with zero items. I call this the empty state problem, where you would see your empty state screen displayed for a split second, and then your items would load into place. This was a suboptimal experience, but is now a thing of the past. You shouldn't notice any changes when using the Store's initializer, but you will now have this fancy method that shows you if the store has finished loading. ```swift await store.itemsHaveLoaded() ``` --- What this allows you to do is to drive a SwiftUI/UIKit/AppKit view based on the Store's state. A simplified example looks like this. ```swift struct ItemListView: View { @State private var itemsHaveLoaded = false var body: some View { VStack { AlwaysVisibleBanner() if self.itemsHaveLoaded { if self.items.isEmpty { EmptyStateView() } else { ItemView(items: self.items) } } else { LoadingStateView() } } .task({ try await self.itemsController.items.itemsHaveLoaded() self.itemsHaveLoaded = true }) } } ``` This is a a really readable solution to a tricky problem, so once again, thank you Pavel. 👏🏻 --- ### Breaking Changes - `StoredValue.binding` is now computed property rather than a `StoredValue.binding()` function. - I've added back the `Store.Operation.add` functions which allowed for chained operations, they were accidentally marked as deprecated, oops.
Little Touches
1 year ago
The work never stops at the Boutique! One touch up, and one oops to fix. (My bad…) - In 2.0.4 I added a `binding()` function on `@StoredValue` and `@AsyncStoredValue`, it's now a computed property. - In 2.1.0 I accidentally removed the `add` function from `Store.Operation`, now it's back. It will be deprecated later, not now.
Goodbye Add, Hello Insert
1 year ago
As discussed in #36, the Store's `add` function can be a little bit ambiguously named. When you call `add` on the `Store` it will either add an item or update an existing item, if that item already exists in the `Store. Naming this function `add` makes sense if you think of the `Store` as a bag of items that you can add or remove items from, but when an update occurs, the name is no longer as obvious. Having had a few months to use Boutique in production I've come to believe that `insert` is a better and less ambiguous name than `add`. The cool thing about being the benevolent dictator of Boutique is that I can decide to treat the `Store` like a set if the Store is going to act like a `Set`. (I also consulted with many developers to get their feedback, I'm not some kind of monster.) From this day forward `add` will be renamed `insert`, to match how Swift's naming convention in [`Set`](https://developer.apple.com/documentation/swift/set/insert(_:)-nads). The functionality of the `add` and `insert` are identical, which means that `add` will continue to work for some time going forward (with a warning in Xcode), and migrating to `insert` will change none of your app's functionality. Even though you don't have to migrate yet, migrating will be as simple as changing one line of code. ```swift // Before store.add(item) // After store.insert(item) ``` This process will be made even easier by providing an Xcode fixit in the deprecation warning that the user can click to change the function name on their behalf. And don't worry, the `add` function will continue working as it has until it is fully removed. Sincerely, Your benevolent dictator (of Boutique) 👑
Can You Help? I'm In A Bit Of A Bind...ing
1 year ago
Do you use SwiftUI? Cool, me too, and boy are there a lot of Bindings. This release includes a small improvement for `@StoredValue` and `@AsyncStoredValue`, allowing you to create a binding from a value backed by either of our property wrappers. Before you would have to write out this `isEnabled` Binding manually. ```swift Checkbox( title: "Red Panda Button", description: "They're the best, aren't they? Press this button to enable some red pandas.", isEnabled: Binding(get: { self.preferences.isRedPandaModeEnabled }, set: { self.preferences.$isRedPandaModeEnabled.set($0) }) ) ``` But now, automagically generated for you with the power of functions. ```swift Checkbox( title: "Red Panda Button", description: "They're the best, aren't they? Press this button to enable some red pandas.", isEnabled: self.preferences.$isRedPandaModeEnabled.binding() ) ``` How nice is that? `@StoredValue` and `@AsyncStoredValue` are more powerful than ever.
All The Best Words Are In The Dictionary
1 year ago
This release includes a small improvement for `@StoredValue` and `@AsyncStoredValue` when those values are of the type `Dictionary`. Previously when you stored a `Dictionary` and wanted to update it, you would have to make a mutable copy of current dictionary, change the value, and then save it back. To make this a little easier Boutique 2.0.3 introduces an `update(key:value)` function that turns this into one line of code. A small example to illustrate the improvement Before ```swift var updatedRedPandaList = self.redPandaList updatedRedPandaList["best"] = "Pabu" self.$redPandaList.set(updatedRedPandaList) ``` After! ```swift self.$redPandaList.update(key: "best", value: "Pabu") ``` So much cleaner, enjoy!
iOS macOS
mergesort/TableFlip 1.3.1
A simpler way to do cool UITableView animations! (╯°□°)╯︵ ┻━┻
⭐️ 553
🕓 3 years ago
🔖 Release Notes

Releases

The markdown parsing is broken/disabled for release notes. Sorry about that, I'm chasing the source of a crash that's been bringing this website down for the last couple of days.
Sensible Defaults
4 years ago
- Add default `timeInterval` parameters in animation enums. - The library now requires Swift 5.1 to support default parameters.
The Highest Swift Version So Far
5 years ago
- Adding Swift 4.2 support.
More Animated Than Ever ✨
6 years ago
- Adding a completion handler when `reloadData(inPlace: animated:)` is called synchronously. - A fix for `animateWithFade()` potentially calling it's completion block multiple times. --- #### ⚠️ Breaking changes ⚠️ - ~`animateTableView()`~ and ~`animateCells()`~ have been renamed to `animate()` and are differentiated based on the parameter passed in. - Renaming ~`reloadData(smoothly: animated:)`~ to `reloadData(inPlace: animated:)`.
Bug or feature?
6 years ago
This release updates behavior that cell animations will fire their completion block after all the cell animations have completed, not after each individual animation. It is not a minor semantic version bump because while it is a bug fix, it produces a change in behavior which some people may have been relying on.
Flip select indices
6 years ago
This update adds an `indexPaths` parameter to `func animateCells`. ~`func animateCells(animation: TableViewAnimation.Cell, completion: (() -> Void)? = nil)`~ `func animateCells(animation: TableViewAnimation.Cell, indexPaths: [IndexPath]? = nil, completion: (() -> Void)? = nil)` This change is non-breaking, since `indexPaths` parameter is optional. Your old code should continue to work as it did before.
Flipping out (╯°□°)╯︵ ┻━┻
7 years ago
Now calm down.
iOS
mergesort/Bodega 2.1.2
A simple store for all your basic needs, and a foundational data layer primitive for iOS and Mac apps. 🐱
⭐️ 279
🕓 10 weeks ago
🔖 Release Notes

Releases

The markdown parsing is broken/disabled for release notes. Sorry about that, I'm chasing the source of a crash that's been bringing this website down for the last couple of days.
Stay Safe Out There
10 weeks ago
In version 2.1.1 I had accidentally committed `[.unsafeFlags(["-strict-concurrency=complete"])]` into Bodegal's `Package.swift`. That prevented the library from compiling on production versions of Xcode, it is now removed, so finally we're all safe again.
The Right Key To Unlock Your Door
22 weeks ago
This release improves Bodega's API with fixes (#27) and improvements (#25) courtesy of @dannynorth, thank you! The changes are: - A new `keysExist` function that more efficiently searches for and filters existing keys in a `StorageEngine`. - Fixes the `readDataAndKeys` function to always return the correct data and keys. Previously if the `readDataAndKeys` function was provided an invalid key in the `keys` array incorrect values would be returned after the invalid key index.
Hard To Pin Down
31 weeks ago
This release fixes incompatibilities with other libraries (#16) by removing the explicit dependency on Version 0.13.3 of SQLite.swift. Bodega is now pinned to any version of SQLite.swift greater than 0.13.2, that way conflicts are less likely to emerge.
Never Feel Empty Using Bodega Again
1 year ago
This release includes a small fix to the `SQLiteStorageEngine`. Previously this code would throw an error. ```swift let noItems = [] // You can imagine some computation that ends up with an empty array rather than setting [] directly storageEngine.write(noItems) ``` Now it will not throw an error, instead the `write` function will return early and act as a no-op, more accurately matching a user's expectations.
Dependency Fix Release
1 year ago
Ugh you know how sometimes you accidentally mess up a tag and it's not easily fixable? This version fixes a dependency issue for [Boutique](https://github.com/mergesort/Boutique), nothing more, nothing less.
Version 2.0: Bring Your Own Database
1 year ago
***Welcome to the future!*** Bodega v2 allows you to extend Bodega far and wide by giving you the power to bring your own database to Bodega in one easy step. Out of the box Bodega v2 is 5-10x faster because the default database is now powered by the blazing fast `SQLiteStorageEngine`. But if SQLite or the `DiskStorageEngine` aren't your jam, you can build your own `StorageEngine` by conforming to a simple protocol. Using Bodega is still as simple as it ever was, requiring very little code, no special objects, with simple defaults out the box. As a bonus if you do that it will automatically with any [Boutique](https://github.com/mergesort/Boutique)-powered app, providing you the same single source of truth, realtime updates, and a fully offline-capable app you've come to know and love, in only a couple of lines of code. --- > **Warning** > This version contains breaking changes --- In the Version 1.x series of Bodega the `DiskStorage` type was responsible for persisting data to disk. `ObjectStorage` was a `Codable` layer built on top of `DiskStorage`, letting you work with Swift types rather than `Data`. Like the name implies `DiskStorage` was backed by the file system, but what if you don't want to save `Data` to disk? Saving data to disk is a simple and effective starting point, but can get slow when working with large data sets. One of Bodega's goals is to work with every app without causing developers to make tradeoffs, so version 2.0 is focused on eliminating those tradeoffs without ruining the streamlined simplicity Bodega brings. In the spirit of not making tradeoffs here's how Bodega works with any database you want, say hello to the new `StorageEngine` protocol. ```swift public protocol StorageEngine: Actor { func write(_ data: Data, key: CacheKey) async throws func write(_ dataAndKeys: [(key: CacheKey, data: Data)]) async throws func read(key: CacheKey) async -> Data? func read(keys: [CacheKey]) async -> [Data] func readDataAndKeys(keys: [CacheKey]) async -> [(key: CacheKey, data: Data)] func readAllData() async -> [Data] func readAllDataAndKeys() async -> [(key: CacheKey, data: Data)] func remove(key: CacheKey) async throws func remove(keys: [CacheKey]) async throws func removeAllData() async throws func keyExists(_ key: CacheKey) async -> Bool func keyCount() async -> Int func allKeys() async -> [CacheKey] func createdAt(key: CacheKey) async -> Date? func updatedAt(key: CacheKey) async -> Date? } ``` By providing your own `write`, `read`, `remove`, `key`, and `timestamp` related functions, you can make any persistence layer compatible with `ObjectStorage`. Whether your app is backed by Realm, Core Data, or even CloudKit, when you create a new `StorageEngine` it automatically becomes usable by `ObjectStorage`, with one drop dead simple API. The first `StorageEngine` to be implemented is an `SQLiteStorageEngine`, bundled with Bodega. I'll explain all the possibilities below, but first let's take a second to see how much faster your apps using Bodega and Boutique will be. ![StorageEngine Read Performance](https://user-images.githubusercontent.com/716513/180575528-2d2d4fdb-aaba-4d46-b106-0dfda56eecf1.png) ![StorageEngine Write Performance](https://user-images.githubusercontent.com/716513/180575533-ab81e6c4-43ad-46fb-9b88-9e701a2f7718.png) --- If it's not obvious, a SQLite foundation for Bodega is incredibly faster than using the file system. The `DiskStorageStorageEngine` is still available, but if you use the `SQLiteStorageEngine` loading 10,000 objects into memory will be more than 400% faster, and writing 5,000 objects is more than 500% faster. With this release I feel confident that you should be able to use Bodega and Boutique in the largest of apps, while counterintuitively becoming a more flexible framework. ### Breaking There's only one change to your code, `ObjectStorage` now has a type constraint. This isn't required for `ObjectStorage` but was an oversight in the 1.x versions, and a big version bump felt like the right time to fix this mistake. Before you would initialize a new ObjectStorage like this. ```swift let storage = ObjectStorage( directory: .documents(appendingPath: "Animals") ) ``` And now it looks like this, a small tweak! ```swift // Change #1: Instead of a directory we now provide a StorageEngine // Change #2: Add a type constraint of <Animal> to ObjectStorage let storage = ObjectStorage<Animal>( storage: SQLiteStorageEngine(directory: .documents(appendingPath: "Animals"))! ) ``` And of course the same thing is true if you use `DiskStorage` or any `StorageEngine` you may build. --- You can find Bodega's documentation [here](https://build.ms/bodega/docs), including a lot of updates for the v2 release. P.S. If you build something useful to others, by all means file a pull request so I can add it to Bodega!
Version 2.0 RC 2: Bring Your Own Database
1 year ago
If you'd like to see all of the v2 changes please consult the v2 RC 1 [release notes](https://github.com/mergesort/Bodega/releases/tag/2.0.0-rc-1). ## RC 2 Changes - Bodega is now fully documented, available at [build.ms/bodega/docs](https://build.ms/bodega/docs) - `StorageEngine` functions have now been marked `async`. Method signatures of conforming types will have to be updated to add `async`, but there is be no change to functionality. The `StorageEngine` protocol has an `actor` conformance so the functions would already run `async`, this change just enforces that in the function signature. - `SQLiteStorageEngine` provides two new ways of creating an `SQLiteStorageEngine`, either by calling `SQLiteStorageEngine.default` or `SQLiteStorageEngine.default(appendingPath: "Red Panda Store")`. - Adding retry logic to `SQLiteStorage`. This wouldn't be an issue in an app, but would crop up as an issue when running tests.
Version 2.0 RC 1: Bring Your Own Database
1 year ago
**Welcome to the future!** Bodega v2 allows you to extend Bodega far and wide by giving you the power to bring your own database to Bodega in one easy step. Out of the box Bodega v2 is 5-10x faster because the default database is now powered by the blazing fast `SQLiteStorageEngine`. But if SQLite or the `DiskStorageEngine` aren't your jam, you can build your own `StorageEngine` by conforming to a simple protocol. Using Bodega is still as simple as it ever was, taking very little code, and no special objects, with simple defaults. As a bonus if you do that it will automatically with any [Boutique](https://github.com/mergesort/Boutique)-powered app, providing you the same single source of truth, realtime updates, and a fully offline-capable app you've come to know and love, in only a couple of lines of code. --- > **Warning** > This version contains breaking changes --- In the Version 1.x series of Bodega the `DiskStorage` type was responsible for persisting data to disk. `ObjectStorage` was a `Codable` layer built on top of `DiskStorage`, letting you work with Swift types rather than `Data`. Like the name implies `DiskStorage` was backed by the file system, but what if you don't want to save `Data` to disk? Saving data to disk is a simple and effective starting point, but can get slow when working with large data sets. One of Bodega's goals is to work with every app without causing developers to make tradeoffs, so version 2.0 is focused on eliminating those tradeoffs without ruining the streamlined simplicity Bodega brings. In the spirit of not making tradeoffs here's how Bodega works with any database you want, say hello to the new `StorageEngine` protocol. ```swift public protocol StorageEngine: Actor { func write(_ data: Data, key: CacheKey) async throws func write(_ dataAndKeys: [(key: CacheKey, data: Data)]) async throws func read(key: CacheKey) async -> Data? func read(keys: [CacheKey]) async -> [Data] func readDataAndKeys(keys: [CacheKey]) async -> [(key: CacheKey, data: Data)] func readAllData() async -> [Data] func readAllDataAndKeys() async -> [(key: CacheKey, data: Data)] func remove(key: CacheKey) async throws func remove(keys: [CacheKey]) async throws func removeAllData() async throws func keyExists(_ key: CacheKey) async -> Bool func keyCount() async -> Int func allKeys() async -> [CacheKey] func createdAt(key: CacheKey) async -> Date? func updatedAt(key: CacheKey) async -> Date? } ``` By providing your own `write`, `read`, `remove`, `key`, and `timestamp` related functions, you can make any persistence layer compatible with `ObjectStorage`. Whether your app is backed by Realm, Core Data, or even CloudKit, when you create a new `StorageEngine` it automatically becomes usable by `ObjectStorage`, with one drop dead simple API. The first `StorageEngine` to be implemented is an `SQLiteStorageEngine`, bundled with Bodega. I'll explain all the possibilities below, but first let's take a second to see how much faster your apps using Bodega and Boutique will be. ![StorageEngine Read Performance](https://user-images.githubusercontent.com/716513/180575528-2d2d4fdb-aaba-4d46-b106-0dfda56eecf1.png) ![StorageEngine Write Performance](https://user-images.githubusercontent.com/716513/180575533-ab81e6c4-43ad-46fb-9b88-9e701a2f7718.png) --- If it's not obvious, a SQLite foundation for Bodega is incredibly faster than using the file system. The `DiskStorageStorageEngine` is still available, but if you use the `SQLiteStorageEngine` loading 10,000 objects into memory will be more than 400% faster, and writing 5,000 objects is more than 500% faster. With this release I feel confident that you should be able to use Bodega and Boutique in the largest of apps, while counterintuitively becoming a more flexible framework. ### Breaking There's only one change to your code, `ObjectStorage` now has a type constraint. This isn't required for `ObjectStorage` but was an oversight in the 1.x versions, and a big version bump felt like the right time to fix this mistake. Before you would initialize a new ObjectStorage like this. ```swift let storage = ObjectStorage( storage: SQLiteStorageEngine(directory: .documents(appendingPath: "Animals"))! ) ``` And now it looks like this, one small tweak! ```swift let storage = ObjectStorage<Animal>( storage: SQLiteStorageEngine(directory: .documents(appendingPath: "Animals"))! ) ``` And of course the same thing is true if you use `DiskStorage` or any `StorageEngine` you may build. --- P.S. If you build something useful to others, by all means file a pull request so I can add it to Bodega!
A Direct(ory) Improvement
1 year ago
### Adding a public `FileManager.Directory` initializer In 1.1.0 I added a fancy new type, `FileManager.Directory`. ```swift public extension FileManager { struct Directory { public let url: URL } } ``` But I forgot to add a public initializer, so that’s what this release is for… whoops. :sweat_smile:
Improvements & Changes, Some Breaking
1 year ago
This release has many changes, and some of them are breaking. This is work that's important for Bodega (and [Boutique](https://github.com/mergesort/Boutique)), improving upon and fixing some assumptions I'd made earlier in the development lifecycle of Bodega, before there even was a Boutique. Aside from improvements this version will lay the foundation for a version 2.0 of Bodega and Boutique, one that will offer some very significant and much-needed performance improvements. Having gone through the exercise of optimizing most anything that can provide a reasonable boost in performance, now 85% of the time Bodega spends on reading or writing is filesystem-based operations, many of which are ones I can't tune. To remedy this Bodega 2.0 will offer a database-powered variant of the underlying Storage (currently `DiskStorage`), while leaving `ObjectStorage` unchanged. This will mean that your code doesn't have to change, but may require data that you can't repopulate to be manually migrated. You will have the ability to stay on 1.x versions of the library if you don't want to make any changes, but Bodega 2.0 will provide a new `DatabaseStorage` option that uses SQLite under the hood and provides 400% faster performance. This is especially useful for apps that have large data sets (10,000+ objects), as many production apps do, and will be the default storage option for Boutique 2.0. --- Now that we know why these changes are being made, here are the changes in this pull request. - Removing the concept of subdirectories, and all of `subdirectory` parameters. The subdirectory is complicated, error-prone, and in practice doesn't have much use. When I first started working on Bodega I was using subdirectories to shard objects, but now you can easily create a new `ObjectStorage` or `DiskStorage` pointing to a subdirectory to replicate the functionality the `subdirectory` parameter offers. The benefit is a much simpler and clearer API, and removes much surface area for bugs such as this code. ```swift let keys = store.allKeys(inSubdirectory: "subdirectory") let objects = store.objects(forKeys: keys, inSubdirectory: "subdirectory") // Returns 0 objects because you're actually querying folder/subdirectory/subdirectory, not folder/subdirectory as you may expect. ``` - Removing `.lastAccessed()` from `ObjectStorage`. When `ObjectStorage` was guaranteed to have a `DiskStorage` under the hood we could call the underlying `DiskStorage`'s version of this method to figure out when the object was last accessed. But going forward `ObjectStorage` is no longer guaranteed to use `DiskStorage`, for example as we use will use `DatabaseStorage` in the future. The method will still remain available on `DiskStorage`, with no changes to `lastModified()` or `creationDate()`. - Adding `applicationSupportDirectory()` on the Mac. If you have suggestions for other useful directories please let me know. - Adding a new type, `FileManager.Directory`, to provide a type-safe replacement for the folders in `DiskStorage+Directories`. The initializer for `ObjectStorage` or `DiskStorage` now looks like `init(directory: Directory)` rather than `init(storagePath: URL)`, which allows for shorter, type-safe, and file-system safe initializers such as `DiskStorage(directory: .documents(appendingPath: "Notes"))`.
iOS macOS
mergesort/Slope 1.3.0
A simpler way to implement gradients on iOS.
⭐️ 237
🕓 3 years ago
🔖 Release Notes

Releases

The markdown parsing is broken/disabled for release notes. Sorry about that, I'm chasing the source of a crash that's been bringing this website down for the last couple of days.
A Gradient Colored Package
4 years ago
- Adding Swift 5.1 support. - Adding SPM support.
Swift Updates Ironically Take Forever
5 years ago
- Adding Swift 4.2 support.
Dangling pointers
5 years ago
- Exposing `GradientComponents.color` and `GradientComponents.location` publicly for those who want to create their own `Gradient`s. - Exposing `GradientAngle.reversed` publicly.
A Cute And Not Cute Angles
5 years ago
A Cute And Not Cute Angles #### Thanks to [@adamstener](https://github.com/adamstener) for the big help with this release! --- - `GradientAngle` has been added. You can choose from four predefined angles (`.vertical`, `.horizontal`, `.bottomLeftToTopRight`, `topLeftToBottomRight`) or initialize your own start and end points. ### Breaking changes - `GradientDirection` has been removed. If you need to use `GradientDirection.darkToLight`, instead use the `.reversed` property of `GradientAngle`.
Shiny Things
5 years ago
Initial commit.
iOS
mergesort/FeedbackEffect 1.5.0
A library for playing sounds and providing haptic feedback with ease.
⭐️ 228
🕓 3 years ago
🔖 Release Notes

Releases

The markdown parsing is broken/disabled for release notes. Sorry about that, I'm chasing the source of a crash that's been bringing this website down for the last couple of days.
Putting a nice bow on the package
4 years ago
- Adding support for `.soft`, `.rigid` haptics available in iOS 13. - Adding Swift 5 support. - Adding SPM support. > View build details and download artifacts on buddybuild: > [FeedbackEffect (iOS, Example - Debug)](https://dashboard.buddybuild.com/apps/59b1c96ebb97170001f8ebd4/build/5d2b9f83ce96350001541bb3)
Muted Feedback
5 years ago
- Adding Swift 4.2 support. > View build details and download artifacts on buddybuild: > [FeedbackEffect (iOS, FeedbackEffect - Debug)](https://dashboard.buddybuild.com/apps/59b1c96ebb97170001f8ebd4/build/5c212364a5e7540001a866ea)
Quite An Impact
6 years ago
- Users can now specify a `UIImpactFeedbackStyle` as such. ```swift HapticFeedback.impact(.heavy).prepare() FeedbackEffect.play(sound: nil, feedback: HapticFeedback.impact(.heavy)) ``` > View build details and download artifacts on buddybuild: > [FeedbackEffect (iOS, FeedbackEffect - Debug)](https://dashboard.buddybuild.com/apps/59b1c96ebb97170001f8ebd4/build/59e8f57a70b45f0001b341e6)
Prepare Yourself
6 years ago
- Adding the ability to `prepare()` a `UIImpactGenerator`. > View build details and download artifacts on buddybuild: > [FeedbackEffect (iOS, FeedbackEffect - Debug)](https://dashboard.buddybuild.com/apps/59b1c96ebb97170001f8ebd4/build/59cd14c1c6092b000121c45d)
Feedback Received
6 years ago
Added the ability to specify which level of `UIImpactFeedbackStyle` you want. Supporting: - `.light` - `.medium` - `.heavy` > View build details and download artifacts on buddybuild: > [FeedbackEffect (iOS, FeedbackEffect - Debug)](https://dashboard.buddybuild.com/apps/59b1c96ebb97170001f8ebd4/build/59b30111f72e0e0001320056)
Feedback Appreciated
6 years ago
(That's a pun.) > View build details and download artifacts on buddybuild: > [FeedbackEffect (iOS, FeedbackEffect - Debug)](https://dashboard.buddybuild.com/apps/59b1c96ebb97170001f8ebd4/build/59b2db3a021dcc00011f8a0c)
iOS
mergesort/GenericCells 1.4.1
Creating generic UITableViewCells and UICollectionViewCells instead of subclasses.
⭐️ 82
🕓 3 years ago
🔖 Release Notes

Releases

The markdown parsing is broken/disabled for release notes. Sorry about that, I'm chasing the source of a crash that's been bringing this website down for the last couple of days.
Fixing podspec
4 years ago
- The podspec now specifies Swift version 5.1 rather than 4.2.
SPM and Swift 5.1 Support
4 years ago
- Adding Swift 5.1 support. - Adding SPM support.
Swift 4.2 Support
5 years ago
- That's all I got!
Swift 4 U + .01
6 years ago
- `contentView.preservesSuperviewLayoutMargins` defaults to false with `.layoutMargins` defaults to `.zero`, to have more consistent behavior with `UITableViewCell` and `UICollectionViewCell`.
Swift 4 U
6 years ago
- Adding support for Swift 4. #### ⚠️ Breaking changes ⚠️ - `GenericTableCell` and `GenericCollectionCell` now are bound to the the cell's `self.contentView.layoutMarginsGuide` instead of `self.contentView` anchors. - This replaces the old functionality where you would set a `contentInsets` property. If you wish to set custom insets, instead of writing: ```swift cell.contentInsets = UIEdgeInsets(top: 10.0, left: 20.0, bottom: 10.0, right: 20.0) ``` Now write: ```swift cell.layoutMargins = UIEdgeInsets(top: 10.0, left: 20.0, bottom: 10.0, right: 20.0) ```
Prettying things up
7 years ago
Were you tired of writing 3 lines of code? So was I. There is now a default implementation for `prepareForReuse`, in case you don't have anything that needs reusing in your cell.
Expanding The Scope
7 years ago
GenericTableCell is now GenericCells. This means that all the fun stuff you could do with a UITableView, you can now do with a UICollectionView. 🎊 🦁 🎊
Initial release 🎉
7 years ago
iOS
mergesort/Anchorman 3.2.0
An autolayout library for the damn fine citizens of San Diego.
⭐️ 79
🕓 3 years ago
🔖 Release Notes

Releases

The markdown parsing is broken/disabled for release notes. Sorry about that, I'm chasing the source of a crash that's been bringing this website down for the last couple of days.
I love scotch, scotchy, scotchy, scotch
4 years ago
- Adding support for Swift 5.1. - Adding SPM support. - Cleaning up redundant public access level warnings.
Hey aqualung!
5 years ago
- Adding Swift 4.2 support.
Don’t act like you’re not impressed
6 years ago
- The underlying framework is completely rewritten. If you find any bugs, please report them. 🐛 - This version is written in and supports Swift 4. If you need to use Swift 3, use version 2.0 of Anchorman instead. --- #### New Features - **Anchorman now supports `UILayoutGuide` along with `UIView` constraints.** - Adding support for `.left` and `.right` `EdgeAnchor`s. --- #### ⚠️ Breaking changes ⚠️ - Removed `func translateAutoresizingMasks(on: Bool)` - Removed `static func activateAllConstraints(constraints: [[NSLayoutConstraint]])` - Removed `static func deactivateAllConstraints(constraints: [[NSLayoutConstraint]])` - For functions that had a parameter `activate: Bool`, it has now been renamed `isActive: Bool`. ### API changes for pinning ~~`func pinToSuperview(_ edges: [EdgeAnchor], relation: NSLayoutRelation, activate: Bool) -> [NSLayoutConstraint]`~~ `func pinToSuperview(_ edges: [EdgeAnchor], relation: NSLayoutRelation, isActive: Bool) -> [NSLayoutConstraint]` --- ~~`func pin(toView view: UIView, edges: [EdgeAnchor], relation: NSLayoutRelation, activate: Bool) -> [NSLayoutConstraint]`~~ `func pin(to view: UIView, edges: [EdgeAnchor], relation: NSLayoutRelation, isActive: Bool) -> [NSLayoutConstraint]` --- ~~`func pin(edge: EdgeAnchor, toEdge: EdgeAnchor, ofView view: UIView, relation: NSLayoutRelation, constant: CGFloat, priority: UILayoutPriority, activate: Bool) -> NSLayoutConstraint`~~ `func pin(edge: EdgeAnchor, toEdge: EdgeAnchor, of view: UIView, relation: NSLayoutRelation = .equal, constant: CGFloat, priority: UILayoutPriority, isActive: Bool) -> NSLayoutConstraint` --- ~~`func set(size sizeAnchor: SizeAnchor, relation: NSLayoutRelation, activate: Bool) -> NSLayoutConstraint`~~ `func set(size sizeAnchor: SizeAnchor, relation: NSLayoutRelation, isActive: Bool) -> NSLayoutConstraint` --- ~~`func set(size sizeAnchors: [SizeAnchor] = [ SizeAnchor.width, SizeAnchor.height ], relation: NSLayoutRelation, activate: Bool) -> [NSLayoutConstraint]`~~ `func set(size sizeAnchors: [SizeAnchor] = [ SizeAnchor.width, SizeAnchor.height ], relation: NSLayoutRelation, isActive: Bool) -> [NSLayoutConstraint]` --- ~~`func set(relativeSize sizeAnchor: SizeAnchor, toSizeAnchor: SizeAnchor, ofView view: UIView, multiplier: CGFloat, constant: CGFloat, relation: NSLayoutRelation, activate: Bool) -> NSLayoutConstraint`~~ `func set(relativeSize sizeAnchor: SizeAnchor, toSizeAnchor: SizeAnchor, ofAnchorable anchorable: Anchorable, multiplier: CGFloat, constant: CGFloat, relation: NSLayoutRelation, isActive: Bool)`
The human torch was denied a bank loan
7 years ago
### This release is a completely breaking change to the API. • The library is now compatible with Swift 3 only. The previous release will continue to work with Swift 2.2.
There were horses, and a man on fire, and I killed a guy with a trident
7 years ago
• Fixes a bug that set `self.translatesAutoresizingMaskIntoConstraints = false` onto the view being pinned to, which is bad when the view is `self.view`.
I would like to extend to you an invitation to the pants party
7 years ago
**This version supports Swift 3, but not 2.2 or 2.3.** Your code that compiled for Swift 2.2 or 2.3 should not change, and remains compatible. A future update will add Swift 3 naming conventions into the code. • Brings the library support to 3.0. • Code clean up.
Baxter, is that you? Baxter! Bark twice if you’re in Milwaukee
7 years ago
**This version supports Swift 2.2 and 2.3, but not 3.** • Removing an unused variable. ¯_(ツ)_/¯
They've done studies, you know. 60% of the time, it works, every time
7 years ago
• Adding `setRelativeSize` for multiplier support. • Fixing uncaught EdgeAnchors.
You look like a blueberry
7 years ago
• `setSize` now overloads allowing to take in either a `SizeAnchor`, or `[SizeAnchor]`, depending on the parameter passed in, returning `NSLayoutConstraint` and `[NSLayoutConstraint]` respectively. • Breaking change: `.allEdges` has been renamed to `.allSides`, since those semantics are representative of pinning a view's `.leading`, `.trailing`, `.top`, and `.bottom` edges.
You know I don’t speak Spanish
7 years ago
Adding support for NSLayoutRelations, `.Equal`, `.GreaterThanOrEqual`, and `.LessThanOrEqual`
iOS
mergesort/TypedNotifications 1.4.0
A mechanism for sending typed notifications with payloads across your iOS app.
⭐️ 76
🕓 3 years ago
🔖 Release Notes

Releases

The markdown parsing is broken/disabled for release notes. Sorry about that, I'm chasing the source of a crash that's been bringing this website down for the last couple of days.
Notifying You Of SPM Support
4 years ago
- Adding SPM support. > View build details and download artifacts on buddybuild: > [TypedNotifications (iOS, Example - Debug)](https://dashboard.buddybuild.com/apps/59a836506532420001f89b3b/build/5d2b9c546d24e7000125f027)
Reclaiming The Object
4 years ago
Background: Before I was using the `Notification.object` to pass around data for the typed notifications. More Background: I am not smart. Result: I've moved it to privately use the `Notification.userInfo`, which was unused since we're using fully formed types, and so you can optionally pass in an `Object` to the `Notification.post`. Since this is an additive change with it shouldn't break any of your existing code, but you should now be able to use all that `Notification` has to offer. > View build details and download artifacts on buddybuild: > [TypedNotifications (iOS, TypedNotifications - Debug)](https://dashboard.buddybuild.com/apps/59a836506532420001f89b3b/build/5d177cd52ec52b000140dcc7)
No Funny Name Just Swift 4.2 support
5 years ago
- Adding Swift 4.2 support. > View build details and download artifacts on buddybuild: > [TypedNotifications (iOS, TypedNotifications - Debug)](https://dashboard.buddybuild.com/apps/59a836506532420001f89b3b/build/5c211cd66c28cf00013daace)
Free Your Mind
6 years ago
In version 1.0 `TypedNotification`s always had to have a payload. Now in version 1.1 there are 2 distinct types, `TypedNotification` and `TypedPayloadNotification`. The API remains the same so you still call the same functions to register and post messages. ```swift NotificationCenter.default.register(type: MyTypedNotification.self, observer: self, selector: #selector(myTypedNotificationWasReceived)) ``` and ```swift NotificationCenter.default.post(typedNotification: myTypedNotification) ``` The only difference is that a `TypedNotification` cannot call `getPayload` (because, well, there is no payload), and that now your types with payloads have to conform to `TypedPayloadNotification` instead of `TypedNotification`. To restore functionality from version 1.0 should be trivial, all you have to do is add the word `Payload` in your protocol conformance of `TypedNotification`. > View build details and download artifacts on buddybuild: > [TypedNotifications (iOS, TypedNotifications - Debug)](https://dashboard.buddybuild.com/apps/59a836506532420001f89b3b/build/59ada9e751ae5b00013a5298)
Getting The Party Started
6 years ago
> View build details and download artifacts on buddybuild: > [TypedNotifications (iOS, TypedNotifications - Debug)](https://dashboard.buddybuild.com/apps/59a836506532420001f89b3b/build/59ad658ab2dbc50001bba4f2)

Swiftpack is being maintained by Petr Pavlik | @ptrpavlik | @swiftpackco | API | Analytics