5170 Commits
Author SHA1 Message Date
GianmarcoandGianmarco 34de002f79 Fix: Transfer leadingScreensForBatching from pending state on node load (#2130)
* Fix: Transfer leadingScreensForBatching from pending state on node load
and add tests

* Address PR comments

* try to fix build

* Adding whitespace to see if builds run

* Revert prev commit to check if builds are working

---------

Co-authored-by: Gianmarco <gfolchi@pinterest.com>
releases/p13.47 releases/p14.1 releases/p14.10 releases/p14.11 releases/p14.12 releases/p14.13 releases/p14.14 releases/p14.15 releases/p14.2 releases/p14.3 releases/p14.4 releases/p14.5 releases/p14.6 releases/p14.7 releases/p14.8 releases/p14.9
2025-11-19 11:27:15 -08:00
Andy Finnell e696302586 Add default image provider (#2127)
* Add default image provider

## Summary

Right now image nodes hardcode logic to use either PINRemoteImage or the basic image downloader. Additionally, Texture is missing annotations for nullability and sendable for several of the image blocks and types.

Introduce ASDefaultImageDownloader as a singleton. It takes blocks to return the default image downloader and image cache. Update both image nodes to use this. By default ASDefaultImageDownloader uses the same logic the image nodes used to, so change in behavior.

## Test plan

Run the Kittens example and verify it works.

* Appears we need Xcode 16.4

* And need at least 18.5
releases/p13.43 releases/p13.44 releases/p13.45 releases/p13.46
2025-10-22 12:36:55 -04:00
ricky 5d75e12fd3 [iOS26] Fix warning in iOS26 (#2126)
When compiling in Xcode 26 we get the warning `variable length array folded to constant array as an extension` for the arrays in this struct:
```
typedef struct ASLayoutElementStyleExtensions {
  // Values to store extensions
  BOOL boolExtensions[kMaxLayoutElementBoolExtensions];
  NSInteger integerExtensions[kMaxLayoutElementStateIntegerExtensions];
  UIEdgeInsets edgeInsetsExtensions[kMaxLayoutElementStateEdgeInsetExtensions];
} ASLayoutElementStyleExtensions;
```
To make it happy we could make these constants `#define` or stick them in an `enum`. I chose the latter.
releases/p13.37 releases/p13.38 releases/p13.39 releases/p13.40 releases/p13.41 releases/p13.42
2025-09-12 11:14:14 -07:00
Andy Finnell be0b74b4e2 Check for batch fetching on scroll (#2124)
## Summary

For collection views, we do not currently check for batch fetching on scroll. This appears to be a bug, as `scrollViewDidScroll:` does call `_checkForBatchFetching`, and [the commit that added it states it's trying to check on each scroll](https://github.com/TextureGroup/Texture/commit/df497b82c286771658a0ef0826945c716baaa783). Unfortunately, `_checkForBatchFetching` checks for `isTracking` and `isDragging` first and returns if either are `YES`. Since we're in a scroll, they are `YES`. So the call is effectively a no-op.

This bug has been around for 9 years and I'm unsure of the performance implications of turning the batch fetching check on for each scroll tick. Therefore, put the fix behind an experiment feature flag _and_ only call it on the first scroll tick for the scroll session, instead of every scroll tick. Finally, I moved the check after the delegate call, in case the delegate has logic in it to turn on or off the batch fetching.

## Test plan

Ran the app and manually tested batching getting called or not. Also ran `build.sh tests`.
releases/p13.22 releases/p13.23 releases/p13.24 releases/p13.25 releases/p13.26 releases/p13.27 releases/p13.28 releases/p13.29 releases/p13.30 releases/p13.31 releases/p13.32 releases/p13.33 releases/p13.34 releases/p13.35 releases/p13.36
2025-05-22 13:07:34 -04:00
Andy Finnell e894389a7e Add experimental feature of hierarchyDisplayDidFinish being recursive (#2123)
* Add experimental feature of hierarchyDisplayDidFinish being recursive

## Summary

`hierarchyDisplayDidFinish` is currently called when a node's immediate subnodes implement custom CALayer rendering. For example, ASNetworkImageNode and ASTextNode perform this sort of rendering. However, it can be useful -- and less fragile -- if `hierarchyDisplayDidFinish` is propagated further up the node hierarchy. That way, if a feature is refactored such that an extra node appears between the node implementing `hierarchyDisplayDidFinish` and the nodes doing CALayer rendering, we don't lose the callback.

## Test plan

Manually tested using a modified version of the Kittens example. Also ran `build.sh tests`.

* Revert SDK change since it's still Xcode 15
releases/p13.20 releases/p13.21
2025-05-08 13:19:07 -07:00
ricky 2a5ae7dd50 [ASDisplayNode] Fix a crash in insertSubnode (#2122)
* [ASDisplayNode] Fix a crash in insertSubnode

If a node is already a subnode to a supernode, Inserting it again can lead to a crash.

Here is a simple repro of the crash:
```
  ASDisplayNode *subnode = [[ASDisplayNode alloc] init];
  ASDisplayNode *supernode = [[ASDisplayNode alloc] init];

  [supernode addSubnode:subnode];
  // Crash on next line
  [supernode insertSubnode:subnode atIndex:1];
```

The issue is that all the checks around subnode array boundaries are done BEFORE `subnode` is removed from its `supernode`. If it happens that the `supernode` is self, then removing the `subnode` causes all our index checks to no longer be valid.

Here is the relevant code:

```
  __instanceLock__.lock();
    NSUInteger subnodesCount = _subnodes.count;
  __instanceLock__.unlock();
   ////// Here we check our indexes
  if (subnodeIndex > subnodesCount || subnodeIndex < 0) {
    ASDisplayNodeFailAssert(@"Cannot insert a subnode at index %ld. Count is %ld", (long)subnodeIndex, (long)subnodesCount);
    return;
  }

…

  ///////// Here our indexes could invalidate if self subnode’s supernode
  [subnode removeFromSupernode];
  [oldSubnode removeFromSupernode];

  __instanceLock__.lock();
    if (_subnodes == nil) {
      _subnodes = [[NSMutableArray alloc] init];
    }
    ////// Here would can crash if our index is too big
    [_subnodes insertObject:subnode atIndex:subnodeIndex];
    _cachedSubnodes = nil;
  __instanceLock__.unlock();
```

* add a separate check for this special case
releases/p13.10 releases/p13.11 releases/p13.12 releases/p13.13 releases/p13.14 releases/p13.15 releases/p13.16 releases/p13.17 releases/p13.18 releases/p13.19 releases/p13.7 releases/p13.8 releases/p13.9
2025-02-06 15:15:25 -08:00
ricky 83c6ff8440 Add NS_SWIFT_UI_ACTOR to methods always called on main (#2121)
* Add NS_SWIFT_UI_ACTOR to methods always called on main

* found a couple more main actor blocks/methods
releases/p13.6
2025-01-29 11:33:56 -08:00
ricky 2d7bf71e7c [_ASDisplayLayer] Add protection around setting a layer’s position and transform (#2116)
There is built in protection around setting invalid bounds for `_ASDisplayLayer`. Let’s extend this to also include protecting against setting an invalid position and an invalid transform.
releases/p12.35 releases/p12.36 releases/p12.37 releases/p12.38 releases/p12.39 releases/p12.40 releases/p12.41 releases/p12.42 releases/p12.43 releases/p12.44 releases/p12.45 releases/p12.46 releases/p12.47 releases/p13.1 releases/p13.2 releases/p13.3 releases/p13.4 releases/p13.5
2024-09-04 08:44:57 -07:00
Gabriel Liévano f8d91810fb Make ASInternalHelpers public (#2114) releases/p12.30 releases/p12.31 releases/p12.32 releases/p12.33 releases/p12.34 2024-07-31 09:21:01 -07:00
Gabriel Liévano 9b2b01a57f Update umbrella header for completion (#2109) 2024-07-29 11:23:15 -07:00
Andy Finnell 2ef9a4f450 Bump version to 3.2.0 (#2108)
## Summary

Following RELEASE.md steps, bump to 3.2.0.
3.2.0 releases/p12.22 releases/p12.23 releases/p12.24 releases/p12.25 releases/p12.26 releases/p12.27 releases/p12.28 releases/p12.29
2024-05-21 15:43:31 -04:00
Andy Finnell e9bd484149 Update to Xcode 15 (#2107)
* Update to Xcode 15

## Summary

We want to make Texture build with recent tools like Xcode 15. Part of this is bumping the minimum supported OS to iOS 14.

I only removed the dead code (i.e. code gated on versions older than iOS 14). There are still some warnings about outdated APIs in use, but they are non-trivial changes to fix.

## Test plan

Run all the examples. Run all the unit tests and make them pass.

* Use Xcode 15.3 so we have 17.4 simulators

* Fixing an asset catalog compiler error when building against iPhone SE

* Fix a couple of file extensions for assets

A couple of PNGs were marked as JPGs
2024-05-21 13:04:39 -04:00
Carlos Compean 923901a1ce Fix build errors and a crash in xcode 15 (#2093)
* Fix build errors and a crash in xcode 15

* early return if 0 or negative dimensions found
releases/p11.35 releases/p11.36 releases/p11.37 releases/p11.38 releases/p11.39 releases/p11.40 releases/p11.41 releases/p11.42 releases/p11.43 releases/p11.44 releases/p11.45 releases/p12.10 releases/p12.11 releases/p12.12 releases/p12.13 releases/p12.14 releases/p12.15 releases/p12.16 releases/p12.17 releases/p12.18 releases/p12.19 releases/p12.2 releases/p12.20 releases/p12.21 releases/p12.3 releases/p12.4 releases/p12.5 releases/p12.6 releases/p12.7 releases/p12.8 releases/p12.9
2023-09-11 10:43:37 -07:00
ricky 28e3baef76 [ASCellNodeVisibilityEvent] Add a new event when scrolling stops (#2084)
We have `ASCellNodeVisibilityEvent` events that roughly correlate to the scrollViewDid… delegate methods in UIScrollView. With the current events we get a callback when a user stops dragging a cell, but if the cell decelerates we do not get an event when it comes to a rest. I’ve added  `ASCellNodeVisibilityEventDidStopScrolling` to have both `ASTableView` and `ASCollectionView` send this event to the cells in `_cellsForVisibilityUpdates` in `- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView`.

I created unit tests to ensure that the proper events are being called for the proper scroll delegate methods.
releases/p11.29 releases/p11.30 releases/p11.31 releases/p11.32 releases/p11.33 releases/p11.34
2023-07-26 14:43:46 -07:00
ricky a90ea0e0d5 Trying to get CI to work (#2085)
* Try to get CI to work

* update to xcode that exists

* Use a more recent sim; update podfiles not to use github as source

* fix clang 10 error about 10 being extern

* don’t override deprecated method

* Fix tests and snapshot tests
2023-07-26 13:29:42 -07:00
Koichiro Ueki 68dd71cf70 fix typo: ASStackLayoutElement.h (#2067) releases/p11.15 releases/p11.16 releases/p11.17 releases/p11.18 releases/p11.19 releases/p11.20 releases/p11.21 releases/p11.22 releases/p11.23 releases/p11.24 releases/p11.25 releases/p11.26 releases/p11.27 releases/p11.28 2023-04-05 11:30:40 -07:00
Joe Ferrucci e88783e920 Docs: Fix references of ASViewController/ASNavigationController (non-existent) to ASDKViewController/ASDKNavigationController (#2072)
* Fix references of ASViewController (non-existent) to ASDKViewController

* More references changes to old ASViewController

* Also update ASNavigationController -> ASDKNavigationController
2023-04-05 11:30:08 -07:00
ricky 2c7ba22347 [ASTextKitRenderer] Adding locking when accessing the text renderer cache (#2075)
While NSCache is thread safe, that alone does not make `rendererForAttributes` thread safe. This experiment will add a lock to `rendererForAttributes` to see how that affects performance/stability. As second experiment will forego the cache altogether to validate that we are getting some value out of having a renderer cache.
releases/p11.12 releases/p11.13 releases/p11.14 releases/p11.5 releases/p11.6 releases/p11.7 releases/p11.8 releases/p11.9
2023-01-24 16:07:19 -08:00
Garrett Moon c989e1ac06 Switch UITextWritingDirection to NSWritingDirection (#2071) releases/p11.2 releases/p11.3 releases/p11.4 2022-12-13 14:18:13 -08:00
Mussa Charles fea847be55 Increase default diskCache byte limit from 20 to 50MB(PINCache default) (#2002) releases/p10.45 releases/p11.1 2022-08-18 09:27:58 -07:00
ricky 50426db996 Bring back ASInitializeFrameworkMainThread so we don't break the API (#2050)
In PR 2032 we added alloc/dealloc texture initialization methods for the case where texture is automatically initializing. However, in doing so we have removed the `ASInitializeFrameworkMainThread` method that clients may already be using.

This PR brings back the `ASInitializeFrameworkMainThread` to live side by side with `ASInitializeFrameworkMainThreadOnConstructor` and `ASInitializeFrameworkMainThreadOnDestructor`. This should keep the old functionality in place for any client using `ASInitializeFrameworkMainThread` directly.
2021-12-06 14:32:22 -08:00
David Ha 053688bfb2 Xcode 13 ASLoadFrameworkInitializer dead lock fix on running unit test (#2032)
* replace destructor for after main execute

* separate ASLoadFrameworkInitializer between constructor and destructor

* AS_EXTERN -> ASDK_EXTERN

* initialSetNeedsDisplayCount should be called once on iOS 15

* Loaded node of contentsScale must be 2.0 on layer backed

* initialSetNeedsDisplayCount must called once
2021-12-03 09:32:05 -08:00
ricky c53eae6532 Try to fix the CI (#2047)
It looks like github updated so that `macos-latest` is now macos-11. It can't find Xcode_11_5. Let's try to set the runs-on version explicitly and see if some magic happens.

also allow warnings for podlint because that started failing too
2021-12-02 21:11:54 -08:00
Huy Nguyen e3bdf89461 Remove AssetsLibrary dependency for tvOS (#2034)
- The framework isn't available on tvOS. This causes CocoaPods linting to fail which prevented me from pushing the new release out.
- One way to fix this is to have a different `default_subspecs` for tvOS that doesn't have AssetsLibrary subspec, but per-platform `default_subspecs` doesn't seem to be supported by CocoaPods. So I updated the subspec itself to only depend on the framework for iOS. This means the subspec is empty/useless for tvOS (and other platforms FWIW).
- Tested with `pod spec lint Texture.podspec`.
- Fixes #1992 and part of #1549. Also unblocks 3.1.0 release.
- For the long term, we can remove the subspec entirely when iOS 9 is deprecated (#1828).
3.1.0
2021-09-29 14:12:03 -07:00
Huy Nguyen eba2a53aa2 Update RELEASE.md 2021-09-29 08:02:08 -07:00
Huy Nguyen b7bcd6faae [3.1.0] Update .github_changelog_generator 2021-09-29 08:00:34 -07:00
Huy Nguyen 0753a6be30 [3.1.0] Update CHANGELOG 2021-09-29 07:57:30 -07:00
ricky 63f510caaa [3.1.0] Create new version of ASDK (#2021)
With the breaking change of renaming ASNavigationController to ASDKNavigationController, we have released a new version of Texture. Please see `ThreeMigrationGuide.md` for how to handle the breaking changes in 3.1.0.
2021-09-09 16:33:53 -07:00
ricky 1ae1e9cef9 Rename ASNavigationController to ASDKNavigationController to fix name collision (#2020)
As of iOS15 the AuthenticationServices framework has a class named `ASNavigationController`. We need to rename our `ASNavigationController` to protect against undefined behavior.

Note: This change was based on this PR https://github.com/TextureGroup/Texture/pull/2014. We were slow in merging it and the author has not replied, so I'm making a new one to get this landed.
2021-09-08 16:15:37 -07:00
ricky 03e7d1fabd [RTL] Guard access of flipsHorizontallyInOppositeLayoutDirection for iOS >= 11 (#2003)
`flipsHorizontallyInOppositeLayoutDirection` is available in iOS11 and greater. Texture still supports iOS9 so we need to make sure not to call this it in those cases.
2021-06-03 11:19:45 -07:00
ricky 18d805f523 [RTL/Batching] Make ASDisplayShouldFetchBatchForScrollView aware of flipped CV layouts (#1985)
* [RTL/Batching] Make ASDisplayShouldFetchBatchForScrollView aware of flipped CV layouts

UICollectionViewLayout has a property called `flipsHorizontallyInOppositeLayoutDirection`. If this is set to `YES` then a RTL collectionView’s contentOffset behaves like it does in LTR. In other words, the first item is at contentOffset 0. In this case, the existing logic for `ASDisplayShouldFetchBatchForScrollView` works in RTL.

If you don’t override `flipsHorizontallyInOppositeLayoutDirection` to be `YES`, then it means that in RTL languages the first item in your collectionView will actually be at x offset `collectionView.contentSize.width - collectionView.frame.size.width`. As you scroll to the right, the content offset will decrease until you reach the end of the data at a content offset of 0,0. In this case, `ASDisplayShouldFetchBatchForScrollView` needs to know that you are in RTL and the layout is not flipped. It can then use the contentOffset as the `remainingDistance` to determine when to fetch.

* fix indentation

* assert that we are on main when accessing CV layout
2021-05-03 13:32:48 -07:00
ricky 8912ff1765 [Layout] Add RTL support to LayoutSpecs (#1983)
* [Layout] Add RTL support to LayoutSpecs

This is largely a slight update for https://github.com/TextureGroup/Texture/pull/1805. If RTL is enabled, `calculateLayoutLayoutSpec:` will flip the origin of all sublayouts.

The new part of the diff is that ASBatchFetching now supports proper fetching on RTL horizontal scrollViews.

* Fix build and add RTL batch fetching tests
2021-04-14 11:20:42 -07:00
Sylvain Defresne 39ea9fe2f2 Remove trailing semicolons between method parameters and body (#1973)
Having a semi-colon between a method parameters list and a method
body is not not correct and is usually caused by a copy and paste
error while creating the method definition from its declaration.

Fixes the following compilation warnings when building with
-Wsemicolon-before-method-body (which is part of -Wextra):

  ASPINRemoteImageDownloader.mm:230:85: error: semicolon before method body is ignored [-Werror,-Wsemicolon-before-method-body]
  - (id <ASImageContainerProtocol>)synchronouslyFetchedCachedImageWithURL:(NSURL *)URL;
                                                                                      ^
  ASPINRemoteImageDownloader.mm:275:76: error: semicolon before method body is ignored [-Werror,-Wsemicolon-before-method-body]
                           completion:(ASImageDownloaderCompletion)completion;
                                                                             ^
  2 errors generated.

Fixes applied to both code, examples and samples in documentation.
2021-03-26 11:07:31 -07:00
rqueue dca5a223b7 Expand ASExperimentalRangeUpdateOnChangesetUpdate to ASTableView (#1979)
A previous commit
(https://github.com/TextureGroup/Texture/commit/8f7444e0ece61d6ab12ecceb590be26d8d7cc99d)
aimed to fix a preloading bug for ASCollectionView. This commit expands this
fix to ASTableView as the bug occurs there too.

Previous commit message for context:

This experiment makes sure a ASCollectionView's `rangeController` updates when
a changeset WITH updates is applied. Currently it is possible for nodes
inserted into the preload range to not get preloaded when performing a batch
update.

For example, suppose a collection node has:
- Tuning parameters with a preload range of 1 screenful for the given range
  mode.
- Nodes A and B where A is visible and B is off screen.
Currently if node B is deleted and a new node C is inserted in its place, node
C will not get preloaded until the collection node is scrolled. This is because
the preloading mechanism relies on a `setNeedsUpdate` call on the range
controller as part of the `-collectionView:willDisplayCell:forItemAtIndexPath:`
delegate method when the batch update is submitted. However, in the example
outlined above, this sometimes doesn't happen automtically, causing the range
update to be delayed until the next the view scrolls.
2021-03-25 16:20:29 -07:00
Andrew Yates 05b81a5f3e Remove Facebook and shift everything around, add Remix by Buffer (#1978) 2021-03-25 13:57:23 -07:00
rqueue 8f7444e0ec Add experiment to ensure ASCollectionView's range controller updates on changeset updates (#1976)
This experiment makes sure a ASCollectionView's `rangeController` updates when
a changeset WITH updates is applied. Currently it is possible for nodes
inserted into the preload range to not get preloaded when performing a batch
update.

For example, suppose a collection node has:
- Tuning parameters with a preload range of 1 screenful for the given range
  mode.
- Nodes A and B where A is visible and B is off screen.
Currently if node B is deleted and a new node C is inserted in its place, node
C will not get preloaded until the collection node is scrolled. This is because
the preloading mechanism relies on a `setNeedsUpdate` call on the range
controller as part of the `-collectionView:willDisplayCell:forItemAtIndexPath:`
delegate method when the batch update is submitted. However, in the example
outlined above, this sometimes doesn't happen automtically, causing the range
update to be delayed until the next the view scrolls.
2021-03-25 11:45:20 -07:00
Christos Gkekas 17d4d13463 Exposes a new option in ASImageDownloaderProtocol to retry image downloads (#1948)
* Exposes a new option in ASImageDownloaderProtocol to retry image downloads

At the moment the ASBasicImageDownloader does not automatically retry image downloads if the remote
host is unreachable. On the contrary the ASPINRemoteImageDownloader automatically retries. Retrying is
something that ultimately clients need to be able to control, for example to fail fast to an alternative image
rather than keep retrying for more than one minute while not displaying any image. This change exposes
a new option in the ASImageDownloaderProtocol to retry image downloads. It also uses this new option
in both ASNetworkImageNode and also ASMultiplexImageNode, setting it to YES to preserve the current
behaviour.

* Fixes a failing test in ASMultiplexImageNodeTests

* Fixes ScreenNode.m

ScreenNode.m is implementing ASImageDownloaderProtocol and needs to
be fixed to reflect changes in the latter.
2021-02-19 08:23:01 -08:00
Ted Janeczko b4a4e2c150 Fix order-dependent ASTextNodeTests (#1963) 2021-02-17 10:09:09 -08:00
Mussa Charles 6e629bd29a Update asdkGram swift sample to swift version 5.3 (#1962) 2021-02-08 10:55:27 -08:00
Zev Eisenberg 9e8de03845 Fix WKWebView Accessibility (#1955)
* Return nil instead of empty array when no accessibility elements are found. Fixes #1954.

* Use nullability annotations to fix static analyzer warnings.

* Add UI test target.

* Add UI test to make sure web view stays accessible.

* Revert "Add UI test to make sure web view stays accessible."

This reverts commit 00253f49a0af329602b0d9709b58bc92dcd90147.

* Revert "Add UI test target."

This reverts commit 288b5e0f564ef3ba3fb5568baa832bc03124cdc9.

* Add unit test to make sure accessibility elements are correct when a WKWebView is wrapped in an ASDisplayNode.
2021-02-03 13:24:39 -08:00
Joseph Price 82e6a46943 fix missing hidden class (#1952) 2021-02-01 16:56:33 -08:00
Joseph Price ad70335ba4 use https for slack link (#1950) 2021-02-01 10:58:53 -08:00
Zev Eisenberg f91e733b04 Fix mutation of variable that is never read. (#1961) 2021-02-01 10:55:35 -08:00
Zev Eisenberg b2b996e761 Remove redundant assignment. (#1960) 2021-01-30 13:57:48 -08:00
Zev Eisenberg 5edf4e01cf Update CocoaPods to use the CDN instead of the old trunk repo. (#1957) 2021-01-29 12:46:55 -08:00
Douglas Poveda 823ac0a6f3 Set ASTableView isAccessibilityElement, accessibilityElementsHidden properties from its Element's node (#1941) 2020-12-16 16:11:41 -08:00
ricky 68a1bec062 [ASTextNode2] Make some ASTextNode2 layout files public (#1939)
* Trying to make ASTextLinePositionModifier public

* d’oh

* be a little more restrictive on the files we pull into the pod

* Never mind, I guess we need all of these

* update the project file as well

* try this again

* I think this will work this time.
2020-12-14 12:30:25 -08:00
Huy Nguyen ec19b928a8 Ship ASExperimentalDispatchApply (#1924)
Closes #1850.
2020-10-06 11:42:37 -07:00
Ben Dolman efdd8acd99 Fix hit point when ASCollectionNode inverted set to true (#1781)
* Account for possible inverted transform during hit test

When ASCollectionNode has the `inverted` flag set, a transform gets
set on the cell node. We need to make sure that we account for that
when dealing in the view coordinates.

* Store self.node and self.node.view in local variables for better readability.

* Add a test for hit testing in an inverted ASCollectionNode
2020-10-06 09:36:43 -07:00
Huy Nguyen 5a205d84c8 Fix failing ASConfigurationTests (#1923)
* Fix failing ASConfigurationTests

* Update configuration.json as well
2020-10-01 21:27:55 -07:00